Project

General

Profile

Feature #43355 » Feature__add_pagination_support_for_members_and_groups_management_views.patch

Jan Catrysse, 2025-10-15 22:23

View differences:

/dev/null (revision 59918e63d53302ede2b34c2476c318494d6bda7f) → app/controllers/concerns/members_pagination.rb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20
module MembersPagination
21
  extend ActiveSupport::Concern
22

  
23
  private
24

  
25
  def paginate_project_members(project)
26
    scope = project.memberships.preload(:project).sorted
27
    @member_count = scope.count
28
    @member_pages = Redmine::Pagination::Paginator.new(@member_count, per_page_option,
29
                                                       params[:members_page], 'members_page')
30
    @memberships =
31
      scope.offset(@member_pages.offset).limit(@member_pages.per_page).to_a
32
  end
33
end
app/controllers/groups_controller.rb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/controllers/groups_controller.rb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
84 84
  end
85 85

  
86 86
  def edit
87
    load_group_users if @group.givable?
87 88
  end
88 89

  
89 90
  def update
......
116 117
  def add_users
117 118
    @users = User.not_in_group(@group).where(:id => (params[:user_id] || params[:user_ids])).to_a
118 119
    @group.users << @users
120
    load_group_users
119 121
    respond_to do |format|
120
      format.html {redirect_to edit_group_path(@group, :tab => 'users')}
122
      format.html {redirect_to edit_group_path(@group, group_users_query)}
121 123
      format.js
122 124
      format.api do
123 125
        if @users.any?
......
131 133

  
132 134
  def remove_user
133 135
    @group.users.delete(User.find(params[:user_id])) if request.delete?
136
    load_group_users
134 137
    respond_to do |format|
135
      format.html {redirect_to edit_group_path(@group, :tab => 'users')}
138
      format.html {redirect_to edit_group_path(@group, group_users_query)}
136 139
      format.js
137 140
      format.api {render_api_ok}
138 141
    end
......
159 162
    end
160 163
    h
161 164
  end
165

  
166
  def load_group_users
167
    scope = @group.users.sorted
168
    @group_users_count = scope.count
169
    @group_users_pages = Paginator.new(@group_users_count, per_page_option,
170
                                       params[:users_page], 'users_page')
171
    @group_users =
172
      scope.offset(@group_users_pages.offset).limit(@group_users_pages.per_page).to_a
173
  end
174

  
175
  def group_users_query
176
    query = {:tab => 'users'}
177
    query[:users_page] = params[:users_page] if params[:users_page].present?
178
    query[:per_page] = params[:per_page] if params[:per_page].present?
179
    query
180
  end
162 181
end
app/controllers/members_controller.rb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/controllers/members_controller.rb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
18 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 19

  
20 20
class MembersController < ApplicationController
21
  include MembersPagination
21 22
  model_object Member
22 23
  before_action :find_model_object, :except => [:index, :new, :create, :autocomplete]
23 24
  before_action :find_project_from_association, :except => [:index, :new, :create, :autocomplete]
......
65 66
      @project.members << members
66 67
    end
67 68

  
69
    paginate_project_members(@project)
70

  
68 71
    respond_to do |format|
69 72
      format.html {redirect_to_settings_in_projects}
70 73
      format.js do
......
91 94
      @member.set_editable_role_ids(params[:membership][:role_ids])
92 95
    end
93 96
    saved = @member.save
97
    paginate_project_members(@project)
94 98
    respond_to do |format|
95 99
      format.html {redirect_to_settings_in_projects}
96 100
      format.js
......
108 112
    if @member.deletable?
109 113
      @member.destroy
110 114
    end
115
    paginate_project_members(@project)
111 116
    respond_to do |format|
112 117
      format.html {redirect_to_settings_in_projects}
113 118
      format.js
......
130 135
  private
131 136

  
132 137
  def redirect_to_settings_in_projects
133
    redirect_to settings_project_path(@project, :tab => 'members')
138
    query = {:tab => 'members'}
139
    query[:members_page] = params[:members_page] if params[:members_page].present?
140
    query[:per_page] = params[:per_page] if params[:per_page].present?
141
    redirect_to settings_project_path(@project, query)
134 142
  end
135 143
end
app/controllers/projects_controller.rb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/controllers/projects_controller.rb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
43 43
  helper :repositories
44 44
  helper :members
45 45
  helper :trackers
46
  include MembersPagination
46 47

  
47 48
  # Lists visible projects
48 49
  def index
......
204 205
    @member ||= @project.members.new
205 206
    @trackers = Tracker.sorted.to_a
206 207

  
208
    if User.current.allowed_to?(:manage_members, @project)
209
      paginate_project_members(@project)
210
    end
211

  
207 212
    @version_status = params[:version_status] || 'open'
208 213
    @version_name = params[:version_name]
209 214
    @versions = @project.shared_versions.status(@version_status).like(@version_name).sorted
