2019-11-07 16:08:14 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2020-08-05 15:29:11 +00:00
|
|
|
require 'filemagic'
|
|
|
|
|
2019-11-07 16:08:14 +00:00
|
|
|
# Define un campo de archivo
|
|
|
|
class MetadataFile < MetadataTemplate
|
2023-10-09 16:23:50 +00:00
|
|
|
include Metadata::NonIndexableConcern
|
|
|
|
include Metadata::AlwaysPublicConcern
|
|
|
|
|
2019-11-07 16:08:14 +00:00
|
|
|
# Una ruta vacía a la imagen con una descripción vacía
|
|
|
|
def default_value
|
2023-10-09 16:23:50 +00:00
|
|
|
super || { 'path' => nil, 'description' => '' }
|
2019-11-07 16:08:14 +00:00
|
|
|
end
|
|
|
|
|
2023-10-09 16:23:50 +00:00
|
|
|
# La descripción es opcional
|
|
|
|
#
|
|
|
|
# @return [Boolean]
|
2019-11-07 16:08:14 +00:00
|
|
|
def empty?
|
2023-10-09 16:23:50 +00:00
|
|
|
value.nil? || value['path'].blank?
|
2019-11-07 16:08:14 +00:00
|
|
|
end
|
|
|
|
|
2022-03-18 18:42:39 +00:00
|
|
|
# No hay valores sugeridos para archivos subidos.
|
2023-10-09 16:23:50 +00:00
|
|
|
def values
|
|
|
|
raise NotImplementedError, "#{self.class} no tiene valores sugeridos"
|
|
|
|
end
|
2022-03-18 18:42:39 +00:00
|
|
|
|
2019-11-07 16:08:14 +00:00
|
|
|
def validate
|
|
|
|
super
|
|
|
|
|
2021-04-24 22:55:15 +00:00
|
|
|
errors << I18n.t("metadata.#{type}.site_invalid") if site.invalid?
|
2020-11-25 14:59:55 +00:00
|
|
|
errors << I18n.t("metadata.#{type}.path_required") if path_missing?
|
2022-03-07 23:52:33 +00:00
|
|
|
errors << I18n.t("metadata.#{type}.attachment_missing") if path? && !static_file
|
2019-11-07 16:08:14 +00:00
|
|
|
|
|
|
|
errors.compact!
|
|
|
|
errors.empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
# Asociar la imagen subida al sitio y obtener la ruta
|
2022-07-12 18:38:16 +00:00
|
|
|
# @return [Boolean]
|
2019-11-07 16:08:14 +00:00
|
|
|
def save
|
2023-10-09 16:23:50 +00:00
|
|
|
return true unless changed?
|
|
|
|
|
|
|
|
self[:value] = default_value if empty?
|
|
|
|
self[:value] = sanitize(value)
|
|
|
|
|
|
|
|
value['path'] = relative_destination_path_with_filename.to_s if static_file
|
2019-11-07 16:08:14 +00:00
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2021-02-23 22:00:38 +00:00
|
|
|
# Almacena el archivo en el sitio y lo devuelve o lo obtiene de la
|
|
|
|
# base de datos.
|
|
|
|
#
|
|
|
|
# Existen tres casos:
|
|
|
|
#
|
|
|
|
# * El archivo fue subido a través de HTTP
|
|
|
|
# * El archivo es una ruta que apunta a un archivo asociado al sitio
|
|
|
|
# * El archivo es una ruta a un archivo dentro del repositorio
|
2019-11-07 16:08:14 +00:00
|
|
|
#
|
2022-03-04 22:21:03 +00:00
|
|
|
# @todo encontrar una forma de obtener el attachment sin tener que
|
|
|
|
# recurrir al último subido.
|
|
|
|
#
|
|
|
|
# @return [ActiveStorage::Attachment,nil]
|
2019-11-07 16:08:14 +00:00
|
|
|
def static_file
|
2021-02-23 22:00:38 +00:00
|
|
|
@static_file ||=
|
|
|
|
case value['path']
|
|
|
|
when ActionDispatch::Http::UploadedFile
|
|
|
|
site.static_files.last if site.static_files.attach(value['path'])
|
|
|
|
when String
|
2022-07-15 22:32:06 +00:00
|
|
|
site.static_files.find_by(blob_id: blob_id) || migrate_static_file!
|
2019-11-07 16:08:14 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-10-09 16:23:50 +00:00
|
|
|
private
|
2022-03-04 22:19:40 +00:00
|
|
|
|
2023-10-09 16:23:50 +00:00
|
|
|
# Valida que estemos pasando el formato correcto
|
2022-03-04 22:21:03 +00:00
|
|
|
#
|
2023-10-09 16:23:50 +00:00
|
|
|
# @param :value [Any]
|
|
|
|
# @return [Hash,nil]
|
|
|
|
def sanitize(value)
|
|
|
|
return unless value.is_a? Hash
|
2021-02-16 21:08:42 +00:00
|
|
|
|
2023-10-09 16:23:50 +00:00
|
|
|
value.dup.tap do |v|
|
|
|
|
v['description'] = super(v['description'])
|
|
|
|
end
|
2022-03-04 22:16:36 +00:00
|
|
|
end
|
2021-02-23 22:00:38 +00:00
|
|
|
|
2022-03-04 22:21:26 +00:00
|
|
|
# Obtener la ruta al archivo relativa al sitio
|
|
|
|
#
|
|
|
|
# @return [Pathname]
|
|
|
|
def destination_path
|
|
|
|
Pathname.new(static_file_path)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Agrega el nombre de archivo a la ruta para tener retrocompatibilidad
|
|
|
|
#
|
|
|
|
# @return [Pathname]
|
|
|
|
def destination_path_with_filename
|
|
|
|
destination_path.realpath
|
2022-03-22 23:15:59 +00:00
|
|
|
# Si el archivo no llegara a existir, en lugar de hacer fallar todo,
|
|
|
|
# devolvemos la ruta original, que puede ser el archivo que no existe
|
|
|
|
# o vacía si se está subiendo uno.
|
|
|
|
rescue Errno::ENOENT => e
|
2022-07-13 17:18:39 +00:00
|
|
|
ExceptionNotifier.notify_exception(e, data: { site: site.name, path: value['path'] })
|
2022-03-22 23:15:59 +00:00
|
|
|
|
2023-01-11 20:04:22 +00:00
|
|
|
Pathname.new(File.join(site.path, value['path']))
|
2022-03-04 22:21:26 +00:00
|
|
|
end
|
|
|
|
|
2023-01-11 19:54:45 +00:00
|
|
|
# Obtener la ruta relativa al sitio.
|
|
|
|
#
|
|
|
|
# Si algo falla, devolver la ruta original para no romper el archivo.
|
|
|
|
#
|
|
|
|
# @return [String, nil]
|
2022-03-04 22:21:26 +00:00
|
|
|
def relative_destination_path_with_filename
|
2022-03-08 16:10:34 +00:00
|
|
|
destination_path_with_filename.relative_path_from(Pathname.new(site.path).realpath)
|
2022-07-13 17:18:39 +00:00
|
|
|
rescue ArgumentError => e
|
|
|
|
ExceptionNotifier.notify_exception(e, data: { site: site.name, path: value['path'] })
|
2023-01-11 19:54:45 +00:00
|
|
|
|
|
|
|
value['path']
|
2022-03-04 22:21:26 +00:00
|
|
|
end
|
|
|
|
|
2023-10-09 16:23:50 +00:00
|
|
|
# La ruta absoluta al archivo
|
|
|
|
#
|
|
|
|
# @todo Eliminar retrocompatibilidad
|
|
|
|
# @return [String]
|
2022-03-04 22:21:26 +00:00
|
|
|
def static_file_path
|
2022-03-09 22:32:53 +00:00
|
|
|
case static_file.blob.service.name
|
|
|
|
when :local
|
|
|
|
File.join(site.path, 'public', static_file.key, static_file.filename.to_s)
|
|
|
|
else
|
|
|
|
static_file.blob.service.path_for(static_file.key)
|
|
|
|
end
|
2022-03-04 22:21:26 +00:00
|
|
|
end
|
|
|
|
|
2022-07-15 22:32:06 +00:00
|
|
|
# Obtiene el id del blob asociado
|
|
|
|
#
|
|
|
|
# @return [Integer,nil]
|
|
|
|
def blob_id
|
|
|
|
@blob_id ||= ActiveStorage::Blob.where(key: key_from_path, service_name: site.name).pluck(:id).first
|
|
|
|
end
|
|
|
|
|
|
|
|
# Genera el blob para un archivo que ya se encuentra en el
|
|
|
|
# repositorio y lo agrega a la base de datos.
|
|
|
|
#
|
|
|
|
# @return [ActiveStorage::Attachment]
|
|
|
|
def migrate_static_file!
|
|
|
|
raise ArgumentError, 'El archivo no existe' unless path? && pathname.exist?
|
|
|
|
|
|
|
|
Site.transaction do
|
|
|
|
blob =
|
|
|
|
ActiveStorage::Blob.create_after_unfurling!(key: key_from_path,
|
|
|
|
io: pathname.open,
|
|
|
|
filename: pathname.basename,
|
|
|
|
service_name: site.name)
|
|
|
|
|
|
|
|
ActiveStorage::Attachment.create!(name: 'static_files', record: site, blob: blob)
|
|
|
|
end
|
|
|
|
rescue ArgumentError => e
|
|
|
|
ExceptionNotifier.notify_exception(e, data: { site: site.name, path: value['path'] })
|
|
|
|
nil
|
|
|
|
end
|
2023-10-09 16:23:50 +00:00
|
|
|
|
|
|
|
# Determina si necesitamos la imagen pero no la tenemos
|
|
|
|
def path_missing?
|
|
|
|
required && !path?
|
|
|
|
end
|
|
|
|
|
|
|
|
# Determina si el archivo ya fue subido
|
|
|
|
def uploaded?
|
|
|
|
value['path'].is_a?(String)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Obtiene la ruta absoluta al archivo
|
|
|
|
#
|
|
|
|
# @return [Pathname]
|
|
|
|
def pathname
|
|
|
|
raise NoMethodError unless uploaded?
|
|
|
|
|
|
|
|
@pathname ||= Pathname.new(File.join(site.path, value['path']))
|
|
|
|
end
|
|
|
|
|
|
|
|
# Obtiene la key del attachment a partir de la ruta
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
def key_from_path
|
|
|
|
@key_from_path ||= pathname.dirname.basename.to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
# @todo Este método no se puede correr sobre archivos recién subidos
|
|
|
|
def path?
|
|
|
|
value['path'].present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def description?
|
|
|
|
value['description'].present?
|
|
|
|
end
|
2019-11-07 16:08:14 +00:00
|
|
|
end
|