module Ddr::Models module FileManagement extend ActiveSupport::Concern included do define_model_callbacks :add_file before_add_file :virus_scan after_add_file :set_original_filename after_save :create_virus_check_event end FileToAdd = Struct.new(:dsid, :source_path, :original_filename) # @param file [String, File, ActionDispatch::Http::UploadedFile] # The file, or path to file, to add. # @param dsid [String] The datastream ID to which file should be added. # @param mime_type [String] Explicit mime type to set # (otherwise discerned from file path or name). # @param external [Boolean] Add file to external datastream. # Not required for external datastream classes # or datastream instances having controlGroup 'E'. def add_file(file, dsid, mime_type: nil, external: false, original_filename: nil) mime_type ||= MediaType.call(file) # XXX Should we use original_filename, if present? source_path = Ddr::Utils.file_path(file) original_filename ||= Ddr::Utils.file_name(file) file_to_add = FileToAdd.new(dsid, source_path, original_filename) cache.with(file_to_add: file_to_add) do run_callbacks(:add_file) do if external || ( datastreams.include?(dsid) && datastreams[dsid].external? ) add_external_file(file, dsid, mime_type: mime_type) else add_file_datastream(file, dsid: dsid, mimeType: mime_type) end end end end def add_file_datastream(file, opts={}) if Ddr::Utils.file_path?(file) file = File.new(file, "rb") end super end # @api private # Normally this method should not be called directly. # Call `add_file` with dsid for external datastream, or with # `:external=>true` option if no spec for dsid. def add_external_file(file, dsid, mime_type: nil) file_path = Ddr::Utils.file_path(file) # raises ArgumentError ds = datastreams[dsid] || add_external_datastream(dsid) unless ds.external? raise ArgumentError, "Cannot add external file to non-external datastream." end ds.add_file(file_path, mime_type: mime_type) end def external_datastreams datastreams.values.select { |ds| ds.external? } end def external_datastream_file_paths external_datastreams.map(&:file_paths).flatten end def add_external_datastream dsid, opts={} create_datastream(Ddr::Datastreams::ExternalFileDatastream, dsid).tap do |ds| add_datastream(ds) self.class.build_datastream_accessor(dsid) end end protected def virus_scan file_to_add = cache.get(:file_to_add) result = Ddr::Actions::VirusCheck.call(file_to_add.source_path) if file_to_add.dsid == Ddr::Datastreams::CONTENT cache.put(:virus_scan_result, result) end rescue ArgumentError => e # file is a blob (string) logger.error(e) end def create_virus_check_event if result = cache.get(:virus_scan_result) Ddr::Events::VirusCheckEvent.create(result.merge(pid: pid)) end end def set_original_filename if file_to_add = cache.get(:file_to_add) if file_to_add.dsid == Ddr::Datastreams::CONTENT self.original_filename = file_to_add.original_filename end end end end end