mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-29 15:06:22 +00:00
WIP: vistas para trabajar con artículos
This commit is contained in:
parent
f65a7f5fe2
commit
9cb877c5aa
31 changed files with 321 additions and 401 deletions
|
@ -226,7 +226,7 @@ GEM
|
||||||
net-ssh (5.2.0)
|
net-ssh (5.2.0)
|
||||||
netaddr (2.0.3)
|
netaddr (2.0.3)
|
||||||
nio4r (2.3.1)
|
nio4r (2.3.1)
|
||||||
nokogiri (1.10.3)
|
nokogiri (1.10.4)
|
||||||
mini_portile2 (~> 2.4.0)
|
mini_portile2 (~> 2.4.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
parallel (1.17.0)
|
parallel (1.17.0)
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
$(document).on('turbolinks:load', function() {
|
|
||||||
var md = window.markdownit();
|
|
||||||
$('#post_content').markdown({ parser: md.render.bind(md) });
|
|
||||||
});
|
|
|
@ -1,4 +1,5 @@
|
||||||
$(document).on('turbolinks:load', function() {
|
$(document).on('turbolinks:load', function() {
|
||||||
|
// Previene el envío del formulario al presionar <Enter>
|
||||||
$(document).on('keypress', '.post :input:not(textarea):not([type=submit])', function(e) {
|
$(document).on('keypress', '.post :input:not(textarea):not([type=submit])', function(e) {
|
||||||
if (e.keyCode == 13) {
|
if (e.keyCode == 13) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -6,6 +7,7 @@ $(document).on('turbolinks:load', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Al enviar el formulario del artículo, aplicar la validación
|
||||||
$('.submit-post').click(function(e) {
|
$('.submit-post').click(function(e) {
|
||||||
var form = $(this).parents('form.form');
|
var form = $(this).parents('form.form');
|
||||||
var invalid_help = $('.invalid_help');
|
var invalid_help = $('.invalid_help');
|
||||||
|
@ -13,11 +15,22 @@ $(document).on('turbolinks:load', function() {
|
||||||
|
|
||||||
invalid_help.addClass('d-none');
|
invalid_help.addClass('d-none');
|
||||||
sending_help.addClass('d-none');
|
sending_help.addClass('d-none');
|
||||||
|
form.find('[aria-invalid="true"]')
|
||||||
|
.attr('aria-invalid', false)
|
||||||
|
.attr('aria-describedby', function() {
|
||||||
|
return $(this).siblings('.feedback').attr('id');
|
||||||
|
});
|
||||||
|
|
||||||
if (form[0].checkValidity() === false) {
|
if (form[0].checkValidity() === false) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
invalid_help.removeClass('d-none');
|
invalid_help.removeClass('d-none');
|
||||||
|
|
||||||
|
form.find(':invalid')
|
||||||
|
.attr('aria-invalid', true)
|
||||||
|
.attr('aria-describedby', function() {
|
||||||
|
return $(this).siblings('.invalid-feedback').attr('id');
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
sending_help.removeClass('d-none');
|
sending_help.removeClass('d-none');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
// TODO: Encontrar la forma de generar esto desde los locales de Rails
|
||||||
|
$custom-file-text: (
|
||||||
|
en: 'Browse',
|
||||||
|
es: 'Buscar archivo'
|
||||||
|
);
|
||||||
|
|
||||||
@import "bootstrap";
|
@import "bootstrap";
|
||||||
@import "bootstrap-markdown/css/bootstrap-markdown.min";
|
@import "bootstrap-markdown/css/bootstrap-markdown.min";
|
||||||
@import "font-awesome";
|
@import "font-awesome";
|
||||||
|
|
|
@ -8,10 +8,11 @@ class PostsController < ApplicationController
|
||||||
def index
|
def index
|
||||||
authorize Post
|
authorize Post
|
||||||
@site = find_site
|
@site = find_site
|
||||||
@lang = find_lang(@site)
|
# TODO: por qué no lo está leyendo @site.posts?
|
||||||
|
@site.read
|
||||||
@category = session[:category] = params.dig(:category)
|
@category = session[:category] = params.dig(:category)
|
||||||
@posts = policy_scope(@site.posts_for(@lang),
|
# TODO: Aplicar policy_scope
|
||||||
policy_scope_class: PostPolicy::Scope)
|
@posts = @site.posts(lang: I18n.locale)
|
||||||
|
|
||||||
if params[:sort_by].present?
|
if params[:sort_by].present?
|
||||||
begin
|
begin
|
||||||
|
@ -33,12 +34,8 @@ class PostsController < ApplicationController
|
||||||
def new
|
def new
|
||||||
authorize Post
|
authorize Post
|
||||||
@site = find_site
|
@site = find_site
|
||||||
@lang = find_lang(@site)
|
# TODO: Implementar layout
|
||||||
@template = find_template(@site)
|
@post = @site.posts.build(lang: I18n.locale)
|
||||||
@post = Post.new(site: @site,
|
|
||||||
front_matter: { date: Time.now },
|
|
||||||
lang: @lang,
|
|
||||||
template: @template)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -2,26 +2,23 @@
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
# Devuelve el atributo name de un campo posiblemente anidado
|
# Devuelve el atributo name de un campo anidado en el formato que
|
||||||
def field_name_for_post(names)
|
# esperan los helpers *_field
|
||||||
return ['post', names] if names.is_a? String
|
#
|
||||||
|
# [ 'post', :image, :description ]
|
||||||
names = names.dup
|
# [ 'post[image]', :description ]
|
||||||
root = 'post'
|
# 'post[image][description]'
|
||||||
|
def field_name_for(*names)
|
||||||
name = names.pop
|
name = names.pop
|
||||||
|
root = names.shift
|
||||||
|
|
||||||
names.each do |n|
|
names.each do |n|
|
||||||
root = "#{root}[#{n}]"
|
root += "[#{n}]"
|
||||||
end
|
end
|
||||||
|
|
||||||
[root, name]
|
[root, name]
|
||||||
end
|
end
|
||||||
|
|
||||||
def field_name_for_post_as_string(names)
|
|
||||||
f = field_name_for_post(names)
|
|
||||||
|
|
||||||
"#{f.first}[#{f.last}]"
|
|
||||||
end
|
|
||||||
|
|
||||||
def distance_of_time_in_words_if_more_than_a_minute(seconds)
|
def distance_of_time_in_words_if_more_than_a_minute(seconds)
|
||||||
if seconds > 60
|
if seconds > 60
|
||||||
distance_of_time_in_words seconds
|
distance_of_time_in_words seconds
|
||||||
|
@ -49,4 +46,53 @@ module ApplicationHelper
|
||||||
def form_class(model)
|
def form_class(model)
|
||||||
model.errors.messages.empty? ? 'needs-validation' : 'was-validated'
|
model.errors.messages.empty? ? 'needs-validation' : 'was-validated'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Opciones por defecto para el campo de un formulario
|
||||||
|
def field_options(attribute, metadata)
|
||||||
|
{
|
||||||
|
class: 'form-control',
|
||||||
|
required: metadata.required,
|
||||||
|
aria: {
|
||||||
|
describedby: id_for_help(attribute),
|
||||||
|
required: metadata.required
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Devuelve la clase is-invalid si el campo tiene un error
|
||||||
|
def invalid(post, attribute)
|
||||||
|
'is-invalid' if post.errors[attribute].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Busca la traducción de una etiqueta en los metadatos de un post
|
||||||
|
def post_label_t(*attribute, post:)
|
||||||
|
label = post_t(*attribute, post: post, type: :label)
|
||||||
|
|
||||||
|
if post.send(attribute.first).required
|
||||||
|
label += I18n.t('posts.attributes.required.label')
|
||||||
|
end
|
||||||
|
|
||||||
|
label
|
||||||
|
end
|
||||||
|
|
||||||
|
def post_help_t(*attribute, post:)
|
||||||
|
post_t(*attribute, post: post, type: :help)
|
||||||
|
end
|
||||||
|
|
||||||
|
def id_for_help(*attribute)
|
||||||
|
"#{attribute.join('-')}-help"
|
||||||
|
end
|
||||||
|
|
||||||
|
def id_for_feedback(*attribute)
|
||||||
|
"#{attribute.join('-')}-feedback"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def post_t(*attribute, post:, type:)
|
||||||
|
post.layout.metadata.dig(*attribute, type.to_s, I18n.locale.to_s) ||
|
||||||
|
post.layout.metadata.dig(*attribute,
|
||||||
|
type.to_s, I18n.default_locale.to_s) ||
|
||||||
|
I18n.t("posts.attributes.#{attribute.join('.')}.#{type}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
34
app/models/metadata_content.rb
Normal file
34
app/models/metadata_content.rb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Se encarga del contenido del artículo y quizás otros campos que
|
||||||
|
# requieran texto largo.
|
||||||
|
class MetadataContent < MetadataTemplate
|
||||||
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
|
||||||
|
def default_value
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
def value
|
||||||
|
sanitize(self[:value] || document.content || default_value,
|
||||||
|
sanitize_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Etiquetas y atributos HTML a permitir
|
||||||
|
#
|
||||||
|
# No queremos permitir mucho más que cosas de las que nos falten en
|
||||||
|
# CommonMark.
|
||||||
|
#
|
||||||
|
# TODO: Permitir una lista de atributos y etiquetas en el Layout
|
||||||
|
#
|
||||||
|
# XXX: Vamos a generar un reproductor de video/audio directamente
|
||||||
|
# desde un plugin de Jekyll
|
||||||
|
def sanitize_options
|
||||||
|
{
|
||||||
|
tags: %w[span],
|
||||||
|
attributes: %w[title class lang]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,4 @@
|
||||||
class MetadataDate < MetadataTemplate
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class MetadataDate < MetadataTemplate
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
class MetadataImage < MetadataTemplate
|
class MetadataImage < MetadataTemplate
|
||||||
# Una ruta vacía a la imagen con una descripción vacía
|
# Una ruta vacía a la imagen con una descripción vacía
|
||||||
def default_value
|
def default_value
|
||||||
{ path: '', description: '' }
|
{ 'path' => '', 'description' => '' }
|
||||||
end
|
end
|
||||||
|
|
||||||
def empty?
|
def empty?
|
||||||
|
|
|
@ -17,6 +17,10 @@ class MetadataPath < MetadataTemplate
|
||||||
Pathname.new(value).relative_path_from(Pathname.new(site.path)).to_s
|
Pathname.new(value).relative_path_from(Pathname.new(site.path)).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def basename
|
||||||
|
File.basename(value, ext)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def ext
|
def ext
|
||||||
|
|
|
@ -6,4 +6,8 @@ class MetadataString < MetadataTemplate
|
||||||
def default_value
|
def default_value
|
||||||
''
|
''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def value
|
||||||
|
super.strip
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,10 +10,10 @@ require 'jekyll/utils'
|
||||||
# rubocop:disable Style/MissingRespondToMissing
|
# rubocop:disable Style/MissingRespondToMissing
|
||||||
class Post < OpenStruct
|
class Post < OpenStruct
|
||||||
# Atributos por defecto
|
# Atributos por defecto
|
||||||
# XXX: Volver document opcional cuando estemos creando
|
|
||||||
DEFAULT_ATTRIBUTES = %i[site document layout].freeze
|
DEFAULT_ATTRIBUTES = %i[site document layout].freeze
|
||||||
# Otros atributos que no vienen en los metadatos
|
# Otros atributos que no vienen en los metadatos
|
||||||
ATTRIBUTES = %i[content lang path date slug attributes errors].freeze
|
PRIVATE_ATTRIBUTES = %i[lang path slug attributes errors].freeze
|
||||||
|
PUBLIC_ATTRIBUTES = %i[date].freeze
|
||||||
|
|
||||||
# Redefinir el inicializador de OpenStruct
|
# Redefinir el inicializador de OpenStruct
|
||||||
#
|
#
|
||||||
|
@ -27,14 +27,7 @@ class Post < OpenStruct
|
||||||
super(args)
|
super(args)
|
||||||
|
|
||||||
# Genera un método con todos los atributos disponibles
|
# Genera un método con todos los atributos disponibles
|
||||||
self.attributes = DEFAULT_ATTRIBUTES +
|
self.attributes = layout.metadata.keys.map(&:to_sym) + PUBLIC_ATTRIBUTES
|
||||||
ATTRIBUTES +
|
|
||||||
layout.metadata.keys.map(&:to_sym)
|
|
||||||
|
|
||||||
# El contenido
|
|
||||||
# TODO: Mover a su propia clase para poder hacer limpiezas
|
|
||||||
# independientemente
|
|
||||||
self.content = document.content
|
|
||||||
self.errors = {}
|
self.errors = {}
|
||||||
|
|
||||||
# Genera un atributo por cada uno de los campos de la plantilla,
|
# Genera un atributo por cada uno de los campos de la plantilla,
|
||||||
|
@ -63,6 +56,10 @@ class Post < OpenStruct
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/AbcSize
|
# rubocop:enable Metrics/AbcSize
|
||||||
|
|
||||||
|
def id
|
||||||
|
path.basename
|
||||||
|
end
|
||||||
|
|
||||||
# Levanta un error si al construir el artículo no pasamos un atributo.
|
# Levanta un error si al construir el artículo no pasamos un atributo.
|
||||||
def default_attributes_missing(**args)
|
def default_attributes_missing(**args)
|
||||||
DEFAULT_ATTRIBUTES.each do |attr|
|
DEFAULT_ATTRIBUTES.each do |attr|
|
||||||
|
@ -100,10 +97,11 @@ class Post < OpenStruct
|
||||||
# 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
|
||||||
# plantilla
|
# plantilla
|
||||||
def attribute?(mid)
|
def attribute?(mid)
|
||||||
|
attrs = DEFAULT_ATTRIBUTES + PRIVATE_ATTRIBUTES + PUBLIC_ATTRIBUTES
|
||||||
if singleton_class.method_defined? :attributes
|
if singleton_class.method_defined? :attributes
|
||||||
attributes.include? attribute_name(mid)
|
(attrs + attributes).include? attribute_name(mid)
|
||||||
else
|
else
|
||||||
(DEFAULT_ATTRIBUTES + ATTRIBUTES).include? attribute_name(mid)
|
attrs.include? attribute_name(mid)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -119,7 +117,7 @@ class Post < OpenStruct
|
||||||
# Asegurarse que haya un layout
|
# Asegurarse que haya un layout
|
||||||
yaml['layout'] = layout.name.to_s
|
yaml['layout'] = layout.name.to_s
|
||||||
|
|
||||||
"#{yaml.to_yaml}---\n\n#{content}"
|
"#{yaml.to_yaml}---\n\n#{content.value}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Eliminar el artículo del repositorio y de la lista de artículos del
|
# Eliminar el artículo del repositorio y de la lista de artículos del
|
||||||
|
|
|
@ -32,10 +32,10 @@ class PostRelation < Array
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def build_layout(layout = :post)
|
def build_layout(layout = nil)
|
||||||
return layout if layout.is_a? Layout
|
return layout if layout.is_a? Layout
|
||||||
|
|
||||||
site.layouts[layout]
|
site.layouts[layout || :post]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Devuelve una colección Jekyll que hace pasar el documento
|
# Devuelve una colección Jekyll que hace pasar el documento
|
||||||
|
|
|
@ -162,7 +162,7 @@ class Site < ApplicationRecord
|
||||||
@layouts ||= data.fetch('layouts', {}).map do |name, metadata|
|
@layouts ||= data.fetch('layouts', {}).map do |name, metadata|
|
||||||
{ name.to_sym => Layout.new(site: self,
|
{ name.to_sym => Layout.new(site: self,
|
||||||
name: name.to_sym,
|
name: name.to_sym,
|
||||||
metadata: metadata) }
|
metadata: metadata.with_indifferent_access) }
|
||||||
end.inject(:merge)
|
end.inject(:merge)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,14 @@ class PostPolicy
|
||||||
# Las usuarias pueden ver todos los posts
|
# Las usuarias pueden ver todos los posts
|
||||||
#
|
#
|
||||||
# Les invitades solo pueden ver sus propios posts
|
# Les invitades solo pueden ver sus propios posts
|
||||||
|
#
|
||||||
|
# TODO: Arreglar
|
||||||
def resolve
|
def resolve
|
||||||
return scope if scope.try(:first).try(:site).try(:usuarie?, usuarie)
|
return scope if scope.try(:first).try(:site).try(:usuarie?, usuarie)
|
||||||
|
|
||||||
# Asegurarse que al menos devolvemos []
|
# Asegurarse que al menos devolvemos []
|
||||||
[scope.find do |post|
|
[scope.find do |post|
|
||||||
post.author == usuarie.email
|
post.author.value == usuarie.email
|
||||||
end].flatten.compact
|
end].flatten.compact
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
!!!
|
!!!
|
||||||
%html
|
%html{ lang: I18n.locale, dir: t('dir') }
|
||||||
%head
|
%head
|
||||||
%meta{ content: 'text/html; charset=UTF-8',
|
%meta{ content: 'text/html; charset=UTF-8',
|
||||||
'http-equiv': 'Content-Type' }/
|
'http-equiv': 'Content-Type' }/
|
||||||
|
@ -11,10 +11,7 @@
|
||||||
= javascript_include_tag 'application',
|
= javascript_include_tag 'application',
|
||||||
'data-turbolinks-track': 'reload'
|
'data-turbolinks-track': 'reload'
|
||||||
|
|
||||||
- if @site.try(:persisted?) && @site.try(:config).try(:dig, 'css')
|
-# TODO: Reimplementar get_url_from_site
|
||||||
%link{ rel: 'stylesheet',
|
|
||||||
type: 'text/css',
|
|
||||||
href: @site.get_url_from_site(@site.config.dig('css')) }
|
|
||||||
|
|
||||||
- style = "background-image: url(#{@site.try(:cover)})"
|
- style = "background-image: url(#{@site.try(:cover)})"
|
||||||
-# haml-lint:disable InlineStyles
|
-# haml-lint:disable InlineStyles
|
||||||
|
|
5
app/views/posts/_attribute_feedback.haml
Normal file
5
app/views/posts/_attribute_feedback.haml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
%small.feedback.form-text.text-muted{ id: id_for_help(*attribute) }
|
||||||
|
= post_help_t(*attribute, post: post)
|
||||||
|
- if metadata.required
|
||||||
|
.invalid-feedback{ id: id_for_feedback(*attribute) }
|
||||||
|
= t('posts.attributes.required.feedback')
|
|
@ -1,144 +1,34 @@
|
||||||
- unless @post.errors.empty?
|
- unless post.errors.empty?
|
||||||
.alert.alert-danger
|
.alert.alert-danger
|
||||||
%ul
|
%ul
|
||||||
- @post.errors.each do |key, error|
|
- post.errors.each do |key, error|
|
||||||
%li
|
%li
|
||||||
%strong= @post.template_fields.find { |tf| tf.key == key.to_s }.try(:label) || key
|
%strong
|
||||||
|
= key.capitalize
|
||||||
= [error].flatten.join("\n")
|
= [error].flatten.join("\n")
|
||||||
|
|
||||||
-# TODO seleccionar la dirección por defecto según el idioma actual
|
-# TODO: habilitar form_for
|
||||||
- direction = @post.get_front_matter('dir') || 'ltr'
|
:ruby
|
||||||
-# string para configurar la clase con direccion de texto
|
if post.new?
|
||||||
- field_class = "form-control #{direction}"
|
url = site_posts_path(site)
|
||||||
-# TODO habilitar form_for
|
method = :post
|
||||||
- if @post.new?
|
else
|
||||||
- url = site_posts_path(@site, lang: @lang)
|
url = site_post_path(site, post)
|
||||||
- method = :post
|
method = :patch
|
||||||
- else
|
end
|
||||||
- url = site_post_path(@site, @post, lang: @lang)
|
|
||||||
- method = :patch
|
|
||||||
- if pre = @post.template.try(:get_front_matter, 'pre')
|
|
||||||
= render 'layouts/help', help: CommonMarker.render_doc(pre).to_html
|
|
||||||
= form_tag url,
|
|
||||||
method: method,
|
|
||||||
class: "form post #{@invalid ? 'was-validated' : ''}",
|
|
||||||
novalidate: true,
|
|
||||||
multipart: true do
|
|
||||||
|
|
||||||
= hidden_field_tag 'template', params[:template]
|
-# Comienza el formulario
|
||||||
.form-group
|
= form_tag url, method: method, class: 'form post', multipart: true do
|
||||||
= submit_tag t('posts.save'), class: 'btn btn-success submit-post'
|
|
||||||
= submit_tag t('posts.save_incomplete'), class: 'btn btn-info submit-post-incomplete', name: 'commit_incomplete'
|
-# Botones de guardado
|
||||||
.invalid_help.alert.alert-danger.d-none= @site.config.dig('invalid_help') || t('posts.invalid_help')
|
= render 'posts/submit', site: site
|
||||||
.sending_help.alert.alert-success.d-none= @site.config.dig('sending_help') || t('posts.sending_help')
|
|
||||||
- if @site.usuarie? current_user
|
-# Dibuja cada atributo
|
||||||
.form-group
|
- post.attributes.each do |attribute|
|
||||||
= label_tag 'post_author', t('posts.author')
|
- type = post.send(attribute).type
|
||||||
- todxs = (@site.usuaries + @site.invitades).compact.uniq.map(&:email)
|
= render "posts/attributes/#{type}",
|
||||||
= select_tag 'post[author]',
|
post: post, attribute: attribute,
|
||||||
options_for_select(todxs, @post.author || current_user.email),
|
metadata: post.send(attribute)
|
||||||
{ class: 'form-control select2',
|
|
||||||
data: { tags: true,
|
-# Botones de guardado
|
||||||
placeholder: t('posts.select.placeholder'),
|
= render 'posts/submit', site: site
|
||||||
'allow-clear': true } }
|
|
||||||
%small.text-muted.form-text= t('posts.author_help')
|
|
||||||
- if @post.has_field? :dir
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_dir', t('posts.dir')
|
|
||||||
= select_tag 'post[dir]',
|
|
||||||
options_for_select([[t('posts.ltr'), 'ltr'], [t('posts.rtl'), 'rtl']], direction),
|
|
||||||
{ class: 'form-control' }
|
|
||||||
%small.text-muted.form-text= t('posts.dir_help')
|
|
||||||
- if @post.has_field? :title
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_title', t('posts.title')
|
|
||||||
= text_field 'post', 'title', value: @post.title, class: field_class, required: true
|
|
||||||
- if @post.content?
|
|
||||||
.form-group{class: direction}
|
|
||||||
= label_tag 'post_content', t('posts.content')
|
|
||||||
= render 'layouts/help', help: [ t('help.markdown.intro'),
|
|
||||||
t('help.distraction_free_html'),
|
|
||||||
t('help.preview_html') ]
|
|
||||||
= text_area_tag 'post[content]', @post.content,
|
|
||||||
class: 'post-content'
|
|
||||||
- if @post.has_field? :date
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_date', t('posts.date')
|
|
||||||
= date_field 'post', 'date', value: @post.date.try(:strftime, '%F'),
|
|
||||||
class: 'form-control'
|
|
||||||
%small.text-muted.form-text= t('posts.date_help')
|
|
||||||
= render 'layouts/help', help: t('help.autocomplete_html')
|
|
||||||
- if @post.has_field? :categories
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_categories', t('posts.categories')
|
|
||||||
= select_tag 'post[categories][]',
|
|
||||||
options_for_select(@site.categories(lang: @lang), @post.categories),
|
|
||||||
{ class: 'form-control select2', multiple: 'multiple',
|
|
||||||
data: { tags: true,
|
|
||||||
placeholder: t('posts.select.placeholder'),
|
|
||||||
'allow-clear': true } }
|
|
||||||
- if @post.has_field? :tags
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_tags', t('posts.tags')
|
|
||||||
= select_tag 'post[tags][]',
|
|
||||||
options_for_select(@site.tags(lang: @lang), @post.tags),
|
|
||||||
{ class: 'form-control select2', multiple: 'multiple',
|
|
||||||
data: { tags: true,
|
|
||||||
placeholder: t('posts.select.placeholder'),
|
|
||||||
'allow-clear': true } }
|
|
||||||
- if @post.has_field? :slug
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_slug', t('posts.slug')
|
|
||||||
= text_field 'post', 'slug', value: @post.slug,
|
|
||||||
class: 'form-control'
|
|
||||||
%small.text-muted.form-text= t('posts.slug_help')
|
|
||||||
- if @post.has_field? :permalink
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_permalink', t('posts.permalink')
|
|
||||||
= text_field 'post', 'permalink', value: @post.get_front_matter('permalink'),
|
|
||||||
class: 'form-control'
|
|
||||||
%small.text-muted.form-text= t('posts.permalink_help')
|
|
||||||
- if @post.has_field? :layout
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_layout', t('posts.layout')
|
|
||||||
= select_tag 'post[layout]',
|
|
||||||
options_for_select(@site.layouts, @post.get_front_matter('layout')),
|
|
||||||
{ class: 'form-control select2' }
|
|
||||||
%small.text-muted.form-text= t('posts.layout_help')
|
|
||||||
- if @site.i18n?
|
|
||||||
- @site.translations.each do |lang|
|
|
||||||
- next if lang == @lang
|
|
||||||
.form-group
|
|
||||||
= label_tag 'post_lang', t("posts.lang.#{lang}")
|
|
||||||
= select_tag "post[lang][#{lang}]",
|
|
||||||
options_for_select(@site.posts_for(lang).map { |p| [p.title, p.id] },
|
|
||||||
@post.get_front_matter('lang').try(:dig, lang)),
|
|
||||||
{ class: 'form-control select2' }
|
|
||||||
%small.text-muted.form-text= t('posts.lang_help')
|
|
||||||
-# Genera todos los campos de la plantilla
|
|
||||||
- @post.template_fields.each do |template|
|
|
||||||
- next unless type = template.type
|
|
||||||
- if template.title.present?
|
|
||||||
%h1{id: template.title.tr(' ', '_').titleize}= template.title
|
|
||||||
- if template.subtitle.present?
|
|
||||||
%p= template.subtitle
|
|
||||||
- value = @post.new? ? template.values : @post.get_front_matter(template.key)
|
|
||||||
.form-group
|
|
||||||
= label_tag "post_#{template}", id: template do
|
|
||||||
= link_to '#' + template.key, class: 'text-muted',
|
|
||||||
data: { turbolinks: 'false' } do
|
|
||||||
= fa_icon 'link', title: t('posts.anchor')
|
|
||||||
- if template.private?
|
|
||||||
= fa_icon 'lock', title: t('posts.private')
|
|
||||||
= sanitize_markdown template.label, tags: %w[a]
|
|
||||||
- if template.help
|
|
||||||
%small.text-muted.form-text= template.help
|
|
||||||
= render "posts/template_field/#{type}", template: template, name: template.key, value: value
|
|
||||||
.invalid-feedback= t('posts.invalid')
|
|
||||||
.form-group
|
|
||||||
= submit_tag t('posts.save'), class: 'btn btn-success submit-post'
|
|
||||||
= submit_tag t('posts.save_incomplete'), class: 'btn btn-info submit-post-incomplete', name: 'commit_incomplete'
|
|
||||||
.invalid_help.alert.alert-danger.d-none= @site.config.dig('invalid_help') || t('posts.invalid_help')
|
|
||||||
.sending_help.alert.alert-success.d-none= @site.config.dig('sending_help') || t('posts.sending_help')
|
|
||||||
- if post = @post.template.try(:get_front_matter, 'post')
|
|
||||||
= render 'layouts/help', help: CommonMarker.render_doc(post).to_html
|
|
||||||
|
|
9
app/views/posts/_submit.haml
Normal file
9
app/views/posts/_submit.haml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.form-group
|
||||||
|
= submit_tag t('.save'), class: 'btn btn-success submit-post'
|
||||||
|
= submit_tag t('.save_incomplete'),
|
||||||
|
class: 'btn btn-info submit-post-incomplete',
|
||||||
|
name: 'commit_incomplete'
|
||||||
|
.invalid_help.alert.alert-danger.d-none
|
||||||
|
= site.config.fetch('invalid_help', t('.invalid_help'))
|
||||||
|
.sending_help.alert.alert-success.d-none
|
||||||
|
= site.config.fetch('sending_help', t('.sending_help'))
|
7
app/views/posts/attributes/_array.haml
Normal file
7
app/views/posts/attributes/_array.haml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
-# TODO: Convertir a select2 o nuestro reemplazo
|
||||||
|
.form-group{ class: invalid(post, attribute) }
|
||||||
|
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||||
|
= text_field 'post', attribute, value: metadata.value.join(', '),
|
||||||
|
**field_options(attribute, metadata)
|
||||||
|
= render 'posts/attribute_feedback',
|
||||||
|
post: post, attribute: attribute, metadata: metadata
|
6
app/views/posts/attributes/_content.haml
Normal file
6
app/views/posts/attributes/_content.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.form-group{ class: invalid(post, attribute) }
|
||||||
|
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||||
|
= render 'posts/attribute_feedback',
|
||||||
|
post: post, attribute: attribute, metadata: metadata
|
||||||
|
= text_area_tag "post[#{attribute}]", metadata.value,
|
||||||
|
**field_options(attribute, metadata)
|
6
app/views/posts/attributes/_document_date.haml
Normal file
6
app/views/posts/attributes/_document_date.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.form-group{ class: invalid(post, attribute) }
|
||||||
|
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||||
|
= date_field 'post', attribute, value: metadata.value.strftime('%F'),
|
||||||
|
**field_options(attribute, metadata)
|
||||||
|
= render 'posts/attribute_feedback',
|
||||||
|
post: post, attribute: attribute, metadata: metadata
|
20
app/views/posts/attributes/_image.haml
Normal file
20
app/views/posts/attributes/_image.haml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
.form-group{ class: invalid(post, attribute) }
|
||||||
|
- if metadata.value['path'].present?
|
||||||
|
= image_tag metadata.value[:path], alt: metadata.value['description']
|
||||||
|
|
||||||
|
.custom-file
|
||||||
|
= file_field(*field_name_for('post', attribute, :path),
|
||||||
|
**field_options(attribute, metadata), class: 'custom-file-input')
|
||||||
|
= label_tag "post_#{attribute}_path",
|
||||||
|
post_label_t(attribute, :path, post: post), class: 'custom-file-label'
|
||||||
|
= render 'posts/attribute_feedback',
|
||||||
|
post: post, attribute: [attribute, :path], metadata: metadata
|
||||||
|
|
||||||
|
.form-group{ class: invalid(post, attribute) }
|
||||||
|
= label_tag "post_#{attribute}_description",
|
||||||
|
post_label_t(attribute, :description, post: post)
|
||||||
|
= text_field(*field_name_for('post', attribute, :description),
|
||||||
|
value: metadata.value['description'],
|
||||||
|
**field_options(attribute, metadata))
|
||||||
|
= render 'posts/attribute_feedback',
|
||||||
|
post: post, attribute: [attribute, :description], metadata: metadata
|
6
app/views/posts/attributes/_slug.haml
Normal file
6
app/views/posts/attributes/_slug.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.form-group{ class: invalid(post, attribute) }
|
||||||
|
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||||
|
= text_field 'post', attribute, value: metadata.value,
|
||||||
|
**field_options(attribute, metadata)
|
||||||
|
= render 'posts/attribute_feedback',
|
||||||
|
post: post, attribute: attribute, metadata: metadata
|
6
app/views/posts/attributes/_string.haml
Normal file
6
app/views/posts/attributes/_string.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.form-group{ class: invalid(post, attribute) }
|
||||||
|
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||||
|
= text_field 'post', attribute, value: metadata.value,
|
||||||
|
**field_options(attribute, metadata)
|
||||||
|
= render 'posts/attribute_feedback',
|
||||||
|
post: post, attribute: attribute, metadata: metadata
|
|
@ -1,44 +1,21 @@
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
= render 'layouts/breadcrumb',
|
= render 'layouts/breadcrumb',
|
||||||
crumbs: [ link_to(t('sites.index'), sites_path), @site.name, link_to(t('posts.index'), site_posts_path(@site)), @category ]
|
crumbs: [link_to(t('sites.index'), sites_path),
|
||||||
|
@site.name,
|
||||||
|
link_to(t('posts.index'),
|
||||||
|
site_posts_path(@site)),
|
||||||
|
@category]
|
||||||
= render 'layouts/help', help: t('help.breadcrumbs')
|
= render 'layouts/help', help: t('help.breadcrumbs')
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
%h1= @site.config.fetch('title', @site.name_with_i18n(@lang))
|
%h1= @site.title
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
.btn-group
|
.btn-group
|
||||||
- if @site.templates.empty?
|
= link_to t('posts.new'), new_site_post_path(@site),
|
||||||
= link_to t('posts.new'), new_site_post_path(@site, lang: @lang),
|
|
||||||
class: 'btn btn-success'
|
class: 'btn btn-success'
|
||||||
- else
|
|
||||||
= link_to t('posts.new_with_template', template: @site.templates.first.id.humanize),
|
|
||||||
new_site_post_path(@site, lang: @lang, template: @site.templates.first.id),
|
|
||||||
class: 'btn btn-success'
|
|
||||||
- if @site.usuarie? current_usuarie
|
|
||||||
%button.btn.btn-success.dropdown-toggle.dropdown-toggle-split{data: { toggle: 'split' },
|
|
||||||
aria: { haspopup: 'true', expanded: 'false' }}
|
|
||||||
%span.sr-only= t('posts.dropdown')
|
|
||||||
.dropdown-menu
|
|
||||||
- @site.templates.each do |template|
|
|
||||||
= link_to template.id.humanize,
|
|
||||||
new_site_post_path(@site, lang: @lang, template: template.id),
|
|
||||||
class: 'dropdown-item'
|
|
||||||
- @site.translations.each do |l|
|
|
||||||
= link_to t("i18n.#{l}"), site_posts_path(@site, category: @category, lang: l),
|
|
||||||
class: 'btn btn-info'
|
|
||||||
.btn-group.pull-right
|
|
||||||
= link_to t('posts.categories'), site_posts_path(@site, lang: @lang), class: 'btn btn-secondary'
|
|
||||||
%button.btn.btn-secondary.dropdown-toggle.dropdown-toggle-split{data: { toggle: 'split' },
|
|
||||||
aria: { haspopup: 'true', expanded: 'false' }}
|
|
||||||
%span.sr-only= t('posts.dropdown')
|
|
||||||
.dropdown-menu
|
|
||||||
- @site.categories.each do |c|
|
|
||||||
= link_to c.split(':').first,
|
|
||||||
site_posts_path(@site, lang: @lang, category: c),
|
|
||||||
class: (params[:category] == c) ? 'dropdown-item active' : 'dropdown-item'
|
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
|
@ -54,78 +31,33 @@
|
||||||
category: @category,
|
category: @category,
|
||||||
lang: @lang,
|
lang: @lang,
|
||||||
sort_by: s)
|
sort_by: s)
|
||||||
= form_tag site_reorder_posts_path, method: :post do
|
%table.table.table-condensed.table-striped
|
||||||
= hidden_field 'posts', 'lang', value: @lang
|
|
||||||
- if policy(@site).reorder_posts?
|
|
||||||
- if @site.ordered? @lang
|
|
||||||
.reorder-posts-panel.alert.alert-info.alert-dismissible.fade.show{role: 'alert'}
|
|
||||||
= raw t('help.posts.reorder')
|
|
||||||
%br
|
|
||||||
= submit_tag t('posts.reorder_posts'), class: 'btn btn-success'
|
|
||||||
%button.close{type: 'button',
|
|
||||||
'aria-label': t('help.close') }
|
|
||||||
%span{'aria-hidden': true} ×
|
|
||||||
- else
|
|
||||||
.alert.alert-danger.alert-dismissible.fade.show{role: 'alert'}
|
|
||||||
= raw t('errors.posts.disordered')
|
|
||||||
%br
|
|
||||||
= hidden_field 'posts', 'force', value: true
|
|
||||||
= submit_tag t('errors.posts.disordered_button'), class: 'btn btn-danger'
|
|
||||||
%button.close{type: 'button',
|
|
||||||
data: { dismiss: 'alert' },
|
|
||||||
'aria-label': t('help.close') }
|
|
||||||
%span{'aria-hidden': true} ×
|
|
||||||
%table.table.table-condensed.table-striped{class: (@site.ordered? @lang) ? 'table-draggable' : ''}
|
|
||||||
%tbody
|
%tbody
|
||||||
- @posts.each_with_index do |post, i|
|
- @posts.each do |post|
|
||||||
|
-#
|
||||||
|
saltearse el post a menos que esté en la categoría por
|
||||||
|
la que estamos filtrando
|
||||||
- if @category
|
- if @category
|
||||||
-# saltearse el post a menos que esté en la categoría
|
- next unless post.categories.value.include?(@category)
|
||||||
-# por la que estamos filtrando
|
|
||||||
- next unless post.categories.include?(@category)
|
|
||||||
-# establecer la direccion del texto
|
|
||||||
- direction = post.get_front_matter(:dir)
|
|
||||||
%tr
|
%tr
|
||||||
- if policy(@site).reorder_posts? && @site.ordered?(@lang)
|
|
||||||
%td
|
%td
|
||||||
= fa_icon 'arrows-v', class: 'handle'
|
= link_to post.title.value,
|
||||||
= hidden_field 'posts[order]', i, value: post.order, class: 'post_order'
|
site_post_path(@site, post.id)
|
||||||
%small
|
- unless post.categories.value.empty?
|
||||||
%br
|
|
||||||
%span.order.is= post.order
|
|
||||||
%span.order.was.d-none{data: { order: post.order }}= "(#{post.order})"
|
|
||||||
|
|
||||||
%td{class: direction}
|
|
||||||
= link_to post.title, site_post_path(@site, post, lang: @lang)
|
|
||||||
- unless post.categories.empty?
|
|
||||||
%br
|
%br
|
||||||
%small
|
%small
|
||||||
- post.categories.each do |c|
|
- post.categories.value.each do |c|
|
||||||
= link_to c, site_posts_path(@site, category: c, lang: @lang),
|
= link_to c, site_posts_path(@site, category: c)
|
||||||
data: { toggle: 'tooltip' }, title: t('help.category')
|
|
||||||
- if post.draft? || post.incomplete?
|
|
||||||
%br
|
|
||||||
- if post.draft?
|
|
||||||
%span.badge.badge-info= t('posts.draft')
|
|
||||||
- if post.incomplete?
|
|
||||||
%span.badge.badge-warning= t('posts.incomplete')
|
|
||||||
|
|
||||||
%td
|
%td= post.date.value.strftime('%F')
|
||||||
- if post.translations
|
|
||||||
%small
|
|
||||||
- post.translations.each do |pt|
|
|
||||||
= link_to pt.title, site_post_path(@site, pt, lang: pt.lang),
|
|
||||||
data: { toggle: 'tooltip' }, title: t("i18n.#{pt.lang}")
|
|
||||||
%br
|
|
||||||
|
|
||||||
%td= post.date.strftime('%F')
|
|
||||||
%td
|
%td
|
||||||
- if policy(post).edit?
|
- if policy(post).edit?
|
||||||
= link_to t('posts.edit'),
|
= link_to t('posts.edit'),
|
||||||
edit_site_post_path(@site, post, lang: @lang),
|
edit_site_post_path(@site, post.id),
|
||||||
class: 'btn btn-info'
|
class: 'btn btn-info'
|
||||||
- if policy(post).destroy?
|
- if policy(post).destroy?
|
||||||
= link_to t('posts.destroy'),
|
= link_to t('posts.destroy'),
|
||||||
site_post_path(@site, post, lang: @lang),
|
site_post_path(@site, post.id),
|
||||||
class: 'btn btn-danger',
|
class: 'btn btn-danger',
|
||||||
method: :delete,
|
method: :delete,
|
||||||
data: { confirm: t('posts.confirm_destroy') }
|
data: { confirm: t('posts.confirm_destroy') }
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
.row
|
.row
|
||||||
.col
|
.col
|
||||||
= render 'layouts/breadcrumb', crumbs: [ link_to(t('sites.index'), sites_path), @site.name, link_to(t('posts.index'), site_posts_path(@site)), t('posts.new') ]
|
= render 'layouts/breadcrumb',
|
||||||
|
crumbs: [link_to(t('sites.index'), sites_path),
|
||||||
|
@site.name,
|
||||||
|
link_to(t('posts.index'),
|
||||||
|
site_posts_path(@site)), t('posts.new')]
|
||||||
|
|
||||||
.row.justify-content-center
|
.row.justify-content-center
|
||||||
.col-md-8
|
.col-md-8
|
||||||
= render 'posts/form'
|
= render 'posts/form', site: @site, post: @post
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
en:
|
en:
|
||||||
|
dir: ltr
|
||||||
site_service:
|
site_service:
|
||||||
create: 'Created %{name}'
|
create: 'Created %{name}'
|
||||||
update: 'Updated %{name}'
|
update: 'Updated %{name}'
|
||||||
|
@ -53,6 +54,8 @@ en:
|
||||||
lang: 'Main language'
|
lang: 'Main language'
|
||||||
site:
|
site:
|
||||||
name: 'Name'
|
name: 'Name'
|
||||||
|
title: 'Title'
|
||||||
|
description: 'Description'
|
||||||
errors:
|
errors:
|
||||||
models:
|
models:
|
||||||
site:
|
site:
|
||||||
|
@ -297,6 +300,17 @@ en:
|
||||||
en: 'English'
|
en: 'English'
|
||||||
ar: 'Arabic'
|
ar: 'Arabic'
|
||||||
posts:
|
posts:
|
||||||
|
submit:
|
||||||
|
save: 'Save'
|
||||||
|
save_incomplete: 'Save as draft'
|
||||||
|
invalid_help: 'Some fields need attention! Please search for the fields marked as invalid.'
|
||||||
|
attributes:
|
||||||
|
date:
|
||||||
|
label: Date
|
||||||
|
help: Publication date for this post. If you use a date in the future the post won't be published until then.
|
||||||
|
required:
|
||||||
|
label: ' (required)'
|
||||||
|
feedback: 'This field cannot be empty!'
|
||||||
reorder_posts: 'Reorder posts'
|
reorder_posts: 'Reorder posts'
|
||||||
sort:
|
sort:
|
||||||
by: 'Sort by'
|
by: 'Sort by'
|
||||||
|
@ -310,59 +324,8 @@ en:
|
||||||
categories: 'Everything'
|
categories: 'Everything'
|
||||||
index: 'Posts'
|
index: 'Posts'
|
||||||
edit: 'Edit'
|
edit: 'Edit'
|
||||||
save: 'Send'
|
|
||||||
save_incomplete: 'Save for later'
|
|
||||||
draft: revision
|
draft: revision
|
||||||
incomplete: draft
|
incomplete: draft
|
||||||
author: 'Author'
|
|
||||||
author_help: 'You can change the authorship of the post. If your site accepts guests, changing the authorship to an e-mail address will allow them to edit the post.'
|
|
||||||
date: 'Publication date'
|
|
||||||
date_help: 'This changes the articles order!'
|
|
||||||
title: 'Title'
|
|
||||||
tags: 'Tags'
|
|
||||||
tags_help: 'Comma separated!'
|
|
||||||
tags: 'Tags'
|
|
||||||
slug: 'Slug'
|
|
||||||
slug_help: 'This is the name of the article on the URL, ie. /title/. You can leave it empty. If you changed the title and you want to change the file name, empty this field.'
|
|
||||||
cover: 'Cover'
|
|
||||||
cover_help: 'Path to the cover'
|
|
||||||
layout: 'Layout'
|
|
||||||
layout_help: 'The layout of this post'
|
|
||||||
objetivos: 'Objectives'
|
|
||||||
objetivos_help: 'Objectives of this session'
|
|
||||||
permalink: 'Permanent link'
|
|
||||||
permalink_help: "If you want to access the post from a specific URL, use this field. Don't forget to start with a /"
|
|
||||||
recomendaciones: 'Recommendations'
|
|
||||||
recomendaciones_help: 'Recommendations for this session'
|
|
||||||
duracion: 'Duration'
|
|
||||||
duracion_help: "How long does the session take to finish?"
|
|
||||||
habilidades: 'Skill level'
|
|
||||||
habilidades_help: 'Skills required for this session'
|
|
||||||
formato: 'Format'
|
|
||||||
formato_help: 'Format of this session'
|
|
||||||
conocimientos: 'Required knowledge'
|
|
||||||
conocimientos_help: 'Select all required knowledge for this session'
|
|
||||||
sesiones_ejercicios_relacionados: 'Related sessions/exercises'
|
|
||||||
sesiones_ejercicios_relacionados_help: 'Select all related sessions/exercises'
|
|
||||||
materiales_requeridos: 'Needed materials'
|
|
||||||
materiales_requeridos_help: 'Select all materials needed for this session'
|
|
||||||
lang:
|
|
||||||
es: 'Castillian Spanish'
|
|
||||||
en: 'English'
|
|
||||||
ar: 'Arabic'
|
|
||||||
lang_help: 'The same article in another language.'
|
|
||||||
rtl: 'Right to left'
|
|
||||||
ltr: 'Left to right'
|
|
||||||
dir: 'Text direction'
|
|
||||||
dir_help: 'The reading direction of the language'
|
|
||||||
logger:
|
|
||||||
rm: 'Removed %{path}'
|
|
||||||
errors:
|
|
||||||
path: 'File already exist'
|
|
||||||
file: "Couldn't write the file"
|
|
||||||
title: 'Post needs a title'
|
|
||||||
date: 'Post needs a valid date'
|
|
||||||
slug_with_path: "The slug is the short name for the article, as shown in the URL. It can't contain \"/\" ;)"
|
|
||||||
invalid: 'This field is required!'
|
invalid: 'This field is required!'
|
||||||
open: 'Tip: You can add new options by typing them and pressing Enter'
|
open: 'Tip: You can add new options by typing them and pressing Enter'
|
||||||
private: '🔒 The values of this field will remain private'
|
private: '🔒 The values of this field will remain private'
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
es:
|
es:
|
||||||
|
dir: ltr
|
||||||
site_service:
|
site_service:
|
||||||
create: 'Creado %{name}'
|
create: 'Creado %{name}'
|
||||||
update: 'Actualizado %{name}'
|
update: 'Actualizado %{name}'
|
||||||
|
@ -312,6 +313,17 @@ es:
|
||||||
en: 'inglés'
|
en: 'inglés'
|
||||||
ar: 'árabe'
|
ar: 'árabe'
|
||||||
posts:
|
posts:
|
||||||
|
submit:
|
||||||
|
save: 'Guardar'
|
||||||
|
save_incomplete: 'Guardar como borrador'
|
||||||
|
invalid_help: '¡Te faltan completar algunos campos! Busca los que estén marcados como inválidos'
|
||||||
|
attributes:
|
||||||
|
date:
|
||||||
|
label: Fecha
|
||||||
|
help: La fecha de publicación del artículo. Si colocas una fecha en el futuro no se publicará hasta ese día.
|
||||||
|
required:
|
||||||
|
label: ' (requerido)'
|
||||||
|
feedback: '¡Este campo no puede estar vacío!'
|
||||||
reorder_posts: 'Reordenar artículos'
|
reorder_posts: 'Reordenar artículos'
|
||||||
sort:
|
sort:
|
||||||
by: 'Ordenar por'
|
by: 'Ordenar por'
|
||||||
|
@ -327,60 +339,6 @@ es:
|
||||||
edit: 'Editar'
|
edit: 'Editar'
|
||||||
draft: en revisión
|
draft: en revisión
|
||||||
incomplete: borrador
|
incomplete: borrador
|
||||||
save: 'Enviar'
|
|
||||||
save_incomplete: 'Guardar para después'
|
|
||||||
author: 'Autorx'
|
|
||||||
author_help: 'Puedes cambiar la autoría del artículo aquí. Si el sitio acepta invitadxs, poner la dirección de correo de alguien aquí le permite editarlo.'
|
|
||||||
date: 'Fecha de publicación'
|
|
||||||
date_help: '¡Esto cambia el orden de los artículos!'
|
|
||||||
title: 'Título'
|
|
||||||
categories: 'Categorías'
|
|
||||||
tags: 'Etiquetas'
|
|
||||||
slug: 'Nombre la URL'
|
|
||||||
slug_help: 'Esto es el nombre del artículo en la URL, por ejemplo
|
|
||||||
/título/. Puedes dejarlo vacío. Si cambiaste el título y quieres
|
|
||||||
que la URL cambie, borra el contenido de este campo.'
|
|
||||||
cover: 'Portada'
|
|
||||||
cover_help: 'La dirección de la portada'
|
|
||||||
layout: 'Plantilla'
|
|
||||||
layout_help: 'El tipo de plantilla'
|
|
||||||
objetivos: 'Objetivos'
|
|
||||||
objetivos_help: 'Objetivos de esta sesión'
|
|
||||||
permalink: 'Dirección del enlace'
|
|
||||||
permalink_help: 'Si quieres que el artículo se acceda desde esta URL completa aquí, no te olvides de empezar con /'
|
|
||||||
habilidades: 'Habilidades'
|
|
||||||
habilidades_help: 'Habilidades requeridas para esta sesión'
|
|
||||||
formato: 'Formato'
|
|
||||||
formato_help: 'Formato de esta sesión'
|
|
||||||
conocimientos: 'Conocimientos'
|
|
||||||
conocimientos_help: 'Elige todos los conocimientos requeridos para abordar esta sesión'
|
|
||||||
sesiones_ejercicios_relacionados: 'Sesiones y ejercicios relacionados'
|
|
||||||
sesiones_ejercicios_relacionados_help: 'Elige todas las sesiones relacionadas con esta'
|
|
||||||
materiales_requeridos: 'Materiales requeridos'
|
|
||||||
materiales_requeridos_help: 'Materiales necesarios para esta sesión'
|
|
||||||
recomendaciones: 'Recomendaciones'
|
|
||||||
recomendaciones_help: 'Recomendaciones para esta sesión'
|
|
||||||
duracion: 'Duración'
|
|
||||||
duracion_help: '¿Cuánto dura la sesión?'
|
|
||||||
lang:
|
|
||||||
es: 'Artículo en castellano'
|
|
||||||
en: 'Artículo en inglés'
|
|
||||||
ar: 'Artículo en árabe'
|
|
||||||
lang_help: 'El mismo artículo en otro idioma'
|
|
||||||
rtl: 'Derecha a izquierda'
|
|
||||||
ltr: 'Izquierda a derecha'
|
|
||||||
dir: 'Dirección del texto'
|
|
||||||
dir_help: 'La dirección de lectura del idioma'
|
|
||||||
dir_help: 'Cambiar la dirección del texto, por ej. el árabe se lee de derecha a izquierda'
|
|
||||||
logger:
|
|
||||||
rm: 'Eliminado %{path}'
|
|
||||||
errors:
|
|
||||||
path: 'El archivo destino ya existe'
|
|
||||||
file: 'No se pudo escribir el archivo'
|
|
||||||
title: 'Necesita un título'
|
|
||||||
date: 'Necesita una fecha'
|
|
||||||
slug_with_path: 'El slug es el nombre corto del artículo, tal como figura en la URL. No puede contener "/" ;)'
|
|
||||||
invalid: '¡Este campo es obligatorio!'
|
|
||||||
open: 'Nota: Puedes agregar más opciones a medida que las escribes y presionas Entrar'
|
open: 'Nota: Puedes agregar más opciones a medida que las escribes y presionas Entrar'
|
||||||
private: '🔒 Los valores de este campo serán privados'
|
private: '🔒 Los valores de este campo serán privados'
|
||||||
select:
|
select:
|
||||||
|
|
|
@ -98,3 +98,11 @@ Al instanciar un `Post`, se pasan el sitio y la plantilla por defecto.
|
||||||
utilizada)
|
utilizada)
|
||||||
* Reimplementar orden de artículos (ver doc)
|
* Reimplementar orden de artículos (ver doc)
|
||||||
* Convertir layout a params
|
* Convertir layout a params
|
||||||
|
* Reimplementar plantillas
|
||||||
|
* Reimplementar subida de imagenes/archivos
|
||||||
|
* Reimplementar campo 'pre' y 'post' en los layouts.yml
|
||||||
|
* Implementar autoría como un array
|
||||||
|
* Reimplementar draft e incomplete (por qué eran distintos?)
|
||||||
|
|
||||||
|
* Convertir idiomas disponibles a pestañas?
|
||||||
|
* Implementar traducciones sin adivinar. Vincular artículos entre sí
|
||||||
|
|
|
@ -162,6 +162,7 @@ class PostTest < ActiveSupport::TestCase
|
||||||
test 'se pueden crear nuevos' do
|
test 'se pueden crear nuevos' do
|
||||||
post = @site.posts.build(layout: :post)
|
post = @site.posts.build(layout: :post)
|
||||||
post.title.value = 'test'
|
post.title.value = 'test'
|
||||||
|
post.content.value = 'test'
|
||||||
|
|
||||||
assert post.new?
|
assert post.new?
|
||||||
assert post.save
|
assert post.save
|
||||||
|
|
Loading…
Reference in a new issue