5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2025-03-14 20:18:18 +00:00

Merge branch 'issue-15068' into production.panel.sutty.nl

This commit is contained in:
f 2024-05-24 13:24:48 -03:00
commit 3a628028a1
No known key found for this signature in database
21 changed files with 109 additions and 75 deletions

View file

@ -2,7 +2,7 @@
# Helpers
module ApplicationHelper
BRACKETS = /[\[\]]/
BRACKETS = /[\[\]]/.freeze
# Devuelve el atributo name de un campo anidado en el formato que
# esperan los helpers *_field

View file

@ -19,8 +19,12 @@ class MetadataArray < MetadataTemplate
true && !private?
end
def titleize?
true
end
def to_s
value.join(', ')
value.select(&:present?).join(', ')
end
# Obtiene el valor desde el documento, convirtiéndolo a Array si no lo

View file

@ -13,6 +13,10 @@ class MetadataBelongsTo < MetadataRelatedPosts
''
end
def to_s
belongs_to.try(:title).try(:value).to_s
end
# Obtiene el valor desde el documento.
#
# @return [String]
@ -107,6 +111,6 @@ class MetadataBelongsTo < MetadataRelatedPosts
end
def sanitize(uuid)
uuid.to_s.gsub(/[^a-f0-9\-]/i, '')
uuid.to_s.gsub(/[^a-f0-9-]/i, '')
end
end

View file

@ -10,6 +10,10 @@ class MetadataPredefinedValue < MetadataString
@values ||= layout.dig(:metadata, name, 'values', I18n.locale.to_s)&.invert || {}
end
def to_s
values.invert[value].to_s
end
private
# Solo permite almacenar los valores predefinidos.

View file

@ -22,6 +22,10 @@ class MetadataRelatedPosts < MetadataArray
false
end
def titleize?
false
end
def indexable_values
posts.where(uuid: value).map(&:title).map(&:value)
end
@ -41,12 +45,12 @@ class MetadataRelatedPosts < MetadataArray
end
def title(post)
"#{post&.title&.value || post&.slug&.value} #{post&.date&.value.strftime('%F')} (#{post.layout.humanized_name})"
"#{post&.title&.value || post&.slug&.value} #{post&.date&.value&.strftime('%F')} (#{post.layout.humanized_name})"
end
def sanitize(uuid)
super(uuid.map do |u|
u.to_s.gsub(/[^a-f0-9\-]/i, '')
u.to_s.gsub(/[^a-f0-9-]/i, '')
end)
end
end

View file

@ -25,7 +25,7 @@ require 'jekyll/utils'
class MetadataSlug < MetadataTemplate
# 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
Jekyll::Utils.slugify(title || SecureRandom.uuid, mode: site.slugify_mode)
end
def value

View file

@ -11,6 +11,10 @@ class MetadataString < MetadataTemplate
true && !private?
end
def titleize?
true
end
private
# No se permite HTML en las strings

View file

@ -16,6 +16,11 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
false
end
# El valor puede ser parte de un título auto-generado
def titleize?
false
end
def inspect
"#<#{self.class} site=#{site.name.inspect} post=#{post.id.inspect} value=#{value.inspect}>"
end

View file

@ -1,4 +1,8 @@
# frozen_string_literal: true
# Un campo de texto largo
class MetadataText < MetadataString; end
class MetadataText < MetadataString
def titleize?
false
end
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
# El título es obligatorio para todos los Post, si el esquema no lo
# incluye, tenemos que poder generar un valor legible por humanes.
class MetadataTitle < MetadataString
# Obtener todos los valores de texto del artículo y generar un título
# en base a eso.
#
# @return [String]
def default_value
@default_value ||=
post.attributes.select do |attr|
post[attr].titleize?
end.map do |attr|
post[attr].to_s
end.compact.join(' ').strip.squeeze(' ')
end
end

View file

