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

246 lines
5.9 KiB
Ruby
Raw Permalink Normal View History

2019-08-06 17:54:17 +00:00
# frozen_string_literal: true
# Representa la plantilla de un campo en los metadatos del artículo
#
# TODO: Validar el tipo de valor pasado a value= según el :type
2019-08-13 23:33:57 +00:00
#
2019-08-06 17:54:17 +00:00
MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
2019-08-08 18:28:23 +00:00
:value, :help, :required, :errors, :post,
2019-08-07 21:35:37 +00:00
:layout, keyword_init: true) do
# Determina si el campo es indexable
def indexable?
false
end
2021-02-24 20:37:13 +00:00
def inspect
"#<#{self.class} site=#{site.name.inspect} post=#{post.id.inspect} value=#{value.inspect}>"
end
2020-11-11 20:00:09 +00:00
# Queremos que los artículos nuevos siempre cacheen, si usamos el UUID
# siempre vamos a obtener un item nuevo.
def cache_key
2021-05-06 20:54:49 +00:00
return "#{layout.value}/#{name}" if post.new?
2020-11-11 20:00:09 +00:00
2021-05-06 20:54:49 +00:00
@cache_key ||= "post/#{post.uuid.value}/#{name}"
2020-11-11 20:00:09 +00:00
end
# Genera una versión de caché en base a la fecha de modificación del
# Post, el valor actual y los valores posibles, de forma que cualquier
# cambio permita renovar la caché.
#
# @return [String]
2020-11-11 20:00:09 +00:00
def cache_version
post.cache_version + value.hash.to_s + values.hash.to_s
2020-11-11 20:00:09 +00:00
end
# @return [String]
2020-11-11 20:00:09 +00:00
def cache_key_with_version
2021-05-06 20:54:49 +00:00
"#{cache_key}-#{cache_version}"
2020-11-11 20:00:09 +00:00
end
2021-02-11 19:45:03 +00:00
# XXX: Deberíamos sanitizar durante la asignación?
2020-10-04 01:32:52 +00:00
def value=(new_value)
@value_was = value
self[:value] = new_value
end
# Siempre obtener el valor actual y solo obtenerlo del documento una
# vez.
def value_was
return @value_was if instance_variable_defined? '@value_was'
@value_was = document_value
end
2020-10-04 01:32:52 +00:00
def changed?
value_was != value
2020-10-04 01:32:52 +00:00
end
2020-12-24 19:13:29 +00:00
# Obtiene el valor del JekyllDocument
def document_value
document.data[name.to_s]
end
# Trae el idioma actual del sitio o del panel
# @return [String]
def lang
@lang ||= post&.lang&.value || I18n.locale
end
2019-08-06 17:54:17 +00:00
# El valor por defecto
def default_value
layout.metadata.dig(name, 'default', lang.to_s)
2019-08-06 17:54:17 +00:00
end
# Valores posibles, busca todos los valores actuales en otros
# artículos del mismo sitio
def values
site.everything_of(name, lang: lang)
2019-08-06 17:54:17 +00:00
end
# Valor actual o por defecto. Al memoizarlo podemos modificarlo
# usando otros métodos que el de asignación.
2019-08-06 17:54:17 +00:00
def value
self[:value] ||= if (data = document_value).present?
private? ? decrypt(data) : data
2020-08-20 23:38:31 +00:00
else
default_value
2020-08-20 23:38:31 +00:00
end
end
# Detecta si el valor está vacío
def empty?
value.blank?
end
# Comprueba si el metadato es válido
def valid?
validate
end
def validate
self.errors = []
2020-07-02 14:26:00 +00:00
errors << I18n.t('metadata.cant_be_empty') unless can_be_empty?
errors.empty?
end
# Usa el valor por defecto para generar el formato de StrongParam
# necesario.
#
# @return [Symbol,Hash]
2019-08-13 23:33:57 +00:00
def to_param
case default_value
when Hash then { name => default_value.keys.map(&:to_sym) }
when Array then { name => [] }
else name
end
2019-08-13 23:33:57 +00:00
end
def to_s
value.to_s
end
2019-08-13 23:33:57 +00:00
# Decide si el metadato se coloca en el front_matter o no
def front_matter?
true
end
2019-08-16 23:25:07 +00:00
def array?
type == 'array'
end
2019-08-22 01:09:29 +00:00
# En caso de que algún campo necesite realizar acciones antes de ser
# guardado
def save
if !changed?
self[:value] = document_value if private?
return true
end
2021-02-11 19:45:03 +00:00
self[:value] = sanitize value
2020-08-20 23:38:31 +00:00
self[:value] = encrypt(value) if private?
2019-08-22 01:09:29 +00:00
true
end
2020-07-22 23:35:43 +00:00
def related_posts?
false
end
def related_methods
@related_methods ||= [].freeze
2020-07-22 23:35:43 +00:00
end
2020-08-20 23:38:31 +00:00
# Determina si el campo es privado y debería ser cifrado
def private?
2021-02-11 19:45:03 +00:00
layout.metadata.dig(name, 'private').present?
2020-08-20 23:38:31 +00:00
end
# Determina si el campo debería estar deshabilitado
def disabled?
layout.metadata.dig(name, 'disabled') || !writable?
end
# Determina si el campo es de solo lectura
#
# once => el campo solo se puede modificar si estaba vacío
def writable?
case layout.metadata.dig(name, 'writable')
when 'once' then value.blank?
else true
end
end
private
# Si es obligatorio no puede estar vacío
def can_be_empty?
true unless required && empty?
2019-08-06 17:54:17 +00:00
end
# No usamos sanitize_action_text_content porque espera un ActionText
#
# Ver ActionText::ContentHelper#sanitize_action_text_content
def sanitize(string)
return if string.nil?
return string unless string.is_a? String
sanitizer
.sanitize(string.tr("\r", '').unicode_normalize,
tags: allowed_tags,
attributes: allowed_attributes)
.strip
.html_safe
2020-11-16 16:09:04 +00:00
end
2020-11-14 21:02:30 +00:00
def sanitizer
@sanitizer ||= Rails::Html::Sanitizer.safe_list_sanitizer.new
end
def allowed_attributes
2021-03-26 13:27:50 +00:00
@allowed_attributes ||= %w[style href src alt controls data-align data-multimedia data-multimedia-inner id
name rel target referrerpolicy class colspan rowspan role data-turbo start type reversed].freeze
2020-11-14 21:02:30 +00:00
end
def allowed_tags
2022-04-02 14:34:12 +00:00
@allowed_tags ||= %w[strong em del u mark p h1 h2 h3 h4 h5 h6 ul ol li img iframe audio video div figure blockquote
figcaption a sub sup small table thead tbody tfoot tr th td br code].freeze
end
2020-08-20 23:38:31 +00:00
# Decifra el valor
#
# XXX: Otros tipos de valores necesitan implementar su propio método
# de decifrado (Array).
def decrypt(value)
return value if value.blank?
box.decrypt_str value.to_s
rescue Lockbox::DecryptionError => e
if value.to_s.include? ' '
value
else
ExceptionNotifier.notify_exception(e, data: { site: site.name, post: post.path.absolute, name: name })
2020-08-20 23:38:31 +00:00
I18n.t('lockbox.help.decryption_error')
end
2020-08-20 23:38:31 +00:00
end
# Cifra el valor.
#
# XXX: Otros tipos de valores necesitan implementar su propio método
# de cifrado (Array).
def encrypt(value)
box.encrypt value.to_s
end
# Genera una lockbox a partir de la llave privada del sitio
#
# @return [Lockbox]
def box
@box ||= Lockbox.new key: site.private_key, padding: true, encode: true
end
2019-08-06 17:54:17 +00:00
end