5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-24 23:06:21 +00:00

fix: lints

This commit is contained in:
maki 2024-06-04 12:49:37 -03:00
parent 8992ff4dfe
commit 70528aa602
50 changed files with 492 additions and 371 deletions

View file

@ -670,3 +670,16 @@ a.black {
} }
} }
.break-all {
word-break: break-all;
}
.details-agregar {
@extend .d-flex;
@extend .border;
@extend .border-magenta;
@extend .justify-content-between;
@extend .align-items-center;
@extend .w-100;
@extend .mb-3;
}

View file

@ -86,9 +86,7 @@ class ApplicationController < ActionController::Base
end end
def site def site
@site ||= find_site.tap do |s| @site ||= find_site.tap(&:reindex_changes!)
s.reindex_changes!
end
end end
protected protected

View file

@ -175,7 +175,7 @@ class StatsController < ApplicationController
locale: I18n.locale, locale: I18n.locale,
empty: I18n.t('stats.index.empty', **please_return_at), empty: I18n.t('stats.index.empty', **please_return_at),
loading: I18n.t('stats.index.loading'), loading: I18n.t('stats.index.loading'),
html: %(<div id="%{id}" class="d-flex align-items-center justify-content-center" style="height: %{height}; width: %{width};">%{loading}</div>) html: %(<div id="%<id>s" class="d-flex align-items-center justify-content-center" style="height: %<height>s; width: %<width>s;">%<loading>s</div>)
} }
end end

View file

@ -30,10 +30,9 @@ module ApplicationHelper
if k == key if k == key
case v case v
when Array then [k, v - [value]] when Array then [k, v - [value]]
else nil
end end
else else
[ k, v ] [k, v]
end end
end.compact.to_h end.compact.to_h
end end

View file

@ -17,7 +17,7 @@ class DeployDistributedPress < Deploy
before_create :create_remote_site! before_create :create_remote_site!
before_destroy :delete_remote_site! before_destroy :delete_remote_site!
DEPENDENCIES = %i[deploy_local] DEPENDENCIES = %i[deploy_local].freeze
# Actualiza la información y luego envía los cambios # Actualiza la información y luego envía los cambios
# #
@ -32,9 +32,7 @@ class DeployDistributedPress < Deploy
create_remote_site! if remote_site_id.blank? create_remote_site! if remote_site_id.blank?
save save
if remote_site_id.blank? raise DeployJob::DeployException, 'El sitio no se creó en Distributed Press' if remote_site_id.blank?
raise DeployJob::DeployException, 'El sitio no se creó en Distributed Press'
end
site_client.tap do |c| site_client.tap do |c|
stdout = Thread.new(publisher.logger_out) do |io| stdout = Thread.new(publisher.logger_out) do |io|
@ -54,7 +52,7 @@ class DeployDistributedPress < Deploy
end end
if status if status
self.remote_info[:distributed_press] = c.show(publishing_site).to_h remote_info[:distributed_press] = c.show(publishing_site).to_h
save save
end end
@ -123,7 +121,10 @@ class DeployDistributedPress < Deploy
# #
# @return [DistributedPressPublisher::V1::Schemas::NewSite] # @return [DistributedPressPublisher::V1::Schemas::NewSite]
def create_site def create_site
DistributedPress::V1::Schemas::NewSite.new.call(domain: hostname, protocols: { http: true, ipfs: true, hyper: true }) DistributedPress::V1::Schemas::NewSite.new.call(domain: hostname,
protocols: {
http: true, ipfs: true, hyper: true
})
end end
# Crea el sitio en la instancia con el hostname especificado # Crea el sitio en la instancia con el hostname especificado
@ -149,7 +150,7 @@ class DeployDistributedPress < Deploy
# @param log [String] # @param log [String]
# @return [nil] # @return [nil]
def create_stat!(status, log) def create_stat!(status, log)
build_stats.create action: publisher.to_s,log: log, seconds: time_spent_in_seconds, bytes: size, status: status build_stats.create action: publisher.to_s, log: log, seconds: time_spent_in_seconds, bytes: size, status: status
nil nil
end end

View file

@ -86,7 +86,7 @@ class DeployLocal < Deploy
'AIRBRAKE_PROJECT_ID' => site.id.to_s, 'AIRBRAKE_PROJECT_ID' => site.id.to_s,
'AIRBRAKE_PROJECT_KEY' => site.airbrake_api_key, 'AIRBRAKE_PROJECT_KEY' => site.airbrake_api_key,
'YARN_CACHE_FOLDER' => yarn_cache_dir, 'YARN_CACHE_FOLDER' => yarn_cache_dir,
'GEMS_SOURCE' => ENV['GEMS_SOURCE'] 'GEMS_SOURCE' => ENV.fetch('GEMS_SOURCE', nil)
} }
end end

View file