app/views/groups/_new_users_modal.html.erb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/views/groups/_new_users_modal.html.erb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
1 1
<h3 class="title"><%= l(:label_user_new) %></h3>
2 2

  
3
<%= form_for(@group, :url => group_users_path(@group), :remote => true, :method => :post) do |f| %>
3
<% users_query = {} %>
4
<% users_query[:users_page] = params[:users_page] if params[:users_page].present? %>
5
<% users_query[:per_page] = params[:per_page] if params[:per_page].present? %>
6
<%= form_for(@group, :url => group_users_path(@group, users_query), :remote => true, :method => :post) do |f| %>
7
  <%= hidden_field_tag :users_page, params[:users_page] if params[:users_page].present? %>
8
  <%= hidden_field_tag :per_page, params[:per_page] if params[:per_page].present? %>
4 9
  <%= render :partial => 'new_users_form' %>
5 10
  <p class="buttons">
6 11
    <%= submit_tag l(:button_add) %>
app/views/groups/_users.html.erb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/views/groups/_users.html.erb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
1
<p><%= link_to l(:label_user_new), new_group_users_path(@group), :remote => true, :class => "icon icon-add" %></p>
1
<% current_users_page = @group_users_pages&.page || params[:users_page] %>
2
<% current_users_per_page = @group_users_pages&.per_page || params[:per_page].presence %>
3
<% users_query = {} %>
4
<% users_query[:users_page] = current_users_page if current_users_page.present? %>
5
<% users_query[:per_page] = current_users_per_page if current_users_per_page.present? %>
6
<p><%= link_to l(:label_user_new), new_group_users_path(@group, users_query), :remote => true, :class => "icon icon-add" %></p>
2 7

  
3
<% if @group.users.any? %>
8
<% users = @group_users || [] %>
9
<% users_count = @group_users_count || users.size %>
10
<% if users.any? %>
4 11
  <table class="list users">
5 12
    <thead><tr>
6 13
      <th><%= l(:label_user) %></th>
7 14
      <th style="width:15%"></th>
8 15
    </tr></thead>
9 16
    <tbody>
10
    <% @group.users.sort.each do |user| %>
17
    <% users.each do |user| %>
11 18
      <tr id="user-<%= user.id %>">
12 19
        <td class="name"><%= link_to_user user %></td>
13 20
        <td class="buttons">
14
          <%= delete_link group_user_path(@group, :user_id => user), :remote => true %>
21
          <%= delete_link group_user_path(@group, users_query.merge(:user_id => user)), :remote => true %>
15 22
        </td>
16 23
      </tr>
17 24
    <% end %>
18 25
    </tbody>
19 26
  </table>
27
  <% if @group_users_pages %>
28
    <% pagination_params = request.query_parameters.merge(:tab => 'users') %>
29
    <span class="pagination"><%= pagination_links_full(@group_users_pages, users_count) do |text, parameters, options|
30
      link_to text, edit_group_path(@group, pagination_params.merge(parameters)), options
31
    end %></span>
32
  <% end %>
20 33
<% else %>
21 34
  <p class="nodata"><%= l(:label_no_data) %></p>
22 35
<% end %>
app/views/members/_edit.html.erb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/views/members/_edit.html.erb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
2 2
                      :as => :membership,
3 3
                      :remote => request.xhr?,
4 4
                      :method => :put) do |f| %>
5
  <%= hidden_field_tag :members_page, params[:members_page] if params[:members_page].present? %>
6
  <%= hidden_field_tag :per_page, params[:per_page] if params[:per_page].present? %>
5 7
  <p>
6 8
    <% @roles.each do |role| %>
7 9
    <label class="block">
app/views/members/_new_modal.html.erb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/views/members/_new_modal.html.erb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
1 1
<h3 class="title"><%= l(:label_member_new) %></h3>
2 2

  
3
<%= form_for @member, :as => :membership, :url => project_memberships_path(@project), :remote => true, :method => :post do |f| %>
3
<% members_query = {} %>
4
<% members_query[:members_page] = params[:members_page] if params[:members_page].present? %>
5
<% members_query[:per_page] = params[:per_page] if params[:per_page].present? %>
6
<%= form_for @member, :as => :membership, :url => project_memberships_path(@project, members_query), :remote => true, :method => :post do |f| %>
7
  <%= hidden_field_tag :members_page, params[:members_page] if params[:members_page].present? %>
8
  <%= hidden_field_tag :per_page, params[:per_page] if params[:per_page].present? %>
4 9
  <%= render :partial => 'new_form' %>
5 10
  <p class="buttons">
6 11
    <%= submit_tag l(:button_add), :id => 'member-add-submit' %>
app/views/projects/settings/_members.html.erb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → app/views/projects/settings/_members.html.erb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
1
<% members = @project.memberships.preload(:project).sorted.to_a %>
1
<% current_members_page = @member_pages&.page || params[:members_page] %>
2
<% current_members_per_page = @member_pages&.per_page || params[:per_page].presence %>
3
<% members_query = {} %>
4
<% members_query[:members_page] = current_members_page if current_members_page.present? %>
5
<% members_query[:per_page] = current_members_per_page if current_members_per_page.present? %>
6
<% members = @memberships || [] %>
7
<% member_count = @member_count || members.size %>
2 8

  
3 9
<% if User.current.admin? %>
4 10
  <div class="contextual"><%= link_to l(:label_administration), users_path, :class => "icon icon-settings" %></div>
