soporte para jekyll-ical :D
This commit is contained in:
parent
00042d3f57
commit
9db8215510
9 changed files with 195 additions and 9 deletions
|
@ -13,12 +13,18 @@ module ApplicationHelper
|
|||
root = names.shift
|
||||
|
||||
names.each do |n|
|
||||
root += "[#{n}]"
|
||||
root += '[' + n.to_s + ']'
|
||||
end
|
||||
|
||||
[root, name]
|
||||
end
|
||||
|
||||
def plain_field_name_for(*names)
|
||||
root, name = field_name_for(*names)
|
||||
|
||||
root + '[' + name.to_s + ']'
|
||||
end
|
||||
|
||||
def distance_of_time_in_words_if_more_than_a_minute(seconds)
|
||||
if seconds > 60
|
||||
distance_of_time_in_words seconds
|
||||
|
@ -62,13 +68,15 @@ module ApplicationHelper
|
|||
|
||||
# Opciones por defecto para el campo de un formulario
|
||||
def field_options(attribute, metadata, **extra)
|
||||
required = metadata.required || extra[:required]
|
||||
|
||||
{
|
||||
class: "form-control #{invalid(metadata.post, attribute)} #{extra[:class]}",
|
||||
required: metadata.required,
|
||||
required: required,
|
||||
autofocus: (metadata.post.attributes.first == attribute),
|
||||
aria: {
|
||||
describedby: id_for_help(attribute),
|
||||
required: metadata.required
|
||||
required: required
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -30,7 +30,58 @@ import "prosemirror-menu/style/menu.css"
|
|||
import "prosemirror-view/style/prosemirror.css"
|
||||
import "prosemirror-example-setup/style/style.css"
|
||||
|
||||
// Lista de equivalencias entre Date#getTimezoneOffset de JS y
|
||||
// MetadataEvent
|
||||
const timeZoneOffsets = {
|
||||
'720': '-12:00',
|
||||
'660': '-11:00',
|
||||
'600': '-10:00',
|
||||
'570': '-09:30',
|
||||
'540': '-09:00',
|
||||
'480': '-08:00',
|
||||
'420': '-07:00',
|
||||
'360': '-06:00',
|
||||
'300': '-05:00',
|
||||
'240': '-04:00',
|
||||
'210': '-03:30',
|
||||
'180': '-03:00',
|
||||
'120': '-02:00',
|
||||
'60': '-01:00',
|
||||
'0': '00:00',
|
||||
'-60': '+01:00',
|
||||
'-120': '+02:00',
|
||||
'-180': '+03:00',
|
||||
'-210': '+03:30',
|
||||
'-240': '+04:00',
|
||||
'-270': '+04:30',
|
||||
'-300': '+05:00',
|
||||
'-330': '+05:30',
|
||||
'-345': '+05:45',
|
||||
'-360': '+06:00',
|
||||
'-390': '+06:30',
|
||||
'-420': '+07:00',
|
||||
'-480': '+08:00',
|
||||
'-525': '+08:45',
|
||||
'-540': '+09:00',
|
||||
'-570': '+09:30',
|
||||
'-600': '+10:00',
|
||||
'-630': '+10:30',
|
||||
'-660': '+11:00',
|
||||
'-720': '+12:00',
|
||||
'-765': '+12:45',
|
||||
'-780': '+13:00',
|
||||
'-840': '+14:00'
|
||||
};
|
||||
|
||||
// Obtiene el huso horario local
|
||||
const timeZoneOffset = timeZoneOffsets[(new Date).getTimezoneOffset().toString()];
|
||||
|
||||
document.addEventListener('turbolinks:load', () => {
|
||||
|
||||
// Aplicar el huso horario descubierto en los campos de evento solo
|
||||
// cuando estamos creando un artículo.
|
||||
document.querySelectorAll('.new .event .zone select').forEach(zone => zone.value = timeZoneOffset);
|
||||
|
||||
document.querySelectorAll('.markdown-content').forEach(mdc => {
|
||||
let textArea = mdc.querySelector(".content"),
|
||||
editor = mdc.querySelector(".editor");
|
||||
|
|
73
app/models/metadata_event.rb
Normal file
73
app/models/metadata_event.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Gestiona eventos compatibles con jekyll-ical
|
||||
class MetadataEvent < MetadataTemplate
|
||||
# Preferimos los husos horarios en números porque los husos con
|
||||
# nombres dejan afuera territorios que comparten el mismo huso que
|
||||
# otras ciudades más hegemónicas.
|
||||
#
|
||||
# @see {https://en.wikipedia.org/wiki/List_of_UTC_time_offsets}
|
||||
TIMEZONES = %w[-12:00 -11:00 -10:00 -09:30 -09:00 -08:00 -07:00 -06:00
|
||||
-05:00 -04:00 -03:30 -03:00 -02:00 -01:00 00:00 +01:00 +02:00 +03:00
|
||||
+03:30 +04:00 +04:30 +05:00 +05:30 +05:45 +06:00 +06:30 +07:00 +08:00
|
||||
+08:45 +09:00 +09:30 +10:00 +10:30 +11:00 +12:00 +12:45 +13:00
|
||||
+14:00].freeze
|
||||
|
||||
# El valor por defecto es un Hash con algunas llaves pero queremos que
|
||||
# sea opcional.
|
||||
#
|
||||
# @return [Hash]
|
||||
def default_value
|
||||
{}
|
||||
end
|
||||
|
||||
def to_param
|
||||
{ name => {} }
|
||||
end
|
||||
|
||||
# Dates are required and need to be parseable
|
||||
def validate
|
||||
self.errors = []
|
||||
times = []
|
||||
|
||||
%w[dtstart dtend].each do |dt|
|
||||
errors << I18n.t("metadata.#{type}.zone_missing") unless TIMEZONES.include? value.dig(dt, 'zone')
|
||||
errors << I18n.t("metadata.#{type}.date_missing") if value.dig(dt, 'date').blank?
|
||||
|
||||
begin
|
||||
Date.parse value.dig(dt, 'date')
|
||||
rescue ArgumentError
|
||||
errors << I18n.t("metadata.#{type}.date_non_parseable")
|
||||
end
|
||||
|
||||
unless (time = value.dig(dt, 'time')).blank?
|
||||
errors << I18n.t("metadata.#{type}.time_non_parseable") unless /[0-5][0-9]:[0-5][0-9]/ =~ time
|
||||
end
|
||||
|
||||
times << value.dig(dt, 'date') + ' ' + value.dig(dt, 'time')
|
||||
end
|
||||
|
||||
begin
|
||||
dtstart, dtend = times.map { |t| Time.parse t }
|
||||
errors << I18n.t("metadata.#{type}.end_in_the_past") if dtstart > dtend
|
||||
rescue ArgumentError
|
||||
errors << I18n.t("metadata.#{type}.time_non_parseable")
|
||||
end
|
||||
|
||||
errors.empty?
|
||||
end
|
||||
|
||||
def sanitize(hash)
|
||||
self[:value] = %w[dtstart dtend].map do |dt|
|
||||
time = hash.dig(dt, 'time')
|
||||
|
||||
{
|
||||
dt => {
|
||||
'zone' => hash.dig(dt, 'zone'),
|
||||
'date' => Date.parse(hash.dig(dt, 'date')).to_s,
|
||||
'time' => time.blank? ? '00:00' : time
|
||||
}
|
||||
}
|
||||
end.inject(:merge)
|
||||
end
|
||||
end
|
|
@ -22,9 +22,6 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
|
|||
end
|
||||
|
||||
# Valor actual o por defecto
|
||||
#
|
||||
# XXX: No estamos sanitizando la entrada, cada tipo tiene que
|
||||
# auto-sanitizarse.
|
||||
def value
|
||||
self[:value] || document.data.fetch(name.to_s, default_value)
|
||||
end
|
||||
|
|
|
@ -8,15 +8,17 @@
|
|||
if post.new?
|
||||
url = site_posts_path(site, locale: @locale)
|
||||
method = :post
|
||||
extra_class = 'new'
|
||||
else
|
||||
url = site_post_path(site, post.id, locale: @locale)
|
||||
method = :patch
|
||||
extra_class = 'edit'
|
||||
end
|
||||
|
||||
- dir = t("locales.#{@locale}.dir")
|
||||
|
||||
-# Comienza el formulario
|
||||
= form_tag url, method: method, class: 'form post', multipart: true do
|
||||
= form_tag url, method: method, class: 'form post ' + extra_class, multipart: true do
|
||||
|
||||
-# Botones de guardado
|
||||
= render 'posts/submit', site: site, post: post
|
||||
|
|
7
app/views/posts/attribute_ro/_event.haml
Normal file
7
app/views/posts/attribute_ro/_event.haml
Normal file
|
@ -0,0 +1,7 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td
|
||||
%dl
|
||||
- %i[dtstart dtend].each do |dt|
|
||||
%dt= post_label_t(attribute, dt, post: post)
|
||||
%dl= Time.parse %w[date time zone].map { |x| metadata.value[dt.to_s][x] }.join(' ')
|
32
app/views/posts/attributes/_event.haml
Normal file
32
app/views/posts/attributes/_event.haml
Normal file
|
@ -0,0 +1,32 @@
|
|||
-#
|
||||
El evento tiene dos grupos, comienzo y final del evento, cada uno con
|
||||
fecha, hora y zona horaria
|
||||
|
||||
%fieldset.event
|
||||
%legend= post_label_t(attribute, post: post)
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: attribute, metadata: metadata
|
||||
|
||||
- %i[dtstart dtend].each do |dt|
|
||||
.row{ class: dt }
|
||||
.col
|
||||
.date.form-group
|
||||
= label_tag "post_#{attribute}_#{dt}_date",
|
||||
post_label_t(attribute, :date, post: post)
|
||||
= date_field_tag(*field_name_for('post', attribute, dt, :date),
|
||||
value: metadata.value.dig(dt.to_s, 'date'),
|
||||
**field_options(attribute, metadata, required: true))
|
||||
.col
|
||||
.time.form-group
|
||||
= label_tag "post_#{attribute}_#{dt}_time",
|
||||
post_label_t(attribute, :time, post: post)
|
||||
= time_field_tag(*field_name_for('post', attribute, dt, :time),
|
||||
value: metadata.value.dig(dt.to_s, 'time'),
|
||||
**field_options(attribute, metadata))
|
||||
.col
|
||||
.zone.form-group
|
||||
= label_tag "post_#{attribute}_#{dt}_zone",
|
||||
post_label_t(attribute, :zone, post: post)
|
||||
= select_tag(plain_field_name_for('post', attribute, dt, :zone),
|
||||
options_for_select(MetadataEvent::TIMEZONES, metadata.value.dig(dt.to_s, 'zone') || '00:00'),
|
||||
**field_options(attribute, metadata))
|
|
@ -37,6 +37,12 @@ en:
|
|||
cant_be_empty: 'This field cannot be empty'
|
||||
image:
|
||||
cant_be_empty: 'This field cannot be empty'
|
||||
event:
|
||||
zone_missing: 'Timezone is incorrect'
|
||||
date_missing: 'Event date is required'
|
||||
date_non_parseable: 'Time is not in the correct format'
|
||||
time_non_parseable: 'Date is not in the correct format'
|
||||
end_in_the_past: "Event end can't happen before the start"
|
||||
exceptions:
|
||||
post:
|
||||
site_missing: 'Needs an instance of Site'
|
||||
|
|
|
@ -39,6 +39,12 @@ es:
|
|||
cant_be_empty: 'El campo no puede estar vacío'
|
||||
not_an_image: 'No es una imagen'
|
||||
path_required: 'Se necesita un archivo de imagen'
|
||||
event:
|
||||
zone_missing: 'El huso horario no es correcto'
|
||||
date_missing: 'La fecha es obligatoria'
|
||||
date_non_parseable: 'La fecha no está en el formato correcto'
|
||||
time_non_parseable: 'La hora no está en el formato correcto'
|
||||
end_in_the_past: 'El fin del evento no puede ser anterior al comienzo'
|
||||
exceptions:
|
||||
post:
|
||||
site_missing: 'Necesita una instancia de Site'
|
||||
|
@ -446,9 +452,13 @@ es:
|
|||
image:
|
||||
multiple: 'Puedes seleccionar varias imágenes usando la tecla <kbd>Ctrl</kbd> o <kbd>Cmd</kbd> en tu teclado.'
|
||||
url: 'La dirección debe comenzar con http:// o https://'
|
||||
blank: En blanco
|
||||
blank: Vacío
|
||||
destroy: Borrar
|
||||
confirm_destroy: ¿Estás segurx?
|
||||
confirm_destroy: ¿Estás segure?
|
||||
form:
|
||||
errors:
|
||||
title: Hay errores en el formulario
|
||||
help: Por favor, verifica que todos los valores sean correctos.
|
||||
usuaries:
|
||||
invite_as:
|
||||
usuaries: usuaries
|
||||
|
|
Loading…
Reference in a new issue