From 460635e940fca637bf41499cb8d4070beb9a3db7 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:03:02 -0300 Subject: [PATCH 01/35] ya no hace falta analizar archivos en primer plano --- config/initializers/analyze_job.rb | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 config/initializers/analyze_job.rb diff --git a/config/initializers/analyze_job.rb b/config/initializers/analyze_job.rb deleted file mode 100644 index f268e0dd..00000000 --- a/config/initializers/analyze_job.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -# TODO: Estamos procesando el análisis de los archivos en el momento -# porque queremos obtener la ruta del archivo en el momento y no -# después. Necesitaríamos poder generar el vínculo en el -# repositorio a destiempo, modificando el Job de ActiveStorage -ActiveStorage::AnalyzeJob.queue_adapter = :inline From 29950e4380b4c6d5122a0bfd3f8024178b44a40d Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:05:11 -0300 Subject: [PATCH 02/35] cargar decorators --- config/application.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/application.rb b/config/application.rb index 7326ae0f..0e6e7f44 100644 --- a/config/application.rb +++ b/config/application.rb @@ -38,6 +38,12 @@ module Sutty config.active_storage.variant_processor = :vips + config.to_prepare do + Dir.glob(File.join(File.dirname(__FILE__), '..', 'app', '**', '*_decorator.rb')) do |c| + Rails.configuration.cache_classes ? require(c) : load(c) + end + end + config.after_initialize do ActiveStorage::DirectUploadsController.include ActiveStorage::AuthenticatedDirectUploadsController From 3de1228c26541185a62fa7a528dfc170308018bf Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:08:09 -0300 Subject: [PATCH 03/35] implementa un servicio de carga de archivos en jekyll --- .../active_storage/service/jekyll_service.rb | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/lib/active_storage/service/jekyll_service.rb diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb new file mode 100644 index 00000000..686c3979 --- /dev/null +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module ActiveStorage + module Service + # Sube los archivos a cada repositorio y los agrega al LFS de su + # repositorio git. + # + # @todo: Implementar LFS. No nos gusta mucho la idea porque duplica + # el espacio en disco, pero es la única forma que tenemos (hasta que + # implementemos IPFS) para poder transferir los archivos junto con el + # sitio. + class JekyllService < Service::DiskService + BLOB_NAME = 'blob' + + # Para poder guardar el archivo con el nombre original pero poder + # recuperarlo durante el download, luego de subirlo le cambiamos el + # nombre y creamos un link simbólico a un nombre conocido. + def upload(key, io, checksum: nil, **options) + super.tap do + path = path_for(key) + filename = options[:filename].to_s + + FileUtils.mv path, path.sub(/#{BLOB_NAME}\z/, filename) + FileUtils.ln_s filename, path + end + end + + # Mantener retrocompatibilidad con cómo gestionamos los archivos + # subidos hasta ahora. + # + # @param :key [String] + # @return [String] + def folder_for(key) + key + end + + # Crea una ruta para la llave con un nombre conocido. + def path_for(key) + File.join root, folder_for(key), BLOB_NAME + end + end + end +end From 873f2c7bcb8db83a1bdc46580158ff28ce3e67e9 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:09:06 -0300 Subject: [PATCH 04/35] informar cuando no se pudo cargar el archivo --- app/models/metadata_file.rb | 1 + config/locales/en.yml | 2 ++ config/locales/es.yml | 2 ++ 3 files changed, 5 insertions(+) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 80cefa27..b020d078 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -19,6 +19,7 @@ class MetadataFile < MetadataTemplate 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") unless static_file errors.compact! errors.empty? diff --git a/config/locales/en.yml b/config/locales/en.yml index b814796d..8bbe3621 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -41,10 +41,12 @@ en: not_an_image: 'Not an image' path_required: 'Missing image for upload' no_file_for_description: "Description with no associated image" + attachment_missing: "I couldn't save the image :(" file: site_invalid: 'The file cannot be stored if the site configuration is not valid' path_required: "Missing file for upload" no_file_for_description: "Description with no associated file" + attachment_missing: "I couldn't save the file :(" event: zone_missing: 'Inexistent timezone' date_missing: 'Event date is required' diff --git a/config/locales/es.yml b/config/locales/es.yml index a6fbd407..93dd162e 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -41,10 +41,12 @@ es: not_an_image: 'No es una imagen' path_required: 'Se necesita una imagen' no_file_for_description: 'Se envió una descripción sin imagen asociada' + attachment_missing: 'no pude guardar el archivo :(' file: site_invalid: 'El archivo no se puede almacenar si la configuración del sitio no es válida' path_required: 'Se necesita un archivo' no_file_for_description: 'se envió una descripción sin archivo asociado' + attachment_missing: 'no pude guardar el archivo :(' event: zone_missing: 'El huso horario no es correcto' date_missing: 'La fecha es obligatoria' From c4139c4b92746c84cdbdaaffe1fea31a69afc9a8 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:12:06 -0300 Subject: [PATCH 05/35] =?UTF-8?q?eliminar=20c=C3=B3digo=20que=20no=20se=20?= =?UTF-8?q?usa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_file.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index b020d078..58adc857 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -35,12 +35,6 @@ class MetadataFile < MetadataTemplate value['path'].is_a?(String) end - # Determina si la ruta es opcional pero deja pasar si la ruta se - # especifica - def path_optional? - !required && !path? - end - # Asociar la imagen subida al sitio y obtener la ruta # # XXX: Si evitamos guardar cambios con changed? no tenemos forma de From 3d5267451eec0e879fad28ef4086cd240884d683 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:15:38 -0300 Subject: [PATCH 06/35] =?UTF-8?q?eliminar=20c=C3=B3digo=20que=20ya=20no=20?= =?UTF-8?q?se=20usa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_file.rb | 65 ------------------------------------- 1 file changed, 65 deletions(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 58adc857..86644fcc 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -93,71 +93,6 @@ class MetadataFile < MetadataTemplate private - def filemagic - @filemagic ||= FileMagic.new(FileMagic::MAGIC_MIME) - end - - # @return [Pathname] - def path - @path ||= Pathname.new(File.join(site.path, value['path'])) - end - - def file - return unless path? - - @file ||= - case value['path'] - when ActionDispatch::Http::UploadedFile then value['path'].tempfile.path - when String then File.join(site.path, value['path']) - end - end - - # Hacemos un link duro para colocar el archivo dentro del repositorio - # y no duplicar el espacio que ocupan. Esto requiere que ambos - # directorios estén dentro del mismo punto de montaje. - # - # XXX: Asumimos que el archivo destino no existe porque siempre - # contiene una key única. - # - # @return [Boolean] - def hardlink - return if hardlink? - return if File.exist? destination_path - - FileUtils.mkdir_p(File.dirname(destination_path)) - FileUtils.ln(uploaded_path, destination_path).zero? - end - - def hardlink? - File.stat(uploaded_path).ino == File.stat(destination_path).ino - rescue Errno::ENOENT - false - end - - # Obtener la ruta al archivo - # https://stackoverflow.com/a/53908358 - def uploaded_relative_path - ActiveStorage::Blob.service.path_for(static_file.key) - end - - # @return [String] - def uploaded_path - Rails.root.join uploaded_relative_path - end - - # La ruta del archivo mantiene el nombre original pero contiene el - # nombre interno y único del archivo para poder relacionarlo con el - # archivo subido en Sutty. - # - # @return [String] - def relative_destination_path - @relative_destination_path ||= File.join('public', static_file.key, static_file.filename.to_s) - end - - # @return [String] - def destination_path - @destination_path ||= File.join(site.path, relative_destination_path) - end # No hay archivo pero se lo describió def no_file_for_description? From d8fd25a7cb32ab6cdec028f4d054cf398df4b97c Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:16:36 -0300 Subject: [PATCH 07/35] =?UTF-8?q?chequear=20la=20descripci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_file.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 86644fcc..20d093b2 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -91,11 +91,14 @@ class MetadataFile < MetadataTemplate value['path'].present? end - private + def description? + value['description'].present? + end + private # No hay archivo pero se lo describió def no_file_for_description? - value['description'].present? && value['path'].blank? + !path? && description? end end From 63f8b869eb185dd9c607ed4b684baf9ab478a08d Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:17:03 -0300 Subject: [PATCH 08/35] fixup! cargar decorators --- config/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index 0e6e7f44..031c1909 100644 --- a/config/application.rb +++ b/config/application.rb @@ -39,7 +39,7 @@ module Sutty config.active_storage.variant_processor = :vips config.to_prepare do - Dir.glob(File.join(File.dirname(__FILE__), '..', 'app', '**', '*_decorator.rb')) do |c| + Dir.glob(File.join(File.dirname(__FILE__), '..', 'app', '**', '*_decorator.rb')).sort.each do |c| Rails.configuration.cache_classes ? require(c) : load(c) end end From 37deb361785d9f79c2d721898c3545a5c7323985 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:19:40 -0300 Subject: [PATCH 09/35] conseguir la ruta al archivo --- app/models/metadata_file.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 20d093b2..89e6d461 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -83,6 +83,15 @@ class MetadataFile < MetadataTemplate 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 + def key_from_path path.dirname.basename.to_s end From 03d9913f58f4be0bd0e2c3417577bab6f8a67c4a Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:21:03 -0300 Subject: [PATCH 10/35] obtener el archivo subido --- app/models/metadata_file.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 89e6d461..e4c0d037 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -66,18 +66,19 @@ class MetadataFile < MetadataTemplate # XXX: La última opción provoca archivos duplicados, pero es lo mejor # que tenemos hasta que resolvamos https://0xacab.org/sutty/sutty/-/issues/213 # - # @return [ActiveStorage::Attachment] + # @todo encontrar una forma de obtener el attachment sin tener que + # recurrir al último subido. + # + # @return [ActiveStorage::Attachment,nil] def static_file - return unless path? - @static_file ||= case value['path'] when ActionDispatch::Http::UploadedFile site.static_files.last if site.static_files.attach(value['path']) when String - if (blob = ActiveStorage::Blob.where(key: key_from_path).pluck(:id).first) - site.static_files.find_by(blob_id: blob) - elsif site.static_files.attach(io: path.open, filename: path.basename) + 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 end end @@ -92,8 +93,11 @@ class MetadataFile < MetadataTemplate @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 - path.dirname.basename.to_s + pathname.dirname.basename.to_s end def path? From fdbe724f7c92953e9ba2cfa74f8470a6fa617c2e Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:21:26 -0300 Subject: [PATCH 11/35] asociar el archivo subido al post --- app/models/metadata_file.rb | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index e4c0d037..1c859481 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -43,13 +43,7 @@ class MetadataFile < MetadataTemplate # repetida. def save value['description'] = sanitize value['description'] - - if path? - hardlink - value['path'] = relative_destination_path - else - value['path'] = nil - end + value['path'] = static_file ? relative_destination_path_with_filename.to_s : nil true end @@ -110,6 +104,28 @@ class MetadataFile < MetadataTemplate 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 + end + + def relative_destination_path_with_filename + destination_path_with_filename.relative_path_from(site.path) + end + + def static_file_path + static_file.blob.service.path_for(static_file.key) + end + # No hay archivo pero se lo describió def no_file_for_description? !path? && description? From f3df5504944cbdbf2f07aa0cd33f8fd8cd7f55a0 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:21:48 -0300 Subject: [PATCH 12/35] =?UTF-8?q?no=20permitir=20subir=20im=C3=A1genes=20q?= =?UTF-8?q?ue=20no=20son=20para=20web?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_image.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/models/metadata_image.rb b/app/models/metadata_image.rb index f91a6273..f86c5c26 100644 --- a/app/models/metadata_image.rb +++ b/app/models/metadata_image.rb @@ -13,8 +13,6 @@ class MetadataImage < MetadataFile # Determina si es una imagen def image? - return true unless file - - filemagic.file(file).starts_with? 'image/' + static_file&.blob&.send(:web_image?) end end From 02b52b23b91b0c88a9c48b01604eb9ee22692d9f Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:24:04 -0300 Subject: [PATCH 13/35] compartir el nombre de archivo con JekyllService --- app/models/active_storage/blob_decorator.rb | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 app/models/active_storage/blob_decorator.rb diff --git a/app/models/active_storage/blob_decorator.rb b/app/models/active_storage/blob_decorator.rb new file mode 100644 index 00000000..9c01251a --- /dev/null +++ b/app/models/active_storage/blob_decorator.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module ActiveStorage + # Modificaciones a ActiveStorage::Blob + module BlobDecorator + extend ActiveSupport::Concern + + included do + # Permitir que llegue el nombre de archivo al servicio de subida de + # archivos. + # + # @return [Hash] + def service_metadata + if forcibly_serve_as_binary? + { content_type: ActiveStorage.binary_content_type, disposition: :attachment, filename: filename } + elsif !allowed_inline? + { content_type: content_type, disposition: :attachment, filename: filename } + else + { content_type: content_type, filename: filename } + end + end + end + end +end + +ActiveStorage::Blob.include ActiveStorage::BlobDecorator From 69e7df4c31b8f3b72d9e2cde2b7430bb3cef4f66 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 4 Mar 2022 19:26:56 -0300 Subject: [PATCH 14/35] instanciar JekyllService para cada sitio --- .../service/registry_decorator.rb | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 app/lib/active_storage/service/registry_decorator.rb diff --git a/app/lib/active_storage/service/registry_decorator.rb b/app/lib/active_storage/service/registry_decorator.rb new file mode 100644 index 00000000..f7f20784 --- /dev/null +++ b/app/lib/active_storage/service/registry_decorator.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module ActiveStorage + module Service + # Modificaciones a ActiveStorage::Service::Registry + module RegistryDecorator + extend ActiveSupport::Concern + + included do + # El mismo comportamiento que #fetch con el agregado de generar + # un {JekyllService} para cada sitio. + def fetch(name) + services.fetch(name.to_sym) do |key| + if configurations.include?(key) + services[key] = configurator.build(key) + elsif (site = Site.find_by_name(key)) + root = File.join(site.path, 'public') + services[key] = ActiveStorage::Service::JekyllService.new(root: root, public: true).tap do |s| + s.name = key.to_sym + end + elsif block_given? + yield key + else + raise KeyError, "Missing configuration for the #{key} Active Storage service. " \ + "Configurations available for the #{configurations.keys.to_sentence} services." + end + end + end + end + end + end +end + +ActiveStorage::Service::Registry.include ActiveStorage::Service::RegistryDecorator From 15e978c877cacc8b5eb3a6c79c5e0e9cc3349eda Mon Sep 17 00:00:00 2001 From: f Date: Sat, 5 Mar 2022 20:08:55 -0300 Subject: [PATCH 15/35] eran clases --- app/lib/active_storage/service/jekyll_service.rb | 2 +- app/lib/active_storage/service/registry_decorator.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb index 686c3979..173f6898 100644 --- a/app/lib/active_storage/service/jekyll_service.rb +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ActiveStorage - module Service + class Service # Sube los archivos a cada repositorio y los agrega al LFS de su # repositorio git. # diff --git a/app/lib/active_storage/service/registry_decorator.rb b/app/lib/active_storage/service/registry_decorator.rb index f7f20784..f6794607 100644 --- a/app/lib/active_storage/service/registry_decorator.rb +++ b/app/lib/active_storage/service/registry_decorator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ActiveStorage - module Service + class Service # Modificaciones a ActiveStorage::Service::Registry module RegistryDecorator extend ActiveSupport::Concern From 7f6063475bb63eed2cab8523d5f15d1ef71b15ea Mon Sep 17 00:00:00 2001 From: f Date: Sat, 5 Mar 2022 20:09:09 -0300 Subject: [PATCH 16/35] =?UTF-8?q?m=C3=A9todo=20gen=C3=A9rico=20para=20inst?= =?UTF-8?q?anciar=20el=20servicio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/active_storage/service/jekyll_service.rb | 7 +++++++ app/lib/active_storage/service/registry_decorator.rb | 5 +---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb index 173f6898..20dda523 100644 --- a/app/lib/active_storage/service/jekyll_service.rb +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -12,6 +12,13 @@ module ActiveStorage class JekyllService < Service::DiskService BLOB_NAME = 'blob' + # Genera un servicio para un sitio determinado + def self.build_for_site(site:) + new(root: File.join(site.path, 'public'), public: true).tap do |js| + js.name = site.name.to_sym + end + end + # Para poder guardar el archivo con el nombre original pero poder # recuperarlo durante el download, luego de subirlo le cambiamos el # nombre y creamos un link simbólico a un nombre conocido. diff --git a/app/lib/active_storage/service/registry_decorator.rb b/app/lib/active_storage/service/registry_decorator.rb index f6794607..c7096356 100644 --- a/app/lib/active_storage/service/registry_decorator.rb +++ b/app/lib/active_storage/service/registry_decorator.rb @@ -14,10 +14,7 @@ module ActiveStorage if configurations.include?(key) services[key] = configurator.build(key) elsif (site = Site.find_by_name(key)) - root = File.join(site.path, 'public') - services[key] = ActiveStorage::Service::JekyllService.new(root: root, public: true).tap do |s| - s.name = key.to_sym - end + services[key] = ActiveStorage::Service::JekyllService.build_for_site(site: site) elsif block_given? yield key else From 10eef47ce8bfb0f233083e070207f7d1c8bbf578 Mon Sep 17 00:00:00 2001 From: f Date: Sat, 5 Mar 2022 20:09:37 -0300 Subject: [PATCH 17/35] todos los archivos subidos se asocian al sitio --- .../attached/changes/create_one_decorator.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/lib/active_storage/attached/changes/create_one_decorator.rb diff --git a/app/lib/active_storage/attached/changes/create_one_decorator.rb b/app/lib/active_storage/attached/changes/create_one_decorator.rb new file mode 100644 index 00000000..bfb92478 --- /dev/null +++ b/app/lib/active_storage/attached/changes/create_one_decorator.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module ActiveStorage + module Attached::Changes::CreateOneDecorator + extend ActiveSupport::Concern + + included do + private + + # A partir de ahora todos los archivos se suben al servicio de + # cada sitio. + def attachment_service_name + record.name.to_sym + end + end + end +end + +ActiveStorage::Attached::Changes::CreateOne.include ActiveStorage::Attached::Changes::CreateOneDecorator From bfb8e95599762f73cc5c8b093932d1750b55c5ea Mon Sep 17 00:00:00 2001 From: f Date: Sun, 6 Mar 2022 12:51:06 -0300 Subject: [PATCH 18/35] obtener el nombre de archivo desde la key esto agrega una query pero permite no tener que agregar una ruta falsa --- .../active_storage/service/jekyll_service.rb | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb index 20dda523..02316e65 100644 --- a/app/lib/active_storage/service/jekyll_service.rb +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -10,8 +10,6 @@ module ActiveStorage # implementemos IPFS) para poder transferir los archivos junto con el # sitio. class JekyllService < Service::DiskService - BLOB_NAME = 'blob' - # Genera un servicio para un sitio determinado def self.build_for_site(site:) new(root: File.join(site.path, 'public'), public: true).tap do |js| @@ -19,16 +17,7 @@ module ActiveStorage end end - # Para poder guardar el archivo con el nombre original pero poder - # recuperarlo durante el download, luego de subirlo le cambiamos el - # nombre y creamos un link simbólico a un nombre conocido. - def upload(key, io, checksum: nil, **options) - super.tap do - path = path_for(key) - filename = options[:filename].to_s - FileUtils.mv path, path.sub(/#{BLOB_NAME}\z/, filename) - FileUtils.ln_s filename, path end end @@ -41,9 +30,17 @@ module ActiveStorage key end + # Obtiene el nombre de archivo para esta key + # + # @param :key [String] + # @return [String] + def filename_for(key) + @filename_for ||= ActiveStorage::Blob.where(key: key).limit(1).pluck(:filename).first + end + # Crea una ruta para la llave con un nombre conocido. def path_for(key) - File.join root, folder_for(key), BLOB_NAME + File.join root, folder_for(key), filename_for(key) end end end From 85cab49208cbfae13b1876ab5a57495544b4220c Mon Sep 17 00:00:00 2001 From: f Date: Sun, 6 Mar 2022 15:25:15 -0300 Subject: [PATCH 19/35] subir los archivos desde el editor al sitio --- .../direct_uploads_controller_decorator.rb | 18 ++++++++++++++++++ app/controllers/posts_controller.rb | 6 ++++++ .../active_storage/service/jekyll_service.rb | 4 ---- 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 app/controllers/active_storage/direct_uploads_controller_decorator.rb diff --git a/app/controllers/active_storage/direct_uploads_controller_decorator.rb b/app/controllers/active_storage/direct_uploads_controller_decorator.rb new file mode 100644 index 00000000..f27c4cfb --- /dev/null +++ b/app/controllers/active_storage/direct_uploads_controller_decorator.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module ActiveStorage + # Modifica la creación de un blob antes de subir el archivo para que + # incluya el JekyllService adecuado. + module DirectUploadsControllerDecorator + extend ActiveSupport::Concern + + included do + def create + blob = ActiveStorage::Blob.create_before_direct_upload!(service_name: session[:service_name], **blob_args) + render json: direct_upload_json(blob) + end + end + end +end + +ActiveStorage::DirectUploadsController.include ActiveStorage::DirectUploadsControllerDecorator diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index dbdd4d0a..2aff9ac9 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -6,6 +6,7 @@ class PostsController < ApplicationController rescue_from Pundit::NilPolicyError, with: :page_not_found before_action :authenticate_usuarie! + before_action :service_for_direct_upload, only: %i[new edit] # TODO: Traer los comunes desde ApplicationController breadcrumb -> { current_usuarie.email }, :edit_usuarie_registration_path @@ -166,4 +167,9 @@ class PostsController < ApplicationController def post @post ||= site.posts(lang: locale).find(params[:post_id] || params[:id]) end + + # Recuerda el nombre del servicio de subida de archivos + def service_for_direct_upload + session[:service_name] = site.name.to_sym + end end diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb index 02316e65..601b6f2f 100644 --- a/app/lib/active_storage/service/jekyll_service.rb +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -17,10 +17,6 @@ module ActiveStorage end end - - end - end - # Mantener retrocompatibilidad con cómo gestionamos los archivos # subidos hasta ahora. # From c13c021fe92d749cf4a98673bc3cc504c6e90d78 Mon Sep 17 00:00:00 2001 From: f Date: Sun, 6 Mar 2022 15:28:25 -0300 Subject: [PATCH 20/35] asignar el nombre de archivo en la subida directa --- .../active_storage/service/jekyll_service.rb | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb index 601b6f2f..a2e04336 100644 --- a/app/lib/active_storage/service/jekyll_service.rb +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -17,6 +17,32 @@ module ActiveStorage end end + # Lo mismo que en DiskService agregando el nombre de archivo en la + # firma. Esto permite que luego podamos guardar el archivo donde + # corresponde. + def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) + instrument :url, key: key do |payload| + verified_token_with_expiration = ActiveStorage.verifier.generate( + { + key: key, + content_type: content_type, + content_length: content_length, + checksum: checksum, + service_name: name, + filename: filename_for(key) + }, + expires_in: expires_in, + purpose: :blob_token + ) + + generated_url = url_helpers.update_rails_disk_service_url(verified_token_with_expiration, host: current_host) + + payload[:url] = generated_url + + generated_url + end + end + # Mantener retrocompatibilidad con cómo gestionamos los archivos # subidos hasta ahora. # From 211fb308e39ddd1a380b711e98d240c59e9576d6 Mon Sep 17 00:00:00 2001 From: f Date: Sun, 6 Mar 2022 15:59:57 -0300 Subject: [PATCH 21/35] asignar archivos subidos desde el editor al sitio --- .../disk_controller_decorator.rb | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 app/controllers/active_storage/disk_controller_decorator.rb diff --git a/app/controllers/active_storage/disk_controller_decorator.rb b/app/controllers/active_storage/disk_controller_decorator.rb new file mode 100644 index 00000000..14366a15 --- /dev/null +++ b/app/controllers/active_storage/disk_controller_decorator.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module ActiveStorage + # Modificar {DiskController} para poder asociar el blob a un sitio + module DiskControllerDecorator + extend ActiveSupport::Concern + + included do + # Asociar el archivo subido al sitio correspondiente. Cada sitio + # tiene su propio servicio de subida de archivos. + def update + if (token = decode_verified_token) + if acceptable_content?(token) + named_disk_service(token[:service_name]).upload token[:key], request.body, checksum: token[:checksum] + + blob = ActiveStorage::Blob.find_by_key token[:key] + site = Site.find_by_name token[:service_name] + + site.static_files.attach(blob) + else + head :unprocessable_entity + end + else + head :not_found + end + rescue ActiveStorage::IntegrityError + head :unprocessable_entity + end + end + end +end + +ActiveStorage::DiskController.include ActiveStorage::DiskControllerDecorator From 61622a4c416b73288cc897b89b8721fe3bb700c9 Mon Sep 17 00:00:00 2001 From: f Date: Sun, 6 Mar 2022 16:00:21 -0300 Subject: [PATCH 22/35] =?UTF-8?q?documentaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/active_storage/service/jekyll_service.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb index a2e04336..3b1db7ec 100644 --- a/app/lib/active_storage/service/jekyll_service.rb +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -11,6 +11,9 @@ module ActiveStorage # sitio. class JekyllService < Service::DiskService # Genera un servicio para un sitio determinado + # + # @param :site [Site] + # @return [ActiveStorage::Service::JekyllService] def self.build_for_site(site:) new(root: File.join(site.path, 'public'), public: true).tap do |js| js.name = site.name.to_sym @@ -20,6 +23,13 @@ module ActiveStorage # Lo mismo que en DiskService agregando el nombre de archivo en la # firma. Esto permite que luego podamos guardar el archivo donde # corresponde. + # + # @param :key [String] + # @param :expires_in [Integer] + # @param :content_type [String] + # @param :content_length [Integer] + # @param :checksum [String] + # @return [String] def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) instrument :url, key: key do |payload| verified_token_with_expiration = ActiveStorage.verifier.generate( @@ -61,6 +71,9 @@ module ActiveStorage end # Crea una ruta para la llave con un nombre conocido. + # + # @param :key [String] + # @return [String] def path_for(key) File.join root, folder_for(key), filename_for(key) end From 6c9288b03bd64f907760d63a5c0ddd638b9ea74f Mon Sep 17 00:00:00 2001 From: f Date: Sun, 6 Mar 2022 16:01:16 -0300 Subject: [PATCH 23/35] =?UTF-8?q?deprecar=20la=20migraci=C3=B3n=20de=20arc?= =?UTF-8?q?hivos=20est=C3=A1ticos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit siempre fue super lenta y falible --- app/models/site.rb | 7 +--- app/models/site/static_file_migration.rb | 52 ------------------------ 2 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 app/models/site/static_file_migration.rb diff --git a/app/models/site.rb b/app/models/site.rb index 5b78d625..7d4875e5 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -57,7 +57,7 @@ class Site < ApplicationRecord # Carga el sitio Jekyll una vez que se inicializa el modelo o después # de crearlo after_initialize :load_jekyll - after_create :load_jekyll, :static_file_migration! + after_create :load_jekyll # Cambiar el nombre del directorio before_update :update_name! before_save :add_private_key_if_missing! @@ -474,11 +474,6 @@ class Site < ApplicationRecord config.hostname = hostname end - # Migra los archivos a Sutty - def static_file_migration! - Site::StaticFileMigration.new(site: self).migrate! - end - # Valida si el sitio tiene al menos una forma de alojamiento asociada # y es la local # diff --git a/app/models/site/static_file_migration.rb b/app/models/site/static_file_migration.rb deleted file mode 100644 index 36a882bf..00000000 --- a/app/models/site/static_file_migration.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -class Site - # Obtiene todos los archivos relacionados en artículos del sitio y los - # sube a Sutty. - class StaticFileMigration - # Tipos de metadatos que contienen archivos - STATIC_TYPES = %i[file image].freeze - - attr_reader :site - - def initialize(site:) - @site = site - end - - def migrate! - modified = site.docs.map do |doc| - next unless STATIC_TYPES.map do |field| - next unless doc.attribute? field - next unless doc[field].path? - next unless doc[field].static_file - - true - end.any? - - log.write "#{doc.path.relative};no se pudo guardar\n" unless doc.save(validate: false) - - doc.path.absolute - end.compact - - log.close - - return if modified.empty? - - # TODO: Hacer la migración desde el servicio de creación de sitios? - site.repository.commit(file: modified, - message: I18n.t('sites.static_file_migration'), - usuarie: author) - end - - private - - def author - @author ||= GitAuthor.new email: "sutty@#{Site.domain}", - name: 'Sutty' - end - - def log - @log ||= File.open(File.join(site.path, 'migration.csv'), 'w') - end - end -end From f7d8a3ecf9e0142292ea819e7ab94c6b6b096a56 Mon Sep 17 00:00:00 2001 From: f Date: Sun, 6 Mar 2022 17:07:37 -0300 Subject: [PATCH 24/35] fixup! obtener el nombre de archivo desde la key --- app/lib/active_storage/service/jekyll_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/active_storage/service/jekyll_service.rb b/app/lib/active_storage/service/jekyll_service.rb index 3b1db7ec..92b26e0e 100644 --- a/app/lib/active_storage/service/jekyll_service.rb +++ b/app/lib/active_storage/service/jekyll_service.rb @@ -67,7 +67,7 @@ module ActiveStorage # @param :key [String] # @return [String] def filename_for(key) - @filename_for ||= ActiveStorage::Blob.where(key: key).limit(1).pluck(:filename).first + ActiveStorage::Blob.where(key: key).limit(1).pluck(:filename).first end # Crea una ruta para la llave con un nombre conocido. From 81cbafef66e31ba60ac6bf396adc88696d85f142 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 7 Mar 2022 20:52:33 -0300 Subject: [PATCH 25/35] validaciones --- app/models/metadata_file.rb | 2 +- app/models/metadata_image.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 1c859481..5be7f84c 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -19,7 +19,7 @@ class MetadataFile < MetadataTemplate 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") unless static_file + errors << I18n.t("metadata.#{type}.attachment_missing") if path? && !static_file errors.compact! errors.empty? diff --git a/app/models/metadata_image.rb b/app/models/metadata_image.rb index f86c5c26..85ee062a 100644 --- a/app/models/metadata_image.rb +++ b/app/models/metadata_image.rb @@ -5,7 +5,7 @@ class MetadataImage < MetadataFile def validate super - errors << I18n.t('metadata.image.not_an_image') unless image? + errors << I18n.t('metadata.image.not_an_image') unless path? && image? errors.compact! errors.empty? From 7de96a4581a1d1483628c3da11f99e1a4e454ec0 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 8 Mar 2022 13:10:34 -0300 Subject: [PATCH 26/35] =?UTF-8?q?usar=20la=20ruta=20absoluta=20tambi=C3=A9?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit en producción usamos links simbólicos entre directorios y sin resolver la ubicación real de los sitios estábamos generando rutas erróneas. --- app/models/metadata_file.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 5be7f84c..31becc64 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -119,7 +119,7 @@ class MetadataFile < MetadataTemplate end def relative_destination_path_with_filename - destination_path_with_filename.relative_path_from(site.path) + destination_path_with_filename.relative_path_from(Pathname.new(site.path).realpath) end def static_file_path From 79974b710497c2db0498de7f9f4375f2802711c9 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 8 Mar 2022 13:21:13 -0300 Subject: [PATCH 27/35] =?UTF-8?q?validar=20im=C3=A1genes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/metadata_image.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/metadata_image.rb b/app/models/metadata_image.rb index 85ee062a..fc61a4d7 100644 --- a/app/models/metadata_image.rb +++ b/app/models/metadata_image.rb @@ -5,7 +5,7 @@ class MetadataImage < MetadataFile def validate super - errors << I18n.t('metadata.image.not_an_image') unless path? && image? + errors << I18n.t('metadata.image.not_an_image') if path? && !image? errors.compact! errors.empty? From d4d1acc8d91e5cf23db4fa056a5c0520d7179c43 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 9 Mar 2022 19:32:53 -0300 Subject: [PATCH 28/35] retrocompatibilidad los archivos subidos como archivos locales ya estaban copiados al sitio --- app/models/metadata_file.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 31becc64..0f50d643 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -123,7 +123,12 @@ class MetadataFile < MetadataTemplate end def static_file_path - static_file.blob.service.path_for(static_file.key) + 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ó From 263394baef931995b78e4a962a6bebfbc5f76b27 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 18 Mar 2022 15:42:39 -0300 Subject: [PATCH 29/35] no fallar al serializar Site#everything_of el problema era que los archivos subidos no se pueden marshalear y estamos usando MetadataTemplate#values para poder cachearlos. closes #1315 closes #1316 closes #1842 closes #2368 closes #2369 closes #2370 closes #2371 closes #2372 closes #2373 closes #2393 closes #2396 closes #2922 closes #3347 closes #3350 closes #3353 closes #3356 closes #3437 closes #3486 closes #3487 closes #3488 closes #3551 closes #3554 closes #4229 closes #4256 closes #4259 closes #4262 closes #4265 closes #4553 closes #4556 closes #4603 closes #4606 closes #4647 closes #4759 closes #4767 closes #4787 closes #4794 closes #4833 closes #4835 closes #4836 closes #4875 closes #4876 closes #4877 closes #4879 closes #4881 closes #4882 closes #4883 closes #4884 closes #4885 closes #4886 closes #4887 closes #4888 closes #4889 closes #4890 closes #4891 closes #4892 closes #4906 closes #4907 closes #4908 closes #4976 closes #4977 closes #4978 closes #4979 closes #4980 closes #4981 closes #4982 closes #4983 closes #4984 closes #4985 closes #4986 closes #4987 --- app/models/metadata_file.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 0f50d643..2c1a6d59 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -13,6 +13,11 @@ class MetadataFile < MetadataTemplate 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 From c5896a63cfdd48128592087aac35aaf1a6523df1 Mon Sep 17 00:00:00 2001 From: f Date: Tue, 22 Mar 2022 20:15:59 -0300 Subject: [PATCH 30/35] resiliencia closes #5061 closes #5060 closes #5058 closes #5057 closes #5056 closes #5054 closes #5053 closes #5051 closes #5050 closes #5048 closes #5047 closes #5045 closes #5042 closes #5040 closes #5038 closes #5036 closes #5034 closes #5033 closes #5032 closes #5030 closes #4946 closes #3856 closes #3562 closes #3558 closes #3557 closes #2707 closes #2706 closes #2705 closes #2703 closes #2702 closes #2297 closes #2296 closes #2295 closes #1970 closes #1969 closes #1768 --- app/models/metadata_file.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index 2c1a6d59..eca87478 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -121,6 +121,13 @@ class MetadataFile < MetadataTemplate # @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 From ecb823f40746b4eae0d61cc3acca63ac620881f0 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 23 Mar 2022 11:37:00 -0300 Subject: [PATCH 31/35] al migrar un archivo reutilizar la key --- app/models/metadata_file.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb index eca87478..71d3f049 100644 --- a/app/models/metadata_file.rb +++ b/app/models/metadata_file.rb @@ -78,7 +78,9 @@ class MetadataFile < MetadataTemplate 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 + site.static_files.last.tap do |s| + s.blob.update(key: key_from_path) + end end end end From 455070f2ea576775fe28f0bb2d01a6b62d2dc7d0 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 4 Apr 2022 13:56:40 -0300 Subject: [PATCH 32/35] normalizar todas las strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit convierte la codificación de windows/osx en unicode normalizado. --- app/models/metadata_markdown.rb | 2 +- app/models/metadata_markdown_content.rb | 2 +- app/models/metadata_permalink.rb | 2 +- app/models/metadata_string.rb | 2 +- app/models/metadata_template.rb | 9 ++++++--- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/models/metadata_markdown.rb b/app/models/metadata_markdown.rb index 1e8b4fc8..7816ec33 100644 --- a/app/models/metadata_markdown.rb +++ b/app/models/metadata_markdown.rb @@ -12,6 +12,6 @@ class MetadataMarkdown < MetadataText # markdown y se eliminan autolinks. Mejor es habilitar la generación # SAFE de CommonMark en la configuración del sitio. def sanitize(string) - string + string.unicode_normalize(:nfkc) end end diff --git a/app/models/metadata_markdown_content.rb b/app/models/metadata_markdown_content.rb index 92a1ab21..cb4124db 100644 --- a/app/models/metadata_markdown_content.rb +++ b/app/models/metadata_markdown_content.rb @@ -25,6 +25,6 @@ class MetadataMarkdownContent < MetadataText # markdown y se eliminan autolinks. Mejor es deshabilitar la # generación SAFE de CommonMark en la configuración del sitio. def sanitize(string) - string.tr("\r", '') + string.tr("\r", '').unicode_normalize(:nfkc) end end diff --git a/app/models/metadata_permalink.rb b/app/models/metadata_permalink.rb index 59b68461..9b0c063c 100644 --- a/app/models/metadata_permalink.rb +++ b/app/models/metadata_permalink.rb @@ -19,7 +19,7 @@ class MetadataPermalink < MetadataString # puntos suspensivos, la primera / para que siempre sea relativa y # agregamos una / al final si la ruta no tiene extensión. def sanitize(value) - value = value.strip.gsub('..', '/').gsub('./', '').squeeze('/') + value = value.strip.unicode_normalize(:nfkc).gsub('..', '/').gsub('./', '').squeeze('/') value = value[1..-1] if value.start_with? '/' value += '/' if File.extname(value).blank? diff --git a/app/models/metadata_string.rb b/app/models/metadata_string.rb index 95aac4d4..28bfe82a 100644 --- a/app/models/metadata_string.rb +++ b/app/models/metadata_string.rb @@ -17,7 +17,7 @@ class MetadataString < MetadataTemplate def sanitize(string) return '' if string.blank? - sanitizer.sanitize(string.strip, + sanitizer.sanitize(string.strip.unicode_normalize(:nfkc), tags: [], attributes: []).strip.html_safe end diff --git a/app/models/metadata_template.rb b/app/models/metadata_template.rb index 5baa7a4a..a72f8e83 100644 --- a/app/models/metadata_template.rb +++ b/app/models/metadata_template.rb @@ -184,9 +184,12 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, return if string.nil? return string unless string.is_a? String - sanitizer.sanitize(string.tr("\r", ''), - tags: allowed_tags, - attributes: allowed_attributes).strip.html_safe + sanitizer + .sanitize(string.tr("\r", '').unicode_normalize(:nfkc), + tags: allowed_tags, + attributes: allowed_attributes) + .strip + .html_safe end def sanitizer From 30bc14d83e790c6540a6fb19e8afa8184eef6c37 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 4 Apr 2022 14:27:00 -0300 Subject: [PATCH 33/35] normalizar los nombres de archivo enviados por subida directa --- .../direct_uploads_controller_decorator.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/controllers/active_storage/direct_uploads_controller_decorator.rb b/app/controllers/active_storage/direct_uploads_controller_decorator.rb index f27c4cfb..3052f974 100644 --- a/app/controllers/active_storage/direct_uploads_controller_decorator.rb +++ b/app/controllers/active_storage/direct_uploads_controller_decorator.rb @@ -11,6 +11,17 @@ module ActiveStorage blob = ActiveStorage::Blob.create_before_direct_upload!(service_name: session[:service_name], **blob_args) render json: direct_upload_json(blob) end + + private + + # Normalizar los caracteres unicode en los nombres de archivos + # para que puedan propagarse correctamente a través de todo el + # stack. + def blob_args + params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type, metadata: {}).to_h.symbolize_keys.tap do |ba| + ba[:filename] = ba[:filename].unicode_normalize(:nfkc) + end + end end end end From 4d6a26d67178ec90e4b8f580889cee528f74d195 Mon Sep 17 00:00:00 2001 From: f Date: Mon, 4 Apr 2022 14:32:58 -0300 Subject: [PATCH 34/35] normalizar todos los nombres de archivos subidos --- .../http/uploaded_file_decorator.rb | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/lib/action_dispatch/http/uploaded_file_decorator.rb diff --git a/app/lib/action_dispatch/http/uploaded_file_decorator.rb b/app/lib/action_dispatch/http/uploaded_file_decorator.rb new file mode 100644 index 00000000..c171c81c --- /dev/null +++ b/app/lib/action_dispatch/http/uploaded_file_decorator.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActionDispatch + module Http + # Normaliza los nombres de archivo para que se propaguen + # correctamente a través de todo el stack. + module UploadedFileDecorator + extend ActiveSupport::Concern + + included do + # Devolver el nombre de archivo con caracteres unicode + # normalizados + def original_filename + @original_filename.unicode_normalize(:nfkc) + end + end + end + end +end + +ActionDispatch::Http::UploadedFile.include ActionDispatch::Http::UploadedFileDecorator From eb7d0679bbc411d8f2afe831a23d958bc0e91abd Mon Sep 17 00:00:00 2001 From: f Date: Thu, 28 Apr 2022 10:34:57 -0300 Subject: [PATCH 35/35] normalizar strings sin romperlas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit en castellano no nos afectó pero en árabe rompe el significado --- .../active_storage/direct_uploads_controller_decorator.rb | 2 +- app/lib/action_dispatch/http/uploaded_file_decorator.rb | 2 +- app/models/metadata_markdown.rb | 2 +- app/models/metadata_markdown_content.rb | 2 +- app/models/metadata_permalink.rb | 2 +- app/models/metadata_string.rb | 2 +- app/models/metadata_template.rb | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/active_storage/direct_uploads_controller_decorator.rb b/app/controllers/active_storage/direct_uploads_controller_decorator.rb index 3052f974..c62dae2a 100644 --- a/app/controllers/active_storage/direct_uploads_controller_decorator.rb +++ b/app/controllers/active_storage/direct_uploads_controller_decorator.rb @@ -19,7 +19,7 @@ module ActiveStorage # stack. def blob_args params.require(:blob).permit(:filename, :byte_size, :checksum, :content_type, metadata: {}).to_h.symbolize_keys.tap do |ba| - ba[:filename] = ba[:filename].unicode_normalize(:nfkc) + ba[:filename] = ba[:filename].unicode_normalize end end end diff --git a/app/lib/action_dispatch/http/uploaded_file_decorator.rb b/app/lib/action_dispatch/http/uploaded_file_decorator.rb index c171c81c..0bdebdc0 100644 --- a/app/lib/action_dispatch/http/uploaded_file_decorator.rb +++ b/app/lib/action_dispatch/http/uploaded_file_decorator.rb @@ -11,7 +11,7 @@ module ActionDispatch # Devolver el nombre de archivo con caracteres unicode # normalizados def original_filename - @original_filename.unicode_normalize(:nfkc) + @original_filename.unicode_normalize end end end diff --git a/app/models/metadata_markdown.rb b/app/models/metadata_markdown.rb index 7816ec33..a09e351c 100644 --- a/app/models/metadata_markdown.rb +++ b/app/models/metadata_markdown.rb @@ -12,6 +12,6 @@ class MetadataMarkdown < MetadataText # markdown y se eliminan autolinks. Mejor es habilitar la generación # SAFE de CommonMark en la configuración del sitio. def sanitize(string) - string.unicode_normalize(:nfkc) + string.unicode_normalize end end diff --git a/app/models/metadata_markdown_content.rb b/app/models/metadata_markdown_content.rb index cb4124db..75088e30 100644 --- a/app/models/metadata_markdown_content.rb +++ b/app/models/metadata_markdown_content.rb @@ -25,6 +25,6 @@ class MetadataMarkdownContent < MetadataText # markdown y se eliminan autolinks. Mejor es deshabilitar la # generación SAFE de CommonMark en la configuración del sitio. def sanitize(string) - string.tr("\r", '').unicode_normalize(:nfkc) + string.tr("\r", '').unicode_normalize end end diff --git a/app/models/metadata_permalink.rb b/app/models/metadata_permalink.rb index 9b0c063c..30ad32cc 100644 --- a/app/models/metadata_permalink.rb +++ b/app/models/metadata_permalink.rb @@ -19,7 +19,7 @@ class MetadataPermalink < MetadataString # puntos suspensivos, la primera / para que siempre sea relativa y # agregamos una / al final si la ruta no tiene extensión. def sanitize(value) - value = value.strip.unicode_normalize(:nfkc).gsub('..', '/').gsub('./', '').squeeze('/') + value = value.strip.unicode_normalize.gsub('..', '/').gsub('./', '').squeeze('/') value = value[1..-1] if value.start_with? '/' value += '/' if File.extname(value).blank? diff --git a/app/models/metadata_string.rb b/app/models/metadata_string.rb index 28bfe82a..c1d888b1 100644 --- a/app/models/metadata_string.rb +++ b/app/models/metadata_string.rb @@ -17,7 +17,7 @@ class MetadataString < MetadataTemplate def sanitize(string) return '' if string.blank? - sanitizer.sanitize(string.strip.unicode_normalize(:nfkc), + sanitizer.sanitize(string.strip.unicode_normalize, tags: [], attributes: []).strip.html_safe end diff --git a/app/models/metadata_template.rb b/app/models/metadata_template.rb index a72f8e83..ddcd100e 100644 --- a/app/models/metadata_template.rb +++ b/app/models/metadata_template.rb @@ -185,7 +185,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type, return string unless string.is_a? String sanitizer - .sanitize(string.tr("\r", '').unicode_normalize(:nfkc), + .sanitize(string.tr("\r", '').unicode_normalize, tags: allowed_tags, attributes: allowed_attributes) .strip