From d29bbbbd71dba072ff993268579acf2ec36351f2 Mon Sep 17 00:00:00 2001 From: evazion Date: Fri, 27 Sep 2019 20:26:39 -0500 Subject: [PATCH] Fix #4178: add ability to mass undo tag edits. Adds checkboxes to the /post_versions index allowing you to select and undo multiple versions at once. --- app/controllers/post_versions_controller.rb | 11 ++--- app/javascript/packs/application.js | 1 + .../src/javascripts/post_version.js | 45 +++++++++++++++++++ .../src/styles/specific/post_versions.scss | 9 ++++ app/models/post_archive.rb | 16 +++++-- .../post_versions/_revert_listing.html.erb | 30 ++++++++----- .../post_versions/_secondary_links.html.erb | 3 ++ .../post_versions/_standard_listing.html.erb | 27 ++++++----- app/views/post_versions/undo.js.erb | 2 +- test/system/post_version_test.rb | 33 ++++++++++++++ test/test_helpers/system_test_helper.rb | 7 +++ 11 files changed, 150 insertions(+), 34 deletions(-) create mode 100644 app/javascript/src/javascripts/post_version.js create mode 100644 app/javascript/src/styles/specific/post_versions.scss create mode 100644 test/system/post_version_test.rb diff --git a/app/controllers/post_versions_controller.rb b/app/controllers/post_versions_controller.rb index e202c6f22..7c9935ea0 100644 --- a/app/controllers/post_versions_controller.rb +++ b/app/controllers/post_versions_controller.rb @@ -2,8 +2,10 @@ class PostVersionsController < ApplicationController before_action :member_only, except: [:index, :search] before_action :check_availabililty respond_to :html, :xml, :json + respond_to :js, only: [:undo] def index + # XXX statement timeouts @post_versions = PostArchive.includes(:updater, post: [:versions]).search(search_params).paginate(params[:page], :limit => params[:limit], :search_count => params[:search]) respond_with(@post_versions) end @@ -13,14 +15,9 @@ class PostVersionsController < ApplicationController def undo @post_version = PostArchive.find(params[:id]) + @post_version.undo! - if @post_version.post.visible? - @post_version.undo! - end - - respond_with(@post_version) do |format| - format.js - end + respond_with(@post_version) end private diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 92dce5c9d..da34763a3 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -36,6 +36,7 @@ export { default as Note } from '../src/javascripts/notes.js'; export { default as Post } from '../src/javascripts/posts.js.erb'; export { default as PostModeMenu } from '../src/javascripts/post_mode_menu.js'; export { default as PostTooltip } from '../src/javascripts/post_tooltips.js'; +export { default as PostVersion } from '../src/javascripts/post_version.js'; export { default as RelatedTag } from '../src/javascripts/related_tag.js'; export { default as Shortcuts } from '../src/javascripts/shortcuts.js'; export { default as Upload } from '../src/javascripts/uploads.js.erb'; diff --git a/app/javascript/src/javascripts/post_version.js b/app/javascript/src/javascripts/post_version.js new file mode 100644 index 000000000..0125b693e --- /dev/null +++ b/app/javascript/src/javascripts/post_version.js @@ -0,0 +1,45 @@ +import Utility from './utility'; + +let PostVersion = {}; + +PostVersion.initialize_all = function() { + if ($("#c-post-versions #a-index").length) { + PostVersion.initialize_undo(); + } +}; + +PostVersion.initialize_undo = function() { + /* Expand the clickable area of the checkbox to the entire table cell. */ + $(".post-version-select-column").on("click.danbooru", function(event) { + $(event.target).find(".post-version-select-checkbox:not(:disabled)").prop("checked", (_, checked) => !checked).change(); + }); + + $("#post-version-select-all-checkbox").on("change.danbooru", function(event) { + $("td .post-version-select-checkbox:not(:disabled)").prop("checked", $("#post-version-select-all-checkbox").prop("checked")).change(); + }); + + $(".post-version-select-checkbox").on("change.danbooru", function(event) { + let checked = $("td .post-version-select-checkbox:checked"); + $("#subnav-undo-selected-link").text(`Undo selected (${checked.length})`).toggle(checked.length > 0); + }); + + $("#subnav-undo-selected-link").on("click.danbooru", PostVersion.undo_selected); +}; + +PostVersion.undo_selected = async function () { + event.preventDefault(); + + let updated = 0; + let selected_rows = $("td .post-version-select-checkbox:checked").parents("tr"); + + for (let row of selected_rows) { + let id = $(row).data("post-version-id"); + await $.ajax(`/post_versions/${id}/undo.json`, { method: "PUT" }); + + updated++; + Utility.notice(`${updated}/${selected_rows.length} changes undone.`); + } +}; + +$(document).ready(PostVersion.initialize_all); +export default PostVersion; diff --git a/app/javascript/src/styles/specific/post_versions.scss b/app/javascript/src/styles/specific/post_versions.scss new file mode 100644 index 000000000..b832e3622 --- /dev/null +++ b/app/javascript/src/styles/specific/post_versions.scss @@ -0,0 +1,9 @@ +body.c-post-versions.a-index { + #subnav-undo-selected-link { + display: none; + } + + .post-version-select-column { + min-width: 2em; + } +} diff --git a/app/models/post_archive.rb b/app/models/post_archive.rb index 12e3b006c..1c732e2fd 100644 --- a/app/models/post_archive.rb +++ b/app/models/post_archive.rb @@ -1,4 +1,5 @@ class PostArchive < ApplicationRecord + class RevertError < Exception ; end extend Memoist belongs_to :post @@ -218,7 +219,9 @@ class PostArchive < ApplicationRecord source.gsub(/^http:\/\//, "").sub(/\/.+/, "") end - def undo + def undo! + raise RevertError unless post.visible? + added = changes[:added_tags] - changes[:obsolete_added_tags] removed = changes[:removed_tags] - changes[:obsolete_removed_tags] @@ -239,11 +242,16 @@ class PostArchive < ApplicationRecord post.tag_string = "#{post.tag_string} #{tag}".strip end end + + post.save! end - def undo! - undo - post.save! + def can_undo?(user) + version > 1 && post&.visible? && user.is_member? + end + + def can_revert_to?(user) + post&.visible? && user.is_member? end def updater_name diff --git a/app/views/post_versions/_revert_listing.html.erb b/app/views/post_versions/_revert_listing.html.erb index 30e7f9cab..55ff0d624 100644 --- a/app/views/post_versions/_revert_listing.html.erb +++ b/app/views/post_versions/_revert_listing.html.erb @@ -2,6 +2,11 @@ + <% if CurrentUser.user.is_builder? %> + + <% end %> @@ -18,7 +23,12 @@ <% @post_versions.each do |post_version| %> - + + <% if CurrentUser.user.is_builder? %> + + <% end %> <% end %> - <% if CurrentUser.is_member? %> - - <% end %> + <% end %> diff --git a/app/views/post_versions/_secondary_links.html.erb b/app/views/post_versions/_secondary_links.html.erb index 1c193a4c0..e3d45135c 100644 --- a/app/views/post_versions/_secondary_links.html.erb +++ b/app/views/post_versions/_secondary_links.html.erb @@ -4,4 +4,7 @@ <%= subnav_link_to "Search", search_post_versions_path %> <%= subnav_link_to "Changes", post_versions_path %> <%= subnav_link_to "Help", wiki_pages_path(:title => "help:posts") %> + <% if params[:action] == "index" %> + | <%= subnav_link_to "Undo selected", "" %> + <% end %> <% end %> diff --git a/app/views/post_versions/_standard_listing.html.erb b/app/views/post_versions/_standard_listing.html.erb index d796bb59d..cb188e718 100644 --- a/app/views/post_versions/_standard_listing.html.erb +++ b/app/views/post_versions/_standard_listing.html.erb @@ -2,6 +2,11 @@
+ + Post Date User
+ > + <%= link_to("#{post_version.post_id}.#{post_version.id}", post_path(post_version.post_id)) %> <%= compact_time(post_version.updated_at) %> @@ -34,16 +44,14 @@ <%= post_version_diff(post_version) %> - <% if post_version.visible? %> - <% if post_version.version != 1 %> - <%= link_to "Undo", undo_post_version_path(post_version), :method => :put, :remote => true %> | - <% end %> - <%= link_to "Revert to", revert_post_path(post_version.post_id, :version_id => post_version.id), :method => :put, :remote => true %> - <% end %> - + <% if post_version.can_undo?(CurrentUser.user) %> + <%= link_to "Undo", undo_post_version_path(post_version), method: :put, remote: true, class: "post-version-undo-link" %> | + <% end %> + <% if post_version.can_revert_to?(CurrentUser.user) %> + <%= link_to "Revert to", revert_post_path(post_version.post_id, version_id: post_version.id), method: :put, remote: true %> + <% end %> +
+ <% if CurrentUser.user.is_builder? %> + + <% end %> @@ -18,7 +23,12 @@ <% @post_versions.each do |post_version| %> - + + <% if CurrentUser.user.is_builder? %> + + <% end %> <% end %> - <% if CurrentUser.is_member? %> - - <% end %> + <% end %>
+ + Post Date User
+ > + <%= link_to("#{post_version.post_id}.#{post_version.id}", post_path(post_version.post_id)) %> <%= link_to "ยป", post_versions_path(search: {post_id: post_version.post_id}) %> @@ -37,18 +47,13 @@ <%= post_version_diff(post_version) %> - <% if post_version.visible? %> - <% if post_version.version != 1 %> - <%= link_to "Undo", undo_post_version_path(post_version), :method => :put, :remote => true %> - <% end %> - <% end %> - + <% if post_version.can_undo?(CurrentUser.user) %> + <%= link_to "Undo", undo_post_version_path(post_version), method: :put, remote: true, class: "post-version-undo-link" %> + <% end %> +
- diff --git a/app/views/post_versions/undo.js.erb b/app/views/post_versions/undo.js.erb index bcebe9d37..0a29cb84b 100644 --- a/app/views/post_versions/undo.js.erb +++ b/app/views/post_versions/undo.js.erb @@ -1 +1 @@ -location.reload(); \ No newline at end of file +Danbooru.Utility.notice("1 change undone."); diff --git a/test/system/post_version_test.rb b/test/system/post_version_test.rb new file mode 100644 index 000000000..56bae2d85 --- /dev/null +++ b/test/system/post_version_test.rb @@ -0,0 +1,33 @@ +require "application_system_test_case" + +class PostVersionTest < ApplicationSystemTestCase + context "Post versions" do + setup do + @user = create(:builder_user) + + as @user do + @post = create(:post, tag_string: "tagme") + travel 2.hours + @post.update!(tag_string: "touhou") + travel 2.hours + @post.update!(tag_string: "touhou bkub") + travel 2.hours + end + + signin @user + visit post_versions_path + end + + context "clicking the undo selected button" do + should "undo all selected post versions" do + check id: "post-version-select-all-checkbox" + assert all("td .post-version-select-checkbox:not(:disabled)").all?(&:checked?) + + click_link "subnav-undo-selected-link" + assert_selector "#notice span.prose", text: "2/2 changes undone." + + assert_equal("tagme", @post.reload.tag_string) + end + end + end +end diff --git a/test/test_helpers/system_test_helper.rb b/test/test_helpers/system_test_helper.rb index eeefffec6..9aa865ec8 100644 --- a/test/test_helpers/system_test_helper.rb +++ b/test/test_helpers/system_test_helper.rb @@ -6,4 +6,11 @@ module SystemTestHelper fill_in "Password confirmation", with: password click_button "Sign up" end + + def signin(user) + visit new_session_path + fill_in "Name", with: user.name + fill_in "Password", with: user.password + click_button "Submit" + end end