mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-15 03:41:41 +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)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
haml (5.0.4)
|
||||
haml (5.1.2)
|
||||
temple (>= 0.8.0)
|
||||
tilt
|
||||
haml-lint (0.999.999)
|
||||
|
@ -277,7 +277,7 @@ GEM
|
|||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
rake (12.3.2)
|
||||
rake (12.3.3)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.10.0)
|
||||
ffi (~> 1.0)
|
||||
|
@ -306,7 +306,7 @@ GEM
|
|||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
rouge (3.3.0)
|
||||
rubocop (0.72.0)
|
||||
rubocop (0.74.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.6)
|
||||
|
|
|
@ -40,29 +40,8 @@ class ApplicationController < ActionController::Base
|
|||
site
|
||||
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
|
||||
I18n.locale = current_usuarie.lang
|
||||
I18n.locale = current_usuarie.lang if current_usuarie
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -8,8 +8,6 @@ class PostsController < ApplicationController
|
|||
def index
|
||||
authorize Post
|
||||
@site = find_site
|
||||
# TODO: por qué no lo está leyendo @site.posts?
|
||||
@site.read
|
||||
@category = session[:category] = params.dig(:category)
|
||||
# TODO: Aplicar policy_scope
|
||||
@posts = @site.posts(lang: I18n.locale)
|
||||
|
@ -26,8 +24,7 @@ class PostsController < ApplicationController
|
|||
|
||||
def show
|
||||
@site = find_site
|
||||
@lang = find_lang(@site)
|
||||
@post = find_post(@site)
|
||||
@post = @site.posts.find params[:id]
|
||||
authorize @post
|
||||
end
|
||||
|
||||
|
@ -54,8 +51,7 @@ class PostsController < ApplicationController
|
|||
|
||||
def edit
|
||||
@site = find_site
|
||||
@lang = find_lang(@site)
|
||||
@post = find_post(@site)
|
||||
@post = @site.posts.find params[:id]
|
||||
|
||||
authorize @post
|
||||
end
|
||||
|
|
|
@ -27,8 +27,21 @@ module ApplicationHelper
|
|||
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 = {})
|
||||
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
|
||||
|
||||
def invalid?(model, field)
|
||||
|
|
|
@ -25,6 +25,12 @@ class PostRelation < Array
|
|||
post
|
||||
end
|
||||
|
||||
def find(id)
|
||||
super() do |p|
|
||||
p.id == id
|
||||
end
|
||||
end
|
||||
|
||||
# Intenta guardar todos y devuelve true si pudo
|
||||
def save_all
|
||||
map(&:save).all?
|
||||
|
|
|
@ -108,6 +108,9 @@ class Site < ApplicationRecord
|
|||
|
||||
# Lee el sitio y todos los artículos
|
||||
def read
|
||||
# No hacer nada si ya se leyó antes
|
||||
return unless @jekyll.layouts.empty?
|
||||
|
||||
@jekyll.read
|
||||
end
|
||||
|
||||
|
@ -115,7 +118,7 @@ class Site < ApplicationRecord
|
|||
#
|
||||
# XXX: Leer directamente sin pasar por Jekyll
|
||||
def data
|
||||
read if @jekyll.data.empty?
|
||||
read
|
||||
|
||||
@jekyll.data
|
||||
end
|
||||
|
@ -123,7 +126,7 @@ class Site < ApplicationRecord
|
|||
# Traer las colecciones. Todos los artículos van a estar dentro de
|
||||
# colecciones.
|
||||
def collections
|
||||
read if @jekyll.collections.empty?
|
||||
read
|
||||
|
||||
@jekyll.collections
|
||||
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
|
||||
# saber los parámetros tenemos que haber instanciado el post
|
||||
# primero.
|
||||
post.update_attributes(post_params) &&
|
||||
site.repository.commit(file: post.path.absolute,
|
||||
usuarie: usuarie,
|
||||
message: I18n.t('post_service.created',
|
||||
title: post.title.value))
|
||||
post.update_attributes(post_params) && commit(action: :created)
|
||||
|
||||
# Devolver el post aunque no se haya salvado para poder rescatar los
|
||||
# errores
|
||||
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
|
||||
# errores
|
||||
|
@ -25,6 +32,13 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
|
||||
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
|
||||
def 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
|
||||
.col
|
||||
= render 'layouts/breadcrumb',
|
||||
crumbs: [link_to(t('sites.index'), sites_path),
|
||||
@site.name,
|
||||
link_to(t('posts.index'),
|
||||
site_posts_path(@site)),
|
||||
@post.title]
|
||||
link_to(t('posts.index'), site_posts_path(@site)),
|
||||
@post.title.value]
|
||||
|
||||
.row
|
||||
.col
|
||||
%h1{ class: @post.get_front_matter(:dir) }= @post.title
|
||||
%article.content
|
||||
= link_to t('posts.edit'),
|
||||
edit_site_post_path(@site, @post.id),
|
||||
class: 'btn btn-info btn-block'
|
||||
|
||||
%p
|
||||
- translations = @post.translations.map do |translation|
|
||||
- link_to translation.title,
|
||||
site_post_path(@site, translation, lang: translation.lang)
|
||||
= raw translations.join(' / ')
|
||||
|
||||
.row
|
||||
.col
|
||||
= link_to t('posts.edit'),
|
||||
edit_site_post_path(@site, @post, lang: @lang),
|
||||
class: 'btn btn-info'
|
||||
|
||||
.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
|
||||
%tbody
|
||||
- @post.front_matter.each do |key, data|
|
||||
%table.table.table-condensed.table-striped.table-responsive
|
||||
%thead
|
||||
%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
|
||||
%tr
|
||||
- !data.empty? && data.first.keys.each do |k|
|
||||
%th= k.humanize
|
||||
%tbody
|
||||
- data.each do |r|
|
||||
%tr
|
||||
- r.each do |_, v|
|
||||
%td
|
||||
- if v.is_a? Array
|
||||
- v.each do |s|
|
||||
%span.badge.badge-secondary= s
|
||||
- else
|
||||
= v
|
||||
- else
|
||||
- data.each do |d|
|
||||
%span.badge.badge-secondary= d
|
||||
- elsif data.is_a? Hash
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
- data.keys.each do |k|
|
||||
%th= k.humanize
|
||||
%tbody
|
||||
%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
|
||||
%th.text-center{ colspan: 2 }= t('.front_matter')
|
||||
%tbody
|
||||
-#
|
||||
TODO: Cambiar por un método que nos deje interactuar
|
||||
directamente con los metadatos
|
||||
- @post.attributes.each do |attr|
|
||||
:ruby
|
||||
metadata = @post.send(attr)
|
||||
|
||||
next unless metadata.front_matter?
|
||||
|
||||
= render "posts/attribute_ro/#{metadata.type}", post: @post,
|
||||
attribute: attr, metadata: metadata, tags: all_html_tags
|
||||
|
||||
-# Mostrar todo lo que no va en el front_matter (el contenido)
|
||||
- @post.attributes.each do |attr|
|
||||
|
||||
- next if @post.send(attr).front_matter?
|
||||
|
||||
%section{ id: attr }
|
||||
= sanitize_markdown @post.send(attr).value, tags: all_html_tags
|
||||
|
|
|
@ -5,6 +5,7 @@ en:
|
|||
update: 'Updated %{name}'
|
||||
post_service:
|
||||
created: 'Created "%{title}"'
|
||||
updated: 'Updated "%{title}"'
|
||||
metadata:
|
||||
array:
|
||||
cant_be_empty: 'This field cannot be empty'
|
||||
|
@ -300,6 +301,8 @@ en:
|
|||
en: 'English'
|
||||
ar: 'Arabic'
|
||||
posts:
|
||||
show:
|
||||
front_matter: Post metadata
|
||||
submit:
|
||||
save: 'Save'
|
||||
save_incomplete: 'Save as draft'
|
||||
|
@ -326,7 +329,6 @@ en:
|
|||
edit: 'Edit'
|
||||
draft: revision
|
||||
incomplete: draft
|
||||
invalid: 'This field is required!'
|
||||
open: 'Tip: You can add new options by typing them and pressing Enter'
|
||||
private: '🔒 The values of this field will remain private'
|
||||
select:
|
||||
|
|
|
@ -5,6 +5,7 @@ es:
|
|||
update: 'Actualizado %{name}'
|
||||
post_service:
|
||||
created: 'Creado "%{title}"'
|
||||
updated: 'Modificado "%{title}"'
|
||||
metadata:
|
||||
array:
|
||||
cant_be_empty: 'El campo no puede estar vacío'
|
||||
|
@ -313,6 +314,8 @@ es:
|
|||
en: 'inglés'
|
||||
ar: 'árabe'
|
||||
posts:
|
||||
show:
|
||||
front_matter: Metadatos del artículo
|
||||
submit:
|
||||
save: 'Guardar'
|
||||
save_incomplete: 'Guardar como borrador'
|
||||
|
|
|
@ -20,7 +20,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||
@site.destroy
|
||||
end
|
||||
|
||||
test 'se pueden ver' do
|
||||
test 'se pueden ver todos' do
|
||||
get site_posts_url(@site), headers: @authorization
|
||||
|
||||
assert_match @site.name, response.body
|
||||
|
@ -39,7 +39,6 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
# TODO: implementar reload?
|
||||
site = Site.find(@site.id)
|
||||
site.read
|
||||
new_post = site.posts.first
|
||||
|
||||
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),
|
||||
@site.repository.rugged.head.target.message
|
||||
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
|
||||
|
|
|
@ -8,7 +8,6 @@ class PostTest < ActiveSupport::TestCase
|
|||
#
|
||||
# TODO: Cambiar a skel cuando publiquemos los códigos y privacidad
|
||||
@site = create :site, name: 'sutty.nl'
|
||||
@site.read
|
||||
@post = @site.posts.sample
|
||||
end
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ class SiteTest < ActiveSupport::TestCase
|
|||
|
||||
test 'se puede leer un sitio' do
|
||||
site = create :site, name: 'sutty.nl'
|
||||
site.read
|
||||
|
||||
assert site.valid?
|
||||
assert !site.posts.empty?
|
||||
|
@ -89,7 +88,6 @@ class SiteTest < ActiveSupport::TestCase
|
|||
|
||||
test 'el sitio tiene artículos en distintos idiomas' do
|
||||
site = create :site, name: 'sutty.nl'
|
||||
site.read
|
||||
|
||||
I18n.available_locales.each do |locale|
|
||||
assert site.posts(lang: locale).size.positive?
|
||||
|
|
Loading…
Reference in a new issue