duke-libraries/ddr-models

View on GitHub
lib/ddr/models/file_management.rb

Summary

Maintainability
A
45 mins
Test Coverage
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