Commit d09c5df540181267cc6d2a71a7bf8465f43b33f5
1 parent
ce0d03ab
Exists in
master
and in
8 other branches
Allow admins to set and reset admin role on environment users.
Allow admins to activate and deactivate environment users.
Showing
13 changed files
with
269 additions
and
0 deletions
Show diff stats
| ... | ... | @@ -0,0 +1,44 @@ |
| 1 | +class EnvironmentUsersController < AdminController | |
| 2 | + | |
| 3 | + protect 'manage_environment_users', :environment | |
| 4 | + | |
| 5 | + def per_page | |
| 6 | + 10 | |
| 7 | + end | |
| 8 | + | |
| 9 | + def index | |
| 10 | + @q = params[:q] | |
| 11 | + if @q.blank? | |
| 12 | + @collection = environment.people.no_templates(environment).paginate( | |
| 13 | + :per_page => per_page, | |
| 14 | + :page => params[:npage] | |
| 15 | + ) | |
| 16 | + else | |
| 17 | + @collection = find_by_contents(:people, environment.people.no_templates(environment), @q, {:per_page => per_page, :page => params[:npage]})[:results] | |
| 18 | + end | |
| 19 | + end | |
| 20 | + | |
| 21 | + def set_admin_role | |
| 22 | + @person = environment.people.find(params[:id]) | |
| 23 | + environment.add_admin(@person) | |
| 24 | + redirect_to :action => :index, :q => params[:q] | |
| 25 | + end | |
| 26 | + | |
| 27 | + def reset_admin_role | |
| 28 | + @person = environment.people.find(params[:id]) | |
| 29 | + environment.remove_admin(@person) | |
| 30 | + redirect_to :action => :index, :q => params[:q] | |
| 31 | + end | |
| 32 | + | |
| 33 | + def activate | |
| 34 | + @person = environment.people.find(params[:id]) | |
| 35 | + @person.user.activate | |
| 36 | + redirect_to :action => :index, :q => params[:q] | |
| 37 | + end | |
| 38 | + | |
| 39 | + def deactivate | |
| 40 | + @person = environment.people.find(params[:id]) | |
| 41 | + @person.user.deactivate | |
| 42 | + redirect_to :action => :index, :q => params[:q] | |
| 43 | + end | |
| 44 | +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 | ... | ... |
app/views/environment_users/_environment_users_search_form.rhtml
0 → 100644
| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | +<% form_tag( { :controller => 'environment_users', :action => 'index' }, :method => 'get', :class => 'environment-users-search' ) do %> | |
| 2 | + <div class="search-field"> | |
| 3 | + <span class="formfield"> | |
| 4 | + <%= text_field_tag 'q', @q, :title => _("Find users") %> | |
| 5 | + </span> | |
| 6 | + <%= submit_button(:search, _('Search')) %> | |
| 7 | + </div> | |
| 8 | +<% end %> | ... | ... |
| ... | ... | @@ -0,0 +1,30 @@ |
| 1 | +<% title = _('All Users') %> | |
| 2 | + | |
| 3 | +<h3><%= title %></h3> | |
| 4 | +<table> | |
| 5 | + <tr> | |
| 6 | + <th><%= _('Member') %></th> | |
| 7 | + <th><%= _('Actions') %></th> | |
| 8 | + </tr> | |
| 9 | + <% @collection.each do |p| %> | |
| 10 | + <tr title="<%= p.name %>"> | |
| 11 | + <td><%= link_to_profile p.short_name, p.identifier, :title => p.name %> </td> | |
| 12 | + <td> | |
| 13 | + <div class="members-buttons-cell"> | |
| 14 | + <% if p.is_admin? %> | |
| 15 | + <%= button_without_text :'reset-admin-role', _('Reset admin role'), :action => 'reset_admin_role', :id => p, :q => @q %> | |
| 16 | + <% else %> | |
| 17 | + <%= button_without_text :'set-admin-role', _('Set admin role'), :action => 'set_admin_role', :id => p, :q => @q %> | |
| 18 | + <% end %> | |
| 19 | + <% if !p.user.activated? %> | |
| 20 | + <%= button_without_text :'activate-user', _('Activate user'), :action => 'activate', :id => p, :q => @q %> | |
| 21 | + <% else %> | |
| 22 | + <%= button_without_text :'deactivate-user', _('Deactivate user'), :action => 'deactivate', :id => p, :q => @q %> | |
| 23 | + <% end %> | |
| 24 | + </div> | |
| 25 | + </td> | |
| 26 | + </tr> | |
| 27 | + <% end %> | |
| 28 | +</table> | |
| 29 | + | |
| 30 | +<%= pagination_links @collection, {:param_name => 'npage', :page_links => true} %> | |
| 0 | 31 | \ No newline at end of file | ... | ... |
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +<h1><%= _('Edit Users')%></h1> | |
| 2 | + | |
| 3 | +<%= render :partial => 'index_buttons' %> | |
| 4 | + | |
| 5 | +<div id="search-users"> | |
| 6 | + <%= render :partial => 'environment_users_search_form' %> | |
| 7 | +</div> | |
| 8 | +<div id="users-list"> | |
| 9 | + <%= render :partial => 'users_list' %> | |
| 10 | +</div> | |
| 11 | + | |
| 12 | +<%= render :partial => 'index_buttons' %> | ... | ... |
app/views/users/index.rhtml
public/designs/icons/tango/style.css
| ... | ... | @@ -101,6 +101,11 @@ |
| 101 | 101 | .icon-user-unknown { background-image: url(Tango/16x16/status/dialog-error.png) } |
| 102 | 102 | .icon-alert { background-image: url(Tango/16x16/status/dialog-warning.png) } |
| 103 | 103 | |
| 104 | +.icon-activate-user { background-image: url(Tango/16x16/emblems/emblem-system.png) } | |
| 105 | +.icon-deactivate-user { background-image: url(Tango/16x16/emblems/emblem-unreadable.png) } | |
| 106 | +.icon-set-admin-role { background-image: url(mod/16x16/apps/user.png) } | |
| 107 | +.icon-reset-admin-role { background-image: url(/images/icons-app/person-icon.png) } | |
| 108 | + | |
| 104 | 109 | /******************LARGE ICONS********************/ |
| 105 | 110 | .image-gallery-item .folder { background-image: url(mod/96x96/places/folder.png) } |
| 106 | 111 | .image-gallery-item .gallery { background-image: url(mod/96x96/mimetypes/image-x-generic.png) } | ... | ... |
public/stylesheets/application.css
| ... | ... | @@ -4137,6 +4137,20 @@ h1#agenda-title { |
| 4137 | 4137 | -webkit-border-radius:10px; |
| 4138 | 4138 | } |
| 4139 | 4139 | |
| 4140 | +/* ==> public/stylesheets/controller_environment_users.css <== */ | |
| 4141 | +.controller-environment_users table { | |
| 4142 | + text-align: left; | |
| 4143 | +} | |
| 4144 | + | |
| 4145 | +#environment-users-search form { | |
| 4146 | + padding: 10px; | |
| 4147 | + margin-bottom: 15px; | |
| 4148 | + background-color: #E6E6E6; | |
| 4149 | + -moz-border-radius: 5px; | |
| 4150 | + -webkit-border-radius: 5px; | |
| 4151 | +} | |
| 4152 | + | |
| 4153 | + | |
| 4140 | 4154 | /* * * Profile search * * * * * * * */ |
| 4141 | 4155 | |
| 4142 | 4156 | #public-profile-search, #profile-search-results form, .profile-search-block form { | ... | ... |
| ... | ... | @@ -0,0 +1,103 @@ |
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | |
| 2 | +require 'environment_users_controller' | |
| 3 | + | |
| 4 | +# Re-raise errors caught by the controller. | |
| 5 | +class EnvironmentUsersController; def rescue_action(e) raise e end; end | |
| 6 | + | |
| 7 | +class EnvironmentUsersControllerTest < ActionController::TestCase | |
| 8 | + | |
| 9 | + # all_fixtures | |
| 10 | + def setup | |
| 11 | + @controller = EnvironmentUsersController.new | |
| 12 | + @request = ActionController::TestRequest.new | |
| 13 | + @response = ActionController::TestResponse.new | |
| 14 | + | |
| 15 | + admin_user = create_user_with_permission('adminuser', 'manage_environment_users', Environment.default) | |
| 16 | + login_as('adminuser') | |
| 17 | + end | |
| 18 | + | |
| 19 | + should 'not access without right permission' do | |
| 20 | + guest = create_user('guest') | |
| 21 | + login_as 'guest' | |
| 22 | + | |
| 23 | + get :index | |
| 24 | + assert_response 403 # forbidden | |
| 25 | + end | |
| 26 | + | |
| 27 | + should 'grant access with right permission' do | |
| 28 | + get :index | |
| 29 | + assert_response :success | |
| 30 | + end | |
| 31 | + | |
| 32 | + should 'blank search results include activated and deactivated users' do | |
| 33 | + deactivated = create_user('deactivated') | |
| 34 | + deactivated.activated_at = nil | |
| 35 | + deactivated.person.visible = false | |
| 36 | + deactivated.save! | |
| 37 | + get :index, :q => '' | |
| 38 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /adminuser/}} | |
| 39 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /deactivated/}} | |
| 40 | + end | |
| 41 | + | |
| 42 | + should 'blank search include all users' do | |
| 43 | + (1..5).each {|i| | |
| 44 | + u = create_user('user'+i.to_s) | |
| 45 | + } | |
| 46 | + get :index, :q => '' # blank search | |
| 47 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /adminuser/}} | |
| 48 | + (1..5).each {|i| | |
| 49 | + u = 'user'+i.to_s | |
| 50 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => u}} | |
| 51 | + } | |
| 52 | + end | |
| 53 | + | |
| 54 | + should 'search not include all users' do | |
| 55 | + (1..5).each {|i| | |
| 56 | + u = create_user('user'+i.to_s) | |
| 57 | + } | |
| 58 | + get :index, :q => 'er5' # search | |
| 59 | + assert_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => /user5/}} | |
| 60 | + (1..4).each {|i| | |
| 61 | + u = 'user'+i.to_s | |
| 62 | + assert_no_tag :tag => 'div', :attributes => { :id => /users-list/ }, :descendant => {:tag => 'a', :attributes => {:title => u}} | |
| 63 | + } | |
| 64 | + end | |
| 65 | + | |
| 66 | + should 'set admin role' do | |
| 67 | + u = create_user() | |
| 68 | + assert_equal false, u.person.is_admin? | |
| 69 | + post :set_admin_role, :id => u.person.id, :q => '' | |
| 70 | + u.reload | |
| 71 | + assert u.person.is_admin? | |
| 72 | + end | |
| 73 | + | |
| 74 | + should 'reset admin role' do | |
| 75 | + u = create_user() | |
| 76 | + e = Environment.default | |
| 77 | + e.add_admin(u.person) | |
| 78 | + u.reload | |
| 79 | + assert u.person.is_admin? | |
| 80 | + post :reset_admin_role, :id => u.person.id, :q => '' | |
| 81 | + u.reload | |
| 82 | + assert_equal false, u.person.is_admin? | |
| 83 | + end | |
| 84 | + | |
| 85 | + should 'activate user' do | |
| 86 | + u = create_user() | |
| 87 | + assert_equal false, u.activated? | |
| 88 | + post :activate, :id => u.person.id, :q => '' | |
| 89 | + u.reload | |
| 90 | + assert u.activated? | |
| 91 | + end | |
| 92 | + | |
| 93 | + should 'deactivate user' do | |
| 94 | + u = create_user() | |
| 95 | + u.activated_at = Time.now.utc | |
| 96 | + u.activation_code = nil | |
| 97 | + u.person.visible = true | |
| 98 | + assert u.activated? | |
| 99 | + post :deactivate, :id => u.person.id, :q => '' | |
| 100 | + u.reload | |
| 101 | + assert_equal false, u.activated? | |
| 102 | + end | |
| 103 | +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') | ... | ... |