diff options
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin.pm | 17 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Around.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Council.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report.pm | 58 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/New.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/Result/User.pm | 20 | ||||
-rw-r--r-- | t/app/controller/report_inspect.t | 97 | ||||
-rw-r--r-- | templates/web/base/report/_inspect.html | 17 | ||||
-rw-r--r-- | templates/web/base/report/_main.html | 29 |
9 files changed, 196 insertions, 52 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index c853827d0..1713c4ff9 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -859,14 +859,11 @@ $problem->bodies_str. sub report_edit_location : Private { my ($self, $c, $problem) = @_; - return 1 unless $c->forward('/report/new/determine_location'); - - if ( $c->stash->{latitude} != $problem->latitude || $c->stash->{longitude} != $problem->longitude ) { - delete $c->stash->{prefetched_all_areas}; - delete $c->stash->{all_areas}; - delete $c->stash->{fetch_all_areas}; - delete $c->stash->{all_areas_mapit}; - $c->forward('/council/load_and_check_areas'); + return 1 unless $c->forward('/location/determine_location_from_coords'); + + my ($lat, $lon) = map { Utils::truncate_coordinate($_) } $problem->latitude, $problem->longitude; + if ( $c->stash->{latitude} != $lat || $c->stash->{longitude} != $lon ) { + $c->forward('/council/load_and_check_areas', []); $c->forward('/report/new/setup_categories_and_bodies'); my %allowed_bodies = map { $_ => 1 } @{$problem->bodies_str_ids}; my @new_bodies = @{$c->stash->{bodies_to_list}}; @@ -885,8 +882,8 @@ sub categories_for_point : Private { # We have a report, stash its location $c->forward('/report/new/determine_location_from_report'); # Look up the areas for this location - $c->stash->{prefetched_all_areas} = [ grep { $_ } split ',', $c->stash->{report}->areas ]; - $c->forward('/around/check_location_is_acceptable'); + my $prefetched_all_areas = [ grep { $_ } split ',', $c->stash->{report}->areas ]; + $c->forward('/around/check_location_is_acceptable', [ $prefetched_all_areas ]); # As with a new report, fetch the bodies/categories $c->forward('/report/new/setup_categories_and_bodies'); diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index c4feca8b6..ec84ca09a 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -52,7 +52,7 @@ sub index : Path : Args(0) { } # Check to see if the spot is covered by a area - if not show an error. - return unless $c->forward('check_location_is_acceptable'); + return unless $c->forward('check_location_is_acceptable', []); # If we have a partial - redirect to /report/new so that it can be # completed. diff --git a/perllib/FixMyStreet/App/Controller/Council.pm b/perllib/FixMyStreet/App/Controller/Council.pm index 06a23aec9..0e7553dc4 100644 --- a/perllib/FixMyStreet/App/Controller/Council.pm +++ b/perllib/FixMyStreet/App/Controller/Council.pm @@ -36,7 +36,7 @@ there are no areas then return false. =cut sub load_and_check_areas : Private { - my ( $self, $c ) = @_; + my ( $self, $c, $prefetched_all_areas ) = @_; my $latitude = $c->stash->{latitude}; my $longitude = $c->stash->{longitude}; @@ -55,10 +55,10 @@ sub load_and_check_areas : Private { $params{generation} = $c->config->{MAPIT_GENERATION} if $c->config->{MAPIT_GENERATION}; - if ($c->stash->{prefetched_all_areas}) { + if ($prefetched_all_areas) { $all_areas = { map { $_ => { id => $_ } } - @{$c->stash->{prefetched_all_areas}} + @$prefetched_all_areas }; } elsif ( $c->stash->{fetch_all_areas} ) { my %area_types = map { $_ => 1 } @$area_types; diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 6a7a14b5c..582de092c 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -3,6 +3,7 @@ package FixMyStreet::App::Controller::Report; use Moose; use namespace::autoclean; use JSON::MaybeXS; +use List::MoreUtils qw(any); BEGIN { extends 'Catalyst::Controller'; } @@ -299,7 +300,8 @@ sub inspect : Private { $c->forward('/auth/get_csrf_token'); $c->forward( 'load_problem_or_display_error', [ $id ] ); - $c->forward( 'check_has_permission_to', [ 'report_inspect' ] ); + my $permissions = $c->forward( 'check_has_permission_to', + [ qw/report_inspect report_edit_category report_edit_priority/ ] ); $c->forward( 'load_updates' ); $c->forward( 'format_problem_for_display' ); @@ -310,28 +312,32 @@ sub inspect : Private { if ( $c->get_param('save') || $c->get_param('save_inspected') ) { $c->forward('/auth/check_csrf_token'); - foreach (qw/detailed_location detailed_information traffic_information/) { - $problem->set_extra_metadata( $_ => $c->get_param($_) ); - } + if ($permissions->{report_inspect}) { + foreach (qw/detailed_location detailed_information traffic_information/) { + $problem->set_extra_metadata( $_ => $c->get_param($_) ); + } - if ( $c->get_param('save_inspected') ) { - $problem->set_extra_metadata( inspected => 1 ); - } + if ( $c->get_param('save_inspected') ) { + $problem->set_extra_metadata( inspected => 1 ); + } - # Handle the state changing - my $old_state = $problem->state; - $problem->state($c->get_param('state')); - if ( $problem->is_visible() and $old_state eq 'unconfirmed' ) { - $problem->confirmed( \'current_timestamp' ); - } - if ( $problem->state eq 'hidden' ) { - $problem->get_photoset->delete_cached; - } - if ( $problem->state ne $old_state ) { - $c->forward( '/admin/log_edit', [ $id, 'problem', 'state_change' ] ); + # Handle the state changing + my $old_state = $problem->state; + $problem->state($c->get_param('state')); + if ( $problem->is_visible() and $old_state eq 'unconfirmed' ) { + $problem->confirmed( \'current_timestamp' ); + } + if ( $problem->state eq 'hidden' ) { + $problem->get_photoset->delete_cached; + } + if ( $problem->state ne $old_state ) { + $c->forward( '/admin/log_edit', [ $id, 'problem', 'state_change' ] ); + } } - $problem->response_priority( $problem->response_priorities->find({ id => $c->get_param('priority') }) ); + if ($c->get_param('priority') && ($permissions->{report_inspect} || $permissions->{report_edit_priority})) { + $problem->response_priority( $problem->response_priorities->find({ id => $c->get_param('priority') }) ); + } my $valid = 1; if ( !$c->forward( '/admin/report_edit_location', [ $problem ] ) ) { @@ -340,7 +346,9 @@ sub inspect : Private { $c->stash->{errors} = [ _('Invalid location. New location must be covered by the same council.') ]; } - $c->forward( '/admin/report_edit_category', [ $problem ] ); + if ($permissions->{report_inspect} || $permissions->{report_edit_category}) { + $c->forward( '/admin/report_edit_category', [ $problem ] ); + } if ($valid) { $problem->update; @@ -362,18 +370,22 @@ sub map : Private { =head2 check_has_permission_to -Ensure the currently logged-in user has a particular permission that applies +Ensure the currently logged-in user has any of the provided permissions applied to the current Problem in $c->stash->{problem}. Shows the 403 page if not. =cut sub check_has_permission_to : Private { - my ( $self, $c, $permission ) = @_; + my ( $self, $c, @permissions ) = @_; my $bodies = $c->stash->{problem}->bodies_str; + my %permissions = map { $_ => $c->user->has_permission_to($_, $bodies) } @permissions + if $c->user_exists; $c->detach('/page_error_403_access_denied', [ _("Sorry, you don't have permission to do that.") ] ) - unless $c->user_exists && $c->user->has_permission_to($permission, $bodies); + unless $c->user_exists && any { $_ } values %permissions; + + return \%permissions; }; __PACKAGE__->meta->make_immutable; diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index cfb572d83..7960f5e4c 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -481,7 +481,7 @@ sub determine_location : Private { || $c->forward('/location/determine_location_from_coords') || $c->forward('determine_location_from_report') ) # - && $c->forward('/around/check_location_is_acceptable'); + && $c->forward('/around/check_location_is_acceptable', []); return; } diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 6444cfe6a..0ba7e252c 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -240,18 +240,34 @@ sub split_name { return { first => $first || '', last => $last || '' }; } +sub permissions { + my ($self, $c, $body_id) = @_; + + if ($self->is_superuser) { + my $perms = $c->cobrand->available_permissions; + return { map { %$_ } values %$perms }; + } + + return unless $self->belongs_to_body($body_id); + + my @permissions = $self->user_body_permissions->search({ + body_id => $self->from_body->id, + })->all; + return { map { $_->permission_type => 1 } @permissions }; +} + sub has_permission_to { my ($self, $permission_type, $body_id) = @_; return 1 if $self->is_superuser; - return unless $self->belongs_to_body($body_id); + return 0 unless $self->belongs_to_body($body_id); my $permission = $self->user_body_permissions->find({ permission_type => $permission_type, body_id => $self->from_body->id, }); - return $permission ? 1 : undef; + return $permission ? 1 : 0; } =head2 has_body_permission_to diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t new file mode 100644 index 000000000..adba77011 --- /dev/null +++ b/t/app/controller/report_inspect.t @@ -0,0 +1,97 @@ +use strict; +use warnings; +use Test::More; + +use FixMyStreet::TestMech; + +my $mech = FixMyStreet::TestMech->new; + +my $brum = $mech->create_body_ok(2514, 'Birmingham City Council'); +my $oxon = $mech->create_body_ok(2237, 'Oxfordshire County Council'); +my $contact = $mech->create_contact_ok( body_id => $oxon->id, category => 'Cows', email => 'cows@example.net' ); +my $rp = FixMyStreet::DB->resultset("ResponsePriority")->create({ + body => $oxon, + name => 'High Priority', +}); +FixMyStreet::DB->resultset("ContactResponsePriority")->create({ + contact => $contact, + response_priority => $rp, +}); + +my ($report) = $mech->create_problems_for_body(1, $oxon->id, 'Test', { + category => 'Cows', cobrand => 'fixmystreet', areas => ',2237,' }); +my $report_id = $report->id; + +my $user = $mech->log_in_ok('test@example.com'); +$user->update( { from_body => $oxon } ); + +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.mysociety.org/', + ALLOWED_COBRANDS => 'fixmystreet', +}, sub { + subtest "test inspect page" => sub { + $mech->get_ok("/report/$report_id"); + $mech->content_lacks('Inspect'); + $mech->content_lacks('Manage'); + + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_edit_priority' }); + $mech->get_ok("/report/$report_id"); + $mech->content_contains('Manage'); + $mech->follow_link_ok({ text => 'Manage' }); + + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_inspect' }); + $mech->get_ok("/report/$report_id"); + $mech->content_contains('Inspect'); + $mech->follow_link_ok({ text => 'Inspect' }); + }; + + subtest "test basic inspect submission" => sub { + $mech->submit_form_ok({ button => 'save', with_fields => { traffic_information => 'Lots', state => 'Planned' } }); + $report->discard_changes; + is $report->state, 'planned', 'report state changed'; + is $report->get_extra_metadata('traffic_information'), 'Lots', 'report data changed'; + }; + + subtest "test location changes" => sub { + $mech->get_ok("/report/$report_id/inspect"); + $mech->submit_form_ok({ button => 'save', with_fields => { latitude => 55, longitude => -2 } }); + $mech->content_contains('Invalid location'); + $mech->submit_form_ok({ button => 'save', with_fields => { latitude => 51.754926, longitude => -1.256179 } }); + $mech->content_lacks('Invalid location'); + }; + + foreach my $test ( + { type => 'report_edit_priority', priority => 1 }, + { type => 'report_edit_category', category => 1 }, + { type => 'report_inspect', priority => 1, category => 1, detailed => 1 }, + ) { + subtest "test $test->{type} permission" => sub { + $user->user_body_permissions->delete; + $user->user_body_permissions->create({ body => $oxon, permission_type => $test->{type} }); + $mech->get_ok("/report/$report_id/inspect"); + has_or_lacks($test->{priority}, 'Priority'); + has_or_lacks($test->{priority}, 'High'); + has_or_lacks($test->{category}, 'Category'); + has_or_lacks($test->{detailed}, 'Detailed problem information'); + $mech->submit_form_ok({ + button => 'save', + with_fields => { + $test->{priority} ? (priority => 1) : (), + $test->{category} ? (category => 'Cows') : (), + $test->{detailed} ? (detailed_information => 'Highland ones') : (), + } + }); + }; + } +}; + +END { + $mech->delete_body($oxon); + $mech->delete_body($brum); + done_testing(); +} + +sub has_or_lacks { + my ($flag, $text) = @_; + $flag ? $mech->content_contains($text) : $mech->content_lacks($text); +} diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html index 891d94c43..ce5110c54 100644 --- a/templates/web/base/report/_inspect.html +++ b/templates/web/base/report/_inspect.html @@ -1,4 +1,5 @@ [% PROCESS 'admin/report_blocks.html'; # For the report state dropdown %] +[% permissions = c.user.permissions(c, problem.bodies_str) %] [% second_column = BLOCK -%] <div id="side-report-secondary"> <div class="problem-inspector-fields clearfix"> @@ -7,6 +8,8 @@ <label for="problem_id">[% loc('Report ID:') %]</label> <input type="text" readonly id="problem_id" value="[% problem.id %]"> </p> + +[% IF permissions.report_edit_priority OR permissions.report_inspect %] <p class="right"> <label for="problem_priority">[% loc('Priority:') %]</label> <select name="priority" id="problem_priority"> @@ -16,6 +19,9 @@ [% END %] </select> </p> +[% END %] + +[% IF permissions.report_inspect %] <p class="left"> <label for="state">[% loc('State:') %]</label> [%# XXX this is duplicated from admin/report_edit.html, should be refactored %] @@ -29,6 +35,9 @@ [% END %] </select> </p> +[% END %] + +[% IF permissions.report_edit_category OR permissions.report_inspect %] <p class="right"> <label for="category">[% loc('Category:') %]</label> [%# XXX this is duplicated from admin/report_edit.html, should be refactored %] @@ -47,6 +56,8 @@ [% END %] </select> </p> +[% END %] + <p> [% SET local_coords = problem.local_coords; %] <strong>[% loc('Easting/Northing:') %]</strong> @@ -59,6 +70,8 @@ <a href="#" id="geolocate_link">[% loc('Use my current location') %]</a>, [% loc('or drag the pin on the map') %] » </p> + +[% IF permissions.report_inspect %] <p> <label for="detailed_information">[% loc('Detailed problem location:') %]</label> <textarea rows="2" name="detailed_location">[% problem.get_extra_metadata('detailed_location') | html %]</textarea> @@ -71,6 +84,8 @@ <label for="traffic_information">[% loc('Traffic management information:') %]</label> <textarea rows="2" name="traffic_information">[% problem.get_extra_metadata('traffic_information') | html %]</textarea> </p> +[% END %] + <p> <input type="hidden" name="token" value="[% csrf_token %]"> <a href="[% c.uri_for( '/report', problem.id ) %]" class="btn">[% loc('Cancel') %]</a> @@ -90,4 +105,4 @@ </form> </div> </div> -[%- END %]
\ No newline at end of file +[%- END %] diff --git a/templates/web/base/report/_main.html b/templates/web/base/report/_main.html index 1b1a6e29e..e02d4b2b0 100644 --- a/templates/web/base/report/_main.html +++ b/templates/web/base/report/_main.html @@ -1,5 +1,6 @@ -[% can_moderate = c.user && c.user.has_permission_to('moderate', problem.bodies_str) %] -[% can_inspect = c.user && c.user.has_permission_to('report_inspect', problem.bodies_str) && !hide_inspect_button %] +[% IF c.user_exists %] + [% DEFAULT permissions = c.user.permissions(c, problem.bodies_str) %] +[%- END %] <a href="[% c.uri_for( '/around', { lat => latitude, lon => longitude } ) %]" class="problem-back js-back-to-report-list">[% loc('Back to all reports') %]</a> @@ -23,7 +24,7 @@ </form> [% END %] - [% IF can_moderate %] + [% IF permissions.moderate %] [% original = problem_original %] <form method="post" action="/moderate/report/[% problem.id %]"> <input type="hidden" name="token" value="[% csrf_token %]"> @@ -31,7 +32,7 @@ <h1 class="moderate-display">[% problem.title | html %]</h1> - [% IF can_moderate %] + [% IF permissions.moderate %] <div class="moderate-edit"> [% IF problem.title != original.title %] <label> @@ -60,7 +61,7 @@ [% INCLUDE 'report/_support.html' %] - [% IF can_moderate %] + [% IF permissions.moderate %] [% IF problem.photo or original.photo %] <p class="moderate-edit"> <label> @@ -76,7 +77,7 @@ [% problem.detail | add_links | html_para %] </div> - [% IF can_moderate %] + [% IF permissions.moderate %] <p class="moderate-edit"> [% IF problem.detail != original.detail %] <label> @@ -105,18 +106,24 @@ </div> [% END %] - [% IF can_moderate OR can_inspect %] + [% IF permissions.keys.grep('moderate|report_inspect|report_edit_category|report_edit_priority').size %] <p class="moderate-display"> - [% IF can_moderate %] + [% IF permissions.moderate %] <input type="button" class="btn moderate" value="Moderate this report"> [% END %] - [% IF can_inspect %] - <a href="/report/[% problem.id %]/inspect" class="btn inspect">Inspect</a> + [% IF !hide_inspect_button AND permissions.keys.grep('report_inspect|report_edit_category|report_edit_priority').size %] + <a href="/report/[% problem.id %]/inspect" class="btn inspect"> + [%- IF permissions.report_inspect %] + [%- loc('Inspect') %] + [%- ELSE %] + [%- loc('Manage') %] + [%- END ~%] + </a> [% END %] </p> [% END %] - [% IF can_moderate %] + [% IF permissions.moderate %] </form> [% END %] |