5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-16 16:21:41 +00:00

Merge branch 'rails' into actualizacion-de-cuidados

This commit is contained in:
f 2021-10-22 17:34:15 -03:00
commit 443950819f
18 changed files with 192 additions and 66 deletions

View file

@ -69,6 +69,7 @@ gem 'terminal-table'
gem 'validates_hostname'
gem 'webpacker'
gem 'yaml_db', git: 'https://0xacab.org/sutty/yaml_db.git'
gem 'kaminari'
# database
gem 'hairtrigger'

View file

@ -309,6 +309,18 @@ GEM
jekyll-write-and-commit-changes (0.2.0)
jekyll (~> 4)
rugged (~> 1)
kaminari (1.2.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.1)
kaminari-activerecord (= 1.2.1)
kaminari-core (= 1.2.1)
kaminari-actionview (1.2.1)
actionview
kaminari-core (= 1.2.1)
kaminari-activerecord (1.2.1)
activerecord
kaminari-core (= 1.2.1)
kaminari-core (1.2.1)
kramdown (2.3.1)
rexml
kramdown-parser-gfm (1.1.0)
@ -669,6 +681,7 @@ DEPENDENCIES
jekyll-data!
jekyll-images
jekyll-include-cache
kaminari
letter_opener
listen (>= 3.0.5, < 3.2)
loaf

View file

@ -108,7 +108,6 @@ ota-js: assets ## Actualizar Javascript en el nodo delegado
ota: ## Actualizar Rails en el nodo delegado
umask 022; git format-patch $(commit)
scp ./0*.patch $(delegate):/tmp/
ssh $(delegate) mkdir -p /tmp/patches-$(commit)/
scp ./0*.patch $(delegate):/tmp/patches-$(commit)/
scp ./ota.sh $(delegate):/tmp/

View file

@ -22,21 +22,21 @@ class PostsController < ApplicationController
# XXX: Cada vez que cambiamos un Post tocamos el sitio con lo que es
# más simple saber si hubo cambios.
if stale?([current_usuarie, site, filter_params])
# Todos los artículos de este sitio para el idioma actual
@posts = site.indexed_posts.where(locale: locale)
# De este tipo
@posts = @posts.where(layout: filter_params[:layout]) if filter_params[:layout]
# Que estén dentro de la categoría
@posts = @posts.in_category(filter_params[:category]) if filter_params[:category]
# Aplicar los parámetros de búsqueda
@posts = @posts.search(locale, filter_params[:q]) if filter_params[:q].present?
# A los que este usuarie tiene acceso
@posts = PostPolicy::Scope.new(current_usuarie, @posts).resolve
return unless stale?([current_usuarie, site, filter_params])
# Filtrar los posts que les invitades no pueden ver
@usuarie = site.usuarie? current_usuarie
end
# Todos los artículos de este sitio para el idioma actual
@posts = site.indexed_posts.where(locale: locale)
# De este tipo
@posts = @posts.where(layout: filter_params[:layout]) if filter_params[:layout]
# Que estén dentro de la categoría
@posts = @posts.in_category(filter_params[:category]) if filter_params[:category]
# Aplicar los parámetros de búsqueda
@posts = @posts.search(locale, filter_params[:q]) if filter_params[:q].present?
# A los que este usuarie tiene acceso
@posts = PostPolicy::Scope.new(current_usuarie, @posts).resolve
# Filtrar los posts que les invitades no pueden ver
@usuarie = site.usuarie? current_usuarie
end
def show
@ -54,7 +54,7 @@ class PostsController < ApplicationController
def new
authorize Post
@post = site.posts.build(lang: locale, layout: params[:layout])
@post = site.posts(lang: locale).build(layout: params[:layout])
breadcrumb I18n.t('loaf.breadcrumbs.posts.new', layout: @post.layout.humanized_name.downcase), ''
end
@ -154,7 +154,9 @@ class PostsController < ApplicationController
#
# @return [Hash]
def filter_params
@filter_params ||= params.permit(:q, :category, :layout).to_h.select { |_, v| v.present? }
@filter_params ||= params.permit(:q, :category, :layout).to_hash.select do |_, v|
v.present?
end.transform_keys(&:to_sym)
end
def site

View file

@ -25,10 +25,19 @@ class MetadataBoolean < MetadataTemplate
# * false
# * true
def value
return document.data.fetch(name.to_s, default_value) if self[:value].nil?
return self[:value] unless self[:value].is_a? String
case self[:value]
when NilClass
document.data.fetch(name.to_s, default_value)
when String
true_values.include? self[:value]
else
self[:value]
end
end
self[:value] = true_values.include? self[:value]
# Siempre guardar el valor de este campo a menos que sea nulo
def empty?
value.nil?
end
private

View file

