versión preliminar de los artículos closes #176
This commit is contained in:
parent
caaafc50c0
commit
74ce28c447
19 changed files with 168 additions and 12 deletions
4
Gemfile
4
Gemfile
|
@ -57,6 +57,10 @@ gem 'jekyll', git: 'https://0xacab.org/sutty/jekyll/jekyll.git',
|
|||
branch: 'master'
|
||||
gem 'jekyll-data', require: 'jekyll-data',
|
||||
git: 'https://0xacab.org/sutty/jekyll/jekyll-data.git'
|
||||
gem 'jekyll-commonmark'
|
||||
gem 'jekyll-images'
|
||||
gem 'jekyll-include-cache'
|
||||
gem 'sutty-liquid'
|
||||
gem 'lockbox'
|
||||
gem 'mini_magick'
|
||||
gem 'mobility'
|
||||
|
|
|
@ -236,6 +236,9 @@ GEM
|
|||
nokogiri (>= 1.6)
|
||||
jbuilder (2.10.1)
|
||||
activesupport (>= 5.0.0)
|
||||
jekyll-commonmark (1.3.1)
|
||||
commonmarker (~> 0.14)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-feed (0.15.1)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-images (0.2.7)
|
||||
|
@ -590,7 +593,10 @@ DEPENDENCIES
|
|||
inline_svg
|
||||
jbuilder (~> 2.5)
|
||||
jekyll!
|
||||
jekyll-commonmark
|
||||
jekyll-data!
|
||||
jekyll-images
|
||||
jekyll-include-cache
|
||||
letter_opener
|
||||
listen (>= 3.0.5, < 3.2)
|
||||
lockbox
|
||||
|
@ -625,6 +631,7 @@ DEPENDENCIES
|
|||
sucker_punch
|
||||
sutty-donaciones-jekyll-theme
|
||||
sutty-jekyll-theme
|
||||
sutty-liquid
|
||||
sutty-minima
|
||||
symbol-fstring
|
||||
terminal-table
|
||||
|
|
|
@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base
|
|||
around_action :set_locale
|
||||
|
||||
rescue_from ActionController::RoutingError, with: :page_not_found
|
||||
rescue_from ActionController::ParameterMissing, with: :page_not_found
|
||||
|
||||
before_action do
|
||||
Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?('@' + ENV.fetch('SUTTY', 'sutty.nl'))
|
||||
|
|
|
@ -52,6 +52,18 @@ class PostsController < ApplicationController
|
|||
fresh_when @post
|
||||
end
|
||||
|
||||
# Genera una previsualización del artículo.
|
||||
#
|
||||
# TODO: No todos los artículos tienen previsualización!
|
||||
def preview
|
||||
@site = find_site
|
||||
@post = @site.posts(lang: locale).find params[:post_id]
|
||||
|
||||
authorize @post
|
||||
|
||||
render html: @post.render
|
||||
end
|
||||
|
||||
def new
|
||||
authorize Post
|
||||
@site = find_site
|
||||
|
|
|
@ -106,6 +106,27 @@ class SitesController < ApplicationController
|
|||
redirect_to sites_path
|
||||
end
|
||||
|
||||
# Obtiene y streamea archivos estáticos desde el repositorio mismo,
|
||||
# pero sólo los públicos (es decir los archivos subidos desde Sutty).
|
||||
def static_file
|
||||
authorize site
|
||||
|
||||
file = params.require(:file) + '.' + params.require(:format)
|
||||
|
||||
raise ActionController::RoutingError unless file.start_with? 'public/'
|
||||
|
||||
path = site.relative_path file
|
||||
|
||||
raise ActionController::RoutingError unless File.exist? path
|
||||
|
||||
# TODO: Hacer esto usa recursos, pero menos que generar el sitio
|
||||
# cada vez. Para poder usar X-Accel tendríamos que montar los
|
||||
# repositorios en el servidor web, cosa que no queremos, o hacer
|
||||
# links simbólicos desde todos los public, o usar un servidor web
|
||||
# local que soporte sendfile mejor que Rails (nghttpd?)
|
||||
send_file path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def site
|
||||
|
|
11
app/lib/jekyll/tags/base.rb
Normal file
11
app/lib/jekyll/tags/base.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Jekyll
|
||||
module Tags
|
||||
class Base < Liquid::Tag
|
||||
def render(context)
|
||||
context.registers[:site].config['url']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
19
app/lib/jekyll/tags/empty.rb
Normal file
19
app/lib/jekyll/tags/empty.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Jekyll
|
||||
module Tags
|
||||
# Genera un tag vacío que sirve para reemplazar tags provistos por
|
||||
# complementos que no vamos a cargar dentro del panel (seo, feed,
|
||||
# etc.) Lo correcto sería modificar Liquid::Document para que
|
||||
# ignore los tags desconocidos, pero en nuestras pruebas los toma
|
||||
# como el comienzo de un bloque e ignora HTML adyacente, así que
|
||||
# preferimos avanzar con una lista predeterminada.
|
||||
#
|
||||
# @see config/initializers/core_extensions.rb
|
||||
class Empty < Liquid::Tag
|
||||
def render(_)
|
||||
''
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -75,6 +75,51 @@ class Post < OpenStruct
|
|||
"#<Post id=\"#{id}\">"
|
||||
end
|
||||
|
||||
# Renderiza el artículo para poder previsualizarlo. Leemos solo la
|
||||
# información básica, con lo que no van a funcionar artículos
|
||||
# relacionados y otras cuestiones.
|
||||
#
|
||||
# @see app/lib/jekyll/tags/base.rb
|
||||
def render
|
||||
Dir.chdir site.path do
|
||||
# Compatibilidad con jekyll-locales, necesario para el filtro
|
||||
# date_local
|
||||
#
|
||||
# TODO: Cambiar el locale en otro lado
|
||||
site.jekyll.config['lang'] = lang.value
|
||||
site.jekyll.config['locale'] = lang.value
|
||||
|
||||
# Payload básico con traducciones.
|
||||
document.renderer.payload = {
|
||||
'site' => {
|
||||
'data' => site.data,
|
||||
'i18n' => site.data[lang.value],
|
||||
'lang' => lang.value,
|
||||
'locale' => lang.value
|
||||
},
|
||||
'page' => document.to_liquid
|
||||
}
|
||||
|
||||
# Renderizar lo estrictamente necesario y convertir a HTML para
|
||||
# poder reemplazar valores.
|
||||
html = Nokogiri::HTML document.renderer.render_document
|
||||
# Las imágenes se cargan directamente desde el repositorio, porque
|
||||
# no son públicas hasta que se publica el artículo.
|
||||
html.css('img').each do |img|
|
||||
next if %r{\Ahttps?://} =~ img.attributes['src']
|
||||
|
||||
img.attributes['src'].value = Rails.application.routes.url_helpers.site_static_file_url(site, file: img.attributes['src'].value)
|
||||
end
|
||||
|
||||
# Notificar a les usuaries que están viendo una previsualización
|
||||
# XXX: Asume que estamos usando Bootstrap :B
|
||||
html.at_css('body').first_element_child.before("<div class=\"alert alert-warning text-center\">#{I18n.t('posts.preview_message')}</div>")
|
||||
|
||||
# Cacofonía
|
||||
html.to_html.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
# Devuelve una llave para poder guardar el post en una cache
|
||||
def cache_key
|
||||
'posts/' + uuid.value
|
||||
|
|
|
@ -146,6 +146,13 @@ class Site < ApplicationRecord
|
|||
File.join(Site.site_path, name_was)
|
||||
end
|
||||
|
||||
# Limpiar la ruta y unirla con el separador de directorios del
|
||||
# sistema operativo. Como si algún día fuera a cambiar o
|
||||
# soportáramos Windows :P
|
||||
def relative_path(suspicious_path)
|
||||
File.join(path, *suspicious_path.gsub('..', '/').gsub('./', '').squeeze('/').split('/'))
|
||||
end
|
||||
|
||||
# Obtiene la lista de traducciones actuales
|
||||
#
|
||||
# Siempre tiene que tener algo porque las traducciones están
|
||||
|
|
|
@ -18,6 +18,10 @@ class PostPolicy
|
|||
post.site.usuarie?(usuarie) || post.usuaries.include?(usuarie)
|
||||
end
|
||||
|
||||
def preview?
|
||||
show?
|
||||
end
|
||||
|
||||
def new?
|
||||
create?
|
||||
end
|
||||
|
|
|
@ -24,6 +24,11 @@ class SitePolicy
|
|||
!current_role.temporal
|
||||
end
|
||||
|
||||
# Todes pueden ver los archivos
|
||||
def static_file?
|
||||
true
|
||||
end
|
||||
|
||||
# Todes pueden crear nuevos sitios
|
||||
def new?
|
||||
true
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
= link_to t('posts.edit'),
|
||||
edit_site_post_path(@site, @post.id),
|
||||
class: 'btn btn-block'
|
||||
= link_to t('posts.preview'),
|
||||
site_post_preview_path(@site, @post.id),
|
||||
class: 'btn btn-block'
|
||||
|
||||
%table.table.table-condensed
|
||||
%thead
|
||||
|
|
|
@ -68,6 +68,9 @@ Rails.application.configure do
|
|||
config.action_mailer.perform_caching = false
|
||||
config.action_mailer.delivery_method = :letter_opener
|
||||
config.action_mailer.perform_deliveries = true
|
||||
config.action_mailer.default_url_options = { host: 'localhost',
|
||||
port: 3000 }
|
||||
config.action_mailer.default_url_options = { host: 'panel.sutty.local', port: 3000, protocol: 'https' }
|
||||
|
||||
Rails.application.routes.default_url_options[:host] = 'panel.sutty.local'
|
||||
Rails.application.routes.default_url_options[:port] = 3000
|
||||
Rails.application.routes.default_url_options[:protocol] = 'https'
|
||||
end
|
||||
|
|
|
@ -126,7 +126,8 @@ Rails.application.configure do
|
|||
|
||||
# Recibir por mail notificaciones de excepciones
|
||||
config.action_mailer.default_url_options = {
|
||||
host: "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}"
|
||||
host: "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}",
|
||||
protocol: 'https'
|
||||
}
|
||||
config.action_mailer.perform_deliveries = true
|
||||
config.action_mailer.raise_delivery_errors = true
|
||||
|
@ -144,4 +145,7 @@ Rails.application.configure do
|
|||
sender_address: ENV['DEFAULT_FROM'],
|
||||
exception_recipients: ENV['EXCEPTION_TO']
|
||||
}
|
||||
|
||||
Rails.application.routes.default_url_options[:host] = "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}"
|
||||
Rails.application.routes.default_url_options[:protocol] = 'https'
|
||||
end
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
Rails.application.config.content_security_policy do |policy|
|
||||
policy.default_src :self
|
||||
# XXX: Varios scripts generan estilos en línea
|
||||
policy.style_src :self, :unsafe_inline
|
||||
policy.style_src :self, :unsafe_inline, :https
|
||||
# Repetimos la default para poder saber cuál es la política en falta
|
||||
policy.script_src :self
|
||||
policy.font_src :self
|
||||
policy.font_src :self, :https
|
||||
# XXX: Los íconos de Trix se cargan vía data:
|
||||
policy.img_src :self, :data, :https
|
||||
# Ya no usamos applets!
|
||||
|
|
|
@ -3,6 +3,14 @@
|
|||
String.include CoreExtensions::String::StripTags
|
||||
Jekyll::Document.include CoreExtensions::Jekyll::Document::Path
|
||||
|
||||
# Definir tags de Liquid que provienen de complementos para que siempre
|
||||
# devuelvan contenido vacío.
|
||||
%w[seo feed_meta turbolinks].each do |tag|
|
||||
Liquid::Template.register_tag(tag, Jekyll::Tags::Empty)
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('base', Jekyll::Tags::Base)
|
||||
|
||||
module ActionDispatch
|
||||
# Redefinir el formateo de URLs de Rails para eliminar parámetros
|
||||
# selectivamente
|
||||
|
@ -28,12 +36,6 @@ end
|
|||
#
|
||||
# TODO: Aplicar monkey patches en otro lado...
|
||||
module Jekyll
|
||||
Site.class_eval do
|
||||
def setup
|
||||
ensure_not_in_dest
|
||||
end
|
||||
end
|
||||
|
||||
Reader.class_eval do
|
||||
def retrieve_posts(_); end
|
||||
|
||||
|
|
|
@ -483,6 +483,8 @@ en:
|
|||
categories: 'Everything'
|
||||
index: 'Posts'
|
||||
edit: 'Edit'
|
||||
preview: 'Preliminary version'
|
||||
preview_message: 'This is a preliminary version, use the Publish changes button back on the panel to publish the article on your site.'
|
||||
open: 'Tip: You can add new options by typing them and pressing Enter'
|
||||
private: '🔒 The values of this field will remain private'
|
||||
select:
|
||||
|
|
|
@ -492,6 +492,8 @@ es:
|
|||
new: 'Agregar:'
|
||||
index: 'Artículos'
|
||||
edit: 'Editar'
|
||||
preview: 'Versión preliminar'
|
||||
preview_message: 'Esta es una versión preliminar, para que el artículo aparezca en tu sitio utiliza el botón Publicar cambios en el panel'
|
||||
open: 'Nota: Puedes agregar más opciones a medida que las escribes y presionas Entrar'
|
||||
private: '🔒 Los valores de este campo serán privados'
|
||||
select:
|
||||
|
|
|
@ -30,6 +30,8 @@ Rails.application.routes.draw do
|
|||
# alias en nginx sin tener que usar expresiones regulares para
|
||||
# detectar el nombre del sitio.
|
||||
get '/sites/private/:site_id(*file)', to: 'private#show', constraints: { site_id: %r{[^/]+} }
|
||||
# Obtener archivos estáticos desde el directorio público
|
||||
get '/sites/:site_id/static_file/(*file)', to: 'sites#static_file', as: 'site_static_file', constraints: { site_id: %r{[^/]+} }
|
||||
|
||||
resources :sites, constraints: { site_id: %r{[^/]+}, id: %r{[^/]+} } do
|
||||
# Gestionar actualizaciones del sitio
|
||||
|
@ -53,7 +55,9 @@ Rails.application.routes.draw do
|
|||
nested do
|
||||
scope '(:locale)' do
|
||||
post :'posts/reorder', to: 'posts#reorder'
|
||||
resources :posts
|
||||
resources :posts do
|
||||
get :preview, to: 'posts#preview'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue