5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-17 10:26:21 +00:00

Merge remote-tracking branch 'origin/rails' into prosemirror

This commit is contained in:
void 2019-11-20 00:43:07 -03:00
commit 375226d7dd
20 changed files with 287 additions and 93 deletions

View file

@ -60,6 +60,10 @@ ENV RAILS_ENV production
# Instalar las dependencias, separamos la librería de base de datos para
# poder reutilizar este primer paso desde otros contenedores
RUN apk add --no-cache libxslt libxml2 tzdata ruby ruby-bundler ruby-json ruby-bigdecimal ruby-rake
# Chequear que la versión de ruby sea la correcta
RUN test "2.5.7" = `ruby -e 'puts RUBY_VERSION'`
RUN apk add --no-cache postgresql-libs libssh2
# Necesitamos yarn para que Jekyll pueda generar los sitios
# XXX: Eliminarlo cuando extraigamos la generación de sitios del proceso

View file

@ -14,6 +14,7 @@ git_source(:github) do |repo_name|
"https://github.com/#{repo_name}.git"
end
# Cambiar en Dockerfile también
ruby '2.5.7'
gem 'dotenv-rails', require: 'dotenv/rails-now'
@ -21,7 +22,7 @@ gem 'dotenv-rails', require: 'dotenv/rails-now'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6'
# Use Puma as the app server
gem 'puma', '~> 3.7'
gem 'puma'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets

View file

@ -14,63 +14,63 @@ GIT
rake (>= 0.8.7)
GEM
remote: https://gems.sutty.nl/
remote: https://rubygems.org/
specs:
actioncable (6.0.0)
actionpack (= 6.0.0)
actioncable (6.0.1)
actionpack (= 6.0.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.0.0)
actionpack (= 6.0.0)
activejob (= 6.0.0)
activerecord (= 6.0.0)
activestorage (= 6.0.0)
activesupport (= 6.0.0)
actionmailbox (6.0.1)
actionpack (= 6.0.1)
activejob (= 6.0.1)
activerecord (= 6.0.1)
activestorage (= 6.0.1)
activesupport (= 6.0.1)
mail (>= 2.7.1)
actionmailer (6.0.0)
actionpack (= 6.0.0)
actionview (= 6.0.0)
activejob (= 6.0.0)
actionmailer (6.0.1)
actionpack (= 6.0.1)
actionview (= 6.0.1)
activejob (= 6.0.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.0.0)
actionview (= 6.0.0)
activesupport (= 6.0.0)
actionpack (6.0.1)
actionview (= 6.0.1)
activesupport (= 6.0.1)
rack (~> 2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.0.0)
actionpack (= 6.0.0)
activerecord (= 6.0.0)
activestorage (= 6.0.0)
activesupport (= 6.0.0)
actiontext (6.0.1)
actionpack (= 6.0.1)
activerecord (= 6.0.1)
activestorage (= 6.0.1)
activesupport (= 6.0.1)
nokogiri (>= 1.8.5)
actionview (6.0.0)
activesupport (= 6.0.0)
actionview (6.0.1)
activesupport (= 6.0.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.0.0)
activesupport (= 6.0.0)
activejob (6.0.1)
activesupport (= 6.0.1)
globalid (>= 0.3.6)
activemodel (6.0.0)
activesupport (= 6.0.0)
activerecord (6.0.0)
activemodel (= 6.0.0)
activesupport (= 6.0.0)
activestorage (6.0.0)
actionpack (= 6.0.0)
activejob (= 6.0.0)
activerecord (= 6.0.0)
activemodel (6.0.1)
activesupport (= 6.0.1)
activerecord (6.0.1)
activemodel (= 6.0.1)
activesupport (= 6.0.1)
activestorage (6.0.1)
actionpack (= 6.0.1)
activejob (= 6.0.1)
activerecord (= 6.0.1)
marcel (~> 0.3.1)
activesupport (6.0.0)
activesupport (6.0.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.1, >= 2.1.8)
zeitwerk (~> 2.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
airbrussh (1.3.4)
@ -139,7 +139,7 @@ GEM
email_address (0.1.11)
netaddr (~> 2.0)
simpleidn
erubi (1.8.0)
erubi (1.9.0)
eventmachine (1.2.7)
exception_notification (4.4.0)
actionmailer (>= 4.0, < 7)
@ -178,7 +178,7 @@ GEM
railties (>= 4.0.1)
hiredis (0.6.3)
http_parser.rb (0.6.0)
i18n (1.6.0)
i18n (1.7.0)
concurrent-ruby (~> 1.0)
image_processing (1.9.3)
mini_magick (>= 4.9.5, < 5)
@ -233,7 +233,7 @@ GEM
mini_magick (4.9.5)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.11.3)
minitest (5.13.0)
mobility (0.8.9)
i18n (>= 0.6.10, < 2)
request_store (~> 1.0)
@ -241,7 +241,7 @@ GEM
net-ssh (>= 2.6.5, < 6.0.0)
net-ssh (5.2.0)
netaddr (2.0.3)
nio4r (2.5.1)
nio4r (2.5.2)
nokogiri (1.10.5)
mini_portile2 (~> 2.4.0)
orm_adapter (0.5.0)
@ -256,7 +256,8 @@ GEM
coderay (~> 1.1.0)
method_source (~> 0.9.0)
public_suffix (4.0.1)
puma (3.12.1)
puma (4.3.0)
nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)
rack (2.0.7)
@ -264,20 +265,20 @@ GEM
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.0)
actioncable (= 6.0.0)
actionmailbox (= 6.0.0)
actionmailer (= 6.0.0)
actionpack (= 6.0.0)
actiontext (= 6.0.0)
actionview (= 6.0.0)
activejob (= 6.0.0)
activemodel (= 6.0.0)
activerecord (= 6.0.0)
activestorage (= 6.0.0)
activesupport (= 6.0.0)
rails (6.0.1)
actioncable (= 6.0.1)
actionmailbox (= 6.0.1)
actionmailer (= 6.0.1)
actionpack (= 6.0.1)
actiontext (= 6.0.1)
actionview (= 6.0.1)
activejob (= 6.0.1)
activemodel (= 6.0.1)
activerecord (= 6.0.1)
activestorage (= 6.0.1)
activesupport (= 6.0.1)
bundler (>= 1.3.0)
railties (= 6.0.0)
railties (= 6.0.1)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
@ -289,9 +290,9 @@ GEM
railties (>= 6.0.0, < 7)
rails_warden (0.6.0)
warden (>= 1.2.0)
railties (6.0.0)
actionpack (= 6.0.0)
activesupport (= 6.0.0)
railties (6.0.1)
actionpack (= 6.0.1)
activesupport (= 6.0.1)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
@ -422,7 +423,7 @@ GEM
websocket-extensions (0.1.4)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.1.10)
zeitwerk (2.2.1)
PLATFORMS
ruby
@ -461,7 +462,7 @@ DEPENDENCIES
mobility
pg
pry
puma (~> 3.7)
puma
pundit
rails (~> 6)
rails-i18n