@ -5,7 +5,7 @@
class DeployRsync < Deploy class DeployRsync < Deploy
store :values, accessors: %i[hostname destination host_keys], coder: JSON store :values, accessors: %i[hostname destination host_keys], coder: JSON
DEPENDENCIES = %i[deploy_local deploy_zip] DEPENDENCIES = %i[deploy_local deploy_zip].freeze
def deploy(output: false) def deploy(output: false)
ssh? && rsync(output: output) ssh? && rsync(output: output)
@ -39,6 +39,7 @@ class DeployRsync < Deploy
# @return [Boolean] # @return [Boolean]
def ssh? def ssh?
return true if destination.start_with? 'rsync://' return true if destination.start_with? 'rsync://'
user, host = user_host user, host = user_host
ssh_available = false ssh_available = false
@ -66,7 +67,7 @@ class DeployRsync < Deploy
{ {
'HOME' => home_dir, 'HOME' => home_dir,
'PATH' => '/usr/bin', 'PATH' => '/usr/bin',
'LANG' => ENV['LANG'] 'LANG' => ENV.fetch('LANG', nil)
} }
end end
@ -92,7 +93,8 @@ class DeployRsync < Deploy
# #
# @return [Boolean] # @return [Boolean]
def rsync(output: false) def rsync(output: false)
run %(rsync -aviH --delete-after --timeout=5 #{Shellwords.escape source}/ #{Shellwords.escape destination}/), output: output run %(rsync -aviH --delete-after --timeout=5 #{Shellwords.escape source}/ #{Shellwords.escape destination}/),
output: output
end end
# El origen es el destino de la compilación # El origen es el destino de la compilación

View file

@ -38,15 +38,15 @@ class IndexedPost < ApplicationRecord
# #
# @param :attribute [String,Symbol] # @param :attribute [String,Symbol]
# @return [Array] # @return [Array]
scope :everything_of, ->(attribute) do scope :everything_of, lambda { |attribute|
where('front_matter ? :attribute', attribute: attribute) where('front_matter ? :attribute', attribute: attribute)
.pluck( .pluck(
Arel.sql( Arel.sql(
ActiveRecord::Base::sanitize_sql(['front_matter -> :attribute', attribute: attribute]) ActiveRecord::Base.sanitize_sql(['front_matter -> :attribute', { attribute: attribute }])
) )
) )
.flatten.uniq .flatten.uniq
end }
validates_presence_of :layout, :path, :locale validates_presence_of :layout, :path, :locale

View file

@ -29,6 +29,6 @@ class MetadataBelongsTo < MetadataRelatedPosts
private private
def sanitize(uuid) def sanitize(uuid)
uuid.to_s.gsub(/[^a-f0-9\-]/i, '') uuid.to_s.gsub(/[^a-f0-9-]/i, '')
end end
end end

View file

@ -53,18 +53,15 @@ class MetadataContent < MetadataTemplate
# Eliminar elementos sin src y comprobar su origen # Eliminar elementos sin src y comprobar su origen
html.css(elements).each do |element| html.css(elements).each do |element|
begin
raise URI::Error unless element['src'].present? raise URI::Error unless element['src'].present?
uri = URI element['src'] uri = URI element['src']
# No permitimos recursos externos, solo si sabemos cuales son # No permitimos recursos externos, solo si sabemos cuales son
# los recursos locales # los recursos locales
if Rails.application.config.hosts.present? if Rails.application.config.hosts.present? && !Rails.application.config.hosts.include?(uri.hostname)
unless Rails.application.config.hosts.include?(uri.hostname)
raise URI::Error raise URI::Error
end end
end
element['src'] = convert_src_to_internal_path uri element['src'] = convert_src_to_internal_path uri
@ -72,7 +69,6 @@ class MetadataContent < MetadataTemplate
rescue URI::Error rescue URI::Error
element.remove element.remove
end end
end
# Eliminar figure sin contenido # Eliminar figure sin contenido
html.css('figure').each do |figure| html.css('figure').each do |figure|
@ -101,11 +97,10 @@ class MetadataContent < MetadataTemplate
# @param style [String] # @param style [String]
# @return [String] # @return [String]
def sanitize_style(style) def sanitize_style(style)
style.split(';').reduce({}) do |style_hash, style_string| style.split(';').each_with_object({}) do |style_string, style_hash|
key, value = style_string.split(':', 2) key, value = style_string.split(':', 2)
style_hash[key] ||= value style_hash[key] ||= value
style_hash
end.slice(*allowed_styles).map do |style_pair| end.slice(*allowed_styles).map do |style_pair|
style_pair.join(':') style_pair.join(':')
end.join(';') end.join(';')

View file

@ -10,7 +10,8 @@ class MetadataRelatedPosts < MetadataArray
def values def values
@values ||= posts.pluck(:title, :created_at, :layout, :post_id).to_h do |row| @values ||= posts.pluck(:title, :created_at, :layout, :post_id).to_h do |row|
row.tap do |value| row.tap do |value|
value[0] = "#{value[0]} #{value.delete_at(1).strftime('%F')} (#{site.layouts[value.delete_at(1)].humanized_name})" value[0] =
"#{value[0]} #{value.delete_at(1).strftime('%F')} (#{site.layouts[value.delete_at(1)].humanized_name})"
end end
end end
end end
@ -46,7 +47,7 @@ class MetadataRelatedPosts < MetadataArray
def sanitize(uuid) def sanitize(uuid)
super(uuid.map do |u| super(uuid.map do |u|
u.to_s.gsub(/[^a-f0-9\-]/i, '') u.to_s.gsub(/[^a-f0-9-]/i, '')
end) end)
end end
end end

View file

@ -132,6 +132,7 @@ class Post
src = element.attributes['src'] src = element.attributes['src']
next unless src&.value&.start_with? 'public/' next unless src&.value&.start_with? 'public/'
file = MetadataFile.new(site: site, post: self, document: document, layout: layout) file = MetadataFile.new(site: site, post: self, document: document, layout: layout)
file.value['path'] = src.value file.value['path'] = src.value
@ -250,7 +251,8 @@ class Post
# La fecha de creación inmodificable del post # La fecha de creación inmodificable del post
def created_at def created_at
@metadata[:created_at] ||= MetadataCreatedAt.new(document: document, site: site, layout: layout, name: :created_at, type: :created_at, post: self, required: true) @metadata[:created_at] ||= MetadataCreatedAt.new(document: document, site: site, layout: layout, name: :created_at,
type: :created_at, post: self, required: true)
end end
# Detecta si es un atributo válido o no, a partir de la tabla de la # Detecta si es un atributo válido o no, a partir de la tabla de la

View file

@ -6,7 +6,6 @@ class Post
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
# @return [IndexedPost,nil] # @return [IndexedPost,nil]
def indexed_post def indexed_post
site.indexed_posts.find_by_post_id(uuid.value) site.indexed_posts.find_by_post_id(uuid.value)

View file

@ -419,7 +419,6 @@ class Site < ApplicationRecord
FileUtils.rm_rf path FileUtils.rm_rf path
end end
# Sincroniza algunos atributos del sitio con su configuración y # Sincroniza algunos atributos del sitio con su configuración y
# guarda los cambios # guarda los cambios
# #

View file

@ -104,8 +104,8 @@ class Site
indexable_posts.select do |delta| indexable_posts.select do |delta|
MODIFIED_STATUSES.include? delta.status MODIFIED_STATUSES.include? delta.status
end.each do |delta| end.each do |delta|
locale, _ = locale_and_path_from(delta.new_file[:path]) locale, = locale_and_path_from(delta.new_file[:path])
full_path = File.join(self.path, delta.new_file[:path]) full_path = File.join(path, delta.new_file[:path])
Post.build(path: full_path, site: self, layout: Post.find_layout(full_path), locale: locale).index! Post.build(path: full_path, site: self, layout: Post.find_layout(full_path), locale: locale).index!
end end

View file

@ -148,7 +148,7 @@ class Site
# @param :rm [Array] Archivos a eliminar # @param :rm [Array] Archivos a eliminar
# @param :usuarie [Usuarie] Quién hace el commit # @param :usuarie [Usuarie] Quién hace el commit
# @param :message [String] Mensaje # @param :message [String] Mensaje
def commit(add: [], rm: [], usuarie:, message:) def commit(usuarie:, message:, add: [], rm: [])
# Cargar el árbol actual # Cargar el árbol actual
rugged.index.read_tree rugged.head.target.tree rugged.index.read_tree rugged.head.target.tree
@ -182,7 +182,7 @@ class Site
# #
# @return [Boolean] # @return [Boolean]
def gc def gc
git_sh("git", "gc") git_sh('git', 'gc')
end end
# Pushea cambios al repositorio remoto # Pushea cambios al repositorio remoto
@ -196,8 +196,8 @@ class Site
# Hace limpieza de LFS # Hace limpieza de LFS
def lfs_cleanup def lfs_cleanup
git_sh("git", "lfs", "prune") git_sh('git', 'lfs', 'prune')
git_sh("git", "lfs", "dedup") git_sh('git', 'lfs', 'dedup')
end end
private private
@ -257,7 +257,7 @@ class Site
# @param :args [Array] # @param :args [Array]
# @return [Boolean] # @return [Boolean]
def git_sh(*args) def git_sh(*args)
env = { 'PATH' => '/usr/bin', 'LANG' => ENV['LANG'], 'HOME' => path } env = { 'PATH' => '/usr/bin', 'LANG' => ENV.fetch('LANG', nil), 'HOME' => path }
r = nil r = nil
Open3.popen2e(env, *args, unsetenv_others: true, chdir: path) do |_, _, t| Open3.popen2e(env, *args, unsetenv_others: true, chdir: path) do |_, _, t|

View file

@ -145,10 +145,10 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
# Si les usuaries modifican o crean una licencia, considerarla # Si les usuaries modifican o crean una licencia, considerarla
# personalizada en el panel. # personalizada en el panel.
def update_site_license! def update_site_license!
if site.usuarie?(usuarie) && post.layout.name == :license && !site.licencia.custom? return unless site.usuarie?(usuarie) && post.layout.name == :license && !site.licencia.custom?
site.update licencia: Licencia.find_by_icons('custom') site.update licencia: Licencia.find_by_icons('custom')
end end
end
# @return [Set<String>] # @return [Set<String>]
def associated_posts_to_save def associated_posts_to_save

View file

@ -2,5 +2,5 @@
- id = "#{field.object_name}_#{name}" - id = "#{field.object_name}_#{name}"
- name = "#{field.object_name}[#{name}]" - name = "#{field.object_name}[#{name}]"
= render 'bootstrap/custom_checkbox', id: id, name: name, content: content, required: local_assigns[:required], value: "1" do = render 'bootstrap/custom_checkbox', id: id, name: name, content: content, required: local_assigns[:required], value: '1' do
= yield = yield

View file

@ -12,7 +12,6 @@
as: resource_name, as: resource_name,
url: confirmation_path(resource_name), url: confirmation_path(resource_name),
html: { method: :post }) do |f| html: { method: :post }) do |f|
:ruby :ruby
value = if resource.pending_reconfirmation? value = if resource.pending_reconfirmation?
resource.unconfirmed_email resource.unconfirmed_email

