diff options
author | Marius Halden <marius.h@lden.org> | 2020-09-29 14:23:52 +0200 |
---|---|---|
committer | Marius Halden <marius.h@lden.org> | 2020-09-29 14:23:52 +0200 |
commit | a27ce1524d801d2742a2bdb6ec1da45126d64353 (patch) | |
tree | 64123c4e17dc1776aa0a7cd65ee01d49d3e7d978 /perllib/FixMyStreet/App/Controller/Admin.pm | |
parent | 377bd96aab7cad3434185c30eb908c9da447fe40 (diff) | |
parent | 2773c60226b9370fe8ee00f7b205b571bb87c3b5 (diff) |
Merge tag 'v3.0.1' into fiksgatami-dev
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Admin.pm')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin.pm | 832 |
1 files changed, 85 insertions, 747 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index 2f4669456..038cba9e5 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -4,20 +4,9 @@ use namespace::autoclean; BEGIN { extends 'Catalyst::Controller'; } -use Path::Class; -use POSIX qw(strftime strcoll); -use Digest::SHA qw(sha1_hex); -use mySociety::EmailUtil qw(is_valid_email is_valid_email_list); -use DateTime::Format::Strptime; +use POSIX qw(strcoll); use List::Util 'first'; -use List::MoreUtils 'uniq'; -use mySociety::ArrayUtils; -use Text::CSV; -use Try::Tiny; - -use FixMyStreet::SendReport; use FixMyStreet::SMS; -use Utils; =head1 NAME @@ -72,57 +61,27 @@ Displays some summary information for the requests. sub index : Path : Args(0) { my ( $self, $c ) = @_; - if ($c->cobrand->moniker eq 'zurich' && $c->stash->{admin_type} ne 'super') { - return $c->cobrand->admin(); + if ($c->cobrand->moniker eq 'zurich') { + if ($c->stash->{admin_type} eq 'super') { + $c->forward('/admin/stats/gather'); + return 1; + } else { + return $c->cobrand->admin(); + } } - $c->forward('/admin/stats/state'); - my @unsent = $c->cobrand->problems->search( { state => [ FixMyStreet::DB::Result::Problem::open_states() ], whensent => undef, bodies_str => { '!=', undef }, + # Ignore very recent ones that probably just haven't been sent yet + confirmed => { '<', \"current_timestamp - '5 minutes'::interval" }, + }, + { + order_by => 'confirmed', } )->all; $c->stash->{unsent_reports} = \@unsent; - my $alerts = $c->model('DB::Alert')->summary_report_alerts( $c->cobrand->restriction ); - - my %alert_counts = - map { $_->confirmed => $_->get_column('confirmed_count') } $alerts->all; - - $alert_counts{0} ||= 0; - $alert_counts{1} ||= 0; - - $c->stash->{alerts} = \%alert_counts; - - my $contacts = $c->model('DB::Contact')->summary_count(); - - my %contact_counts = - map { $_->state => $_->get_column('state_count') } $contacts->all; - - $contact_counts{confirmed} ||= 0; - $contact_counts{unconfirmed} ||= 0; - $contact_counts{total} = $contact_counts{confirmed} + $contact_counts{unconfirmed}; - - $c->stash->{contacts} = \%contact_counts; - - my $questionnaires = $c->model('DB::Questionnaire')->summary_count( $c->cobrand->restriction ); - - my %questionnaire_counts = map { - $_->get_column('answered') => $_->get_column('questionnaire_count') - } $questionnaires->all; - $questionnaire_counts{1} ||= 0; - $questionnaire_counts{0} ||= 0; - - $questionnaire_counts{total} = - $questionnaire_counts{0} + $questionnaire_counts{1}; - $c->stash->{questionnaires_pc} = - $questionnaire_counts{total} - ? sprintf( '%.1f', - $questionnaire_counts{1} / $questionnaire_counts{total} * 100 ) - : _('n/a'); - $c->stash->{questionnaires} = \%questionnaire_counts; - $c->forward('fetch_all_bodies'); return 1; @@ -143,47 +102,38 @@ sub timeline : Path( 'timeline' ) : Args(0) { my %time; - try { - $c->model('DB')->schema->storage->sql_maker->quote_char( '"' ); - $c->model('DB')->schema->storage->sql_maker->name_sep( '.' ); + my $probs = $c->cobrand->problems->timeline; - my $probs = $c->cobrand->problems->timeline; - - foreach ($probs->all) { - push @{$time{$_->created->epoch}}, { type => 'problemCreated', date => $_->created, obj => $_ }; - push @{$time{$_->confirmed->epoch}}, { type => 'problemConfirmed', date => $_->confirmed, obj => $_ } if $_->confirmed; - push @{$time{$_->whensent->epoch}}, { type => 'problemSent', date => $_->whensent, obj => $_ } if $_->whensent; - } + foreach ($probs->all) { + push @{$time{$_->created->epoch}}, { type => 'problemCreated', date => $_->created, obj => $_ }; + push @{$time{$_->confirmed->epoch}}, { type => 'problemConfirmed', date => $_->confirmed, obj => $_ } if $_->confirmed; + push @{$time{$_->whensent->epoch}}, { type => 'problemSent', date => $_->whensent, obj => $_ } if $_->whensent; + } - my $questionnaires = $c->model('DB::Questionnaire')->timeline( $c->cobrand->restriction ); + my $questionnaires = $c->model('DB::Questionnaire')->timeline( $c->cobrand->restriction ); - foreach ($questionnaires->all) { - push @{$time{$_->whensent->epoch}}, { type => 'quesSent', date => $_->whensent, obj => $_ }; - push @{$time{$_->whenanswered->epoch}}, { type => 'quesAnswered', date => $_->whenanswered, obj => $_ } if $_->whenanswered; - } + foreach ($questionnaires->all) { + push @{$time{$_->whensent->epoch}}, { type => 'quesSent', date => $_->whensent, obj => $_ }; + push @{$time{$_->whenanswered->epoch}}, { type => 'quesAnswered', date => $_->whenanswered, obj => $_ } if $_->whenanswered; + } - my $updates = $c->cobrand->updates->timeline; + my $updates = $c->cobrand->updates->timeline; - foreach ($updates->all) { - push @{$time{$_->created->epoch}}, { type => 'update', date => $_->created, obj => $_} ; - } + foreach ($updates->all) { + push @{$time{$_->created->epoch}}, { type => 'update', date => $_->created, obj => $_} ; + } - my $alerts = $c->model('DB::Alert')->timeline_created( $c->cobrand->restriction ); + my $alerts = $c->model('DB::Alert')->timeline_created( $c->cobrand->restriction ); - foreach ($alerts->all) { - push @{$time{$_->whensubscribed->epoch}}, { type => 'alertSub', date => $_->whensubscribed, obj => $_ }; - } + foreach ($alerts->all) { + push @{$time{$_->whensubscribed->epoch}}, { type => 'alertSub', date => $_->whensubscribed, obj => $_ }; + } - $alerts = $c->model('DB::Alert')->timeline_disabled( $c->cobrand->restriction ); + $alerts = $c->model('DB::Alert')->timeline_disabled( $c->cobrand->restriction ); - foreach ($alerts->all) { - push @{$time{$_->whendisabled->epoch}}, { type => 'alertDel', date => $_->whendisabled, obj => $_ }; - } - } catch { - die $_; - } finally { - $c->model('DB')->schema->storage->sql_maker->quote_char( '' ); - }; + foreach ($alerts->all) { + push @{$time{$_->whendisabled->epoch}}, { type => 'alertDel', date => $_->whendisabled, obj => $_ }; + } $c->stash->{time} = \%time; @@ -195,7 +145,7 @@ sub fetch_contacts : Private { my $contacts = $c->stash->{body}->contacts->search(undef, { order_by => [ 'category' ] } ); $c->stash->{contacts} = $contacts; - $c->stash->{live_contacts} = $contacts->not_deleted; + $c->stash->{live_contacts} = $contacts->not_deleted_admin; $c->stash->{any_not_confirmed} = $contacts->search({ state => 'unconfirmed' })->count; if ( $c->get_param('text') && $c->get_param('text') eq '1' ) { @@ -221,167 +171,6 @@ sub fetch_languages : Private { return 1; } -sub reports : Path('reports') { - my ( $self, $c ) = @_; - - $c->stash->{edit_body_contacts} = 1 - if grep { $_ eq 'body' } keys %{$c->stash->{allowed_pages}}; - - my $query = {}; - if ( $c->cobrand->moniker eq 'zurich' ) { - my $type = $c->stash->{admin_type}; - my $body = $c->stash->{body}; - if ( $type eq 'dm' ) { - my @children = map { $_->id } $body->bodies->all; - my @all = (@children, $body->id); - $query = { bodies_str => \@all }; - } elsif ( $type eq 'sdm' ) { - $query = { bodies_str => $body->id }; - } - } - - my $order = $c->get_param('o') || 'created'; - my $dir = defined $c->get_param('d') ? $c->get_param('d') : 1; - $c->stash->{order} = $order; - $c->stash->{dir} = $dir; - $order .= ' desc' if $dir; - - my $p_page = $c->get_param('p') || 1; - my $u_page = $c->get_param('u') || 1; - - return if $c->cobrand->call_hook(report_search_query => $query, $p_page, $u_page, $order); - - if (my $search = $c->get_param('search')) { - $search = $self->trim($search); - - # In case an email address, wrapped in <...> - if ($search =~ /^<(.*)>$/) { - my $possible_email = $1; - my $parsed = FixMyStreet::SMS->parse_username($possible_email); - $search = $possible_email if $parsed->{email}; - } - - $c->stash->{searched} = $search; - - my $search_n = 0; - $search_n = int($search) if $search =~ /^\d+$/; - - my $like_search = "%$search%"; - - my $parsed = FixMyStreet::SMS->parse_username($search); - my $valid_phone = $parsed->{phone}; - my $valid_email = $parsed->{email}; - - # when DBIC creates the join it does 'JOIN users user' in the - # SQL which makes PostgreSQL unhappy as user is a reserved - # word. So look up user ID for email separately. - my @user_ids = $c->model('DB::User')->search({ - email => { ilike => $like_search }, - }, { columns => [ 'id' ] } )->all; - @user_ids = map { $_->id } @user_ids; - - my @user_ids_phone = $c->model('DB::User')->search({ - phone => { ilike => $like_search }, - }, { columns => [ 'id' ] } )->all; - @user_ids_phone = map { $_->id } @user_ids_phone; - - if ($valid_email) { - $query->{'-or'} = [ - 'me.user_id' => { -in => \@user_ids }, - ]; - } elsif ($valid_phone) { - $query->{'-or'} = [ - 'me.user_id' => { -in => \@user_ids_phone }, - ]; - } elsif ($search =~ /^id:(\d+)$/) { - $query->{'-or'} = [ - 'me.id' => int($1), - ]; - } elsif ($search =~ /^area:(\d+)$/) { - $query->{'-or'} = [ - 'me.areas' => { like => "%,$1,%" } - ]; - } elsif ($search =~ /^ref:(\d+)$/) { - $query->{'-or'} = [ - 'me.external_id' => { like => "%$1%" } - ]; - } else { - $query->{'-or'} = [ - 'me.id' => $search_n, - 'me.user_id' => { -in => [ @user_ids, @user_ids_phone ] }, - 'me.external_id' => { ilike => $like_search }, - 'me.name' => { ilike => $like_search }, - 'me.title' => { ilike => $like_search }, - detail => { ilike => $like_search }, - bodies_str => { like => $like_search }, - cobrand_data => { like => $like_search }, - ]; - } - - my $problems = $c->cobrand->problems->search( - $query, - { - rows => 50, - order_by => [ \"(state='hidden')", \$order ] - } - )->page( $p_page ); - - $c->stash->{problems} = [ $problems->all ]; - $c->stash->{problems_pager} = $problems->pager; - - if ($valid_email) { - $query = [ - 'me.user_id' => { -in => \@user_ids }, - ]; - } elsif ($valid_phone) { - $query = [ - 'me.user_id' => { -in => \@user_ids_phone }, - ]; - } elsif ($search =~ /^id:(\d+)$/) { - $query = [ - 'me.id' => int($1), - 'me.problem_id' => int($1), - ]; - } elsif ($search =~ /^area:(\d+)$/) { - $query = []; - } else { - $query = [ - 'me.id' => $search_n, - 'problem.id' => $search_n, - 'me.user_id' => { -in => [ @user_ids, @user_ids_phone ] }, - 'me.name' => { ilike => $like_search }, - text => { ilike => $like_search }, - 'me.cobrand_data' => { ilike => $like_search }, - ]; - } - - if (@$query) { - my $updates = $c->cobrand->updates->search( - { - -or => $query, - }, - { - -select => [ 'me.*', qw/problem.bodies_str problem.state/ ], - prefetch => [qw/problem/], - rows => 50, - order_by => [ \"(me.state='hidden')", \"(problem.state='hidden')", { -desc => 'me.created' } ] - } - )->page( $u_page ); - $c->stash->{updates} = [ $updates->all ]; - $c->stash->{updates_pager} = $updates->pager; - } - - } else { - - my $problems = $c->cobrand->problems->search( - $query, - { order_by => $order, rows => 50 } - )->page( $p_page ); - $c->stash->{problems} = [ $problems->all ]; - $c->stash->{problems_pager} = $problems->pager; - } -} - sub update_user : Private { my ($self, $c, $object) = @_; my $parsed = FixMyStreet::SMS->parse_username($c->get_param('username')); @@ -395,471 +184,6 @@ sub update_user : Private { return 0; } -sub report_edit_display : Private { - my ( $self, $c ) = @_; - - my $problem = $c->stash->{problem}; - - $c->stash->{page} = 'admin'; - FixMyStreet::Map::display_map( - $c, - latitude => $problem->latitude, - longitude => $problem->longitude, - pins => $problem->used_map - ? [ { - latitude => $problem->latitude, - longitude => $problem->longitude, - colour => $c->cobrand->pin_colour($problem, 'admin'), - type => 'big', - draggable => 1, - } ] - : [], - print_report => 1, - ); -} - -sub report_edit : Path('report_edit') : Args(1) { - my ( $self, $c, $id ) = @_; - - my $problem = $c->cobrand->problems->search( { id => $id } )->first; - - $c->detach( '/page_error_404_not_found', [] ) - unless $problem; - - unless ( - $c->cobrand->moniker eq 'zurich' - || $c->user->has_permission_to(report_edit => $problem->bodies_str_ids) - ) { - $c->detach( '/page_error_403_access_denied', [] ); - } - - $c->stash->{problem} = $problem; - if ( $problem->extra ) { - my @fields; - if ( my $fields = $problem->get_extra_fields ) { - for my $field ( @{$fields} ) { - my $name = $field->{description} ? - "$field->{description} ($field->{name})" : - "$field->{name}"; - push @fields, { name => $name, val => $field->{value} }; - } - } - my $extra = $problem->get_extra_metadata; - if ( $extra->{duplicates} ) { - push @fields, { name => 'Duplicates', val => join( ',', @{ $problem->get_extra_metadata('duplicates') } ) }; - delete $extra->{duplicates}; - } - for my $key ( keys %$extra ) { - push @fields, { name => $key, val => $extra->{$key} }; - } - - $c->stash->{extra_fields} = \@fields; - } - - $c->forward('/auth/get_csrf_token'); - - $c->forward('categories_for_point'); - - $c->forward('alerts_for_report'); - - $c->forward('check_username_for_abuse', [ $problem->user ] ); - - $c->stash->{updates} = - [ $c->model('DB::Comment') - ->search( { problem_id => $problem->id }, { order_by => [ 'created', 'id' ] } ) - ->all ]; - - if (my $rotate_photo_param = $self->_get_rotate_photo_param($c)) { - $self->rotate_photo($c, $problem, @$rotate_photo_param); - $c->detach('report_edit_display'); - } - - if ( $c->cobrand->moniker eq 'zurich' ) { - my $done = $c->cobrand->admin_report_edit(); - $c->detach('report_edit_display') if $done; - } - - if ( $c->get_param('resend') ) { - $c->forward('/auth/check_csrf_token'); - - $problem->resend; - $problem->update(); - $c->stash->{status_message} = - '<p><em>' . _('That problem will now be resent.') . '</em></p>'; - - $c->forward( 'log_edit', [ $id, 'problem', 'resend' ] ); - } - elsif ( $c->get_param('mark_sent') ) { - $c->forward('/auth/check_csrf_token'); - $problem->update({ whensent => \'current_timestamp' })->discard_changes; - $c->stash->{status_message} = '<p><em>' . _('That problem has been marked as sent.') . '</em></p>'; - $c->forward( 'log_edit', [ $id, 'problem', 'marked sent' ] ); - } - elsif ( $c->get_param('flaguser') ) { - $c->forward('users/flag'); - $c->stash->{problem}->discard_changes; - } - elsif ( $c->get_param('removeuserflag') ) { - $c->forward('users/flag_remove'); - $c->stash->{problem}->discard_changes; - } - elsif ( $c->get_param('banuser') ) { - $c->forward('users/ban'); - } - elsif ( $c->get_param('submit') ) { - $c->forward('/auth/check_csrf_token'); - - my $old_state = $problem->state; - - my %columns = ( - flagged => $c->get_param('flagged') ? 1 : 0, - non_public => $c->get_param('non_public') ? 1 : 0, - ); - foreach (qw/state anonymous title detail name external_id external_body external_team/) { - $columns{$_} = $c->get_param($_); - } - $problem->set_inflated_columns(\%columns); - - if ($c->get_param('closed_updates')) { - $problem->set_extra_metadata(closed_updates => 1); - } else { - $problem->unset_extra_metadata('closed_updates'); - } - - $c->forward( '/admin/report_edit_category', [ $problem, $problem->state ne $old_state ] ); - $c->forward('update_user', [ $problem ]); - - # Deal with photos - my $remove_photo_param = $self->_get_remove_photo_param($c); - if ($remove_photo_param) { - $self->remove_photo($c, $problem, $remove_photo_param); - } - - if ($problem->state eq 'hidden') { - $problem->get_photoset->delete_cached; - } - - if ( $problem->is_visible() and $old_state eq 'unconfirmed' ) { - $problem->confirmed( \'current_timestamp' ); - } - - $problem->lastupdate( \'current_timestamp' ); - $problem->update; - - if ( $problem->state ne $old_state ) { - $c->forward( 'log_edit', [ $id, 'problem', 'state_change' ] ); - - my $name = $c->user->moderating_user_name; - my $extra = { is_superuser => 1 }; - if ($c->user->from_body) { - delete $extra->{is_superuser}; - $extra->{is_body_user} = $c->user->from_body->id; - } - my $timestamp = \'current_timestamp'; - $problem->add_to_comments( { - text => $c->stash->{update_text} || '', - created => $timestamp, - confirmed => $timestamp, - user_id => $c->user->id, - name => $name, - mark_fixed => 0, - anonymous => 0, - state => 'confirmed', - problem_state => $problem->state, - extra => $extra - } ); - } - $c->forward( 'log_edit', [ $id, 'problem', 'edit' ] ); - - $c->stash->{status_message} = - '<p><em>' . _('Updated!') . '</em></p>'; - - # do this here otherwise lastupdate and confirmed times - # do not display correctly - $problem->discard_changes; - } - - $c->detach('report_edit_display'); -} - -=head2 report_edit_category - -Handles changing a problem's category and the complexity that comes with it. -Returns 1 if category changed, 0 if no change. - -=cut - -sub report_edit_category : Private { - my ($self, $c, $problem, $no_comment) = @_; - - if ((my $category = $c->get_param('category')) ne $problem->category) { - my $category_old = $problem->category; - $problem->category($category); - my @contacts = grep { $_->category eq $problem->category } @{$c->stash->{contacts}}; - my @new_body_ids = map { $_->body_id } @contacts; - # If the report has changed bodies (and not to a subset!) we need to resend it - my %old_map = map { $_ => 1 } @{$problem->bodies_str_ids}; - if (grep !$old_map{$_}, @new_body_ids) { - $problem->resend; - } - # If the send methods of the old/new contacts differ we need to resend the report - my @new_send_methods = uniq map { - ( $_->body->can_be_devolved && $_->send_method ) ? - $_->send_method : $_->body->send_method - ? $_->body->send_method - : $c->cobrand->_fallback_body_sender()->{method}; - } @contacts; - my %old_send_methods = map { $_ => 1 } split /,/, ($problem->send_method_used || "Email"); - if (grep !$old_send_methods{$_}, @new_send_methods) { - $problem->resend; - } - - $problem->bodies_str(join( ',', @new_body_ids )); - my $update_text = '*' . sprintf(_('Category changed from ā%sā to ā%sā'), $category_old, $category) . '*'; - if ($no_comment) { - $c->stash->{update_text} = $update_text; - } else { - $problem->add_to_comments({ - text => $update_text, - created => \'current_timestamp', - confirmed => \'current_timestamp', - user_id => $c->user->id, - name => $c->user->from_body ? $c->user->from_body->name : $c->user->name, - state => 'confirmed', - mark_fixed => 0, - anonymous => 0, - }); - } - return 1; - } - return 0; -} - -=head2 report_edit_location - -Handles changing a problem's location and the complexity that comes with it. -For now, we reject the new location if the new location and old locations aren't -covered by the same body. - -Returns 2 if the new position (if any) is acceptable and changed, -1 if acceptable and unchanged, undef otherwise. - -NB: This must be called before report_edit_category, as that might modify -$problem->bodies_str. - -=cut - -sub report_edit_location : Private { - my ($self, $c, $problem) = @_; - - 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 ) { - # The two actions below change the stash, setting things up for e.g. a - # new report. But here we're only doing it in order to check the found - # bodies match; we don't want to overwrite the existing report data if - # this lookup is bad. So let's save the stash and restore it after the - # comparison. - my $safe_stash = { %{$c->stash} }; - $c->stash->{fetch_all_areas} = 1; - $c->stash->{area_check_action} = 'admin'; - $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 = keys %{$c->stash->{bodies_to_list}}; - my $bodies_match = grep { exists( $allowed_bodies{$_} ) } @new_bodies; - $c->stash($safe_stash); - return unless $bodies_match; - $problem->latitude($c->stash->{latitude}); - $problem->longitude($c->stash->{longitude}); - my $areas = $c->stash->{all_areas_mapit}; - $problem->areas( ',' . join( ',', sort keys %$areas ) . ',' ); - return 2; - } - return 1; -} - -sub categories_for_point : Private { - my ($self, $c) = @_; - - $c->stash->{report} = $c->stash->{problem}; - # We have a report, stash its location - $c->forward('/report/new/determine_location_from_report'); - # Look up the areas for this location - 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'); - - # Remove the "Pick a category" option - shift @{$c->stash->{category_options}} if @{$c->stash->{category_options}}; - - $c->stash->{categories_hash} = { map { $_->category => 1 } @{$c->stash->{category_options}} }; -} - -sub alerts_for_report : Private { - my ($self, $c) = @_; - - $c->stash->{alert_count} = $c->model('DB::Alert')->search({ - alert_type => 'new_updates', - parameter => $c->stash->{report}->id, - confirmed => 1, - whendisabled => undef, - })->count(); -} - -sub templates : Path('templates') : Args(0) { - my ( $self, $c ) = @_; - - my $user = $c->user; - - if ($user->is_superuser) { - $c->forward('fetch_all_bodies'); - $c->stash->{template} = 'admin/templates_index.html'; - } elsif ( $user->from_body ) { - $c->forward('load_template_body', [ $user->from_body->id ]); - $c->res->redirect( $c->uri_for( 'templates', $c->stash->{body}->id ) ); - } else { - $c->detach( '/page_error_404_not_found', [] ); - } -} - -sub templates_view : Path('templates') : Args(1) { - my ($self, $c, $body_id) = @_; - - $c->forward('load_template_body', [ $body_id ]); - - my @templates = $c->stash->{body}->response_templates->search( - undef, - { - order_by => 'title' - } - ); - - $c->stash->{response_templates} = \@templates; - - $c->stash->{template} = 'admin/templates.html'; -} - -sub template_edit : Path('templates') : Args(2) { - my ( $self, $c, $body_id, $template_id ) = @_; - - $c->forward('load_template_body', [ $body_id ]); - - my $template; - if ($template_id eq 'new') { - $template = $c->stash->{body}->response_templates->new({}); - } - else { - $template = $c->stash->{body}->response_templates->find( $template_id ) - or $c->detach( '/page_error_404_not_found', [] ); - } - - $c->forward('fetch_contacts'); - my @contacts = $template->contacts->all; - my @live_contacts = $c->stash->{live_contacts}->all; - my %active_contacts = map { $_->id => 1 } @contacts; - my @all_contacts = map { { - id => $_->id, - category => $_->category_display, - active => $active_contacts{$_->id}, - email => $_->email, - } } @live_contacts; - $c->stash->{contacts} = \@all_contacts; - - # bare block to use 'last' if form is invalid. - if ($c->req->method eq 'POST') { { - if ($c->get_param('delete_template') && $c->get_param('delete_template') eq _("Delete template")) { - $template->contact_response_templates->delete_all; - $template->delete; - } else { - my @live_contact_ids = map { $_->id } @live_contacts; - my @new_contact_ids = grep { $c->get_param("contacts[$_]") } @live_contact_ids; - my %new_contacts = map { $_ => 1 } @new_contact_ids; - for my $contact (@all_contacts) { - $contact->{active} = $new_contacts{$contact->{id}}; - } - - $template->title( $c->get_param('title') ); - $template->text( $c->get_param('text') ); - $template->state( $c->get_param('state') ); - $template->external_status_code( $c->get_param('external_status_code') ); - - if ( $template->state && $template->external_status_code ) { - $c->stash->{errors} ||= {}; - $c->stash->{errors}->{state} = _("State and external status code cannot be used simultaneously."); - $c->stash->{errors}->{external_status_code} = _("State and external status code cannot be used simultaneously."); - } - - $template->auto_response( $c->get_param('auto_response') && ( $template->state || $template->external_status_code ) ? 1 : 0 ); - if ($template->auto_response) { - my @check_contact_ids = @new_contact_ids; - # If the new template has not specific categories (i.e. it - # applies to all categories) then we need to check each of those - # category ids for existing auto-response templates. - if (!scalar @check_contact_ids) { - @check_contact_ids = @live_contact_ids; - } - my $query = { - 'auto_response' => 1, - 'contact.id' => [ @check_contact_ids, undef ], - -or => { - $template->state ? ('me.state' => $template->state) : (), - $template->external_status_code ? ('me.external_status_code' => $template->external_status_code) : (), - }, - }; - if ($template->in_storage) { - $query->{'me.id'} = { '!=', $template->id }; - } - if ($c->stash->{body}->response_templates->search($query, { - join => { 'contact_response_templates' => 'contact' }, - })->count) { - $c->stash->{errors} ||= {}; - $c->stash->{errors}->{auto_response} = _("There is already an auto-response template for this category/state."); - } - } - - last if $c->stash->{errors}; - - $template->update_or_insert; - $template->contact_response_templates->search({ - contact_id => { '!=' => \@new_contact_ids }, - })->delete; - foreach my $contact_id (@new_contact_ids) { - $template->contact_response_templates->find_or_create({ - contact_id => $contact_id, - }); - } - } - - $c->res->redirect( $c->uri_for( 'templates', $c->stash->{body}->id ) ); - } } - - $c->stash->{response_template} = $template; - - $c->stash->{template} = 'admin/template_edit.html'; -} - -sub load_template_body : Private { - my ($self, $c, $body_id) = @_; - - my $zurich_user = $c->user->from_body && $c->cobrand->moniker eq 'zurich'; - my $has_permission = $c->user->has_body_permission_to('template_edit', $body_id); - - unless ( $zurich_user || $has_permission ) { - $c->detach( '/page_error_404_not_found', [] ); - } - - # Regular users can only view their own body's templates - if ( !$c->user->is_superuser && $body_id ne $c->user->from_body->id ) { - $c->res->redirect( $c->uri_for( 'templates', $c->user->from_body->id ) ); - } - - $c->stash->{body} = $c->model('DB::Body')->find($body_id) - or $c->detach( '/page_error_404_not_found', [] ); -} - sub update_edit : Path('update_edit') : Args(1) { my ( $self, $c, $id ) = @_; @@ -872,8 +196,8 @@ sub update_edit : Path('update_edit') : Args(1) { $c->stash->{update} = $update; - if (my $rotate_photo_param = $self->_get_rotate_photo_param($c)) { - $self->rotate_photo($c, $update, @$rotate_photo_param); + if (my $rotate_photo_param = $c->forward('_get_rotate_photo_param')) { + $c->forward('rotate_photo', [ $update, @$rotate_photo_param ]); return 1; } @@ -907,18 +231,17 @@ sub update_edit : Path('update_edit') : Args(1) { $edited = 1; } - my $remove_photo_param = $self->_get_remove_photo_param($c); + my $remove_photo_param = $c->forward('_get_remove_photo_param'); if ($remove_photo_param) { - $self->remove_photo($c, $update, $remove_photo_param); + $c->forward('remove_photo', [$update, $remove_photo_param]); } - $c->stash->{status_message} = '<p><em>' . _('Updated!') . '</em></p>'; + $c->stash->{status_message} = _('Updated!'); # Must call update->hide while it's not hidden (so is_latest works) if ($new_state eq 'hidden') { my $outcome = $update->hide; - $c->stash->{status_message} .= - '<p><em>' . _('Problem marked as open.') . '</em></p>' + $c->stash->{status_message} .= _('Problem marked as open.') if $outcome->{reopened}; } @@ -1014,8 +337,8 @@ sub set_allowed_pages : Private { sub get_user : Private { my ( $self, $c ) = @_; - my $user = $c->req->remote_user(); - $user ||= ($c->user && $c->user->name); + my $user = ($c->user && $c->user->name); + $user ||= $c->req->remote_user(); $user ||= ''; return $user; @@ -1075,7 +398,7 @@ Rotate a photo 90 degrees left or right =cut # returns index of photo to rotate, if any -sub _get_rotate_photo_param { +sub _get_rotate_photo_param : Private { my ($self, $c) = @_; my $key = first { /^rotate_photo/ } keys %{ $c->req->params } or return; my ($index) = $key =~ /(\d+)$/; @@ -1105,7 +428,7 @@ Remove a photo from a report =cut # Returns index of photo(s) to remove, if any -sub _get_remove_photo_param { +sub _get_remove_photo_param : Private { my ($self, $c) = @_; return 'ALL' if $c->get_param('remove_photo'); @@ -1117,8 +440,8 @@ sub _get_remove_photo_param { sub remove_photo : Private { my ($self, $c, $object, $keys) = @_; if ($keys eq 'ALL') { - $object->photo(undef); $object->get_photoset->delete_cached; + $object->photo(undef); } else { my $fileids = $object->get_photoset->remove_images($keys); $object->photo($fileids); @@ -1194,31 +517,46 @@ sub update_extra_fields : Private { my $meta = {}; $meta->{code} = $c->get_param("metadata[$i].code"); next unless $meta->{code}; + $meta->{order} = int $c->get_param("metadata[$i].order"); - $meta->{datatype} = $c->get_param("metadata[$i].datatype"); - my $required = $c->get_param("metadata[$i].required") && $c->get_param("metadata[$i].required") eq 'on'; - $meta->{required} = $required ? 'true' : 'false'; - my $notice = $c->get_param("metadata[$i].notice") && $c->get_param("metadata[$i].notice") eq 'on'; - $meta->{variable} = $notice ? 'false' : 'true'; - $meta->{description} = $c->get_param("metadata[$i].description"); - $meta->{datatype_description} = $c->get_param("metadata[$i].datatype_description"); - $meta->{automated} = $c->get_param("metadata[$i].automated") - if $c->get_param("metadata[$i].automated"); - - if ( $meta->{datatype} eq "singlevaluelist" ) { - $meta->{values} = []; - my $re = qr{^metadata\[$i\]\.values\[\d+\]\.key}; - my @vindices = grep { /$re/ } keys %{ $c->req->params }; - @vindices = sort map { /values\[(\d+)\]/ } @vindices; - foreach my $j (@vindices) { - my $name = $c->get_param("metadata[$i].values[$j].name"); - my $key = $c->get_param("metadata[$i].values[$j].key"); - push(@{$meta->{values}}, { - name => $name, - key => $key, - }) if $name; + $meta->{protected} = $c->get_param("metadata[$i].protected") ? 'true' : 'false'; + + my $behaviour = $c->get_param("metadata[$i].behaviour") || 'question'; + if ($behaviour eq 'question') { + $meta->{required} = $c->get_param("metadata[$i].required") ? 'true' : 'false'; + $meta->{variable} = 'true'; + my $desc = $c->get_param("metadata[$i].description"); + $meta->{description} = FixMyStreet::Template::sanitize($desc); + $meta->{datatype} = $c->get_param("metadata[$i].datatype"); + + if ( $meta->{datatype} eq "singlevaluelist" ) { + $meta->{values} = []; + my $re = qr{^metadata\[$i\]\.values\[\d+\]\.key}; + my @vindices = grep { /$re/ } keys %{ $c->req->params }; + @vindices = sort map { /values\[(\d+)\]/ } @vindices; + foreach my $j (@vindices) { + my $name = $c->get_param("metadata[$i].values[$j].name"); + my $key = $c->get_param("metadata[$i].values[$j].key"); + my $disable = $c->get_param("metadata[$i].values[$j].disable"); + my $disable_message = $c->get_param("metadata[$i].values[$j].disable_message"); + push(@{$meta->{values}}, { + name => $name, + key => $key, + $disable ? (disable => 1, disable_message => $disable_message) : (), + }) if $name; + } } + } elsif ($behaviour eq 'notice') { + $meta->{variable} = 'false'; + my $desc = $c->get_param("metadata[$i].description"); + $meta->{description} = FixMyStreet::Template::sanitize($desc); + $meta->{disable_form} = $c->get_param("metadata[$i].disable_form") ? 'true' : 'false'; + } elsif ($behaviour eq 'hidden') { + $meta->{automated} = 'hidden_field'; + } elsif ($behaviour eq 'server') { + $meta->{automated} = 'server_set'; } + push @extra_fields, $meta; } @extra_fields = sort { $a->{order} <=> $b->{order} } @extra_fields; |