diff options
author | Hakim Cassimally <hakim@mysociety.org> | 2015-02-27 15:34:24 +0000 |
---|---|---|
committer | Dave Arter <davea@mysociety.org> | 2015-10-06 09:09:24 +0100 |
commit | e7c35a0de101331436b1d5d6f2346a8c85233737 (patch) | |
tree | 895462731355a23da52c5d504c59871c9342a426 | |
parent | 34f3f5a36b6773ad51060256d4e95efd7c45b3c8 (diff) |
[Zurich] admin workflow changes available states
See mysociety/FixMyStreet-Commercial#690
- external_message for Extern/Wunsch states
- as per PDF mockups, the "public response" field is now editable
from more states, namely:
- Ruckmeldung ausstehend
- Extern
- Zustandigkeit unbekannt
- Wunsch
- Nicht kontaktierbar
- Unsichtbar
- test fixes
NB: slight hack to reorder Zurich report_display tests
As these remove 'investigating' from hidden_states, these break
tests for default cobrand (the FMS::App code expects to be called
once on request start, not multiple times within one test)
- Not contactable button
and tests for this and the similar `send_back` functionality
(previously untested).
NB: Unsure why we need to specify form_number=2 just for these submits,
but test won't find the buttons otherwise.
- Make sure SDMs' "time_spent" figure is recorded.
(See mysociety/FixMyStreet-Commercial#718)
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Zurich.pm | 153 | ||||
-rw-r--r-- | perllib/FixMyStreet/DB/ResultSet/Problem.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/SendReport/Zurich.pm | 3 | ||||
-rw-r--r-- | t/app/controller/report_display.t | 140 | ||||
-rw-r--r-- | t/cobrand/zurich.t | 294 | ||||
-rw-r--r-- | templates/email/zurich/problem-wish.txt | 26 | ||||
-rw-r--r-- | templates/email/zurich/submit-external-personal.txt | 2 | ||||
-rw-r--r-- | templates/email/zurich/submit-external-wish.txt | 22 | ||||
-rw-r--r-- | templates/email/zurich/submit-external.txt | 2 | ||||
-rw-r--r-- | templates/web/zurich/admin/report_edit-sdm.html | 1 | ||||
-rw-r--r-- | templates/web/zurich/admin/report_edit.html | 192 |
11 files changed, 558 insertions, 279 deletions
diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index c08d48161..908550adb 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -545,10 +545,23 @@ sub admin_report_edit { my $internal_note_text = ""; # Workflow things + # + # Note that 2 types of email may be sent + # 1) _admin_send_email() sends an email to the *user*, if their email is confirmed + # 2) setting $problem->whensent(undef) may make it eligible for generating an email + # to the body (internal or external). See DBRS::Problem->send_reports for Zurich- + # specific categories which are eligible for this. + my $redirect = 0; - my $new_cat = $c->get_param('category'); - if ( $new_cat && $new_cat ne $problem->category ) { - my $cat = $c->model('DB::Contact')->search( { category => $c->get_param('category') } )->first; + my $new_cat = $c->get_param('category') || ''; + my $state = $c->get_param('state') || ''; + + if ( + ($state eq 'confirmed') + && $new_cat + && $new_cat ne $problem->category + ) { + my $cat = $c->model('DB::Contact')->search({ category => $c->get_param('category') } )->first; my $old_cat = $problem->category; $problem->category( $new_cat ); $problem->external_body( undef ); @@ -565,16 +578,40 @@ sub admin_report_edit { $problem->bodies_str( $subdiv ); $problem->whensent( undef ); $redirect = 1; - } elsif ( my $external = $c->get_param('body_external') ) { + } elsif ( my $external = $c->get_param('body_external') and $state =~/^(closed|investigating)$/) { + # Extern | Wish + my $external_body = $c->model('DB::Body')->find($external) + or die "Body $external not found"; $problem->set_extra_metadata_if_undefined( moderated_overdue => $self->overdue( $problem ) ); - $self->set_problem_state($c, $problem, 'closed'); $problem->set_extra_metadata_if_undefined( closed_overdue => $self->overdue( $problem ) ); $problem->external_body( $external ); $problem->whensent( undef ); - _admin_send_email( $c, 'problem-external.txt', $problem ); + $self->set_problem_state($c, $problem, $state); + if ( my $external_message = $c->req->params->{external_message} ) { + $problem->add_to_comments( { + text => ( + sprintf '(%s %s) %s', + $state eq 'closed' ? + _('Forwarded to external body') : + _('Forwarded wish to external body'), + $external_body->name, + $external_message, + ), + user => $c->user->obj, + state => 'hidden', # seems best fit, should not be shown publicly + mark_fixed => 0, + anonymous => 1, + extra => { is_internal_note => 1, is_external_message => 1 }, + } ); + # set the external_message in extra, so that it will be picked up + # later by send-reports + $problem->set_extra_metadata( external_message => $external_message ); + } + my $template = ($state eq 'investigating') ? 'problem-wish.txt' : 'problem-external.txt'; + _admin_send_email( $c, $template, $problem ); $redirect = 1; } else { - if (my $state = $c->get_param('state')) { + if ($state) { if ($problem->state eq 'unconfirmed' and $state ne 'unconfirmed') { # only set this for the first state change @@ -606,6 +643,7 @@ sub admin_report_edit { _admin_send_email( $c, 'problem-closed.txt', $problem ); } } + $c->stash->{default_public_response} = "\nFreundliche Grüsse\n\nIhre Stadt Zürich\n"; $problem->lastupdate( \'current_timestamp' ); $problem->update; @@ -642,6 +680,7 @@ sub admin_report_edit { ->search( { problem_id => $problem->id }, { order_by => 'created' } ) ->all ]; + $self->stash_states($problem); return 1; } @@ -650,15 +689,26 @@ sub admin_report_edit { # Has cut-down edit template for adding update and sending back up only $c->stash->{template} = 'admin/report_edit-sdm.html'; - if ($c->get_param('send_back')) { + if ($c->get_param('send_back') or $c->get_param('not_contactable')) { + # SDM can send back a report either to be assigned to a different + # subdivision, or because the customer was not contactable. + # We handle these in the same way but with different statuses. + $c->forward('check_token'); + my $not_contactable = $c->req->param('not_contactable'); + $problem->bodies_str( $body->parent->id ); - $self->set_problem_state($c, $problem, 'confirmed'); + my $new_state = $not_contactable ? 'partial' : 'confirmed'; + $self->set_problem_state($c, $problem, $new_state); $problem->update; - # log here + $c->forward( 'log_edit', [ $problem->id, 'problem', + $not_contactable ? + _('Customer not contactable') + : _('Sent report back') ] ); + # Make sure the problem's time_spent is updated + $self->update_admin_log($c, $problem); $c->res->redirect( '/admin/summary' ); - } elsif ($c->get_param('submit')) { $c->forward('check_token'); @@ -683,6 +733,8 @@ sub admin_report_edit { anonymous => 1, } ); } + # Make sure the problem's time_spent is updated + $self->update_admin_log($c, $problem); $c->stash->{status_message} = '<p><em>' . _('Updated!') . '</em></p>'; @@ -701,14 +753,93 @@ sub admin_report_edit { ->search( { problem_id => $problem->id }, { order_by => 'created' } ) ->all ]; + $self->stash_states($problem); return 1; } + $self->stash_states($problem); return 0; } +sub stash_states { + my ($self, $problem) = @_; + my $c = $self->{c}; + + # current problem state affects which states are visible in dropdowns + my @states = ( + { + # Erfasst + state => 'unconfirmed', + trans => _('Submitted'), + unconfirmed => 1, + hidden => 1, + }, + { + # Aufgenommen + state => 'confirmed', + trans => _('Open'), + unconfirmed => 1, + }, + { + # Rueckmeldung ausstehend + state => 'planned', + trans => _('Planned'), + }, + { + # Unsichtbar (hidden) + state => 'hidden', + trans => _('Hidden'), + unconfirmed => 1, + hidden => 1, + }, + { + # Extern + state => 'closed', + trans => _('Extern'), + }, + { + # Zustaendigkeit unbekannt + state => 'unable to fix', + trans => _('Jurisdiction unknown'), + }, + { + # Wunsch (hidden) + state => 'investigating', + trans => _('Wish'), + }, + { + # Nicht kontaktierbar (hidden) + state => 'partial', + trans => _('Not contactable'), + }, + ); + my $state = $problem->state; + if ($state eq 'in progress') { + push @states, { + state => 'in progress', + trans => _('In progress'), + }; + } + elsif ($state eq 'fixed - council') { + push @states, { + state => 'fixed - council', + trans => _('Closed'), + }; + } + elsif ($state =~/^(hidden|unconfirmed)$/) { + @states = grep { $_->{$state} } @states; + } + $c->stash->{states} = \@states; +} + +=head2 _admin_send_email + +Send an email to the B<user> who logged the problem, if their email address is confirmed. + +=cut + sub _admin_send_email { my ( $c, $template, $problem ) = @_; diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index f01ddfff1..40076d374 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -247,7 +247,7 @@ sub send_reports { my $site = $site_override || CronFns::site($base_url); my $states = [ 'confirmed', 'fixed' ]; - $states = [ 'unconfirmed', 'confirmed', 'in progress', 'planned', 'closed' ] if $site eq 'zurich'; + $states = [ 'unconfirmed', 'confirmed', 'in progress', 'planned', 'closed', 'investigating' ] if $site eq 'zurich'; my $unsent = $rs->search( { state => $states, whensent => undef, diff --git a/perllib/FixMyStreet/SendReport/Zurich.pm b/perllib/FixMyStreet/SendReport/Zurich.pm index 40417b41e..a5df06ee3 100644 --- a/perllib/FixMyStreet/SendReport/Zurich.pm +++ b/perllib/FixMyStreet/SendReport/Zurich.pm @@ -12,6 +12,7 @@ sub build_recipient_list { if ( $row->external_body ) { $body = FixMyStreet::App->model("DB::Body")->find( { id => $row->external_body } ); $h->{bodies_name} = $body->name; + $h->{external_message} = $row->get_extra_metadata('external_message') || ''; } my $body_email = $body->endpoint; @@ -39,6 +40,8 @@ sub get_template { $template = 'submit-in-progress.txt'; } elsif ( $row->state eq 'planned' ) { $template = 'submit-feedback-pending.txt'; + } elsif ( $row->state eq 'investigating' ) { + $template = 'submit-external-wish.txt'; } elsif ( $row->state eq 'closed' ) { $template = 'submit-external.txt'; if ( $row->extra->{third_personal} ) { diff --git a/t/app/controller/report_display.t b/t/app/controller/report_display.t index 002cdc1e5..c863f829f 100644 --- a/t/app/controller/report_display.t +++ b/t/app/controller/report_display.t @@ -96,19 +96,6 @@ subtest "change report to unconfirmed and check for 404 status" => sub { }; -subtest "Zurich unconfirmeds are 200" => sub { - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'zurich' ], - }, sub { - $mech->host( 'zurich.example.com' ); - ok $report->update( { state => 'unconfirmed' } ), 'unconfirm report'; - $mech->get_ok("/report/$report_id"); - $mech->content_contains( 'Überprüfung ausstehend' ); - ok $report->update( { state => 'confirmed' } ), 'confirm report again'; - $mech->host( 'www.fixmystreet.com' ); - }; -}; - subtest "change report to hidden and check for 410 status" => sub { ok $report->update( { state => 'hidden' } ), 'hide report'; ok $mech->get("/report/$report_id"), "get '/report/$report_id'"; @@ -400,6 +387,67 @@ for my $test ( }; } +my $body_westminster = $mech->create_body_ok(2504, 'Westminster City Council'); +my $body_camden = $mech->create_body_ok(2505, 'Camden Borough Council'); + +for my $test ( + { + desc => 'no state dropdown if user not from authority', + from_body => undef, + no_state => 1, + report_body => $body_westminster->id, + }, + { + desc => 'state dropdown if user from authority', + from_body => $body_westminster->id, + no_state => 0, + report_body => $body_westminster->id, + }, + { + desc => 'no state dropdown if user not from same body as problem', + from_body => $body_camden->id, + no_state => 1, + report_body => $body_westminster->id, + }, + { + desc => 'state dropdown if user from authority and problem sent to multiple bodies', + from_body => $body_westminster->id, + no_state => 0, + report_body => $body_westminster->id . ',2506', + }, +) { + subtest $test->{desc} => sub { + $mech->log_in_ok( $user->email ); + $user->from_body( $test->{from_body} ); + $user->update; + + $report->discard_changes; + $report->bodies_str( $test->{report_body} ); + $report->update; + + $mech->get_ok("/report/$report_id"); + my $fields = $mech->visible_form_values( 'updateForm' ); + if ( $test->{no_state} ) { + ok !$fields->{state}; + } else { + ok $fields->{state}; + } + }; +} + +subtest "Zurich unconfirmeds are 200" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $mech->host( 'zurich.example.com' ); + ok $report->update( { state => 'unconfirmed' } ), 'unconfirm report'; + $mech->get_ok("/report/$report_id"); + $mech->content_contains( 'Überprüfung ausstehend' ); + ok $report->update( { state => 'confirmed' } ), 'confirm report again'; + $mech->host( 'www.fixmystreet.com' ); + }; +}; + subtest "Zurich banners are displayed correctly" => sub { FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'zurich' ], @@ -428,8 +476,8 @@ subtest "Zurich banners are displayed correctly" => sub { { description => 'closed report', state => 'closed', - banner_id => 'fixed', - banner_text => 'Beantwortet', + banner_id => 'closed', + banner_text => _('Extern'), }, { description => 'in progress report', @@ -443,6 +491,21 @@ subtest "Zurich banners are displayed correctly" => sub { banner_id => 'progress', banner_text => 'In Bearbeitung', }, + { + description => 'planned report', + state => 'planned', + banner_id => 'progress', + banner_text => 'In Bearbeitung', + }, + { + description => 'jurisdiction unknown', + state => 'unable to fix', + banner_id => 'fixed', + # We can't use _('Jurisdiction Unknown') here because + # TestMech::extract_problem_banner decodes the HTML entities before + # the string is passed back. + banner_text => 'Zust\x{e4}ndigkeit unbekannt', + }, ) { subtest "banner for $test->{description}" => sub { $report->state( $test->{state} ); @@ -470,53 +533,6 @@ subtest "Zurich banners are displayed correctly" => sub { }; }; -my $body_westminster = $mech->create_body_ok(2504, 'Westminster City Council'); -my $body_camden = $mech->create_body_ok(2505, 'Camden Borough Council'); - -for my $test ( - { - desc => 'no state dropdown if user not from authority', - from_body => undef, - no_state => 1, - report_body => $body_westminster->id, - }, - { - desc => 'state dropdown if user from authority', - from_body => $body_westminster->id, - no_state => 0, - report_body => $body_westminster->id, - }, - { - desc => 'no state dropdown if user not from same body as problem', - from_body => $body_camden->id, - no_state => 1, - report_body => $body_westminster->id, - }, - { - desc => 'state dropdown if user from authority and problem sent to multiple bodies', - from_body => $body_westminster->id, - no_state => 0, - report_body => $body_westminster->id . ',2506', - }, -) { - subtest $test->{desc} => sub { - $mech->log_in_ok( $user->email ); - $user->from_body( $test->{from_body} ); - $user->update; - - $report->discard_changes; - $report->bodies_str( $test->{report_body} ); - $report->update; - - $mech->get_ok("/report/$report_id"); - my $fields = $mech->visible_form_values( 'updateForm' ); - if ( $test->{no_state} ) { - ok !$fields->{state}; - } else { - ok $fields->{state}; - } - }; -} END { $mech->delete_user('test@example.com'); diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index 5dbbc3bbe..1b059e192 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -28,7 +28,9 @@ ok $sample_file->exists, "sample file $sample_file exists"; my $sample_photo = $sample_file->slurp_raw; # This is a helper method that will send the reports but with the config -# correctly set - notably SEND_REPORTS_ON_STAGING needs to be true. +# correctly set - notably SEND_REPORTS_ON_STAGING needs to be true, and +# zurich must be allowed cobrand if we want to be able to call cobrand +# methods on it. sub send_reports_for_zurich { FixMyStreet::override_config { SEND_REPORTS_ON_STAGING => 1, @@ -41,16 +43,13 @@ sub send_reports_for_zurich { sub reset_report_state { my ($report, $created) = @_; $report->discard_changes; - my $extra = $report->extra; - delete $extra->{moderated_overdue}; - delete $extra->{subdiv_overdue}; - delete $extra->{closed_overdue}; + $report->unset_extra_metadata('moderated_overdue'); + $report->unset_extra_metadata('subdiv_overdue'); + $report->unset_extra_metadata('closed_overdue'); $report->whensent(undef); - $report->update({ - extra => { %$extra }, - state => 'unconfirmed', - $created ? ( created => $created ) : (), - }); + $report->state('unconfirmed'); + $report->created($created) if $created; + $report->update; } use FixMyStreet::TestMech; @@ -158,6 +157,9 @@ subtest "changing of categories" => sub { ); } + # full Categories dropdown is hidden for unconfirmed reports + $report->update({ state => 'confirmed' }); + # put report into known category my $original_category = $report->category; $report->update({ category => 'Cat1' }); @@ -213,7 +215,9 @@ sub get_moderated_count { subtest "report_edit" => sub { - ok ( ! exists ${$report->extra}{moderated_overdue}, 'Report currently unmoderated' ); + reset_report_state($report); + + ok ( ! $report->get_extra_metadata('moderated_overdue'), 'Report currently unmoderated' ); is get_moderated_count(), 0; @@ -236,7 +240,7 @@ subtest "report_edit" => sub { $report->discard_changes; - is ( $report->extra->{moderated_overdue}, 0, 'Report now marked moderated' ); + is ( $report->get_extra_metadata('moderated_overdue'), 0, 'Report now marked moderated' ); is get_moderated_count(), 1; @@ -254,7 +258,7 @@ subtest "report_edit" => sub { $mech->get_ok( '/report/' . $report->id ); }; $report->discard_changes; - is ( $report->extra->{moderated_overdue}, 1, 'moderated_overdue set correctly when overdue' ); + is ( $report->get_extra_metadata('moderated_overdue'), 1, 'moderated_overdue set correctly when overdue' ); is get_moderated_count(), 0, 'Moderated count not increased when overdue'; reset_report_state($report, $created); @@ -267,8 +271,8 @@ subtest "report_edit" => sub { $mech->get_ok( '/report/' . $report->id ); }; $report->discard_changes; - is ( $report->extra->{moderated_overdue}, 0, 'Marking confirmed sets moderated_overdue' ); - is ( $report->extra->{closed_overdue}, undef, 'Marking confirmed does NOT set closed_overdue' ); + is ( $report->get_extra_metadata('moderated_overdue'), 0, 'Marking confirmed sets moderated_overdue' ); + is ( $report->get_extra_metadata('closed_overdue'), undef, 'Marking confirmed does NOT set closed_overdue' ); is get_moderated_count(), 1; FixMyStreet::override_config { @@ -279,14 +283,14 @@ subtest "report_edit" => sub { $mech->get_ok( '/admin/report_edit/' . $report->id ); }; $report->discard_changes; - is ( $report->extra->{moderated_overdue}, 0, 'Still marked moderated_overdue' ); - is ( $report->extra->{closed_overdue}, 0, 'Marking hidden also set closed_overdue' ); + is ( $report->get_extra_metadata('moderated_overdue'), 0, 'Still marked moderated_overdue' ); + is ( $report->get_extra_metadata('closed_overdue'), 0, 'Marking hidden also set closed_overdue' ); is get_moderated_count(), 1, 'Check still counted moderated' or diag $report->get_column('extra'); reset_report_state($report); - is ( $report->extra->{moderated_overdue}, undef, 'Sanity check' ); + is ( $report->get_extra_metadata('moderated_overdue'), undef, 'Sanity check' ); is get_moderated_count(), 0; # Check that setting to 'hidden' also triggers moderation @@ -298,8 +302,8 @@ subtest "report_edit" => sub { $mech->get_ok( '/admin/report_edit/' . $report->id ); }; $report->discard_changes; - is ( $report->extra->{moderated_overdue}, 0, 'Marking hidden from scratch sets moderated_overdue' ); - is ( $report->extra->{closed_overdue}, 0, 'Marking hidden from scratch also set closed_overdue' ); + is ( $report->get_extra_metadata('moderated_overdue'), 0, 'Marking hidden from scratch sets moderated_overdue' ); + is ( $report->get_extra_metadata('closed_overdue'), 0, 'Marking hidden from scratch also set closed_overdue' ); is get_moderated_count(), 1; is ($cobrand->get_or_check_overdue($report), 0, 'sanity check'); @@ -346,57 +350,88 @@ $mech->clear_emails_ok; $mech->log_out_ok; -my $user = $mech->log_in_ok( 'sdm1@example.org') ; -$user->update({ from_body => undef }); -FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'zurich' ], -}, sub { - $mech->get_ok( '/admin' ); -}; -is $mech->uri->path, '/my', "got sent to /my"; -$user->from_body( $subdivision->id ); -$user->update; +subtest 'SDM' => sub { + my $user = $mech->log_in_ok( 'sdm1@example.org') ; + $user->update({ from_body => undef }); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $mech->get_ok( '/admin' ); + }; + is $mech->uri->path, '/my', "got sent to /my"; + $user->from_body( $subdivision->id ); + $user->update; -FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'zurich' ], -}, sub { - $mech->get_ok( '/admin' ); -}; -is $mech->uri->path, '/admin', "am logged in"; + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $mech->get_ok( '/admin' ); + }; + is $mech->uri->path, '/admin', "am logged in"; -$mech->content_contains( 'report_edit/' . $report->id ); -$mech->content_contains( DateTime->now->strftime("%d.%m.%Y") ); -$mech->content_contains( 'In Bearbeitung' ); + $mech->content_contains( 'report_edit/' . $report->id ); + $mech->content_contains( DateTime->now->strftime("%d.%m.%Y") ); + $mech->content_contains( 'In Bearbeitung' ); -FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'zurich' ], -}, sub { - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_contains( 'Initial internal note' ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->content_contains( 'Initial internal note' ); - $mech->submit_form_ok( { with_fields => { status_update => 'This is an update.' } } ); - is $mech->uri->path, '/admin/report_edit/' . $report->id, "still on edit page"; - $mech->content_contains('This is an update'); - ok $mech->form_with_fields( 'status_update' ); - $mech->submit_form_ok( { button => 'no_more_updates' } ); - is $mech->uri->path, '/admin/summary', "redirected now finished with report."; + $mech->submit_form_ok( { with_fields => { status_update => 'This is an update.' } } ); + is $mech->uri->path, '/admin/report_edit/' . $report->id, "still on edit page"; + $mech->content_contains('This is an update'); + ok $mech->form_with_fields( 'status_update' ); + $mech->submit_form_ok( { button => 'no_more_updates' } ); + is $mech->uri->path, '/admin/summary', "redirected now finished with report."; - $mech->get_ok( '/report/' . $report->id ); - $mech->content_contains('In Bearbeitung'); - $mech->content_contains('Test Test'); -}; + $mech->get_ok( '/report/' . $report->id ); + $mech->content_contains('In Bearbeitung'); + $mech->content_contains('Test Test'); + }; -send_reports_for_zurich(); -$email = $mech->get_email; -like $email->header('Subject'), qr/Feedback/, 'subject looks okay'; -like $email->header('To'), qr/division\@example.org/, 'to line looks correct'; -$mech->clear_emails_ok; + send_reports_for_zurich(); + $email = $mech->get_email; + like $email->header('Subject'), qr/Feedback/, 'subject looks okay'; + like $email->header('To'), qr/division\@example.org/, 'to line looks correct'; + $mech->clear_emails_ok; -$report->discard_changes; -is $report->state, 'planned', 'Report now in planned state'; + $report->discard_changes; + is $report->state, 'planned', 'Report now in planned state'; + + subtest 'send_back' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $report->update({ bodies_str => $subdivision->id, state => 'in progress' }); + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->submit_form_ok( { form_number => 2, button => 'send_back' } ); + $report->discard_changes; + is $report->state, 'confirmed', 'Report sent back to confirmed state'; + is $report->bodies_str, $division->id, 'Report sent back to division'; + }; + }; -$mech->log_out_ok; -$user = $mech->log_in_ok( 'dm1@example.org') ; + subtest 'not contactable' => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $report->update({ bodies_str => $subdivision->id, state => 'in progress' }); + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->submit_form_ok( { button => 'not_contactable', form_number => 2 } ); + $report->discard_changes; + is $report->state, 'partial', 'Report sent back to partial (not_contactable) state'; + is $report->bodies_str, $division->id, 'Report sent back to division'; + }; + }; + + $report->update({ state => 'planned' }); + + $mech->log_out_ok; +}; + +my $user = $mech->log_in_ok( 'dm1@example.org') ; FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'zurich' ], }, sub { @@ -407,9 +442,7 @@ $mech->content_contains( 'report_edit/' . $report->id ); $mech->content_contains( DateTime->now->strftime("%d.%m.%Y") ); # User confirms their email address -my $extra = $report->extra; -$extra->{email_confirmed} = 1; -$report->extra ( { %$extra } ); +$report->set_extra_metadata(email_confirmed => 1); $report->update; FixMyStreet::override_config { @@ -477,49 +510,82 @@ $mech->email_count_is(0); }); $report = $reports[0]; -FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'zurich' ], -}, sub { - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->submit_form_ok( { with_fields => { body_external => $external_body->id } } ); - $mech->get_ok( '/report/' . $report->id ); -}; -$mech->content_contains('Beantwortet'); -$mech->content_contains('Third Test'); -$mech->content_contains('Wir haben Ihr Anliegen an External Body weitergeleitet'); -send_reports_for_zurich(); -$email = $mech->get_email; -like $email->header('Subject'), qr/Weitergeleitete Meldung/, 'subject looks okay'; -like $email->header('To'), qr/external_body\@example.org/, 'to line looks correct'; -like $email->body, qr/External Body/, 'body has right name'; -unlike $email->body, qr/test\@example.com/, 'body does not contain email address'; -$mech->clear_emails_ok; +subtest "external report triggers email" => sub { + my $EXTERNAL_MESSAGE = 'Look Ma, no hands!'; + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $report->update({ state => 'closed' }); # required to see body_external field + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->submit_form_ok( { + with_fields => { + body_external => $external_body->id, + external_message => $EXTERNAL_MESSAGE, + } }); + $mech->get_ok( '/report/' . $report->id ); + }; + $mech->content_contains('Extern'); + $mech->content_contains('Third Test'); + $mech->content_contains($report->get_extra_metadata('public_response')) or die $mech->content; + send_reports_for_zurich(); + $email = $mech->get_email; + like $email->header('Subject'), qr/Weitergeleitete Meldung/, 'subject looks okay'; + like $email->header('To'), qr/external_body\@example.org/, 'to line looks correct'; + like $email->body, qr/External Body/, 'body has right name'; + like $email->body, qr/$EXTERNAL_MESSAGE/, 'external_message was passed on'; + unlike $email->body, qr/test\@example.com/, 'body does not contain email address'; + $mech->clear_emails_ok; + + subtest "Test third_personal boolean setting" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $mech->get_ok( '/admin' ); + $report->update({ state => 'closed' }); # required to see body_external field + is $mech->uri->path, '/admin', "am logged in"; + $mech->content_contains( 'report_edit/' . $report->id ); + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->submit_form_ok( { with_fields => { body_external => $external_body->id, third_personal => 1 } } ); + $mech->get_ok( '/report/' . $report->id ); + }; + $mech->content_contains('Extern'); + $mech->content_contains('Third Test'); + $mech->content_contains($report->get_extra_metadata('public_response')); + send_reports_for_zurich(); + $email = $mech->get_email; + like $email->header('Subject'), qr/Weitergeleitete Meldung/, 'subject looks okay'; + like $email->header('To'), qr/external_body\@example.org/, 'to line looks correct'; + like $email->body, qr/External Body/, 'body has right name'; + like $email->body, qr/test\@example.com/, 'body does contain email address'; + $mech->clear_emails_ok; + }; -# Test calling back, and third_personal boolean setting -FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'zurich' ], -}, sub { - $mech->get_ok( '/admin' ); - is $mech->uri->path, '/admin', "am logged in"; - $mech->content_contains( 'report_edit/' . $report->id ); - $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->submit_form_ok( { with_fields => { state => 'unconfirmed' } } ); - $mech->submit_form_ok( { with_fields => { body_external => $external_body->id, third_personal => 1 } } ); - $mech->get_ok( '/report/' . $report->id ); + subtest "Test external wish sending" => sub { + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'zurich' ], + }, sub { + $report->update({ state => 'investigating' }); # Wish + $mech->get_ok( '/admin/report_edit/' . $report->id ); + $mech->submit_form_ok( { + with_fields => { + body_external => $external_body->id, + external_message => $EXTERNAL_MESSAGE, + } }); + }; + send_reports_for_zurich(); + $email = $mech->get_email; + like $email->header('Subject'), qr/Weitergeleitete Meldung/, 'subject looks okay'; + like $email->header('To'), qr/external_body\@example.org/, 'to line looks correct'; + like $email->body, qr/External Body/, 'body has right name'; + like $email->body, qr/$EXTERNAL_MESSAGE/, 'external_message was passed on'; + like $email->body, qr/test\@example.com/, 'body contains email address'; + $mech->clear_emails_ok; + }; + $report->comments->delete; # delete the comments, as they confuse later tests }; -$mech->content_contains('Beantwortet'); -$mech->content_contains('Third Test'); -$mech->content_contains('Wir haben Ihr Anliegen an External Body weitergeleitet'); -send_reports_for_zurich(); -$email = $mech->get_email; -like $email->header('Subject'), qr/Weitergeleitete Meldung/, 'subject looks okay'; -like $email->header('To'), qr/external_body\@example.org/, 'to line looks correct'; -like $email->body, qr/External Body/, 'body has right name'; -like $email->body, qr/test\@example.com/, 'body does contain email address'; -$mech->clear_emails_ok; -$mech->log_out_ok; subtest "superuser and dm can see stats" => sub { + $mech->log_out_ok; $user = $mech->log_in_ok( 'super@example.org' ); FixMyStreet::override_config { @@ -622,7 +688,12 @@ subtest "problems can't be assigned to deleted bodies" => sub { $mech->get_ok( '/admin/body/' . $external_body->id ); $mech->submit_form_ok( { with_fields => { deleted => 1 } } ); $mech->get_ok( '/admin/report_edit/' . $report->id ); - $mech->content_lacks( $external_body->name ); + $mech->content_lacks( $external_body->name ) + or do { + diag $mech->content; + diag $external_body->name; + die; + }; }; $user->from_body( $division->id ); $user->update; @@ -631,9 +702,7 @@ subtest "problems can't be assigned to deleted bodies" => sub { subtest "hidden report email are only sent when requested" => sub { $user = $mech->log_in_ok( 'dm1@example.org') ; - $extra = $report->extra; - $extra->{email_confirmed} = 1; - $report->extra ( { %$extra } ); + $report->set_extra_metadata(email_confirmed => 1); $report->update; FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'zurich' ], @@ -714,7 +783,10 @@ subtest "test admin_log" => sub { object_type => 'problem', object_id => $report->id, }); - is scalar @entries, 4, 'State changes logged'; + + # XXX: following is dependent on all of test up till now, rewrite to explicitly + # test which things need to be logged! + is scalar @entries, 1, 'State changes logged'; is $entries[-1]->action, 'state change to hidden', 'State change logged as expected'; }; @@ -732,6 +804,8 @@ subtest 'email images to external partners' => sub { my $fileid = $photoset->data; $report->set_extra_metadata('publish_photo' => 1); + # The below email comparison must not have an external message. + $report->unset_extra_metadata('external_message'); $report->update({ state => 'closed', photo => $fileid, diff --git a/templates/email/zurich/problem-wish.txt b/templates/email/zurich/problem-wish.txt new file mode 100644 index 000000000..09cc9c7ca --- /dev/null +++ b/templates/email/zurich/problem-wish.txt @@ -0,0 +1,26 @@ +Subject: Züri wie neu: Meldung #[% problem.id %] + +Grüezi [% problem.name %] + +Besten Dank für Ihre Meldung auf <<Züri wie neu>>. Wir haben Ihr Anliegen an [% problem.body(c).name %] +weitergeleitet, da es nicht in den Zuständigkeitsbereich der beteiligten Fachbereiche fällt. + +Unter: + [% url %] +finden Sie Ihre Meldung auf der Website. + + +Ihre Meldung lautet: + +[% problem.detail %] + + + +Freundliche Grüsse + +Ihre Stadt Zürich + + + +Dieses E-Mail wurde automatisch generiert. Bitte antworten Sie nicht darauf. + diff --git a/templates/email/zurich/submit-external-personal.txt b/templates/email/zurich/submit-external-personal.txt index 5e0823871..2d4fce606 100644 --- a/templates/email/zurich/submit-external-personal.txt +++ b/templates/email/zurich/submit-external-personal.txt @@ -6,6 +6,8 @@ Die folgende Meldung wurde auf http://www.stadt-zuerich.ch/zueriwieneu erfasst: Öffentliche URL: <?=$values['url']?> +<?=$values['external_message']?> + Name des Meldenden: <?=$values['name']?> Email des Meldenden: <?=$values['email']?> diff --git a/templates/email/zurich/submit-external-wish.txt b/templates/email/zurich/submit-external-wish.txt new file mode 100644 index 000000000..8a6dc34dd --- /dev/null +++ b/templates/email/zurich/submit-external-wish.txt @@ -0,0 +1,22 @@ +Subject: Züri wie neu: Weitergeleitete Meldung #<?=$values['id']?> + +Grüezi <?=$values['bodies_name']?>, + +Die folgende Meldung wurde auf http://www.stadt-zuerich.ch/zueriwieneu erfasst: + +Öffentliche URL: <?=$values['url']?> + +<?=$values['external_message']?> + +"Züri wie neu" ist eine Dienstleistung der Stadt Zürich zum Thema +Bürgerbeteiligung. Auf dieser Plattform kann die Bevölkerung auf Schäden und +Mängel an der städtischen Infrastruktur hinweisen. +Diese Meldung wurde Ihnen von der Stadt Zürich gesendet, da es Ihr +Zuständigkeitsgebiet betreffen könnte. + +Bei Fragen zu "Züri wie neu" wenden Sie sich bitte an gis-zentrum@zuerich.ch. + + +Freundliche Grüsse + +Ihre Stadt Zürich diff --git a/templates/email/zurich/submit-external.txt b/templates/email/zurich/submit-external.txt index bf409dccc..8a6dc34dd 100644 --- a/templates/email/zurich/submit-external.txt +++ b/templates/email/zurich/submit-external.txt @@ -6,6 +6,8 @@ Die folgende Meldung wurde auf http://www.stadt-zuerich.ch/zueriwieneu erfasst: Öffentliche URL: <?=$values['url']?> +<?=$values['external_message']?> + "Züri wie neu" ist eine Dienstleistung der Stadt Zürich zum Thema Bürgerbeteiligung. Auf dieser Plattform kann die Bevölkerung auf Schäden und Mängel an der städtischen Infrastruktur hinweisen. diff --git a/templates/web/zurich/admin/report_edit-sdm.html b/templates/web/zurich/admin/report_edit-sdm.html index 6e952022a..7f8a0d74f 100644 --- a/templates/web/zurich/admin/report_edit-sdm.html +++ b/templates/web/zurich/admin/report_edit-sdm.html @@ -16,6 +16,7 @@ <input type="hidden" name="submit" value="1" > <p align="right"><input type="submit" name="send_back" value="[% loc('Not for my subdivision') %]"></p> +<p align="right"><input type="submit" name="not_contactable" value="[% loc('Customer not contactable') %]"></p> <ul class="no-bullets"> <li><a href="[% c.uri_for_email( '/report', problem.id ) %]">[% loc('View report on site' )%]</a></li> diff --git a/templates/web/zurich/admin/report_edit.html b/templates/web/zurich/admin/report_edit.html index 1bf7c2a5c..19a076435 100644 --- a/templates/web/zurich/admin/report_edit.html +++ b/templates/web/zurich/admin/report_edit.html @@ -91,28 +91,10 @@ [% END %] </ul> -<p><label for="new_internal_note">[% loc('New internal note:') %]</label> -<textarea name='new_internal_note' id='new_internal_note' cols=60 rows=5>[% new_internal_note | html %]</textarea></p> - <p><span class="mock-label">[% loc('State:') %]</span> <select name="state" id="state"> <option value="">--</option> - [% FOREACH s IN [ - ['unconfirmed', loc('Submitted')] - ['confirmed', loc('Open')], - ['planned', loc('Planned')], - ['hidden', loc('Hidden')], - ['investigating', loc('Wish')], - ['partial', loc('Not contactable')], - ['unable to fix', loc('Jurisdiction unknown')], - ] %] - <option [% 'selected ' IF s.0 == problem.state %] value="[% s.0 %]">[% s.1 %]</option> - [% END %] - [% IF problem.state == 'closed' %] - <option selected value="closed">[% loc('Extern') %]</option> - [% ELSIF problem.state == 'fixed - council' %] - <option selected value="fixed - council">[% loc('Closed') %]</option> - [% ELSIF problem.state == 'in progress' %] - <option selected value="in progress">[% loc('In progress') %]</option> + [% FOREACH s IN states %] + <option [% 'selected ' IF s.state == problem.state %] value="[% s.state %]">[% s.trans %]</option> [% END %] </select></p> @@ -123,113 +105,133 @@ <script type="text/javascript"> $(function(){ - // Show or hide the automatic reply field + + $('.assignation select').change(function(){ + if (this.value == "") { + $('.assignation').css('color', '#000'); + } else { + var a = $(this).closest('li').css('color', '#000'); + $('.assignation select').not(this).val(""); + $('.assignation').not(a).css('color', '#999'); + } + }); + $('#state').change(function(){ - if ($(this).val() === 'hidden') { + // Show or hide the automatic reply field + var state = $(this).val(); + if (state === 'hidden') { $('#automatic-reply').show(); } else { $('#automatic-reply').hide(); } + + // Show or hide the categories + if (state === 'confirmed') { + $('#assignation__category').show(); + $('#assignation__subdivision').show(); + } else { + $('#assignation__category').hide(); + $('#assignation__subdivision').hide(); + $('#assignation__category select').val(''); + $('#assignation__subdivision select').val(''); + } + + if ((state === 'closed') || (state === 'investigating')) { + $('#assignation__external').show(); + } else { + $('#assignation__external select').val(''); + $('#assignation__external').hide(); + } + }).change(); }); </script> -[% IF problem.state == 'unconfirmed' OR problem.state == 'confirmed' %] - <ul class="no-bullets"> -[% list = FOR body IN bodies %] - [%- NEXT UNLESS body.parent.id == c.user.from_body.id %] - <option value="[% body.id %]"[% IF body.id == problem.bodies_str %] selected[% END %]>[% body.name %]</option> -[% END %] - [% IF admin_type != 'super' AND list %] - <li class="assignation"> - <label for="body_subdivision">[% loc('Assign to subdivision:') %]</label> - <select name="body_subdivision" id="body_subdivision"> - <option value="">--</option> - [% list %] - </select> - </li> - [% END %] +[% IF problem.state == 'confirmed' %] <li class="assignation"> <label for="category"> [% loc('Category:') %] [% problem.category %]<br> -[% loc('Assign to different category:') %]</label> - <select name="category" id="category"> - <option value="">--</option> - [% FOREACH cat IN categories %] - <option value="[% cat %]">[% cat %]</option> - [% END %] - </select></li> -<li class="assignation"> -<label for="body_external">[% loc('Assign to external body:') %]</label> - <select name="body_external" id="body_external"> - <option value="">--</option> - [% FOR body IN bodies %] - [% NEXT IF body.parent OR body.bodies OR body.deleted %] - <option value="[% body.id %]"[% IF body.id == problem.bodies_str %] selected[% END %]>[% body.name %]</option> - [% END %] + <div id="assignation__category"> + [% loc('Assign to different category:') %]</label> + <select name="category" id="category"> + <option value="">--</option> + [% FOREACH cat IN categories %] + <option value="[% cat %]">[% cat %]</option> + [% END %] </select> - <br> - <input type="checkbox" name="third_personal" id="third_personal" value="1"[% ' checked' IF problem.extra.third_personal %]> - <label for="third_personal" class="inline">[% loc('Include reporter personal details') %]</label> - -</ul> - -<script type="text/javascript"> -$(function(){ - $('.assignation select').change(function(){ - if (this.value == "") { - $('.assignation').css('color', '#000'); - } else { - var a = $(this).closest('li').css('color', '#000'); - $('.assignation select').not(this).val(""); - $('.assignation').not(a).css('color', '#999'); - } - }); -}); -</script> - -[% ELSIF problem.state == 'planned' %] + </div> +</li> -<ul class="no-bullets"> [% list = FOR body IN bodies %] [%- NEXT UNLESS body.parent.id == c.user.from_body.id %] - <option value="[% body.id %]">[% body.name %]</option> + <option value="[% body.id %]"[% IF body.id == problem.bodies_str %] selected[% END %]>[% body.name %]</option> [% END %] [% IF admin_type != 'super' AND list %] - <li class="assignation"> - <label for="body_subdivision">[% loc('Assign to subdivision:') %]</label> + <li class="assignation" id="assignation__subdivision"> + <label for="body_subdivision">[% loc('Assign to subdivision:') %]</label> <select name="body_subdivision" id="body_subdivision"> - <option value="" selected>--</option> + <option value="">--</option> [% list %] </select> </li> [% END %] - <li><label for="status_update">[% loc('Public response:') %]</label> - [% INCLUDE 'admin/response_templates_select.html' for='status_update' %] - <textarea name='status_update' id='status_update' cols=60 rows=5> - [%- IF problem.extra.public_response -%] - [%- problem.extra.public_response | html -%] - [%- ELSE -%] - -Freundliche Grüsse +[% END %] -Ihre Stadt Zürich - [%- END %]</textarea> +[%# 3rd party messages sent for Extern/Wunsch states %] +[% SWITCH problem.state %] + [% CASE ['closed','investigating'] %] + <ul class="no-bullets"> + <li class="assignation" id="assignation__external"> + <label for="body_external"> + [% IF problem.state == 'closed' %] + [% loc('Assign to external body:') %] + [% ELSE %] + [% loc('Assign to competent body:') %] + [% END %] + </label> + <select name="body_external" id="body_external"> + <option value="">--</option> + [% FOR body IN bodies %] + [% NEXT IF body.parent OR body.bodies OR body.deleted %] + <option value="[% body.id %]"[% IF body.id == problem.external_body %] selected[% END %]>[% body.name %]</option> + [% END %] + </select> + <br> + [% IF problem.state == 'closed' %] + <input type="checkbox" name="third_personal" id="third_personal" value="1"[% ' checked' IF problem.extra.third_personal %]> + <label for="third_personal" class="inline">[% loc('Include reporter personal details') %]</label> + [% END %] + [% INCLUDE 'admin/response_templates_select.html' for='external_body' %] + <textarea name='external_message' id='external_body' cols=60 rows=5></textarea> </li> -</ul> - -[% ELSIF problem.state == 'fixed - council' %] - -<p><span class="mock-label">[% loc('Public response:') %]</span> -[% problem.extra.public_response | html %] -</p> + </ul> +[% END %] +[%# Public response field shown for Ruckmeldung ausstehend/Extern/ + # Zustandigkeit unbekannt/Wunsch/Nicht kontaktierbar/Unsichtbar %] +[% SWITCH problem.state %] + [% CASE ['planned','closed','unable to fix','investigating','partial','hidden'] %] + <ul class="no-bullets"> + <li><label for="status_update">[% loc('Public response:') %]</label> + [% INCLUDE 'admin/response_templates_select.html' for='status_update' %] + <textarea name='status_update' id='status_update' cols=60 rows=5> + [%- problem.extra.public_response || default_public_response | html -%] + </textarea> + </li> + </ul> + [% CASE 'fixed - council' %] + <p><span class="mock-label">[% loc('Public response:') %]</span> + [% problem.extra.public_response | html %] + </p> [% END %] +<p><label for="new_internal_note">[% loc('New internal note:') %]</label> +<textarea name='new_internal_note' id='new_internal_note' cols=60 rows=5>[% new_internal_note | html %]</textarea></p> + <p> <label for="time_spent">[% loc('Time spent (in minutes):') %]</label> <input type="text" name="time_spent" id="form_time_spent" style="width: 4em" value="0"> |