@ -56,7 +56,7 @@ class MetadataContent < MetadataTemplate
uri = URI element['src']
# No permitimos recursos externos
element.remove unless uri.hostname.end_with? Site.domain
element.remove unless uri.scheme == 'https' && uri.hostname.end_with?(Site.domain)
rescue URI::Error
element.remove
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
# Un campo numérico de punto flotante
class MetadataFloat < MetadataTemplate
# Nada
def default_value
super || nil
end
def save
return true unless changed?
self[:value] = value.to_f
self[:value] = encrypt(value) if private?
true
end
# Indicarle al navegador que acepte números decimales
#
# @return [Float]
def step
0.05
end
private
def decrypt(value)
super(value).to_f
end
end

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
# Un campo de texto seleccionado de una lista de valores posibles
class MetadataPredefinedValue < MetadataString
# Obtiene todos los valores desde el layout, en un formato compatible
# con options_for_select.
#
# @return [Hash]
def values
@values ||= layout.dig(:metadata, name, 'values', I18n.locale.to_s)&.invert || {}
end
private
# Solo permite almacenar los valores predefinidos.
#
# @return [String]
def sanitize(string)
v = super string
return '' unless values.values.include? v
v
end
end

View file

@ -65,9 +65,6 @@ class Site < ApplicationRecord
accepts_nested_attributes_for :deploys, allow_destroy: true
# El sitio en Jekyll
attr_reader :jekyll
# XXX: Es importante incluir luego de los callbacks de :load_jekyll
include Site::Index
@ -180,29 +177,28 @@ class Site < ApplicationRecord
# Trae los datos del directorio _data dentro del sitio
def data
unless @jekyll.data.present?
@jekyll.reader.read_data
# Define los valores por defecto según la llave buscada
@jekyll.data.default_proc = proc do |data, key|
data[key] = case key
when 'layout' then {}
end
unless jekyll.data.present?
run_in_path do
jekyll.reader.read_data
jekyll.data['layouts'] ||= {}
end
end
@jekyll.data
jekyll.data
end
# Traer las colecciones. Todos los artículos van a estar dentro de
# colecciones.
def collections
unless @read
@jekyll.reader.read_collections
run_in_path do
jekyll.reader.read_collections
end
@read = true
end
@jekyll.collections
jekyll.collections
end
# Traer la configuración de forma modificable
@ -290,7 +286,9 @@ class Site < ApplicationRecord
#
# @return [Hash]
def theme_layouts
@jekyll.reader.read_layouts
run_in_path do
jekyll.reader.read_layouts
end
end
# Trae todos los valores disponibles para un campo
@ -332,6 +330,12 @@ class Site < ApplicationRecord
status == 'building'
end
def jekyll
run_in_path do
@jekyll ||= Jekyll::Site.new(configuration)
end
end
# Cargar el sitio Jekyll
#
# TODO: En lugar de leer todo junto de una vez, extraer la carga de
@ -345,10 +349,7 @@ class Site < ApplicationRecord
def reload_jekyll!
reset
Dir.chdir(path) do
@jekyll = Jekyll::Site.new(configuration)
end
jekyll
end
def reload
@ -526,4 +527,8 @@ class Site < ApplicationRecord
errors.add(:design_id,
I18n.t('activerecord.errors.models.site.attributes.design_id.layout_incompatible.error'))
end
def run_in_path(&block)
Dir.chdir path, &block
end
end

View file

@ -0,0 +1,3 @@
%tr{ id: attribute }
%th= post_label_t(attribute, post: post)
%td{ dir: dir, lang: locale }= metadata.value

View file

@ -0,0 +1,3 @@
%tr{ id: attribute }
%th= post_label_t(attribute, post: post)
%td{ dir: dir, lang: locale }= metadata.value

View file

@ -0,0 +1,6 @@
.form-group
= label_tag "#{base}_#{attribute}", post_label_t(attribute, post: post)
= number_field base, attribute, value: metadata.value, step: metadata.step,
**field_options(attribute, metadata)
= render 'posts/attribute_feedback',
post: post, attribute: attribute, metadata: metadata

View file

@ -0,0 +1,7 @@
.form-group
= label_tag "#{base}_#{attribute}", post_label_t(attribute, post: post)
= select_tag(plain_field_name_for(base, attribute),
options_for_select(metadata.values, metadata.value),
**field_options(attribute, metadata), include_blank: t('.empty'))
= render 'posts/attribute_feedback',
post: post, attribute: attribute, metadata: metadata

View file