View file

@ -12,7 +12,6 @@
= form_for(resource, as: resource_name, = form_for(resource, as: resource_name,
url: password_path(resource_name), url: password_path(resource_name),
html: { method: :put }) do |f| html: { method: :put }) do |f|
= f.hidden_field :reset_password_token = f.hidden_field :reset_password_token
.form-group .form-group

View file

@ -12,7 +12,6 @@
as: resource_name, as: resource_name,
url: registration_path(resource_name), url: registration_path(resource_name),
html: { method: :put }) do |f| html: { method: :put }) do |f|
.form-group .form-group
= f.label :email = f.label :email
= f.email_field :email, autofocus: true, autocomplete: 'email', = f.email_field :email, autofocus: true, autocomplete: 'email',

View file

@ -11,7 +11,6 @@
= form_for(resource, = form_for(resource,
as: resource_name, as: resource_name,
url: registration_path(resource_name, params: { locale: params[:locale] })) do |f| url: registration_path(resource_name, params: { locale: params[:locale] })) do |f|
.form-group .form-group
= f.label :email, class: 'sr-only' = f.label :email, class: 'sr-only'
= f.email_field :email, autofocus: true, autocomplete: 'email', = f.email_field :email, autofocus: true, autocomplete: 'email',
@ -48,7 +47,7 @@
- content = t(".#{field}.label") - content = t(".#{field}.label")
- href = t(".#{field}.href", default: '') - href = t(".#{field}.href", default: '')
- help_content = t(".#{field}.help") - help_content = t(".#{field}.help")
= render 'bootstrap/custom_checkbox', id: id, name: name, content: content, required: required, value: "1" do = render 'bootstrap/custom_checkbox', id: id, name: name, content: content, required: required, value: '1' do
- if href.present? - if href.present?
= link_to help_content, href, target: '_blank', rel: 'noopener' = link_to help_content, href, target: '_blank', rel: 'noopener'
- else - else

