SpontaneousCMS/spontaneous

View on GitHub
lib/spontaneous/field/file.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# encoding: UTF-8

require 'tempfile'
require 'digest/md5'

module Spontaneous::Field
  class File < Base
    has_editor

    def blank?
      values[:path].blank?
    end

    # In the case of clearing the field we will have been given a pending_value of ""
    # we don't want that to run asynchronously
    def asynchronous?
      return false if (pending_value && pending_value[:value].blank?)
      true
    end

    def outputs
      [:html, :path, :filesize, :filename, :storage_name]
    end

    def set_pending_value(value, site)
      file = process_upload(value, site)
      pending = case file
        when nil
          ""
        when String
          { tempfile: file }
        else
          serialize_pending_file(file)
        end
      super(pending, site)
    end

    def page_lock_description
      "Processing file '#{pending_value[:value][:filename]}'"
    end

    def serialize_pending_file(file)
      { tempfile: file.path, type: file.mimetype, filename: file.filename, filesize: file.filesize, src: file.url }
    end

    def storage_headers(content_type, filename)
      headers = { content_type: content_type }
      if prototype && prototype.options[:attachment]
        headers.update(content_disposition: %(attachment; filename=#{Rack::Utils.escape(filename)}))
      end
      headers
    end

    def process_upload(value, site)
      return nil if value.blank?
      file, filename, mimetype = fileinfo(value)
      digest = file_digest(file)
      media_file = site.tempfile(self, filename, digest, storage_headers(mimetype, filename))
      media_file.copy(file)
      media_file
    end

    def preprocess(image, site)
      file, filename, mimetype = fileinfo(image)
      if file.nil?
        set_unprocessed_value(["", ""].to_json)
        return ""
      end
      unless ::File.exist?(file)
        set_unprocessed_value([file, ""].to_json)
        return file
      end
      digest = file_digest(file)
      set_unprocessed_value([filename, digest].to_json)

      media_file = site.file(owner, filename, digest, storage_headers(mimetype, filename))
      media_file.copy(file)
      media_file
    end

    def file_digest(file)
      Digest::MD5.file(file).hexdigest
    rescue Errno::ENOENT => e
      ""
    end

    def fileinfo(fileinfo)
      file = filename = mimetype = nil
      case fileinfo
      when Hash
        file, filename, mimetype = fileinfo.values_at(:tempfile, :filename, :type)
      when ::String
        filename = ::File.basename(fileinfo)
        file     = fileinfo
      end
      [file, filename, mimetype]
    end

    def generate_filesize(input, site)
      if input.respond_to?(:filesize)
        input.filesize
      else
        ::File.exist?(input) ? ::File.size(input) : 0
      end
    end

    def generate_filename(input, site)
      if input.respond_to?(:filename)
        input.filename
      else
        ::File.basename(input.to_s)
      end
    end

    def generate_path(input, site)
      return input if input.is_a?(::String)
      input.url
    end

    def generate_html(input, site)
      generate_path(input, site)
    end

    def generate_storage_name(input, site)
      if (storage = input.try(:storage))
        return storage.name
      end
      nil
    end

    def storage
      site.storage(storage_name)
    end

    def export(user = nil)
      super(user).merge({
        processed_value: processed_values
      })
    end

    def original_filename
      ::File.basename(file_info[0] || "")
    end

    def file_hash
      file_info[1]
    end

    def file_info
      @file_info ||= Spontaneous::JSON.parse(unprocessed_value)
    end

    def value(format = :html)
      case format
      when :html, "html"
        path
      else
        super
      end
    end

    def path
      storage.to_url(super)
    end

    def url
      path
    end

    def mimetype
      MIME::Types.type_for(original_filename).first
    end

    self.register
  end
end