aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rwxr-xr-xbin/fixmystreet.com/defra_stats69
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm5
-rw-r--r--perllib/FixMyStreet/App/Controller/Contact.pm5
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Develop.pm123
-rw-r--r--perllib/FixMyStreet/App/Controller/Moderate.pm201
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Questionnaire.pm7
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm16
-rw-r--r--perllib/FixMyStreet/App/Controller/Tokens.pm26
-rw-r--r--perllib/FixMyStreet/DB/Result/User.pm27
-rw-r--r--perllib/FixMyStreet/Template.pm2
-rw-r--r--t/app/controller/develop.t45
-rw-r--r--t/app/controller/moderate.t20
-rwxr-xr-xtemplates/web/base/develop/index.html41
-rw-r--r--templates/web/base/report/_inspect.html2
-rw-r--r--templates/web/base/report/_main.html18
-rw-r--r--templates/web/base/report/display.html2
-rw-r--r--templates/web/base/report/update.html13
-rw-r--r--web/cobrands/bristol/assets.js2
-rw-r--r--web/cobrands/fixmystreet/assets.js8
-rw-r--r--web/cobrands/fixmystreet/fixmystreet.js9
22 files changed, 410 insertions, 236 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d1f53ed8b..08ae958b5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -34,6 +34,7 @@
- Allow questionnaire link to be revisited in quick succession. #2123
- Update Google Maps directions link.
- Fix inspector pin dragging. #2073.
+ - Maintain all single newlines in text output, not only the first.
- Make sure Home clickable with Try again overlay.
- Open311 improvements:
- CLOSED status maps to 'closed' state if extended statuses are enabled.
@@ -45,6 +46,8 @@
- Make it easier to prevent a form_detail_placeholder being printed.
- Include user agent in contact form emails. #2206
- Use site name in contact email subject line.
+ - Add /_dev endpoints for previewing confirmation/submission pages.
+ - Allow cobrand to add extra ability to moderate.
* v2.3.4 (7th June 2018)
diff --git a/bin/fixmystreet.com/defra_stats b/bin/fixmystreet.com/defra_stats
new file mode 100755
index 000000000..563559a21
--- /dev/null
+++ b/bin/fixmystreet.com/defra_stats
@@ -0,0 +1,69 @@
+#!/usr/bin/env perl
+#
+# this script generates litter stats for defra
+
+use strict;
+use warnings;
+use v5.14;
+
+BEGIN {
+ use File::Basename qw(dirname);
+ use File::Spec;
+ my $d = dirname(File::Spec->rel2abs($0));
+ require "$d/../../setenv.pl";
+}
+
+use FixMyStreet;
+use FixMyStreet::DB;
+use Utils;
+use Text::CSV;
+
+# list of categories pulled from FMS Aug 2018
+my @categories = (
+ 'Accumulated Litter', 'Damage Public Litter Bin', 'Dog and litter bins', 'Dog Bin',
+ 'Dog Bin overflow', 'Dog bin overflowing', 'Dog fouling', 'Dog Fouling', 'Dog Mess',
+ 'Dogs and Dogs Fouling', 'Dog Waste Bin', 'Dog Waste Bin on a verge or open space',
+ 'Dumped cylinder', 'Dumped rubbish', 'Flytipping', 'Fly tipping', 'Fly-tipping',
+ 'Fly Tipping', 'Fly-Tipping', 'Flytipping and dumped rubbish',
+ 'Fly Tipping on a public right of way', 'Fly Tipping on a road, footway, verge or open space',
+ 'Litter', 'Litter bin', 'Litter Bin', 'Litter Bin on a verge or open space',
+ 'Litter Bin overflow', 'Litter Bin Overflowing', 'Litter in the street',
+ 'Overflowing litter bin', 'Overflowing Street Litter Bin', 'Rubbish or fly tipping on the roads',
+);
+
+use Getopt::Long::Descriptive;
+
+my ($opt, $usage) = describe_options(
+ '%c %o',
+ [ 'file=s', "Filename to output results to", { required => 1 } ],
+ [ 'start_date=s', "Start date for stats, defaults to 6 months ago" ],
+ [ 'end_date=s', "End date for stats, defaults today" ],
+ [ 'help', "print usage message and exit", { shortcircuit => 1 } ],
+);
+print($usage->text), exit if $opt->help;
+
+my $now = DateTime->now();
+my $start_date = $opt->start_date || $now->clone->add(months => -6)->strftime('%Y-%m-%d');
+my $end_date = $opt->end_date || $now->clone->strftime('%Y-%m-%d');
+
+my $fh;
+open($fh, '>', $opt->file) or die $!;
+
+my @rows;
+my $csv = Text::CSV->new({ eol => $/ });
+$csv->print( $fh, [ qw(Latitude Longitude Easting Northing Date Category) ] );
+
+my $problems = FixMyStreet::DB->resultset('Problem')->search( {
+ category => \@categories,
+ confirmed => { '>=' => $start_date, '<=' => $end_date }
+ }
+);
+
+while (my $p = $problems->next) {
+ my ( $easting, $northing ) = Utils::convert_latlon_to_en(
+ $p->latitude,
+ $p->longitude
+ );
+
+ $csv->print($fh, [ $p->latitude, $p->longitude, $easting, $northing, $p->confirmed, $p->category ] );
+}
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm
index 41674e377..9f948e0f9 100644
--- a/perllib/FixMyStreet/App/Controller/Auth.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth.pm
@@ -54,11 +54,6 @@ sub general : Path : Args(0) {
}
-sub general_test : Path('_test_') : Args(0) {
- my ( $self, $c ) = @_;
- $c->stash->{template} = 'auth/token.html';
-}
-
sub authenticate : Private {
my ($self, $c, $type, $username, $password) = @_;
return 1 if $type eq 'email' && $c->authenticate({ email => $username, email_verified => 1, password => $password });
diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm
index 3a37d9fa9..aabeb650e 100644
--- a/perllib/FixMyStreet/App/Controller/Contact.pm
+++ b/perllib/FixMyStreet/App/Controller/Contact.pm
@@ -42,11 +42,6 @@ Handle contact us form submission
sub submit : Path('submit') : Args(0) {
my ( $self, $c ) = @_;
- if (my $testing = $c->get_param('_test_')) {
- $c->stash->{success} = $c->get_param('success');
- return;
- }
-
$c->res->redirect( '/contact' ) and return unless $c->req->method eq 'POST';
return
diff --git a/perllib/FixMyStreet/App/Controller/Develop.pm b/perllib/FixMyStreet/App/Controller/Develop.pm
index 0bc52883f..1cc8f8906 100755
--- a/perllib/FixMyStreet/App/Controller/Develop.pm
+++ b/perllib/FixMyStreet/App/Controller/Develop.pm
@@ -26,10 +26,21 @@ Makes sure this controller is only available when run in development.
sub auto : Private {
my ($self, $c) = @_;
- $c->detach( '/page_error_404_not_found' ) unless $c->config->{STAGING_SITE};
+ $c->detach( '/page_error_404_not_found' ) unless $c->user_exists && $c->user->is_superuser;
return 1;
}
+=item index
+
+Shows a list of links to preview HTML emails.
+
+=cut
+
+sub index : Path('/_dev') : Args(0) {
+ my ( $self, $c ) = @_;
+ $c->stash->{problem} = $c->model('DB::Problem')->first;
+}
+
=item email_list
Shows a list of links to preview HTML emails.
@@ -49,6 +60,7 @@ sub email_list : Path('/_dev/email') : Args(0) {
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,
+ 'confirm_report_sent' => 1,
'problem-moderated' => 1, 'questionnaire' => 1, 'submit' => 1);
my $update = $c->model('DB::Comment')->first;
@@ -130,6 +142,115 @@ sub email_previewer : Path('/_dev/email') : Args(1) {
$c->response->body($html);
}
+=item problem_confirm_previewer
+
+Displays the confirmation page for a given problem.
+
+=back
+
+=cut
+
+sub problem_confirm_previewer : Path('/_dev/confirm_problem') : Args(1) {
+ my ( $self, $c, $id ) = @_;
+
+ $c->log->info('Previewing confirmation page for problem ' . $id);
+
+ my $problem = $c->model('DB::Problem')->find( { id => $id } )
+ || $c->detach( '/page_error_404_not_found', [ _('Unknown problem ID') ] );
+ $c->stash->{report} = $problem;
+
+ $c->log->info('Problem ' . $id . ' found: ' . $problem->title);
+ $c->stash->{template} = 'tokens/confirm_problem.html';
+}
+
+=item update_confirm_previewer
+
+Displays the confirmation page for an update on the given problem.
+
+=back
+
+=cut
+
+sub update_confirm_previewer : Path('/_dev/confirm_update') : Args(1) {
+ my ( $self, $c, $id ) = @_;
+
+ my $problem = $c->model('DB::Problem')->find( { id => $id } )
+ || $c->detach( '/page_error_404_not_found', [ _('Unknown problem ID') ] );
+ $c->stash->{problem} = $problem;
+
+ $c->stash->{template} = 'tokens/confirm_update.html';
+}
+
+=item alert_confirm_previewer
+
+Displays the confirmation page for an alert, with the supplied
+confirmation type (ie: subscribed, or unsubscribed).
+
+=back
+
+=cut
+
+sub alert_confirm_previewer : Path('/_dev/confirm_alert') : Args(1) {
+ my ( $self, $c, $confirm_type ) = @_;
+ $c->stash->{confirm_type} = $confirm_type;
+ $c->stash->{template} = 'tokens/confirm_alert.html';
+}
+
+=item contact_submit_previewer
+
+Displays the contact submission page, with success based on the
+truthyness of the supplied argument.
+
+=back
+
+=cut
+
+sub contact_submit_previewer : Path('/_dev/contact_submit') : Args(1) {
+ my ( $self, $c, $success ) = @_;
+ $c->stash->{success} = $success;
+ $c->stash->{template} = 'contact/submit.html';
+}
+
+=item questionnaire_completed_previewer
+
+Displays the questionnaire completed page, with content based on
+the supplied ?new_state and ?been_fixed query params.
+
+=back
+
+=cut
+
+sub questionnaire_completed_previewer : Path('/_dev/questionnaire_completed') : Args(0) {
+ my ( $self, $c ) = @_;
+ $c->stash->{been_fixed} = $c->get_param('been_fixed');
+ $c->stash->{new_state} = $c->get_param('new_state');
+ $c->stash->{template} = 'questionnaire/completed.html';
+}
+
+=item questionnaire_creator_fixed_previewer
+
+Displays the page a user sees after they mark their own report as fixed.
+
+=back
+
+=cut
+
+sub questionnaire_creator_fixed_previewer : Path('/_dev/questionnaire_creator_fixed') : Args(0) {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} = 'questionnaire/creator_fixed.html';
+}
+
+sub auth_preview : Path('/_dev/auth') : Args(0) {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} = 'auth/token.html';
+}
+
+sub report_new_preview : Path('/_dev/report_new') : Args(0) {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} = 'email_sent.html';
+ $c->stash->{email_type} = $c->get_param('email_type');
+}
+
__PACKAGE__->meta->make_immutable;
1;
diff --git a/perllib/FixMyStreet/App/Controller/Moderate.pm b/perllib/FixMyStreet/App/Controller/Moderate.pm
index 86143b5ea..45a303309 100644
--- a/perllib/FixMyStreet/App/Controller/Moderate.pm
+++ b/perllib/FixMyStreet/App/Controller/Moderate.pm
@@ -42,6 +42,7 @@ sub moderate : Chained('/') : PathPart('moderate') : CaptureArgs(0) { }
sub report : Chained('moderate') : PathPart('report') : CaptureArgs(1) {
my ($self, $c, $id) = @_;
my $problem = $c->model('DB::Problem')->find($id);
+ $c->detach unless $problem;
my $cobrand_base = $c->cobrand->base_url_for_report( $problem );
my $report_uri = $cobrand_base . $problem->url;
@@ -49,9 +50,8 @@ sub report : Chained('moderate') : PathPart('report') : CaptureArgs(1) {
$c->stash->{report_uri} = $report_uri;
$c->res->redirect( $report_uri ); # this will be the final endpoint after all processing...
- # ... and immediately, if the user isn't authorized
+ # ... and immediately, if the user isn't logged in
$c->detach unless $c->user_exists;
- $c->detach unless $c->user->has_permission_to(moderate => $problem->bodies_str_ids);
$c->forward('/auth/check_csrf_token');
@@ -69,13 +69,16 @@ sub report : Chained('moderate') : PathPart('report') : CaptureArgs(1) {
sub moderate_report : Chained('report') : PathPart('') : Args(0) {
my ($self, $c) = @_;
+ # Make sure user can moderate this report
+ $c->detach unless $c->user->can_moderate($c->stash->{problem});
+
$c->forward('report_moderate_hide');
my @types = grep $_,
- $c->forward('report_moderate_title'),
- $c->forward('report_moderate_detail'),
- $c->forward('report_moderate_anon'),
- $c->forward('report_moderate_photo');
+ $c->forward('moderate_text', [ 'title' ]),
+ $c->forward('moderate_text', [ 'detail' ]),
+ $c->forward('moderate_boolean', [ 'anonymous', 'show_name' ]),
+ $c->forward('moderate_boolean', [ 'photo' ]);
$c->detach( 'report_moderate_audit', \@types )
}
@@ -135,82 +138,71 @@ sub report_moderate_hide : Private {
}
}
-sub report_moderate_title : Private {
- my ( $self, $c ) = @_;
-
- my $problem = $c->stash->{problem} or die;
- my $original = $c->stash->{problem_original};
+sub moderate_text : Private {
+ my ($self, $c, $thing) = @_;
+
+ my ($object, $original, $param);
+ my $thing_for_original_table = $thing;
+ if (my $comment = $c->stash->{comment}) {
+ $object = $comment;
+ $original = $c->stash->{comment_original};
+ $param = 'update_';
+ # Update 'text' field is stored in original table's 'detail' field
+ $thing_for_original_table = 'detail' if $thing eq 'text';
+ } else {
+ $object = $c->stash->{problem};
+ $original = $c->stash->{problem_original};
+ $param = 'problem_';
+ }
- my $old_title = $problem->title;
- my $original_title = $original->title;
+ my $old = $object->$thing;
+ my $original_thing = $original->$thing_for_original_table;
- my $title = $c->get_param('problem_revert_title') ?
- $original_title
- : $c->get_param('problem_title');
+ my $new = $c->get_param($param . 'revert_' . $thing) ?
+ $original_thing
+ : $c->get_param($param . $thing);
- if ($title ne $old_title) {
+ if ($new ne $old) {
$original->insert unless $original->in_storage;
- $problem->update({ title => $title });
- return 'title';
+ $object->update({ $thing => $new });
+ return $thing_for_original_table;
}
return;
}
-sub report_moderate_detail : Private {
- my ( $self, $c ) = @_;
-
- my $problem = $c->stash->{problem} or die;
- my $original = $c->stash->{problem_original};
-
- my $old_detail = $problem->detail;
- my $original_detail = $original->detail;
- my $detail = $c->get_param('problem_revert_detail') ?
- $original_detail
- : $c->get_param('problem_detail');
-
- if ($detail ne $old_detail) {
- $original->insert unless $original->in_storage;
- $problem->update({ detail => $detail });
- return 'detail';
+sub moderate_boolean : Private {
+ my ( $self, $c, $thing, $reverse ) = @_;
+
+ my ($object, $original, $param);
+ if (my $comment = $c->stash->{comment}) {
+ $object = $comment;
+ $original = $c->stash->{comment_original};
+ $param = 'update_';
+ } else {
+ $object = $c->stash->{problem};
+ $original = $c->stash->{problem_original};
+ $param = 'problem_';
}
- return;
-}
-
-sub report_moderate_anon : Private {
- my ( $self, $c ) = @_;
-
- my $problem = $c->stash->{problem} or die;
- my $original = $c->stash->{problem_original};
-
- my $show_user = $c->get_param('problem_show_name') ? 1 : 0;
- my $anonymous = $show_user ? 0 : 1;
- my $old_anonymous = $problem->anonymous ? 1 : 0;
- if ($anonymous != $old_anonymous) {
+ return if $thing eq 'photo' && !$original->photo;
- $original->insert unless $original->in_storage;
- $problem->update({ anonymous => $anonymous });
- return 'anonymous';
+ my $new;
+ if ($reverse) {
+ $new = $c->get_param($param . $reverse) ? 0 : 1;
+ } else {
+ $new = $c->get_param($param . $thing) ? 1 : 0;
}
- return;
-}
-
-sub report_moderate_photo : Private {
- my ( $self, $c ) = @_;
-
- my $problem = $c->stash->{problem} or die;
- my $original = $c->stash->{problem_original};
-
- return unless $original->photo;
+ my $old = $object->$thing ? 1 : 0;
- my $show_photo = $c->get_param('problem_show_photo') ? 1 : 0;
- my $old_show_photo = $problem->photo ? 1 : 0;
-
- if ($show_photo != $old_show_photo) {
+ if ($new != $old) {
$original->insert unless $original->in_storage;
- $problem->update({ photo => $show_photo ? $original->photo : undef });
- return 'photo';
+ if ($thing eq 'photo') {
+ $object->update({ $thing => $new ? $original->photo : undef });
+ } else {
+ $object->update({ $thing => $new });
+ }
+ return $thing;
}
return;
}
@@ -219,6 +211,9 @@ sub update : Chained('report') : PathPart('update') : CaptureArgs(1) {
my ($self, $c, $id) = @_;
my $comment = $c->stash->{problem}->comments->find($id);
+ # Make sure user can moderate this update
+ $c->detach unless $comment && $c->user->can_moderate($comment);
+
my $original = $comment->find_or_new_related( moderation_original_data => {
detail => $comment->text,
photo => $comment->photo,
@@ -234,9 +229,9 @@ sub moderate_update : Chained('update') : PathPart('') : Args(0) {
$c->forward('update_moderate_hide');
my @types = grep $_,
- $c->forward('update_moderate_detail'),
- $c->forward('update_moderate_anon'),
- $c->forward('update_moderate_photo');
+ $c->forward('moderate_text', [ 'text' ]),
+ $c->forward('moderate_boolean', [ 'anonymous', 'show_name' ]),
+ $c->forward('moderate_boolean', [ 'photo' ]);
$c->detach( 'update_moderate_audit', \@types )
}
@@ -274,72 +269,6 @@ sub update_moderate_hide : Private {
return;
}
-sub update_moderate_detail : Private {
- my ( $self, $c ) = @_;
-
- my $problem = $c->stash->{problem} or die;
- my $comment = $c->stash->{comment} or die;
- my $original = $c->stash->{comment_original};
-
- my $old_detail = $comment->text;
- my $original_detail = $original->detail;
- my $detail = $c->get_param('update_revert_detail') ?
- $original_detail
- : $c->get_param('update_detail');
-
- if ($detail ne $old_detail) {
- $original->insert unless $original->in_storage;
- $comment->update({ text => $detail });
- return 'detail';
- }
- return;
-}
-
-sub update_moderate_anon : Private {
- my ( $self, $c ) = @_;
-
- my $problem = $c->stash->{problem} or die;
- my $comment = $c->stash->{comment} or die;
- my $original = $c->stash->{comment_original};
-
- my $show_user = $c->get_param('update_show_name') ? 1 : 0;
- my $anonymous = $show_user ? 0 : 1;
- my $old_anonymous = $comment->anonymous ? 1 : 0;
-
- if ($anonymous != $old_anonymous) {
- $original->insert unless $original->in_storage;
- $comment->update({ anonymous => $anonymous });
- return 'anonymous';
- }
- return;
-}
-
-sub update_moderate_photo : Private {
- my ( $self, $c ) = @_;
-
- my $problem = $c->stash->{problem} or die;
- my $comment = $c->stash->{comment} or die;
- my $original = $c->stash->{comment_original};
-
- return unless $original->photo;
-
- my $show_photo = $c->get_param('update_show_photo') ? 1 : 0;
- my $old_show_photo = $comment->photo ? 1 : 0;
-
- if ($show_photo != $old_show_photo) {
- $original->insert unless $original->in_storage;
- $comment->update({ photo => $show_photo ? $original->photo : undef });
- return 'photo';
- }
-}
-
-sub return_text : Private {
- my ($self, $c, $text) = @_;
-
- $c->res->content_type('text/plain; charset=utf-8');
- $c->res->body( $text // '' );
-}
-
__PACKAGE__->meta->make_immutable;
1;
diff --git a/perllib/FixMyStreet/App/Controller/Questionnaire.pm b/perllib/FixMyStreet/App/Controller/Questionnaire.pm
index 696529660..d2b0bf3f4 100755
--- a/perllib/FixMyStreet/App/Controller/Questionnaire.pm
+++ b/perllib/FixMyStreet/App/Controller/Questionnaire.pm
@@ -65,15 +65,8 @@ sub submit : Path('submit') {
my ( $self, $c ) = @_;
if (my $token = $c->get_param('token')) {
- if ($token eq '_test_') {
- $c->stash->{been_fixed} = $c->get_param('been_fixed');
- $c->stash->{new_state} = $c->get_param('new_state');
- $c->stash->{template} = 'questionnaire/completed.html';
- return;
- }
$c->forward('submit_standard');
} elsif (my $p = $c->get_param('problem')) {
- $c->detach('creator_fixed') if $p eq '_test_';
$c->forward('submit_creator_fixed');
} else {
$c->detach( '/page_error_404_not_found' );
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index e285687bc..854dbf3ea 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -140,7 +140,7 @@ sub load_problem_or_display_error : Private {
}
$c->stash->{problem} = $problem;
- if ( $c->user_exists && $c->user->has_permission_to(moderate => $problem->bodies_str_ids) ) {
+ if ( $c->user_exists && $c->user->can_moderate($problem) ) {
$c->stash->{problem_original} = $problem->find_or_new_related(
moderation_original_data => {
title => $problem->title,
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 376d4a000..8fadbac88 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -118,12 +118,6 @@ sub report_new : Path : Args(0) {
$c->forward('redirect_or_confirm_creation');
}
-sub report_new_test : Path('_test_') : Args(0) {
- my ( $self, $c ) = @_;
- $c->stash->{template} = 'email_sent.html';
- $c->stash->{email_type} = $c->get_param('email_type');
-}
-
# This is for the new phonegap versions of the app. It looks a lot like
# report_new but there's a few workflow differences as we only ever want
# to sent JSON back here
@@ -264,14 +258,8 @@ sub by_category_ajax_data : Private {
my ($self, $c, $type, $category) = @_;
my $generate;
- if ( $c->stash->{category_extras}->{$category} && @{ $c->stash->{category_extras}->{$category} } >= 1 ) {
- $c->stash->{category_extras} = { $category => $c->stash->{category_extras}->{$category} };
- $generate = 1;
- }
- if ($c->stash->{unresponsive}->{$category}) {
- $generate = 1;
- }
- if ($c->stash->{report_extra_fields}) {
+ if (($c->stash->{category_extras}->{$category} && @{ $c->stash->{category_extras}->{$category} } >= 1) or \
+ $c->stash->{unresponsive}->{$category} or $c->stash->{report_extra_fields}) {
$generate = 1;
}
diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm
index bb6140e0a..659d763de 100644
--- a/perllib/FixMyStreet/App/Controller/Tokens.pm
+++ b/perllib/FixMyStreet/App/Controller/Tokens.pm
@@ -28,17 +28,6 @@ problem but are not logged in.
sub confirm_problem : Path('/P') {
my ( $self, $c, $token_code ) = @_;
- if ($token_code eq '_test_') {
- $c->stash->{report} = {
- id => 123,
- title => 'Title of Report',
- bodies_str => '1',
- url => '/report/123',
- service => $c->get_param('service'),
- };
- return;
- }
-
my $auth_token =
$c->forward( 'load_auth_token', [ $token_code, 'problem' ] );
@@ -88,11 +77,6 @@ alert but are not logged in.
sub confirm_alert : Path('/A') {
my ( $self, $c, $token_code ) = @_;
- if ($token_code eq '_test_') {
- $c->stash->{confirm_type} = $c->get_param('confirm_type');
- return;
- }
-
my $auth_token = $c->forward( 'load_auth_token', [ $token_code, 'alert' ] );
# Load the alert
@@ -134,16 +118,6 @@ update but are not logged in.
sub confirm_update : Path('/C') {
my ( $self, $c, $token_code ) = @_;
- if ($token_code eq '_test_') {
- $c->stash->{problem} = {
- id => 123,
- title => 'Title of Report',
- bodies_str => '1',
- url => '/report/123',
- };
- return;
- }
-
my $auth_token =
$c->forward( 'load_auth_token', [ $token_code, 'comment' ] );
diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm
index 5ba597f74..5afd9d89c 100644
--- a/perllib/FixMyStreet/DB/Result/User.pm
+++ b/perllib/FixMyStreet/DB/Result/User.pm
@@ -330,6 +330,26 @@ sub split_name {
return { first => $first || '', last => $last || '' };
}
+sub can_moderate {
+ my ($self, $object, %perms) = @_;
+
+ my ($type, $ids);
+ if ($object->isa("FixMyStreet::DB::Result::Comment")) {
+ $type = 'update';
+ $ids = $object->problem->bodies_str_ids;
+ } else {
+ $type = 'problem';
+ $ids = $object->bodies_str_ids;
+ }
+
+ my $staff_perm = exists($perms{staff}) ? $perms{staff} : $self->has_permission_to(moderate => $ids);
+ return 1 if $staff_perm;
+
+ # See if the cobrand wants to allow it in some circumstance
+ my $cobrand = $self->result_source->schema->cobrand;
+ return $cobrand->call_hook('moderate_permission', $self, $type => $object);
+}
+
has body_permissions => (
is => 'ro',
lazy => 1,
@@ -340,13 +360,16 @@ has body_permissions => (
);
sub permissions {
- my ($self, $c, $body_id) = @_;
+ my ($self, $problem) = @_;
+ my $cobrand = $self->result_source->schema->cobrand;
if ($self->is_superuser) {
- my $perms = $c->cobrand->available_permissions;
+ my $perms = $cobrand->available_permissions;
return { map { %$_ } values %$perms };
}
+ my $body_id = $problem->bodies_str;
+
return unless $self->belongs_to_body($body_id);
my @permissions = grep { $_->body_id == $self->from_body->id } @{$self->body_permissions};
diff --git a/perllib/FixMyStreet/Template.pm b/perllib/FixMyStreet/Template.pm
index 4a9cffecb..9c565114b 100644
--- a/perllib/FixMyStreet/Template.pm
+++ b/perllib/FixMyStreet/Template.pm
@@ -114,7 +114,7 @@ into <br>s too.
sub html_paragraph : Filter('html_para') {
my $text = shift;
my @paras = split(/(?:\r?\n){2,}/, $text);
- s/\r?\n/<br>\n/ for @paras;
+ s/\r?\n/<br>\n/g for @paras;
$text = "<p>\n" . join("\n</p>\n\n<p>\n", @paras) . "</p>\n";
return $text;
}
diff --git a/t/app/controller/develop.t b/t/app/controller/develop.t
index 92aa86721..8f26a9f16 100644
--- a/t/app/controller/develop.t
+++ b/t/app/controller/develop.t
@@ -6,14 +6,20 @@ my ($problem) = $mech->create_problems_for_body(1, 2504, 'title');
my $update = $mech->create_comment_for_problem($problem, $problem->user, 'Name', 'Text', 'f', 'confirmed', 'confirmed');
subtest 'not visible on live site' => sub {
- FixMyStreet::override_config {
- STAGING_SITE => 0
- }, sub {
- $mech->get('/_dev/email/');
- is $mech->res->code, 404;
- $mech->get('/_dev/email/login');
- is $mech->res->code, 404;
- };
+ $mech->get('/_dev/');
+ is $mech->res->code, 404;
+ $mech->get('/_dev/email');
+ is $mech->res->code, 404;
+ $mech->get('/_dev/email/login');
+ is $mech->res->code, 404;
+};
+
+$problem->user->update({ is_superuser => 1 });
+$mech->log_in_ok($problem->user->email);
+
+subtest 'dev index' => sub {
+ $mech->get_ok('/_dev/');
+ $mech->content_contains('<h1>/_dev</h1>');
};
subtest 'dev email index page' => sub {
@@ -30,4 +36,27 @@ subtest 'individual email previews' => sub {
$mech->get_ok('/_dev/email/update-confirm?update=' . $update->id);
};
+subtest 'problem confirmation page preview' => sub {
+ $mech->get_ok('/_dev/confirm_problem/' . $problem->id );
+};
+
+subtest 'update confirmation page preview' => sub {
+ $mech->get_ok('/_dev/confirm_update/' . $problem->id);
+};
+
+subtest 'alert confirmation page preview' => sub {
+ $mech->get_ok('/_dev/confirm_alert/subscribe');
+};
+
+subtest 'contact form submission page preview' => sub {
+ $mech->get_ok('/_dev/contact_submit/1');
+};
+
+subtest 'questionnaire completion page previews' => sub {
+ $mech->get_ok('/_dev/questionnaire_completed?been_fixed=Yes');
+ $mech->get_ok('/_dev/questionnaire_completed?been_fixed=Unknown');
+ $mech->get_ok('/_dev/questionnaire_completed?new_state=confirmed');
+ $mech->get_ok('/_dev/questionnaire_creator_fixed');
+};
+
done_testing();
diff --git a/t/app/controller/moderate.t b/t/app/controller/moderate.t
index 4b2f0cfe3..c2ac3ad5a 100644
--- a/t/app/controller/moderate.t
+++ b/t/app/controller/moderate.t
@@ -86,7 +86,7 @@ subtest 'Auth' => sub {
my %problem_prepopulated = (
problem_show_name => 1,
- problem_show_photo => 1,
+ problem_photo => 1,
problem_title => 'Good bad good',
problem_detail => 'Good bad bad bad good bad',
);
@@ -146,7 +146,7 @@ subtest 'Problem moderation' => sub {
$mech->submit_form_ok({ with_fields => {
%problem_prepopulated,
- problem_show_photo => 0,
+ problem_photo => 0,
}});
$mech->base_like( qr{\Q$REPORT_URL\E} );
@@ -154,7 +154,7 @@ subtest 'Problem moderation' => sub {
$mech->submit_form_ok({ with_fields => {
%problem_prepopulated,
- problem_show_photo => 1,
+ problem_photo => 1,
}});
$mech->base_like( qr{\Q$REPORT_URL\E} );
@@ -251,8 +251,8 @@ sub create_update {
}
my %update_prepopulated = (
update_show_name => 1,
- update_show_photo => 1,
- update_detail => 'update good good bad good',
+ update_photo => 1,
+ update_text => 'update good good bad good',
);
my $update = create_update();
@@ -263,7 +263,7 @@ subtest 'updates' => sub {
$mech->get_ok($REPORT_URL);
$mech->submit_form_ok({ with_fields => {
%update_prepopulated,
- update_detail => 'update good good good',
+ update_text => 'update good good good',
}}) or die $mech->content;
$mech->base_like( qr{\Q$REPORT_URL\E} );
@@ -274,7 +274,7 @@ subtest 'updates' => sub {
subtest 'Revert text' => sub {
$mech->submit_form_ok({ with_fields => {
%update_prepopulated,
- update_revert_detail => 1,
+ update_revert_text => 1,
}});
$mech->base_like( qr{\Q$REPORT_URL\E} );
@@ -314,7 +314,7 @@ subtest 'updates' => sub {
$mech->submit_form_ok({ with_fields => {
%update_prepopulated,
- update_show_photo => 0,
+ update_photo => 0,
}});
$mech->base_like( qr{\Q$REPORT_URL\E} );
@@ -322,7 +322,7 @@ subtest 'updates' => sub {
$mech->submit_form_ok({ with_fields => {
%update_prepopulated,
- update_show_photo => 1,
+ update_photo => 1,
}});
$mech->base_like( qr{\Q$REPORT_URL\E} );
@@ -348,7 +348,7 @@ subtest 'Update 2' => sub {
$mech->get_ok($REPORT_URL);
$mech->submit_form_ok({ with_fields => {
%update_prepopulated,
- update_detail => 'update good good good',
+ update_text => 'update good good good',
}}) or die $mech->content;
$update2->discard_changes;
diff --git a/templates/web/base/develop/index.html b/templates/web/base/develop/index.html
new file mode 100755
index 000000000..56af2319d
--- /dev/null
+++ b/templates/web/base/develop/index.html
@@ -0,0 +1,41 @@
+[% INCLUDE 'header.html' %]
+
+<h1>/_dev</h1>
+
+<p><a href="/_dev/email">Email previews</a></p>
+
+<dl>
+
+<dt>Questionnaire responses</dt>
+<dd><ul>
+<li><a href="/_dev/questionnaire_completed?been_fixed=Yes">Yes</a>
+<li><a href="/_dev/questionnaire_completed?new_state=confirmed">No</a>
+<li><a href="/_dev/questionnaire_completed?been_fixed=Unknown">Unknown</a>
+<li><a href="/_dev/questionnaire_creator_fixed">Mark fixed as part of update by reporter</a>
+</ul></dd>
+
+<dt>Confirmation pages</dt>
+<dd><ul>
+<li><a href="/_dev/confirm_problem/[% problem.id %]">Problem</a>
+<li><a href="/_dev/confirm_update/[% problem.id %]">Update</a>
+<li><a href="/_dev/confirm_alert/subscribe">Alert</a>
+<li><a href="/_dev/confirm_alert/unsubscribe">Alert unsubscribe</a>
+</ul></dd>
+
+<dt>Contact form</dt>
+<dd><ul>
+<li><a href="/_dev/contact_submit/1">Success</a>
+<li><a href="/_dev/contact_submit/0">Failure</a>
+</ul></dd>
+
+<dt>Check email pages</dt>
+<dd><ul>
+<li><a href="/_dev/auth">Auth</a>
+ <li><a href="/_dev/report_new?email_type=problem">New report</a>
+ <li><a href="/_dev/report_new?email_type=update">New update</a>
+ <li><a href="/_dev/report_new?email_type=alert">New alert</a>
+</ul></dd>
+
+</dl>
+
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html
index adb56190d..e5094d02e 100644
--- a/templates/web/base/report/_inspect.html
+++ b/templates/web/base/report/_inspect.html
@@ -1,4 +1,4 @@
-[% permissions = c.user.permissions(c, problem.bodies_str) %]
+[% permissions = c.user.permissions(problem) %]
[% second_column = BLOCK -%]
<div id="side-inspect">
diff --git a/templates/web/base/report/_main.html b/templates/web/base/report/_main.html
index fe0fe74d5..1e427fd86 100644
--- a/templates/web/base/report/_main.html
+++ b/templates/web/base/report/_main.html
@@ -1,3 +1,5 @@
+[% can_moderate = permissions.moderate OR c.user.can_moderate(problem, staff = permissions.moderate) %]
+
<a href="[% c.uri_for( '/around', { lat => latitude, lon => longitude } ) %]"
class="problem-back js-back-to-report-list">[% loc('Back to all reports') %]</a>
@@ -30,7 +32,7 @@
</form>
[% END %]
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
[% original = problem_original %]
<form method="post" action="/moderate/report/[% problem.id %]">
<input type="hidden" name="token" value="[% csrf_token %]">
@@ -38,7 +40,7 @@
<h1 class="moderate-display">[% problem.title | html %]</h1>
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
<div class="moderate-edit">
[% IF problem.title != original.title %]
<label>
@@ -71,11 +73,11 @@
[% INCLUDE 'report/_support.html' %]
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
[% IF problem.photo or original.photo %]
<p class="moderate-edit">
<label>
- <input type="checkbox" name="problem_show_photo" [% problem.photo ? 'checked' : '' %]>
+ <input type="checkbox" name="problem_photo" [% problem.photo ? 'checked' : '' %]>
[% loc('Show photo') %]
</label>
</p>
@@ -87,7 +89,7 @@
[% problem.detail | add_links | html_para %]
</div>
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
<p class="moderate-edit">
[% IF problem.detail != original.detail %]
<label>
@@ -116,13 +118,13 @@
</div>
[% END %]
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
</form>
[% END %]
- [% IF permissions.moderate OR permissions.planned_reports %]
+ [% IF can_moderate OR permissions.planned_reports %]
<div class="moderate-display segmented-control" role="menu">
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
<a class="js-moderate btn" role="menuitem" aria-label="[% loc('Moderate this report') %]">[% loc('Moderate') %]</a>
[% END %]
[% IF permissions.planned_reports %]
diff --git a/templates/web/base/report/display.html b/templates/web/base/report/display.html
index ebe969994..eedbc4f85 100644
--- a/templates/web/base/report/display.html
+++ b/templates/web/base/report/display.html
@@ -40,7 +40,7 @@
[% INCLUDE 'report/banner.html' %]
[% IF c.user_exists %]
- [% DEFAULT permissions = c.user.permissions(c, problem.bodies_str) %]
+ [% DEFAULT permissions = c.user.permissions(problem) %]
[%- END %]
[% INCLUDE 'report/_main.html' %]
diff --git a/templates/web/base/report/update.html b/templates/web/base/report/update.html
index 4a2642c9a..d8b3b5629 100644
--- a/templates/web/base/report/update.html
+++ b/templates/web/base/report/update.html
@@ -1,3 +1,4 @@
+[% can_moderate = NOT update.whenanswered AND (permissions.moderate OR c.user.can_moderate(update, staff = permissions.moderate)) %]
[% IF loop.first %]
<section class="full-width">
<h4 class="static-with-rule">[% loc('Updates') %]</h4>
@@ -5,7 +6,7 @@
[% END %]
<li class="item-list__item item-list__item--updates">
<a name="update_[% update.id %]" class="internal-link-fixed-header"></a>
- [% IF permissions.moderate; original_update = update.moderation_original_data %]
+ [% IF can_moderate; original_update = update.moderation_original_data %]
<form method="post" action="/moderate/report/[% problem.id %]/update/[% update.id %]">
<input type="hidden" name="token" value="[% csrf_token %]">
<input type="button" class="btn js-moderate moderate-display" value="[% loc('Moderate this update') %]">
@@ -15,7 +16,7 @@
<label><input type="checkbox" name="update_show_name" [% update.anonymous ? '' : 'checked' %]>
[% loc('Show name publicly?') %]</label>
[% IF update.photo or original_update.photo %]
- <label><input type="checkbox" name="update_show_photo" [% update.photo ? 'checked' : '' %]>
+ <label><input type="checkbox" name="update_photo" [% update.photo ? 'checked' : '' %]>
[% loc('Show Photo?') %]</label>
[% END %]
</div>
@@ -31,13 +32,13 @@
<div class="moderate-display">
[% update.text | add_links | markup(update.user) | html_para %]
</div>
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
<div class="moderate-edit">
[% IF update.text != original.detail %]
- <label><input type="checkbox" name="update_revert_detail" class="revert-textarea">
+ <label><input type="checkbox" name="update_revert_text" class="revert-textarea">
[% loc('Revert to original') %]</label>
[% END %]
- <textarea class="form-control" name="update_detail">[% update.text | add_links %]</textarea>
+ <textarea class="form-control" name="update_text">[% update.text | add_links %]</textarea>
</div>
[% END %]
@@ -45,7 +46,7 @@
</div>
[% END %]
</div>
- [% IF permissions.moderate %]
+ [% IF can_moderate %]
<div class="moderate-edit">
<label for="moderation_reason">[% loc('Describe why you are moderating this') %]</label>
<input type="text" class="form-control" name="moderation_reason">
diff --git a/web/cobrands/bristol/assets.js b/web/cobrands/bristol/assets.js
index 1b32379dc..b886c1f2b 100644
--- a/web/cobrands/bristol/assets.js
+++ b/web/cobrands/bristol/assets.js
@@ -14,7 +14,7 @@ var options = {
min_resolution: 0.00001,
asset_id_field: 'COD_ASSET_ID',
asset_type: 'spot',
- propertyNames: [ 'COD_ASSET_ID', 'COD_USRN' ],
+ propertyNames: [ 'COD_ASSET_ID', 'COD_USRN', 'COD_ASSET_TYPE' ],
attributes: {
asset_id: 'COD_ASSET_ID',
usrn: 'COD_USRN'
diff --git a/web/cobrands/fixmystreet/assets.js b/web/cobrands/fixmystreet/assets.js
index c27d19211..2cd93eb33 100644
--- a/web/cobrands/fixmystreet/assets.js
+++ b/web/cobrands/fixmystreet/assets.js
@@ -54,7 +54,7 @@ OpenLayers.Layer.VectorAsset = OpenLayers.Class(OpenLayers.Layer.Vector, {
// This function is called when the asset category is
// selected, and will zoom the map in to the first level that
// makes the asset layer visible if it's not already shown.
- if (!this.inRange) {
+ if (!this.inRange && this.resolutions) {
var firstVisibleResolution = this.resolutions[0];
var zoomLevel = fixmystreet.map.getZoomForResolution(firstVisibleResolution);
fixmystreet.map.zoomTo(zoomLevel);
@@ -522,6 +522,8 @@ fixmystreet.assets = {
layer_options.projection = new OpenLayers.Projection(fixmystreet.wmts_config.map_projection);
}
if (options.filter_key) {
+ // Add this filter to the layer, so it can potentially be used
+ // in the request (though only Bristol currently does this).
if (OpenLayers.Util.isArray(options.filter_value)) {
layer_options.filter = new OpenLayers.Filter.FeatureId({
type: OpenLayers.Filter.Function,
@@ -540,8 +542,10 @@ fixmystreet.assets = {
property: options.filter_key,
value: options.filter_value
});
- layer_options.strategies.push(new OpenLayers.Strategy.Filter({filter: layer_options.filter}));
}
+ // Add a strategy filter to the layer, to filter the incoming results
+ // after they are received. Bristol does not need this, but has to ask
+ // for the filter data in its response so it doesn't then disappear.
layer_options.strategies.push(new OpenLayers.Strategy.Filter({filter: layer_options.filter}));
}
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js
index a5267d4f2..b663d37ba 100644
--- a/web/cobrands/fixmystreet/fixmystreet.js
+++ b/web/cobrands/fixmystreet/fixmystreet.js
@@ -391,7 +391,9 @@ $.extend(fixmystreet.set_up, {
// /report/new), fetch it first. That will then automatically call this
// function again, due to it calling change() on the category if set.
if (!fixmystreet.reporting_data) {
- fixmystreet.update_pin(fixmystreet.map.getCenter(), false);
+ if (fixmystreet.map) {
+ fixmystreet.update_pin(fixmystreet.map.getCenter(), false);
+ }
return;
}
@@ -399,6 +401,11 @@ $.extend(fixmystreet.set_up, {
data = fixmystreet.reporting_data.by_category[category],
$category_meta = $('#category_meta');
+ if (!data) {
+ // The Pick a category option, or something gone wrong
+ return;
+ }
+
fixmystreet.bodies = data.bodies || [];
if (fixmystreet.body_overrides) {
fixmystreet.body_overrides.clear();