WIP editor de i18n funcionando
This commit is contained in:
parent
89c6339d1e
commit
4ff314046a
9 changed files with 182 additions and 22 deletions
|
@ -11,11 +11,22 @@ class I18nController < ApplicationController
|
|||
@site = find_site
|
||||
@lang_from = I18n.locale.to_s
|
||||
@lang_to = @site.config['i18n'].reject { |i| i == @lang_from }.sample
|
||||
@lang_to = 'es'
|
||||
@lang_to = 'ar'
|
||||
end
|
||||
|
||||
def update
|
||||
@site = find_site
|
||||
binding.pry
|
||||
# No usamos params porque nos obliga a hacer una lista blanca de
|
||||
# todos los parámetros que queremos, pero no tenemos forma aun de
|
||||
# pasarse a permit un array de todas las keys y sus tipos en base al
|
||||
# idioma que ya existe
|
||||
p = request.parameters[:i18n][:ar]
|
||||
i = JekyllI18n.new(site: @site, lang: :ar, attributes: p)
|
||||
|
||||
if i.save
|
||||
redirect_to site_path(@site)
|
||||
else
|
||||
render 'i18n/edit'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
88
app/models/jekyll_i18n.rb
Normal file
88
app/models/jekyll_i18n.rb
Normal file
|
@ -0,0 +1,88 @@
|
|||
# Esta clase se encarga de guardan los archivos YAML de idiomas
|
||||
#
|
||||
# TODO se podría convertir en una clase genérica de editor de YAML
|
||||
class JekyllI18n
|
||||
attr_reader :site, :lang, :attributes
|
||||
|
||||
def initialize(site:, lang:, attributes:)
|
||||
unless site.is_a? Site
|
||||
raise ArgumentError, I18n.t('errors.argument_error', argument: :site, class: Site)
|
||||
end
|
||||
|
||||
unless I18n.available_locales.include? lang.to_sym
|
||||
raise ArgumentError, I18n.t('errors.unknown_locale', locale: lang)
|
||||
end
|
||||
|
||||
@site = site
|
||||
@lang = lang.to_sym
|
||||
@attributes = attributes.to_hash # porque enviamos parametros
|
||||
end
|
||||
|
||||
# Vuelva los datos a YAML y los guarda en el archivo correspondiente
|
||||
#
|
||||
# TODO es necesario evitar que se agreguen llaves nuevas? No veo nada
|
||||
# inseguro...
|
||||
#
|
||||
# https://codeclimate.com/blog/rails-remote-code-execution-vulnerability-explained/
|
||||
#
|
||||
# Pero parece que ya se resolvió hace rato. En general es mala
|
||||
# práctica aceptar cualquier input de las usuarias, pero en este caso
|
||||
# parece que no nos tenemos que preocupar?
|
||||
#
|
||||
# En cualquier caso habría que hacer un deep_merge, descartando las
|
||||
# llaves que no están en el original y/o en el destino (pero queremos
|
||||
# que el destino se parezca al original) y chequeando que no haya
|
||||
# deserialización de objetos
|
||||
#
|
||||
# Jekyll usa SafeYAML para cargar los datos, con lo que estaríamos
|
||||
# bien en cuanto a inyección de código.
|
||||
def save
|
||||
# Reemplaza los datos en el sitio
|
||||
replace_lang_in_site
|
||||
# Escribe los cambios en disco
|
||||
write
|
||||
end
|
||||
|
||||
# Obtiene la ruta a partir del sitio y el idioma
|
||||
def path
|
||||
File.join(@site.path, '_data', "#{@lang.to_s}.yml")
|
||||
end
|
||||
|
||||
def exist?
|
||||
File.exist? path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def replace_lang_in_site
|
||||
@site.data[@lang.to_s] = @attributes
|
||||
end
|
||||
|
||||
# Escribe los cambios en disco
|
||||
#
|
||||
# TODO unificar con Post.write
|
||||
def write
|
||||
r = File.open(path, File::RDWR | File::CREAT, 0o640) do |f|
|
||||
# Bloquear el archivo para que no sea accedido por otro
|
||||
# proceso u otra editora
|
||||
f.flock(File::LOCK_EX)
|
||||
|
||||
# Empezar por el principio
|
||||
f.rewind
|
||||
|
||||
# Escribir
|
||||
f.write(content)
|
||||
|
||||
# Eliminar el resto
|
||||
f.flush
|
||||
f.truncate(f.pos)
|
||||
end
|
||||
|
||||
return true if r.zero?
|
||||
false
|
||||
end
|
||||
|
||||
def content
|
||||
@attributes.to_yaml
|
||||
end
|
||||
end
|
|
@ -22,9 +22,21 @@ class Site
|
|||
end
|
||||
alias :to_s :id
|
||||
|
||||
def read
|
||||
@jekyll.read
|
||||
end
|
||||
|
||||
# Fuerza relectura del sitio, eliminando el sitio actual en favor de
|
||||
# un sitio nuevo, leído desde cero
|
||||
def read!
|
||||
@jekyll = Site.load_jekyll(@jekyll.path)
|
||||
|
||||
@jekyll.read
|
||||
end
|
||||
|
||||
def data
|
||||
if @jekyll.data.empty?
|
||||
@jekyll.read
|
||||
read
|
||||
Rails.logger.info 'Leyendo data'
|
||||
end
|
||||
|
||||
|
@ -33,7 +45,7 @@ class Site
|
|||
|
||||
def config
|
||||
if @jekyll.config.empty?
|
||||
@jekyll.read
|
||||
read
|
||||
Rails.logger.info 'Leyendo config'
|
||||
end
|
||||
|
||||
|
@ -95,6 +107,17 @@ class Site
|
|||
File.directory?(dir) && File.exist?(File.join(dir, '_config.yml'))
|
||||
end
|
||||
|
||||
def self.load_jekyll(path)
|
||||
config = ::Jekyll.configuration('source' => path)
|
||||
|
||||
# No necesitamos cargar plugins en este momento
|
||||
%w[plugins gems].each do |unneeded|
|
||||
config[unneeded] = [] if config.key? unneeded
|
||||
end
|
||||
|
||||
Jekyll::Site.new(config)
|
||||
end
|
||||
|
||||
# Obtener todos los directorios de sitios asociados a esta usuaria
|
||||
def self.all_for(usuaria)
|
||||
@sites ||= Pathname.new(Site.site_path_for(usuaria))
|
||||
|
@ -103,14 +126,8 @@ class Site
|
|||
next unless Site.jekyll? j
|
||||
|
||||
Dir.chdir(j) do
|
||||
config = ::Jekyll.configuration('source' => Dir.pwd)
|
||||
|
||||
# No necesitamos cargar plugins en este momento
|
||||
%w[plugins gems].each do |unneeded|
|
||||
config[unneeded] = [] if config.key? unneeded
|
||||
end
|
||||
|
||||
Site.new(jekyll: ::Jekyll::Site.new(config), path: j)
|
||||
jekyll = Site.load_jekyll(Dir.pwd)
|
||||
Site.new(jekyll: jekyll, path: j)
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
|
|
@ -1,15 +1,34 @@
|
|||
- dir = 'ltr'
|
||||
- if @lang_to == 'ar'
|
||||
- dir = 'rtl'
|
||||
.form-group
|
||||
- key = keys.pop
|
||||
- form_keys = keys.map { |k| "[#{k.to_s}]" }.join('')
|
||||
-# si la key es numerica, queremos un array de hashes, no un hash de
|
||||
-# hashes con keys numericas
|
||||
- form_keys = keys.map do |k|
|
||||
- if k.is_a? Integer
|
||||
- '[]'
|
||||
- else
|
||||
- "[#{k.to_s}]"
|
||||
- form_keys = form_keys.join('')
|
||||
- form_help = (keys.size > 0) ? [keys,key].flatten.join('.') : key
|
||||
- value_to = @site.data[@lang_to]
|
||||
-# recorrer el hash hasta obtener el valor original
|
||||
- [keys,key].flatten.each do |k|
|
||||
- if value_to.nil?
|
||||
- value_to = ''
|
||||
- break
|
||||
- value_to = value_to[k]
|
||||
= label_tag "i18n[#{@lang_to}]#{form_keys}", value
|
||||
-# no especificar el id en una key numerica para que no se genere un
|
||||
-# hash en lugar de un array de valores
|
||||
- key = '' if key.is_a? Integer
|
||||
= label_tag "i18n[#{@lang_to}]#{form_keys}[#{key}]", value
|
||||
-# creamos un campo a mano porque los helpers de niveles mas altos
|
||||
-# quieren hacer magia con los ids y fallan
|
||||
- if value.length > 140
|
||||
= text_area "i18n[#{@lang_to}]#{form_keys}", key, value: value_to,
|
||||
class: 'form-control'
|
||||
= text_area_tag "i18n[#{@lang_to}]#{form_keys}[#{key}]", value_to,
|
||||
class: "form-control #{dir}"
|
||||
- else
|
||||
= text_field "i18n[#{@lang_to}]#{form_keys}", key, value: value_to,
|
||||
class: 'form-control'
|
||||
= text_field_tag "i18n[#{@lang_to}]#{form_keys}[#{key}]", value_to,
|
||||
class: "form-control #{dir}"
|
||||
%small.text-muted.form-text= form_help
|
||||
|
|
22
config/deploy/production.rb
Normal file
22
config/deploy/production.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
set :deploy_user, 'app'
|
||||
set :deploy_to, '/srv/http/sutty.kefir.red'
|
||||
set :branch, 'rails'
|
||||
set :rack_env, 'production'
|
||||
|
||||
set :tmp_dir, "#{fetch :deploy_to}/tmp"
|
||||
set :bundle_path, '/srv/http/gems.kefir.red'
|
||||
|
||||
set :rbenv_type, :user
|
||||
set :rbenv_ruby, '2.3.6'
|
||||
|
||||
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
|
||||
set :rbenv_map_bins, %w(rake gem bundle ruby rails)
|
||||
set :rbenv_roles, :all # default value
|
||||
|
||||
# Evitar compilar nokogiri
|
||||
set :bundle_env_variables, nokogiri_use_system_libraries: 1
|
||||
|
||||
server 'miso',
|
||||
user: fetch(:deploy_user),
|
||||
roles: %w(app web db)
|
|
@ -66,9 +66,6 @@ Rails.application.configure do
|
|||
# the I18n.default_locale when a translation cannot be found).
|
||||
config.i18n.fallbacks = true
|
||||
|
||||
config.i18n.available_locales = [:es, :en]
|
||||
config.i18n.default_locale = :es
|
||||
|
||||
# Send deprecation notices to registered listeners.
|
||||
config.active_support.deprecation = :notify
|
||||
|
||||
|
|
4
config/initializers/locale.rb
Normal file
4
config/initializers/locale.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
Rails.application.configure do
|
||||
config.i18n.available_locales = [:es, :en, :ar]
|
||||
config.i18n.default_locale = :es
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
en:
|
||||
errors:
|
||||
argument_error: 'Argument `%{argument}` must be an instance of %{class}'
|
||||
unknown_locale: 'Unknown %{locale} locale'
|
||||
login:
|
||||
email: 'E-mail'
|
||||
password: 'Password'
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
es:
|
||||
errors:
|
||||
argument_error: 'El argumento `%{argument}` debe ser una instancia de %{class}'
|
||||
unknown_locale: 'El idioma %{locale} es desconocido'
|
||||
login:
|
||||
email: 'Dirección de correo'
|
||||
password: 'Contraseña'
|
||||
|
|
Loading…
Reference in a new issue