aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App/Controller/Admin.pm
diff options
context:
space:
mode:
authorMarius Halden <marius.h@lden.org>2020-09-29 14:23:52 +0200
committerMarius Halden <marius.h@lden.org>2020-09-29 14:23:52 +0200
commita27ce1524d801d2742a2bdb6ec1da45126d64353 (patch)
tree64123c4e17dc1776aa0a7cd65ee01d49d3e7d978 /perllib/FixMyStreet/App/Controller/Admin.pm
parent377bd96aab7cad3434185c30eb908c9da447fe40 (diff)
parent2773c60226b9370fe8ee00f7b205b571bb87c3b5 (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.pm832
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;