View file

@ -1,6 +1,5 @@
//= require rails-ujs
//= require turbolinks
//= require zepto/dist/zepto.min.js
//= require input-tag/input-tag.js
//= require table-dragger/dist/table-dragger
//= require zepto/dist/zepto.min.js
//= require_tree .

View file

@ -1,14 +0,0 @@
$(document).on('turbolinks:load', function() {
var table = document.querySelector('.table-draggable');
if (table == null) return;
tableDragger(table, {
mode: 'row',
onlyBody: true,
dragHandler: '.handle'
}).on('drop', function(from, to, el, mode) {
$('.reorder').val(function(i,v) { return i; });
$('.submit-reorder').removeClass('d-none');
});
});

View file

@ -15,6 +15,8 @@
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
import tableDragger from "table-dragger"
import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"
import {Schema, DOMParser} from "prosemirror-model"
@ -162,4 +164,17 @@ document.addEventListener('turbolinks:load', e => {
textareaEl.value = editor.markdown
}, true)
}
const table = document.querySelector('.table-draggable')
if (table == null) return
tableDragger(table, {
mode: 'row',
onlyBody: true,
dragHandler: '.handle',
}).on('drop', (from, to, el, mode) => {
$('.reorder').val((i, v) => i)
$('.submit-reorder').removeClass('d-none')
})
})

View file

@ -56,6 +56,8 @@ class MetadataFile < MetadataTemplate
#
# @return ActiveStorage::Attachment
def static_file
return @static_file if @static_file
ActiveRecord::Base.connection_pool.with_connection do
if uploaded?
blob = ActiveStorage::Blob.find_by(key: key_from_path)
@ -68,10 +70,12 @@ class MetadataFile < MetadataTemplate
private
def path
@path ||= Pathname.new value['path']
end
def key_from_path
# XXX: No podemos usar self#extension porque en este punto todavía
# no sabemos el static_file
File.basename(value['path'], '.*')
path.dirname.basename.to_s
end
# Hacemos un link duro para colocar el archivo dentro del repositorio
@ -83,7 +87,7 @@ class MetadataFile < MetadataTemplate
end
def extension
static_file.blob.content_type.split('/').last
@extension ||= static_file.filename.to_s.split('.').last
end
# Obtener la ruta al archivo
@ -97,7 +101,7 @@ class MetadataFile < MetadataTemplate
end
def relative_destination_path
File.join('public', [static_file.key, extension].join('.'))
File.join('public', static_file.key, static_file.filename.to_s)
end
def destination_path

