aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App/Controller
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/App/Controller')
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm243
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/ExorDefects.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/Stats.pm27
-rw-r--r--perllib/FixMyStreet/App/Controller/Alert.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Around.pm32
-rw-r--r--perllib/FixMyStreet/App/Controller/Contact.pm18
-rw-r--r--perllib/FixMyStreet/App/Controller/Council.pm8
-rw-r--r--perllib/FixMyStreet/App/Controller/Dashboard.pm58
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Develop.pm136
-rwxr-xr-xperllib/FixMyStreet/App/Controller/FakeMapit.pm16
-rw-r--r--perllib/FixMyStreet/App/Controller/My.pm3
-rw-r--r--perllib/FixMyStreet/App/Controller/Open311.pm11
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Questionnaire.pm65
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm88
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm77
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/Update.pm17
-rw-r--r--perllib/FixMyStreet/App/Controller/Reports.pm55
17 files changed, 645 insertions, 213 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.');
}
diff --git a/perllib/FixMyStreet/App/Controller/Admin/ExorDefects.pm b/perllib/FixMyStreet/App/Controller/Admin/ExorDefects.pm
index bdeecc1a3..d965dd8f2 100644
--- a/perllib/FixMyStreet/App/Controller/Admin/ExorDefects.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin/ExorDefects.pm
@@ -43,7 +43,7 @@ sub download : Path('download') : Args(0) {
$c->detach( '/page_error_404_not_found', [] );
}
- my $parser = DateTime::Format::Strptime->new( pattern => '%d/%m/%Y' );
+ my $parser = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d' );
my $start_date = $parser-> parse_datetime ( $c->get_param('start_date') );
my $end_date = $parser-> parse_datetime ( $c->get_param('end_date') ) ;
my $one_day = DateTime::Duration->new( days => 1 );
diff --git a/perllib/FixMyStreet/App/Controller/Admin/Stats.pm b/perllib/FixMyStreet/App/Controller/Admin/Stats.pm
index 2860b3531..5f82094d6 100644
--- a/perllib/FixMyStreet/App/Controller/Admin/Stats.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin/Stats.pm
@@ -72,4 +72,31 @@ sub questionnaire : Local : Args(0) {
return 1;
}
+sub refused : Local : Args(0) {
+ my ($self, $c) = @_;
+
+ my $contacts = $c->model('DB::Contact')->not_deleted->search([
+ { email => 'REFUSED' },
+ { 'body.can_be_devolved' => 1, 'me.send_method' => 'Refused' },
+ ], { prefetch => 'body' });
+ my %bodies;
+ while (my $contact = $contacts->next) {
+ my $body = $contact->body;
+ $bodies{$body->id}{body} = $body unless $bodies{$body->id}{body};
+ push @{$bodies{$body->id}{contacts}}, $contact;
+ }
+
+ my $bodies = $c->model('DB::Body')->search({ send_method => 'Refused' });
+ while (my $body = $bodies->next) {
+ $bodies{$body->id}{body} = $body;
+ $bodies{$body->id}{all} = 1;
+ }
+
+ my @bodies;
+ foreach (sort { $bodies{$a}{body}->name cmp $bodies{$b}{body}->name } keys %bodies) {
+ push @bodies, $bodies{$_};
+ }
+ $c->stash->{bodies} = \@bodies;
+}
+
1;
diff --git a/perllib/FixMyStreet/App/Controller/Alert.pm b/perllib/FixMyStreet/App/Controller/Alert.pm
index 9d522dbc9..1060c080b 100644
--- a/perllib/FixMyStreet/App/Controller/Alert.pm
+++ b/perllib/FixMyStreet/App/Controller/Alert.pm
@@ -369,7 +369,7 @@ sub process_user : Private {
# return 1;
# }
#
-# $alert_user->password( Utils::trim_text( $params{password_register} ) );
+# $alert_user->password( $params{password_register} );
}
=head2 setup_coordinate_rss_feeds
diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm
index ae7d83f55..8fed5c3aa 100644
--- a/perllib/FixMyStreet/App/Controller/Around.pm
+++ b/perllib/FixMyStreet/App/Controller/Around.pm
@@ -228,19 +228,18 @@ sub check_and_stash_category : Private {
my @bodies = $c->model('DB::Body')->active->for_areas(keys %$all_areas)->all;
my %bodies = map { $_->id => $_ } @bodies;
- my @contacts = $c->model('DB::Contact')->not_deleted->search(
+ my @categories = $c->model('DB::Contact')->not_deleted->search(
{
body_id => [ keys %bodies ],
},
{
- columns => [ 'category' ],
+ columns => [ 'category', 'extra' ],
order_by => [ 'category' ],
distinct => 1
}
)->all;
- my @categories = map { { name => $_->category, value => $_->category_display } } @contacts;
$c->stash->{filter_categories} = \@categories;
- my %categories_mapped = map { $_->{name} => 1 } @categories;
+ my %categories_mapped = map { $_->category => 1 } @categories;
my $categories = [ $c->get_param_list('filter_category', 1) ];
my %valid_categories = map { $_ => 1 } grep { $_ && $categories_mapped{$_} } @$categories;
@@ -257,12 +256,16 @@ sub map_features : Private {
return if $c->get_param('js'); # JS will request the same (or more) data client side
+ # Allow the cobrand to add in any additional query parameters
+ my $extra_params = $c->cobrand->call_hook('display_location_extra_params');
+
my ( $on_map, $nearby, $distance ) =
FixMyStreet::Map::map_features(
$c, %$extra,
categories => [ keys %{$c->stash->{filter_category}} ],
states => $c->stash->{filter_problem_states},
order => $c->stash->{sort_order},
+ extra => $extra_params,
);
my @pins;
@@ -305,6 +308,27 @@ sub ajax : Path('/ajax') {
$c->forward('/reports/ajax', [ 'around/on_map_list_items.html' ]);
}
+sub location_closest_address : Path('/ajax/closest') {
+ my ( $self, $c ) = @_;
+ $c->res->content_type('application/json; charset=utf-8');
+
+ my $lat = $c->get_param('lat');
+ my $lon = $c->get_param('lon');
+ unless ($lat && $lon) {
+ $c->res->status(404);
+ $c->res->body('');
+ return;
+ }
+
+ my $closest = $c->cobrand->find_closest({ latitude => $lat, longitude => $lon });
+ my $data = {
+ road => $closest->{address}{addressLine},
+ full_address => $closest->{name},
+ };
+
+ $c->res->body(encode_json($data));
+}
+
sub location_autocomplete : Path('/ajax/geocode') {
my ( $self, $c ) = @_;
$c->res->content_type('application/json; charset=utf-8');
diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm
index f2c3be47c..997009b87 100644
--- a/perllib/FixMyStreet/App/Controller/Contact.pm
+++ b/perllib/FixMyStreet/App/Controller/Contact.pm
@@ -87,12 +87,24 @@ sub determine_contact_type : Private {
} elsif ($id) {
$c->forward( '/report/load_problem_or_display_error', [ $id ] );
if ($update_id) {
- my $update = $c->model('DB::Comment')->find(
- { id => $update_id }
- );
+ my $update = $c->model('DB::Comment')->search(
+ {
+ id => $update_id,
+ problem_id => $id,
+ state => 'confirmed',
+ }
+ )->first;
+
+ unless ($update) {
+ $c->detach( '/page_error_404_not_found', [ _('Unknown update ID') ] );
+ }
$c->stash->{update} = $update;
}
+
+ if ( $c->get_param("reject") && $c->user->has_permission_to(report_reject => $c->stash->{problem}->bodies_str_ids) ) {
+ $c->stash->{rejecting_report} = 1;
+ }
}
return 1;
diff --git a/perllib/FixMyStreet/App/Controller/Council.pm b/perllib/FixMyStreet/App/Controller/Council.pm
index 0e7553dc4..2e2dce0f7 100644
--- a/perllib/FixMyStreet/App/Controller/Council.pm
+++ b/perllib/FixMyStreet/App/Controller/Council.pm
@@ -49,6 +49,14 @@ sub load_and_check_areas : Private {
$area_types = $c->cobrand->area_types;
}
+ # Cobrand may wish to add area types to look up for a point at runtime.
+ # This can be used for, e.g., parish councils on a particular council
+ # cobrand. NB three-tier councils break the alerts pages, so don't run the
+ # hook if we're on an alerts page.
+ unless ($c->stash->{area_check_action} eq 'alert') {
+ $area_types = $c->cobrand->call_hook("add_extra_area_types" => $area_types) || $area_types;
+ }
+
my $all_areas;
my %params;
diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm
index 032c36e05..ffd8d76c1 100644
--- a/perllib/FixMyStreet/App/Controller/Dashboard.pm
+++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm
@@ -103,7 +103,10 @@ sub index : Path : Args(0) {
$c->stash->{bodies} = \@bodies;
}
- $c->stash->{start_date} = $c->get_param('start_date');
+ my $days30 = DateTime->now(time_zone => FixMyStreet->time_zone || FixMyStreet->local_time_zone)->subtract(days => 30);
+ $days30->truncate( to => 'day' );
+
+ $c->stash->{start_date} = $c->get_param('start_date') || $days30->strftime('%Y-%m-%d');
$c->stash->{end_date} = $c->get_param('end_date');
$c->stash->{q_state} = $c->get_param('state') || '';
@@ -136,30 +139,14 @@ sub construct_rs_filter : Private {
}
my $dtf = $c->model('DB')->storage->datetime_parser;
- my $date = DateTime->now( time_zone => FixMyStreet->local_time_zone )->subtract(days => 30);
- $date->truncate( to => 'day' );
-
- $where{'me.confirmed'} = { '>=', $dtf->format_datetime($date) };
-
- my $start_date = $c->stash->{start_date};
- my $end_date = $c->stash->{end_date};
- if ($start_date or $end_date) {
- my @parts;
- if ($start_date) {
- my $date = $dtf->parse_datetime($start_date);
- push @parts, { '>=', $dtf->format_datetime( $date ) };
- }
- if ($end_date) {
- my $one_day = DateTime::Duration->new( days => 1 );
- my $date = $dtf->parse_datetime($end_date);
- push @parts, { '<', $dtf->format_datetime( $date + $one_day ) };
- }
- if (scalar @parts == 2) {
- $where{'me.confirmed'} = [ -and => $parts[0], $parts[1] ];
- } else {
- $where{'me.confirmed'} = $parts[0];
- }
+ my $start_date = $dtf->parse_datetime($c->stash->{start_date});
+ $where{'me.confirmed'} = { '>=', $dtf->format_datetime($start_date) };
+
+ if (my $end_date = $c->stash->{end_date}) {
+ my $one_day = DateTime::Duration->new( days => 1 );
+ $end_date = $dtf->parse_datetime($end_date) + $one_day;
+ $where{'me.confirmed'} = [ -and => $where{'me.confirmed'}, { '<', $dtf->format_datetime($end_date) } ];
}
$c->stash->{params} = \%where;
@@ -291,7 +278,7 @@ sub export_as_csv : Private {
my $csv = $c->stash->{csv} = {
problems => $c->stash->{problems_rs}->search_rs({}, {
prefetch => 'comments',
- order_by => 'me.confirmed'
+ order_by => ['me.confirmed', 'me.id'],
}),
headers => [
'Report ID',
@@ -346,6 +333,7 @@ sub export_as_csv : Private {
} sort keys %where
},
};
+ $c->cobrand->call_hook("dashboard_export_add_columns");
$c->forward('generate_csv');
}
@@ -358,6 +346,8 @@ Generates a CSV output, given a 'csv' stash hashref containing:
* columns: an arrayref of the columns (looked up in the row's as_hashref, plus
the following: user_name_display, acknowledged, fixed, closed, wards,
local_coords_x, local_coords_y, url).
+* extra_data: If present, a function that is passed the report and returns a
+hashref of extra data to include that can be used by 'columns'.
=cut
@@ -371,21 +361,16 @@ sub generate_csv : Private {
my $fixed_states = FixMyStreet::DB::Result::Problem->fixed_states;
my $closed_states = FixMyStreet::DB::Result::Problem->closed_states;
- my $wards = 0;
- my $comments = 0;
- foreach (@{$c->stash->{csv}->{columns}}) {
- $wards = 1 if $_ eq 'wards';
- $comments = 1 if $_ eq 'acknowledged';
- }
+ my %asked_for = map { $_ => 1 } @{$c->stash->{csv}->{columns}};
my $problems = $c->stash->{csv}->{problems};
while ( my $report = $problems->next ) {
- my $hashref = $report->as_hashref($c);
+ my $hashref = $report->as_hashref($c, \%asked_for);
$hashref->{user_name_display} = $report->anonymous
? '(anonymous)' : $report->user->name;
- if ($comments) {
+ if ($asked_for{acknowledged}) {
for my $comment ($report->comments) {
my $problem_state = $comment->problem_state or next;
next unless $comment->state eq 'confirmed';
@@ -400,7 +385,7 @@ sub generate_csv : Private {
}
}
- if ($wards) {
+ if ($asked_for{wards}) {
$hashref->{wards} = join ', ',
map { $c->stash->{children}->{$_}->{name} }
grep {$c->stash->{children}->{$_} }
@@ -411,6 +396,11 @@ sub generate_csv : Private {
$report->local_coords;
$hashref->{url} = join '', $c->cobrand->base_url_for_report($report), $report->url;
+ if (my $fn = $c->stash->{csv}->{extra_data}) {
+ my $extra = $fn->($report);
+ $hashref = { %$hashref, %$extra };
+ }
+
$csv->combine(
@{$hashref}{
@{$c->stash->{csv}->{columns}}
diff --git a/perllib/FixMyStreet/App/Controller/Develop.pm b/perllib/FixMyStreet/App/Controller/Develop.pm
new file mode 100755
index 000000000..0bc52883f
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Develop.pm
@@ -0,0 +1,136 @@
+package FixMyStreet::App::Controller::Develop;
+use Moose;
+use namespace::autoclean;
+
+use File::Basename;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Develop - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Developer-helping Catalyst Controller.
+
+=head1 METHODS
+
+=over 4
+
+=item auto
+
+Makes sure this controller is only available when run in development.
+
+=cut
+
+sub auto : Private {
+ my ($self, $c) = @_;
+ $c->detach( '/page_error_404_not_found' ) unless $c->config->{STAGING_SITE};
+ return 1;
+}
+
+=item email_list
+
+Shows a list of links to preview HTML emails.
+
+=cut
+
+sub email_list : Path('/_dev/email') : Args(0) {
+ my ( $self, $c ) = @_;
+
+ my @include_path = @{ $c->cobrand->path_to_email_templates($c->stash->{lang_code}) };
+ push @include_path, $c->view('Email')->config->{INCLUDE_PATH}->[0];
+ my %templates;
+ foreach (@include_path) {
+ $templates{$_} = 1 for grep { /^[^_]/ } map { s/\.html$//; basename $_ } glob "$_/*.html";
+ }
+
+ my %with_update = ('update-confirm' => 1, 'other-updated' => 1);
+ my %with_problem = ('alert-update' => 1, 'other-reported' => 1,
+ 'problem-confirm' => 1, 'problem-confirm-not-sending' => 1,
+ 'problem-moderated' => 1, 'questionnaire' => 1, 'submit' => 1);
+
+ my $update = $c->model('DB::Comment')->first;
+ my $problem = $c->model('DB::Problem')->first;
+
+ $c->stash->{templates} = [];
+ foreach (sort keys %templates) {
+ my $url = $c->uri_for('/_dev/email', $_);
+ $url .= "?problem=" . $problem->id if $problem && $with_problem{$_};
+ $url .= "?update=" . $update->id if $update && $with_update{$_};
+ push @{$c->stash->{templates}}, { name => $_, url => $url };
+ }
+}
+
+=item email_previewer
+
+Previews an HTML email template. A problem or update ID can be provided as a
+query parameter, and other data is taken from the database.
+
+=back
+
+=cut
+
+sub email_previewer : Path('/_dev/email') : Args(1) {
+ my ( $self, $c, $template ) = @_;
+
+ my $vars = {};
+ if (my $id = $c->get_param('update')) {
+ $vars->{update} = $c->model('DB::Comment')->find($id);
+ $vars->{problem} = $vars->{report} = $vars->{update}->problem;
+ } elsif ($id = $c->get_param('problem')) {
+ $vars->{problem} = $vars->{report} = $c->model('DB::Problem')->find($id);
+ }
+
+ # Special case needed variables
+ if ($template =~ /^alert-problem/) {
+ $vars->{area_name} = 'Area Name';
+ $vars->{ward_name} = 'Ward Name';
+ $vars->{data} = [ $c->model('DB::Problem')->search({}, { rows => 5 })->all ];
+ } elsif ($template eq 'alert-update') {
+ $vars->{data} = [];
+ my $q = $c->model('DB::Comment')->search({}, { rows => 5 });
+ while (my $u = $q->next) {
+ my $fn = sub {
+ return FixMyStreet::App::Model::PhotoSet->new({
+ db_data => $u->photo,
+ })->get_image_data( num => 0, size => 'fp' );
+ };
+ push @{$vars->{data}}, {
+ item_photo => $u->photo, get_first_image_fp => $fn, item_text => $u->text,
+ item_name => $u->name, item_anonymous => $u->anonymous, confirmed => $u->confirmed };
+ }
+ } elsif ($template eq 'questionnaire') {
+ $vars->{created} = 'N weeks';
+ }
+
+ my $email = $c->construct_email("$template.txt", $vars);
+
+ # Look through the Email::MIME email for the text/html part, and any inline
+ # images. Turn the images into data: URIs.
+ my $html = '';
+ my %images;
+ $email->walk_parts(sub {
+ my ($part) = @_;
+ return if $part->subparts;
+ if ($part->content_type =~ m[^image/]i) {
+ (my $cid = $part->header('Content-ID')) =~ s/[<>]//g;
+ (my $ct = $part->content_type) =~ s/;.*//;
+ $images{$cid} = "$ct;base64," . $part->body_raw;
+ } elsif ($part->content_type =~ m[text/html]i) {
+ $html = $part->body_str;
+ }
+ });
+
+ foreach (keys %images) {
+ $html =~ s/cid:([^"]*)/data:$images{$1}/g;
+ }
+
+ $c->response->body($html);
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
diff --git a/perllib/FixMyStreet/App/Controller/FakeMapit.pm b/perllib/FixMyStreet/App/Controller/FakeMapit.pm
index 0ec13ebfa..51975254a 100755
--- a/perllib/FixMyStreet/App/Controller/FakeMapit.pm
+++ b/perllib/FixMyStreet/App/Controller/FakeMapit.pm
@@ -2,7 +2,7 @@ package FixMyStreet::App::Controller::FakeMapit;
use Moose;
use namespace::autoclean;
use JSON::MaybeXS;
-use LWP::Simple;
+use LWP::UserAgent;
BEGIN { extends 'Catalyst::Controller'; }
@@ -22,13 +22,25 @@ world is one area, with ID 161 and name "Everywhere".
my $area = { "name" => "Everywhere", "type" => "ZZZ", "id" => 161 };
+has user_agent => (
+ is => 'ro',
+ lazy => 1,
+ default => sub {
+ my $ua = LWP::UserAgent->new;
+ my $api_key = FixMyStreet->config('MAPIT_API_KEY');
+ $ua->agent("FakeMapit proxy");
+ $ua->default_header( 'X-Api-Key' => $api_key ) if $api_key;
+ return $ua;
+ }
+);
+
# The user should have the web server proxying this,
# but for development we can also do it on the server.
sub proxy : Path('/mapit') {
my ($self, $c) = @_;
(my $path = $c->req->uri->path_query) =~ s{^/mapit/}{};
my $url = FixMyStreet->config('MAPIT_URL') . $path;
- my $kml = LWP::Simple::get($url);
+ my $kml = $self->user_agent->get($url)->content;
$c->response->body($kml);
}
diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm
index 1693766ba..883ccc0ce 100644
--- a/perllib/FixMyStreet/App/Controller/My.pm
+++ b/perllib/FixMyStreet/App/Controller/My.pm
@@ -159,11 +159,10 @@ sub setup_page_data : Private {
my @categories = $c->stash->{problems_rs}->search({
state => [ FixMyStreet::DB::Result::Problem->visible_states() ],
}, {
- columns => [ 'category' ],
+ columns => [ 'category', 'extra' ],
distinct => 1,
order_by => [ 'category' ],
} )->all;
- @categories = map { { name => $_->category, value => $_->category_display } } @categories;
$c->stash->{filter_categories} = \@categories;
$c->stash->{page} = 'my';
diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm
index c7e4e5bee..83b9b8202 100644
--- a/perllib/FixMyStreet/App/Controller/Open311.pm
+++ b/perllib/FixMyStreet/App/Controller/Open311.pm
@@ -231,14 +231,15 @@ sub output_requests : Private {
$problem->state( $statusmap{$problem->state} );
+ my ($lat, $lon) = map { Utils::truncate_coordinate($_) } $problem->latitude, $problem->longitude;
my $request =
{
'service_request_id' => $id,
'title' => $problem->title, # Not in Open311 v2
'detail' => $problem->detail, # Not in Open311 v2
'description' => $problem->title .': ' . $problem->detail,
- 'lat' => $problem->latitude,
- 'long' => $problem->longitude,
+ 'lat' => $lat,
+ 'long' => $lon,
'status' => $problem->state,
# 'status_notes' => {},
# Zurich has visible unconfirmed reports
@@ -307,6 +308,7 @@ sub get_requests : Private {
# Only provide access to the published reports
my $states = FixMyStreet::DB::Result::Problem->visible_states();
delete $states->{unconfirmed};
+ delete $states->{submitted};
my $criteria = {
state => [ keys %$states ]
};
@@ -409,6 +411,7 @@ sub get_request : Private {
my $states = FixMyStreet::DB::Result::Problem->visible_states();
delete $states->{unconfirmed};
+ delete $states->{submitted};
my $criteria = {
state => [ keys %$states ],
id => $id,
@@ -419,6 +422,7 @@ sub get_request : Private {
sub format_output : Private {
my ( $self, $c, $hashref ) = @_;
my $format = $c->stash->{format};
+ $c->response->header('Access-Control-Allow-Origin' => '*');
if ('json' eq $format) {
$c->res->content_type('application/json; charset=utf-8');
$c->res->body( encode_json($hashref) );
@@ -441,11 +445,10 @@ sub is_jurisdiction_id_ok : Private {
# Input: DateTime object
# Output: 2011-04-23T10:28:55+02:00
-# FIXME Need generic solution to find time zone
sub w3date : Private {
my $datestr = shift;
return unless $datestr;
- return DateTime::Format::W3CDTF->format_datetime($datestr);
+ return DateTime::Format::W3CDTF->format_datetime($datestr->truncate(to => 'second'));
}
=head1 AUTHOR
diff --git a/perllib/FixMyStreet/App/Controller/Questionnaire.pm b/perllib/FixMyStreet/App/Controller/Questionnaire.pm
index 1b338732b..58848f546 100755
--- a/perllib/FixMyStreet/App/Controller/Questionnaire.pm
+++ b/perllib/FixMyStreet/App/Controller/Questionnaire.pm
@@ -27,13 +27,13 @@ finds out if this user has answered the "ever reported" question before.
=cut
sub check_questionnaire : Private {
- my ( $self, $c ) = @_;
+ my ( $self, $c, $unanswered ) = @_;
my $questionnaire = $c->stash->{questionnaire};
my $problem = $questionnaire->problem;
- if ( $questionnaire->whenanswered ) {
+ if ( $unanswered && $questionnaire->whenanswered ) {
my $problem_url = $c->cobrand->base_url_for_report( $problem ) . $problem->url;
my $contact_url = $c->uri_for( "/contact" );
my $message = sprintf(_("You have already answered this questionnaire. If you have a question, please <a href='%s'>get in touch</a>, or <a href='%s'>view your problem</a>.\n"), $contact_url, $problem_url);
@@ -46,6 +46,11 @@ sub check_questionnaire : Private {
$c->stash->{problem} = $problem;
$c->stash->{answered_ever_reported} = $problem->user->answered_ever_reported;
+ $c->stash->{been_fixed} = $c->get_param('been_fixed') || '';
+
+ # In case they've already visited the questionnaire page, so take what was stored then
+ my $old_state = $c->stash->{old_state} = $questionnaire->old_state || $problem->state;
+ $c->stash->{was_fixed} = FixMyStreet::DB::Result::Problem->fixed_states()->{$old_state};
}
=head2 submit
@@ -144,35 +149,51 @@ sub submit_creator_fixed : Private {
return 1;
}
-sub submit_standard : Private {
+sub record_state_change : Private {
my ( $self, $c ) = @_;
- $c->forward( '/tokens/load_questionnaire', [ $c->get_param('token') ] );
- $c->forward( 'check_questionnaire' );
- $c->forward( 'process_questionnaire' );
+ return unless $c->stash->{been_fixed};
my $problem = $c->stash->{problem};
- my $old_state = $problem->state;
+ my $old_state = $c->stash->{old_state};
my $new_state = '';
- $new_state = 'fixed - user' if $c->stash->{been_fixed} eq 'Yes' &&
+ $new_state = 'fixed - user' if $c->stash->{been_fixed} eq 'Yes' &&
FixMyStreet::DB::Result::Problem->open_states()->{$old_state};
$new_state = 'fixed - user' if $c->stash->{been_fixed} eq 'Yes' &&
FixMyStreet::DB::Result::Problem->closed_states()->{$old_state};
$new_state = 'confirmed' if $c->stash->{been_fixed} eq 'No' &&
FixMyStreet::DB::Result::Problem->fixed_states()->{$old_state};
+ $c->stash->{new_state} = $new_state;
# Record state change, if there was one
if ( $new_state ) {
$problem->state( $new_state );
$problem->lastupdate( \'current_timestamp' );
- }
-
- # If it's not fixed and they say it's still not been fixed, record time update
- if ( $c->stash->{been_fixed} eq 'No' &&
+ } elsif ($problem->state ne $old_state) {
+ $problem->state( $old_state );
+ $problem->lastupdate( \'current_timestamp' );
+ } elsif ( $c->stash->{been_fixed} eq 'No' &&
FixMyStreet::DB::Result::Problem->open_states->{$old_state} ) {
+ # If it's not fixed and they say it's still not been fixed, record time update
$problem->lastupdate( \'current_timestamp' );
}
+ $problem->update;
+ $c->stash->{questionnaire}->update({
+ whenanswered => \'current_timestamp',
+ old_state => $old_state,
+ new_state => $c->stash->{been_fixed} eq 'Unknown' ? 'unknown' : ($new_state || $old_state),
+ });
+}
+
+sub submit_standard : Private {
+ my ( $self, $c ) = @_;
+
+ $c->forward( '/tokens/load_questionnaire', [ $c->get_param('token') ] );
+ $c->forward( 'check_questionnaire' );
+ $c->forward( 'process_questionnaire' );
+ $c->forward( 'record_state_change' );
+
# Record questionnaire response
my $reported = undef;
$reported = 1 if $c->stash->{reported} eq 'Yes';
@@ -180,14 +201,13 @@ sub submit_standard : Private {
my $q = $c->stash->{questionnaire};
$q->update( {
- whenanswered => \'current_timestamp',
ever_reported => $reported,
- old_state => $old_state,
- new_state => $c->stash->{been_fixed} eq 'Unknown' ? 'unknown' : ($new_state || $old_state),
} );
+ my $problem = $c->stash->{problem};
+
# Record an update if they've given one, or if there's a state change
- if ( $new_state || $c->stash->{update} ) {
+ if ( $c->stash->{new_state} || $c->stash->{update} ) {
my $update = $c->stash->{update} || _('Questionnaire filled in by problem reporter');
$update = $c->model('DB::Comment')->new(
{
@@ -196,8 +216,8 @@ sub submit_standard : Private {
user => $problem->user,
text => $update,
state => 'confirmed',
- mark_fixed => $new_state eq 'fixed - user' ? 1 : 0,
- mark_open => $new_state eq 'confirmed' ? 1 : 0,
+ mark_fixed => $c->stash->{new_state} eq 'fixed - user' ? 1 : 0,
+ mark_open => $c->stash->{new_state} eq 'confirmed' ? 1 : 0,
lang => $c->stash->{lang_code},
cobrand => $c->cobrand->moniker,
cobrand_data => '',
@@ -205,6 +225,7 @@ sub submit_standard : Private {
anonymous => $problem->anonymous,
}
);
+ $update->set_extra_metadata( questionnaire_id => $q->id );
if ( my $fileid = $c->stash->{upload_fileid} ) {
$update->photo( $fileid );
}
@@ -216,14 +237,13 @@ sub submit_standard : Private {
if ($c->stash->{been_fixed} eq 'No' || $c->stash->{been_fixed} eq 'Unknown') && $c->stash->{another} eq 'Yes';
$problem->update;
- $c->stash->{new_state} = $new_state;
$c->stash->{template} = 'questionnaire/completed.html';
}
sub process_questionnaire : Private {
my ( $self, $c ) = @_;
- map { $c->stash->{$_} = $c->get_param($_) || '' } qw(been_fixed reported another update);
+ map { $c->stash->{$_} = $c->get_param($_) || '' } qw(reported another update);
$c->stash->{update} = Utils::cleanup_text($c->stash->{update}, { allow_multiline => 1 });
@@ -240,7 +260,7 @@ sub process_questionnaire : Private {
if ($c->stash->{been_fixed} eq 'No' || $c->stash->{been_fixed} eq 'Unknown') && !$c->stash->{another};
push @errors, _('Please provide some explanation as to why you\'re reopening this report')
- if $c->stash->{been_fixed} eq 'No' && $c->stash->{problem}->is_fixed() && !$c->stash->{update};
+ if $c->stash->{been_fixed} eq 'No' && $c->stash->{was_fixed} && !$c->stash->{update};
$c->forward('/photo/process_photo');
push @errors, $c->stash->{photo_error}
@@ -258,7 +278,8 @@ sub process_questionnaire : Private {
# Sent here from email token action. Simply load and display questionnaire.
sub show : Private {
my ( $self, $c ) = @_;
- $c->forward( 'check_questionnaire' );
+ $c->forward( 'check_questionnaire', [ 'unanswered' ] );
+ $c->forward( 'record_state_change' );
$c->forward( 'display' );
}
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index b1cc5885a..799985f8e 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -76,7 +76,7 @@ sub _display : Private {
$c->forward( 'load_updates' );
$c->forward( 'format_problem_for_display' );
- my $permissions = $c->stash->{_permissions} = $c->forward( 'check_has_permission_to',
+ my $permissions = $c->stash->{_permissions} ||= $c->forward( 'check_has_permission_to',
[ qw/report_inspect report_edit_category report_edit_priority/ ] );
if (any { $_ } values %$permissions) {
$c->stash->{template} = 'report/inspect.html';
@@ -121,14 +121,17 @@ sub load_problem_or_display_error : Private {
$c->detach( '/page_error_404_not_found', [ _('Unknown problem ID') ] )
unless $c->cobrand->show_unconfirmed_reports ;
}
- elsif ( $problem->hidden_states->{ $problem->state } or
- (($problem->get_extra_metadata('closure_status')||'') eq 'hidden')) {
+ elsif ( $problem->hidden_states->{ $problem->state } ) {
$c->detach(
'/page_error_410_gone',
[ _('That report has been removed from FixMyStreet.') ] #
);
} elsif ( $problem->non_public ) {
- if ( !$c->user || $c->user->id != $problem->user->id ) {
+ # 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}) ) {
$c->detach(
'/page_error_403_access_denied',
[ sprintf(_('That report cannot be viewed on %s.'), $c->stash->{site_name}) ]
@@ -163,17 +166,28 @@ sub load_updates : Private {
{
problem_id => $c->stash->{problem}->id,
whenanswered => { '!=', undef },
- old_state => 'confirmed', new_state => 'confirmed',
+ old_state => [ -and =>
+ { -in => [ FixMyStreet::DB::Result::Problem::closed_states, FixMyStreet::DB::Result::Problem::open_states ] },
+ \'= new_state',
+ ]
},
{ order_by => 'whenanswered' }
);
my @combined;
+ my %questionnaires_with_updates;
while (my $update = $updates->next) {
push @combined, [ $update->confirmed, $update ];
+ if (my $qid = $update->get_extra_metadata('questionnaire_id')) {
+ $questionnaires_with_updates{$qid} = $update;
+ }
}
- while (my $update = $questionnaires->next) {
- push @combined, [ $update->whenanswered, $update ];
+ while (my $q = $questionnaires->next) {
+ if (my $update = $questionnaires_with_updates{$q->id}) {
+ $update->set_extra_metadata('open_from_questionnaire', 1);
+ next;
+ }
+ push @combined, [ $q->whenanswered, $q ];
}
@combined = map { $_->[1] } sort { $a->[0] <=> $b->[0] } @combined;
$c->stash->{updates} = \@combined;
@@ -207,9 +221,15 @@ sub format_problem_for_display : Private {
if ( $c->stash->{ajax} ) {
$c->res->content_type('application/json; charset=utf-8');
+
+ # encode_json doesn't like DateTime objects, so strip them out
+ my $report_hashref = $c->cobrand->problem_as_hashref( $problem, $c );
+ delete $report_hashref->{created};
+ delete $report_hashref->{confirmed};
+
my $content = encode_json(
{
- report => $c->cobrand->problem_as_hashref( $problem, $c ),
+ report => $report_hashref,
updates => $c->cobrand->updates_as_hashref( $problem, $c ),
}
);
@@ -337,6 +357,8 @@ 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') ) {
@@ -380,7 +402,7 @@ sub inspect : Private {
if ( $problem->state eq 'duplicate') {
if (my $duplicate_of = $c->get_param('duplicate_of')) {
$problem->set_duplicate_of($duplicate_of);
- } elsif (not $c->get_param('public_update')) {
+ } elsif (not $c->get_param('include_update')) {
$valid = 0;
push @{ $c->stash->{errors} }, _('Please provide a duplicate ID or public update for this report.');
}
@@ -404,7 +426,7 @@ sub inspect : Private {
cobrand_data => $problem->cobrand_data,
lang => $problem->lang,
};
- $problem->user->create_alert($problem->id, $options);
+ $c->user->create_alert($problem->id, $options);
}
# If the state has been changed to action scheduled and they've said
@@ -450,22 +472,30 @@ sub inspect : Private {
}
$problem->lastupdate( \'current_timestamp' );
$problem->update;
- my $timestamp = \'current_timestamp';
- if (my $saved_at = $c->get_param('saved_at')) {
- $timestamp = DateTime->from_epoch( epoch => $saved_at );
+ if ($update_text || %update_params) {
+ my $timestamp = \'current_timestamp';
+ if (my $saved_at = $c->get_param('saved_at')) {
+ # this comes in as a UTC epoch but the database expects everything
+ # 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,
+ epoch => $saved_at
+ );
+ }
+ my $name = $c->user->from_body ? $c->user->from_body->name : $c->user->name;
+ $problem->add_to_comments( {
+ text => $update_text,
+ created => $timestamp,
+ confirmed => $timestamp,
+ user_id => $c->user->id,
+ name => $name,
+ state => 'confirmed',
+ mark_fixed => 0,
+ anonymous => 0,
+ %update_params,
+ } );
}
- my $name = $c->user->from_body ? $c->user->from_body->name : $c->user->name;
- $problem->add_to_comments( {
- text => $update_text,
- created => $timestamp,
- confirmed => $timestamp,
- user_id => $c->user->id,
- name => $name,
- state => 'confirmed',
- mark_fixed => 0,
- anonymous => 0,
- %update_params,
- } );
my $redirect_uri;
$problem->discard_changes;
@@ -523,8 +553,10 @@ sub nearby_json : Private {
# 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');
+
my $nearby = $c->model('DB::Nearby')->nearby(
- $c, $dist, [ $p->id ], 5, $p->latitude, $p->longitude, [ $p->category ], undef
+ $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 ];
@@ -536,8 +568,8 @@ sub nearby_json : Private {
} @$nearby;
my $list_html = $c->render_fragment(
- 'around/on_map_list_items.html',
- { around_map => [], on_map => $nearby }
+ 'report/nearby.html',
+ { reports => $nearby }
);
my $json = { pins => \@pins };
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index f9e07dd41..331067c1a 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -632,6 +632,8 @@ sub setup_categories_and_bodies : Private {
my %bodies_to_list = (); # Bodies with categories assigned
my @category_options = (); # categories to show
my %category_extras = (); # extra fields to fill in for open311
+ my %category_extras_hidden =
+ (); # whether all of a category's fields are hidden
my %non_public_categories =
(); # categories for which the reports are not public
$c->stash->{unresponsive} = {};
@@ -664,10 +666,11 @@ sub setup_categories_and_bodies : Private {
$bodies_to_list{ $contact->body_id } = $contact->body;
unless ( $seen{$contact->category} ) {
- push @category_options, { name => $contact->category, value => $contact->category_display, group => $contact->get_extra_metadata('group') || '' };
+ push @category_options, $contact;
my $metas = $contact->get_metadata_for_input;
$category_extras{$contact->category} = $metas if @$metas;
+ $category_extras_hidden{$contact->category} = (grep { !$c->cobrand->category_extra_hidden($_) } @$metas) ? 0 : 1;
my $body_send_method = $bodies{$contact->body_id}->send_method || '';
$c->stash->{unresponsive}{$contact->category} = $contact->body_id
@@ -676,15 +679,15 @@ sub setup_categories_and_bodies : Private {
$non_public_categories{ $contact->category } = 1 if $contact->non_public;
}
- $seen{$contact->category} = $contact->category_display;
+ $seen{$contact->category} = $contact;
}
if (@category_options) {
# If there's an Other category present, put it at the bottom
@category_options = (
- { name => _('-- Pick a category --'), value => _('-- Pick a category --'), group => '' },
- grep { $_->{name} ne _('Other') } @category_options );
- push @category_options, { name => _('Other'), value => $seen{_('Other')}, group => _('Other') } if $seen{_('Other')};
+ { category => _('-- Pick a category --'), category_display => _('-- Pick a category --'), group => '' },
+ grep { $_->category ne _('Other') } @category_options );
+ push @category_options, $seen{_('Other')} if $seen{_('Other')};
}
$c->cobrand->call_hook(munge_category_list => \@category_options, \@contacts, \%category_extras);
@@ -692,11 +695,10 @@ sub setup_categories_and_bodies : Private {
# put results onto stash for display
$c->stash->{bodies} = \%bodies;
$c->stash->{contacts} = \@contacts;
- $c->stash->{bodies_to_list} = [ keys %bodies_to_list ];
- $c->stash->{bodies_to_list_names} = [ map { $_->name } values %bodies_to_list ];
- $c->stash->{bodies_to_list_urls} = [ map { $_->external_url } values %bodies_to_list ];
+ $c->stash->{bodies_to_list} = \%bodies_to_list;
$c->stash->{category_options} = \@category_options;
$c->stash->{category_extras} = \%category_extras;
+ $c->stash->{category_extras_hidden} = \%category_extras_hidden;
$c->stash->{non_public_categories} = \%non_public_categories;
$c->stash->{extra_name_info} = $first_area->{id} == COUNCIL_ID_BROMLEY ? 1 : 0;
@@ -709,7 +711,8 @@ sub setup_categories_and_bodies : Private {
if ( $c->cobrand->call_hook('enable_category_groups') ) {
my %category_groups = ();
for my $category (@category_options) {
- push @{$category_groups{$category->{group}}}, $category;
+ my $group = $category->{group} // $category->get_extra_metadata('group') // '';
+ push @{$category_groups{$group}}, $category;
}
my @category_groups = ();
@@ -827,7 +830,7 @@ sub process_user : Private {
$c->forward('update_user', [ \%params ]);
if ($params{password_register}) {
$c->forward('/auth/test_password', [ $params{password_register} ]);
- $report->user->password(Utils::trim_text($params{password_register}));
+ $report->user->password($params{password_register});
}
return 1;
@@ -870,6 +873,8 @@ sub process_report : Private {
'subcategory', #
'partial', #
'service', #
+ 'non_public',
+ 'single_body_only'
);
# load the report
@@ -897,6 +902,8 @@ sub process_report : Private {
$report->anonymous( $params{may_show_name} ? 0 : 1 );
}
+ $report->non_public($params{non_public} ? 1 : 0);
+
# clean up text before setting
$report->title( Utils::cleanup_text( $params{title} ) );
@@ -912,6 +919,7 @@ sub process_report : Private {
# set these straight from the params
$report->category( _ $params{category} ) if $params{category};
+ $c->cobrand->call_hook(report_new_munge_category => $report);
$report->subcategory( $params{subcategory} );
my $areas = $c->stash->{all_areas_mapit};
@@ -925,7 +933,7 @@ sub process_report : Private {
return 1;
}
- my $bodies = $c->forward('contacts_to_bodies', [ $report->category ]);
+ my $bodies = $c->forward('contacts_to_bodies', [ $report->category, $params{single_body_only} ]);
my $body_string = join(',', map { $_->id } @$bodies) || '-1';
$report->bodies_str($body_string);
@@ -940,7 +948,7 @@ sub process_report : Private {
if ( $c->stash->{non_public_categories}->{ $report->category } ) {
$report->non_public( 1 );
}
- } elsif ( @{ $c->stash->{bodies_to_list} } ) {
+ } elsif ( %{ $c->stash->{bodies_to_list} } ) {
# There was an area with categories, but we've not been given one. Bail.
$c->stash->{field_errors}->{category} = _('Please choose a category');
@@ -959,11 +967,19 @@ sub process_report : Private {
my $value = $c->get_param($form_name) || '';
$c->stash->{field_errors}->{$form_name} = _('This information is required')
if $field->{required} && !$value;
+ if ($field->{validator}) {
+ eval {
+ $value = $field->{validator}->($value);
+ };
+ if ($@) {
+ $c->stash->{field_errors}->{$form_name} = $@;
+ }
+ }
$report->set_extra_metadata( $form_name => $value );
}
# set defaults that make sense
- $report->state('unconfirmed');
+ $report->state($c->cobrand->default_problem_state);
# save the cobrand and language related information
$report->cobrand( $c->cobrand->moniker );
@@ -974,10 +990,18 @@ sub process_report : Private {
}
sub contacts_to_bodies : Private {
- my ($self, $c, $category) = @_;
+ my ($self, $c, $category, $single_body_only) = @_;
my @contacts = grep { $_->category eq $category } @{$c->stash->{contacts}};
+ # check that we've not indicated we only want to sent to a single body
+ # and if we find a matching one then only send to that. e.g. if we clicked
+ # on a TfL road on the map.
+ if ($single_body_only) {
+ my @contacts_filtered = grep { $_->body->name eq $single_body_only } @contacts;
+ @contacts = @contacts_filtered if scalar @contacts_filtered;
+ }
+
if ($c->stash->{unresponsive}{$category} || $c->stash->{unresponsive}{ALL} || !@contacts) {
[];
} else {
@@ -999,7 +1023,7 @@ sub set_report_extras : Private {
foreach my $contact (@$contacts) {
my $metas = $contact->get_metadata_for_input;
foreach my $field ( @$metas ) {
- if ( lc( $field->{required} ) eq 'true' && !$c->cobrand->category_extra_hidden($field->{code})) {
+ if ( lc( $field->{required} ) eq 'true' && !$c->cobrand->category_extra_hidden($field)) {
unless ( $c->get_param($param_prefix . $field->{code}) ) {
$c->stash->{field_errors}->{ $field->{code} } = _('This information is required');
}
@@ -1016,7 +1040,7 @@ sub set_report_extras : Private {
my $metas = $extra_fields->get_extra_fields;
$param_prefix = "extra[" . $extra_fields->id . "]";
foreach my $field ( @$metas ) {
- if ( lc( $field->{required} ) eq 'true' && !$c->cobrand->category_extra_hidden($field->{code})) {
+ if ( lc( $field->{required} ) eq 'true' && !$c->cobrand->category_extra_hidden($field)) {
unless ( $c->get_param($param_prefix . $field->{code}) ) {
$c->stash->{field_errors}->{ $field->{code} } = _('This information is required');
}
@@ -1296,9 +1320,17 @@ sub save_user_and_report : Private {
if ( $c->cobrand->never_confirm_reports ) {
$report->user->update_or_insert;
$report->confirm();
- } elsif ( $c->forward('created_as_someone_else', [ $c->stash->{bodies} ]) ) {
- # If created on behalf of someone else, we automatically confirm it,
- # but we don't want to update the user account
+ # If created on behalf of someone else, we automatically confirm it,
+ # but we don't want to update the user account
+ } elsif ($c->stash->{contributing_as_another_user}) {
+ $report->set_extra_metadata( contributed_as => 'another_user');
+ $report->set_extra_metadata( contributed_by => $c->user->id );
+ $report->confirm();
+ } elsif ($c->stash->{contributing_as_body}) {
+ $report->set_extra_metadata( contributed_as => 'body' );
+ $report->confirm();
+ } elsif ($c->stash->{contributing_as_anonymous_user}) {
+ $report->set_extra_metadata( contributed_as => 'anonymous_user' );
$report->confirm();
} elsif ( !$report->user->in_storage ) {
# User does not exist.
@@ -1328,6 +1360,8 @@ sub save_user_and_report : Private {
$c->log->info($report->user->id . ' exists, but is not logged in for this report');
}
+ $c->cobrand->call_hook(report_new_munge_before_insert => $report);
+
$report->update_or_insert;
# tidy up
@@ -1338,11 +1372,6 @@ sub save_user_and_report : Private {
return 1;
}
-sub created_as_someone_else : Private {
- my ($self, $c, $bodies) = @_;
- return $c->stash->{contributing_as_another_user} || $c->stash->{contributing_as_body} || $c->stash->{contributing_as_anonymous_user};
-}
-
=head2 generate_map
Add the html needed to for the map to the stash.
diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm
index 99eae8659..4a5b8db5d 100644
--- a/perllib/FixMyStreet/App/Controller/Report/Update.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm
@@ -156,7 +156,7 @@ sub process_user : Private {
if ($params{password_register}) {
$c->forward('/auth/test_password', [ $params{password_register} ]);
- $update->user->password(Utils::trim_text($params{password_register}));
+ $update->user->password($params{password_register});
}
return 1;
@@ -240,6 +240,7 @@ This makes sure we only proceed to processing if we've had the form submitted
sub check_form_submitted : Private {
my ( $self, $c ) = @_;
+ return if $c->stash->{problem}->get_extra_metadata('closed_updates');
return $c->get_param('submit_update') || '';
}
@@ -444,9 +445,17 @@ sub save_update : Private {
if ( $c->cobrand->never_confirm_updates ) {
$update->user->update_or_insert;
$update->confirm();
- } elsif ( $c->forward('/report/new/created_as_someone_else', [ $update->problem->bodies_str ]) ) {
- # If created on behalf of someone else, we automatically confirm it,
- # but we don't want to update the user account
+ # If created on behalf of someone else, we automatically confirm it,
+ # but we don't want to update the user account
+ } elsif ($c->stash->{contributing_as_another_user}) {
+ $update->set_extra_metadata( contributed_as => 'another_user');
+ $update->set_extra_metadata( contributed_by => $c->user->id );
+ $update->confirm();
+ } elsif ($c->stash->{contributing_as_body}) {
+ $update->set_extra_metadata( contributed_as => 'body' );
+ $update->confirm();
+ } elsif ($c->stash->{contributing_as_anonymous_user}) {
+ $update->set_extra_metadata( contributed_as => 'anonymous_user' );
$update->confirm();
} elsif ( !$update->user->in_storage ) {
# User does not exist.
diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm
index 7c3796c42..dc9e2c913 100644
--- a/perllib/FixMyStreet/App/Controller/Reports.pm
+++ b/perllib/FixMyStreet/App/Controller/Reports.pm
@@ -93,6 +93,7 @@ sub index : Path : Args(0) {
}
} else {
my @bodies = $c->model('DB::Body')->active->translated->with_area_count->all_sorted;
+ @bodies = @{$c->cobrand->call_hook('reports_hook_restrict_bodies_list', \@bodies) || \@bodies };
$c->stash->{bodies} = \@bodies;
}
@@ -125,6 +126,17 @@ sub ward : Path : Args(2) {
my @wards = split /\|/, $ward || "";
$c->forward( 'body_check', [ $body ] );
+ # If viewing multiple wards, rewrite the url from
+ # /reports/Borsetshire?ward=North&ward=East
+ # to
+ # /reports/Borsetshire/North|East
+ my @ward_params = $c->get_param_list('ward');
+ if ( @ward_params ) {
+ $c->stash->{wards} = [ map { { name => $_ } } (@wards, @ward_params) ];
+ delete $c->req->params->{ward};
+ $c->detach("redirect_body");
+ }
+
my $body_short = $c->cobrand->short_name( $c->stash->{body} );
$c->stash->{body_url} = '/reports/' . $body_short;
@@ -155,11 +167,10 @@ sub ward : Path : Args(2) {
$c->stash->{stats} = $c->cobrand->get_report_stats();
my @categories = $c->stash->{body}->contacts->not_deleted->search( undef, {
- columns => [ 'category' ],
+ columns => [ 'category', 'extra' ],
distinct => 1,
order_by => [ 'category' ],
} )->all;
- @categories = map { { name => $_->category, value => $_->category_display } } @categories;
$c->stash->{filter_categories} = \@categories;
$c->stash->{filter_category} = { map { $_ => 1 } $c->get_param_list('filter_category', 1) };
@@ -413,6 +424,8 @@ sub summary : Private {
my ($self, $c) = @_;
my $dashboard = $c->forward('load_dashboard_data');
+ $c->log->info($c->user->email . ' viewed ' . $c->req->uri->path_query) if $c->user_exists;
+
eval {
my $data = path(FixMyStreet->path_to('../data/all-reports-dashboard.json'))->slurp_utf8;
$data = decode_json($data);
@@ -487,8 +500,8 @@ sub export_summary_csv : Private {
'id',
'title',
'category',
- 'created_pp',
- 'confirmed_pp',
+ 'created',
+ 'confirmed',
'state',
'latitude', 'longitude',
'postcode',
@@ -544,20 +557,27 @@ sub load_and_group_problems : Private {
my $states = $c->stash->{filter_problem_states};
my $where = {
- non_public => 0,
state => [ keys %$states ]
};
+
+ my $body = $c->stash->{body}; # Might be undef
+
+ if ($c->user_exists && ($c->user->is_superuser || ($body && $c->user->has_permission_to('report_inspect', $body->id)))) {
+ # See all reports, no restriction
+ } else {
+ $where->{non_public} = 0;
+ }
+
my $filter = {
order_by => $c->stash->{sort_order},
rows => $c->cobrand->reports_per_page,
};
- if ($c->user_exists && $c->stash->{body}) {
- my $bid = $c->stash->{body}->id;
+ if ($c->user_exists && $body) {
my $prefetch = [];
- if ($c->user->has_permission_to('planned_reports', $bid)) {
+ if ($c->user->has_permission_to('planned_reports', $body->id)) {
push @$prefetch, 'user_planned_reports';
}
- if ($c->user->has_permission_to('report_edit_priority', $bid) || $c->user->has_permission_to('report_inspect', $bid)) {
+ if ($c->user->has_permission_to('report_edit_priority', $body->id) || $c->user->has_permission_to('report_inspect', $body->id)) {
push @$prefetch, 'response_priority';
}
$prefetch = $prefetch->[0] if @$prefetch == 1;
@@ -589,9 +609,9 @@ sub load_and_group_problems : Private {
$where->{areas} = [
map { { 'like', '%,' . $_->{id} . ',%' } } @{$c->stash->{wards}}
];
- $problems = $problems->to_body($c->stash->{body});
- } elsif ($c->stash->{body}) {
- $problems = $problems->to_body($c->stash->{body});
+ $problems = $problems->to_body($body);
+ } elsif ($body) {
+ $problems = $problems->to_body($body);
}
if (my $bbox = $c->get_param('bbox')) {
@@ -609,7 +629,7 @@ sub load_and_group_problems : Private {
my ( %problems, @pins );
while ( my $problem = $problems->next ) {
- if ( !$c->stash->{body} ) {
+ if ( !$body ) {
add_row( $c, $problem, 0, \%problems, \@pins );
next;
}
@@ -623,7 +643,7 @@ sub load_and_group_problems : Private {
# Add to bodies it was sent to
my $bodies = $problem->bodies_str_ids;
foreach ( @$bodies ) {
- next if $_ != $c->stash->{body}->id;
+ next if $_ != $body->id;
add_row( $c, $problem, $_, \%problems, \@pins );
}
}
@@ -697,9 +717,9 @@ sub stash_report_filter_status : Private {
$filter_status{unshortlisted} = 1;
}
- if ($c->user and ($c->user->is_superuser or (
- $c->stash->{body} and $c->user->belongs_to_body($c->stash->{body}->id)
- ))) {
+ my $body_user = $c->user_exists && $c->stash->{body} && $c->user->belongs_to_body($c->stash->{body}->id);
+ my $staff_user = $c->user_exists && ($c->user->is_superuser || $body_user);
+ if ($staff_user || $c->cobrand->call_hook('filter_show_all_states')) {
$c->stash->{filter_states} = $c->cobrand->state_groups_inspect;
foreach my $state (FixMyStreet::DB::Result::Problem->visible_states()) {
if ($status{$state}) {
@@ -727,6 +747,7 @@ sub stash_report_sort : Private {
created => 'confirmed',
comments => 'comment_count',
);
+ $types{created} = 'created' if $c->cobrand->moniker eq 'zurich';
my $sort = $c->get_param('sort') || $default;
$sort = $default unless $sort =~ /^((updated|created)-(desc|asc)|comments-desc|shortlist)$/;