aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStruan Donald <struan@exo.org.uk>2019-08-13 15:37:29 +0100
committerStruan Donald <struan@exo.org.uk>2019-09-27 17:43:36 +0100
commit863c29fda1198e06bc9e1b424532e570cb70ca37 (patch)
tree459bb79cac56d649a4aec45148c41c675eb9b33c
parent921d68cf084617405f4b978bff846ca94808b738 (diff)
[IsleOfWight] add an admin interface to triage reports
On reports with a state of `for_triage` override the `_inspect.html` template to display one which allows only allows the user to change the category from one with a 'Triage' send_method to a non Triage send_method. When saved this records a comment with the original category and the category it was triaged to. This is hidden from the site as it's only used for audit purposes and sending over Open311. This assumes that the triage categories have the same names as the groups of the confirm categories. It uses this to select the first confirm category in the group corresponding to the triage category. This also makes the sidebar geolocation onclick handler check for the presence of the button, as the triage inspect form does not have it.
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm3
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin/Triage.pm149
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm11
-rw-r--r--perllib/FixMyStreet/Cobrand/IsleOfWight.pm53
-rw-r--r--t/app/controller/admin/triage.t112
-rw-r--r--t/cobrand/isleofwight.t39
-rw-r--r--templates/email/isleofwight/other-reported.html42
-rw-r--r--templates/email/isleofwight/other-reported.txt43
-rw-r--r--templates/web/base/admin/report-category.html5
-rw-r--r--templates/web/base/admin/triage/_inspect.html77
-rw-r--r--templates/web/base/admin/triage/_list-filters.html53
-rw-r--r--templates/web/base/admin/triage/index.html42
-rw-r--r--templates/web/base/report/_inspect.html67
-rw-r--r--templates/web/base/report/inspect/information.html56
-rw-r--r--templates/web/base/report/inspect/public_update.html14
-rw-r--r--templates/web/base/report/updates.html3
-rw-r--r--templates/web/isleofwight/main_nav_items.html59
-rw-r--r--templates/web/isleofwight/report/inspect.html10
-rw-r--r--web/cobrands/fixmystreet/staff.js29
-rw-r--r--web/cobrands/isleofwight/assets.js15
20 files changed, 788 insertions, 94 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index 5373220a7..c2c4e7588 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -678,6 +678,9 @@ sub categories_for_point : Private {
shift @{$c->stash->{category_options}} if @{$c->stash->{category_options}};
$c->stash->{categories_hash} = { map { $_->category => 1 } @{$c->stash->{category_options}} };
+
+ $c->forward('/admin/triage/setup_categories');
+
}
sub alerts_for_report : Private {
diff --git a/perllib/FixMyStreet/App/Controller/Admin/Triage.pm b/perllib/FixMyStreet/App/Controller/Admin/Triage.pm
new file mode 100644
index 000000000..42028a6f0
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/Admin/Triage.pm
@@ -0,0 +1,149 @@
+package FixMyStreet::App::Controller::Admin::Triage;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::Admin::Triage - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Admin pages for triaging reports.
+
+This allows reports to be triaged before being sent to the council. It works
+by having a set of categories with a send_method of Triage which sets the report
+state to 'for_triage'. Any reports with the state are then show on '/admin/triage'
+which is available to users with the 'triage' permission.
+
+Clicking on reports on this list will then allow a user to change the category of
+the report to one that has an alternative send method, which will trigger the report
+to be resent.
+
+In order for this to work additional work needs to be done to the cobrand to only
+display triageable categories to the user.
+
+=head1 METHODS
+
+=cut
+
+sub auto : Private {
+ my ( $self, $c ) = @_;
+
+ unless ( $c->user->has_body_permission_to('triage') ) {
+ $c->detach('/page_error_403_access_denied', []);
+ }
+}
+
+sub index : Path : Args(0) {
+ my ( $self, $c ) = @_;
+
+ # default sort to oldest
+ unless ( $c->get_param('sort') ) {
+ $c->set_param('sort', 'created-asc');
+ }
+ $c->stash->{body} = $c->forward('/reports/body_find', [ $c->cobrand->council_area ]);
+ $c->forward( 'stash_report_filter_status' );
+ $c->forward( '/reports/load_and_group_problems' );
+ $c->stash->{page} = 'reports'; # So the map knows to make clickable pins
+
+ if ($c->get_param('ajax')) {
+ my $ajax_template = $c->stash->{ajax_template} || 'reports/_problem-list.html';
+ $c->detach('/reports/ajax', [ $ajax_template ]);
+ }
+
+ my @categories = $c->stash->{body}->contacts->not_deleted->search( undef, {
+ columns => [ 'id', 'category', 'extra' ],
+ distinct => 1,
+ order_by => [ 'category' ],
+ } )->all;
+ $c->stash->{filter_categories} = \@categories;
+ $c->stash->{filter_category} = { map { $_ => 1 } $c->get_param_list('filter_category', 1) };
+ my $pins = $c->stash->{pins} || [];
+
+ my %map_params = (
+ latitude => @$pins ? $pins->[0]{latitude} : 0,
+ longitude => @$pins ? $pins->[0]{longitude} : 0,
+ area => [ $c->stash->{wards} ? map { $_->{id} } @{$c->stash->{wards}} : keys %{$c->stash->{body}->areas} ],
+ any_zoom => 1,
+ );
+ FixMyStreet::Map::display_map(
+ $c, %map_params, pins => $pins,
+ );
+}
+
+sub stash_report_filter_status : Private {
+ my ( $self, $c ) = @_;
+ $c->stash->{filter_problem_states} = { 'for triage' => 1 };
+ return 1;
+}
+
+sub setup_categories : Private {
+ my ( $self, $c ) = @_;
+
+ if ( $c->stash->{problem}->state eq 'for triage' ) {
+ $c->stash->{holding_options} = [ grep { $_->send_method && $_->send_method eq 'Triage' } @{$c->stash->{category_options}} ];
+ $c->stash->{holding_categories} = { map { $_->category => 1 } @{$c->stash->{holding_options}} };
+ $c->stash->{end_options} = [ grep { !$_->send_method || $_->send_method ne 'Triage' } @{$c->stash->{category_options}} ];
+ $c->stash->{end_categories} = { map { $_->category => 1 } @{$c->stash->{end_options}} };
+ delete $c->stash->{categories_hash};
+ my %category_groups = ();
+ for my $category (@{$c->stash->{end_options}}) {
+ my $group = $category->{group} // $category->get_extra_metadata('group') // [''];
+ # this could be an array ref or a string
+ my @groups = ref $group eq 'ARRAY' ? @$group : ($group);
+ push( @{$category_groups{$_}}, $category ) for @groups;
+ }
+ my @category_groups = ();
+ for my $group ( grep { $_ ne _('Other') } sort keys %category_groups ) {
+ push @category_groups, { name => $group, categories => $category_groups{$group} };
+ }
+ $c->stash->{end_groups} = \@category_groups;
+ }
+
+ return 1;
+}
+
+sub update : Private {
+ my ($self, $c) = @_;
+
+ my $problem = $c->stash->{problem};
+
+ my $current_category = $problem->category;
+ my $new_category = $c->get_param('category');
+
+ my $changed = $c->forward('/admin/report_edit_category', [ $problem, 1 ] );
+
+ if ( $changed ) {
+ $c->stash->{problem}->update( { state => 'confirmed' } );
+ $c->forward( '/admin/log_edit', [ $problem->id, 'problem', 'triage' ] );
+
+ my $name = $c->user->moderating_user_name;
+ my $extra = { is_superuser => 1 };
+ if ($c->user->from_body) {
+ delete $extra->{is_superuser};
+ $extra->{is_body_user} = $c->user->from_body->id;
+ }
+
+ $extra->{triage_report} = 1;
+ $extra->{holding_category} = $current_category;
+ $extra->{new_category} = $new_category;
+
+ my $timestamp = \'current_timestamp';
+ $problem->add_to_comments( {
+ text => "Report triaged from $current_category to $new_category",
+ created => $timestamp,
+ confirmed => $timestamp,
+ user_id => $c->user->id,
+ name => $name,
+ mark_fixed => 0,
+ anonymous => 0,
+ state => 'confirmed',
+ problem_state => $problem->state,
+ extra => $extra
+ } );
+ }
+}
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index 9b90da161..eb6050fde 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -86,7 +86,7 @@ sub display :PathPart('') :Chained('id') :Args(0) {
$c->forward( 'format_problem_for_display' );
my $permissions = $c->stash->{_permissions} ||= $c->forward( 'check_has_permission_to',
- [ qw/report_inspect report_edit_category report_edit_priority report_mark_private/ ] );
+ [ qw/report_inspect report_edit_category report_edit_priority report_mark_private triage/ ] );
if (any { $_ } values %$permissions) {
$c->stash->{template} = 'report/inspect.html';
$c->forward('inspect');
@@ -396,7 +396,14 @@ sub inspect : Private {
$c->stash->{max_detailed_info_length} = $c->cobrand->max_detailed_info_length;
- if ( $c->get_param('save') ) {
+ if ( $c->get_param('triage') ) {
+ $c->forward('/auth/check_csrf_token');
+ $c->forward('/admin/triage/update');
+ my $redirect_uri = $c->uri_for( '/admin/triage' );
+ $c->log->debug( "Redirecting to: " . $redirect_uri );
+ $c->res->redirect( $redirect_uri );
+ }
+ elsif ( $c->get_param('save') ) {
$c->forward('/auth/check_csrf_token');
my $valid = 1;
diff --git a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm
index 9e48b4d77..f2cd1a687 100644
--- a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm
+++ b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm
@@ -47,15 +47,6 @@ sub updates_disallowed {
sub get_geocoder { 'OSM' }
-sub open311_get_update_munging {
- my ($self, $comment) = @_;
-
- # If we've received an update via Open311 that's closed
- # or fixed the report, also close it to updates.
- $comment->problem->set_extra_metadata(closed_updates => 1)
- if !$comment->problem->is_open;
-}
-
sub open311_pre_send {
my ($self, $row, $open311) = @_;
@@ -89,7 +80,51 @@ sub open311_munge_update_params {
$params->{description} = "[The customer indicated that this issue had been fixed]\n\n" . $params->{description};
}
+ if ( $comment->get_extra_metadata('triage_report') ) {
+ $params->{description} = "Triaged by " . $comment->user->name . ' (' . $comment->user->email . "). " . $params->{description};
+ }
+
$params->{description} = "FMS-Update: " . $params->{description};
}
+sub munge_category_list {
+ my ($self, $options, $contacts, $extras) = @_;
+
+ my $user = $self->{c}->user;
+ my %bodies = map { $_->body->name => $_->body } @$contacts;
+ my $b = $bodies{'Isle of Wight Council'};
+
+ if ( $user && ( $user->is_superuser || $user->belongs_to_body( $b->id ) ) ) {
+ return;
+ }
+
+ @$contacts = grep { $_->send_method eq 'Triage' } @$contacts;
+ my $seen = { map { $_->category => 1 } @$contacts };
+ @$options = grep { my $c = ($_->{category} || $_->category); $c =~ 'Pick a category' || $seen->{ $c } } @$options;
+}
+
+sub open311_get_update_munging {
+ my ($self, $comment) = @_;
+
+ # If we've received an update via Open311 that's closed
+ # or fixed the report, also close it to updates.
+ $comment->problem->set_extra_metadata(closed_updates => 1)
+ if !$comment->problem->is_open;
+}
+
+sub admin_pages {
+ my $self = shift;
+ my $pages = $self->next::method();
+ $pages->{triage} = [ undef, undef ];
+ return $pages;
+}
+
+sub available_permissions {
+ my $self = shift;
+
+ my $perms = $self->next::method();
+ $perms->{Problems}->{triage} = "Triage reports";
+
+ return $perms;
+}
1;
diff --git a/t/app/controller/admin/triage.t b/t/app/controller/admin/triage.t
new file mode 100644
index 000000000..81eb28a5d
--- /dev/null
+++ b/t/app/controller/admin/triage.t
@@ -0,0 +1,112 @@
+use FixMyStreet::TestMech;
+
+my $mech = FixMyStreet::TestMech->new;
+
+my $user = $mech->create_user_ok('test@example.com', name => 'Test User');
+my $user2 = $mech->create_user_ok('test2@example.com', name => 'Test User 2');
+my $superuser = $mech->create_user_ok(
+ 'superuser@example.com',
+ name => 'Super User',
+ is_superuser => 1
+);
+
+my $iow = $mech->create_body_ok(2636, 'Isle of Wight Council', { can_be_devolved => 1 } );
+my $iow_contact = $mech->create_contact_ok(
+ body_id => $iow->id,
+ category => 'Potholes',
+ email => 'potholes@example.com',
+ send_method => 'Triage'
+);
+$mech->create_contact_ok(
+ body_id => $iow->id,
+ category => 'Traffic lights',
+ email => 'lights@example.com'
+);
+
+my $dt = DateTime->now();
+
+my ($report) = $mech->create_problems_for_body(
+ 1,
+ $iow->id,
+ 'TITLE',
+ {
+ areas => 2636,
+ category => 'Potholes',
+ whensent => $dt,
+ latitude => 50.71086,
+ longitude => -1.29573,
+ send_method_used => 'Triage',
+ }
+);
+
+warn $report->bodies_str;
+
+FixMyStreet::override_config {
+ STAGING_FLAGS => { send_reports => 1, skip_checks => 0 },
+ ALLOWED_COBRANDS => [ 'isleofwight' ],
+ MAPIT_URL => 'http://mapit.uk/',
+}, sub {
+ subtest "user can access triage page with triage permission" => sub {
+ $user->update({ from_body => $iow });
+ $mech->log_out_ok;
+ $mech->get_ok('/admin/triage');
+
+ $mech->log_in_ok($user->email);
+ $mech->get('/admin/triage');
+ is $mech->res->code, 403, 'permission denied';
+
+ $user->user_body_permissions->create( { body => $iow, permission_type => 'triage' } );
+ $mech->get_ok('/admin/triage');
+ };
+
+ subtest "reports marked for triage show triage interface" => sub {
+ $mech->log_out_ok;
+ $mech->log_in_ok( $user->email );
+
+ $mech->get_ok('/report/' . $report->id);
+ $mech->content_lacks('CONFIRM Subject');
+
+ $report->update( { state => 'for triage' } );
+
+ $mech->get_ok('/report/' . $report->id);
+ $mech->content_contains('CONFIRM Subject');
+ };
+
+ subtest "changing report category marks report as confirmed" => sub {
+ my $report_url = '/report/' . $report->id;
+ $mech->get_ok($report_url);
+
+ $mech->content_contains('Traffic lights');
+
+ $mech->submit_form_ok( {
+ with_fields => {
+ category => 'Traffic lights',
+ include_update => 0,
+ }
+ },
+ 'triage form submitted'
+ );
+
+ $mech->content_contains('Potholes');
+
+ $report->discard_changes;
+ is $report->state, 'confirmed', 'report marked as confirmed';
+ ok !$report->whensent, 'report marked to resend';
+
+ my @comments = $report->comments;
+ my $comment = $comments[0];
+ my $extra = $comment->get_extra_metadata();
+ is $extra->{triage_report}, 1, 'comment indicates it is for triage in extra';
+ is $extra->{holding_category}, 'Potholes', 'comment extra has previous category';
+ is $extra->{new_category}, 'Traffic lights', 'comment extra has new category';
+
+ $mech->get_ok($report_url);
+ $mech->content_contains('Report triaged from Potholes to Traffic lights');
+
+ $mech->log_out_ok;
+ $mech->get_ok($report_url);
+ $mech->content_lacks('Report triaged from Potholes to Traffic lights');
+ };
+};
+
+done_testing();
diff --git a/t/cobrand/isleofwight.t b/t/cobrand/isleofwight.t
index 6c6450028..414b683cd 100644
--- a/t/cobrand/isleofwight.t
+++ b/t/cobrand/isleofwight.t
@@ -212,7 +212,6 @@ subtest "fixing passes along the correct message" => sub {
};
};
-
subtest 'Check special Open311 request handling', sub {
my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, 'Title', { category => 'Potholes', latitude => 50.7108, longitude => -1.29573 });
$p->set_extra_fields({ name => 'urgent', value => 'no'});
@@ -237,6 +236,44 @@ subtest 'Check special Open311 request handling', sub {
is $c->param('attribute[urgent]'), undef, 'no urgent param sent';
};
+subtest "triaging a report includes user in message" => sub {
+ FixMyStreet::override_config {
+ MAPIT_URL => 'http://mapit.uk/',
+ ALLOWED_COBRANDS => 'isleofwight',
+ }, sub {
+ my $test_res = HTTP::Response->new();
+ $test_res->code(200);
+ $test_res->message('OK');
+ $test_res->content('<?xml version="1.0" encoding="utf-8"?><service_request_updates><request_update><update_id>248</update_id></request_update></service_request_updates>');
+
+ my $o = Open311->new(
+ fixmystreet_body => $isleofwight,
+ test_mode => 1,
+ test_get_returns => { 'servicerequestupdates.xml' => $test_res },
+ );
+
+ my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, 'Title', { external_id => 1 });
+
+ my $c = FixMyStreet::App->model('DB::Comment')->create({
+ problem => $p, user => $p->user, anonymous => 't', text => 'Update text',
+ problem_state => 'confirmed', state => 'confirmed', mark_fixed => 0,
+ confirmed => DateTime->now(),
+ });
+ $c->set_extra_metadata('triage_report', 1);
+ $c->update;
+
+ my $id = $o->post_service_request_update($c);
+ is $id, 248, 'correct update ID returned';
+ my $cgi = CGI::Simple->new($o->test_req_used->content);
+ my $name = $p->user->name . ' \(' . $p->user->email . '\)';
+ like $cgi->param('description'), qr/^FMS-Update:/, 'FMS update prefix included';
+ like $cgi->param('description'), qr/Triaged by $name/, 'Triage user details included';
+
+ $p->comments->delete;
+ $p->delete;
+ };
+};
+
my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, '', { cobrand => 'isleofwight' });
my $alert = FixMyStreet::App->model('DB::Alert')->create( {
parameter => $p->id,
diff --git a/templates/email/isleofwight/other-reported.html b/templates/email/isleofwight/other-reported.html
new file mode 100644
index 000000000..8e85c5729
--- /dev/null
+++ b/templates/email/isleofwight/other-reported.html
@@ -0,0 +1,42 @@
+[%
+
+email_summary = "Thanks for logging your report";
+email_columns = 2;
+
+PROCESS '_email_settings.html';
+INCLUDE '_email_top.html';
+
+%]
+
+<th style="[% td_style %][% primary_column_style %]" id="primary_column">
+ [% start_padded_box %]
+ <h1 style="[% h1_style %]">Your report has been&nbsp;logged</h1>
+ [% IF report.state == 'for triage' %]
+ <p style="[% p_style %]">Thank you for submitting your report to FixMyStreet, it will be submitted to Island Roads for review.</p>
+ [% ELSE %]
+ <p style="[% p_style %]">Thank you, your enquiry has been received by Island Roads and appropriate action will be taken.</p>
+
+ <p style="[% p_style %]">We don't routinely contact customers regarding their enquiries, unless we have a specific query about
+ the issue. Any status updates for the issue can be tracked via FixMyStreet, if you contact us again about this issue,
+ please quote your 8-digit reference number.</p>
+
+ <p style="[% p_style %]">Thank you for submitting your enquiry to us via FixMyStreet.</p>
+ [% END %]
+
+[% IF cobrand.is_council && !cobrand.owns_problem( report ) %]
+<p style="[% p_style %]">Please note that [% cobrand.council_name %] is not responsible for this type
+of report, so it will instead be sent to [% report.body %].</p>
+[% ELSE %]
+[% TRY %][% INCLUDE '_council_reference.html' problem=report %][% CATCH file %][% END %]
+[% END %]
+ <p style="margin: 20px auto; text-align: center">
+ <a style="[% button_style %]" href="[% cobrand.base_url_for_report(report) %][% report.url %]">View my report</a>
+ </p>
+ [% end_padded_box %]
+</th>
+[% WRAPPER '_email_sidebar.html' object = report %]
+ <h2 style="[% h2_style %]">[% report.title | html %]</h2>
+ <p style="[% secondary_p_style %]">[% report.detail | html %]</p>
+[% END %]
+
+[% INCLUDE '_email_bottom.html' %]
diff --git a/templates/email/isleofwight/other-reported.txt b/templates/email/isleofwight/other-reported.txt
new file mode 100644
index 000000000..a368bd95e
--- /dev/null
+++ b/templates/email/isleofwight/other-reported.txt
@@ -0,0 +1,43 @@
+Subject: Your report has been logged: [% report.title %]
+
+Hello [% report.name %],
+
+[% IF report.state == 'for triage' %]
+Thank you for submitting your report to FixMyStreet, it will be
+submitted to Island Roads for review.
+[% ELSE %]
+Thank you, your enquiry has been received by Island Roads and
+appropriate action will be taken.
+
+We don't routinely contact customers regarding their enquiries,
+unless we have a specific query about the issue. Any status
+updates for the issue can be tracked via FixMyStreet, if you
+contact us again about this issue, please quote your 8-digit
+reference number.
+
+Thank you for submitting your enquiry to us via FixMyStreet.
+[% END %]
+
+[% IF cobrand.is_council && !cobrand.owns_problem( report ) %]
+Please note that [% cobrand.council_name %] is not responsible for this type
+of report, so it will instead be sent to [% report.body %].
+[% ELSE %]
+[% TRY %][% INCLUDE '_council_reference.txt' problem=report %][% CATCH file %][% END %]
+[% END %]
+
+It is available to view at:
+
+[% cobrand.base_url_for_report(report) %][% report.url %]
+
+Your report has the title:
+
+[% report.title %]
+
+And details:
+
+[% report.detail %]
+
+[% signature %]
+
+This email was sent automatically, from an unmonitored email account - so
+please do not reply to it.
diff --git a/templates/web/base/admin/report-category.html b/templates/web/base/admin/report-category.html
index 0416d71c0..64aa474b9 100644
--- a/templates/web/base/admin/report-category.html
+++ b/templates/web/base/admin/report-category.html
@@ -1,4 +1,7 @@
-<select class="form-control" name="category" id="category">
+[%~ IF NOT select_name %]
+ [%~ select_name = 'category' %]
+[%~ END %]
+<select class="form-control" name="[% select_name %]" id="[% select_name %]">
[% IF NOT problem.category OR NOT categories_hash.${problem.category} %]
<optgroup label="[% loc('Existing category') %]">
<option selected value="[% problem.category | html %]">[% (problem.category_display OR '-') | html %]</option>
diff --git a/templates/web/base/admin/triage/_inspect.html b/templates/web/base/admin/triage/_inspect.html
new file mode 100644
index 000000000..926197ceb
--- /dev/null
+++ b/templates/web/base/admin/triage/_inspect.html
@@ -0,0 +1,77 @@
+[% BLOCK category_list %]
+<select class="form-control" name="[% field_name %]" id="[% field_name %]">
+ [% IF category_options.size %]
+ [%~ IF category_groups.size ~%]
+ [%~ FOREACH group IN category_groups ~%]
+ [% IF group.name %]<optgroup label="[% group.name %]">[% END %]
+ [% group_select = 0 %]
+ [% selected = 0 %]
+ [%~ FOREACH cat_op IN group.categories ~%]
+ [% IF group_select == 0 AND problem.category == group.name %]
+ [% selected = 1; group_select = 1 %]
+ [% END %]
+ <option value="[% cat_op.category | html %]"[% ' selected' IF selected OR problem.category == cat_op.category %]>[% cat_op.category_display | html %] ([% cat_op.email | html %])</option>
+ [% selected = 0 %]
+ [%~ END ~%]
+ [% IF group.name %]</optgroup>[% END %]
+ [%~ END =%]
+ [% ELSE %]
+ [% FOREACH cat IN category_options %]
+ <option value="[% cat.category | html %]"[% ' selected' IF problem.category == cat.category %]>[% cat.category_display | html %]</option>
+ [% END %]
+ [% END %]
+ [% END %]
+</select>
+[% END %]
+
+[% permissions = c.user.permissions(problem) %]
+[% second_column = BLOCK -%]
+ <div id="side-inspect">
+
+ <h2 class="inspect-form-heading">[% loc('Inspect report') %]</h2>
+
+ [% INCLUDE 'errors.html' %]
+
+ <form name="report_inspect_form" id="report_inspect_form" method="post" action="[% c.uri_for( '/report', problem.id ) %]" class="validate">
+
+ [% INCLUDE 'report/inspect/information.html' no_relocate=1 %]
+
+ [% IF permissions.report_edit_category OR permissions.report_inspect OR permissions.triage %]
+ <div class="inspect-section">
+ <p>
+ <strong>Holding category:</strong> [% problem.category %]
+ </p>
+
+ <p>
+ <label for="category">CONFIRM Subject</label>
+ [% PROCESS category_list category_groups=end_groups field_name="category" categories_hash=end_categories category_options=end_options %]
+ </p>
+
+ </div>
+ [% END %]
+
+ <div class="inspect-section">
+ [%- extra_fields = problem.get_extra_fields -%]
+ [% FOR field IN extra_fields %]
+ [% NEXT IF field.name == 'urgent' %]
+ <p>
+ <label for="[% field.name %]">[% field.description %]</label>
+ <input class="form-control" name="[% field.name %]" type="text" value="[% field.value | html %]" disabled>
+ </p>
+ [% END %]
+
+ [% IF permissions.report_inspect OR permissions.triage %]
+ [% INCLUDE 'report/inspect/public_update.html' public_update_defaulted=0 %]
+ [% END %]
+
+ <p>
+ <input type="hidden" name="token" value="[% csrf_token %]" />
+ <input class="btn btn-primary" type="submit" value="[% loc('Save changes') %]" data-value-original="[% loc('Save changes') %]" data-value-duplicate="[% loc('Save + close as duplicate') %]" name="save" />
+ </p>
+ </div>
+
+ <input type="hidden" name="js" value="">
+ <input type="hidden" name="triage" value="1">
+ </form>
+ </div>
+[%- END %]
diff --git a/templates/web/base/admin/triage/_list-filters.html b/templates/web/base/admin/triage/_list-filters.html
new file mode 100644
index 000000000..f6bedcb80
--- /dev/null
+++ b/templates/web/base/admin/triage/_list-filters.html
@@ -0,0 +1,53 @@
+[% select_category = BLOCK %]
+ [% IF filter_categories.size %]
+ <select class="form-control js-multiple" name="filter_category" id="filter_categories" multiple data-all="[% loc('Everything') %]">
+ [% FOR cat IN filter_categories %]
+ <option value="[% cat.category | html %]"[% ' selected' IF filter_category.${cat.category} %]>
+ [% cat.category_display | html %]
+ [%~ IF cat.get_extra_metadata('help_text') %] ([% cat.get_extra_metadata('help_text') %])[% END ~%]
+ </option>
+ [% END %]
+ </select>
+ [% ELSE %]
+ [% loc('Everything') %]
+ [% END %]
+[% END %]
+
+<div class="report-list-filters-wrapper">
+
+[% IF use_form_wrapper %]
+ <form method="get" action="">
+[% END %]
+
+ <p class="report-list-filters">
+ [% tprintf(loc('<label for="statuses">Show</label> %s reports <label for="filter_categories">about</label> %s', "The first %s is a dropdown of all/fixed/etc, the second is a dropdown of categories"), 'untriaged', select_category) %]
+ <input type="submit" name="filter_update" value="[% loc('Go') %]">
+ </p>
+
+ <p class="report-list-filters">
+ <label for="sort">[% loc('Sort by') %]</label>
+ <select class="form-control" name="sort" id="sort">
+ [% IF shortlist %]
+ <option value="shortlist"[% ' selected' IF sort_key == 'shortlist' %]>[% loc('Manual order') %]</option>
+ [% END %]
+ <option value="created-desc"[% ' selected' IF sort_key == 'created-desc' %]>[% loc('Newest') %]</option>
+ <option value="created-asc"[% ' selected' IF sort_key == 'created-asc' %]>[% loc('Oldest') %]</option>
+ <option value="updated-desc"[% ' selected' IF sort_key == 'updated-desc' %]>[% loc('Recently updated') %]</option>
+ <option value="updated-asc"[% ' selected' IF sort_key == 'updated-asc' %]>[% loc('Least recently updated') %]</option>
+ <option value="comments-desc"[% ' selected' IF sort_key == 'comments-desc' %]>[% loc('Most commented') %]</option>
+ </select>
+ <input type="submit" name="filter_update" value="[% loc('Go') %]">
+ </p>
+ [% IF page == 'around' %]
+ <p id="show_old_reports_wrapper" class="report-list-filters[% ' hidden' UNLESS num_old_reports > 0 %]">
+ <label for="show_old_reports">[% loc('Show older reports') %]</label>
+ <input type="checkbox" name="show_old_reports" id="show_old_reports" value="1"[% ' checked' IF show_old_reports %]>
+ <input type="submit" name="filter_update" value="[% loc('Go') %]">
+ </p>
+ [% END %]
+
+[% IF use_form_wrapper %]
+ </form>
+[% END %]
+
+</div>
diff --git a/templates/web/base/admin/triage/index.html b/templates/web/base/admin/triage/index.html
new file mode 100644
index 000000000..f00bbc1fa
--- /dev/null
+++ b/templates/web/base/admin/triage/index.html
@@ -0,0 +1,42 @@
+[% SET body_name = body.name %]
+[% IF c.cobrand.moniker == 'hounslow' %]
+ [% SET body_name = 'Hounslow Highways' %]
+[% ELSIF c.cobrand.moniker == 'isleofwight' %]
+ [% SET body_name = 'Island Roads' %]
+[% END %]
+
+[%
+ PROCESS "report/photo-js.html";
+ PROCESS "maps/${map.type}.html";
+ SET bodyclass = 'mappage';
+ INCLUDE 'header.html',
+ title = 'Awaiting triage'
+ rss = [ tprintf(loc('Problems within %s, %s', "First %s is the body name, second %s the site name"), name, site_name), rss_url ]
+%]
+
+[% map_html %]
+
+</div>
+<div id="map_sidebar">
+ <div id="side">
+
+ <h1 id="reports_heading">
+ Awaiting triage
+ </h1>
+
+<section class="full-width">
+[% INCLUDE "admin/triage/_list-filters.html", use_form_wrapper = 1 %]
+<div class="js-pagination">
+[% INCLUDE 'pagination.html', param = 'p' %]
+</div>
+<div id="js-reports-list">
+ [% INCLUDE 'reports/_problem-list.html' %]
+</div>
+<div class="js-pagination">
+[% INCLUDE 'pagination.html', param = 'p' %]
+</div>
+</section>
+
+ </div>
+</div>
+[% INCLUDE 'footer.html' %]
diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html
index fa79d9912..10f3b6b84 100644
--- a/templates/web/base/report/_inspect.html
+++ b/templates/web/base/report/_inspect.html
@@ -11,60 +11,7 @@
<form name="report_inspect_form" id="report_inspect_form" method="post" action="[% c.uri_for( '/report', problem.id ) %]" class="validate">
- <div class="inspect-section">
- <p style="float: right">
- <label for="non_public">[% loc('Private') %]</label>
- <input type="checkbox" id="non_public" name="non_public" value="1"[% ' checked' IF problem.non_public %]>
- </p>
- <p>
- <strong>[% loc('Report ID:') %]</strong>
- <span class="js-report-id">[% problem.id %]</span>
- [% IF c.user_exists AND c.cobrand.admin_allow_user(c.user) AND c.user.has_permission_to('report_edit', problem.bodies_str_ids) %]
- (<a href="[% c.uri_for_action( "admin/report_edit", problem.id ) %]">[% loc('admin') %]</a>)
- [% END %]
- </p>
- [% IF permissions.report_inspect AND problem.user.phone %]
- <p>
- <strong>[% loc('Phone Reporter:') %]</strong>
- <a href="tel:[% problem.user.phone | html %]">[% problem.user.phone_display | html %]</a>
- </p>
- [% END %]
- <p>
- [% SET local_coords = problem.local_coords; %]
- [% IF local_coords %]
- <strong>[% loc('Easting/Northing:') %]</strong>
- <span id="problem_easting">[% local_coords.0 %]</span>,
- <span id="problem_northing">[% local_coords.1 %]</span>
- [% ELSE %]
- <strong>[% loc('Latitude/Longitude:') %]</strong>
- <span id="problem_latitude">[% problem.latitude %]</span>,
- <span id="problem_longitude">[% problem.longitude %]</span>
- [% END %]
- <input type="hidden" name="longitude" value="[% problem.longitude %]">
- <input type="hidden" name="latitude" value="[% problem.latitude %]">
- </p>
- [% IF problem.nearest_address() %]
- <p>
- <strong>[% loc('Nearest calculated address:') %]</strong>
- [% problem.nearest_address() %]
- </p>
- [% END %]
- <p>
- <a target="_blank" href="https://www.google.com/maps/dir/?api=1&destination=[% problem.latitude %],[% problem.longitude %]" class="btn btn--block btn--navigate">[% loc('Navigate to this problem') %]</a>
- </p>
- <p>
- <a href="#" class="btn btn--block btn--geolocate">[% loc('Set to my current location') %]</a>
- </p>
- [% IF permissions.report_reject %]
- [% TRY %]
- [% INCLUDE 'report/_inspect_reject_button.html' %]
- [% CATCH file %]
- <p>
- <a href="[% c.uri_for( '/contact', { id => problem.id, reject => 1 } ) %]" class="btn btn--block">[% loc('Reject report') %]</a>
- </p>
- [% END %]
- [% END %]
- </div>
+ [% INCLUDE 'report/inspect/information.html' %]
[% IF permissions.report_edit_category OR permissions.report_inspect %]
<div class="inspect-section">
@@ -182,17 +129,7 @@
<div class="inspect-section">
[% IF permissions.report_inspect %]
- <p>
- <label class="label-containing-checkbox">
- <input type="checkbox" name="include_update" value="1" class="js-toggle-public-update" checked>
- [% loc('Save with a public update') %]
- </label>
- </p>
- <p>
- <label for="public_update">[% loc('Public update:') %]</label>
- [% INCLUDE 'admin/response_templates_select.html' for='public_update' %]
- <textarea rows="2" name="public_update" id="public_update" class="form-control">[% public_update | html %]</textarea>
- </p>
+ [% INCLUDE 'report/inspect/public_update.html' %]
[% END %]
[% IF problem.get_extra_metadata('inspected') %]
diff --git a/templates/web/base/report/inspect/information.html b/templates/web/base/report/inspect/information.html
new file mode 100644
index 000000000..cc8989522
--- /dev/null
+++ b/templates/web/base/report/inspect/information.html
@@ -0,0 +1,56 @@
+ <div class="inspect-section">
+ <p style="float: right">
+ <label for="non_public">[% loc('Private') %]</label>
+ <input type="checkbox" id="non_public" name="non_public" value="1"[% ' checked' IF problem.non_public %]>
+ </p>
+ <p>
+ <strong>[% loc('Report ID:') %]</strong>
+ <span class="js-report-id">[% problem.id %]</span>
+ [% IF c.user_exists AND c.cobrand.admin_allow_user(c.user) AND c.user.has_permission_to('report_edit', problem.bodies_str_ids) %]
+ (<a href="[% c.uri_for_action( "admin/report_edit", problem.id ) %]">[% loc('admin') %]</a>)
+ [% END %]
+ </p>
+ [% IF permissions.report_inspect AND problem.user.phone %]
+ <p>
+ <strong>[% loc('Phone Reporter:') %]</strong>
+ <a href="tel:[% problem.user.phone | html %]">[% problem.user.phone_display | html %]</a>
+ </p>
+ [% END %]
+ <p>
+ [% SET local_coords = problem.local_coords; %]
+ [% IF local_coords %]
+ <strong>[% loc('Easting/Northing:') %]</strong>
+ <span id="problem_easting">[% local_coords.0 %]</span>,
+ <span id="problem_northing">[% local_coords.1 %]</span>
+ [% ELSE %]
+ <strong>[% loc('Latitude/Longitude:') %]</strong>
+ <span id="problem_latitude">[% problem.latitude %]</span>,
+ <span id="problem_longitude">[% problem.longitude %]</span>
+ [% END %]
+ <input type="hidden" name="longitude" value="[% problem.longitude %]">
+ <input type="hidden" name="latitude" value="[% problem.latitude %]">
+ </p>
+ [% IF problem.nearest_address() %]
+ <p>
+ <strong>[% loc('Nearest calculated address:') %]</strong>
+ [% problem.nearest_address() %]
+ </p>
+ [% END %]
+ <p>
+ <a target="_blank" href="https://www.google.com/maps/dir/?api=1&destination=[% problem.latitude %],[% problem.longitude %]" class="btn btn--block btn--navigate">[% loc('Navigate to this problem') %]</a>
+ </p>
+ [% UNLESS no_relocate %]
+ <p>
+ <a href="#" class="btn btn--block btn--geolocate">[% loc('Set to my current location') %]</a>
+ </p>
+ [% END %]
+ [% IF permissions.report_reject %]
+ [% TRY %]
+ [% INCLUDE 'report/_inspect_reject_button.html' %]
+ [% CATCH file %]
+ <p>
+ <a href="[% c.uri_for( '/contact', { id => problem.id, reject => 1 } ) %]" class="btn btn--block">[% loc('Reject report') %]</a>
+ </p>
+ [% END %]
+ [% END %]
+ </div>
diff --git a/templates/web/base/report/inspect/public_update.html b/templates/web/base/report/inspect/public_update.html
new file mode 100644
index 000000000..be07d7b89
--- /dev/null
+++ b/templates/web/base/report/inspect/public_update.html
@@ -0,0 +1,14 @@
+[% IF NOT public_update_defaulted.defined %]
+ [% public_update_defaulted = 1 %]
+[% END %]
+ <p>
+ <label class="label-containing-checkbox">
+ <input type="checkbox" name="include_update" value="1" class="js-toggle-public-update"[% ' checked' IF public_update_defaulted %]>
+ [% loc('Save with a public update') %]
+ </label>
+ </p>
+ <p>
+ <label for="public_update">[% loc('Public update:') %]</label>
+ [% INCLUDE 'admin/response_templates_select.html' for='public_update' %]
+ <textarea rows="2" name="public_update" id="public_update" class="form-control">[% public_update | html %]</textarea>
+ </p>
diff --git a/templates/web/base/report/updates.html b/templates/web/base/report/updates.html
index 817cc7eb4..93bae4d64 100644
--- a/templates/web/base/report/updates.html
+++ b/templates/web/base/report/updates.html
@@ -6,6 +6,9 @@
[%- END %]
[%- NEXT %]
[%- END %]
+[%- IF update.get_extra_metadata('triage_report') == 1 AND ( NOT c.user OR ( NOT c.user.from_body AND NOT c.user.is_superuser ) ) %]
+ [%- NEXT %]
+[%- END %]
[% INCLUDE 'report/update.html' %]
[% END %]
diff --git a/templates/web/isleofwight/main_nav_items.html b/templates/web/isleofwight/main_nav_items.html
new file mode 100644
index 000000000..5b794a99d
--- /dev/null
+++ b/templates/web/isleofwight/main_nav_items.html
@@ -0,0 +1,59 @@
+[%~ IF problem ~%]
+ [%~ INCLUDE navitem uri='/report/new?longitude=' _ problem.longitude _ '&amp;latitude=' _ problem.latitude label=loc('Report another problem here') attrs='class="report-a-problem-btn"' ~%]
+[%~ ELSIF latitude AND longitude ~%]
+ [%~ INCLUDE navitem uri='/report/new?longitude=' _ longitude _ '&amp;latitude=' _ latitude label=loc('Report a problem here') attrs='class="report-a-problem-btn"' ~%]
+[%~ ELSIF homepage_template ~%]
+ [%~ INCLUDE navitem uri='/report' label=loc('Report a problem') attrs='class="report-a-problem-btn"' ~%]
+[%~ ELSE ~%]
+ [%~ INCLUDE navitem uri='/' label=loc('Report a problem') attrs='class="report-a-problem-btn"' ~%]
+[%~ END ~%]
+
+[%~ IF c.user_exists ~%]
+ [%~ INCLUDE navitem uri='/my' label=loc('Your account') ~%]
+[%~ ELSE ~%]
+ [%~ INCLUDE navitem uri='/auth' label=loc('Sign in') ~%]
+[%~ END ~%]
+
+[%~ IF c.user_exists AND c.user.has_body_permission_to('planned_reports') ~%]
+ [%~ INCLUDE navitem always_url=1 uri='/my/planned' label=loc('Shortlist') ~%]
+[%~ END ~%]
+
+[%~ IF c.user_exists AND c.user.has_body_permission_to('triage') ~%]
+ [%~ INCLUDE navitem always_url=1 uri='/admin/triage' label=loc('Awaiting triage') ~%]
+[%~ END ~%]
+
+[%~ UNLESS hide_all_reports_link ~%]
+ [%~
+ IF c.user_exists AND c.user.categories.size;
+ categories = c.user.categories_string | uri;
+ cat_suffix = "?filter_category=" _ categories;
+ END;
+
+ reports_uri = '/reports';
+ IF c.cobrand.council_area;
+ body_name = c.cobrand.council_area | uri;
+ reports_uri = "${reports_uri}/${body_name}";
+ END;
+
+ INCLUDE navitem uri=reports_uri label=loc('All reports') suffix=cat_suffix;
+ ~%]
+[%~ END ~%]
+
+[%~
+ IF pc;
+ pc_uri = pc | uri;
+ pc_suffix = "/list?pc=" _ pc_uri;
+ END;
+
+ INCLUDE navitem uri='/alert' label=loc('Local alerts') suffix=pc_suffix;
+~%]
+
+[%~ INCLUDE navitem uri='/faq' label=loc('Help') ~%]
+
+[%~ UNLESS hide_privacy_link ~%]
+ [%~ INCLUDE navitem uri=c.cobrand.privacy_policy_url label=loc('Privacy') liattrs='class="nav-menu__item--privacy"' ~%]
+[%~ END ~%]
+
+[%~ IF c.user_exists AND c.cobrand.admin_allow_user(c.user) ~%]
+ [%~ INCLUDE navitem uri='/admin' label=loc('Admin') ~%]
+[%~ END ~%]
diff --git a/templates/web/isleofwight/report/inspect.html b/templates/web/isleofwight/report/inspect.html
new file mode 100644
index 000000000..6cecff926
--- /dev/null
+++ b/templates/web/isleofwight/report/inspect.html
@@ -0,0 +1,10 @@
+[%
+ SET bodyclass = 'mappage with-actions';
+ SET two_column_sidebar = 1;
+ IF problem.state == 'for triage';
+ PROCESS 'admin/triage/_inspect.html';
+ ELSE;
+ PROCESS 'report/_inspect.html';
+ END;
+ INCLUDE 'report/display.html'
+%]
diff --git a/web/cobrands/fixmystreet/staff.js b/web/cobrands/fixmystreet/staff.js
index b074a49f3..17bd54b8b 100644
--- a/web/cobrands/fixmystreet/staff.js
+++ b/web/cobrands/fixmystreet/staff.js
@@ -261,19 +261,22 @@ fixmystreet.staff_set_up = {
if ('geolocation' in navigator) {
var el = document.querySelector('.btn--geolocate');
- fixmystreet.geolocate(el, function(pos) {
- var latlon = new OpenLayers.LonLat(pos.coords.longitude, pos.coords.latitude);
- var bng = latlon.clone().transform(
- new OpenLayers.Projection("EPSG:4326"),
- new OpenLayers.Projection("EPSG:27700") // TODO: Handle other projections
- );
- $("#problem_northing").text(bng.lat.toFixed(1));
- $("#problem_easting").text(bng.lon.toFixed(1));
- $("#problem_latitude").text(latlon.lat.toFixed(6));
- $("#problem_longitude").text(latlon.lon.toFixed(6));
- $inspect_form.find("input[name=latitude]").val(latlon.lat);
- $inspect_form.find("input[name=longitude]").val(latlon.lon);
- });
+ // triage pages may not show geolocation button
+ if (el) {
+ fixmystreet.geolocate(el, function(pos) {
+ var latlon = new OpenLayers.LonLat(pos.coords.longitude, pos.coords.latitude);
+ var bng = latlon.clone().transform(
+ new OpenLayers.Projection("EPSG:4326"),
+ new OpenLayers.Projection("EPSG:27700") // TODO: Handle other projections
+ );
+ $("#problem_northing").text(bng.lat.toFixed(1));
+ $("#problem_easting").text(bng.lon.toFixed(1));
+ $("#problem_latitude").text(latlon.lat.toFixed(6));
+ $("#problem_longitude").text(latlon.lon.toFixed(6));
+ $inspect_form.find("input[name=latitude]").val(latlon.lat);
+ $inspect_form.find("input[name=longitude]").val(latlon.lon);
+ });
+ }
}
// Make the "Provide an update" form toggleable, hidden by default.
diff --git a/web/cobrands/isleofwight/assets.js b/web/cobrands/isleofwight/assets.js
index 3df034f0c..a68b0418b 100644
--- a/web/cobrands/isleofwight/assets.js
+++ b/web/cobrands/isleofwight/assets.js
@@ -97,17 +97,26 @@ fixmystreet.assets.add($.extend(true, {}, defaults, {
})
}));
+function not_found_msg_update() {
+ $('.category_meta_message').html('Please select an item or a road/pavement/path on the map &raquo;');
+ $('.category_meta_message').removeClass('meta-highlight');
+ $("input[name=asset_details]").val('');
+}
+
function found_item(layer, asset) {
var id = asset.attributes.central_asset_id || '';
if (id !== '') {
var attrib = asset.attributes;
var asset_name = attrib.feature_type_name + '; ' + attrib.site_name + '; ' + attrib.feature_location;
$('.category_meta_message').html('You have selected ' + asset_name);
+ $('.category_meta_message').addClass('meta-highlight');
+ $("input[name=asset_details]").val(asset_name);
} else {
- $('.category_meta_message').html('Please select an item or a road/pavement/path on the map &raquo;');
+ not_found_msg_update();
}
}
+
var point_asset_defaults = $.extend(true, {}, defaults, {
snap_threshold: 5,
select_action: true,
@@ -118,7 +127,7 @@ var point_asset_defaults = $.extend(true, {}, defaults, {
found_item(this, asset);
},
asset_not_found: function() {
- $('.category_meta_message').html('Please select an item or a road/pavement/path on the map &raquo;');
+ not_found_msg_update();
}
}
@@ -145,7 +154,7 @@ var line_asset_defaults = $.extend(true, {}, defaults, {
if ( fixmystreet.assets.selectedFeature() ) {
return;
}
- $('.category_meta_message').html('Please select an item or a road/pavement/path on the map &raquo;');
+ not_found_msg_update();
}
}
});