aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/Users.pm52
-rw-r--r--perllib/FixMyStreet/DB/Result/User.pm15
-rw-r--r--t/app/controller/admin/roles.t29
-rw-r--r--templates/web/base/admin/users/form.html14
-rw-r--r--web/cobrands/fixmystreet/admin.js26
5 files changed, 126 insertions, 10 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin/Users.pm b/perllib/FixMyStreet/App/Controller/Admin/Users.pm
index 1edddd960..26f81992a 100644
--- a/perllib/FixMyStreet/App/Controller/Admin/Users.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin/Users.pm
@@ -136,6 +136,18 @@ sub add : Local : Args(0) {
$c->res->redirect( $c->uri_for_action( 'admin/users/edit', $user->id ) );
}
+sub fetch_body_roles : Private {
+ my ($self, $c, $body ) = @_;
+
+ my $roles = $body->roles->search(undef, { order_by => 'name' });
+ unless ($roles) {
+ delete $c->stash->{roles}; # Body doesn't have any roles
+ return;
+ }
+
+ $c->stash->{roles} = [ $roles->all ];
+}
+
sub edit : Path : Args(1) {
my ( $self, $c, $id ) = @_;
@@ -157,6 +169,7 @@ sub edit : Path : Args(1) {
$c->forward('/admin/fetch_all_bodies');
$c->forward('/admin/fetch_body_areas', [ $user->from_body ]) if $user->from_body;
+ $c->forward('fetch_body_roles', [ $user->from_body ]) if $user->from_body;
$c->cobrand->call_hook('admin_user_edit_extra_data');
if ( defined $c->flash->{status_message} ) {
@@ -269,26 +282,45 @@ sub edit : Path : Args(1) {
# If so, we need to re-fetch areas so the UI is up to date.
if ( $user->from_body && $user->from_body->id ne $c->stash->{fetched_areas_body_id} ) {
$c->forward('/admin/fetch_body_areas', [ $user->from_body ]);
+ $c->forward('fetch_body_roles', [ $user->from_body ]);
}
if (!$user->from_body) {
# Non-staff users aren't allowed any permissions or to be in an area
$user->admin_user_body_permissions->delete;
+ $user->user_roles->delete;
$user->area_ids(undef);
delete $c->stash->{areas};
+ delete $c->stash->{roles};
delete $c->stash->{fetched_areas_body_id};
} elsif ($c->stash->{available_permissions}) {
- my @all_permissions = map { keys %$_ } values %{ $c->stash->{available_permissions} };
- my @user_permissions = grep { $c->get_param("permissions[$_]") ? 1 : undef } @all_permissions;
- $user->admin_user_body_permissions->search({
- body_id => $user->from_body->id,
- permission_type => { -not_in => \@user_permissions },
- })->delete;
- foreach my $permission_type (@user_permissions) {
- $user->user_body_permissions->find_or_create({
+ my %valid_roles = map { $_->id => 1 } @{$c->stash->{roles}};
+ my @role_ids = grep { $valid_roles{$_} } $c->get_param_list('roles');
+ if (@role_ids) {
+ # Roles take precedence over permissions
+ $user->admin_user_body_permissions->delete;
+ $user->user_roles->search({
+ role_id => { -not_in => \@role_ids },
+ })->delete;
+ foreach my $role (@role_ids) {
+ $user->user_roles->find_or_create({
+ role_id => $role,
+ });
+ }
+ } else {
+ $user->user_roles->delete;
+ my @all_permissions = map { keys %$_ } values %{ $c->stash->{available_permissions} };
+ my @user_permissions = grep { $c->get_param("permissions[$_]") ? 1 : undef } @all_permissions;
+ $user->admin_user_body_permissions->search({
body_id => $user->from_body->id,
- permission_type => $permission_type,
- });
+ permission_type => { -not_in => \@user_permissions },
+ })->delete;
+ foreach my $permission_type (@user_permissions) {
+ $user->user_body_permissions->find_or_create({
+ body_id => $user->from_body->id,
+ permission_type => $permission_type,
+ });
+ }
}
}
diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm
index edcd20fdf..9dc5df856 100644
--- a/perllib/FixMyStreet/DB/Result/User.pm
+++ b/perllib/FixMyStreet/DB/Result/User.pm
@@ -628,4 +628,19 @@ sub in_area {
return $self->areas_hash->{$area};
}
+has roles_hash => (
+ is => 'ro',
+ lazy => 1,
+ default => sub {
+ my $self = shift;
+ my %ids = map { $_->role_id => 1 } $self->user_roles->all;
+ return \%ids;
+ },
+);
+
+sub in_role {
+ my ($self, $role) = @_;
+ return $self->roles_hash->{$role};
+}
+
1;
diff --git a/t/app/controller/admin/roles.t b/t/app/controller/admin/roles.t
index a7740a572..77c5b0033 100644
--- a/t/app/controller/admin/roles.t
+++ b/t/app/controller/admin/roles.t
@@ -7,10 +7,20 @@ my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super Us
my $body = $mech->create_body_ok(2237, 'Oxfordshire County Council');
my $body2 = $mech->create_body_ok(2482, 'Bromley Council');
my $editor = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $body);
+my $user = $mech->create_user_ok('staffuser@example.com', name => 'Other Council User', from_body => $body);
+
$editor->user_body_permissions->create({
body => $body,
permission_type => 'user_edit',
});
+$editor->user_body_permissions->create({
+ body => $body,
+ permission_type => 'user_manage_permissions',
+});
+$user->user_body_permissions->create({
+ body => $body,
+ permission_type => 'report_edit_priority',
+});
FixMyStreet::DB->resultset("Role")->create({
body => $body,
@@ -25,6 +35,7 @@ FixMyStreet::DB->resultset("Role")->create({
FixMyStreet::override_config {
ALLOWED_COBRANDS => 'oxfordshire',
+ MAPIT_URL => 'http://mapit.uk',
}, sub {
$mech->log_in_ok( $editor->email );
@@ -68,6 +79,24 @@ FixMyStreet::override_config {
$mech->content_lacks('Role A');
};
+ subtest 'assign a user to a role' => sub {
+ $mech->get_ok('/admin/users/' . $user->id);
+ $mech->content_contains('Role B');
+ $mech->content_lacks('Role Z');
+ $mech->submit_form_ok({ with_fields => {
+ roles => 'Role B',
+ }});
+ $mech->content_like(qr/<option[^>]*selected>Role B/);
+ is $user->roles->count, 1, 'in one role';
+ is $user->user_body_permissions->count, 0, 'permissions removed';
+ };
+
+ subtest 'remove user from role' => sub {
+ $mech->submit_form_ok({ with_fields => {
+ roles => undef,
+ }}, 'remove role');
+ };
+
};
subtest 'superuser can see all bodies' => sub {
diff --git a/templates/web/base/admin/users/form.html b/templates/web/base/admin/users/form.html
index 944de802c..c9cc6463b 100644
--- a/templates/web/base/admin/users/form.html
+++ b/templates/web/base/admin/users/form.html
@@ -155,6 +155,20 @@
[% IF available_permissions AND NOT user.is_superuser %]
<li>
+ <div class="admin-hint">
+ <p>
+ [% loc("Users can be assigned one or more roles to give them all the permissions of those roles. Selecting a role or roles will disable manual permission selection.") %]
+ </p>
+ </div>
+ <label for="roles">[% loc('Role:') %]</label>
+ <select class="form-control js-multiple" id="roles" name="roles" multiple>
+ [% FOREACH role IN roles %]
+ <option data-permissions='["[% role.permissions.join('","') | html %]"]' value="[% role.id %]"[% ' selected' IF user.in_role(role.id) %]>[% role.name | html %]</option>
+ [% END %]
+ </select>
+ </li>
+
+ <li>
<fieldset>
<legend>
<div class="admin-hint">
diff --git a/web/cobrands/fixmystreet/admin.js b/web/cobrands/fixmystreet/admin.js
index b947b3e49..8bc956c57 100644
--- a/web/cobrands/fixmystreet/admin.js
+++ b/web/cobrands/fixmystreet/admin.js
@@ -80,6 +80,32 @@ $(function(){
$("form#user_edit .js-user-categories").toggle(show_area);
});
+ $('form#user_edit select#roles').change(function() {
+ var $perms = $('.permissions-checkboxes');
+ if ($(this).val()) {
+ var selected_perms = {};
+ $(this).find(':selected').each(function() {
+ $.each($(this).data('permissions'), function(i, p) {
+ selected_perms['permissions[' + p + ']'] = 1;
+ });
+ });
+ console.log(selected_perms);
+ $perms.css('color', '#666');
+ $perms.find('a').css('color', '#666');
+ $perms.find('input').each(function() {
+ this.checked = selected_perms[this.name] || false;
+ });
+ $perms.find('input').prop('disabled', true);
+ } else {
+ $perms.css('color', '');
+ $perms.find('a').css('color', '');
+ $perms.find('input').each(function() {
+ this.checked = this.hasAttribute('checked');
+ });
+ $perms.find('input').prop('disabled', false);
+ }
+ }).change();
+
// On category edit page, hide the reputation input if inspection isn't required
$("form#category_edit #inspection_required").change(function() {
var $p = $("form#category_edit #reputation_threshold").closest("p");