@ -12,9 +12,21 @@ class Post
DEFAULT_ATTRIBUTES = %i[site document layout].freeze
# Otros atributos que no vienen en los metadatos
PRIVATE_ATTRIBUTES = %i[path slug attributes errors].freeze
PUBLIC_ATTRIBUTES = %i[lang date uuid created_at].freeze
PUBLIC_ATTRIBUTES = %i[title lang date uuid created_at].freeze
ALIASED_ATTRIBUTES = %i[locale].freeze
ATTR_SUFFIXES = %w[? =].freeze
ATTRIBUTE_DEFINITIONS = {
'title' => { 'type' => 'title', 'required' => true },
'lang' => { 'type' => 'lang', 'required' => true },
'date' => { 'type' => 'document_date', 'required' => true },
'uuid' => { 'type' => 'uuid', 'required' => true },
'created_at' => { 'type' => 'created_at', 'required' => true },
'slug' => { 'type' => 'slug', 'required' => true },
'path' => { 'type' => 'path', 'required' => true },
'locale' => { 'alias' => 'lang' }
}.freeze
class PostError < StandardError; end
class UnknownAttributeError < PostError; end
@ -53,10 +65,12 @@ class Post
@layout = args[:layout]
@site = args[:site]
@document = args[:document]
@attributes = layout.attributes + PUBLIC_ATTRIBUTES
@attributes = (layout.attributes + PUBLIC_ATTRIBUTES).uniq
@errors = {}
@metadata = {}
layout.metadata = ATTRIBUTE_DEFINITIONS.merge(layout.metadata).with_indifferent_access
# Leer el documento si existe
# @todo Asignar todos los valores a self[:value] luego de leer
document&.read! unless new?
@ -131,6 +145,7 @@ class Post
src = element.attributes['src']
next unless src&.value&.start_with? 'public/'
file = MetadataFile.new(site: site, post: self, document: document, layout: layout)
file.value['path'] = src.value
@ -192,13 +207,13 @@ class Post
def method_missing(name, *_args)
# Limpiar el nombre del atributo, para que todos los ayudantes
# reciban el método en limpio
unless attribute? name
raise NoMethodError, I18n.t('exceptions.post.no_method', method: name)
end
raise NoMethodError, I18n.t('exceptions.post.no_method', method: name) unless attribute? name
define_singleton_method(name) do
template = layout.metadata[name.to_s]
return public_send(template['alias'].to_sym) if template.key?('alias')
@metadata[name] ||=
MetadataFactory.build(document: document,
post: self,
@ -214,43 +229,6 @@ class Post
public_send name
end
# TODO: Mover a method_missing
def slug
@metadata[:slug] ||= MetadataSlug.new(document: document, site: site, layout: layout, name: :slug, type: :slug,
post: self, required: true)
end
# TODO: Mover a method_missing
def date
@metadata[:date] ||= MetadataDocumentDate.new(document: document, site: site, layout: layout, name: :date,
type: :document_date, post: self, required: true)
end
# TODO: Mover a method_missing
def path
@metadata[:path] ||= MetadataPath.new(document: document, site: site, layout: layout, name: :path, type: :path,
post: self, required: true)
end
# TODO: Mover a method_missing
def lang
@metadata[:lang] ||= MetadataLang.new(document: document, site: site, layout: layout, name: :lang, type: :lang,
post: self, required: true)
end
alias locale lang
# TODO: Mover a method_missing
def uuid
@metadata[:uuid] ||= MetadataUuid.new(document: document, site: site, layout: layout, name: :uuid, type: :uuid,
post: self, required: true)
end
# La fecha de creación inmodificable del post
def created_at
@metadata[:created_at] ||= MetadataCreatedAt.new(document: document, site: site, layout: layout, name: :created_at, type: :created_at, post: self, required: true)
end
# Devuelve los strong params para el layout.
#
# XXX: Nos gustaría no tener que instanciar Metadata acá, pero depende
@ -427,7 +405,8 @@ class Post
def attribute?(mid)
included = DEFAULT_ATTRIBUTES.include?(mid) ||
PRIVATE_ATTRIBUTES.include?(mid) ||
PUBLIC_ATTRIBUTES.include?(mid)
PUBLIC_ATTRIBUTES.include?(mid) ||
ALIASED_ATTRIBUTES.include?(mid)
included = attributes.include? mid if !included && singleton_class.method_defined?(:attributes)

View file

@ -185,10 +185,10 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
# Si les usuaries modifican o crean una licencia, considerarla
# personalizada en el panel.
def update_site_license!
if site.usuarie?(usuarie) && post.layout.name == :license && !site.licencia.custom?
return unless site.usuarie?(usuarie) && post.layout.name == :license && !site.licencia.custom?
site.update licencia: Licencia.find_by_icons('custom')
end
end
# Encuentra todos los posts anidados y los crea o modifica
def create_nested_posts!(post, params)

View file

@ -25,7 +25,6 @@
= render 'layouts/breadcrumb'
= render 'layouts/flash'
= yield(:post_form)
= yield
- if flash[:js]

View file

@ -9,6 +9,7 @@
- next if attribute == :date
- next if attribute == :draft
- next if attribute == inverse
- metadata = post[attribute]
- cache [post, metadata, I18n.locale] do

View file

@ -33,8 +33,7 @@
- dir = t("locales.#{@locale}.dir")
-# Comienza el formulario
= form_tag url, method: method, class: 'form post ' + extra_class, multipart: true do
= form_tag url, method: method, class: "form post #{extra_class}", multipart: true do
-# Botones de guardado
= render 'posts/submit', site: site, post: post
@ -45,3 +44,6 @@
-# Botones de guardado
= render 'posts/submit', site: site, post: post
-# Formularios usados por los modales
= yield(:post_form)

View file

@ -40,3 +40,5 @@
-# Dibuja cada atributo
= render 'posts/attributes', site: site, post: post, dir: dir, base: base, locale: locale, except: except
= yield(:post_form)

View file

@ -0,0 +1,3 @@
%tr{ id: attribute }
%th= post_label_t(attribute, post: post)
%td{ dir: dir, lang: locale }= metadata.value

View file

View file

@ -126,7 +126,8 @@ module Jekyll
unless spec
I18n.with_locale(locale) do
raise Jekyll::Errors::InvalidThemeName, I18n.t('activerecord.errors.models.site.attributes.design_id.missing_gem', theme: name)
raise Jekyll::Errors::InvalidThemeName,
I18n.t('activerecord.errors.models.site.attributes.design_id.missing_gem', theme: name)
rescue Jekyll::Errors::InvalidThemeName => e
ExceptionNotifier.notify_exception(e, data: { theme: name, site: File.basename(site.source) })
raise