Project

General

Profile

Actions

Patch #43640

closed

Improve Group Controller – Enable Adding/Removing Multiple Users from Users View

Added by Florian Walchshofer 5 months ago. Updated 15 days ago.

Status:
Closed
Priority:
Normal
Category:
Groups
Target version:

Description

To improve efficiency in group management,
extend the functionality to allow bulk operations for adding and removing multiple users directly from the Users view (Administration)

This enhancement should include updates to the controller logic, and users view

Patch changes:
  • Group Controller - rename method remove_user to remove_users
    • add new route - PATCH - group_remove_users_path - /groups/:id/remove_users
    • keep the existing DELETE route, but point it to the renamed method that handles multiple user removal.
      delete 'groups/:id/users/:user_id', :to => 'groups#remove_users', :id => /\d+/, :as => 'group_user'
      post   'groups/:id/remove_users',   :to => 'groups#remove_users', :id => /\d+/, :as => 'group_remove_users'
      
  • Users View - Administration
    • add multiple users to a group (the controller already existed)
    • remove multiple users to a group
  • Tests & Localization

Files

01-group-controller-remove_users.patch (5.35 KB) 01-group-controller-remove_users.patch Florian Walchshofer, 2026-01-02 00:07
02-tests-group-controller-remove_users.patch (3.43 KB) 02-tests-group-controller-remove_users.patch Florian Walchshofer, 2026-01-02 00:07
03-localization-group-controller-remove_users.patch (22.6 KB) 03-localization-group-controller-remove_users.patch Florian Walchshofer, 2026-01-02 00:07
AddUsersToGroup.png (45.9 KB) AddUsersToGroup.png Florian Walchshofer, 2026-01-02 00:07
v2-03-localization-group-controller-remove_users.patch (33 KB) v2-03-localization-group-controller-remove_users.patch Florian Walchshofer, 2026-01-03 22:33
v2-01-group-controller-remove_users.patch (7.26 KB) v2-01-group-controller-remove_users.patch Florian Walchshofer, 2026-01-03 22:33
v2-02-tests-group-controller-remove_users.patch (13 KB) v2-02-tests-group-controller-remove_users.patch Florian Walchshofer, 2026-01-03 22:33
ConfirmDialog.png (90.5 KB) ConfirmDialog.png Florian Walchshofer, 2026-01-03 22:34
0001-localization-group-controller-remove_users.patch (32.6 KB) 0001-localization-group-controller-remove_users.patch Florian Walchshofer, 2026-03-09 21:35
0001-group-controller-remove_users.patch (7.85 KB) 0001-group-controller-remove_users.patch Florian Walchshofer, 2026-03-09 21:35
0001-tests-group-controller-remove_users.patch (11.4 KB) 0001-tests-group-controller-remove_users.patch Florian Walchshofer, 2026-03-09 21:35
0002-Add-or-remove-users-from-group-using-the-context-men.patch (7.18 KB) 0002-Add-or-remove-users-from-group-using-the-context-men.patch Marius BĂLTEANU, 2026-05-12 22:45
0001-Refactor-GroupsController-remove_user-to-remove_user.patch (6.74 KB) 0001-Refactor-GroupsController-remove_user-to-remove_user.patch Marius BĂLTEANU, 2026-05-12 22:45
0003-Implement-explicit-confirmation-when-removing-user-s.patch (7.06 KB) 0003-Implement-explicit-confirmation-when-removing-user-s.patch Marius BĂLTEANU, 2026-05-12 22:45
0004-Ensure-correct-redirect-after-user-removal-conf.patch (2.25 KB) 0004-Ensure-correct-redirect-after-user-removal-conf.patch Florian Walchshofer, 2026-05-13 23:08
0004b-Validate-back-url-in-user-removal-confirmation-.patch (1.83 KB) 0004b-Validate-back-url-in-user-removal-confirmation-.patch Florian Walchshofer, 2026-05-14 23:34

Related issues

Related to Redmine - Patch #44115: GroupsController#remove_users tests fail randomlyClosedGo MAEDAActions
Actions #2

Updated by Holger Just 5 months ago

Generally, I like this feature. I would significantly improve usability for admins with a lots of users.

Some random notes:

  • Instead of post 'groups/:id/remove_users', you could also use delete 'groups/:id/users' in config/routes.rb to match the existing POST URL. We have proper HTTP verbs, let's use them :)
  • Why are you manually checking the referer in the controller? Can't you just use the redirect_back_or_default helper?
  • Why are you rendering an API error in GroupsController#remove_users if no user matched? An empty set seems entirely correct and I believe this is also valid everywhere else (including the HTML part)
  • The generic confirm: l(:text_are_you_sure) Alert is rather pointless. Yes, there are a few instances of these in Redmine, but we should not add more. Better add a real confirmation which specifically confirms the action taken, including a full list of users being added / deleted.
  • The context menu option to delete users from a group should filter the groups the selected users are actually in instead of offering all groups. And maybe even disable the option unless there are any groups which each include all of the selected users.
  • In the tests, both the existing route as well as the new route should be tested.
Actions #3

Updated by Florian Walchshofer 5 months ago

Thank you, Holger, for your suggestions.

