mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-29 16:16:22 +00:00
Merge branch 'issue-7537' into 17.3.alpine.panel.sutty.nl
This commit is contained in:
commit
de16847727
18 changed files with 165 additions and 299 deletions
|
@ -55,9 +55,11 @@ class PostsController < ApplicationController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
authorize Post
|
authorize Post
|
||||||
@post = site.posts(lang: locale).build(layout: params[:layout])
|
|
||||||
|
|
||||||
breadcrumb I18n.t('loaf.breadcrumbs.posts.new', layout: @post.layout.humanized_name.downcase), ''
|
layout = site.layouts[params[:layout].to_sym]
|
||||||
|
@post = Post.build(locale: locale, layout: layout, site: site)
|
||||||
|
|
||||||
|
breadcrumb I18n.t('loaf.breadcrumbs.posts.new', layout: layout.humanized_name.downcase), ''
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -161,7 +163,7 @@ class PostsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def post
|
def post
|
||||||
@post ||= site.posts(lang: locale).find(params[:post_id] || params[:id])
|
@post ||= site.indexed_posts.find_by!(locale: locale, path: params[:post_id] || params[:id]).post
|
||||||
end
|
end
|
||||||
|
|
||||||
# Recuerda el nombre del servicio de subida de archivos
|
# Recuerda el nombre del servicio de subida de archivos
|
||||||
|
|
|
@ -34,15 +34,64 @@ class IndexedPost < ApplicationRecord
|
||||||
scope :in_category, ->(category) { where("front_matter->'categories' ? :category", category: category.to_s) }
|
scope :in_category, ->(category) { where("front_matter->'categories' ? :category", category: category.to_s) }
|
||||||
scope :by_usuarie, ->(usuarie) { where("front_matter->'usuaries' @> :usuarie::jsonb", usuarie: usuarie.to_s) }
|
scope :by_usuarie, ->(usuarie) { where("front_matter->'usuaries' @> :usuarie::jsonb", usuarie: usuarie.to_s) }
|
||||||
|
|
||||||
|
# Trae todos los valores únicos para un atributo
|
||||||
|
#
|
||||||
|
# @param :attribute [String,Symbol]
|
||||||
|
# @return [Array]
|
||||||
|
scope :everything_of, ->(attribute) do
|
||||||
|
where('front_matter ? :attribute', attribute: attribute)
|
||||||
|
.pluck(
|
||||||
|
Arel.sql(
|
||||||
|
ActiveRecord::Base::sanitize_sql(['front_matter -> :attribute', attribute: attribute])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.flatten.uniq
|
||||||
|
end
|
||||||
|
|
||||||
belongs_to :site
|
belongs_to :site
|
||||||
|
|
||||||
# Encuentra el post original
|
# La ubicación del Post en el disco
|
||||||
#
|
#
|
||||||
# @return [nil,Post]
|
# @return [String]
|
||||||
def post
|
def full_path
|
||||||
return if post_id.blank?
|
@full_path ||= File.join(site.path, "_#{locale}", "#{path}.markdown")
|
||||||
|
end
|
||||||
|
|
||||||
@post ||= site.posts(lang: locale).find(post_id, uuid: true)
|
# La colección
|
||||||
|
#
|
||||||
|
# @return [Jekyll::Collection]
|
||||||
|
def collection
|
||||||
|
site.collections[locale.to_s]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Obtiene el documento
|
||||||
|
#
|
||||||
|
# @return [Jekyll::Document]
|
||||||
|
def document
|
||||||
|
@document ||= Jekyll::Document.new(full_path, site: site.jekyll, collection: collection)
|
||||||
|
end
|
||||||
|
|
||||||
|
# El Post
|
||||||
|
#
|
||||||
|
# @todo Decidir qué pasa si el archivo ya no existe
|
||||||
|
# @return [Post]
|
||||||
|
def post
|
||||||
|
@post ||= Post.new(document: document, site: site, layout: schema)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Devuelve el esquema de datos
|
||||||
|
#
|
||||||
|
# @todo Renombrar
|
||||||
|
# @return [Layout]
|
||||||
|
def schema
|
||||||
|
site.layouts[layout.to_sym]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Existe físicamente?
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
def exist?
|
||||||
|
File.exist?(full_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Convertir locale a direccionario de PG
|
# Convertir locale a direccionario de PG
|
||||||
|
|
|
@ -77,13 +77,17 @@ class MetadataBelongsTo < MetadataRelatedPosts
|
||||||
end
|
end
|
||||||
|
|
||||||
# El Post relacionado con este artículo
|
# El Post relacionado con este artículo
|
||||||
|
#
|
||||||
|
# @return [Post,nil]
|
||||||
def belongs_to
|
def belongs_to
|
||||||
posts.find(value, uuid: true) if value.present?
|
posts.find_by(post_id: value)&.post if value.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
# El artículo relacionado anterior
|
# El artículo relacionado anterior
|
||||||
|
#
|
||||||
|
# @return [Post,nil]
|
||||||
def belonged_to
|
def belonged_to
|
||||||
posts.find(value_was, uuid: true) if value_was.present?
|
posts.find_by(post_id: value_was)&.post if value_was.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def related_posts?
|
def related_posts?
|
||||||
|
|
|
@ -7,17 +7,21 @@
|
||||||
# apuntando a un Post, que se mantiene actualizado como el actual.
|
# apuntando a un Post, que se mantiene actualizado como el actual.
|
||||||
class MetadataHasMany < MetadataRelatedPosts
|
class MetadataHasMany < MetadataRelatedPosts
|
||||||
# Todos los Post relacionados
|
# Todos los Post relacionados
|
||||||
|
#
|
||||||
|
# @return [Array<Post>]
|
||||||
def has_many
|
def has_many
|
||||||
return default_value if value.blank?
|
return default_value if value.blank?
|
||||||
|
|
||||||
posts.where(uuid: value)
|
posts.where(post_id: value).map(&:post)
|
||||||
end
|
end
|
||||||
|
|
||||||
# La relación anterior
|
# La relación anterior
|
||||||
|
#
|
||||||
|
# @return [Array<Post>]
|
||||||
def had_many
|
def had_many
|
||||||
return default_value if value_was.blank?
|
return default_value if value_was.blank?
|
||||||
|
|
||||||
posts.where(uuid: value_was)
|
posts.where(post_id: value_was).map(&:post)
|
||||||
end
|
end
|
||||||
|
|
||||||
def inverse?
|
def inverse?
|
||||||
|
|
|
@ -6,11 +6,16 @@ class MetadataLocales < MetadataHasAndBelongsToMany
|
||||||
#
|
#
|
||||||
# @return { lang: { title: uuid } }
|
# @return { lang: { title: uuid } }
|
||||||
def values
|
def values
|
||||||
@values ||= site.locales.map do |locale|
|
@values ||= other_locales.to_h do |other_locale|
|
||||||
[locale, posts.where(lang: locale).map do |post|
|
[
|
||||||
[title(post), post.uuid.value]
|
other_locale,
|
||||||
end.to_h]
|
posts.where(locale: other_locale).pluck(:title, :layout, :post_id).to_h do |row|
|
||||||
end.to_h
|
row.tap do |value|
|
||||||
|
value[0] = "#{value[0]} (#{site.layouts[value.delete_at(1)].humanized_name})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Siempre hay una relación inversa
|
# Siempre hay una relación inversa
|
||||||
|
@ -33,17 +38,13 @@ class MetadataLocales < MetadataHasAndBelongsToMany
|
||||||
#
|
#
|
||||||
# @return [Array]
|
# @return [Array]
|
||||||
def other_locales
|
def other_locales
|
||||||
site.locales.reject do |locale|
|
@other_locales ||= site.locales - [locale]
|
||||||
locale == post.lang.value.to_sym
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Obtiene todos los posts de los otros locales con el mismo layout
|
# Obtiene todos los posts de los otros locales con el mismo layout
|
||||||
#
|
#
|
||||||
# @return [PostRelation]
|
# @return [IndexedPost::ActiveRecord_AssociationRelation]
|
||||||
def posts
|
def posts
|
||||||
other_locales.map do |locale|
|
site.indexed_posts(locale: other_locales).where(layout: post.layout.value).where.not(post_id: post.uuid.value)
|
||||||
site.posts(lang: locale).where(layout: post.layout.value)
|
|
||||||
end.reduce(&:concat) || PostRelation.new(site: site, lang: 'any')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ class MetadataOrder < MetadataTemplate
|
||||||
# El valor según la posición del post en la relación ordenada por
|
# 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
|
# fecha, a fecha más alta, posición más alta
|
||||||
def default_value
|
def default_value
|
||||||
super || site.posts(lang: lang).sort_by(:date).index(post)
|
super || (site.indexed_posts(locale: locale).first.order + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
def save
|
def save
|
||||||
|
|
|
@ -3,14 +3,16 @@
|
||||||
# Devuelve una lista de títulos y UUID de todos los posts del mismo
|
# Devuelve una lista de títulos y UUID de todos los posts del mismo
|
||||||
# idioma que el actual, para usar con input-map.js
|
# idioma que el actual, para usar con input-map.js
|
||||||
class MetadataRelatedPosts < MetadataArray
|
class MetadataRelatedPosts < MetadataArray
|
||||||
# Genera un Hash de { title | slug => uuid } y excluye el Post actual
|
# Genera un Hash de { title (schema) => uuid } para usar en
|
||||||
|
# options_for_select
|
||||||
|
#
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
def values
|
def values
|
||||||
@values ||= posts.map do |p|
|
@values ||= posts.pluck(:title, :created_at, :layout, :post_id).to_h do |row|
|
||||||
next if p.uuid.value == post.uuid.value
|
row.tap do |value|
|
||||||
|
value[0] = "#{value[0]} #{value.delete_at(1).strftime('%F')} (#{site.layouts[value.delete_at(1)].humanized_name})"
|
||||||
[title(p), p.uuid.value]
|
end
|
||||||
end.compact.to_h
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Las relaciones nunca son privadas
|
# Las relaciones nunca son privadas
|
||||||
|
@ -23,21 +25,21 @@ class MetadataRelatedPosts < MetadataArray
|
||||||
end
|
end
|
||||||
|
|
||||||
def indexable_values
|
def indexable_values
|
||||||
posts.where(uuid: value).map(&:title).map(&:value)
|
posts.where(post_id: value).pluck(:title)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Obtiene todos los posts y opcionalmente los filtra
|
# Obtiene todos los posts menos el actual y opcionalmente los filtra
|
||||||
|
#
|
||||||
|
# @return [IndexedPost::ActiveRecord_AssociationRelation]
|
||||||
def posts
|
def posts
|
||||||
site.posts(lang: lang).where(**filter)
|
site.indexed_posts.where(locale: locale).where.not(post_id: post.uuid.value).where(**filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
def title(post)
|
# Encuentra el filtro desde el esquema del atributo
|
||||||
"#{post&.title&.value || post&.slug&.value} #{post&.date&.value.strftime('%F')} (#{post.layout.humanized_name})"
|
#
|
||||||
end
|
# @return [Hash]
|
||||||
|
|
||||||
# Encuentra el filtro
|
|
||||||
def filter
|
def filter
|
||||||
layout.metadata.dig(name, 'filter')&.to_h&.symbolize_keys || {}
|
layout.metadata.dig(name, 'filter')&.to_h&.symbolize_keys || {}
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,20 +62,28 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
|
||||||
end
|
end
|
||||||
|
|
||||||
# Trae el idioma actual del sitio o del panel
|
# Trae el idioma actual del sitio o del panel
|
||||||
|
#
|
||||||
|
# @deprecated Empezar a usar locale
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def lang
|
def lang
|
||||||
@lang ||= post&.lang&.value || I18n.locale
|
@lang ||= post&.lang&.value || I18n.locale.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
# El valor por defecto
|
alias_method :locale, :lang
|
||||||
|
|
||||||
|
# El valor por defecto desde el esquema de datos
|
||||||
|
#
|
||||||
|
# @return [any]
|
||||||
def default_value
|
def default_value
|
||||||
layout.metadata.dig(name, 'default', lang.to_s)
|
layout.metadata.dig(name, 'default', lang)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Valores posibles, busca todos los valores actuales en otros
|
# Valores posibles, busca todos los valores actuales en otros
|
||||||
# artículos del mismo sitio
|
# artículos del mismo sitio
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
def values
|
def values
|
||||||
site.everything_of(name, lang: lang)
|
site.indexed_posts.everything_of(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Valor actual o por defecto. Al memoizarlo podemos modificarlo
|
# Valor actual o por defecto. Al memoizarlo podemos modificarlo
|
||||||
|
|
|
@ -35,14 +35,39 @@ class Post
|
||||||
|
|
||||||
:post
|
:post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Genera un Post nuevo
|
||||||
|
#
|
||||||
|
# @params :site [Site]
|
||||||
|
# @params :locale [String, Symbol]
|
||||||
|
# @params :document [Jekyll::Document]
|
||||||
|
# @params :layout [String,Symbol]
|
||||||
|
# @return [Post]
|
||||||
|
def build(**args)
|
||||||
|
args[:document] ||=
|
||||||
|
begin
|
||||||
|
site = args[:site]
|
||||||
|
collection = site.collections[args[:locale].to_s]
|
||||||
|
|
||||||
|
Jekyll::Document.new('', site: site.jekyll, collection: collection).tap do |doc|
|
||||||
|
doc.data['date'] = Date.today.to_time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Post.new(**args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [IndexedPost,nil]
|
||||||
|
def indexed_post
|
||||||
|
site.indexed_posts.find_by(locale: lang.value, path: id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Redefinir el inicializador de OpenStruct
|
# Redefinir el inicializador de OpenStruct
|
||||||
#
|
#
|
||||||
# @param site: [Site] el sitio en Sutty
|
# @param :site [Site] el sitio en Sutty
|
||||||
# @param document: [Jekyll::Document] el documento leído por Jekyll
|
# @param :document [Jekyll::Document] el documento leído por Jekyll
|
||||||
# @param layout: [Layout] la plantilla
|
# @param :layout [Layout] la plantilla
|
||||||
#
|
|
||||||
def initialize(**args)
|
def initialize(**args)
|
||||||
default_attributes_missing(**args)
|
default_attributes_missing(**args)
|
||||||
|
|
||||||
|
@ -291,8 +316,6 @@ class Post
|
||||||
def destroy
|
def destroy
|
||||||
run_callbacks :destroy do
|
run_callbacks :destroy do
|
||||||
FileUtils.rm_f path.absolute
|
FileUtils.rm_f path.absolute
|
||||||
|
|
||||||
site.delete_post self
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias destroy! destroy
|
alias destroy! destroy
|
||||||
|
|
|
@ -40,16 +40,19 @@ class Post
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Los metadatos que se almacenan como objetos JSON. Empezamos con
|
# Los metadatos que se almacenan como objetos JSON.
|
||||||
# las categorías porque se usan para filtrar en el listado de
|
|
||||||
# artículos.
|
|
||||||
#
|
#
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
def indexable_front_matter
|
def indexable_front_matter
|
||||||
{}.tap do |ifm|
|
{}.tap do |ifm|
|
||||||
ifm[:usuaries] = usuaries.map(&:id)
|
ifm[:usuaries] = usuaries.map(&:id)
|
||||||
ifm[:draft] = attribute?(:draft) ? draft.value : false
|
ifm[:draft] = attribute?(:draft) ? draft.value : false
|
||||||
ifm[:categories] = categories.indexable_values if attribute? :categories
|
|
||||||
|
indexable_attributes.select do |attr|
|
||||||
|
self[attr].front_matter?
|
||||||
|
end.each do |attr|
|
||||||
|
ifm[attr] = self[attr].indexable_values
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# La relación de un sitio con sus artículos, esto nos permite generar
|
|
||||||
# artículos como si estuviésemos usando ActiveRecord.
|
|
||||||
class PostRelation < Array
|
|
||||||
# No necesitamos cambiar el sitio
|
|
||||||
attr_reader :site, :lang
|
|
||||||
|
|
||||||
def initialize(site:, lang:)
|
|
||||||
@site = site
|
|
||||||
@lang = lang
|
|
||||||
# Proseguimos la inicialización sin valores por defecto
|
|
||||||
super()
|
|
||||||
end
|
|
||||||
|
|
||||||
# Genera un artículo nuevo con los parámetros que le pasemos y lo suma
|
|
||||||
# al array
|
|
||||||
def build(**args)
|
|
||||||
args[:lang] = lang
|
|
||||||
args[:document] ||= build_document(collection: args[:lang])
|
|
||||||
args[:layout] = build_layout(args[:layout])
|
|
||||||
|
|
||||||
post = Post.new(site: site, **args)
|
|
||||||
|
|
||||||
self << post
|
|
||||||
post
|
|
||||||
end
|
|
||||||
|
|
||||||
def create(**args)
|
|
||||||
post = build(**args)
|
|
||||||
post.save
|
|
||||||
post
|
|
||||||
end
|
|
||||||
|
|
||||||
alias sort_by_generic sort_by
|
|
||||||
alias sort_by_generic! sort_by!
|
|
||||||
|
|
||||||
# Permite ordenar los artículos por sus atributos
|
|
||||||
#
|
|
||||||
# XXX: Prestar atención cuando estamos mezclando artículos con
|
|
||||||
# diferentes tipos de atributos.
|
|
||||||
def sort_by(*attrs)
|
|
||||||
sort_by_generic do |post|
|
|
||||||
attrs.map do |attr|
|
|
||||||
# TODO: detectar el tipo de atributo faltante y obtener el valor
|
|
||||||
# por defecto para hacer la comparación
|
|
||||||
if post.attributes.include? attr
|
|
||||||
post.public_send(attr).value
|
|
||||||
else
|
|
||||||
0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sort_by!(*attrs)
|
|
||||||
replace sort_by(*attrs)
|
|
||||||
end
|
|
||||||
|
|
||||||
alias find_generic find
|
|
||||||
|
|
||||||
# Encontrar un post por su UUID
|
|
||||||
def find(id, uuid: false)
|
|
||||||
find_generic do |p|
|
|
||||||
if uuid
|
|
||||||
p.uuid.value == id
|
|
||||||
else
|
|
||||||
p.id == id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Encuentra el primer post por el valor de los atributos
|
|
||||||
#
|
|
||||||
# @param [Hash]
|
|
||||||
# @return [Post]
|
|
||||||
def find_by(**args)
|
|
||||||
find_generic do |post|
|
|
||||||
args.map do |attr, value|
|
|
||||||
post.attribute?(attr) &&
|
|
||||||
post.public_send(attr).value == value
|
|
||||||
end.all?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Encuentra todos los Post que cumplan las condiciones
|
|
||||||
#
|
|
||||||
# TODO: Implementar caché
|
|
||||||
#
|
|
||||||
# @param [Hash] Mapa de atributo => valor. Valor puede ser un Array
|
|
||||||
# de valores
|
|
||||||
# @return [PostRelation]
|
|
||||||
def where(**args)
|
|
||||||
return self if args.empty?
|
|
||||||
|
|
||||||
begin
|
|
||||||
PostRelation.new(site: site, lang: lang).concat(select do |post|
|
|
||||||
result = args.map do |attr, value|
|
|
||||||
next unless post.attribute?(attr)
|
|
||||||
|
|
||||||
attribute = post[attr]
|
|
||||||
|
|
||||||
# TODO: Si el valor del atributo también es un Array deberíamos
|
|
||||||
# cruzar ambas.
|
|
||||||
case value
|
|
||||||
when Array then value.include? attribute.value
|
|
||||||
else
|
|
||||||
case attribute.value
|
|
||||||
when Array then attribute.value.include? value
|
|
||||||
else attribute.value == value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.compact
|
|
||||||
|
|
||||||
# Un Array vacío devuelve true para all?
|
|
||||||
result.present? && result.all?
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Como Array#select devolviendo una relación
|
|
||||||
#
|
|
||||||
# @return [PostRelation]
|
|
||||||
alias array_select select
|
|
||||||
def select(&block)
|
|
||||||
PostRelation.new(site: site, lang: lang).concat array_select(&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Intenta guardar todos y devuelve true si pudo
|
|
||||||
def save_all(validate: true)
|
|
||||||
map do |post|
|
|
||||||
post.save(validate: validate)
|
|
||||||
end.all?
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def build_layout(layout = nil)
|
|
||||||
return layout if layout.is_a? Layout
|
|
||||||
|
|
||||||
site.layouts[layout&.to_sym || :post]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Devuelve una colección Jekyll que hace pasar el documento
|
|
||||||
def build_collection(label:)
|
|
||||||
Jekyll::Collection.new(site.jekyll, label.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Un documento borrador con algunas propiedades por defecto
|
|
||||||
def build_document(collection:)
|
|
||||||
col = build_collection(label: collection)
|
|
||||||
doc = Jekyll::Document.new('', site: site.jekyll, collection: col)
|
|
||||||
doc.data['date'] = Date.today.to_time
|
|
||||||
doc
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -216,17 +216,10 @@ class Site < ApplicationRecord
|
||||||
jekyll.data
|
jekyll.data
|
||||||
end
|
end
|
||||||
|
|
||||||
# Traer las colecciones. Todos los artículos van a estar dentro de
|
# Trae las colecciones desde el sitio, sin leer su contenido
|
||||||
# colecciones.
|
#
|
||||||
|
# @return [Hash]
|
||||||
def collections
|
def collections
|
||||||
unless @read
|
|
||||||
Site.one_at_a_time.synchronize do
|
|
||||||
jekyll.reader.read_collections
|
|
||||||
end
|
|
||||||
|
|
||||||
@read = true
|
|
||||||
end
|
|
||||||
|
|
||||||
jekyll.collections
|
jekyll.collections
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -235,55 +228,6 @@ class Site < ApplicationRecord
|
||||||
@config ||= Site::Config.new(self)
|
@config ||= Site::Config.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Los posts en el idioma actual o en uno en particular
|
|
||||||
#
|
|
||||||
# @param lang: [String|Symbol] traer los artículos de este idioma
|
|
||||||
def posts(lang: nil)
|
|
||||||
# Traemos los posts del idioma actual por defecto o el que haya
|
|
||||||
lang ||= locales.include?(I18n.locale) ? I18n.locale : default_locale
|
|
||||||
lang = lang.to_sym
|
|
||||||
|
|
||||||
# Crea un Struct dinámico con los valores de los locales, si
|
|
||||||
# llegamos a pasar un idioma que no existe vamos a tener una
|
|
||||||
# excepción NoMethodError
|
|
||||||
@posts ||= Struct.new(*locales).new
|
|
||||||
|
|
||||||
return @posts[lang] unless @posts[lang].blank?
|
|
||||||
|
|
||||||
@posts[lang] = PostRelation.new site: self, lang: lang
|
|
||||||
|
|
||||||
# No fallar si no existe colección para este idioma
|
|
||||||
# XXX: queremos fallar silenciosamente?
|
|
||||||
(collections[lang.to_s]&.docs || []).each do |doc|
|
|
||||||
layout = layouts[Post.find_layout(doc.path)]
|
|
||||||
|
|
||||||
@posts[lang].build(document: doc, layout: layout, lang: lang)
|
|
||||||
rescue TypeError => e
|
|
||||||
ExceptionNotifier.notify_exception(e, data: { site: name, site_id: id, path: doc.path })
|
|
||||||
end
|
|
||||||
|
|
||||||
@posts[lang]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Todos los Post del sitio para poder buscar en todos.
|
|
||||||
#
|
|
||||||
# @return PostRelation
|
|
||||||
def docs
|
|
||||||
@docs ||= PostRelation.new(site: self, lang: :docs).push(locales.flat_map do |locale|
|
|
||||||
posts(lang: locale)
|
|
||||||
end).flatten!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Elimina un artículo de la colección
|
|
||||||
def delete_post(post)
|
|
||||||
lang = post.lang.value
|
|
||||||
|
|
||||||
collections[lang.to_s].docs.delete(post.document) &&
|
|
||||||
posts(lang: lang).delete(post)
|
|
||||||
|
|
||||||
post
|
|
||||||
end
|
|
||||||
|
|
||||||
# Obtiene todas las plantillas de artículos
|
# Obtiene todas las plantillas de artículos
|
||||||
#
|
#
|
||||||
# @return [Hash] { post: Layout }
|
# @return [Hash] { post: Layout }
|
||||||
|
@ -320,24 +264,6 @@ class Site < ApplicationRecord
|
||||||
jekyll.reader.read_layouts
|
jekyll.reader.read_layouts
|
||||||
end
|
end
|
||||||
|
|
||||||
# Trae todos los valores disponibles para un campo
|
|
||||||
#
|
|
||||||
# TODO: Traer recursivamente, si el campo contiene Hash
|
|
||||||
#
|
|
||||||
# TODO: Mover a PostRelation#pluck
|
|
||||||
#
|
|
||||||
# @param attr [Symbol|String] El atributo a buscar
|
|
||||||
# @return Array
|
|
||||||
def everything_of(attr, lang: nil)
|
|
||||||
Rails.cache.fetch("#{cache_key_with_version}/everything_of/#{lang}/#{attr}", expires_in: 1.hour) do
|
|
||||||
attr = attr.to_sym
|
|
||||||
|
|
||||||
posts(lang: lang).flat_map do |p|
|
|
||||||
p[attr].value if p.attribute? attr
|
|
||||||
end.uniq.compact
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Poner en la cola de compilación
|
# Poner en la cola de compilación
|
||||||
def enqueue!
|
def enqueue!
|
||||||
update(status: 'enqueued') if waiting?
|
update(status: 'enqueued') if waiting?
|
||||||
|
@ -452,7 +378,6 @@ class Site < ApplicationRecord
|
||||||
@incompatible_layouts = nil
|
@incompatible_layouts = nil
|
||||||
@jekyll = nil
|
@jekyll = nil
|
||||||
@config = nil
|
@config = nil
|
||||||
@posts = nil
|
|
||||||
@docs = nil
|
@docs = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
||||||
#
|
#
|
||||||
# @return Post
|
# @return Post
|
||||||
def create
|
def create
|
||||||
self.post = site.posts(lang: locale)
|
self.post = Post.build(site: site, locale: locale, layout: layout)
|
||||||
.build(layout: layout)
|
|
||||||
post.usuaries << usuarie
|
post.usuaries << usuarie
|
||||||
params[:post][:draft] = true if site.invitade? usuarie
|
params[:post][:draft] = true if site.invitade? usuarie
|
||||||
|
|
||||||
|
@ -29,8 +28,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
||||||
def create_anonymous
|
def create_anonymous
|
||||||
# XXX: Confiamos en el parámetro de idioma porque estamos
|
# XXX: Confiamos en el parámetro de idioma porque estamos
|
||||||
# verificándolos en Site#posts
|
# verificándolos en Site#posts
|
||||||
self.post = site.posts(lang: locale)
|
self.post = Post.build(site: site, locale: locale, layout: layouts)
|
||||||
.build(layout: layout)
|
|
||||||
# Los artículos anónimos siempre son borradores
|
# Los artículos anónimos siempre son borradores
|
||||||
params[:draft] = true
|
params[:draft] = true
|
||||||
|
|
||||||
|
@ -74,7 +72,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
||||||
# { uuid => 2, uuid => 1, uuid => 0 }
|
# { uuid => 2, uuid => 1, uuid => 0 }
|
||||||
def reorder
|
def reorder
|
||||||
reorder = params.require(:post).permit(reorder: {})&.dig(:reorder)&.transform_values(&:to_i)
|
reorder = params.require(:post).permit(reorder: {})&.dig(:reorder)&.transform_values(&:to_i)
|
||||||
posts = site.posts(lang: locale).where(uuid: reorder.keys)
|
posts = site.indexed_posts(locale: locale).where(post_id: reorder.keys).map(&:post)
|
||||||
|
|
||||||
files = posts.map do |post|
|
files = posts.map do |post|
|
||||||
next unless post.attribute? :order
|
next unless post.attribute? :order
|
||||||
|
@ -118,12 +116,16 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [Symbol]
|
||||||
def locale
|
def locale
|
||||||
params.dig(:post, :lang)&.to_sym || I18n.locale
|
params.dig(:post, :lang)&.to_sym || I18n.locale
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [Layout]
|
||||||
def layout
|
def layout
|
||||||
params.dig(:post, :layout) || params[:layout]
|
site.layouts[
|
||||||
|
(params.dig(:post, :layout) || params[:layout]).to_sym
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Actualiza los artículos relacionados según los métodos que los
|
# Actualiza los artículos relacionados según los métodos que los
|
||||||
|
|
|
@ -145,7 +145,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
|
||||||
return true if site.licencia.custom?
|
return true if site.licencia.custom?
|
||||||
|
|
||||||
with_all_locales do |locale|
|
with_all_locales do |locale|
|
||||||
post = site.posts(lang: locale).find_by(layout: 'license')
|
post = site.indexed_posts(locale: locale).find_by(layout: 'license')&.post
|
||||||
|
|
||||||
change_licencia(post: post) if post
|
change_licencia(post: post) if post
|
||||||
end.compact.map(&:valid?).all?
|
end.compact.map(&:valid?).all?
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
%td
|
%td
|
||||||
%ul{ dir: dir, lang: locale }
|
%ul{ dir: dir, lang: locale }
|
||||||
- metadata.value.each do |v|
|
- metadata.value.each do |v|
|
||||||
- p = site.posts(lang: post.lang.value).find(v, uuid: true)
|
- p = site.indexed_posts(locale: post.lang.value).find_by(post_id: v)&.post
|
||||||
-#
|
-#
|
||||||
XXX: Ignorar todos los posts no encontrados (ej: fueron
|
XXX: Ignorar todos los posts no encontrados (ej: fueron
|
||||||
borrados o el uuid cambió)
|
borrados o el uuid cambió)
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
post: post, attribute: attribute, metadata: metadata
|
post: post, attribute: attribute, metadata: metadata
|
||||||
|
|
||||||
- site.locales.each do |locale|
|
- site.locales.each do |locale|
|
||||||
- next if post.lang.value == locale
|
|
||||||
- locale_t = t("locales.#{locale}.name", default: locale.to_s.humanize)
|
- locale_t = t("locales.#{locale}.name", default: locale.to_s.humanize)
|
||||||
- value = metadata.value.find do |v|
|
- value = metadata.value.find do |v|
|
||||||
- metadata.values[locale].values.include? v
|
- metadata.values[locale].values.include? v
|
||||||
|
|
|
@ -125,8 +125,8 @@ module Jekyll
|
||||||
|
|
||||||
unless spec
|
unless spec
|
||||||
I18n.with_locale(locale) do
|
I18n.with_locale(locale) do
|
||||||
raise ArgumentError, 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 ArgumentError => e
|
rescue Jekyll::Errors::InvalidThemeName => e
|
||||||
ExceptionNotifier.notify_exception(e, data: { theme: name, site: File.basename(site.source) })
|
ExceptionNotifier.notify_exception(e, data: { theme: name, site: File.basename(site.source) })
|
||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
- name_en: 'Minima'
|
- name_en: 'Minima'
|
||||||
name_es: 'Mínima'
|
name_es: 'Mínima'
|
||||||
gem: 'sutty-minima'
|
gem: 'sutty-minima'
|
||||||
url: 'https://0xacab.org/sutty/jekyll/minima'
|
url: 'https://minima.sutty.nl/'
|
||||||
description_en: "Sutty Minima is based on [Minima](https://jekyll.github.io/minima/), a blog-focused theme for Jekyll."
|
description_en: "Sutty Minima is based on [Minima](https://jekyll.github.io/minima/), a blog-focused theme for Jekyll."
|
||||||
description_es: 'Sutty Mínima es una plantilla para blogs basada en [Mínima](https://jekyll.github.io/minima/).'
|
description_es: 'Sutty Mínima es una plantilla para blogs basada en [Mínima](https://jekyll.github.io/minima/).'
|
||||||
license: 'https://0xacab.org/sutty/jekyll/minima/-/blob/master/LICENSE.txt'
|
license: 'https://0xacab.org/sutty/jekyll/minima/-/blob/master/LICENSE.txt'
|
||||||
|
|
Loading…
Reference in a new issue