diff --git a/app/models/metadata_array.rb b/app/models/metadata_array.rb index 368aa546..68f4effb 100644 --- a/app/models/metadata_array.rb +++ b/app/models/metadata_array.rb @@ -2,23 +2,17 @@ # Una lista de valores class MetadataArray < MetadataTemplate + include Metadata::IndexableConcern + include Metadata::AlwaysPublicConcern + # El valor por defecto es una array vacía + # + # @return [Array] def default_value super || [] end - # Los Arrays no se pueden cifrar todavía - # TODO: Cifrar y decifrar arrays - def private? - false - end - - # Solo los datos públicos se indexan, aunque MetadataArray no se cifra - # aun, dejamos esto preparado para la posteridad. - def indexable? - true && !private? - end - + # @return [String] def to_s value.join(', ') end diff --git a/app/models/metadata_content.rb b/app/models/metadata_content.rb index 9516b907..bbcab741 100644 --- a/app/models/metadata_content.rb +++ b/app/models/metadata_content.rb @@ -3,26 +3,29 @@ # Se encarga del contenido del artículo y quizás otros campos que # requieran texto largo. class MetadataContent < MetadataTemplate + include Metadata::ContentConcern + include Metatada::IndexableConcern + + # @return [String] def default_value super || '' end + # @return [String] def value self[:value] || legacy_content || default_value end - def front_matter? - false - end - + # Trae el contenido desde el Post + # + # @return [String] def document_value document.content end - def indexable? - true && !private? - end - + # Eliminar HTML + # + # @return [String] def to_s sanitizer.sanitize value, tags: [], attributes: [] end @@ -30,6 +33,8 @@ class MetadataContent < MetadataTemplate private # Detectar si el contenido estaba en Markdown y pasarlo a HTML + # + # @return [String,nil] def legacy_content return unless document_value return document_value if /^\s* nil, 'lng' => nil } end diff --git a/app/models/metadata_html.rb b/app/models/metadata_html.rb index ea283362..27b3c100 100644 --- a/app/models/metadata_html.rb +++ b/app/models/metadata_html.rb @@ -2,10 +2,11 @@ # Campos en HTML class MetadataHtml < MetadataContent - def front_matter? - true - end + include Metadata::FrontMatterConcern + # Volver a obtener el valor desde el documento + # + # @return [String,nil] def document_value document.data[name.to_s] end diff --git a/app/models/metadata_lang.rb b/app/models/metadata_lang.rb index ff6c08e6..8689111d 100644 --- a/app/models/metadata_lang.rb +++ b/app/models/metadata_lang.rb @@ -2,6 +2,9 @@ # Un campo de idioma class MetadataLang < MetadataTemplate + include Metadata::NonIndexableConcern + include Metadata::AlwaysPublicConcern + def default_value super || I18n.locale end @@ -11,10 +14,14 @@ class MetadataLang < MetadataTemplate document.collection.label.to_sym end + # @return [Symbol] def value self[:value] ||= document_value || default_value end + # Todos los locales + # + # @return [Array] def values site.locales end diff --git a/app/models/metadata_markdown_content.rb b/app/models/metadata_markdown_content.rb index 75088e30..016447a8 100644 --- a/app/models/metadata_markdown_content.rb +++ b/app/models/metadata_markdown_content.rb @@ -1,29 +1,37 @@ # frozen_string_literal: true # Contenido con el editor de Markdown +# +# @todo Deprecar class MetadataMarkdownContent < MetadataText + include Metadata::ContentConcern + # Renderizar a HTML y sanitizar def to_s sanitize CommonMarker.render_doc(value, %i[FOOTNOTES SMART], %i[table strikethrough autolink]).to_html end + # @return [String] def value self[:value] || document_value || default_value end - def front_matter? - false - end - + # Obtener el contenido desde el documento + # # @return [String] def document_value document.content end + private + # XXX: No sanitizamos acá porque se escapan varios símbolos de # markdown y se eliminan autolinks. Mejor es deshabilitar la # generación SAFE de CommonMark en la configuración del sitio. + # + # @param :string [String] + # @return [String] def sanitize(string) string.tr("\r", '').unicode_normalize end diff --git a/app/models/metadata_non_geo.rb b/app/models/metadata_non_geo.rb index 6aec8461..0bc3b956 100644 --- a/app/models/metadata_non_geo.rb +++ b/app/models/metadata_non_geo.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true +# Un mapa no geográfico class MetadataNonGeo < MetadataGeo; end diff --git a/app/models/metadata_number.rb b/app/models/metadata_number.rb index 057e110f..43cbc11d 100644 --- a/app/models/metadata_number.rb +++ b/app/models/metadata_number.rb @@ -2,11 +2,18 @@ # Un campo numérico class MetadataNumber < MetadataTemplate - # Nada + include Metadata::NonIndexableConcern + + # El valor por defecto desde el esquema de datos o nada + # + # @return [Integer, nil] def default_value super || nil end + # Convertir a Integer + # + # @return [Boolean] def save return true unless changed? diff --git a/app/models/metadata_order.rb b/app/models/metadata_order.rb index 1b33a388..21d018a4 100644 --- a/app/models/metadata_order.rb +++ b/app/models/metadata_order.rb @@ -2,12 +2,20 @@ # Un campo de orden class MetadataOrder < MetadataTemplate + include Metadata::NonIndexableConcern + include Metadata::AlwaysPublicConcern + # El valor según la posición del post en la relación ordenada por # fecha, a fecha más alta, posición más alta + # + # @return [Integer] def default_value super || site.posts(lang: lang).sort_by(:date).index(post) end + # Convertir el valor en un entero + # + # @return [Boolean] def save return true unless changed? @@ -15,9 +23,4 @@ class MetadataOrder < MetadataTemplate true end - - # El orden nunca puede ser privado - def private? - false - end end diff --git a/app/models/metadata_password.rb b/app/models/metadata_password.rb index 1e0e2698..5d2d6ffd 100644 --- a/app/models/metadata_password.rb +++ b/app/models/metadata_password.rb @@ -2,12 +2,7 @@ # Almacena una contraseña class MetadataPassword < MetadataString - # Las contraseñas no son indexables - # - # @return [boolean] - def indexable? - false - end + include Metadata::NonIndexableConcern private diff --git a/app/models/metadata_path.rb b/app/models/metadata_path.rb index 95fc7dbb..9b21ede4 100644 --- a/app/models/metadata_path.rb +++ b/app/models/metadata_path.rb @@ -2,6 +2,9 @@ # Este campo representa el archivo donde se almacenan los datos class MetadataPath < MetadataTemplate + include Metadata::NonIndexableConcern + include Metadata::AlwaysPublicConcern + # :label en este caso es el idioma/colección # # @return [String] @@ -22,10 +25,12 @@ class MetadataPath < MetadataTemplate alias absolute value alias to_s value + # @return [String] def relative Pathname.new(value).relative_path_from(Pathname.new(site.path)).to_s end + # @return [String] def basename File.basename(value, ext) end @@ -35,14 +40,19 @@ class MetadataPath < MetadataTemplate private + # Obtiene la extensión + # + # @return [String] def ext document.data['ext'].blank? ? '.markdown' : document.data['ext'] end + # @return [String] def slug post.slug.value end + # @return [String] def date post.date.value.strftime('%F') end diff --git a/app/models/metadata_permalink.rb b/app/models/metadata_permalink.rb index 895b7439..061458d8 100644 --- a/app/models/metadata_permalink.rb +++ b/app/models/metadata_permalink.rb @@ -2,10 +2,7 @@ # Este metadato permite generar rutas manuales. class MetadataPermalink < MetadataString - # Los permalinks nunca pueden ser privados - def private? - false - end + include Metadata::AlwaysPublicConcern private diff --git a/app/models/metadata_related_posts.rb b/app/models/metadata_related_posts.rb index b1ebfe4e..26277dc4 100644 --- a/app/models/metadata_related_posts.rb +++ b/app/models/metadata_related_posts.rb @@ -3,6 +3,10 @@ # Devuelve una lista de títulos y UUID de todos los posts del mismo # idioma que el actual, para usar con input-map.js class MetadataRelatedPosts < MetadataArray + # @todo ¿Deberíamos indexar algo de esto? + include Metadata::NonIndexableConcern + include Metadata::AlwaysPublicConcern + # Genera un Hash de { title (schema) => uuid } para usar en # options_for_select # @@ -15,19 +19,6 @@ class MetadataRelatedPosts < MetadataArray end.to_h end - # Las relaciones nunca son privadas - def private? - false - end - - def indexable? - false - end - - def indexable_values - posts.where(post_id: value).pluck(:title) - end - private # Obtiene todos los posts menos el actual y opcionalmente los filtra diff --git a/app/models/metadata_slug.rb b/app/models/metadata_slug.rb index b0fe8cec..fc810ac3 100644 --- a/app/models/metadata_slug.rb +++ b/app/models/metadata_slug.rb @@ -23,11 +23,17 @@ require 'jekyll/utils' # # TODO: Transliterar tildes? class MetadataSlug < MetadataTemplate + include Metadata::AlwaysPublicConcern + include Metadata::NonIndexableConcern + # Trae el slug desde el título si existe o una string al azar def default_value title ? Jekyll::Utils.slugify(title, mode: site.slugify_mode) : SecureRandom.uuid end + # Permite que Jekyll::Document se encargue de la generación del slug + # + # @return [String] def value self[:value] ||= document.data.fetch('slug', default_value) end diff --git a/app/models/metadata_string.rb b/app/models/metadata_string.rb index c1d888b1..c49a0abd 100644 --- a/app/models/metadata_string.rb +++ b/app/models/metadata_string.rb @@ -2,18 +2,22 @@ # Un campo de texto class MetadataString < MetadataTemplate + include Metadata::IndexableConcern + # Una string vacía + # + # @return [String] def default_value super || '' end - def indexable? - true && !private? - end - private # No se permite HTML en las strings + # + # @todo Escapar https://0xacab.org/sutty/sutty/-/issues/4101 + # @param :string [String] + # @return [String] def sanitize(string) return '' if string.blank? diff --git a/app/models/metadata_uuid.rb b/app/models/metadata_uuid.rb index a644154b..f4e28abd 100644 --- a/app/models/metadata_uuid.rb +++ b/app/models/metadata_uuid.rb @@ -2,11 +2,9 @@ # Asigna un identificador único al artículo class MetadataUuid < MetadataTemplate + include Metadata::AlwaysPublicConcern + def default_value SecureRandom.uuid end - - def private? - false - end end