summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/users_controller.rb15
-rw-r--r--app/views/context_menus/users.html.erb6
-rw-r--r--app/views/users/bulk_destroy.html.erb22
-rw-r--r--config/locales/en.yml2
-rw-r--r--config/routes.rb3
-rw-r--r--test/functional/users_controller_test.rb57
6 files changed, 105 insertions, 0 deletions
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 5cd9eb931..0d80fc33a 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -215,6 +215,21 @@ class UsersController < ApplicationController
end
end
+ def bulk_destroy
+ @users = User.logged.where(id: params[:ids]).where.not(id: User.current)
+ (render_404; return) unless @users.any?
+
+ if params[:lock]
+ @users.update_all status: User::STATUS_LOCKED
+ flash[:notice] = l(:notice_successful_update)
+ redirect_to users_path
+ elsif params[:confirm] == I18n.t(:general_text_Yes)
+ @users.destroy_all
+ flash[:notice] = l(:notice_successful_delete)
+ redirect_to users_path
+ end
+ end
+
private
def find_user(logged = true)
diff --git a/app/views/context_menus/users.html.erb b/app/views/context_menus/users.html.erb
index 2a0c12be1..3bd4f7d83 100644
--- a/app/views/context_menus/users.html.erb
+++ b/app/views/context_menus/users.html.erb
@@ -20,5 +20,11 @@
method: :delete, class: 'icon icon-del' %>
</li>
<% end %>
+ <% else %>
+ <li>
+ <%= context_menu_link l(:button_delete),
+ {controller: 'users', action: 'bulk_destroy', ids: @users.map(&:id)},
+ method: :delete, class: 'icon icon-del' %>
+ </li>
<% end %>
</ul>
diff --git a/app/views/users/bulk_destroy.html.erb b/app/views/users/bulk_destroy.html.erb
new file mode 100644
index 000000000..f18eb815f
--- /dev/null
+++ b/app/views/users/bulk_destroy.html.erb
@@ -0,0 +1,22 @@
+<%= title l(:label_confirmation) %>
+
+<%= form_tag(bulk_destroy_users_path(ids: @users.map(&:id)), method: :delete) do %>
+<div class="warning">
+
+<p><%= simple_format l :text_users_bulk_destroy_head %></p>
+
+<% @users.each do |user| %>
+ <p><strong><%= user.name %></strong> (<%= user.login %>)</p>
+<% end %>
+
+<p><%= l :text_users_bulk_destroy_confirm, yes: l(:general_text_Yes) %></p>
+<p><%= text_field_tag 'confirm' %></p>
+
+</div>
+
+<p>
+ <%= submit_tag l(:button_delete), class: 'btn-alert btn-small' %>
+ <%= submit_tag l(:button_lock), class: 'btn', name: 'lock' %>
+ <%= link_to l(:button_cancel), users_path %>
+</p>
+<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 8f80d309e..335614dfb 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1230,6 +1230,8 @@ en:
text_project_close_confirmation: Are you sure you want to close the '%{value}' project to make it read-only?
text_project_reopen_confirmation: Are you sure you want to reopen the '%{value}' project?
text_project_archive_confirmation: Are you sure you want to archive the '%{value}' project?
+ text_users_bulk_destroy_head: 'You are about to delete the following users and remove all references to them. This cannot be undone. Often, locking users instead of deleting them is the better solution.'
+ text_users_bulk_destroy_confirm: 'To confirm, please enter "%{yes}" below.'
text_workflow_edit: Select a role and a tracker to edit the workflow
text_are_you_sure: Are you sure?
text_journal_changed: "%{label} changed from %{old} to %{new}"
diff --git a/config/routes.rb b/config/routes.rb
index f2106d90f..fa65b4417 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -110,6 +110,9 @@ Rails.application.routes.draw do
match '/users/context_menu', to: 'context_menus#users', as: :users_context_menu, via: [:get, :post]
resources :users do
+ collection do
+ delete 'bulk_destroy'
+ end
resources :memberships, :controller => 'principal_memberships'
resources :email_addresses, :only => [:index, :create, :update, :destroy]
end
diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb
index af2f1871f..c17737fe1 100644
--- a/test/functional/users_controller_test.rb
+++ b/test/functional/users_controller_test.rb
@@ -1092,4 +1092,61 @@ class UsersControllerTest < Redmine::ControllerTest
assert_response 422
end
end
+
+ def test_bulk_destroy
+ assert_difference 'User.count', -1 do
+ delete :bulk_destroy, :params => {:ids => [2], :confirm => 'Yes'}
+ end
+ assert_redirected_to '/users'
+ assert_nil User.find_by_id(2)
+ end
+
+ def test_bulk_destroy_should_not_destroy_current_user
+ assert_difference 'User.count', -1 do
+ delete :bulk_destroy, :params => {:ids => [2, 1], :confirm => 'Yes'}
+ end
+ assert_redirected_to '/users'
+ assert_nil User.find_by_id(2)
+ end
+
+ def test_bulk_destroy_with_lock_param_should_lock_instead
+ assert_no_difference 'User.count' do
+ delete :bulk_destroy, :params => {:ids => [2], :lock => 'lock'}
+ end
+ assert_redirected_to '/users'
+ assert User.find_by_id(2).locked?
+ end
+
+ def test_bulk_destroy_should_require_confirmation
+ assert_no_difference 'User.count' do
+ delete :bulk_destroy, :params => {:ids => [2]}
+ end
+ assert_response :success
+ assert_select '.warning', :text => /You are about to delete the following users/
+ end
+
+ def test_bulk_destroy_should_require_correct_confirmation
+ assert_no_difference 'User.count' do
+ delete :bulk_destroy, :params => {:ids => [2], :confirm => 'wrong'}
+ end
+ assert_response :success
+ assert_select '.warning', :text => /You are about to delete the following users/
+ end
+
+ def test_bulk_destroy_should_be_denied_for_non_admin_users
+ @request.session[:user_id] = 3
+
+ assert_no_difference 'User.count' do
+ delete :bulk_destroy, :params => {:ids => [2], :confirm => 'Yes'}
+ end
+ assert_response 403
+ end
+
+ def test_bulk_destroy_should_be_denied_for_anonymous
+ assert User.find(6).anonymous?
+ assert_no_difference 'User.count' do
+ delete :bulk_destroy, :params => {:ids => [6], :confirm => "Yes"}
+ end
+ assert_response 404
+ end
end