##// END OF EJS Templates
Count users with a single query on group list (#16905)....
Jean-Philippe Lang -
r12874:8aeab43247f9
parent child
Show More
@@ -1,141 +1,150
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class GroupsController < ApplicationController
18 class GroupsController < ApplicationController
19 layout 'admin'
19 layout 'admin'
20
20
21 before_filter :require_admin
21 before_filter :require_admin
22 before_filter :find_group, :except => [:index, :new, :create]
22 before_filter :find_group, :except => [:index, :new, :create]
23 accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user
23 accept_api_auth :index, :show, :create, :update, :destroy, :add_users, :remove_user
24
24
25 helper :custom_fields
25 helper :custom_fields
26
26
27 def index
27 def index
28 @groups = Group.sorted.all
28 @groups = Group.sorted.all
29
30 respond_to do |format|
29 respond_to do |format|
31 format.html
30 format.html {
31 @user_count_by_group_id = user_count_by_group_id
32 }
32 format.api
33 format.api
33 end
34 end
34 end
35 end
35
36
36 def show
37 def show
37 respond_to do |format|
38 respond_to do |format|
38 format.html
39 format.html
39 format.api
40 format.api
40 end
41 end
41 end
42 end
42
43
43 def new
44 def new
44 @group = Group.new
45 @group = Group.new
45 end
46 end
46
47
47 def create
48 def create
48 @group = Group.new
49 @group = Group.new
49 @group.safe_attributes = params[:group]
50 @group.safe_attributes = params[:group]
50
51
51 respond_to do |format|
52 respond_to do |format|
52 if @group.save
53 if @group.save
53 format.html {
54 format.html {
54 flash[:notice] = l(:notice_successful_create)
55 flash[:notice] = l(:notice_successful_create)
55 redirect_to(params[:continue] ? new_group_path : groups_path)
56 redirect_to(params[:continue] ? new_group_path : groups_path)
56 }
57 }
57 format.api { render :action => 'show', :status => :created, :location => group_url(@group) }
58 format.api { render :action => 'show', :status => :created, :location => group_url(@group) }
58 else
59 else
59 format.html { render :action => "new" }
60 format.html { render :action => "new" }
60 format.api { render_validation_errors(@group) }
61 format.api { render_validation_errors(@group) }
61 end
62 end
62 end
63 end
63 end
64 end
64
65
65 def edit
66 def edit
66 end
67 end
67
68
68 def update
69 def update
69 @group.safe_attributes = params[:group]
70 @group.safe_attributes = params[:group]
70
71
71 respond_to do |format|
72 respond_to do |format|
72 if @group.save
73 if @group.save
73 flash[:notice] = l(:notice_successful_update)
74 flash[:notice] = l(:notice_successful_update)
74 format.html { redirect_to(groups_path) }
75 format.html { redirect_to(groups_path) }
75 format.api { render_api_ok }
76 format.api { render_api_ok }
76 else
77 else
77 format.html { render :action => "edit" }
78 format.html { render :action => "edit" }
78 format.api { render_validation_errors(@group) }
79 format.api { render_validation_errors(@group) }
79 end
80 end
80 end
81 end
81 end
82 end
82
83
83 def destroy
84 def destroy
84 @group.destroy
85 @group.destroy
85
86
86 respond_to do |format|
87 respond_to do |format|
87 format.html { redirect_to(groups_path) }
88 format.html { redirect_to(groups_path) }
88 format.api { render_api_ok }
89 format.api { render_api_ok }
89 end
90 end
90 end
91 end
91
92
92 def add_users
93 def add_users
93 @users = User.where(:id => (params[:user_id] || params[:user_ids])).all
94 @users = User.where(:id => (params[:user_id] || params[:user_ids])).all
94 @group.users << @users if request.post?
95 @group.users << @users if request.post?
95 respond_to do |format|
96 respond_to do |format|
96 format.html { redirect_to edit_group_path(@group, :tab => 'users') }
97 format.html { redirect_to edit_group_path(@group, :tab => 'users') }
97 format.js
98 format.js
98 format.api { render_api_ok }
99 format.api { render_api_ok }
99 end
100 end
100 end
101 end
101
102
102 def remove_user
103 def remove_user
103 @group.users.delete(User.find(params[:user_id])) if request.delete?
104 @group.users.delete(User.find(params[:user_id])) if request.delete?
104 respond_to do |format|
105 respond_to do |format|
105 format.html { redirect_to edit_group_path(@group, :tab => 'users') }
106 format.html { redirect_to edit_group_path(@group, :tab => 'users') }
106 format.js
107 format.js
107 format.api { render_api_ok }
108 format.api { render_api_ok }
108 end
109 end
109 end
110 end
110
111
111 def autocomplete_for_user
112 def autocomplete_for_user
112 respond_to do |format|
113 respond_to do |format|
113 format.js
114 format.js
114 end
115 end
115 end
116 end
116
117
117 def edit_membership
118 def edit_membership
118 @membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
119 @membership = Member.edit_membership(params[:membership_id], params[:membership], @group)
119 @membership.save if request.post?
120 @membership.save if request.post?
120 respond_to do |format|
121 respond_to do |format|
121 format.html { redirect_to edit_group_path(@group, :tab => 'memberships') }
122 format.html { redirect_to edit_group_path(@group, :tab => 'memberships') }
122 format.js
123 format.js
123 end
124 end
124 end
125 end
125
126
126 def destroy_membership
127 def destroy_membership
127 Member.find(params[:membership_id]).destroy if request.post?
128 Member.find(params[:membership_id]).destroy if request.post?
128 respond_to do |format|
129 respond_to do |format|
129 format.html { redirect_to edit_group_path(@group, :tab => 'memberships') }
130 format.html { redirect_to edit_group_path(@group, :tab => 'memberships') }
130 format.js
131 format.js
131 end
132 end
132 end
133 end
133
134
134 private
135 private
135
136
136 def find_group
137 def find_group
137 @group = Group.find(params[:id])
138 @group = Group.find(params[:id])
138 rescue ActiveRecord::RecordNotFound
139 rescue ActiveRecord::RecordNotFound
139 render_404
140 render_404
140 end
141 end
142
143 def user_count_by_group_id
144 h = User.joins(:groups).group('group_id').count
145 h.keys.each do |key|
146 h[key.to_i] = h.delete(key)
147 end
148 h
149 end
141 end
150 end
@@ -1,26 +1,25
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to l(:label_group_new), new_group_path, :class => 'icon icon-add' %>
2 <%= link_to l(:label_group_new), new_group_path, :class => 'icon icon-add' %>
3 </div>
3 </div>
4
4
5 <%= title l(:label_group_plural) %>
5 <%= title l(:label_group_plural) %>
6
7 <% if @groups.any? %>
6 <% if @groups.any? %>
8 <table class="list groups">
7 <table class="list groups">
9 <thead><tr>
8 <thead><tr>
10 <th><%=l(:label_group)%></th>
9 <th><%=l(:label_group)%></th>
11 <th><%=l(:label_user_plural)%></th>
10 <th><%=l(:label_user_plural)%></th>
12 <th></th>
11 <th></th>
13 </tr></thead>
12 </tr></thead>
14 <tbody>
13 <tbody>
15 <% @groups.each do |group| %>
14 <% @groups.each do |group| %>
16 <tr class="<%= cycle 'odd', 'even' %>">
15 <tr id="group-<%= group.id %>" class="<%= cycle 'odd', 'even' %>">
17 <td class="name"><%= link_to h(group), edit_group_path(group) %></td>
16 <td class="name"><%= link_to h(group), edit_group_path(group) %></td>
18 <td><%= group.users.size %></td>
17 <td class="user_count"><%= @user_count_by_group_id[group.id] || 0 %></td>
19 <td class="buttons"><%= delete_link group %></td>
18 <td class="buttons"><%= delete_link group %></td>
20 </tr>
19 </tr>
21 <% end %>
20 <% end %>
22 </tbody>
21 </tbody>
23 </table>
22 </table>
24 <% else %>
23 <% else %>
25 <p class="nodata"><%= l(:label_no_data) %></p>
24 <p class="nodata"><%= l(:label_no_data) %></p>
26 <% end %>
25 <% end %>
@@ -1,202 +1,208
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
2 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class GroupsControllerTest < ActionController::TestCase
20 class GroupsControllerTest < ActionController::TestCase
21 fixtures :projects, :users, :members, :member_roles, :roles, :groups_users
21 fixtures :projects, :users, :members, :member_roles, :roles, :groups_users
22
22
23 def setup
23 def setup
24 @request.session[:user_id] = 1
24 @request.session[:user_id] = 1
25 end
25 end
26
26
27 def test_index
27 def test_index
28 get :index
28 get :index
29 assert_response :success
29 assert_response :success
30 assert_template 'index'
30 assert_template 'index'
31 end
31 end
32
32
33 def test_index_should_show_user_count
34 get :index
35 assert_response :success
36 assert_select 'tr#group-11 td.user_count', :text => '1'
37 end
38
33 def test_show
39 def test_show
34 get :show, :id => 10
40 get :show, :id => 10
35 assert_response :success
41 assert_response :success
36 assert_template 'show'
42 assert_template 'show'
37 end
43 end
38
44
39 def test_show_invalid_should_return_404
45 def test_show_invalid_should_return_404
40 get :show, :id => 99
46 get :show, :id => 99
41 assert_response 404
47 assert_response 404
42 end
48 end
43
49
44 def test_new
50 def test_new
45 get :new
51 get :new
46 assert_response :success
52 assert_response :success
47 assert_template 'new'
53 assert_template 'new'
48 assert_select 'input[name=?]', 'group[name]'
54 assert_select 'input[name=?]', 'group[name]'
49 end
55 end
50
56
51 def test_create
57 def test_create
52 assert_difference 'Group.count' do
58 assert_difference 'Group.count' do
53 post :create, :group => {:name => 'New group'}
59 post :create, :group => {:name => 'New group'}
54 end
60 end
55 assert_redirected_to '/groups'
61 assert_redirected_to '/groups'
56 group = Group.order('id DESC').first
62 group = Group.order('id DESC').first
57 assert_equal 'New group', group.name
63 assert_equal 'New group', group.name
58 assert_equal [], group.users
64 assert_equal [], group.users
59 end
65 end
60
66
61 def test_create_and_continue
67 def test_create_and_continue
62 assert_difference 'Group.count' do
68 assert_difference 'Group.count' do
63 post :create, :group => {:name => 'New group'}, :continue => 'Create and continue'
69 post :create, :group => {:name => 'New group'}, :continue => 'Create and continue'
64 end
70 end
65 assert_redirected_to '/groups/new'
71 assert_redirected_to '/groups/new'
66 group = Group.order('id DESC').first
72 group = Group.order('id DESC').first
67 assert_equal 'New group', group.name
73 assert_equal 'New group', group.name
68 end
74 end
69
75
70 def test_create_with_failure
76 def test_create_with_failure
71 assert_no_difference 'Group.count' do
77 assert_no_difference 'Group.count' do
72 post :create, :group => {:name => ''}
78 post :create, :group => {:name => ''}
73 end
79 end
74 assert_response :success
80 assert_response :success
75 assert_template 'new'
81 assert_template 'new'
76 end
82 end
77
83
78 def test_edit
84 def test_edit
79 get :edit, :id => 10
85 get :edit, :id => 10
80 assert_response :success
86 assert_response :success
81 assert_template 'edit'
87 assert_template 'edit'
82
88
83 assert_select 'div#tab-content-users'
89 assert_select 'div#tab-content-users'
84 assert_select 'div#tab-content-memberships' do
90 assert_select 'div#tab-content-memberships' do
85 assert_select 'a', :text => 'Private child of eCookbook'
91 assert_select 'a', :text => 'Private child of eCookbook'
86 end
92 end
87 end
93 end
88
94
89 def test_update
95 def test_update
90 new_name = 'New name'
96 new_name = 'New name'
91 put :update, :id => 10, :group => {:name => new_name}
97 put :update, :id => 10, :group => {:name => new_name}
92 assert_redirected_to '/groups'
98 assert_redirected_to '/groups'
93 group = Group.find(10)
99 group = Group.find(10)
94 assert_equal new_name, group.name
100 assert_equal new_name, group.name
95 end
101 end
96
102
97 def test_update_with_failure
103 def test_update_with_failure
98 put :update, :id => 10, :group => {:name => ''}
104 put :update, :id => 10, :group => {:name => ''}
99 assert_response :success
105 assert_response :success
100 assert_template 'edit'
106 assert_template 'edit'
101 end
107 end
102
108
103 def test_destroy
109 def test_destroy
104 assert_difference 'Group.count', -1 do
110 assert_difference 'Group.count', -1 do
105 post :destroy, :id => 10
111 post :destroy, :id => 10
106 end
112 end
107 assert_redirected_to '/groups'
113 assert_redirected_to '/groups'
108 end
114 end
109
115
110 def test_add_users
116 def test_add_users
111 assert_difference 'Group.find(10).users.count', 2 do
117 assert_difference 'Group.find(10).users.count', 2 do
112 post :add_users, :id => 10, :user_ids => ['2', '3']
118 post :add_users, :id => 10, :user_ids => ['2', '3']
113 end
119 end
114 end
120 end
115
121
116 def test_xhr_add_users
122 def test_xhr_add_users
117 assert_difference 'Group.find(10).users.count', 2 do
123 assert_difference 'Group.find(10).users.count', 2 do
118 xhr :post, :add_users, :id => 10, :user_ids => ['2', '3']
124 xhr :post, :add_users, :id => 10, :user_ids => ['2', '3']
119 assert_response :success
125 assert_response :success
120 assert_template 'add_users'
126 assert_template 'add_users'
121 assert_equal 'text/javascript', response.content_type
127 assert_equal 'text/javascript', response.content_type
122 end
128 end
123 assert_match /John Smith/, response.body
129 assert_match /John Smith/, response.body
124 end
130 end
125
131
126 def test_remove_user
132 def test_remove_user
127 assert_difference 'Group.find(10).users.count', -1 do
133 assert_difference 'Group.find(10).users.count', -1 do
128 delete :remove_user, :id => 10, :user_id => '8'
134 delete :remove_user, :id => 10, :user_id => '8'
129 end
135 end
130 end
136 end
131
137
132 def test_xhr_remove_user
138 def test_xhr_remove_user
133 assert_difference 'Group.find(10).users.count', -1 do
139 assert_difference 'Group.find(10).users.count', -1 do
134 xhr :delete, :remove_user, :id => 10, :user_id => '8'
140 xhr :delete, :remove_user, :id => 10, :user_id => '8'
135 assert_response :success
141 assert_response :success
136 assert_template 'remove_user'
142 assert_template 'remove_user'
137 assert_equal 'text/javascript', response.content_type
143 assert_equal 'text/javascript', response.content_type
138 end
144 end
139 end
145 end
140
146
141 def test_new_membership
147 def test_new_membership
142 assert_difference 'Group.find(10).members.count' do
148 assert_difference 'Group.find(10).members.count' do
143 post :edit_membership, :id => 10, :membership => { :project_id => 2, :role_ids => ['1', '2']}
149 post :edit_membership, :id => 10, :membership => { :project_id => 2, :role_ids => ['1', '2']}
144 end
150 end
145 end
151 end
146
152
147 def test_xhr_new_membership
153 def test_xhr_new_membership
148 assert_difference 'Group.find(10).members.count' do
154 assert_difference 'Group.find(10).members.count' do
149 xhr :post, :edit_membership, :id => 10, :membership => { :project_id => 2, :role_ids => ['1', '2']}
155 xhr :post, :edit_membership, :id => 10, :membership => { :project_id => 2, :role_ids => ['1', '2']}
150 assert_response :success
156 assert_response :success
151 assert_template 'edit_membership'
157 assert_template 'edit_membership'
152 assert_equal 'text/javascript', response.content_type
158 assert_equal 'text/javascript', response.content_type
153 end
159 end
154 assert_match /OnlineStore/, response.body
160 assert_match /OnlineStore/, response.body
155 end
161 end
156
162
157 def test_xhr_new_membership_with_failure
163 def test_xhr_new_membership_with_failure
158 assert_no_difference 'Group.find(10).members.count' do
164 assert_no_difference 'Group.find(10).members.count' do
159 xhr :post, :edit_membership, :id => 10, :membership => { :project_id => 999, :role_ids => ['1', '2']}
165 xhr :post, :edit_membership, :id => 10, :membership => { :project_id => 999, :role_ids => ['1', '2']}
160 assert_response :success
166 assert_response :success
161 assert_template 'edit_membership'
167 assert_template 'edit_membership'
162 assert_equal 'text/javascript', response.content_type
168 assert_equal 'text/javascript', response.content_type
163 end
169 end
164 assert_match /alert/, response.body, "Alert message not sent"
170 assert_match /alert/, response.body, "Alert message not sent"
165 end
171 end
166
172
167 def test_edit_membership
173 def test_edit_membership
168 assert_no_difference 'Group.find(10).members.count' do
174 assert_no_difference 'Group.find(10).members.count' do
169 post :edit_membership, :id => 10, :membership_id => 6, :membership => { :role_ids => ['1', '3']}
175 post :edit_membership, :id => 10, :membership_id => 6, :membership => { :role_ids => ['1', '3']}
170 end
176 end
171 end
177 end
172
178
173 def test_xhr_edit_membership
179 def test_xhr_edit_membership
174 assert_no_difference 'Group.find(10).members.count' do
180 assert_no_difference 'Group.find(10).members.count' do
175 xhr :post, :edit_membership, :id => 10, :membership_id => 6, :membership => { :role_ids => ['1', '3']}
181 xhr :post, :edit_membership, :id => 10, :membership_id => 6, :membership => { :role_ids => ['1', '3']}
176 assert_response :success
182 assert_response :success
177 assert_template 'edit_membership'
183 assert_template 'edit_membership'
178 assert_equal 'text/javascript', response.content_type
184 assert_equal 'text/javascript', response.content_type
179 end
185 end
180 end
186 end
181
187
182 def test_destroy_membership
188 def test_destroy_membership
183 assert_difference 'Group.find(10).members.count', -1 do
189 assert_difference 'Group.find(10).members.count', -1 do
184 post :destroy_membership, :id => 10, :membership_id => 6
190 post :destroy_membership, :id => 10, :membership_id => 6
185 end
191 end
186 end
192 end
187
193
188 def test_xhr_destroy_membership
194 def test_xhr_destroy_membership
189 assert_difference 'Group.find(10).members.count', -1 do
195 assert_difference 'Group.find(10).members.count', -1 do
190 xhr :post, :destroy_membership, :id => 10, :membership_id => 6
196 xhr :post, :destroy_membership, :id => 10, :membership_id => 6
191 assert_response :success
197 assert_response :success
192 assert_template 'destroy_membership'
198 assert_template 'destroy_membership'
193 assert_equal 'text/javascript', response.content_type
199 assert_equal 'text/javascript', response.content_type
194 end
200 end
195 end
201 end
196
202
197 def test_autocomplete_for_user
203 def test_autocomplete_for_user
198 get :autocomplete_for_user, :id => 10, :q => 'smi', :format => 'js'
204 get :autocomplete_for_user, :id => 10, :q => 'smi', :format => 'js'
199 assert_response :success
205 assert_response :success
200 assert_include 'John Smith', response.body
206 assert_include 'John Smith', response.body
201 end
207 end
202 end
208 end
General Comments 0
You need to be logged in to leave comments. Login now