I will address them in the next patch. Please let me know if I understood everything correctly:

  • Switch the route from POST to `DELETE /groups/:id/users`
    delete 'groups/:id/users', :to => 'groups#remove_users', :id => /\d+/, :as => 'group_remove_users'
    
  • Use `redirect_back_or_default` and show the flash notice only when returning to the Users view.
  • For the API, return success even if no users matched, to keep the operation idempotent.
    Should I also change something in the add_users method? I copied it from there.
      def remove_users
        @users = User.in_group(@group).where(:id => (params[:user_id] || params[:user_ids])).to_a
        @group.users.delete(@users)
        respond_to do |format|
          format.html do
            flash[:notice] = l(:notice_successful_delete) unless request.referer.presence == edit_group_path(@group, :tab => 'users')
            redirect_back_or_default edit_group_path(@group, tab: 'users'), referer: true
          end
          format.js
          format.api {render_api_ok}
        end
      end
    
  • Add a specific confirmation dialog that lists the affected users (or shows a count when there are more than 10).
    i will add a new helper, i can not find any in the redmine core
  • Filter the “Delete from group” context-menu options to only show groups that actually include the selected users (and disable the action when none do).
  • In the current test patch, which test is missing here?

Does this align with your expectations before I submit the patch?

Actions #4

Updated by Florian Walchshofer 5 months ago

[PATCH v2] Improve group user management API and context menu

  • Switch the route from POST to DELETE /groups/:id/users
  • Use helper `redirect_back_or_default`
  • API, return success even if no users matched
  • Add confirmation dialog
    data are from the context_menu_controller.rb
    Usernames are displayed in the confirmation dialog when the selected count is less than 10,
    showing only the selected users, not those who are actually not in the group.
  • Filter delete/add group, the available fields are this, when one user from the selected is in group or not
  • All tests are complete, add context_menus_controller_test, api_test/groups_test.
    Update previous tests due to the new group member in the fixtures.

I would also prefer to work on adding multi-user selection and context menu support for
the admin views of groups, users/projects, and project members. (if it is welcomed)

Actions #6

Updated by Marius BĂLTEANU about 1 month ago

  • File 0001-Refactor-GroupsController-remove_user-to-remove_user.patch added
  • File 0002-Add-or-remove-users-from-group-using-the-context-men.patch added

Florian, I rewrote your patches to better match the existing code, can you test it, please?

I will add later the third patch for confirmation message.

Actions #7

Updated by Florian Walchshofer about 1 month ago

I tested your patch and it works as expected
Thanks for taking the time to rework it.

There is one thing I would still prefer to do differently regarding @common_group_ids .
In the current form it performs an extra query per selected user, and it also applies an exclusive logic:
when I select user 1 and user 2 and both are members of different groups, I end up with nothing to select.

For me, it would be clearer and more consistent to handle this the same way as the Add group selection works with your patch, where all groups are always offered regardless of existing memberships. I actually think this behavior is much better and clearer from a UX point of view.
The following code would therefore be my suggestion here, as it better aligns with that behavior.

@common_group_ids = Group.givable.joins(:groups_users).where(groups_users: { user_id: @users.map(&:id) }).distinct.pluck(:id).to_set

Actions #8

Updated by Marius BĂLTEANU 20 days ago

  • File deleted (0001-Refactor-GroupsController-remove_user-to-remove_user.patch)
Actions #9

Updated by Marius BĂLTEANU 20 days ago

  • File deleted (0002-Add-or-remove-users-from-group-using-the-context-men.patch)
Actions #10

Updated by Marius BĂLTEANU 20 days ago

Florian, thanks for testing and for your feedback. I've integrated the change in the second patch, now the "Remove from group" option show all groups of the selected users. The last patch implements the explicit confirmation message when removing a user or multiple users from a group. The old way that use the browser alert window was removed.

Actions #11

Updated by Florian Walchshofer 19 days ago

Added patch 0004 to improve correct redirect behavior after user removal confirmation.

  • Introduces @back = back_url in the GroupsController#remove_users,
    so the cancel button correctly returns to the originating context (users/groups)
  • Redirects to the appropriate page after submit by passing @back via a hidden_field_tag in the remove_users confirmation view
  • Adds a success notice (flash[:notice]) for better user feedback, GroupsController#add_users for add_users only html view

thanks for the clear and well-structured sequence of patches.

Actions #12

Updated by Holger Just 18 days ago

Starting from the 0003 patch (and changed in the 0004 patch), I'm not sure where the initial @back_url variable comes from which is accessed from the view.

In any case, the back_url generally needs to be validated before it can be used. At least the Cancel link does not do this in the patch series and would thus allow an attacker to add an unvalidated link there (and thus a possible XSS).

Actions #13

Updated by Florian Walchshofer 18 days ago

In the updated patch (0004b), I removed the @back variable from GroupsController again, as it was unnecessary and not a good approach.

Validation is now handled via helper methods ( cancel_button_tag and back_url_hidden_field_tag ),
so the back_url is properly checked before being used, including for the cancel link.

Actions #14

Updated by Marius BĂLTEANU 17 days ago

  • Status changed from New to Resolved
  • Assignee set to Marius BĂLTEANU

I've committed the patches!

Holger, thanks for reviewing the code and for pointing out the XSS issues.

I will add a few more tests in the following days for better coverage, including assertions for XSS protection.

Actions #15

Updated by Marius BĂLTEANU 15 days ago

  • Status changed from Resolved to Closed

I've added a few more test cases for this feature.

Actions #16

Updated by Go MAEDA 10 days ago

  • Related to Patch #44115: GroupsController#remove_users tests fail randomly added
Actions

Also available in: Atom PDF