View file

@ -31,5 +31,6 @@
- params.permit! - params.permit!
- I18n.available_locales.each do |locale| - I18n.available_locales.each do |locale|
- next if locale == I18n.locale - next if locale == I18n.locale
%li.nav-item %li.nav-item
= link_to t("switch_locale.#{locale}"), params.to_h.merge(change_locale_to: locale) = link_to t("switch_locale.#{locale}"), params.to_h.merge(change_locale_to: locale)

View file

@ -7,9 +7,8 @@
@param :summary_class [String] Clases para el summary @param :summary_class [String] Clases para el summary
- local_assigns[:summary_class] ||= 'h3' - local_assigns[:summary_class] ||= 'h3'
- local_assigns[:closed] ||= '&#x25B6'.html_safe; - local_assigns[:closed] ||= '&#x25B6'.html_safe
- local_assigns[:open] ||= '&#x25BC'.html_safe; - local_assigns[:open] ||= '&#x25BC'.html_safe
%details.details.py-2{ id: local_assigns[:id], data: { controller: 'details', action: 'toggle->details#store' }, class: local_assigns[:details_class] } %details.details.py-2{ id: local_assigns[:id], data: { controller: 'details', action: 'toggle->details#store' }, class: local_assigns[:details_class] }
%summary.d-flex.flex-row.align-items-center.justify-content-between{ class: local_assigns[:summary_class] } %summary.d-flex.flex-row.align-items-center.justify-content-between{ class: local_assigns[:summary_class] }

View file

@ -7,4 +7,5 @@
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ó)
- next unless p - next unless p
%li= link_to p.title, site_post_path(site, p.path) %li= link_to p.title, site_post_path(site, p.path)

View file

@ -0,0 +1,13 @@
- if !@site.invitade?(cuenta) # esto ta mal
.form-check
= hidden_field_tag "#{base}[#{attribute}]", '0', id: ''
.custom-control.custom-switch
= check_box_tag "#{base}[#{attribute}]", '1', metadata.value,
class: "custom-control-input #{invalid(post, attribute)}",
aria: { describedby: id_for_help(attribute) },
autofocus: autofocus
= label_tag "#{base}_#{attribute}", post_label_t(attribute, post: post),
class: 'custom-control-label'
= render 'posts/attribute_feedback',
post: post, attribute: attribute, metadata: metadata

View file

@ -20,16 +20,26 @@
-# Los archivos requeridos solo se pueden reemplazar -# Los archivos requeridos solo se pueden reemplazar
- unless metadata.required - unless metadata.required
.custom-control.custom-switch .custom-control.custom-switch
= check_box_tag "#{base}[#{attribute}][path]", '', false, id: "#{base}_#{attribute}_destroy", class: 'custom-control-input' = check_box_tag "#{base}[#{attribute}][path]",
= label_tag "#{base}_#{attribute}_destroy", t('posts.attributes.file.destroy'), class: 'custom-control-label' '',
false,
id: "#{base}_#{attribute}_destroy",
class: 'custom-control-input'
= label_tag "#{base}_#{attribute}_destroy",
t('posts.attributes.file.destroy'),
class: 'custom-control-label'
.custom-file .custom-file
= file_field(*field_name_for(base, attribute, :path), = file_field(*field_name_for(base, attribute, :path),
**field_options(attribute, metadata, required: (metadata.required && !metadata.path?)), **field_options(attribute,
metadata,
required: (metadata.required && !metadata.path?)),
class: "custom-file-input #{invalid(post, attribute)}", class: "custom-file-input #{invalid(post, attribute)}",
data: { target: 'file-preview.input', action: 'file-preview#update' }) data: { target: 'file-preview.input',
action: 'file-preview#update' })
= label_tag "#{base}_#{attribute}_path", = label_tag "#{base}_#{attribute}_path",
post_label_t(attribute, :path, post: post), class: 'custom-file-label' post_label_t(attribute, :path, post: post),
class: 'custom-file-label'
= render 'posts/attribute_feedback', = render 'posts/attribute_feedback',
post: post, attribute: [attribute, :path], metadata: metadata post: post, attribute: [attribute, :path], metadata: metadata

View file

@ -11,8 +11,14 @@
-# Las imágenes requeridas solo se pueden reemplazar -# Las imágenes requeridas solo se pueden reemplazar
- unless metadata.required - unless metadata.required
.custom-control.custom-switch .custom-control.custom-switch
= check_box_tag "#{base}[#{attribute}][path]", '', false, id: "#{base}_#{attribute}_destroy", class: 'custom-control-input' = check_box_tag "#{base}[#{attribute}][path]",
= label_tag "#{base}_#{attribute}_destroy", t('posts.attributes.image.destroy'), class: 'custom-control-label' '',
false,
id: "#{base}_#{attribute}_destroy",
class: 'custom-control-input'
= label_tag "#{base}_#{attribute}_destroy",
t('posts.attributes.image.destroy'),
class: 'custom-control-label'
- else - else
= image_tag '', = image_tag '',
alt: metadata.value['description'], alt: metadata.value['description'],
@ -21,12 +27,16 @@
.custom-file .custom-file
= file_field(*field_name_for(base, attribute, :path), = file_field(*field_name_for(base, attribute, :path),
**field_options(attribute, metadata, required: (metadata.required && !metadata.path?)), **field_options(attribute,
metadata,
required: (metadata.required && !metadata.path?)),
class: "custom-file-input #{invalid(post, attribute)}", class: "custom-file-input #{invalid(post, attribute)}",
accept: ActiveStorage.web_image_content_types.join(','), accept: ActiveStorage.web_image_content_types.join(','),
data: { target: 'file-preview.input', action: 'file-preview#update' }) data: { target: 'file-preview.input',
action: 'file-preview#update' })
= label_tag "#{base}_#{attribute}_path", = label_tag "#{base}_#{attribute}_path",
post_label_t(attribute, :path, post: post), class: 'custom-file-label' post_label_t(attribute, :path, post: post),
class: 'custom-file-label'
= render 'posts/attribute_feedback', = render 'posts/attribute_feedback',
post: post, attribute: [attribute, :path], metadata: metadata post: post, attribute: [attribute, :path], metadata: metadata

