Commit 9dfe5a7c15111e4788674c1def837fca5a271831
Exists in
master
and in
29 other branches
Merge branches 'ActionItem2882' and 'ActionItem2882_merge' into ActionItem2882_merge
Showing
16 changed files
with
347 additions
and
19 deletions
Show diff stats
app/controllers/admin/users_controller.rb
... | ... | @@ -4,12 +4,62 @@ class UsersController < AdminController |
4 | 4 | |
5 | 5 | protect 'manage_environment_users', :environment |
6 | 6 | |
7 | + include UsersHelper | |
8 | + | |
9 | + def per_page | |
10 | + 10 | |
11 | + end | |
12 | + | |
7 | 13 | def index |
14 | + @filter = params[:filter] | |
15 | + if @filter.blank? || @filter == 'all_users' | |
16 | + @filter = 'all_users' | |
17 | + scope = environment.people.no_templates(environment) | |
18 | + elsif @filter == 'admin_users' | |
19 | + scope = environment.people.no_templates(environment).admins | |
20 | + elsif @filter == 'activated_users' | |
21 | + scope = environment.people.no_templates(environment).activated | |
22 | + elsif @filter == 'deactivated_users' | |
23 | + scope = environment.people.no_templates(environment).deactivated | |
24 | + end | |
25 | + @q = params[:q] | |
26 | + if @q.blank? | |
27 | + @collection = scope.paginate(:per_page => per_page, :page => params[:npage]) | |
28 | + else | |
29 | + @collection = find_by_contents(:people, scope, @q, {:per_page => per_page, :page => params[:npage]})[:results] | |
30 | + end | |
31 | + end | |
32 | + | |
33 | + def set_admin_role | |
34 | + person = environment.people.find(params[:id]) | |
35 | + environment.add_admin(person) | |
36 | + redirect_to :action => :index, :q => params[:q], :filter => params[:filter] | |
37 | + end | |
38 | + | |
39 | + def reset_admin_role | |
40 | + person = environment.people.find(params[:id]) | |
41 | + environment.remove_admin(person) | |
42 | + redirect_to :action => :index, :q => params[:q], :filter => params[:filter] | |
43 | + end | |
44 | + | |
45 | + def activate | |
46 | + person = environment.people.find(params[:id]) | |
47 | + person.user.activate | |
48 | + redirect_to :action => :index, :q => params[:q], :filter => params[:filter] | |
49 | + end | |
50 | + | |
51 | + def deactivate | |
52 | + person = environment.people.find(params[:id]) | |
53 | + person.user.deactivate | |
54 | + redirect_to :action => :index, :q => params[:q], :filter => params[:filter] | |
55 | + end | |
56 | + | |
57 | + def download | |
8 | 58 | respond_to do |format| |
9 | 59 | format.html |
10 | 60 | format.xml do |
11 | - @users = User.find(:all, :conditions => {:environment_id => environment.id}, :include => [:person]) | |
12 | - send_data @users.to_xml( | |
61 | + users = User.find(:all, :conditions => {:environment_id => environment.id}, :include => [:person]) | |
62 | + send_data users.to_xml( | |
13 | 63 | :skip_types => true, |
14 | 64 | :only => %w[email login created_at updated_at], |
15 | 65 | :include => { :person => {:only => %w[name updated_at created_at address birth_date contact_phone identifier lat lng] } }), | ... | ... |
app/controllers/application_controller.rb
... | ... | @@ -0,0 +1,25 @@ |
1 | +module UsersHelper | |
2 | + | |
3 | + FILTER_TRANSLATION = { | |
4 | + 'all_users' => _('All users'), | |
5 | + 'admin_users' => _('Admin users'), | |
6 | + 'activated_users' => _('Activated users'), | |
7 | + 'deactivated_users' => _('Deativated users'), | |
8 | + } | |
9 | + | |
10 | + def filter_selector(filter, float = 'right') | |
11 | + options = options_for_select(FILTER_TRANSLATION.map {|key, name| [name, key]}, :selected => filter) | |
12 | + url_params = url_for(params.merge(:filter => 'FILTER')) | |
13 | + onchange = "document.location.href = '#{url_params}'.replace('FILTER', this.value)" | |
14 | + select_field = select_tag(:filter, options, :onchange => onchange) | |
15 | + content_tag('div', | |
16 | + content_tag('strong', _('Filter')) + ': ' + select_field, | |
17 | + :class => "environment-users-customize-search" | |
18 | + ) | |
19 | + end | |
20 | + | |
21 | + def filter_title(filter) | |
22 | + FILTER_TRANSLATION[filter] | |
23 | + end | |
24 | + | |
25 | +end | ... | ... |
app/models/person.rb
... | ... | @@ -75,6 +75,10 @@ class Person < Profile |
75 | 75 | named_scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*' |
76 | 76 | named_scope :non_abusers, :joins => "LEFT JOIN tasks ON profiles.id = tasks.requestor_id AND tasks.type='AbuseComplaint'", :conditions => ["tasks.status != 3 OR tasks.id is NULL"], :select => "DISTINCT profiles.*" |
77 | 77 | |
78 | + named_scope :admins, :joins => [:role_assignments => :role], :conditions => ['roles.key = ?', 'environment_administrator' ] | |
79 | + named_scope :activated, :joins => :user, :conditions => ['users.activation_code IS NULL AND users.activated_at IS NOT NULL'] | |
80 | + named_scope :deactivated, :joins => :user, :conditions => ['NOT (users.activation_code IS NULL AND users.activated_at IS NOT NULL)'] | |
81 | + | |
78 | 82 | after_destroy do |person| |
79 | 83 | Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy } |
80 | 84 | end | ... | ... |
app/models/profile.rb
... | ... | @@ -79,6 +79,7 @@ class Profile < ActiveRecord::Base |
79 | 79 | named_scope :enterprises, lambda { {:conditions => (Enterprise.send(:subclasses).map(&:name) << 'Enterprise').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} } |
80 | 80 | named_scope :communities, lambda { {:conditions => (Community.send(:subclasses).map(&:name) << 'Community').map { |klass| "profiles.type = '#{klass}'"}.join(" OR ")} } |
81 | 81 | named_scope :templates, lambda { |environment| { :conditions => {:is_template => true, :environment_id => environment.id} } } |
82 | + named_scope :no_templates, lambda { |environment| { :conditions => {:is_template => false, :environment_id => environment.id} } } | |
82 | 83 | |
83 | 84 | def members |
84 | 85 | scopes = plugins.dispatch_scopes(:organization_members, self) | ... | ... |
app/models/user.rb
... | ... | @@ -143,6 +143,21 @@ class User < ActiveRecord::Base |
143 | 143 | end |
144 | 144 | end |
145 | 145 | |
146 | + # Deactivates the user in the database. | |
147 | + def deactivate | |
148 | + return false unless self.person | |
149 | + self.activated_at = nil | |
150 | + self.person.visible = false | |
151 | + begin | |
152 | + self.person.save! && self.save! | |
153 | + rescue Exception => exception | |
154 | + logger.error(exception.to_s) | |
155 | + false | |
156 | + else | |
157 | + true | |
158 | + end | |
159 | + end | |
160 | + | |
146 | 161 | def activated? |
147 | 162 | self.activation_code.nil? && !self.activated_at.nil? |
148 | 163 | end | ... | ... |
... | ... | @@ -0,0 +1,6 @@ |
1 | +<% button_bar do %> | |
2 | + <%= button :'text-plain', _('User list as [CSV]'), :action => :download, :format => 'csv' %> | |
3 | + <%= button :'text-html', _('User list as [XML]'), :action => :download, :format => 'xml' %> | |
4 | + <%= button :send, _('Send e-mail to all users'), :action => 'send_mail' %> | |
5 | + <%= button :back, _('Back'), :controller => 'admin_panel' %> | |
6 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,33 @@ |
1 | +<div class="environment-users-results-header"> | |
2 | + <div id='environment-users-filter-title'><%= filter_title(@filter) %></div> | |
3 | + <%= filter_selector(@filter) %> | |
4 | + <div style="clear: both"></div> | |
5 | +</div> | |
6 | + | |
7 | +<table> | |
8 | + <tr> | |
9 | + <th><%= _('Member') %></th> | |
10 | + <th><%= _('Actions') %></th> | |
11 | + </tr> | |
12 | + <% @collection.each do |p| %> | |
13 | + <tr title="<%= p.name %>"> | |
14 | + <td><%= link_to_profile p.short_name, p.identifier, :title => p.name %> </td> | |
15 | + <td> | |
16 | + <div class="members-buttons-cell"> | |
17 | + <% if p.is_admin? %> | |
18 | + <%= button_without_text :'reset-admin-role', _('Reset admin role'), :action => 'reset_admin_role', :id => p, :q => @q, :filter => @filter %> | |
19 | + <% else %> | |
20 | + <%= button_without_text :'set-admin-role', _('Set admin role'), :action => 'set_admin_role', :id => p, :q => @q, :filter => @filter %> | |
21 | + <% end %> | |
22 | + <% if !p.user.activated? %> | |
23 | + <%= button_without_text :'activate-user', _('Activate user'), :action => 'activate', :id => p, :q => @q, :filter => @filter %> | |
24 | + <% else %> | |
25 | + <%= button_without_text :'deactivate-user', _('Deactivate user'), :action => 'deactivate', :id => p, :q => @q, :filter => @filter %> | |
26 | + <% end %> | |
27 | + </div> | |
28 | + </td> | |
29 | + </tr> | |
30 | + <% end %> | |
31 | +</table> | |
32 | + | |
33 | +<%= pagination_links @collection, {:param_name => 'npage', :page_links => true} %> | ... | ... |
app/views/users/index.rhtml
1 | 1 | <h1><%= _('Manage users') %></h1> |
2 | 2 | |
3 | -<ul> | |
4 | - <li> | |
5 | - <%= _('Download users list') %>: | |
6 | - <%= link_to '[CSV]', :format => 'csv' %> | |
7 | - <%= link_to '[XML]', :format => 'xml' %> | |
8 | - </li> | |
9 | - <li> | |
10 | - <%= link_to _('Send e-mail to users'), :action => 'send_mail' %> | |
11 | - </li> | |
12 | -</ul> | |
13 | - | |
14 | -<% button_bar do %> | |
15 | - <%= button :back, _('Back to admin panel'), :controller => 'admin_panel' %> | |
3 | +<% form_tag( { :action => 'index' }, :method => 'get', :class => 'users-search' ) do %> | |
4 | +<div id="search-users"> | |
5 | + <%= render :partial => 'users_search_form' %> | |
6 | +</div> | |
7 | +<div id="users-list"> | |
8 | + <%= render :partial => 'users_list' %> | |
9 | +</div> | |
16 | 10 | <% end %> |
11 | + | |
12 | +<%= render :partial => 'index_buttons' %> | |
17 | 13 | \ No newline at end of file | ... | ... |
public/designs/icons/tango/style.css
... | ... | @@ -99,6 +99,11 @@ |
99 | 99 | .icon-user-unknown { background-image: url(Tango/16x16/status/dialog-error.png) } |
100 | 100 | .icon-alert { background-image: url(Tango/16x16/status/dialog-warning.png) } |
101 | 101 | |
102 | +.icon-activate-user { background-image: url(Tango/16x16/emblems/emblem-system.png) } | |
103 | +.icon-deactivate-user { background-image: url(Tango/16x16/emblems/emblem-unreadable.png) } | |
104 | +.icon-set-admin-role { background-image: url(mod/16x16/apps/user.png) } | |
105 | +.icon-reset-admin-role { background-image: url(/images/icons-app/person-icon.png) } | |
106 | + | |
102 | 107 | /******************LARGE ICONS********************/ |
103 | 108 | .image-gallery-item .folder { background-image: url(mod/96x96/places/folder.png) } |
104 | 109 | .image-gallery-item .gallery { background-image: url(mod/96x96/mimetypes/image-x-generic.png) } | ... | ... |
public/stylesheets/application.css
... | ... | @@ -4161,6 +4161,33 @@ h1#agenda-title { |
4161 | 4161 | -webkit-border-radius:10px; |
4162 | 4162 | } |
4163 | 4163 | |
4164 | +/* ==> public/stylesheets/controller_environment_users.css <== */ | |
4165 | +.controller-environment_users table { | |
4166 | + text-align: left; | |
4167 | +} | |
4168 | + | |
4169 | +#environment-users-search form { | |
4170 | + padding: 10px; | |
4171 | + margin-bottom: 15px; | |
4172 | + background-color: #E6E6E6; | |
4173 | + -moz-border-radius: 5px; | |
4174 | + -webkit-border-radius: 5px; | |
4175 | +} | |
4176 | + | |
4177 | +.environment-users-results-header { | |
4178 | + font-size: 0.9em; | |
4179 | + padding: 6px 0px 0px 0px; | |
4180 | + margin:0 0 5px 0; | |
4181 | + border-bottom: 2px dotted #999; | |
4182 | + text-align: right; | |
4183 | +} | |
4184 | +#environment-users-filter-title { | |
4185 | + font-weight: bold; | |
4186 | + font-size: 130%; | |
4187 | + line-height: 35px; | |
4188 | + float: left; | |
4189 | +} | |
4190 | + | |
4164 | 4191 | /* * * Profile search * * * * * * * */ |
4165 | 4192 | |
4166 | 4193 | #public-profile-search, #profile-search-results form, .profile-search-block form { | ... | ... |
test/functional/users_controller_test.rb
... | ... | @@ -6,11 +6,16 @@ class UsersController; def rescue_action(e) raise e end; end |
6 | 6 | |
7 | 7 | class UsersControllerTest < ActionController::TestCase |
8 | 8 | |
9 | - all_fixtures | |
10 | 9 | def setup |
11 | 10 | @controller = UsersController.new |
12 | 11 | @request = ActionController::TestRequest.new |
13 | 12 | @response = ActionController::TestResponse.new |
13 | + | |
14 | + | |
15 | + Environment.delete_all | |
16 | + Environment.create(:name => 'some env', :is_default => true) | |
17 | + admin_user = create_user_with_permission('adminuser', 'manage_environment_users', Environment.default) | |
18 | + login_as('adminuser') | |
14 | 19 | end |
15 | 20 | |
16 | 21 | should 'not access without right permission' do |
... | ... | @@ -28,11 +33,83 @@ class UsersControllerTest < ActionController::TestCase |
28 | 33 | assert_response :success |
29 | 34 | end |
30 | 35 | |
36 | + should 'blank search results include activated and deactivated users' do | |
37 | + deactivated = create_user('deactivated') | |
38 | + deactivated.activated_at = nil | |
39 | + deactivated.person.visible = false | |
40 | + deactivated.save! | |
41 | + get :index, :q => '' | |
42 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /adminuser/}} | |
43 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /deactivated/}} | |
44 | + end | |
45 | + | |
46 | + should 'blank search include all users' do | |
47 | + (1..5).each {|i| | |
48 | + u = create_user('user'+i.to_s) | |
49 | + } | |
50 | + get :index, :q => '' # blank search | |
51 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /adminuser/}} | |
52 | + (1..5).each {|i| | |
53 | + u = 'user'+i.to_s | |
54 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => u}} | |
55 | + } | |
56 | + end | |
57 | + | |
58 | + should 'search not include all users' do | |
59 | + (1..5).each {|i| | |
60 | + u = create_user('user'+i.to_s) | |
61 | + } | |
62 | + get :index, :q => 'er5' # search | |
63 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /user5/}} | |
64 | + (1..4).each {|i| | |
65 | + u = 'user'+i.to_s | |
66 | + assert_no_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => u}} | |
67 | + } | |
68 | + end | |
69 | + | |
70 | + should 'set admin role' do | |
71 | + u = create_user() | |
72 | + assert_equal false, u.person.is_admin? | |
73 | + post :set_admin_role, :id => u.person.id, :q => '' | |
74 | + u.reload | |
75 | + assert u.person.is_admin? | |
76 | + end | |
77 | + | |
78 | + should 'reset admin role' do | |
79 | + u = create_user() | |
80 | + e = Environment.default | |
81 | + e.add_admin(u.person) | |
82 | + u.reload | |
83 | + assert u.person.is_admin? | |
84 | + post :reset_admin_role, :id => u.person.id, :q => '' | |
85 | + u.reload | |
86 | + assert_equal false, u.person.is_admin? | |
87 | + end | |
88 | + | |
89 | + should 'activate user' do | |
90 | + u = create_user() | |
91 | + assert_equal false, u.activated? | |
92 | + post :activate, :id => u.person.id, :q => '' | |
93 | + u.reload | |
94 | + assert u.activated? | |
95 | + end | |
96 | + | |
97 | + should 'deactivate user' do | |
98 | + u = create_user() | |
99 | + u.activated_at = Time.now.utc | |
100 | + u.activation_code = nil | |
101 | + u.person.visible = true | |
102 | + assert u.activated? | |
103 | + post :deactivate, :id => u.person.id, :q => '' | |
104 | + u.reload | |
105 | + assert_equal false, u.activated? | |
106 | + end | |
107 | + | |
31 | 108 | should 'response as XML to export users' do |
32 | 109 | admin_user = create_user_with_permission('admin_user', 'manage_environment_users', Environment.default) |
33 | 110 | login_as('admin_user') |
34 | 111 | |
35 | - get :index, :format => 'xml' | |
112 | + get :download, :format => 'xml' | |
36 | 113 | assert_equal 'text/xml', @response.content_type |
37 | 114 | end |
38 | 115 | |
... | ... | @@ -40,7 +117,7 @@ class UsersControllerTest < ActionController::TestCase |
40 | 117 | admin_user = create_user_with_permission('admin_user', 'manage_environment_users', Environment.default) |
41 | 118 | login_as('admin_user') |
42 | 119 | |
43 | - get :index, :format => 'csv' | |
120 | + get :download, :format => 'csv' | |
44 | 121 | assert_equal 'text/csv', @response.content_type |
45 | 122 | assert_equal 'name;email', @response.body.split("\n")[0] |
46 | 123 | end | ... | ... |
test/unit/person_test.rb
... | ... | @@ -1335,4 +1335,56 @@ class PersonTest < ActiveSupport::TestCase |
1335 | 1335 | assert_includes non_abusers, not_abuser |
1336 | 1336 | end |
1337 | 1337 | |
1338 | + should 'admins named_scope return persons who are admin users' do | |
1339 | + Person.delete_all | |
1340 | + e = Environment.default | |
1341 | + admins = [] | |
1342 | + (1..5).each {|i| | |
1343 | + u = create_user('user'+i.to_s) | |
1344 | + e.add_admin(u.person) | |
1345 | + admins << u.person | |
1346 | + } | |
1347 | + (6..10).each {|i| | |
1348 | + u = create_user('user'+i.to_s) | |
1349 | + } | |
1350 | + assert_equal admins, Person.admins | |
1351 | + end | |
1352 | + | |
1353 | + should 'activated named_scope return persons who are activated users' do | |
1354 | + Person.delete_all | |
1355 | + e = Environment.default | |
1356 | + activated = [] | |
1357 | + (1..5).each {|i| | |
1358 | + u = create_user('user'+i.to_s) | |
1359 | + u.activated_at = Time.now.utc | |
1360 | + u.activation_code = nil | |
1361 | + u.save! | |
1362 | + activated << u.person | |
1363 | + } | |
1364 | + (6..10).each {|i| | |
1365 | + u = create_user('user'+i.to_s) | |
1366 | + u.activated_at = nil | |
1367 | + u.save! | |
1368 | + } | |
1369 | + assert_equal activated, Person.activated | |
1370 | + end | |
1371 | + | |
1372 | + should 'deactivated named_scope return persons who are deactivated users' do | |
1373 | + Person.delete_all | |
1374 | + e = Environment.default | |
1375 | + deactivated = [] | |
1376 | + (1..5).each {|i| | |
1377 | + u = create_user('user'+i.to_s) | |
1378 | + u.activated_at = nil | |
1379 | + u.save! | |
1380 | + deactivated << u.person | |
1381 | + } | |
1382 | + (6..10).each {|i| | |
1383 | + u = create_user('user'+i.to_s) | |
1384 | + u.activated_at = Time.now.utc | |
1385 | + u.activation_code = nil | |
1386 | + u.save! | |
1387 | + } | |
1388 | + assert_equal deactivated, Person.deactivated | |
1389 | + end | |
1338 | 1390 | end | ... | ... |
test/unit/profile_test.rb
... | ... | @@ -1379,6 +1379,18 @@ class ProfileTest < ActiveSupport::TestCase |
1379 | 1379 | assert_not_includes Profile.templates(Environment.default), profile |
1380 | 1380 | end |
1381 | 1381 | |
1382 | + should 'return a list of profiles that are not templates' do | |
1383 | + p1 = fast_create(Profile, :is_template => false) | |
1384 | + p2 = fast_create(Profile, :is_template => false) | |
1385 | + t1 = fast_create(Profile, :is_template => true) | |
1386 | + t2 = fast_create(Profile, :is_template => true) | |
1387 | + | |
1388 | + assert_includes Profile.no_templates(Environment.default), p1 | |
1389 | + assert_includes Profile.no_templates(Environment.default), p2 | |
1390 | + assert_not_includes Profile.no_templates(Environment.default), t1 | |
1391 | + assert_not_includes Profile.no_templates(Environment.default), t2 | |
1392 | + end | |
1393 | + | |
1382 | 1394 | should 'not crash on a profile update with a destroyed template' do |
1383 | 1395 | template = fast_create(Profile, :is_template => true) |
1384 | 1396 | profile = fast_create(Profile, :template_id => template.id) | ... | ... |
test/unit/user_test.rb
... | ... | @@ -518,6 +518,25 @@ class UserTest < ActiveSupport::TestCase |
518 | 518 | end |
519 | 519 | end |
520 | 520 | |
521 | + should 'deactivate an user' do | |
522 | + user = new_user | |
523 | + user.activated_at = Time.now.utc | |
524 | + user.person.visible = true | |
525 | + assert user.deactivate | |
526 | + assert_nil user.activated_at | |
527 | + assert !user.person.visible | |
528 | + end | |
529 | + | |
530 | + should 'return if the user is deactivated' do | |
531 | + user = new_user | |
532 | + user.activated_at = Time.now.utc | |
533 | + user.activation_code = nil | |
534 | + user.person.visible = true | |
535 | + assert user.activated? | |
536 | + user.deactivate | |
537 | + assert !user.activated? | |
538 | + end | |
539 | + | |
521 | 540 | should 'activate right after creation when confirmation is not required' do |
522 | 541 | e = Environment.default |
523 | 542 | e.enable('skip_new_user_email_confirmation') | ... | ... |