aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App/Controller/Admin.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/App/Controller/Admin.pm')
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm243
1 files changed, 176 insertions, 67 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index 85b6204fc..7d04f5ff9 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -12,9 +12,11 @@ use DateTime::Format::Strptime;
use List::Util 'first';
use List::MoreUtils 'uniq';
use mySociety::ArrayUtils;
+use Text::CSV;
use FixMyStreet::SendReport;
use FixMyStreet::SMS;
+use Utils;
=head1 NAME
@@ -216,9 +218,14 @@ sub bodies : Path('bodies') : Args(0) {
$c->forward('check_for_super_user');
$c->forward('/auth/check_csrf_token');
- my $params = $c->forward('body_params');
+ my $values = $c->forward('body_params');
unless ( keys %{$c->stash->{body_errors}} ) {
- my $body = $c->model('DB::Body')->create( $params );
+ my $body = $c->model('DB::Body')->create( $values->{params} );
+ if ($values->{extras}) {
+ $body->set_extra_metadata( $_ => $values->{extras}->{$_} )
+ for keys %{$values->{extras}};
+ $body->update;
+ }
my @area_ids = $c->get_param_list('area_ids');
foreach (@area_ids) {
$c->model('DB::BodyArea')->create( { body => $body, area_id => $_ } );
@@ -264,9 +271,15 @@ sub body_form_dropdowns : Private {
} else {
$areas = mySociety::MaPit::call('areas', $c->cobrand->area_types);
}
+
+ # Some cobrands may want to add extra areas at runtime beyond those
+ # available via MAPIT_WHITELIST or MAPIT_TYPES. This can be used for,
+ # e.g., parish councils on a particular council cobrand.
+ $areas = $c->cobrand->call_hook("add_extra_areas" => $areas) || $areas;
+
$c->stash->{areas} = [ sort { strcoll($a->{name}, $b->{name}) } values %$areas ];
- my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } keys %{ FixMyStreet::SendReport->get_senders };
+ my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } sort keys %{ FixMyStreet::SendReport->get_senders };
$c->stash->{send_methods} = \@methods;
}
@@ -340,6 +353,7 @@ sub update_contacts : Private {
}
$c->forward('update_extra_fields', [ $contact ]);
+ $c->forward('contact_cobrand_extra_fields', [ $contact ]);
if ( %errors ) {
$c->stash->{updated} = _('Please correct the errors below');
@@ -387,9 +401,14 @@ sub update_contacts : Private {
$c->forward('check_for_super_user');
$c->forward('/auth/check_csrf_token');
- my $params = $c->forward( 'body_params' );
+ my $values = $c->forward( 'body_params' );
unless ( keys %{$c->stash->{body_errors}} ) {
- $c->stash->{body}->update( $params );
+ $c->stash->{body}->update( $values->{params} );
+ if ($values->{extras}) {
+ $c->stash->{body}->set_extra_metadata( $_ => $values->{extras}->{$_} )
+ for keys %{$values->{extras}};
+ $c->stash->{body}->update;
+ }
my @current = $c->stash->{body}->body_areas->all;
my %current = map { $_->area_id => 1 } @current;
my @area_ids = $c->get_param_list('area_ids');
@@ -444,6 +463,9 @@ sub body_params : Private {
my %defaults = map { $_ => '' } @fields;
%defaults = ( %defaults,
send_comments => 0,
+ fetch_problems => 0,
+ convert_latlong => 0,
+ blank_updates_permitted => 0,
suppress_alerts => 0,
comment_user_id => undef,
send_extended_statuses => 0,
@@ -453,7 +475,10 @@ sub body_params : Private {
);
my %params = map { $_ => $c->get_param($_) || $defaults{$_} } keys %defaults;
$c->forward('check_body_params', [ \%params ]);
- return \%params;
+ my @extras = qw/fetch_all_problems/;
+ %defaults = map { $_ => '' } @extras;
+ my %extras = map { $_ => $c->get_param($_) || $defaults{$_} } @extras;
+ return { params => \%params, extras => \%extras };
}
sub check_body_params : Private {
@@ -610,7 +635,7 @@ sub category : Chained('body') : PathPart('') {
},
);
$c->stash->{history} = $history;
- my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } keys %{ FixMyStreet::SendReport->get_senders };
+ my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } sort keys %{ FixMyStreet::SendReport->get_senders };
$c->stash->{send_methods} = \@methods;
return 1;
@@ -619,6 +644,9 @@ sub category : Chained('body') : PathPart('') {
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};
@@ -641,6 +669,8 @@ sub reports : Path('reports') {
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')) {
$c->stash->{searched} = $search;
@@ -761,10 +791,6 @@ sub reports : Path('reports') {
$c->stash->{problems} = [ $problems->all ];
$c->stash->{problems_pager} = $problems->pager;
}
-
- $c->stash->{edit_body_contacts} = 1
- if ( grep {$_ eq 'body'} keys %{$c->stash->{allowed_pages}});
-
}
sub update_user : Private {
@@ -780,24 +806,10 @@ sub update_user : Private {
return 0;
}
-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;
+sub report_edit_display : Private {
+ my ( $self, $c ) = @_;
- $c->forward('/auth/get_csrf_token');
+ my $problem = $c->stash->{problem};
$c->stash->{page} = 'admin';
FixMyStreet::Map::display_map(
@@ -814,27 +826,50 @@ sub report_edit : Path('report_edit') : Args(1) {
: [],
print_report => 1,
);
+}
- if (my $rotate_photo_param = $self->_get_rotate_photo_param($c)) {
- $self->rotate_photo($c, $problem, @$rotate_photo_param);
- if ( $c->cobrand->moniker eq 'zurich' ) {
- # Clicking the photo rotation buttons should do nothing
- # except for rotating the photo, so return the user
- # to the report screen now.
- $c->res->redirect( $c->uri_for( 'report_edit', $problem->id ) );
- return;
- } else {
- return 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->forward('categories_for_point');
+ $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} };
+ }
- if ( $c->cobrand->moniker eq 'zurich' ) {
- my $done = $c->cobrand->admin_report_edit();
- return if $done;
+ $c->stash->{extra_fields} = \@fields;
}
+ $c->forward('/auth/get_csrf_token');
+
+ $c->forward('categories_for_point');
+
$c->forward('check_username_for_abuse', [ $problem->user ] );
$c->stash->{updates} =
@@ -842,6 +877,16 @@ sub report_edit : Path('report_edit') : Args(1) {
->search( { problem_id => $problem->id }, { order_by => 'created' } )
->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');
@@ -883,6 +928,12 @@ sub report_edit : Path('report_edit') : Args(1) {
}
$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 ]);
@@ -937,7 +988,7 @@ sub report_edit : Path('report_edit') : Args(1) {
$problem->discard_changes;
}
- return 1;
+ $c->detach('report_edit_display');
}
=head2 report_edit_category
@@ -1010,11 +1061,18 @@ sub report_edit_location : Private {
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->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}};
+ 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});
@@ -1037,8 +1095,7 @@ sub categories_for_point : Private {
# Remove the "Pick a category" option
shift @{$c->stash->{category_options}} if @{$c->stash->{category_options}};
- $c->stash->{category_options_copy} = $c->stash->{category_options};
- $c->stash->{categories_hash} = { map { $_->{name} => 1 } @{$c->stash->{category_options}} };
+ $c->stash->{categories_hash} = { map { $_->category => 1 } @{$c->stash->{category_options}} };
}
sub templates : Path('templates') : Args(0) {
@@ -1096,6 +1153,7 @@ sub template_edit : Path('templates') : Args(2) {
id => $_->id,
category => $_->category_display,
active => $active_contacts{$_->id},
+ email => $_->email,
} } @live_contacts;
$c->stash->{contacts} = \@all_contacts;
@@ -1115,8 +1173,15 @@ sub template_edit : Path('templates') : Args(2) {
$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 ? 1 : 0 );
+ $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
@@ -1128,7 +1193,10 @@ sub template_edit : Path('templates') : Args(2) {
my $query = {
'auto_response' => 1,
'contact.id' => [ @check_contact_ids, undef ],
- 'me.state' => $template->state,
+ -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 };
@@ -1136,9 +1204,8 @@ sub template_edit : Path('templates') : Args(2) {
if ($c->stash->{body}->response_templates->search($query, {
join => { 'contact_response_templates' => 'contact' },
})->count) {
- $c->stash->{errors} = {
- auto_response => _("There is already an auto-response template for this category/state.")
- };
+ $c->stash->{errors} ||= {};
+ $c->stash->{errors}->{auto_response} = _("There is already an auto-response template for this category/state.");
}
}
@@ -1225,7 +1292,7 @@ sub users: Path('users') : Args(0) {
sub update_edit : Path('update_edit') : Args(1) {
my ( $self, $c, $id ) = @_;
- my $update = $c->cobrand->updates->search({ id => $id })->first;
+ my $update = $c->cobrand->updates->search({ 'me.id' => $id })->first;
$c->detach( '/page_error_404_not_found', [] )
unless $update;
@@ -1612,6 +1679,61 @@ sub user_edit : Path('user_edit') : Args(1) {
return 1;
}
+sub user_import : Path('user_import') {
+ my ( $self, $c, $id ) = @_;
+
+ $c->forward('/auth/get_csrf_token');
+ return unless $c->user_exists && $c->user->is_superuser;
+
+ if ($c->req->method eq 'POST') {
+ $c->forward('/auth/check_csrf_token');
+ $c->stash->{new_users} = [];
+ $c->stash->{existing_users} = [];
+
+ my @all_permissions = map { keys %$_ } values %{ $c->cobrand->available_permissions };
+ my %available_permissions = map { $_ => 1 } @all_permissions;
+
+ my $csv = Text::CSV->new({ binary => 1});
+ my $fh = $c->req->upload('csvfile')->fh;
+ $csv->getline($fh); # discard the header
+ while (my $row = $csv->getline($fh)) {
+ my ($name, $email, $from_body, $permissions) = @$row;
+ $email = lc Utils::trim_text($email);
+ my @permissions = split(/:/, $permissions);
+
+ my $user = FixMyStreet::DB->resultset("User")->find_or_new({ email => $email, email_verified => 1 });
+ if ($user->in_storage) {
+ push @{$c->stash->{existing_users}}, $user;
+ next;
+ }
+
+ $user->name($name);
+ $user->from_body($from_body || undef);
+ $user->update_or_insert;
+
+ my @user_permissions = grep { $available_permissions{$_} } @permissions;
+ foreach my $permission_type (@user_permissions) {
+ $user->user_body_permissions->find_or_create({
+ body_id => $user->from_body->id,
+ permission_type => $permission_type,
+ });
+ }
+
+ push @{$c->stash->{new_users}}, $user;
+ }
+
+ }
+}
+
+sub contact_cobrand_extra_fields : Private {
+ my ( $self, $c, $contact ) = @_;
+
+ my $extra_fields = $c->cobrand->call_hook('contact_extra_fields');
+ foreach ( @$extra_fields ) {
+ $contact->set_extra_metadata( $_ => $c->get_param("extra[$_]") );
+ }
+}
+
sub user_cobrand_extra_fields : Private {
my ( $self, $c ) = @_;
@@ -1795,20 +1917,7 @@ sub user_hide_everywhere : Private {
sub user_remove_account : Private {
my ( $self, $c, $user ) = @_;
$c->forward('user_logout_everywhere', [ $user ]);
- $user->problems->update({ anonymous => 1, name => '', send_questionnaire => 0 });
- $user->comments->update({ anonymous => 1, name => '' });
- $user->alerts->update({ whendisabled => \'current_timestamp' });
- $user->password('', 1);
- $user->update({
- email => 'removed-' . $user->id . '@' . FixMyStreet->config('EMAIL_DOMAIN'),
- email_verified => 0,
- name => '',
- phone => '',
- phone_verified => 0,
- title => undef,
- twitter_id => undef,
- facebook_id => undef,
- });
+ $user->anonymize_account;
$c->stash->{status_message} = _('That user’s personal details have been removed.');
}