View file

@ -15,4 +15,5 @@
= select_tag("#{plain_field_name_for(base, attribute)}[]", = select_tag("#{plain_field_name_for(base, attribute)}[]",
options_for_select(metadata.values[locale], value), options_for_select(metadata.values[locale], value),
**field_options(attribute, metadata), include_blank: t('.empty')) **field_options(attribute, metadata),
include_blank: t('.empty'))

View file

@ -14,42 +14,56 @@
= render 'sites/moderation_queue', site: @site, class: 'btn-block' = render 'sites/moderation_queue', site: @site, class: 'btn-block'
= render 'layouts/details', summary: t('posts.filters.title') do = render 'layouts/details', summary: t('posts.filters.title') do
%form{ method: :get }
%form{method: :get}
.border.border-magenta.p-1 .border.border-magenta.p-1
- @filter_params.each do |param, values| - @filter_params.each do |param, values|
- next if param == :layout - next if param == :layout
- [values].flatten.each do |value| - [values].flatten.each do |value|
%input{ type: 'hidden', name: values.is_a?(Array) ? "#{param}[]" : param, value: value } %input{ type: 'hidden',
name: values.is_a?(Array) ? "#{param}[]" : param,
value: value }
%legend.font-weight-bold.m-0.h6= 'Tipo de contenido' %legend.font-weight-bold.m-0.h6= 'Tipo de contenido'
- @site.schema_organization.each do |key, _| - @site.schema_organization.each do |key, _|
.custom-control.custom-checkbox .custom-control.custom-checkbox
- schema = @site.layouts[key] - schema = @site.layouts[key]
= render 'schemas/filter', site: @site, key: key, schema: schema, filter: @filter_params = render 'schemas/filter', site: @site,
key: key,
schema: schema,
filter: @filter_params
%button.btn.btn-secondary.mt-3{ type: 'submit' }= t('posts.filters.submit') %button.btn.btn-secondary.mt-3{ type: 'submit' }= t('posts.filters.submit')
= render 'layouts/details', = render 'layouts/details',
summary: t('posts.new'), summary: t('posts.new'),
summary_class: "h4 magenta font-weight-bold m-0 px-2", summary_class: 'h4 magenta font-weight-bold m-0 px-2',
details_class: "d-flex border border-magenta justify-content-between align-items-center w-100 mb-3", details_class: 'details-agregar',
open: "+", closed: "+", open: '+', closed: '+',
open_class: "h1 magenta font-weight-bold m-0", open_class: 'h1 magenta font-weight-bold m-0',
closed_class: "h1 magenta font-weight-bold m-0" do closed_class: 'h1 magenta font-weight-bold m-0' do
%table.table-sm.w-100 %table.table-sm.w-100
%tbody %tbody
- @site.schema_organization.each do |schema, _| - @site.schema_organization.each do |schema, _|
- schema = @site.layouts[schema] - schema = @site.layouts[schema]
- next if schema.hidden? - next if schema.hidden?
= render 'schemas/row', site: @site, schema: schema, filter: @filter_params
= render 'schemas/row', site: @site,
schema: schema,
filter: @filter_params
- if policy(@site_stat).index? - if policy(@site_stat).index?
= link_to t('stats.index.title'), site_stats_path(@site), class: 'btn btn-secondary' = link_to t('stats.index.title'),
site_stats_path(@site),
class: 'btn btn-secondary'
- if policy(@site).edit? - if policy(@site).edit?
= link_to t('sites.edit.btn', site: @site.title), edit_site_path(@site), class: 'btn btn-secondary' = link_to t('sites.edit.btn', site: @site.title),
edit_site_path(@site),
class: 'btn btn-secondary'
- if policy(@site).private? - if policy(@site).private?
= link_to t('sites.private'), '../private/' + @site.name, class: 'btn btn-secondary', target: '_blank', rel: 'noopener' = link_to t('sites.private'), "../private/#{@site.name}",
class: 'btn btn-secondary',
target: '_blank',
rel: 'noopener'
- if policy(SiteUsuarie.new(@site, current_usuarie)).index? - if policy(SiteUsuarie.new(@site, current_usuarie)).index?
= render 'layouts/btn_with_tooltip', = render 'layouts/btn_with_tooltip',
@ -61,9 +75,13 @@
- if @site.design.credits - if @site.design.credits
= render 'bootstrap/alert' do = render 'bootstrap/alert' do
= sanitize_markdown @site.design.credits = sanitize_markdown @site.design.credits
= link_to t('sites.donations.text'), t('sites.donations.url'), class: 'btn btn-secondary' = link_to t('sites.donations.text'),
t('sites.donations.url'),
class: 'btn btn-secondary'
- if @site.design.designer_url - if @site.design.designer_url
= link_to t('sites.designer_url'), @site.design.designer_url, class: 'btn btn-secondary' = link_to t('sites.designer_url'),
@site.design.designer_url,
class: 'btn btn-secondary'
%section.col %section.col
.d-flex.justify-content-between.align-items-center.pl-2-plus.pr-2-plus.mb-2 .d-flex.justify-content-between.align-items-center.pl-2-plus.pr-2-plus.mb-2
@ -86,7 +104,8 @@
.d-flex.flex-row.justify-content-between .d-flex.flex-row.justify-content-between
%div %div
- if reorder_allowed - if reorder_allowed
= submit_tag t('posts.reorder.submit'), class: 'btn btn-secondary' = submit_tag t('posts.reorder.submit'),
class: 'btn btn-secondary'
%button.btn.btn-secondary{ data: { action: 'reorder#unselect' } } %button.btn.btn-secondary{ data: { action: 'reorder#unselect' } }
= t('posts.reorder.unselect') = t('posts.reorder.unselect')
%span.badge{ data: { target: 'reorder.counter' } } 0 %span.badge{ data: { target: 'reorder.counter' } } 0
@ -94,12 +113,18 @@
%button.btn.btn-secondary{ data: { action: 'reorder#down' } }= t('posts.reorder.down') %button.btn.btn-secondary{ data: { action: 'reorder#down' } }= t('posts.reorder.down')
%button.btn.btn-secondary{ data: { action: 'reorder#top' } }= t('posts.reorder.top') %button.btn.btn-secondary{ data: { action: 'reorder#top' } }= t('posts.reorder.top')
%button.btn.btn-secondary{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom') %button.btn.btn-secondary{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom')
%input{ type: 'hidden', name: 'post[lang]', value: @locale } %input{ type: 'hidden',
name: 'post[lang]',
value: @locale }
- if @site.pagination - if @site.pagination
%div %div
= link_to_prev_page @posts, t('posts.prev'), class: 'btn btn-secondary' = link_to_prev_page @posts,
= link_to_next_page @posts, t('posts.next'), class: 'btn btn-secondary' t('posts.prev'),
class: 'btn btn-secondary'
= link_to_next_page @posts,
t('posts.next'),
class: 'btn btn-secondary'
%tbody %tbody
- dir = @site.data.dig(params[:locale], 'dir') - dir = @site.data.dig(params[:locale], 'dir')
- size = @posts.size - size = @posts.size
@ -107,14 +132,17 @@
-# -#
TODO: Solo les usuaries cachean porque tenemos que separar TODO: Solo les usuaries cachean porque tenemos que separar
les botones por permisos. les botones por permisos.
- begin
- cache_if @usuarie, [post, I18n.locale] do - cache_if @usuarie, [post, I18n.locale] do
- checkbox_id = "checkbox-#{post.post_id}" - checkbox_id = "checkbox-#{post.post_id}"
%tr{ id: post.post_id, data: reorder_target } %tr{ id: post.post_id, data: reorder_target }
- if reorder_allowed - if reorder_allowed
%td %td
.custom-control.custom-checkbox .custom-control.custom-checkbox
%input.custom-control-input{ id: checkbox_id, type: 'checkbox', autocomplete: 'off', data: { action: 'reorder#select' } } %input.custom-control-input{ id: checkbox_id,
type: 'checkbox',
autocomplete: 'off',
data: { action: 'reorder#select' } }
%label.custom-control-label{ for: checkbox_id } %label.custom-control-label{ for: checkbox_id }
%span.sr-only= t('posts.reorder.select') %span.sr-only= t('posts.reorder.select')
-# Orden más alto es mayor prioridad -# Orden más alto es mayor prioridad
@ -128,7 +156,8 @@
%span.badge.badge-primary= I18n.t('posts.attributes.draft.label') %span.badge.badge-primary= I18n.t('posts.attributes.draft.label')
%br %br
%small %small
= link_to @site.layouts[post.layout].humanized_name, site_posts_path(@site, **@filter_params.merge(layout: [post.layout])) = 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| - post.front_matter['categories']&.each do |category|
= link_to site_posts_path(@site, **@filter_params.merge(category: category)) do = link_to site_posts_path(@site, **@filter_params.merge(category: category)) do
%span{ lang: post.locale, dir: dir }= category %span{ lang: post.locale, dir: dir }= category
@ -141,14 +170,23 @@
%td.text-nowrap %td.text-nowrap
.d-flex.flex-row.align-items-start .d-flex.flex-row.align-items-start
- if @usuarie || policy(post).edit? - if @usuarie || policy(post).edit?
= link_to t('posts.edit_post'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary' = link_to t('posts.edit_post'),
edit_site_post_path(@site, post.path),
class: 'btn btn-secondary'
- if @usuarie || policy(post).destroy? - if @usuarie || policy(post).destroy?
= link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-secondary', method: :delete, data: { confirm: t('posts.confirm_destroy') } = link_to t('posts.destroy'),
site_post_path(@site, post.path),
class: 'btn btn-secondary',
method: :delete,
data: { confirm: t('posts.confirm_destroy') }
-# -#
Rescatar cualquier error en un post, notificarlo e Rescatar cualquier error en un post, notificarlo e
ignorar su renderización. ignorar su renderización.
- rescue ActionView::Template::Error => e - rescue ActionView::Template::Error => e
- ExceptionNotifier.notify_exception(e.cause, data: { site: @site.name, post: @post.path.absolute, usuarie: current_usuarie.id }) - ExceptionNotifier.notify_exception(e.cause,
data: { site: @site.name,
post: @post.path.absolute,
usuarie: current_usuarie.id })
#footnotes{ hidden: true } #footnotes{ hidden: true }
- @filter_params.each do |param, value| - @filter_params.each do |param, value|

View file

@ -1 +1,3 @@
= link_to t(schema.humanized_name), new_site_post_path(site, layout: schema.value), class: 'stretched-link black text-decoration-none' = link_to t(schema.humanized_name),
new_site_post_path(site, layout: schema.value),
class: 'stretched-link black text-decoration-none'

View file

@ -1,10 +1,18 @@
%div %div
%input.custom-control-input.magenta{ type: 'checkbox', id: schema, name: "layout[]", class: "", value: schema.name, checked: @filter_params[:layout]&.include?(key.to_s) } %input.custom-control-input.magenta{ type: 'checkbox',
id: schema,
name: 'layout[]',
class: '',
value: schema.name,
checked: @filter_params[:layout]&.include?(key.to_s) }
%label.custom-control-label.font-weight-normal{ for: schema }= schema.humanized_name %label.custom-control-label.font-weight-normal{ for: schema }= schema.humanized_name
-# XXX: Solo un nivel de recursividad -# XXX: Solo un nivel de recursividad
- unless local_assigns[:parent_schema] - unless local_assigns[:parent_schema]
- schema.schemas.each do |s| - schema.schemas.each do |s|
- next if s.hidden? - next if s.hidden?
= render 'schemas/filter', schema: s, key: s.name, site: site, filter: filter
= render 'schemas/filter', schema: s,
key: s.name,
site: site,
filter: filter

View file

@ -1,5 +1,4 @@
%tr.border-top.border-magenta %tr.border-top.border-magenta
%th.font-weight-normal.w-100.position-relative{ scope: 'row' } %th.font-weight-normal.w-100.position-relative{ scope: 'row' }
- if local_assigns[:parent_schema] - if local_assigns[:parent_schema]
%span.text-muted &mdash; %span.text-muted &mdash;
@ -9,5 +8,8 @@
- unless local_assigns[:parent_schema] - unless local_assigns[:parent_schema]
- schema.schemas.each do |s| - schema.schemas.each do |s|
- next if s.hidden? - next if s.hidden?
= render 'schemas/row', schema: s, site: site, filter: filter, parent_schema: schema
= render 'schemas/row', schema: s,
site: site,
filter: filter,
parent_schema: schema

View file

@ -8,7 +8,7 @@
- site.errors.messages.each_pair do |attr, error| - site.errors.messages.each_pair do |attr, error|
- attr = attr.to_s - attr = attr.to_s
- error.each do |e| - error.each do |e|
%li= link_to t('activerecord.attributes.site.' + attr) + ' ' + e, '#' + attr %li= link_to "#{t("activerecord.attributes.site.#{attr}")} #{e}", "##{attr}"
= form_for site, html: { class: form_class(site) } do |f| = form_for site, html: { class: form_class(site) } do |f|
- unless site.persisted? - unless site.persisted?
@ -83,6 +83,7 @@
%p.lead= t('.help.licencia') %p.lead= t('.help.licencia')
- Licencia.all.find_each do |licencia| - Licencia.all.find_each do |licencia|
- next if licencia.custom? && site.licencia != licencia - next if licencia.custom? && site.licencia != licencia
.row.license .row.license
.col .col
.media.mt-1 .media.mt-1
@ -99,7 +100,11 @@
tags: %w[p a strong em ul ol li h1 h2 h3 h4 h5 h6] tags: %w[p a strong em ul ol li h1 h2 h3 h4 h5 h6]
- unless licencia.custom? - unless licencia.custom?
= link_to t('.licencia.url'), licencia.url, target: '_blank', class: 'btn btn-secondary', rel: 'noopener' = link_to t('.licencia.url'),
licencia.url,
target: '_blank',
class: 'btn btn-secondary',
rel: 'noopener'
%hr/ %hr/

View file

@ -4,12 +4,17 @@
%form.mb-3{ action: site_posts_path } %form.mb-3{ action: site_posts_path }
- @filter_params.each do |param, values| - @filter_params.each do |param, values|
- next if param == :q - next if param == :q
- [values].flatten.each do |value| - [values].flatten.each do |value|
%input{ type: 'hidden', name: values.is_a?(Array) ? "#{param}[]" : param, value: value } %input{ type: 'hidden',
name: values.is_a?(Array) ? "#{param}[]" : param,
value: value }
.form-group.flex-grow-0.m-0 .form-group.flex-grow-0.m-0
%label.h3{for: 'q'}= t('posts.index.search') %label.h3{ for: 'q' }= t('posts.index.search')
.input-group .input-group
%input#q.form-control.border.border-magenta.border-right-0{ type: 'search', name: 'q', value: @filter_params[:q] } %input.form-control.border.border-magenta.border-right-0#q{ type: 'search',
name: 'q',
value: @filter_params[:q] }
.input-group-append .input-group-append
%span.input-group-text.background-white.magenta.border.border-magenta.border-top.border-left-0.border-right.border-bottom %span.input-group-text.background-white.magenta.border.border-magenta.border-top.border-left-0.border-right.border-bottom
%i.fa.fa-fw.fa-search %i.fa.fa-fw.fa-search

