diff --git a/app/controllers/user_reverts_controller.rb b/app/controllers/user_reverts_controller.rb new file mode 100644 index 000000000..8dbc3ff13 --- /dev/null +++ b/app/controllers/user_reverts_controller.rb @@ -0,0 +1,15 @@ +class UserRevertsController < ApplicationController + before_filter :janitor_only + + def new + @user = User.find(params[:user_id]) + end + + def create + user = User.find(params[:user_id]) + revert = UserRevert.new(user.id) + revert.process + redirect_to(user_path(user.id)) + end +end + diff --git a/app/logical/user_revert.rb b/app/logical/user_revert.rb new file mode 100644 index 000000000..b1d11a066 --- /dev/null +++ b/app/logical/user_revert.rb @@ -0,0 +1,28 @@ +# reverts all changes made by a user +class UserRevert + THRESHOLD = 1_000 + class TooManyChangesError < RuntimeError ; end + + attr_reader :user_id + + def initialize(user_id) + @user_id = user_id + end + + def process + validate! + revert_post_changes + end + + def validate! + if PostVersion.where(updater_id: user_id).count > THRESHOLD + raise TooManyChangesError.new("This user has too many changes to be reverted") + end + end + + def revert_post_changes + PostVersion.where(updater_id: user_id).find_each do |x| + x.undo! + end + end +end diff --git a/app/presenters/user_presenter.rb b/app/presenters/user_presenter.rb index e226e6aa9..da8587a2c 100644 --- a/app/presenters/user_presenter.rb +++ b/app/presenters/user_presenter.rb @@ -149,7 +149,7 @@ class UserPresenter end def post_version_count(template) - template.link_to(user.post_update_count, template.post_versions_path(:search => {:updater_id => user.id})) + template.link_to(user.post_update_count, template.post_versions_path(:lr => user.id, :search => {:updater_id => user.id})) end def note_version_count(template) diff --git a/app/views/post_versions/index.html.erb b/app/views/post_versions/index.html.erb index 61937fd30..c249f7329 100644 --- a/app/views/post_versions/index.html.erb +++ b/app/views/post_versions/index.html.erb @@ -7,6 +7,10 @@ <% else %> <%= render "listing", :post_versions => @post_versions %> + <% if params[:lr] && CurrentUser.is_janitor? %> +

<%= link_to "Revert this user's changes", new_user_revert_path(:user_id => params[:lr]) %>

+ <% end %> + <%= numbered_paginator(@post_versions) %> <% end %> diff --git a/app/views/user_reverts/new.html.erb b/app/views/user_reverts/new.html.erb new file mode 100644 index 000000000..265560da1 --- /dev/null +++ b/app/views/user_reverts/new.html.erb @@ -0,0 +1,19 @@ +
+
+

Revert Changes

+ +

You are about to revert all changes made by <%= link_to_user @user %>. Continue?

+ + <%= form_tag(user_revert_path) do %> + <%= hidden_field_tag :user_id, params[:user_id] %> + + <%= submit_tag "Yes" %> + <% end %> +
+
+ +<%= render "users/secondary_links" %> + +<% content_for(:page_title) do %> + User Revert - <%= Danbooru.config.app_name %> +<% end %> diff --git a/app/views/users/_statistics.html.erb b/app/views/users/_statistics.html.erb index 2eab13b36..55da1ba23 100644 --- a/app/views/users/_statistics.html.erb +++ b/app/views/users/_statistics.html.erb @@ -56,7 +56,12 @@ Post Changes - <%= presenter.post_version_count(self) %> + + <%= presenter.post_version_count(self) %> + <% if CurrentUser.is_janitor? %> + [<%= link_to "revert all", new_user_revert_path(user_id: user.id) %>] + <% end %> + diff --git a/config/routes.rb b/config/routes.rb index 9f9f6947f..ffdcc6d9e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -299,6 +299,7 @@ Rails.application.routes.draw do post :reject end end + resource :user_revert, :only => [:new, :create] resources :wiki_pages do member do put :revert diff --git a/test/unit/user_revert_test.rb b/test/unit/user_revert_test.rb new file mode 100644 index 000000000..f50d51bba --- /dev/null +++ b/test/unit/user_revert_test.rb @@ -0,0 +1,47 @@ +require 'test_helper' + +class UserRevertTest < ActiveSupport::TestCase + context "Reverting a user's changes" do + setup do + @creator = FactoryGirl.create(:user) + @user = FactoryGirl.create(:user) + CurrentUser.user = @user + CurrentUser.ip_addr = "127.0.0.1" + + CurrentUser.scoped(@creator) do + @parent = FactoryGirl.create(:post) + @post = FactoryGirl.create(:post, :tag_string => "aaa bbb ccc", :rating => "q", :source => "xyz") + end + + @post.stubs(:merge_version?).returns(false) + @post.update_attributes(:tag_string => "bbb ccc xxx", :source => "", :rating => "e") + end + + teardown do + CurrentUser.user = nil + CurrentUser.ip_addr = nil + end + + subject { UserRevert.new(@user.id) } + + should "have the correct data" do + assert_equal("bbb ccc xxx", @post.tag_string) + assert_equal("", @post.source) + assert_equal("e", @post.rating) + end + + context "when processed" do + setup do + subject.process + @post.reload + end + + should "revert the user's changes" do + assert_equal("aaa bbb ccc", @post.tag_string) + assert_equal("xyz", @post.source) + assert_equal("q", @post.rating) + end + end + + end +end