mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-15 06:41:42 +00:00
ver artículos
cada metadato tiene su plantilla de solo lectura para generar una tabla de metadatos dinámica :D
This commit is contained in:
parent
9469cb41e6
commit
1c96e0b0ff
18 changed files with 116 additions and 126 deletions
|
@ -141,7 +141,7 @@ GEM
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
globalid (0.4.2)
|
globalid (0.4.2)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
haml (5.0.4)
|
haml (5.1.2)
|
||||||
temple (>= 0.8.0)
|
temple (>= 0.8.0)
|
||||||
tilt
|
tilt
|
||||||
haml-lint (0.999.999)
|
haml-lint (0.999.999)
|
||||||
|
@ -277,7 +277,7 @@ GEM
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.19.0, < 2.0)
|
thor (>= 0.19.0, < 2.0)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
rake (12.3.2)
|
rake (12.3.3)
|
||||||
rb-fsevent (0.10.3)
|
rb-fsevent (0.10.3)
|
||||||
rb-inotify (0.10.0)
|
rb-inotify (0.10.0)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
|
@ -306,7 +306,7 @@ GEM
|
||||||
actionpack (>= 5.0)
|
actionpack (>= 5.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
rouge (3.3.0)
|
rouge (3.3.0)
|
||||||
rubocop (0.72.0)
|
rubocop (0.74.0)
|
||||||
jaro_winkler (~> 1.5.1)
|
jaro_winkler (~> 1.5.1)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 2.6)
|
parser (>= 2.6)
|
||||||
|
|
|
@ -40,29 +40,8 @@ class ApplicationController < ActionController::Base
|
||||||
site
|
site
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_post(site)
|
|
||||||
id = params[:post_id] || params[:id]
|
|
||||||
lang = find_lang(site)
|
|
||||||
posts = site.posts_for(lang)
|
|
||||||
|
|
||||||
posts.find do |p|
|
|
||||||
p.id == id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_lang(site)
|
|
||||||
params.fetch(:lang, site.default_lang)
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_template(site)
|
|
||||||
id = params[:template_id] || params[:template] || params.dig(:post, :layout)
|
|
||||||
site.templates.find do |t|
|
|
||||||
t.id == id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_locale
|
def set_locale
|
||||||
I18n.locale = current_usuarie.lang
|
I18n.locale = current_usuarie.lang if current_usuarie
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
|
@ -8,8 +8,6 @@ class PostsController < ApplicationController
|
||||||
def index
|
def index
|
||||||
authorize Post
|
authorize Post
|
||||||
@site = find_site
|
@site = find_site
|
||||||
# TODO: por qué no lo está leyendo @site.posts?
|
|
||||||
@site.read
|
|
||||||
@category = session[:category] = params.dig(:category)
|
@category = session[:category] = params.dig(:category)
|
||||||
# TODO: Aplicar policy_scope
|
# TODO: Aplicar policy_scope
|
||||||
@posts = @site.posts(lang: I18n.locale)
|
@posts = @site.posts(lang: I18n.locale)
|
||||||
|
@ -26,8 +24,7 @@ class PostsController < ApplicationController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@site = find_site
|
@site = find_site
|
||||||
@lang = find_lang(@site)
|
@post = @site.posts.find params[:id]
|
||||||
@post = find_post(@site)
|
|
||||||
authorize @post
|
authorize @post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -54,8 +51,7 @@ class PostsController < ApplicationController
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
@site = find_site
|
@site = find_site
|
||||||
@lang = find_lang(@site)
|
@post = @site.posts.find params[:id]
|
||||||
@post = find_post(@site)
|
|
||||||
|
|
||||||
authorize @post
|
authorize @post
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,8 +27,21 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Devuelve todas las etiquetas HTML que queremos mantener
|
||||||
|
def all_html_tags
|
||||||
|
%w[h1 h2 h3 h4 h5 h6 p a ul ol li table tr td th tbody thead
|
||||||
|
tfoot em strong sup blockquote cite pre section article]
|
||||||
|
end
|
||||||
|
|
||||||
def sanitize_markdown(text, options = {})
|
def sanitize_markdown(text, options = {})
|
||||||
sanitize(CommonMarker.render_html(text), options)
|
options.merge!(attributes: %w[id href alt class])
|
||||||
|
|
||||||
|
document = CommonMarker
|
||||||
|
.render_doc(text,
|
||||||
|
%i[FOOTNOTES SMART],
|
||||||
|
%i[table strikethrough autolink])
|
||||||
|
|
||||||
|
sanitize(document.to_html, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def invalid?(model, field)
|
def invalid?(model, field)
|
||||||
|
|
|
@ -25,6 +25,12 @@ class PostRelation < Array
|
||||||
post
|
post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find(id)
|
||||||
|
super() do |p|
|
||||||
|
p.id == id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Intenta guardar todos y devuelve true si pudo
|
# Intenta guardar todos y devuelve true si pudo
|
||||||
def save_all
|
def save_all
|
||||||
map(&:save).all?
|
map(&:save).all?
|
||||||
|
|
|
@ -108,6 +108,9 @@ class Site < ApplicationRecord
|
||||||
|
|
||||||
# Lee el sitio y todos los artículos
|
# Lee el sitio y todos los artículos
|
||||||
def read
|
def read
|
||||||
|
# No hacer nada si ya se leyó antes
|
||||||
|
return unless @jekyll.layouts.empty?
|
||||||
|
|
||||||
@jekyll.read
|
@jekyll.read
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -115,7 +118,7 @@ class Site < ApplicationRecord
|
||||||
#
|
#
|
||||||
# XXX: Leer directamente sin pasar por Jekyll
|
# XXX: Leer directamente sin pasar por Jekyll
|
||||||
def data
|
def data
|
||||||
read if @jekyll.data.empty?
|
read
|
||||||
|
|
||||||
@jekyll.data
|
@jekyll.data
|
||||||
end
|
end
|
||||||
|
@ -123,7 +126,7 @@ class Site < ApplicationRecord
|
||||||
# Traer las colecciones. Todos los artículos van a estar dentro de
|
# Traer las colecciones. Todos los artículos van a estar dentro de
|
||||||
# colecciones.
|
# colecciones.
|
||||||
def collections
|
def collections
|
||||||
read if @jekyll.collections.empty?
|
read
|
||||||
|
|
||||||
@jekyll.collections
|
@jekyll.collections
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,11 +12,18 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
||||||
# TODO: No podemos pasar los post_params a build aun porque para
|
# TODO: No podemos pasar los post_params a build aun porque para
|
||||||
# saber los parámetros tenemos que haber instanciado el post
|
# saber los parámetros tenemos que haber instanciado el post
|
||||||
# primero.
|
# primero.
|
||||||
post.update_attributes(post_params) &&
|
post.update_attributes(post_params) && commit(action: :created)
|
||||||
site.repository.commit(file: post.path.absolute,
|
|
||||||
usuarie: usuarie,
|
# Devolver el post aunque no se haya salvado para poder rescatar los
|
||||||
message: I18n.t('post_service.created',
|
# errores
|
||||||
title: post.title.value))
|
post
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
# TODO: No podemos pasar los post_params a build aun porque para
|
||||||
|
# saber los parámetros tenemos que haber instanciado el post
|
||||||
|
# primero.
|
||||||
|
post.update_attributes(post_params) && commit(action: :updated)
|
||||||
|
|
||||||
# Devolver el post aunque no se haya salvado para poder rescatar los
|
# Devolver el post aunque no se haya salvado para poder rescatar los
|
||||||
# errores
|
# errores
|
||||||
|
@ -25,6 +32,13 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def commit(action:)
|
||||||
|
site.repository.commit(file: post.path.absolute,
|
||||||
|
usuarie: usuarie,
|
||||||
|
message: I18n.t("post_service.#{action}",
|
||||||
|
title: post.title.value))
|
||||||
|
end
|
||||||
|
|
||||||
# Solo permitir cambiar estos atributos de cada articulo
|
# Solo permitir cambiar estos atributos de cada articulo
|
||||||
def post_params
|
def post_params
|
||||||
params.require(:post).permit(post.params)
|
params.require(:post).permit(post.params)
|
||||||
|
|
8
app/views/posts/attribute_ro/_array.haml
Normal file
8
app/views/posts/attribute_ro/_array.haml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
%tr{ id: attribute }
|
||||||
|
%th= post_label_t(attribute, post: post)
|
||||||
|
%td
|
||||||
|
- if metadata.value.respond_to? :each
|
||||||
|
- metadata.value.each do |v|
|
||||||
|
%span.badge.badge-primary= v
|
||||||
|
- else
|
||||||
|
%span.badge.badge-primary= metadata.value
|
3
app/views/posts/attribute_ro/_content.haml
Normal file
3
app/views/posts/attribute_ro/_content.haml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
%tr{ id: attribute }
|
||||||
|
%th= post_label_t(attribute, post: post)
|
||||||
|
%td= sanitize_markdown metadata.value, tags: tags
|
3
app/views/posts/attribute_ro/_document_date.haml
Normal file
3
app/views/posts/attribute_ro/_document_date.haml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
%tr{ id: attribute }
|
||||||
|
%th= post_label_t(attribute, post: post)
|
||||||
|
%td= l metadata.value.to_date
|
6
app/views/posts/attribute_ro/_image.haml
Normal file
6
app/views/posts/attribute_ro/_image.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
%tr{ id: attribute }
|
||||||
|
%th= post_label_t(attribute, :path, post: post)
|
||||||
|
%td
|
||||||
|
%figure
|
||||||
|
= image_tag metadata.value['path'], alt: metadata.value['description']
|
||||||
|
%figcaption= metadata.value['description']
|
3
app/views/posts/attribute_ro/_string.haml
Normal file
3
app/views/posts/attribute_ro/_string.haml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
%tr{ id: attribute }
|
||||||
|
%th= post_label_t(attribute, post: post)
|
||||||
|
%td= metadata.value
|
|
@ -1,91 +1,39 @@
|
||||||
- tags = %w[h1 h2 h3 h4 h5 h6 p a ul ol li table tr td th tbody thead tfoot em strong sup blockquote cite pre]
|
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
= render 'layouts/breadcrumb',
|
= render 'layouts/breadcrumb',
|
||||||
crumbs: [link_to(t('sites.index'), sites_path),
|
crumbs: [link_to(t('sites.index'), sites_path),
|
||||||
@site.name,
|
@site.name,
|
||||||
link_to(t('posts.index'),
|
link_to(t('posts.index'), site_posts_path(@site)),
|
||||||
site_posts_path(@site)),
|
@post.title.value]
|
||||||
@post.title]
|
|
||||||
|
|
||||||
.row
|
|
||||||
.col
|
|
||||||
%h1{ class: @post.get_front_matter(:dir) }= @post.title
|
|
||||||
|
|
||||||
%p
|
|
||||||
- translations = @post.translations.map do |translation|
|
|
||||||
- link_to translation.title,
|
|
||||||
site_post_path(@site, translation, lang: translation.lang)
|
|
||||||
= raw translations.join(' / ')
|
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
|
%article.content
|
||||||
= link_to t('posts.edit'),
|
= link_to t('posts.edit'),
|
||||||
edit_site_post_path(@site, @post, lang: @lang),
|
edit_site_post_path(@site, @post.id),
|
||||||
class: 'btn btn-info'
|
class: 'btn btn-info btn-block'
|
||||||
|
|
||||||
.row
|
|
||||||
.col
|
|
||||||
.content{ class: @post.get_front_matter(:dir) }
|
|
||||||
= sanitize_markdown @post.content,
|
|
||||||
tags: tags
|
|
||||||
|
|
||||||
-#
|
|
||||||
Representar los datos en una tabla:
|
|
||||||
Texto: tal cual en una celda
|
|
||||||
Array: píldoras
|
|
||||||
Array de Hashes: Tabla
|
|
||||||
Hash: Tabla
|
|
||||||
TODO DRY
|
|
||||||
%table.table.table-condensed.table-striped.table-responsive
|
%table.table.table-condensed.table-striped.table-responsive
|
||||||
%tbody
|
|
||||||
- @post.front_matter.each do |key, data|
|
|
||||||
%tr
|
|
||||||
%th= t("posts.#{key}")
|
|
||||||
%td
|
|
||||||
- if data.is_a? Array
|
|
||||||
-# Un Array de Hashes
|
|
||||||
- if data.all? { |a| a.is_a? Hash }
|
|
||||||
%table
|
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
- !data.empty? && data.first.keys.each do |k|
|
%th.text-center{ colspan: 2 }= t('.front_matter')
|
||||||
%th= k.humanize
|
|
||||||
%tbody
|
%tbody
|
||||||
- data.each do |r|
|
-#
|
||||||
%tr
|
TODO: Cambiar por un método que nos deje interactuar
|
||||||
- r.each do |_, v|
|
directamente con los metadatos
|
||||||
%td
|
- @post.attributes.each do |attr|
|
||||||
- if v.is_a? Array
|
:ruby
|
||||||
- v.each do |s|
|
metadata = @post.send(attr)
|
||||||
%span.badge.badge-secondary= s
|
|
||||||
- else
|
next unless metadata.front_matter?
|
||||||
= v
|
|
||||||
- else
|
= render "posts/attribute_ro/#{metadata.type}", post: @post,
|
||||||
- data.each do |d|
|
attribute: attr, metadata: metadata, tags: all_html_tags
|
||||||
%span.badge.badge-secondary= d
|
|
||||||
- elsif data.is_a? Hash
|
-# Mostrar todo lo que no va en el front_matter (el contenido)
|
||||||
%table
|
- @post.attributes.each do |attr|
|
||||||
%thead
|
|
||||||
%tr
|
- next if @post.send(attr).front_matter?
|
||||||
- data.keys.each do |k|
|
|
||||||
%th= k.humanize
|
%section{ id: attr }
|
||||||
%tbody
|
= sanitize_markdown @post.send(attr).value, tags: all_html_tags
|
||||||
%tr
|
|
||||||
- data.each do |_, v|
|
|
||||||
%td= v
|
|
||||||
- elsif data.respond_to? :content
|
|
||||||
-# Contenido del artículo
|
|
||||||
= sanitize_markdown data.content, tags: tags
|
|
||||||
- elsif data.respond_to? :strftime
|
|
||||||
-# Fecha
|
|
||||||
= data.strftime('%F')
|
|
||||||
- else
|
|
||||||
-# Texto
|
|
||||||
- if @post.image? key
|
|
||||||
%img.img-fluid{ src: @site.get_url_for_sutty(data) }
|
|
||||||
- elsif @post.url? key
|
|
||||||
%a{ href: @site.get_url_for_sutty(data) }= data
|
|
||||||
- else
|
|
||||||
= data
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ en:
|
||||||
update: 'Updated %{name}'
|
update: 'Updated %{name}'
|
||||||
post_service:
|
post_service:
|
||||||
created: 'Created "%{title}"'
|
created: 'Created "%{title}"'
|
||||||
|
updated: 'Updated "%{title}"'
|
||||||
metadata:
|
metadata:
|
||||||
array:
|
array:
|
||||||
cant_be_empty: 'This field cannot be empty'
|
cant_be_empty: 'This field cannot be empty'
|
||||||
|
@ -300,6 +301,8 @@ en:
|
||||||
en: 'English'
|
en: 'English'
|
||||||
ar: 'Arabic'
|
ar: 'Arabic'
|
||||||
posts:
|
posts:
|
||||||
|
show:
|
||||||
|
front_matter: Post metadata
|
||||||
submit:
|
submit:
|
||||||
save: 'Save'
|
save: 'Save'
|
||||||
save_incomplete: 'Save as draft'
|
save_incomplete: 'Save as draft'
|
||||||
|
@ -326,7 +329,6 @@ en:
|
||||||
edit: 'Edit'
|
edit: 'Edit'
|
||||||
draft: revision
|
draft: revision
|
||||||
incomplete: draft
|
incomplete: draft
|
||||||
invalid: 'This field is required!'
|
|
||||||
open: 'Tip: You can add new options by typing them and pressing Enter'
|
open: 'Tip: You can add new options by typing them and pressing Enter'
|
||||||
private: '🔒 The values of this field will remain private'
|
private: '🔒 The values of this field will remain private'
|
||||||
select:
|
select:
|
||||||
|
|
|
@ -5,6 +5,7 @@ es:
|
||||||
update: 'Actualizado %{name}'
|
update: 'Actualizado %{name}'
|
||||||
post_service:
|
post_service:
|
||||||
created: 'Creado "%{title}"'
|
created: 'Creado "%{title}"'
|
||||||
|
updated: 'Modificado "%{title}"'
|
||||||
metadata:
|
metadata:
|
||||||
array:
|
array:
|
||||||
cant_be_empty: 'El campo no puede estar vacío'
|
cant_be_empty: 'El campo no puede estar vacío'
|
||||||
|
@ -313,6 +314,8 @@ es:
|
||||||
en: 'inglés'
|
en: 'inglés'
|
||||||
ar: 'árabe'
|
ar: 'árabe'
|
||||||
posts:
|
posts:
|
||||||
|
show:
|
||||||
|
front_matter: Metadatos del artículo
|
||||||
submit:
|
submit:
|
||||||
save: 'Guardar'
|
save: 'Guardar'
|
||||||
save_incomplete: 'Guardar como borrador'
|
save_incomplete: 'Guardar como borrador'
|
||||||
|
|
|
@ -20,7 +20,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||||
@site.destroy
|
@site.destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'se pueden ver' do
|
test 'se pueden ver todos' do
|
||||||
get site_posts_url(@site), headers: @authorization
|
get site_posts_url(@site), headers: @authorization
|
||||||
|
|
||||||
assert_match @site.name, response.body
|
assert_match @site.name, response.body
|
||||||
|
@ -39,7 +39,6 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
|
||||||
# TODO: implementar reload?
|
# TODO: implementar reload?
|
||||||
site = Site.find(@site.id)
|
site = Site.find(@site.id)
|
||||||
site.read
|
|
||||||
new_post = site.posts.first
|
new_post = site.posts.first
|
||||||
|
|
||||||
assert_equal 302, response.status
|
assert_equal 302, response.status
|
||||||
|
@ -52,4 +51,11 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_equal I18n.t('post_service.created', title: new_post.title.value),
|
assert_equal I18n.t('post_service.created', title: new_post.title.value),
|
||||||
@site.repository.rugged.head.target.message
|
@site.repository.rugged.head.target.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'se pueden ver' do
|
||||||
|
get site_post_url(@site, @post.id), headers: @authorization
|
||||||
|
|
||||||
|
assert_equal 200, response.status
|
||||||
|
assert_match @post.title.value, response.body
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,6 @@ class PostTest < ActiveSupport::TestCase
|
||||||
#
|
#
|
||||||
# TODO: Cambiar a skel cuando publiquemos los códigos y privacidad
|
# TODO: Cambiar a skel cuando publiquemos los códigos y privacidad
|
||||||
@site = create :site, name: 'sutty.nl'
|
@site = create :site, name: 'sutty.nl'
|
||||||
@site.read
|
|
||||||
@post = @site.posts.sample
|
@post = @site.posts.sample
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,6 @@ class SiteTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'se puede leer un sitio' do
|
test 'se puede leer un sitio' do
|
||||||
site = create :site, name: 'sutty.nl'
|
site = create :site, name: 'sutty.nl'
|
||||||
site.read
|
|
||||||
|
|
||||||
assert site.valid?
|
assert site.valid?
|
||||||
assert !site.posts.empty?
|
assert !site.posts.empty?
|
||||||
|
@ -89,7 +88,6 @@ class SiteTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
test 'el sitio tiene artículos en distintos idiomas' do
|
test 'el sitio tiene artículos en distintos idiomas' do
|
||||||
site = create :site, name: 'sutty.nl'
|
site = create :site, name: 'sutty.nl'
|
||||||
site.read
|
|
||||||
|
|
||||||
I18n.available_locales.each do |locale|
|
I18n.available_locales.each do |locale|
|
||||||
assert site.posts(lang: locale).size.positive?
|
assert site.posts(lang: locale).size.positive?
|
||||||
|
|
Loading…
Reference in a new issue