View file

@ -15,6 +15,7 @@
%tbody %tbody
- @sites.each do |site| - @sites.each do |site|
- next unless site.jekyll? - next unless site.jekyll?
%tr %tr
%td %td
%h2 %h2

View file

@ -11,10 +11,19 @@
%form.mb-5.form-inline{ method: 'get' } %form.mb-5.form-inline{ method: 'get' }
- Stat::INTERVALS.each do |interval| - Stat::INTERVALS.each do |interval|
= link_to t(".#{interval}"), site_stats_path(interval: interval, urls: params[:urls], period_start: params[:period_start].to_date.try(:"beginning_of_#{interval}").to_date, period_end: params[:period_end]), class: "mb-0 btn #{@interval == interval ? 'btn-primary active' : 'btn-secondary' }" = link_to t(".#{interval}"),
site_stats_path(interval: interval,
urls: params[:urls],
period_start: params[:period_start].to_date.try(:"beginning_of_#{interval}").to_date,
period_end: params[:period_end]),
class: "mb-0 btn #{@interval == interval ? 'btn-primary active' : 'btn-secondary'}"
%input.form-control{ type: 'date', name: :period_start, value: params[:period_start] } %input.form-control{ type: 'date',
%input.form-control{ type: 'date', name: :period_end, value: params[:period_end] } name: :period_start,
value: params[:period_start] }
%input.form-control{ type: 'date',
name: :period_end,
value: params[:period_end] }
%button.btn.btn-secondary.mb-0{ type: 'submit' }= t('.filter') %button.btn.btn-secondary.mb-0{ type: 'submit' }= t('.filter')
.mb-5 .mb-5
@ -22,17 +31,27 @@
%p.lead= t('.host.description') %p.lead= t('.host.description')
= line_chart site_stats_host_path(@chart_params), **@chart_options = line_chart site_stats_host_path(@chart_params), **@chart_options
#custom-urls.mb-5 .mb-5#custom-urls
%h2= t('.urls.title') %h2= t('.urls.title')
%p.lead= t('.urls.description') %p.lead= t('.urls.description')
%form{ method: 'get', action: '#custom-urls' } %form{ method: 'get', action: '#custom-urls' }
%input{ type: 'hidden', name: 'interval', value: @interval } %input{ type: 'hidden',
%input{ type: 'hidden', name: 'period_start', value: params[:period_start] } name: 'interval',
%input{ type: 'hidden', name: 'period_end', value: params[:period_end] } value: @interval }
%input{ type: 'hidden',
name: 'period_start',
value: params[:period_start] }
%input{ type: 'hidden',
name: 'period_end',
value: params[:period_end] }
.form-group .form-group
%label{ for: 'urls' }= t('.urls.label') %label{ for: 'urls' }= t('.urls.label')
%textarea#urls.form-control{ name: 'urls', autocomplete: 'on', required: true, rows: @normalized_urls.size + 1, aria_describedby: 'help-urls' }= @normalized_urls.join("\n") %textarea.form-control#urls{ name: 'urls',
%small#help-urls.feedback.form-text.text-muted= t('.urls.help') autocomplete: 'on',
required: true,
rows: @normalized_urls.size + 1,
aria_describedby: 'help-urls' }= @normalized_urls.join("\n")
%small.feedback.form-text.text-muted#help-urls= t('.urls.help')
.form-group .form-group
%button.btn.btn-secondary{ type: 'submit' }= t('.urls.submit') %button.btn.btn-secondary{ type: 'submit' }= t('.urls.submit')
- if @normalized_urls.present? - if @normalized_urls.present?
@ -41,6 +60,7 @@
.row.mb-5.row-cols-1.row-cols-lg-2 .row.mb-5.row-cols-1.row-cols-lg-2
- @columns.each_pair do |column, values| - @columns.each_pair do |column, values|
- next if values.blank? - next if values.blank?
.col.mb-5 .col.mb-5
%h2= t(".columns.#{column}.title") %h2= t(".columns.#{column}.title")
%p.lead= t(".columns.#{column}.description") %p.lead= t(".columns.#{column}.description")
@ -57,7 +77,8 @@
%tbody %tbody
- values.each_pair do |col, val| - values.each_pair do |col, val|
%tr %tr
%th{ scope: 'row', style: 'word-break: break-all' }= col.blank? ? t(".columns.#{column}.empty") : col %th.break-all{ scope: 'row' }
= col.blank? ? t(".columns.#{column}.empty") : col
%td= val %td= val
.mb-5 .mb-5
%h2= t('.resources.title') %h2= t('.resources.title')
@ -67,4 +88,5 @@
.mb-5 .mb-5
%h3= t(".resources.#{resource}.title") %h3= t(".resources.#{resource}.title")
%p.lead= t(".resources.#{resource}.description") %p.lead= t(".resources.#{resource}.description")
= line_chart site_stats_resources_path(resource: resource, **@chart_params), **@chart_options.merge(StatsController::EXTRA_OPTIONS[resource]) = line_chart site_stats_resources_path(resource: resource, **@chart_params),
**@chart_options.merge(StatsController::EXTRA_OPTIONS[resource])

