Our previous system to delete cached attachments didn't work for documents because the `custom_hash_data` is different for files created from a file and files created from cached attachments. When creating a document attachment, the name of the file is taken into account to calculate the hash. Let's say the original file name is "logo.pdf", and the generated hash is "123456". The cached attachment will be "123456.pdf", so the generated hash using the cached attachment will be something different, like "28af3". So the file that will be removed will be "28af3.pdf", and not "123456.pdf", which will still be present. Furthermore, there are times where users choose a file and then they close the browser or go to a different page. In those cases, we weren't deleting the cached attachments either. So we're adding a rake task to delete these files once a day. This way we can simplify the logic we were using to destroy cached attachments. Note there's related a bug in documents: when editing a record (for example, a proposal), if the title of the document changes, its hash changes, and so it will be impossible to generate a link to that document. Changing the way this hash is generated is not an option because it would break links to existing files. We'll try to fix it when moving to Active Storage.
105 lines
3.8 KiB
Ruby
105 lines
3.8 KiB
Ruby
class Document < ApplicationRecord
|
|
include DocumentsHelper
|
|
include DocumentablesHelper
|
|
has_attached_file :attachment, url: "/system/:class/:prefix/:style/:hash.:extension",
|
|
hash_data: ":class/:style/:custom_hash_data",
|
|
use_timestamp: false,
|
|
hash_secret: Rails.application.secrets.secret_key_base
|
|
attr_accessor :cached_attachment
|
|
|
|
belongs_to :user
|
|
belongs_to :documentable, polymorphic: true
|
|
|
|
# Disable paperclip security validation due to polymorphic configuration
|
|
# Paperclip do not allow to use Procs on valiations definition
|
|
do_not_validate_attachment_file_type :attachment
|
|
validate :attachment_presence
|
|
validate :validate_attachment_content_type, if: -> { attachment.present? }
|
|
validate :validate_attachment_size, if: -> { attachment.present? }
|
|
validates :title, presence: true
|
|
validates :user_id, presence: true
|
|
validates :documentable_id, presence: true, if: -> { persisted? }
|
|
validates :documentable_type, presence: true, if: -> { persisted? }
|
|
|
|
before_save :set_attachment_from_cached_attachment, if: -> { cached_attachment.present? }
|
|
|
|
scope :admin, -> { where(admin: true) }
|
|
|
|
def set_cached_attachment_from_attachment
|
|
self.cached_attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem
|
|
attachment.path
|
|
else
|
|
attachment.url
|
|
end
|
|
end
|
|
|
|
def set_attachment_from_cached_attachment
|
|
self.attachment = if Paperclip::Attachment.default_options[:storage] == :filesystem
|
|
File.open(cached_attachment)
|
|
else
|
|
URI.parse(cached_attachment)
|
|
end
|
|
end
|
|
|
|
Paperclip.interpolates :prefix do |attachment, style|
|
|
attachment.instance.prefix(attachment, style)
|
|
end
|
|
|
|
Paperclip.interpolates :custom_hash_data do |attachment, _style|
|
|
attachment.instance.custom_hash_data(attachment)
|
|
end
|
|
|
|
def prefix(attachment, _style)
|
|
if !attachment.instance.persisted?
|
|
"cached_attachments/user/#{attachment.instance.user_id}"
|
|
else
|
|
":attachment/:id_partition"
|
|
end
|
|
end
|
|
|
|
def custom_hash_data(attachment)
|
|
original_filename = if !attachment.instance.persisted?
|
|
attachment.instance.attachment_file_name
|
|
else
|
|
attachment.instance.title
|
|
end
|
|
"#{attachment.instance.user_id}/#{original_filename}"
|
|
end
|
|
|
|
def humanized_content_type
|
|
attachment_content_type.split("/").last.upcase
|
|
end
|
|
|
|
private
|
|
|
|
def documentable_class
|
|
documentable_type.constantize if documentable_type.present?
|
|
end
|
|
|
|
def validate_attachment_size
|
|
if documentable_class.present? &&
|
|
attachment_file_size > documentable_class.max_file_size
|
|
errors.add(:attachment, I18n.t("documents.errors.messages.in_between",
|
|
min: "0 Bytes",
|
|
max: "#{max_file_size(documentable_class)} MB"))
|
|
end
|
|
end
|
|
|
|
def validate_attachment_content_type
|
|
if documentable_class &&
|
|
!accepted_content_types(documentable_class).include?(attachment_content_type)
|
|
accepted_content_types = documentable_humanized_accepted_content_types(documentable_class)
|
|
message = I18n.t("documents.errors.messages.wrong_content_type",
|
|
content_type: attachment_content_type,
|
|
accepted_content_types: accepted_content_types)
|
|
errors.add(:attachment, message)
|
|
end
|
|
end
|
|
|
|
def attachment_presence
|
|
if attachment.blank? && cached_attachment.blank?
|
|
errors.add(:attachment, I18n.t("errors.messages.blank"))
|
|
end
|
|
end
|
|
end
|