diff options
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(); |