aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm29
-rw-r--r--perllib/FixMyStreet/Cobrand/Default.pm31
-rw-r--r--perllib/FixMyStreet/Cobrand/UKCouncils.pm10
-rw-r--r--t/app/controller/admin.t167
-rw-r--r--t/app/controller/admin_permissions.t201
-rw-r--r--t/app/controller/moderate.t1
-rw-r--r--templates/web/base/admin/user-form.html30
7 files changed, 418 insertions, 51 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index ce2a653a2..c8432df0c 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -1129,6 +1129,10 @@ sub user_edit : Path('user_edit') : Args(1) {
$c->stash->{user} = $user;
+ if ( $user->from_body && $c->user->has_permission_to('user_manage_permissions', $user->from_body->id) ) {
+ $c->stash->{available_permissions} = $c->cobrand->available_permissions;
+ }
+
$c->forward('fetch_all_bodies');
if ( $c->get_param('submit') ) {
@@ -1139,7 +1143,7 @@ sub user_edit : Path('user_edit') : Args(1) {
if ( $user->email ne $c->get_param('email') ||
$user->name ne $c->get_param('name') ||
($user->phone || "") ne $c->get_param('phone') ||
- ($user->from_body && $user->from_body->id ne $c->get_param('body')) ||
+ ($user->from_body && $c->get_param('body') && $user->from_body->id ne $c->get_param('body')) ||
(!$user->from_body && $c->get_param('body'))
) {
$edited = 1;
@@ -1153,14 +1157,33 @@ sub user_edit : Path('user_edit') : Args(1) {
$user->is_superuser( ( $c->user->is_superuser && $c->get_param('is_superuser') ) || 0 );
# Superusers can set from_body to any value, but other staff can only
# set from_body to the same value as their own from_body.
- if ($c->user->is_superuser) {
+ if ( $c->user->is_superuser ) {
$user->from_body( $c->get_param('body') || undef );
- } elsif ($c->get_param('body') eq $c->user->from_body->id) {
+ } elsif ( $c->user->has_permission_to('user_assign_body', $c->user->from_body->id ) &&
+ $c->get_param('body') && $c->get_param('body') eq $c->user->from_body->id ) {
$user->from_body( $c->user->from_body );
} else {
$user->from_body( undef );
}
+ if (!$user->from_body) {
+ # Non-staff users aren't allowed any permissions
+ $user->user_body_permissions->delete_all;
+ } 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->user_body_permissions->search({
+ body_id => $user->from_body->id,
+ permission_type => { '!=' => \@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,
+ });
+ }
+ }
+
unless ($user->email) {
$c->stash->{field_errors}->{email} = _('Please enter a valid email');
return;
diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm
index 8c75a1234..326919654 100644
--- a/perllib/FixMyStreet/Cobrand/Default.pm
+++ b/perllib/FixMyStreet/Cobrand/Default.pm
@@ -658,6 +658,37 @@ sub admin_allow_user {
return 1 if $user->is_superuser;
}
+=head2 available_permissions
+
+Grouped lists of permission types available for use in the admin
+
+=cut
+
+sub available_permissions {
+ my $self = shift;
+
+ return {
+ _("Problems") => {
+ moderate => _("Moderate report details"),
+ report_edit => _("Edit reports"),
+ report_edit_category => _("Edit report category"), # future use
+ report_edit_priority => _("Edit report priority"), # future use
+ report_inspect => _("Markup problem details"),
+ report_instruct => _("Instruct contractors to fix problems"), # future use
+ planned_reports => _("Manage planned reports list"),
+ contribute_as_another_user => _("Create reports/updates on a user's behalf"),
+ contribute_as_body => _("Create reports/updates as the council"),
+ },
+ _("Users") => {
+ user_edit => _("Edit other users' details"),
+ user_manage_permissions => _("Edit other users' permissions"),
+ user_assign_body => _("Grant access to the admin"),
+ user_assign_areas => _("Assign users to areas"), # future use
+ },
+ };
+}
+
+
=head2 area_types
The MaPit types this site handles
diff --git a/perllib/FixMyStreet/Cobrand/UKCouncils.pm b/perllib/FixMyStreet/Cobrand/UKCouncils.pm
index 701a4ca1c..5d72c4962 100644
--- a/perllib/FixMyStreet/Cobrand/UKCouncils.pm
+++ b/perllib/FixMyStreet/Cobrand/UKCouncils.pm
@@ -184,4 +184,14 @@ sub admin_allow_user {
return $user->from_body->id == $self->council_id;
}
+sub available_permissions {
+ my $self = shift;
+
+ my $perms = $self->next::method();
+ $perms->{Problems}->{contribute_as_body} = "Create reports/updates as " . $self->council_name;
+ $perms->{Users}->{user_assign_areas} = "Assign users to areas in " . $self->council_name;
+
+ return $perms;
+}
+
1;
diff --git a/t/app/controller/admin.t b/t/app/controller/admin.t
index 531fa7726..df4a72c4b 100644
--- a/t/app/controller/admin.t
+++ b/t/app/controller/admin.t
@@ -15,6 +15,8 @@ my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super Us
my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237);
my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire);
+my $bromley = $mech->create_body_ok(2482, 'Bromley Council', id => 2482);
+
my $user3 = $mech->create_user_ok('test3@example.com', name => 'Test User 2');
if ( $user3 ) {
@@ -1153,6 +1155,19 @@ for my $test (
phone => '',
flagged => undef,
is_superuser => undef,
+ "permissions[moderate]" => undef,
+ "permissions[planned_reports]" => undef,
+ "permissions[report_edit]" => undef,
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_body]" => undef,
+ "permissions[user_assign_areas]" => undef,
},
changes => {
name => 'Changed User',
@@ -1169,6 +1184,19 @@ for my $test (
phone => '',
flagged => undef,
is_superuser => undef,
+ "permissions[moderate]" => undef,
+ "permissions[planned_reports]" => undef,
+ "permissions[report_edit]" => undef,
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_body]" => undef,
+ "permissions[user_assign_areas]" => undef,
},
changes => {
email => 'changed@example.com',
@@ -1185,6 +1213,19 @@ for my $test (
phone => '',
flagged => undef,
is_superuser => undef,
+ "permissions[moderate]" => undef,
+ "permissions[planned_reports]" => undef,
+ "permissions[report_edit]" => undef,
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_body]" => undef,
+ "permissions[user_assign_areas]" => undef,
},
changes => {
body => $southend->id,
@@ -1201,6 +1242,19 @@ for my $test (
phone => '',
flagged => undef,
is_superuser => undef,
+ "permissions[moderate]" => undef,
+ "permissions[planned_reports]" => undef,
+ "permissions[report_edit]" => undef,
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_body]" => undef,
+ "permissions[user_assign_areas]" => undef,
},
changes => {
flagged => 'on',
@@ -1217,6 +1271,19 @@ for my $test (
phone => '',
flagged => 'on',
is_superuser => undef,
+ "permissions[moderate]" => undef,
+ "permissions[planned_reports]" => undef,
+ "permissions[report_edit]" => undef,
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_body]" => undef,
+ "permissions[user_assign_areas]" => undef,
},
changes => {
flagged => undef,
@@ -1233,10 +1300,38 @@ for my $test (
phone => '',
flagged => undef,
is_superuser => undef,
+ "permissions[moderate]" => undef,
+ "permissions[planned_reports]" => undef,
+ "permissions[report_edit]" => undef,
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_body]" => undef,
+ "permissions[user_assign_areas]" => undef,
},
changes => {
is_superuser => 'on',
},
+ removed => [
+ "permissions[moderate]",
+ "permissions[planned_reports]",
+ "permissions[report_edit]",
+ "permissions[report_edit_category]",
+ "permissions[report_edit_priority]",
+ "permissions[report_inspect]",
+ "permissions[report_instruct]",
+ "permissions[contribute_as_another_user]",
+ "permissions[contribute_as_body]",
+ "permissions[user_edit]",
+ "permissions[user_manage_permissions]",
+ "permissions[user_assign_body]",
+ "permissions[user_assign_areas]",
+ ],
log_count => 5,
log_entries => [qw/edit edit edit edit edit/],
},
@@ -1253,6 +1348,21 @@ for my $test (
changes => {
is_superuser => undef,
},
+ added => {
+ "permissions[moderate]" => undef,
+ "permissions[planned_reports]" => undef,
+ "permissions[report_edit]" => undef,
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_body]" => undef,
+ "permissions[user_assign_areas]" => undef,
+ },
log_count => 5,
log_entries => [qw/edit edit edit edit edit/],
},
@@ -1270,6 +1380,17 @@ for my $test (
$mech->submit_form_ok( { with_fields => $expected } );
+ # Some actions cause visible fields to be added/removed
+ foreach my $x (@{ $test->{removed} }) {
+ delete $expected->{$x};
+ }
+ if ( $test->{added} ) {
+ $expected = {
+ %$expected,
+ %{ $test->{added} }
+ };
+ }
+
$visible = $mech->visible_form_values;
is_deeply $visible, $expected, 'user updated';
@@ -1355,52 +1476,8 @@ subtest "Users with from_body can't access fixmystreet.com admin" => sub {
};
};
-$report->bodies_str(2237);
-$report->cobrand('oxfordshire');
-$report->update;
-
-$mech->log_in_ok( $oxfordshireuser->email );
-
-subtest "Users can't edit report without report_edit permission" => sub {
- FixMyStreet::override_config {
- ALLOWED_COBRANDS => [ 'oxfordshire' ],
- }, sub {
- $mech->get("/admin/report_edit/$report_id");
- ok !$mech->res->is_success(), "want a bad response";
- is $mech->res->code, 404, "got 404, can't edit report without report_edit permission";
- };
-};
-
-subtest "Users can edit report with report_edit permission" => sub {
- FixMyStreet::override_config {
- ALLOWED_COBRANDS => [ 'oxfordshire' ],
- }, sub {
- $oxfordshireuser->user_body_permissions->create({
- body => $oxfordshire,
- permission_type => 'report_edit',
- });
-
- $mech->get_ok("/admin/report_edit/$report_id");
- $mech->content_contains( $report->title );
- };
-};
-
-subtest "Users can't edit another council's reports with their own council's report_edit permission" => sub {
- FixMyStreet::override_config {
- ALLOWED_COBRANDS => [ 'oxfordshire' ],
- }, sub {
- $report->bodies_str(2482);
- $report->cobrand('bromley');
- $report->update;
-
- $mech->get("/admin/report_edit/$report_id");
- ok !$mech->res->is_success(), "want a bad response";
- is $mech->res->code, 404, "got 404, can't edit report with incorrect body in report_edit permission";
- };
-};
-
-
$mech->log_out_ok;
+$user2->user_body_permissions->delete_all;
$oxfordshireuser->user_body_permissions->delete_all;
diff --git a/t/app/controller/admin_permissions.t b/t/app/controller/admin_permissions.t
new file mode 100644
index 000000000..63b753ff3
--- /dev/null
+++ b/t/app/controller/admin_permissions.t
@@ -0,0 +1,201 @@
+use strict;
+use warnings;
+use Test::More;
+use LWP::Protocol::PSGI;
+
+use t::Mock::MapIt;
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+my $user = $mech->create_user_ok('test@example.com', name => 'Test User');
+my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2');
+my $superuser = $mech->create_user_ok('superuser@example.com', name => 'Super User', is_superuser => 1);
+
+my $oxfordshire = $mech->create_body_ok(2237, 'Oxfordshire County Council', id => 2237);
+my $oxfordshireuser = $mech->create_user_ok('counciluser@example.com', name => 'Council User', from_body => $oxfordshire);
+
+my $bromley = $mech->create_body_ok(2482, 'Bromley Council', id => 2482);
+
+END {
+ $mech->delete_user( $user );
+ $mech->delete_user( $user2 );
+ $mech->delete_user( $superuser );
+ $mech->delete_user( $oxfordshireuser );
+}
+
+my $dt = DateTime->new(
+ year => 2011,
+ month => 04,
+ day => 16,
+ hour => 15,
+ minute => 47,
+ second => 23
+);
+
+my ($report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Test');
+my $report_id = $report->id;
+ok $report, "created test report - $report_id";
+
+$mech->log_in_ok( $oxfordshireuser->email );
+
+subtest "Users can't edit report without report_edit permission" => sub {
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'oxfordshire' ],
+ }, sub {
+ $mech->get("/admin/report_edit/$report_id");
+ ok !$mech->res->is_success(), "want a bad response";
+ is $mech->res->code, 403, "got 403, can't edit report without report_edit permission";
+ };
+};
+
+subtest "Users can edit report with report_edit permission" => sub {
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'oxfordshire' ],
+ }, sub {
+ $oxfordshireuser->user_body_permissions->create({
+ body => $oxfordshire,
+ permission_type => 'report_edit',
+ });
+
+ $mech->get_ok("/admin/report_edit/$report_id");
+ $mech->content_contains( $report->title );
+ };
+};
+
+subtest "Users can't edit another council's reports with their own council's report_edit permission" => sub {
+ FixMyStreet::override_config {
+ ALLOWED_COBRANDS => [ 'oxfordshire' ],
+ }, sub {
+ $report->bodies_str($bromley->id);
+ $report->cobrand('bromley');
+ $report->update;
+
+ $mech->get("/admin/report_edit/$report_id");
+ ok !$mech->res->is_success(), "want a bad response";
+ is $mech->res->code, 404, "got 404, can't edit report with incorrect body in report_edit permission";
+ };
+};
+
+
+FixMyStreet::override_config {
+ MAPIT_URL => 'http://mapit.uk/',
+ ALLOWED_COBRANDS => [ 'oxfordshire' ],
+}, sub {
+ LWP::Protocol::PSGI->register(t::Mock::MapIt->run_if_script, host => 'mapit.uk');
+
+ my $user2_id = $user2->id;
+ $report->update({ bodies_str => $oxfordshire->id });
+
+ foreach my $perm (0, 1) {
+ if ($perm) {
+ $oxfordshireuser->user_body_permissions->find_or_create({
+ body => $oxfordshire,
+ permission_type => 'user_edit',
+ });
+ }
+ foreach my $report_user ($user, $user2) {
+ $report->update({ user => $report_user });
+ foreach my $from_body (undef, $bromley, $oxfordshire) {
+ $user2->update({ from_body => $from_body });
+ my $result = ($from_body || '') eq $oxfordshire || $report->user eq $user2 ? ($perm ? 200 : 403 ) : 404;
+ my $u = $result == 200 ? 'can' : 'cannot';
+ my $b = $from_body ? $from_body->name : 'no body';
+ my $p = $perm ? 'with' : 'without';
+ my $r = $report->user eq $user2 ? 'with' : 'without';
+ subtest "User $u edit user for $b $p permission, $r cobrand relation" => sub {
+ $mech->get("/admin/user_edit/$user2_id");
+ my $success = $mech->res->is_success();
+ ok $result == 200 ? $success : !$success, "got correct response";
+ is $mech->res->code, $result, "got $result";
+ };
+ }
+ }
+ }
+
+ $oxfordshireuser->user_body_permissions->create({
+ body => $oxfordshire,
+ permission_type => 'user_assign_body',
+ });
+
+ subtest "Users can edit users of their own council" => sub {
+ $mech->get_ok("/admin/user_edit/$user2_id");
+ $mech->content_contains( $user2->name );
+
+ # We shouldn't be able to see the permissions tick boxes
+ $mech->content_lacks('Moderate report details');
+
+ $mech->submit_form_ok( { with_fields => {
+ name => 'Test Updated User 2',
+ email => $user2->email,
+ body => $user2->from_body->id,
+ phone => '',
+ flagged => undef,
+ } } );
+ $user2->discard_changes;
+ is $user2->name, 'Test Updated User 2', 'name changed';
+ };
+
+ $oxfordshireuser->user_body_permissions->create({
+ body => $oxfordshire,
+ permission_type => 'user_manage_permissions',
+ });
+
+ subtest "Users can edit permissions" => sub {
+ is $user2->user_body_permissions->count, 0, 'user2 has no permissions';
+
+ $mech->get_ok("/admin/user_edit/$user2_id");
+ $mech->content_contains('Moderate report details');
+
+ $mech->submit_form_ok( { with_fields => {
+ name => $user2->name,
+ email => $user2->email,
+ body => $user2->from_body->id,
+ phone => '',
+ flagged => undef,
+ "permissions[moderate]" => 'on',
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_areas]" => undef,
+ } } );
+
+ ok $user2->has_permission_to("moderate", $user2->from_body->id), "user2 has been granted moderate permission";
+ };
+
+ subtest "Unsetting user from_body removes all permissions " => sub {
+ is $user2->user_body_permissions->count, 1, 'user2 has 1 permission';
+
+ $mech->get_ok("/admin/user_edit/$user2_id");
+ $mech->content_contains('Moderate report details');
+
+ $mech->submit_form_ok( { with_fields => {
+ name => $user2->name,
+ email => $user2->email,
+ body => undef,
+ phone => '',
+ flagged => undef,
+ "permissions[moderate]" => 'on', # NB tick box is left on deliberately
+ "permissions[report_edit_category]" => undef,
+ "permissions[report_edit_priority]" => undef,
+ "permissions[report_inspect]" => undef,
+ "permissions[report_instruct]" => undef,
+ "permissions[contribute_as_another_user]" => undef,
+ "permissions[contribute_as_body]" => undef,
+ "permissions[user_edit]" => undef,
+ "permissions[user_manage_permissions]" => undef,
+ "permissions[user_assign_areas]" => undef,
+ } } );
+
+ is $user2->user_body_permissions->count, 0, 'user2 has had permissions removed';
+ };
+};
+
+$mech->log_out_ok;
+
+done_testing();
diff --git a/t/app/controller/moderate.t b/t/app/controller/moderate.t
index 52201c63b..0ccfcf2c2 100644
--- a/t/app/controller/moderate.t
+++ b/t/app/controller/moderate.t
@@ -334,5 +334,6 @@ $update2->delete;
$report->moderation_original_data->delete;
$report->delete;
$report2->delete;
+$mech->delete_user($user);
done_testing();
diff --git a/templates/web/base/admin/user-form.html b/templates/web/base/admin/user-form.html
index 96a51486b..5bca4171a 100644
--- a/templates/web/base/admin/user-form.html
+++ b/templates/web/base/admin/user-form.html
@@ -41,8 +41,7 @@
<option value="[% body.id %]"[% ' selected' IF body.id == user.from_body.id %]>[% body.name %]</option>
[% END %]
</select>
- [% IF user.from_body AND user.has_permission_to('moderate', user.from_body.id) %]*[% END %]
- </li>
+ </li>
[% ELSE %]
<li>
<div class="admin-hint">
@@ -50,7 +49,7 @@
[% loc("Staff users have permission to log in to the admin.") %]
</p>
</div>
- [% loc('Staff:') %] <input type="checkbox" id="body" name="body" value="[% c.user.from_body.id %]" [% user.from_body.id == c.user.from_body.id ? ' checked' : '' %]>
+ [% loc('Staff:') %] <input type="checkbox" id="body" name="body" value="[% c.user.from_body.id %]" [% user.from_body.id == c.user.from_body.id ? ' checked' : '' %] [% 'disabled' UNLESS c.user.has_permission_to('user_assign_body', c.user.from_body.id) %]>
</li>
[% END %]
@@ -79,6 +78,31 @@
[% loc('Superuser:') %] <input type="checkbox" id="is_superuser" name="is_superuser"[% user.is_superuser ? ' checked' : '' %]>
</li>
[% END %]
+
+ [% IF available_permissions AND NOT user.is_superuser %]
+ <li>
+ <div class="admin-hint">
+ <p>
+ [% loc("Users can perform the following actions within their assigned body or area.") %]
+ </p>
+ </div>
+ [% loc('Permissions:') %]
+ </li>
+ [% FOREACH group IN available_permissions.pairs %]
+ <li>
+ [% group.key %]
+ <ul>
+ [% FOREACH permission IN group.value %]
+ <li>
+ <label>
+ <input type="checkbox" id="perms_[% permission.key %]" name="permissions[[% permission.key %]]" [% "checked" IF user.has_permission_to(permission.key, user.from_body.id) %]>
+ [% permission.value %]
+ </label>
+ </li>
+ [% END %]
+ </ul>
+ [% END %]
+ [% END %]
[% END %]
</ul>
<input type="submit" name="Submit changes" value="[% loc('Submit changes') %]" >