5 11
<% end %>
6
<p><%= link_to l(:label_member_new), new_project_membership_path(@project), :remote => true, :class => "icon icon-add" %></p>
12
<p><%= link_to l(:label_member_new), new_project_membership_path(@project, members_query), :remote => true, :class => "icon icon-add" %></p>
7 13

  
8 14
<% if members.any? %>
9 15
<table class="list members">
......
26 32
  </td>
27 33
  <td class="buttons">
28 34
      <%= link_to l(:button_edit),
29
                  edit_membership_path(member),
35
                  edit_membership_path(member, members_query),
30 36
                  :remote => true,
31 37
                  :class => 'icon icon-edit' %>
32
      <%= delete_link membership_path(member),
38
      <%= delete_link membership_path(member, members_query),
33 39
                      :remote => true,
34 40
                      :data => (!User.current.admin? && member.include?(User.current) ? {:confirm => l(:text_own_membership_delete_confirmation)} : {}) if member.deletable? %>
35 41
  </td>
......
38 44
<% end %>
39 45
  </tbody>
40 46
</table>
47
  <% if @member_pages %>
48
    <% pagination_params = request.query_parameters.merge(:tab => 'members') %>
49
    <span class="pagination"><%= pagination_links_full(@member_pages, member_count) do |text, parameters, options|
50
      link_to text, settings_project_path(@project, pagination_params.merge(parameters)), options
51
    end %></span>
52
  <% end %>
41 53
<% else %>
42 54
<p class="nodata"><%= l(:label_no_data) %></p>
43 55
<% end %>
test/functional/groups_controller_test.rb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → test/functional/groups_controller_test.rb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
164 164
    end
165 165
  end
166 166

  
167
  def test_edit_users_tab_should_paginate_group_members
168
    group = Group.generate!
169
    users = Array.new(3) { User.generate! }
170
    users.each {|user| group.users << user }
171

  
172
    with_settings :per_page_options => '2,25,50' do
173
      get(:edit, :params => {:id => group.id, :tab => 'users'})
174
      assert_response :success
175
      assert_select 'table.users tbody tr', 2
176
      assert_select 'span.pagination ul.pages'
177
      assert_select 'span.pagination span.per-page', :text => /Per page: 2, 25/
178

  
179
      get(:edit, :params => {:id => group.id, :tab => 'users', :users_page => 2})
180
      assert_response :success
181
      assert_select 'table.users tbody tr', 1
182
    end
183
  end
184

  
185
  def test_edit_without_users_tab_should_prepare_group_users_pagination
186
    group = Group.generate!
187

  
188
    get(:edit, :params => {:id => group.id})
189
    assert_response :success
190
    assert_kind_of Redmine::Pagination::Paginator, assigns(:group_users_pages)
191
    assert_equal [], assigns(:group_users)
192
  end
193

  
167 194
  def test_update
168 195
    new_name = 'New name'
169 196
    put(
test/functional/projects_controller_test.rb (revision 108d4f28418539c4b4caa9a68d38a9bfbe6b05f5) → test/functional/projects_controller_test.rb (revision 59918e63d53302ede2b34c2476c318494d6bda7f)
988 988
    assert_select "tr#member-#{member.id}"
989 989
  end
990 990

  
991
  def test_settings_members_tab_should_paginate_memberships
992
    project = Project.generate!
993
    3.times do
994
      user = User.generate!
995
      User.add_to_project(user, project)
996
    end
997

  
998
    @request.session[:user_id] = 1
999

  
1000
    with_settings :per_page_options => '2,25,50' do
1001
      get(:settings, :params => {:id => project.id, :tab => 'members'})
1002
      assert_response :success
1003
      assert_select 'table.members tbody tr', 2
1004
      assert_select 'span.pagination ul.pages'
1005
      assert_select 'span.pagination span.per-page', :text => /Per page: 2, 25/
1006

  
1007
      get(:settings, :params => {:id => project.id, :tab => 'members', :members_page => 2})
1008
      assert_response :success
1009
      assert_select 'table.members tbody tr', 1
1010
    end
1011
  end
1012

  
1013
  def test_settings_without_members_tab_should_prepare_membership_pagination
1014
    project = Project.generate!
1015
    @request.session[:user_id] = 1
1016

  
1017
    get(:settings, :params => {:id => project.id})
1018
    assert_response :success
1019
    assert_kind_of Redmine::Pagination::Paginator, assigns(:member_pages)
1020
    assert_equal [], assigns(:memberships)
1021
  end
1022

  
991 1023
  def test_settings_should_show_tabs_depending_on_permission
992 1024
    @request.session[:user_id] = 3
993 1025
    project = Project.find(1)
    (1-1/1)