5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-26 02:16:21 +00:00
panel/app/models/metadata_file.rb

152 lines
4.1 KiB
Ruby

# frozen_string_literal: true
require 'filemagic'
# Define un campo de archivo
class MetadataFile < MetadataTemplate
# Una ruta vacía a la imagen con una descripción vacía
def default_value
super || { 'path' => nil, 'description' => nil }
end
def empty?
value == default_value
end
# No hay valores sugeridos para archivos subidos.
#
# XXX: Esto ayuda a deserializar en {Site#everything_of}
def values; end
def validate
super
errors << I18n.t("metadata.#{type}.site_invalid") if site.invalid?
errors << I18n.t("metadata.#{type}.path_required") if path_missing?
errors << I18n.t("metadata.#{type}.no_file_for_description") if no_file_for_description?
errors << I18n.t("metadata.#{type}.attachment_missing") if path? && !static_file
errors.compact!
errors.empty?
end
# 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
# Asociar la imagen subida al sitio y obtener la ruta
#
# XXX: Si evitamos guardar cambios con changed? no tenemos forma de
# saber que un archivo subido manualmente se convirtió en
# un Attachment y cada vez que lo editemos vamos a subir una imagen
# repetida.
def save
value['description'] = sanitize value['description']
value['path'] = static_file ? relative_destination_path_with_filename.to_s : nil
true
end
# 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
#
# XXX: La última opción provoca archivos duplicados, pero es lo mejor
# que tenemos hasta que resolvamos https://0xacab.org/sutty/sutty/-/issues/213
#
# @todo encontrar una forma de obtener el attachment sin tener que
# recurrir al último subido.
#
# @return [ActiveStorage::Attachment,nil]
def static_file
@static_file ||=
case value['path']
when ActionDispatch::Http::UploadedFile
site.static_files.last if site.static_files.attach(value['path'])
when String
if (blob_id = ActiveStorage::Blob.where(key: key_from_path).pluck(:id).first)
site.static_files.find_by(blob_id: blob_id)
elsif path? && pathname.exist? && site.static_files.attach(io: pathname.open, filename: pathname.basename)
site.static_files.last.tap do |s|
s.blob.update(key: key_from_path)
end
end
end
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
pathname.dirname.basename.to_s
end
def path?
value['path'].present?
end
def description?
value['description'].present?
end
private
# 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
# 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
ExceptionNotifier.notify_exception(e)
value['path']
end
def relative_destination_path_with_filename
destination_path_with_filename.relative_path_from(Pathname.new(site.path).realpath)
end
def static_file_path
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
end
# No hay archivo pero se lo describió
def no_file_for_description?
!path? && description?
end
end