@ -40,7 +40,7 @@
%section.col
= render 'layouts/flash'
.d-flex.justify-content-between.align-items-center.pl-2-plus.pr-2-plus.mb-2
%form
%form{ action: site_posts_path }
- @filter_params.each do |param, value|
- next if param == 'q'
%input{ type: 'hidden', name: param, value: value }
@ -51,7 +51,7 @@
- if @site.locales.size > 1
%nav#locales
- @site.locales.each do |locale|
= link_to t("locales.#{locale}.name"), site_posts_path(@site, **@filter_params.merge(locale: locale)),
= link_to @site.data.dig(locale.to_s, 'locale') || locale, site_posts_path(@site, **@filter_params.merge(locale: locale)),
class: "mr-2 mt-2 mb-2 #{locale == @locale ? 'active font-weight-bold' : ''}"
.pl-2-plus
- @filter_params.each do |param, value|
@ -72,14 +72,18 @@
%thead
%tr
%th.border-0.background-white.position-sticky{ style: 'top: 0; z-index: 2', colspan: '4' }
= submit_tag t('posts.reorder.submit'), class: 'btn'
%button.btn{ data: { action: 'reorder#unselect' } }
= t('posts.reorder.unselect')
%span.badge{ data: { target: 'reorder.counter' } } 0
%button.btn{ data: { action: 'reorder#up' } }= t('posts.reorder.up')
%button.btn{ data: { action: 'reorder#down' } }= t('posts.reorder.down')
%button.btn{ data: { action: 'reorder#top' } }= t('posts.reorder.top')
%button.btn{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom')
.d-flex.flex-row.justify-content-between
%div
= submit_tag t('posts.reorder.submit'), class: 'btn'
%button.btn{ data: { action: 'reorder#unselect' } }
= t('posts.reorder.unselect')
%span.badge{ data: { target: 'reorder.counter' } } 0
%button.btn{ data: { action: 'reorder#up' } }= t('posts.reorder.up')
%button.btn{ data: { action: 'reorder#down' } }= t('posts.reorder.down')
%button.btn{ data: { action: 'reorder#top' } }= t('posts.reorder.top')
%button.btn{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom')
%div
%tbody
- dir = t("locales.#{@locale}.dir")
- size = @posts.size
@ -104,19 +108,19 @@
%span{ lang: post.locale, dir: dir }= post.title
- if post.front_matter['draft'].present?
%span.badge.badge-primary= I18n.t('posts.attributes.draft.label')
- if post.front_matter['categories'].present?
%br
%small
- post.front_matter['categories'].each do |category|
= link_to site_posts_path(@site, **@filter_params.merge(category: category)) do
%span{ lang: post.locale, dir: dir }= category
= '/' unless post.front_matter['categories'].last == category
%br
%small
= link_to @site.layouts[post.layout].humanized_name, site_posts_path(@site, **@filter_params.merge(layout: post.layout))
- post.front_matter['categories']&.each do |category|
= link_to site_posts_path(@site, **@filter_params.merge(category: category)) do
%span{ lang: post.locale, dir: dir }= category
= '/' unless post.front_matter['categories'].last == category
%td
%td.text-nowrap
= post.created_at.strftime('%F')
%br/
= post.order
%td
%td.text-nowrap
- if @usuarie || policy(post).edit?
= link_to t('posts.edit'), edit_site_post_path(@site, post.path), class: 'btn btn-block'
- if @usuarie || policy(post).destroy?

View file

@ -19,8 +19,8 @@ en:
remember_me: 'Keeps session open for %{remember_for}'
actions:
sr-help: "After this form you'll find links to recover your account and other actions."
_true: Yes
_false: No
_true: 'Yes'
_false: 'No'
svg:
sutty:
title: Sutty
@ -163,7 +163,7 @@ en:
signature: 'With love, Sutty'
breadcrumb:
title: 'Your location in Sutty'
logout: Exit
logout: Log out
mutual_aid: Mutual aid
collaborations:
collaborate:
@ -376,6 +376,8 @@ en:
en: 'English'
ar: 'Arabic'
posts:
prev: Previous page
next: Next page
empty: "There are no results for those search parameters."
caption: Post list
attribute_ro:
@ -413,6 +415,8 @@ en:
destroy: Remove image
belongs_to:
empty: "(Empty)"
predefined_value:
empty: "(Empty)"
draft:
label: Draft
reorder:

View file

@ -19,8 +19,8 @@ es:
remember_me: 'Mantiene la sesión abierta por %{remember_for}'
actions:
sr-help: 'Después del formulario encontrarás vínculos para recuperar tu cuenta, entre otras acciones.'
_true:
_false: No
_true: 'Sí'
_false: 'No'
svg:
sutty:
title: Sutty
@ -163,7 +163,7 @@ es:
signature: 'Con cariño, Sutty'
breadcrumb:
title: 'Tu ubicación en Sutty'
logout: Salir
logout: Cerrar sesión
mutual_aid: Ayuda mutua
collaborations:
collaborate:
@ -384,6 +384,8 @@ es:
en: 'inglés'
ar: 'árabe'
posts:
prev: Página anterior
next: Página siguiente
empty: No hay artículos con estos parámetros de búsqueda.
caption: Lista de artículos
attribute_ro:
@ -421,6 +423,8 @@ es:
destroy: 'Eliminar imagen'
belongs_to:
empty: "(Vacío)"
predefined_value:
empty: "(Vacío)"
draft:
label: Borrador
reorder:

View file

@ -57,9 +57,10 @@ Rails.application.routes.draw do
# Gestionar artículos según idioma
nested do
scope '(:locale)' do
scope '/(:locale)', constraint: /[a-z]{2}/ do
post :'posts/reorder', to: 'posts#reorder'
resources :posts do
get 'p/:page', action: :index, on: :collection
get :preview, to: 'posts#preview'
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
# Agrega índices únicos que pensábamos que ya existían.
class AddUniqueness < ActiveRecord::Migration[6.1]
def change
add_index :designs, :name, unique: true
add_index :designs, :gem, unique: true
add_index :licencias, :name, unique: true
end
end