View file

@ -156,8 +156,8 @@ class Post < OpenStruct
# Guarda los cambios
# rubocop:disable Metrics/CyclomaticComplexity
def save
return false unless valid?
def save(validation = true)
return false if validation && !valid?
# Salir si tenemos que cambiar el nombre del archivo y no pudimos
return false if !new? && path_changed? && !update_path!
return false unless save_attributes!

View file

@ -39,7 +39,7 @@ class Site < ApplicationRecord
# Carga el sitio Jekyll una vez que se inicializa el modelo o después
# de crearlo
after_initialize :load_jekyll
after_create :load_jekyll
after_create :load_jekyll, :static_file_migration!
# Cambiar el nombre del directorio
before_update :update_name!
# Guardar la configuración si hubo cambios
@ -323,6 +323,11 @@ class Site < ApplicationRecord
config.url = url
end
# Migra los archivos a Sutty
def static_file_migration!
Site::StaticFileMigration.new(site: self).migrate!
end
# Valida si el sitio tiene al menos una forma de alojamiento asociada
# y es la local
#

View file

@ -0,0 +1,124 @@
# frozen_string_literal: true
class Site
MigrationAuthor = Struct.new :email, :name, keyword_init: true
# Obtiene todos los archivos relacionados en artículos del sitio y los
# sube a Sutty de forma que los podamos seguir utilizando normalmente
# sin casos especiales (ej. soportar archivos locales al repositorio y
# remotos, alojados en Sutty)
#
# TODO: Convertir en reutilizable, por ejemplo correr en cada pull, no
# asumir que la migración se hizo una sola vez...
class StaticFileMigration
# Tipos de metadatos que contienen archivos
STATIC_TYPES = %w[file image].freeze
attr_reader :site
def initialize(site:)
@site = site
end
# Recorre todos los artículos cuyos layouts contengan campos con
# archivos estáticos
def migrate!
log = File.open(File.join(site.path, 'migration.log'), 'w')
modified = []
Dir.chdir site.path do
site.locales.each do |locale|
# Recorrer todos los documentos de todas las colecciones
site.posts(lang: locale).each do |doc|
# Ignoramos los documentos cuyo layout no contiene archivos
next unless layouts.include? doc.layout.name
remove = true
# Buscamos todos los campos con archivos
fields.each do |field|
next unless doc.attribute? field
next unless doc.document.data.key? field.to_s
# Traemos los metadatos, en este punto, Sutty cree que el
# archivo está subido, porque es una string apuntando a un
# archivo.
metadata = doc.public_send(field)
next if metadata.value['path'].blank?
path = Pathname.new(metadata.value['path'])
# Si no existe, agregamos una imagen faltante para no
# romper el sitio en Sutty
unless path.exist?
log.write "#{path} no existe\n"
path = Pathname.new(Rails.root.join('app/assets/images/logo.png'))
remove = false
end
# Agregamos el archivo al sitio y se lo asignamos al campo
metadata.value['path'] = {
io: path.open,
filename: path.basename
}
# Copiar y analizar el archivo sincrónicamente
metadata.static_file.blob.upload path.open
metadata.static_file.blob.analyze
next unless remove
dest = Pathname.new(metadata.send(:relative_destination_path))
# Eliminamos el archivo original y lo vinculamos al subido
# para mantener la ruta y no romper el sitio
FileUtils.rm_f path
# XXX: Link simbólico o duro?
FileUtils.ln_s dest.relative_path_from(path.dirname), path
end
# Guardamos los cambios
unless doc.save(false)
log.write "#{doc.path.relative} no se pudo guardar\n"
end
modified << doc.path.absolute
end
end
end
log.close
# TODO: Hacer la migración desde el servicio de creación de sitios?
site.repository.commit(file: modified,
message: I18n.t('sites.static_file_migration'),
usuarie: author)
end
private
def author
@author = MigrationAuthor.new email: "sutty@#{Site.domain}",
name: 'Sutty'
end
# Encuentra todos los layouts con campos estáticos
def layouts
@layouts ||= site.layouts.reject do |_, layout|
layout.metadata.select do |_, desc|
STATIC_TYPES.include? desc['type']
end.empty?
end.keys
end
# Encuentra todos los campos con archivos estáticos
def fields
@fields ||= layouts.map do |layout|
site.layouts[layout].metadata.select do |_, desc|
STATIC_TYPES.include? desc['type']
end.keys
end.flatten.uniq.map(&:to_sym)
end
end
end

