Noosfero/noosfero

View on GitHub
app/models/uploaded_file.rb

Summary

Maintainability
B
5 hrs
Test Coverage
# Article type that handles uploaded files.
#
# Limitation: only file metadata are versioned. Only the latest version
# of the file itself is kept. (FIXME?)

require "sdbm" unless RUBY_ENGINE == "jruby"

class UploadedFile < Article
  include UploadSanitizer
  include CroppedImage

  attr_accessible :uploaded_data, :title

  include Noosfero::Plugin::HotSpot

  def environment
    profile.environment
  end

  def self.type_name
    _("File")
  end

  DBM_PRIVATE_FILE = "cache/private_files"
  after_save do |uploaded_file|
    if uploaded_file.access_changed?
      dbm = SDBM.open(DBM_PRIVATE_FILE)
      if uploaded_file.access <= Entitlement::Levels.levels[:visitors]
        dbm.delete(uploaded_file.public_filename)
      else
        dbm.store(uploaded_file.public_filename, uploaded_file.full_path)
      end
      dbm.close
    end
  end unless RUBY_ENGINE == "jruby"

  after_save :update_profile_disk_usage
  after_destroy :update_profile_disk_usage

  track_actions :upload_image, :after_create, keep_params: ["view_url", "thumbnail_path", "parent.url", "parent.name"], if: Proc.new { |a| a.access == Entitlement::Levels.levels[:visitors] && a.image? && !a.parent.nil? && a.parent.gallery? }, custom_target: :parent

  def title
    if self.name.present? then self.name else self.filename end
  end

  def title=(value)
    self.name = value
  end

  before_create do |uploaded_file|
    uploaded_file.is_image = true if uploaded_file.image?
  end

  def thumbnail_path
    self.image? ? self.full_filename(:display).to_s.gsub(Rails.root.join("public").to_s, "") : nil
  end

  def first_paragraph
    ""
  end

  def self.max_size
    default = 5.megabytes

    multipliers = {
      KB: :kilobytes,
      MB: :megabytes,
      GB: :gigabytes,
      TB: :terabytes,
    }
    max_upload_size = NOOSFERO_CONF["max_upload_size"]

    if max_upload_size =~ /^(\d+(\.\d+)?)\s*(KB|MB|GB|TB)?$/
      number = $1.to_f
      unit = $3 || :MB
      multiplier = multipliers[unit.to_sym]

      number.send(multiplier).to_i
    else
      default
    end
  end

  # FIXME need to define min/max file size
  #
  # default max_size is 1.megabyte to redefine it set options:
  #  :min_size => 2.megabytes
  #  :max_size => 5.megabytes
  has_attachment storage: :file_system,
                 thumbnails: { icon: [24, 24],
                               bigicon: [50, 50],
                               thumb: "130x130>",
                               slideshow: "320x240>",
                               display: "640X480>" },
                 thumbnail_class: Thumbnail,
                 max_size: self.max_size,
                 processor: "Rmagick"

  validates_attachment size: N_("{fn} of uploaded file was larger than the maximum size of %{size}").sub("%{size}", self.max_size.to_humanreadable).fix_i18n
  validate :profile_quota_usage

  extend AttachmentFuAssistant::ClassMethods
  attachment_fu_thumbnails

  postgresql_attachment_fu

  # Use this method only to get the generic icon for this kind of content.
  # If you want the specific icon for a file type or the iconified version
  # of an image, use FilePresenter.for(uploaded_file).icon_name
  def self.icon_name(article = nil)
    unless article.nil?
      warn = ("=" * 80) + "\n" +
             "The method `UploadedFile.icon_name(obj)` is deprecated. " +
             "You must to encapsulate UploadedFile with `FilePresenter.for()`." +
             "\n" + ("=" * 80)
      raise NoMethodError, warn if ENV["RAILS_ENV"] == "test"

      Rails.logger.warn warn if Rails.logger
      puts warn if ENV["RAILS_ENV"] == "development"
    end
    "upload-file"
  end

  def mime_type
    content_type
  end

  def self.short_description
    _("Uploaded file")
  end

  def self.description
    _("Upload any kind of file you want.")
  end

  alias :orig_set_filename :filename=
  def filename=(value)
    orig_set_filename(value)
    self.name ||= self.filename
  end

  def download_disposition
    case content_type
    when "application/pdf"
      "inline"
    else
      "attachment"
    end
  end

  def data
    File.read(self.full_filename)
  end

  def to_html(options = {})
    warn = ("=" * 80) + "\n" +
           "The method `UploadedFile#to_html()` is deprecated. " +
           "You must to encapsulate UploadedFile with `FilePresenter.for()`." +
           "\n" + ("=" * 80)
    raise NoMethodError, warn if ENV["RAILS_ENV"] == "test"

    Rails.logger.warn warn if Rails.logger
    puts warn if ENV["RAILS_ENV"] == "development"
    article = self
    if image?
      proc do
        image_tag(article.public_filename(:display),
                  class: article.css_class_name,
                  style: "max-width: 100%") +
          content_tag("div", article.abstract, class: "uploaded-file-description")
      end
    else
      proc do
        content_tag("div",
                    link_to(article.title, article.url),
                    class: article.css_class_name) +
          content_tag("div", article.abstract, class: "uploaded-file-description")
      end
    end
  end

  def extension
    dotindex = self.filename.rindex(".")
    return nil unless dotindex

    self.filename[(dotindex + 1)..-1].downcase
  end

  def allow_children?
    false
  end

  def can_display_hits?
    false
  end

  def gallery?
    self.parent && self.parent.folder? && self.parent.gallery?
  end

  def uploaded_file?
    true
  end

  def image?
    mime_type =~ /^image\//
  end

  def notifiable?
    !image?
  end

  def icon
    icons = {
      "no-icon" => "no-icon",
      "edit" => "edit",
      "new" => "plus-circle",
      "save" => "save",
      "send" => "share-square",
      "cancel" => "remove",
      "add" => "plus",
      "up" => "arrow-up",
      "down" => "arrow-down",
      "left" => "arrow-left",
      "right" => "arrow-right",
      "up-disabled" => "arrow-up",
      "down-disabled" => "arrow-down",
      "left-disabled" => "arrow-left",
      "right-disabled" => "arrow-right",
      "up-read" => "quote-right",
      "search" => "search",
      "ok" => "check",
      "login" => "sign-in",
      "help" => "question",
      "spread" => "send",
      "eyes" => "eye",
      "photos" => "image",
      "menu-people" => "user",
      "event" => "calendar",
      "forum" => "users",
      "home" => "home",
      "product" => "shopping-bag",
      "todo" => "clipboard",
      "chat" => "comments",
      "enterprise" => "building-o",
      "blog" => "newspaper",
      "community" => "users",
    }

    icons.default = "generic-icon"

    icons[content_type.split("/").first()]
  end

  private

    def profile_quota_usage
      return if profile.nil?

      if profile.upload_quota.present? &&
         ((size + profile.disk_usage) > profile.upload_quota.megabytes)
        errors.add(:size, _("Your upload quota has exceeded"))
      end
    end

    def update_profile_disk_usage
      profile.update_disk_usage!
    end
end