View file

@ -9,8 +9,8 @@
.form-group .form-group
= f.label :invitaciones do = f.label :invitaciones do
= t('.invitaciones') = t('.invitaciones')
%small.text-muted.form-text= t('.help.invitaciones', %small.text-muted.form-text
invite_as: invite_as) = t('.help.invitaciones', invite_as: invite_as)
= f.text_area :invitaciones, class: 'form-control' = f.text_area :invitaciones, class: 'form-control'
.form-group .form-group
= f.submit t('.submit'), class: 'btn btn-secondary' = f.submit t('.submit'), class: 'btn btn-secondary'

View file

@ -46,7 +46,7 @@ Rails.application.configure do
# The :test delivery method accumulates sent emails in the # The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array. # ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test config.action_mailer.delivery_method = :test
config.action_mailer.default_options = { from: ENV['DEFAULT_FROM'] } config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', nil) }
config.action_mailer.default_url_options = { host: 'localhost', config.action_mailer.default_url_options = { host: 'localhost',
port: 3000 } port: 3000 }
@ -54,8 +54,8 @@ Rails.application.configure do
error_grouping: true, error_grouping: true,
email: { email: {
email_prefix: '', email_prefix: '',
sender_address: ENV['DEFAULT_FROM'], sender_address: ENV.fetch('DEFAULT_FROM', nil),
exception_recipients: ENV['EXCEPTION_TO'], exception_recipients: ENV.fetch('EXCEPTION_TO', nil),
normalize_subject: true normalize_subject: true
} }

View file

@ -167,9 +167,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest
end end
test 'si hay algún error se recupera' do test 'si hay algún error se recupera' do
File.open(File.join(@site.path, '_es', "#{Date.today}-#{SecureRandom.hex}.markdown"), 'w') do |f| File.write(File.join(@site.path, '_es', "#{Date.today}-#{SecureRandom.hex}.markdown"), '')
f.write ''
end
get site_posts_url(@site), headers: @authorization get site_posts_url(@site), headers: @authorization
end end