From c8bf7eba213066f10d5957478c015c4f43a10579 Mon Sep 17 00:00:00 2001 From: f Date: Fri, 1 Nov 2019 19:51:56 -0300 Subject: [PATCH 1/7] metadataorder --- app/models/metadata_order.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/models/metadata_order.rb diff --git a/app/models/metadata_order.rb b/app/models/metadata_order.rb new file mode 100644 index 00000000..0b696ed8 --- /dev/null +++ b/app/models/metadata_order.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# Un campo de orden +class MetadataOrder < MetadataTemplate + # El valor según la posición del post en la relación, siguiendo el + # orden cronológico inverso + def default_value + site.posts(lang: post.lang.value).index(post) + end +end From b0cffa9cfb4d7954d644d42479fa3728a2d61763 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Nov 2019 18:06:19 -0300 Subject: [PATCH 2/7] reordenar articulos arrastrando y soltando --- app/assets/images/arrows-alt-v.svg | 3 + app/assets/javascripts/application.js | 1 + app/assets/javascripts/order.js | 16 +++++ app/assets/stylesheets/application.scss | 6 ++ app/views/posts/index.haml | 79 ++++++++++++++----------- config/locales/en.yml | 2 +- config/locales/es.yml | 4 +- package.json | 2 +- yarn.lock | 75 ++++++++++++----------- 9 files changed, 112 insertions(+), 76 deletions(-) create mode 100644 app/assets/images/arrows-alt-v.svg create mode 100644 app/assets/javascripts/order.js diff --git a/app/assets/images/arrows-alt-v.svg b/app/assets/images/arrows-alt-v.svg new file mode 100644 index 00000000..2b67bd50 --- /dev/null +++ b/app/assets/images/arrows-alt-v.svg @@ -0,0 +1,3 @@ + + diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 19d1215d..fc9a926c 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -2,4 +2,5 @@ //= require turbolinks //= require zepto/dist/zepto.min.js //= require input-tag/input-tag.js +//= require table-dragger/dist/table-dragger //= require_tree . diff --git a/app/assets/javascripts/order.js b/app/assets/javascripts/order.js new file mode 100644 index 00000000..3c72a5f8 --- /dev/null +++ b/app/assets/javascripts/order.js @@ -0,0 +1,16 @@ +$(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) { + // TODO: Detenerse al llegar al elemento que se movió + $('.reorder').val(function(i,v) { return i; }); + + $('.submit-reorder').removeClass('d-none'); + }); +}); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 53501e69..63eb0893 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -235,3 +235,9 @@ svg { color: $white; } } + +.handle { + img { + height: 1rem; + } +} diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index 83de4e61..8aa11735 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -25,42 +25,49 @@ - if @posts.present? .row .col - %table.table.table-condensed - %tbody - - @posts.each do |post| - - next unless policy(post).show? - -# - saltearse el post a menos que esté en la categoría por - la que estamos filtrando - - if @category - - next unless post.attributes.include? :categories - - next unless post.categories.value.include?(@category) - %tr - %td - = link_to post.title.value, - site_post_path(@site, post.id) - - if post.attributes.include? :draft - - if post.draft.value - %span.badge.badge-primary - = post_label_t(:draft, post: post) - - if post.attributes.include? :categories - - unless post.categories.value.empty? - %br - %small - - post.categories.value.each do |c| - = link_to c, site_posts_path(@site, category: c) + = form_tag site_posts_reorder_path, method: :post do + = submit_tag t('posts.reorder'), class: 'btn submit-reorder d-none' + -# TODO: Permitir cambiar el idioma + %table.table.table-condensed.table-draggable + %tbody + - @posts.each_with_index do |post, i| + - next unless policy(post).show? + -# + saltearse el post a menos que esté en la categoría por + la que estamos filtrando + - if @category + - next unless post.attributes.include? :categories + - next unless post.categories.value.include?(@category) + %tr + %td + .handle + = image_tag 'arrows-alt-v.svg' + = hidden_field 'post[reorder]', '', value: i, class: 'reorder' + %td + = link_to post.title.value, + site_post_path(@site, post.id) + - if post.attributes.include? :draft + - if post.draft.value + %span.badge.badge-primary + = post_label_t(:draft, post: post) + - if post.attributes.include? :categories + - unless post.categories.value.empty? + %br + %small + - post.categories.value.each do |c| + = link_to c, site_posts_path(@site, category: c) - %td= post.date.value.strftime('%F') - %td - - if policy(post).edit? - = link_to t('posts.edit'), - edit_site_post_path(@site, post.id), - class: 'btn' - - if policy(post).destroy? - = link_to t('posts.destroy'), - site_post_path(@site, post.id), - class: 'btn', - method: :delete, - data: { confirm: t('posts.confirm_destroy') } + %td= post.date.value.strftime('%F') + %td + - if policy(post).edit? + = link_to t('posts.edit'), + edit_site_post_path(@site, post.id), + class: 'btn' + - if policy(post).destroy? + = link_to t('posts.destroy'), + site_post_path(@site, post.id), + class: 'btn', + method: :delete, + data: { confirm: t('posts.confirm_destroy') } - else %h2= t('posts.none') diff --git a/config/locales/en.yml b/config/locales/en.yml index 238dbc6b..466c4324 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -347,7 +347,7 @@ en: required: label: ' (required)' feedback: 'This field cannot be empty!' - reorder_posts: 'Reorder posts' + reorder: 'Reorder posts' sort: by: 'Sort by' order: 'order' diff --git a/config/locales/es.yml b/config/locales/es.yml index 4eda8ba1..4c104fcb 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -357,7 +357,7 @@ es: required: label: ' (requerido)' feedback: '¡Este campo no puede estar vacío!' - reorder_posts: 'Reordenar artículos' + reorder: 'Reordenar artículos' sort: by: 'Ordenar por' order: 'posición' @@ -366,7 +366,7 @@ es: content: 'Cuerpo del artículo' categories: 'Todos' dropdown: 'Desplegar el menú' - new: 'Empezar un artículo nuevo' + new: 'Crear artículo' new_with_template: 'Comenzar %{template}' index: 'Artículos' edit: 'Editar' diff --git a/package.json b/package.json index 0e7986b5..92eff9b3 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@rails/webpacker": "^4.0.7", "commonmark": "^0.29.0", "input-tag": "https://0xacab.org/sutty/input-tag.git", - "table-dragger": "^1.0.2", + "table-dragger": "https://0xacab.org/sutty/table-dragger.git", "trix": "https://0xacab.org/sutty/trix.git", "zepto": "^1.2.0" }, diff --git a/yarn.lock b/yarn.lock index 7b39840a..c5afe247 100644 --- a/yarn.lock +++ b/yarn.lock @@ -579,6 +579,14 @@ "@babel/helper-regex" "^7.4.4" regexpu-core "^4.5.4" +"@babel/polyfill@^7.4.0": + version "7.7.0" + resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.7.0.tgz#e1066e251e17606ec7908b05617f9b7f8180d8f3" + integrity sha512-/TS23MVvo34dFmf8mwCisCbWGrfhbiWZSwBo6HkADTBhUa2Q/jWltyY/tpofz/b6/RIhqaqQcquptCirqIhOaQ== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.2" + "@babel/preset-env@^7.4.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.5.5.tgz#bc470b53acaa48df4b8db24a570d6da1fef53c9a" @@ -1199,13 +1207,6 @@ babel-plugin-macros@^2.5.0: cosmiconfig "^5.2.0" resolve "^1.10.0" -babel-runtime@^6.20.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -1882,9 +1883,15 @@ core-js-compat@^3.1.1: browserslist "^4.6.6" semver "^6.3.0" -core-js@^2.4.0: - version "2.5.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b" +core-js@^2.6.5: + version "2.6.10" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f" + integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA== + +core-js@^3.0.1: + version "3.3.6" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.3.6.tgz#6ad1650323c441f45379e176ed175c0d021eac92" + integrity sha512-u4oM8SHwmDuh5mWZdDg9UwNVq5s1uqq6ZDLLIs07VY+VJU91i3h4f3K/pgFvtUQPGdeStrZ+odKyfyt4EnKHfA== core-js@^3.1.3: version "3.2.1" @@ -1962,12 +1969,6 @@ crossvent@1.5.4: dependencies: custom-event "1.0.0" -crossvent@^1.5.5: - version "1.5.5" - resolved "https://registry.yarnpkg.com/crossvent/-/crossvent-1.5.5.tgz#ad20878e4921e9be73d9d6976f8b2ecd0f71a0b1" - dependencies: - custom-event "^1.0.0" - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -2179,10 +2180,6 @@ custom-event@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.0.tgz#2e4628be19dc4b214b5c02630c5971e811618062" -custom-event@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" - cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -2408,9 +2405,10 @@ dot-prop@^4.1.1: dependencies: is-obj "^1.0.0" -dragula-with-animation@^3.7.2: +dragula@^3.7.0: version "3.7.2" - resolved "https://registry.yarnpkg.com/dragula-with-animation/-/dragula-with-animation-3.7.2.tgz#9b992db2a274324325c70bb0cb752e77e1faa8af" + resolved "https://registry.yarnpkg.com/dragula/-/dragula-3.7.2.tgz#4a35c9d3981ffac1a949c29ca7285058e87393ce" + integrity sha1-SjXJ05gf+sGpScKcpyhQWOhzk84= dependencies: contra "1.9.4" crossvent "1.5.4" @@ -5696,6 +5694,11 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== +ramda@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" + integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -5811,10 +5814,6 @@ regenerate@^1.4.0: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - regenerator-runtime@^0.13.2: version "0.13.3" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" @@ -6013,9 +6012,12 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" +rxjs@^6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" + integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== + dependencies: + tslib "^1.9.0" safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" @@ -6622,14 +6624,15 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" -table-dragger@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/table-dragger/-/table-dragger-1.0.2.tgz#981f46c62fd2899b3fc5e644055ea72831949707" +"table-dragger@https://0xacab.org/sutty/table-dragger.git": + version "2.0.2" + resolved "https://0xacab.org/sutty/table-dragger.git#a5199975398dca9b3a849f5d56220fd11e68733c" dependencies: - babel-runtime "^6.20.0" - crossvent "^1.5.5" - dragula-with-animation "^3.7.2" - rx "^4.1.0" + "@babel/polyfill" "^7.4.0" + core-js "^3.0.1" + dragula "^3.7.0" + ramda "^0.26.1" + rxjs "^6.5.2" tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" From 3c8babb7d4ec1cdae1976b938bf4cf14ca985fc4 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Nov 2019 18:43:03 -0300 Subject: [PATCH 3/7] aplicar filtro de categorias antes de verificar que le usuarie pueda ver el articulo --- app/views/posts/index.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index 8aa11735..3896354a 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -31,13 +31,13 @@ %table.table.table-condensed.table-draggable %tbody - @posts.each_with_index do |post, i| - - next unless policy(post).show? -# saltearse el post a menos que esté en la categoría por la que estamos filtrando - if @category - next unless post.attributes.include? :categories - next unless post.categories.value.include?(@category) + - next unless policy(post).show? %tr %td .handle From 5c48a2da5e1cc4c56e81ba42bd13507d87531e43 Mon Sep 17 00:00:00 2001 From: f Date: Wed, 6 Nov 2019 19:35:48 -0300 Subject: [PATCH 4/7] =?UTF-8?q?ordenar=20m=C3=A1s=20exactamente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/javascripts/order.js | 2 -- app/controllers/posts_controller.rb | 9 --------- app/models/post.rb | 4 ++++ app/models/post_relation.rb | 5 +++-- app/services/post_service.rb | 19 +++++++------------ app/views/posts/index.haml | 3 ++- doc/reordenar.md | 10 ++++++++++ test/controllers/posts_controller_test.rb | 6 ++++-- 8 files changed, 30 insertions(+), 28 deletions(-) diff --git a/app/assets/javascripts/order.js b/app/assets/javascripts/order.js index 3c72a5f8..9e93a302 100644 --- a/app/assets/javascripts/order.js +++ b/app/assets/javascripts/order.js @@ -8,9 +8,7 @@ $(document).on('turbolinks:load', function() { onlyBody: true, dragHandler: '.handle' }).on('drop', function(from, to, el, mode) { - // TODO: Detenerse al llegar al elemento que se movió $('.reorder').val(function(i,v) { return i; }); - $('.submit-reorder').removeClass('d-none'); }); }); diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 635cfe23..da0fe1f9 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -11,15 +11,6 @@ class PostsController < ApplicationController @category = session[:category] = params.dig(:category) # TODO: Aplicar policy_scope @posts = @site.posts(lang: I18n.locale) - - if params[:sort_by].present? - begin - @posts.sort_by! do |p| - p.send(params[:sort_by].to_s) - end - rescue ArgumentError - end - end end def show diff --git a/app/models/post.rb b/app/models/post.rb index e89e7125..14f7ce43 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -60,6 +60,10 @@ class Post < OpenStruct path.basename end + def sha1 + Digest::SHA1.hexdigest id + end + # Levanta un error si al construir el artículo no pasamos un atributo. def default_attributes_missing(**args) DEFAULT_ATTRIBUTES.each do |attr| diff --git a/app/models/post_relation.rb b/app/models/post_relation.rb index 40ccaa96..ce1063b2 100644 --- a/app/models/post_relation.rb +++ b/app/models/post_relation.rb @@ -33,9 +33,10 @@ class PostRelation < Array alias find_generic find - def find(id) + # Encontra un post por su id convertido a SHA1 + def find(id, sha1: false) find_generic do |p| - p.id == id + p.sha1 == (sha1 ? id : Digest::SHA1.hexdigest(id)) end end diff --git a/app/services/post_service.rb b/app/services/post_service.rb index 890a77fb..768c1fdd 100644 --- a/app/services/post_service.rb +++ b/app/services/post_service.rb @@ -39,26 +39,21 @@ 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 + # Reordena todos los posts que soporten orden de acuerdo a un hash de + # ids y 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 + # { sha1 => 2, sha1 => 1, sha1 => 0 } def reorder posts = site.posts(lang: lang) - reorder = params.require(:post).permit(reorder: []) - .try(:[], :reorder) - .try(:map, &:to_i) || [] + reorder = params.require(:post).permit(reorder: {}).try(:[], :reorder) - # 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] + files = reorder.keys.map do |id| + post = posts.find(id, sha1: true) next unless post.attributes.include? :order post.usuaries << usuarie - post.order.value = new + post.order.value = reorder[id].to_i post.path.absolute end.compact diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index 3896354a..2418b693 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -42,7 +42,8 @@ %td .handle = image_tag 'arrows-alt-v.svg' - = hidden_field 'post[reorder]', '', value: i, class: 'reorder' + = hidden_field 'post[reorder]', post.sha1, + value: i, class: 'reorder' %td = link_to post.title.value, site_post_path(@site, post.id) diff --git a/doc/reordenar.md b/doc/reordenar.md index bf5c0c42..99a9ed07 100644 --- a/doc/reordenar.md +++ b/doc/reordenar.md @@ -13,3 +13,13 @@ 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. + +El problema que tiene esta implementación es que al reordenar los posts +necesitamos mantener el orden original sobre el que estabamos ordenando, +sino terminamos aplicando un orden incorrecto. Esto requiere que +implementemos una forma de mantener el orden entre sesiones e incluso +general. Nos vendría bien para no tener que recargar el sitio una y +otra vez. + +Lo más controlado sería enviar exactamente el id del post con su nueva +ubicación en el orden. Esta es la implementación anterior. diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb index 684818b8..3075948b 100644 --- a/test/controllers/posts_controller_test.rb +++ b/test/controllers/posts_controller_test.rb @@ -145,7 +145,7 @@ class PostsControllerTest < ActionDispatch::IntegrationTest test 'se pueden reordenar' do lang = I18n.available_locales.sample posts = @site.posts(lang: lang) - reorder = posts.each_index.to_a.shuffle + reorder = Hash[posts.map(&:sha1).shuffle.each_with_index.to_a] post site_posts_reorder_url(@site), headers: @authorization, @@ -156,6 +156,8 @@ class PostsControllerTest < ActionDispatch::IntegrationTest assert_equal I18n.t('post_service.reorder'), @site.repository.rugged.head.target.message assert_equal reorder, - @site.posts(lang: lang).map(&:order).map(&:value) + Hash[@site.posts(lang: lang).map do |p| + [p.sha1, p.order.value] + end] end end From 7ddc4e615036d6eb6346e31f759561aa54cda764 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Nov 2019 13:06:40 -0300 Subject: [PATCH 5/7] ordenar! --- app/controllers/posts_controller.rb | 4 +++- app/models/metadata_date.rb | 3 +++ app/models/post_relation.rb | 16 ++++++++++++++++ app/views/posts/index.haml | 11 ++++++++++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index da0fe1f9..14af15fa 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -9,8 +9,10 @@ class PostsController < ApplicationController authorize Post @site = find_site @category = session[:category] = params.dig(:category) + @layout = params.dig(:layout).try :to_sym # TODO: Aplicar policy_scope - @posts = @site.posts(lang: I18n.locale) + @posts = @site.posts(lang: I18n.locale) + @posts.sort_by! :order, :date end def show diff --git a/app/models/metadata_date.rb b/app/models/metadata_date.rb index ea0bad50..7e26c00e 100644 --- a/app/models/metadata_date.rb +++ b/app/models/metadata_date.rb @@ -1,4 +1,7 @@ # frozen_string_literal: true class MetadataDate < MetadataTemplate + def default_value + Date.today + end end diff --git a/app/models/post_relation.rb b/app/models/post_relation.rb index ce1063b2..22eb5ca9 100644 --- a/app/models/post_relation.rb +++ b/app/models/post_relation.rb @@ -31,6 +31,22 @@ class PostRelation < Array post end + alias sort_by_generic! sort_by! + + # Permite ordenar los artículos por sus atributos + # + # XXX: Prestar atención cuando estamos mezclando artículos con + # diferentes tipos de atributos. + def sort_by!(*attrs) + sort_by_generic! do |post| + attrs.map do |attr| + return 0 unless post.attributes.include? attr + + post.public_send(attr).value + end + end + end + alias find_generic find # Encontra un post por su id convertido a SHA1 diff --git a/app/views/posts/index.haml b/app/views/posts/index.haml index 2418b693..12730e82 100644 --- a/app/views/posts/index.haml +++ b/app/views/posts/index.haml @@ -37,6 +37,8 @@ - if @category - next unless post.attributes.include? :categories - next unless post.categories.value.include?(@category) + - if @layout + - next unless post.layout.name == @layout - next unless policy(post).show? %tr %td @@ -45,6 +47,10 @@ = hidden_field 'post[reorder]', post.sha1, value: i, class: 'reorder' %td + %small + = link_to post.layout.name.to_s.humanize, + site_posts_path(@site, layout: post.layout.name) + %br/ = link_to post.title.value, site_post_path(@site, post.id) - if post.attributes.include? :draft @@ -58,7 +64,10 @@ - post.categories.value.each do |c| = link_to c, site_posts_path(@site, category: c) - %td= post.date.value.strftime('%F') + %td + = post.date.value.strftime('%F') + %br/ + = post.try(:order).try(:value) %td - if policy(post).edit? = link_to t('posts.edit'), From 9c54518c8e9457000329d73e3f01363cbc48ed31 Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Nov 2019 13:08:14 -0300 Subject: [PATCH 6/7] metadata file & url --- app/models/metadata_file.rb | 106 +++++++++++++++++++++++ app/models/metadata_image.rb | 97 +-------------------- app/models/metadata_url.rb | 4 + app/views/posts/attribute_ro/_file.haml | 7 ++ app/views/posts/attribute_ro/_order.haml | 3 + app/views/posts/attribute_ro/_url.haml | 3 + app/views/posts/attributes/_file.haml | 22 +++++ app/views/posts/attributes/_order.haml | 1 + app/views/posts/attributes/_url.haml | 6 ++ 9 files changed, 153 insertions(+), 96 deletions(-) create mode 100644 app/models/metadata_file.rb create mode 100644 app/models/metadata_url.rb create mode 100644 app/views/posts/attribute_ro/_file.haml create mode 100644 app/views/posts/attribute_ro/_order.haml create mode 100644 app/views/posts/attribute_ro/_url.haml create mode 100644 app/views/posts/attributes/_file.haml create mode 100644 app/views/posts/attributes/_order.haml create mode 100644 app/views/posts/attributes/_url.haml diff --git a/app/models/metadata_file.rb b/app/models/metadata_file.rb new file mode 100644 index 00000000..06e69be0 --- /dev/null +++ b/app/models/metadata_file.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +# Define un campo de archivo +class MetadataFile < MetadataTemplate + # Una ruta vacía a la imagen con una descripción vacía + def default_value + { 'path' => nil, 'description' => nil } + end + + def empty? + value == default_value + end + + def validate + super + + errors << I18n.t('metadata.image.path_required') if path_missing? + + errors.compact! + errors.empty? + end + + # Determina si necesitamos la imagen pero no la tenemos + def path_missing? + required && !value['path'].blank? + end + + # Determina si el archivo ya fue subido + def uploaded? + value['path'].is_a?(String) + end + + # Determina si la ruta es opcional pero deja pasar si la ruta se + # especifica + def path_optional? + !required && value['path'].blank? + end + + # Asociar la imagen subida al sitio y obtener la ruta + def save + return true if uploaded? + return true if path_optional? + return false unless hardlink.zero? + + # Modificar el valor actual + value['path'] = relative_destination_path + + true + end + + def to_param + { name => %i[description path] } + end + + # Almacena el archivo en el sitio y lo devuelve + # + # @return ActiveStorage::Attachment + def static_file + ActiveRecord::Base.connection_pool.with_connection do + if uploaded? + blob = ActiveStorage::Blob.find_by(key: key_from_path) + @static_file ||= site.static_files.find_by(blob_id: blob.id) + elsif site.static_files.attach(value['path']) + @static_file ||= site.static_files.last + end + end + end + + private + + 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'], '.*') + end + + # Hacemos un link duro para colocar el archivo dentro del repositorio + # y no duplicar el espacio que ocupan. Esto requiere que ambos + # directorios estén dentro del mismo punto de montaje. + def hardlink + FileUtils.mkdir_p File.dirname(destination_path) + FileUtils.ln uploaded_path, destination_path + end + + def extension + static_file.blob.content_type.split('/').last + end + + # Obtener la ruta al archivo + # https://stackoverflow.com/a/53908358 + def uploaded_relative_path + ActiveStorage::Blob.service.path_for(static_file.key) + end + + def uploaded_path + Rails.root.join uploaded_relative_path + end + + def relative_destination_path + File.join('public', [static_file.key, extension].join('.')) + end + + def destination_path + File.join(site.path, relative_destination_path) + end +end diff --git a/app/models/metadata_image.rb b/app/models/metadata_image.rb index 9da909a6..50109ae8 100644 --- a/app/models/metadata_image.rb +++ b/app/models/metadata_image.rb @@ -1,37 +1,16 @@ # frozen_string_literal: true # Define un campo de imagen -# TODO: Validar que sea una imagen -class MetadataImage < MetadataTemplate - # Una ruta vacía a la imagen con una descripción vacía - def default_value - { 'path' => nil, 'description' => nil } - end - - def empty? - value == default_value - end - +class MetadataImage < MetadataFile def validate super - errors << I18n.t('metadata.image.path_required') if path_missing? errors << I18n.t('metadata.image.not_an_image') unless image? errors.compact! errors.empty? end - # Determina si necesitamos la imagen pero no la tenemos - def path_missing? - required && !value['path'].blank? - end - - # Determina si el archivo ya fue subido - def uploaded? - value['path'].is_a?(String) - end - # Determina si es una imagen antes de subirla def image? if value['path'].is_a? ActionDispatch::Http::UploadedFile @@ -44,78 +23,4 @@ class MetadataImage < MetadataTemplate true end end - - # Determina si la ruta es opcional pero deja pasar si la ruta se - # especifica - def path_optional? - !required && value['path'].blank? - end - - # Asociar la imagen subida al sitio y obtener la ruta - def save - return true if uploaded? - return true if path_optional? - return false unless hardlink.zero? - - # Modificar el valor actual - value['path'] = relative_destination_path - - true - end - - def to_param - { name => %i[description path] } - end - - # Almacena el archivo en el sitio y lo devuelve - # - # @return ActiveStorage::Attachment - def static_file - ActiveRecord::Base.connection_pool.with_connection do - if uploaded? - blob = ActiveStorage::Blob.find_by(key: key_from_path) - @static_file ||= site.static_files.find_by(blob_id: blob.id) - elsif site.static_files.attach(value['path']) - @static_file ||= site.static_files.last - end - end - end - - private - - 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'], '.*') - end - - # Hacemos un link duro para colocar el archivo dentro del repositorio - # y no duplicar el espacio que ocupan. Esto requiere que ambos - # directorios estén dentro del mismo punto de montaje. - def hardlink - FileUtils.mkdir_p File.dirname(destination_path) - FileUtils.ln uploaded_path, destination_path - end - - def extension - static_file.blob.content_type.split('/').last - end - - # Obtener la ruta al archivo - # https://stackoverflow.com/a/53908358 - def uploaded_relative_path - ActiveStorage::Blob.service.path_for(static_file.key) - end - - def uploaded_path - Rails.root.join uploaded_relative_path - end - - def relative_destination_path - File.join('public', [static_file.key, extension].join('.')) - end - - def destination_path - File.join(site.path, relative_destination_path) - end end diff --git a/app/models/metadata_url.rb b/app/models/metadata_url.rb new file mode 100644 index 00000000..125d5cdb --- /dev/null +++ b/app/models/metadata_url.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +# Un campo de URL +class MetadataUrl < MetadataString; end diff --git a/app/views/posts/attribute_ro/_file.haml b/app/views/posts/attribute_ro/_file.haml new file mode 100644 index 00000000..55c086f5 --- /dev/null +++ b/app/views/posts/attribute_ro/_file.haml @@ -0,0 +1,7 @@ +%tr{ id: attribute } + %th= post_label_t(attribute, :path, post: post) + %td + - if metadata.value['path'].present? + %figure + = link_to url_for(metadata.static_file) + %figcaption= metadata.value['description'] diff --git a/app/views/posts/attribute_ro/_order.haml b/app/views/posts/attribute_ro/_order.haml new file mode 100644 index 00000000..31dd8f0d --- /dev/null +++ b/app/views/posts/attribute_ro/_order.haml @@ -0,0 +1,3 @@ +%tr{ id: attribute } + %th= post_label_t(attribute, post: post) + %td= metadata.value diff --git a/app/views/posts/attribute_ro/_url.haml b/app/views/posts/attribute_ro/_url.haml new file mode 100644 index 00000000..74c240bd --- /dev/null +++ b/app/views/posts/attribute_ro/_url.haml @@ -0,0 +1,3 @@ +%tr{ id: attribute } + %th= post_label_t(attribute, post: post) + %td= link_to metadata.value diff --git a/app/views/posts/attributes/_file.haml b/app/views/posts/attributes/_file.haml new file mode 100644 index 00000000..cf2d67b4 --- /dev/null +++ b/app/views/posts/attributes/_file.haml @@ -0,0 +1,22 @@ +.form-group + - if metadata.uploaded? + = hidden_field_tag "post[#{attribute}][path]", metadata.value['path'] + + .custom-file + = file_field(*field_name_for('post', attribute, :path), + **field_options(attribute, metadata), + class: "custom-file-input #{invalid(post, attribute)}", + data: { preview: "#{attribute}-preview" }) + = 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 + = 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 diff --git a/app/views/posts/attributes/_order.haml b/app/views/posts/attributes/_order.haml new file mode 100644 index 00000000..0aab9802 --- /dev/null +++ b/app/views/posts/attributes/_order.haml @@ -0,0 +1 @@ +-# nada diff --git a/app/views/posts/attributes/_url.haml b/app/views/posts/attributes/_url.haml new file mode 100644 index 00000000..eff3f1d5 --- /dev/null +++ b/app/views/posts/attributes/_url.haml @@ -0,0 +1,6 @@ +.form-group + = label_tag "post_#{attribute}", post_label_t(attribute, post: post) + = url_field 'post', attribute, value: metadata.value, + **field_options(attribute, metadata) + = render 'posts/attribute_feedback', + post: post, attribute: attribute, metadata: metadata From 0a6b07a4f404f8106061a54bdb49f68fb5b19c7c Mon Sep 17 00:00:00 2001 From: f Date: Thu, 7 Nov 2019 13:09:01 -0300 Subject: [PATCH 7/7] todavia no recibimos correo --- config/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index b2ec1fe0..b2be99a3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -10,7 +10,7 @@ require 'active_record/railtie' require 'active_storage/engine' require 'action_controller/railtie' require 'action_mailer/railtie' -require 'action_mailbox/engine' +# require 'action_mailbox/engine' require 'action_text/engine' require 'action_view/railtie' # require "action_cable/engine"