diff options
author | Marius Halden <marius.h@lden.org> | 2019-10-30 19:28:55 +0100 |
---|---|---|
committer | Marius Halden <marius.h@lden.org> | 2019-10-30 19:28:55 +0100 |
commit | 377bd96aab7cad3434185c30eb908c9da447fe40 (patch) | |
tree | 7ec5527e205d5b62caaa862a7de8cd25199c8bf0 /perllib/FixMyStreet/App/Controller/Report.pm | |
parent | 56f61b1441070aa0b9ddcfc74aca46c20313609f (diff) | |
parent | 92b253904062edd533e55c22824de6fd01e2f7c1 (diff) |
Merge tag 'v2.6' into fiksgatami-dev
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Report.pm')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report.pm | 242 |
1 files changed, 156 insertions, 86 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 799985f8e..7f798f4f4 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -20,8 +20,8 @@ Show a report =head2 index -Redirect to homepage unless C<id> parameter in query, in which case redirect to -'/report/$id'. +Redirect to homepage unless we have a homepage template, +in which case show that. =cut @@ -35,13 +35,13 @@ sub index : Path('') : Args(0) { } } -=head2 report_display +=head2 id -Display a report. +Load in ID, for use by chained pages. =cut -sub display : Path('') : Args(1) { +sub id :PathPart('report') :Chained :CaptureArgs(1) { my ( $self, $c, $id ) = @_; if ( @@ -49,15 +49,17 @@ sub display : Path('') : Args(1) { || $id =~ m{ ^(\d+) \D .* $ }x # trailing garbage ) { - return $c->res->redirect( $c->uri_for($1), 301 ); + $c->res->redirect( $c->uri_for($1), 301 ); + $c->detach; } - $c->forward( '_display', [ $id ] ); + $c->forward( 'load_problem_or_display_error', [ $id ] ); } =head2 ajax -Return JSON formatted details of a report +Return JSON formatted details of a report. +URL used by mobile app so remains /report/ajax/N. =cut @@ -65,40 +67,62 @@ sub ajax : Path('ajax') : Args(1) { my ( $self, $c, $id ) = @_; $c->stash->{ajax} = 1; - $c->forward( '_display', [ $id ] ); + $c->forward('load_problem_or_display_error', [ $id ]); + $c->forward('display'); } -sub _display : Private { - my ( $self, $c, $id ) = @_; +=head2 display + +Display a report. + +=cut + +sub display :PathPart('') :Chained('id') :Args(0) { + my ( $self, $c ) = @_; $c->forward('/auth/get_csrf_token'); - $c->forward( 'load_problem_or_display_error', [ $id ] ); $c->forward( 'load_updates' ); $c->forward( 'format_problem_for_display' ); my $permissions = $c->stash->{_permissions} ||= $c->forward( 'check_has_permission_to', - [ qw/report_inspect report_edit_category report_edit_priority/ ] ); + [ qw/report_inspect report_edit_category report_edit_priority report_mark_private/ ] ); if (any { $_ } values %$permissions) { $c->stash->{template} = 'report/inspect.html'; $c->forward('inspect'); } } -sub support : Path('support') : Args(0) { +sub moderate_report :PathPart('moderate') :Chained('id') :Args(0) { my ( $self, $c ) = @_; - my $id = $c->get_param('id'); + if ($c->user_exists && $c->user->can_moderate($c->stash->{problem})) { + $c->stash->{show_moderation} = 'report'; + $c->stash->{template} = 'report/display.html'; + $c->detach('display'); + } + $c->res->redirect($c->stash->{problem}->url); +} + +sub moderate_update :PathPart('moderate') :Chained('id') :Args(1) { + my ( $self, $c, $update_id ) = @_; - my $uri = - $id - ? $c->uri_for( '/report', $id ) - : $c->uri_for('/'); + my $comment = $c->stash->{problem}->comments->find($update_id); + if ($c->user_exists && $comment && $c->user->can_moderate($comment)) { + $c->stash->{show_moderation} = $update_id; + $c->stash->{template} = 'report/display.html'; + $c->detach('display'); + } + $c->res->redirect($c->stash->{problem}->url); +} - if ( $id && $c->cobrand->can_support_problems && $c->user && $c->user->from_body ) { - $c->forward( 'load_problem_or_display_error', [ $id ] ); +sub support :Chained('id') :Args(0) { + my ( $self, $c ) = @_; + + if ( $c->cobrand->can_support_problems && $c->user && $c->user->from_body ) { $c->stash->{problem}->update( { interest_count => \'interest_count +1' } ); } - $c->res->redirect( $uri ); + + $c->res->redirect($c->stash->{problem}->url); } sub load_problem_or_display_error : Private { @@ -130,8 +154,8 @@ sub load_problem_or_display_error : Private { # Creator, and inspection users can see non_public reports $c->stash->{problem} = $problem; my $permissions = $c->stash->{_permissions} = $c->forward( 'check_has_permission_to', - [ qw/report_inspect report_edit_category report_edit_priority/ ] ); - if ( !$c->user || ($c->user->id != $problem->user->id && !$permissions->{report_inspect}) ) { + [ qw/report_inspect report_edit_category report_edit_priority report_mark_private / ] ); + if ( !$c->user || ($c->user->id != $problem->user->id && !($permissions->{report_inspect} || $permissions->{report_mark_private})) ) { $c->detach( '/page_error_403_access_denied', [ sprintf(_('That report cannot be viewed on %s.'), $c->stash->{site_name}) ] @@ -140,7 +164,7 @@ sub load_problem_or_display_error : Private { } $c->stash->{problem} = $problem; - if ( $c->user_exists && $c->user->has_permission_to(moderate => $problem->bodies_str_ids) ) { + if ( $c->user_exists && $c->user->can_moderate($problem) ) { $c->stash->{problem_original} = $problem->find_or_new_related( moderation_original_data => { title => $problem->title, @@ -162,14 +186,30 @@ sub load_updates : Private { { order_by => [ 'confirmed', 'id' ] } ); - my $questionnaires = $c->model('DB::Questionnaire')->search( + my $questionnaires_still_open = $c->model('DB::Questionnaire')->search( + { + problem_id => $c->stash->{problem}->id, + whenanswered => { '!=', undef }, + -or => [ { + # Any steady state open/closed + old_state => [ -and => + { -in => [ FixMyStreet::DB::Result::Problem::closed_states, FixMyStreet::DB::Result::Problem::open_states ] }, + \'= new_state', + ], + }, { + # Any reopening + new_state => 'confirmed', + } ] + }, + { order_by => 'whenanswered' } + ); + + my $questionnaires_fixed = $c->model('DB::Questionnaire')->search( { problem_id => $c->stash->{problem}->id, whenanswered => { '!=', undef }, - old_state => [ -and => - { -in => [ FixMyStreet::DB::Result::Problem::closed_states, FixMyStreet::DB::Result::Problem::open_states ] }, - \'= new_state', - ] + old_state => { -not_in => [ FixMyStreet::DB::Result::Problem::fixed_states ] }, + new_state => { -in => [ FixMyStreet::DB::Result::Problem::fixed_states ] }, }, { order_by => 'whenanswered' } ); @@ -182,13 +222,36 @@ sub load_updates : Private { $questionnaires_with_updates{$qid} = $update; } } - while (my $q = $questionnaires->next) { + while (my $q = $questionnaires_still_open->next) { if (my $update = $questionnaires_with_updates{$q->id}) { $update->set_extra_metadata('open_from_questionnaire', 1); next; } push @combined, [ $q->whenanswered, $q ]; } + while (my $q = $questionnaires_fixed->next) { + next if $questionnaires_with_updates{$q->id}; + push @combined, [ $q->whenanswered, $q ]; + } + + # And include moderation changes... + my $problem = $c->stash->{problem}; + my $public_history = $c->cobrand->call_hook(public_moderation_history => $problem); + my $user_can_moderate = $c->user_exists && $c->user->can_moderate($problem); + if ($public_history || $user_can_moderate) { + my @history = $problem->moderation_history; + my $last_history = $problem; + foreach my $history (@history) { + push @combined, [ $history->created, { + id => 'm' . $history->id, + type => 'moderation', + last => $last_history, + entry => $history, + } ]; + $last_history = $history; + } + } + @combined = map { $_->[1] } sort { $a->[0] <=> $b->[0] } @combined; $c->stash->{updates} = \@combined; @@ -206,6 +269,9 @@ sub format_problem_for_display : Private { my $problem = $c->stash->{problem}; + # upload_fileid is used by the update form on this page + $c->stash->{problem_upload_fileid} = $problem->get_photoset->data; + ( $c->stash->{latitude}, $c->stash->{longitude} ) = map { Utils::truncate_coordinate($_) } ( $problem->latitude, $problem->longitude ); @@ -251,7 +317,7 @@ sub generate_map_tags : Private { latitude => $problem->latitude, longitude => $problem->longitude, pins => $problem->used_map - ? [ $problem->pin_data($c, 'report', type => 'big') ] + ? [ $problem->pin_data($c, 'report', type => 'big', draggable => 1) ] : [], ); @@ -271,22 +337,18 @@ users too about this change, at which point we can delete: =cut -sub delete :Local :Args(1) { - my ( $self, $c, $id ) = @_; +sub delete :Chained('id') :Args(0) { + my ($self, $c) = @_; $c->forward('/auth/check_csrf_token'); - $c->forward( 'load_problem_or_display_error', [ $id ] ); my $p = $c->stash->{problem}; - my $uri = $c->uri_for( '/report', $id ); - - return $c->res->redirect($uri) unless $c->user_exists; + return $c->res->redirect($p->url) unless $c->user_exists; my $body = $c->user->obj->from_body; - return $c->res->redirect($uri) unless $body; - - return $c->res->redirect($uri) unless $p->bodies->{$body->id}; + return $c->res->redirect($p->url) unless $body; + return $c->res->redirect($p->url) unless $p->bodies->{$body->id}; $p->state('hidden'); $p->lastupdate( \'current_timestamp' ); @@ -299,26 +361,10 @@ sub delete :Local :Args(1) { admin_user => $c->user->from_body->name, object_type => 'problem', action => 'state_change', - object_id => $id, + object_id => $p->id, } ); - return $c->res->redirect($uri); -} - -=head2 action_router - -A router for dispatching handlers for sub-actions on a particular report, -e.g. /report/1/inspect - -=cut - -sub action_router : Path('') : Args(2) { - my ( $self, $c, $id, $action ) = @_; - - $c->go( 'map', [ $id ] ) if $action eq 'map'; - $c->go( 'nearby_json', [ $id ] ) if $action eq 'nearby.json'; - - $c->detach( '/page_error_404_not_found', [] ); + return $c->res->redirect($p->url); } sub inspect : Private { @@ -327,7 +373,7 @@ sub inspect : Private { my $permissions = $c->stash->{_permissions}; $c->forward('/admin/categories_for_point'); - $c->stash->{report_meta} = { map { $_->{name} => $_ } @{ $c->stash->{problem}->get_extra_fields() } }; + $c->stash->{report_meta} = { map { 'x' . $_->{name} => $_ } @{ $c->stash->{problem}->get_extra_fields() } }; if ($c->cobrand->can('council_area_id')) { my $priorities_by_category = FixMyStreet::App->model('DB::ResponsePriority')->by_categories($c->cobrand->council_area_id, @{$c->stash->{contacts}}); @@ -357,8 +403,6 @@ sub inspect : Private { my %update_params = (); if ($permissions->{report_inspect}) { - $problem->non_public($c->get_param('non_public') ? 1 : 0); - $problem->set_extra_metadata( traffic_information => $c->get_param('traffic_information') ); if ( my $info = $c->get_param('detailed_information') ) { @@ -375,12 +419,6 @@ sub inspect : Private { } } - if ( $c->get_param('defect_type') ) { - $problem->defect_type($problem->defect_types->find($c->get_param('defect_type'))); - } else { - $problem->defect_type(undef); - } - if ( $c->get_param('include_update') ) { $update_text = Utils::cleanup_text( $c->get_param('public_update'), { allow_multiline => 1 } ); if (!$update_text) { @@ -438,6 +476,8 @@ sub inspect : Private { } } + $problem->non_public($c->get_param('non_public') ? 1 : 0); + if ( !$c->forward( '/admin/report_edit_location', [ $problem ] ) ) { # New lat/lon isn't valid, show an error $valid = 0; @@ -461,11 +501,26 @@ sub inspect : Private { $c->forward('/report/new/set_report_extras', [ \@contacts, $param_prefix ]); } - # Updating priority must come after category, in case category has changed (and so might have priorities) - if ($c->get_param('priority') && ($permissions->{report_inspect} || $permissions->{report_edit_priority})) { - $problem->response_priority( $problem->response_priorities->find({ id => $c->get_param('priority') }) ); + # Updating priority/defect type must come after category, in case + # category has changed (and so might have priorities/defect types) + if ($permissions->{report_inspect} || $permissions->{report_edit_priority}) { + if ($c->get_param('priority')) { + $problem->response_priority( $problem->response_priorities->find({ id => $c->get_param('priority') }) ); + } else { + $problem->response_priority(undef); + } } + if ($permissions->{report_inspect}) { + if ( $c->get_param('defect_type') ) { + $problem->defect_type($problem->defect_types->find($c->get_param('defect_type'))); + } else { + $problem->defect_type(undef); + } + } + + $c->cobrand->call_hook(report_inspect_update_extra => $problem); + if ($valid) { if ( $reputation_change != 0 ) { $problem->user->update_reputation($reputation_change); @@ -479,7 +534,7 @@ sub inspect : Private { # to have the FMS timezone so we need to add the timezone otherwise # dates come back out the database at time +/- timezone offset. $timestamp = DateTime->from_epoch( - time_zone => FixMyStreet->time_zone || FixMyStreet->local_time_zone, + time_zone => FixMyStreet->local_time_zone, epoch => $saved_at ); } @@ -508,7 +563,7 @@ sub inspect : Private { # shortlist is always a single click away, being on the main nav. if ($c->user->has_body_permission_to('planned_reports')) { unless ($redirect_uri = $c->get_param("post_inspect_url")) { - my $categories = join(',', @{ $c->user->categories }); + my $categories = $c->user->categories_string; my $params = { lat => $problem->latitude, lon => $problem->longitude, @@ -532,10 +587,8 @@ sub inspect : Private { } }; -sub map : Private { - my ( $self, $c, $id ) = @_; - - $c->forward( 'load_problem_or_display_error', [ $id ] ); +sub map :Chained('id') :Args(0) { + my ($self, $c) = @_; my $image = $c->stash->{problem}->static_map; $c->res->content_type($image->{content_type}); @@ -543,27 +596,44 @@ sub map : Private { } -sub nearby_json : Private { - my ( $self, $c, $id ) = @_; +sub nearby_json :PathPart('nearby.json') :Chained('id') :Args(0) { + my ($self, $c) = @_; - $c->forward( 'load_problem_or_display_error', [ $id ] ); my $p = $c->stash->{problem}; - my $dist = 1; + $c->forward('_nearby_json', [ { + latitude => $p->latitude, + longitude => $p->longitude, + categories => [ $p->category ], + ids => [ $p->id ], + } ]); +} + +sub _nearby_json :Private { + my ($self, $c, $params) = @_; # This is for the list template, this is a list on that page. $c->stash->{page} = 'report'; - my $extra_params = $c->cobrand->call_hook('display_location_extra_params'); + # distance in metres + my $dist = $c->get_param('distance') || ''; + $dist = 1000 unless $dist =~ /^\d+$/; + $dist = 1000 if $dist > 1000; + $params->{distance} = $dist / 1000; + + my $pin_size = $c->get_param('pin_size') || ''; + $pin_size = 'small' unless $pin_size =~ /^(mini|small|normal|big)$/; + + $params->{extra} = $c->cobrand->call_hook('display_location_extra_params'); + $params->{limit} = 5; + + my $nearby = $c->model('DB::Nearby')->nearby($c, %$params); - my $nearby = $c->model('DB::Nearby')->nearby( - $c, $dist, [ $p->id ], 5, $p->latitude, $p->longitude, [ $p->category ], undef, $extra_params - ); # Want to treat these as if they were on map $nearby = [ map { $_->problem } @$nearby ]; my @pins = map { my $p = $_->pin_data($c, 'around'); [ $p->{latitude}, $p->{longitude}, $p->{colour}, - $p->{id}, $p->{title}, 'small', JSON->false + $p->{id}, $p->{title}, $pin_size, JSON->false ] } @$nearby; |