diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index cbe806e..4ef6b3b 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -90,4 +90,17 @@ class PostsController < ApplicationController service.destroy redirect_to site_posts_path(@site) end + + # Reordenar los artículos + def reorder + @site = find_site + authorize @site + + service = PostService.new(site: @site, + usuarie: current_usuarie, + params: params) + + service.reorder + redirect_to site_posts_path(@site) + end end diff --git a/app/models/site.rb b/app/models/site.rb index eb37808..48ebb6e 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -221,47 +221,6 @@ class Site < ApplicationRecord status == 'enqueued' end - # Verifica si los posts están ordenados - def ordered?(lang: nil) - posts(lang: lang).map(&:order).all? - end - - # Reordena la colección usando la posición informada - # - # new_order es un hash cuya key es la posición actual del post y el - # valor la posición nueva - # - # TODO: Refactorizar y testear - def reorder_collection(collection, new_order) - # Tenemos que pasar el mismo orden - return if new_order.values.map(&:to_i).sort != new_order.keys.map(&:to_i).sort - - # Solo traer los posts que vamos a modificar - posts_to_order = posts_for(collection).values_at(*new_order.keys.map(&:to_i)) - - # Recorre todos los posts y asigna el nuevo orden - posts_to_order.each_with_index do |p, i| - # Usar el index si el artículo no estaba ordenado, para tener una - # ruta de adopción - oo = (p.order || i).to_s - no = new_order[oo].to_i - # No modificar nada si no hace falta - next if p.order == no - - p.update_attributes order: no - p.save - end - - posts_to_order.map(&:ordered?).all? - end - - # Reordena la colección usando la posición actual de los artículos - def reorder_collection!(collection = 'posts') - order = Hash[posts_for(collection).count.times.map { |i| [i.to_s, i.to_s] }] - reorder_collection collection, order - end - alias reorder_posts! reorder_collection! - # Obtener una ruta disponible para Sutty # # TODO: Refactorizar y testear diff --git a/app/models/site/repository.rb b/app/models/site/repository.rb index 36305b2..1923d06 100644 --- a/app/models/site/repository.rb +++ b/app/models/site/repository.rb @@ -85,9 +85,13 @@ class Site !commits.empty? end - # Guarda los cambios en git, de a un archivo por vez + # Guarda los cambios en git def commit(file:, usuarie:, message:, remove: false) - remove ? rm(file) : add(file) + file = [file] unless file.respond_to? :each + + file.each do |f| + remove ? rm(f) : add(f) + end # Escribir los cambios para que el repositorio se vea tal cual rugged.index.write diff --git a/app/policies/site_policy.rb b/app/policies/site_policy.rb index bf3ac36..1177596 100644 --- a/app/policies/site_policy.rb +++ b/app/policies/site_policy.rb @@ -67,6 +67,11 @@ class SitePolicy pull? end + # Solo les usuaries pueden reordenar artículos + def reorder? + site.usuarie? usuarie + end + private def current_role diff --git a/app/services/post_service.rb b/app/services/post_service.rb index fddb0b6..890a77f 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -39,19 +39,50 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do post end + # Reordena todos los posts que soporten orden de acuerdo a un array + # con las nuevas posiciones. La posición actual la da la posición en + # el array. + # + # [ 1, 0, 2 ] => mover el elemento 2 a la posición 1, mantener 3 + def reorder + posts = site.posts(lang: lang) + reorder = params.require(:post).permit(reorder: []) + .try(:[], :reorder) + .try(:map, &:to_i) || [] + + # Tenemos que pasar un array con la misma cantidad de elementos + return false if reorder.size != posts.size + + files = reorder.map.with_index do |new, cur| + post = posts[cur] + next unless post.attributes.include? :order + + post.usuaries << usuarie + post.order.value = new + post.path.absolute + end.compact + + # TODO: Implementar transacciones! + posts.save_all && commit(action: :reorder, file: files) + end + private - def commit(action:) - site.repository.commit(file: post.path.absolute, + def commit(action:, file: nil) + site.repository.commit(file: file || post.path.absolute, usuarie: usuarie, remove: action == :destroyed, message: I18n.t("post_service.#{action}", - title: post.title.value)) + title: post.try(:title).try(:value))) end # Solo permitir cambiar estos atributos de cada articulo def post_params params.require(:post).permit(post.params) end + + def lang + params[:post][:lang] || I18n.locale + end end # rubocop:enable Metrics/BlockLength diff --git a/config/locales/en.yml b/config/locales/en.yml index 230fcb2..238dbc6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -19,6 +19,7 @@ en: created: 'Created "%{title}"' updated: 'Updated "%{title}"' destroyed: 'Removed "%{title}"' + reorder: 'Reorder' metadata: array: cant_be_empty: 'This field cannot be empty' diff --git a/config/locales/es.yml b/config/locales/es.yml index 75967fd..4eda8ba 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -19,6 +19,7 @@ es: created: 'Creado "%{title}"' updated: 'Modificado "%{title}"' destroyed: 'Eliminado "%{title}"' + reorder: 'Reordenados' metadata: array: cant_be_empty: 'El campo no puede estar vacío' diff --git a/config/routes.rb b/config/routes.rb index 1c2f79d..8ef82cb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,6 +45,7 @@ Rails.application.routes.draw do post 'collaborate', to: 'collaborations#accept_collaboration' # Gestionar artículos + post :'posts/reorder', to: 'posts#reorder' resources :posts # Gestionar traducciones diff --git a/doc/reordenar.md b/doc/reordenar.md index 719856f..bf5c0c4 100644 --- a/doc/reordenar.md +++ b/doc/reordenar.md @@ -1,134 +1,15 @@ # Reordenar los articulos -La interfaz reordena los articulos y los envia en ese orden particular -(se puede enviar un numero de orden completado con js para estar mas -segurxs). Entonces el algoritmo... +Todos los posts tienen un campo `order`. -* Chequea que los posts tengan fechas en orden - -* Si alguno(s) no tienen, busca fechas intermedias - -* Cuando todos tienen fechas en orden, guarda los cambios modificando - cada post - ---- - -* Compara el nuevo orden con el viejo para saber las diferencias - ---- - -* Recorre el nuevo orden uno por uno -* Se fija si el anterior tiene una fecha menor -* Se fija si el siguiente tiene una fecha mayor -* Se autoasigna una fecha en el medio -* Se guarda al final - ---- - -* Todos los posts tienen un ID (su id en el array de la colección) -* Toma el post actual -* Mueve el post a una posición arbitraria con un nuevo id -* Toma la fecha del post siguiente (nuevo_id + 1) -* Recorre hacia atrás los anteriores, corriéndolos de a un día hasta una - fecha anterior -* Los posts posteriores no se tocan - -Teniendo estos artículos - -``` -1 2017-01-01 -2 2017-01-02 -3 2017-01-03 -4 2017-01-04 -5 2017-01-05 -``` - -Movemos el artículo 2 a la posición 4 - -``` -1 1 2017-01-01 -2 3 2017-01-03 -3 4 2017-01-04 -4 2 2017-01-02 -5 5 2017-01-05 -``` - -Reordenamos las fechas - -``` -1 1 2017-01-01 2017-01-01 -2 3 2017-01-03 2017-01-02 -3 4 2017-01-04 2017-01-03 -4 2 2017-01-02 2017-01-04 -5 5 2017-01-05 2017-01-05 -``` - -Movemos varios - -``` -1 4 2017-01-04 -2 5 2017-01-05 -3 2 2017-01-02 -4 3 2017-01-03 -5 1 2017-01-01 -``` - -Cual es la fecha desde la que se empieza? Vamos hacia atras o hacia -adelante? - -Hacia atrás - -``` -1 4 2017-01-04 2016-12-28 -2 5 2017-01-05 2016-12-29 -3 2 2017-01-02 2016-12-30 -4 3 2017-01-03 2016-12-31 -5 1 2017-01-01 2017-01-01 -``` - -Hacia adelante - -``` -1 4 2017-01-04 2017-01-04 -2 5 2017-01-05 2017-01-05 -3 2 2017-01-02 2017-01-06 -4 3 2017-01-03 2017-01-07 -5 1 2017-01-01 2017-01-08 -``` - -En si las fechas no importan, porque estamos priorizando el orden, las -fechas son arbitrarias para engañar a jekyll a tener los posts en cierto -orden. - -Por el contrario, para mantener los cambios mínimos, podemos reemplazar -hacia adelante comenzando desde la fecha mas baja del orden original. - -``` -1 4 2017-01-04 2017-01-01 -2 5 2017-01-05 2017-01-02 -3 2 2017-01-02 2017-01-03 -4 3 2017-01-03 2017-01-04 -5 1 2017-01-01 2017-01-05 -``` - -No quiere decir que en ordenes de fechas mas dispersas se mantengan los -cambios mínimos. - -También podemos tomar todo el set original de fechas y asociarselo al -orden nuevo de posts. Las fechas se mantienen igual, pero cambia el -orden de los posts. - -Qué pasa cuando dos o más artículos comparten la misma fecha? -Normalmente se ordenan por nombre, pero en este algoritmo no entra ese -orden, se asume que siempre están una fecha adelante o atrás - ---- - -Todos los posts tienen un campo order. - -El orden se actualiza en base al orden cronologico. +El orden se actualiza en base al orden cronológico. En la plantilla se puede ordenar cronólogicamente o por orden numérico. -Es más simple de implementar y no tiene comportamientos inesperados, -como "por qué cambió de fecha?" +El orden es independiente de otros metadatos (como layout, categoria, +etc), todos los artículos siguen un orden + +Como el orden es un metadato, tenemos que ignorar los tipos de posts que +no lo tienen + +El orden por defecto es orden natural, más bajo es más alto. diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb index ff0dd9f..93b6e8e 100644 --- a/test/controllers/posts_controller_test.rb +++ b/test/controllers/posts_controller_test.rb @@ -141,4 +141,21 @@ class PostsControllerTest < ActionDispatch::IntegrationTest assert_equal 200, response.status assert_match I18n.t('metadata.image.not_an_image'), response.body end + + test 'se pueden reordenar' do + lang = :es + posts = @site.posts(lang: lang) + reorder = posts.each_index.to_a.shuffle + + post site_posts_reorder_url(@site), + headers: @authorization, + params: { post: { lang: lang, reorder: reorder } } + + @site = Site.find @site.id + + assert_equal I18n.t('post_service.reorder'), + @site.repository.rugged.head.target.message + assert_equal reorder, + @site.posts(lang: lang).map(&:order).map(&:value) + end end