View file

@ -1,4 +1,4 @@
- site = @resources.sites.last
- site = @resource.sites.last
%p= t('devise.mailer.invitation_instructions.hello',
email: @resource.email)

View file

@ -1,4 +1,4 @@
- site = @resources.sites.last
- site = @resource.sites.last
= t('devise.mailer.invitation_instructions.hello', email: @resource.email)
\

View file

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

View file

@ -0,0 +1,6 @@
.form-group
= 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

View file

@ -6,13 +6,16 @@
@category]
%main.row
%aside.col-md-3
%aside.menu.col-md-3
%h1
= link_to @site.title, @site.url
%p.lead= @site.description
= link_to t('posts.new'), new_site_post_path(@site),
class: 'btn'
%h3= t('posts.new')
%ul
- @site.layouts.keys.each do |layout|
%li= link_to layout.to_s.humanize,
new_site_post_path(@site, layout: layout)
- if policy(@site).edit?
= link_to t('sites.edit.btn', site: @site.title),

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
# TODO: Estamos procesando el análisis de los archivos en el momento
# porque queremos obtener la ruta del archivo en el momento y no
# después. Necesitaríamos poder generar el vínculo en el
# repositorio a destiempo, modificando el Job de ActiveStorage
ActiveStorage::AnalyzeJob.queue_adapter = :inline

View file

@ -148,6 +148,7 @@ en:
anexo: 'Appendix'
simple: 'Simple'
sites:
static_file_migration: 'File migration'
index: 'This is the list of sites you can edit.'
edit_translations: "You can edit texts from your site other than
posts', and you can also translate them to other languages."
@ -354,8 +355,7 @@ en:
date: 'date'
order: 'Order'
content: 'Text'
new: 'New post'
new_with_template: 'New %{template}'
new: 'New post as'
dropdown: 'Toggle dropdown'
categories: 'Everything'
index: 'Posts'

View file

@ -149,6 +149,7 @@ es:
anexo: 'Anexo'
simple: 'Simple'
sites:
static_file_migration: 'Migración de archivos'
index: 'Este es el listado de sitios que puedes editar.'
edit_translations: 'Puedes editar los textos que salen en tu sitio
que no corresponden a artículos aquí, además de traducirlos a
@ -366,8 +367,7 @@ es:
content: 'Cuerpo del artículo'
categories: 'Todos'
dropdown: 'Desplegar el menú'
new: 'Crear artículo'
new_with_template: 'Comenzar %{template}'
new: 'Crear artículo en:'
index: 'Artículos'
edit: 'Editar'
open: 'Nota: Puedes agregar más opciones a medida que las escribes y presionas Entrar'

30
doc/static_files.md Normal file
View file

@ -0,0 +1,30 @@
# Archivos estáticos
El objetivo es encontrar una forma de importar los archivos estáticos de
un sitio que se ha migrado a la estructura de Sutty, es decir, subirlos
como archivos de Rails y luego vincularlos a los artículos.
Actualmente, los archivos primero se suben a la interfaz via
ActiveStorage y luego se vinculan internamente al repositorio[^git].
Lo que deberíamos lograr es reconocer los archivos vinculados desde los
artículos y hacer el proceso de cargarlos internamente, modificando los
artículos para que apunten a las nuevas versiones.
Esto podría hacerse por cada artículo individual o durante el proceso de
creación del sitio, después de clonarlo.
El algoritmo sería:
* Clonar el sitio
* Obtener todos los artículos de todas las colecciones
* Encontrar todos los layouts con archivos adjuntos
* Buscar todos los artículos que tengan esos layouts
* Tomar el archivo físico desde los metadatos y asociarlo al sitio via Active Storage
* Asociar el archivo al MetadataFile/Image correspondiente
* ???
* Migración cumplida!
[^git]: esto puede hacer que los repositorios git sean gigantes,
podríamos usar algo como git-annex quizás?

View file

@ -63,6 +63,12 @@ class PostTest < ActiveSupport::TestCase
assert_not @post.valid?
end
test 'se pueden guardar sin validar' do
assert @post.valid?
@post.title.value = ''
assert @post.save(false)
end
test 'se pueden guardar los cambios' do
title = SecureRandom.hex
@post.title.value = title