diff options
72 files changed, 2987 insertions, 140 deletions
diff --git a/.cypress/cypress/fixtures/iow_roadworks.json b/.cypress/cypress/fixtures/iow_roadworks.json new file mode 100644 index 000000000..b20dd8514 --- /dev/null +++ b/.cypress/cypress/fixtures/iow_roadworks.json @@ -0,0 +1,97 @@ +{ + "query": { + "recordcount": 38, + "columnlist": "se_id,phase_id,u_se_id,longitude,latitude,gsymbol_id,tooltip,swtype,org_name_disp,promoter,source,promoter_works_ref,originator_ref,swa_org_ref,promoter_org_ref,publisher_orgref,publisher_organisation_id,promoter_organisation_id,organisation_id,lha_id,start_date,end_date,works_state,impact,works_desc,geom_type,geojson_wgs84,tm_cat", + "data": { + "se_id": [ + 114013162 + ], + "phase_id": [ + 1 + ], + "u_se_id": [ + "114013162:1" + ], + "longitude": [ + -1.29574 + ], + "latitude": [ + 50.71086 + ], + "gsymbol_id": [ + "gcln01y" + ], + "tooltip": [ + "B3401 Whitepit Lane, Newport, Isle of Wight\\n18 Jun 2019 - 23 Jul 2019\\ndelays unlikely" + ], + "swtype": [ + "cw" + ], + "org_name_disp": [ + "Island Roads on behalf of the Isle of Wight Council" + ], + "promoter": [ + "Island Roads on behalf of the Isle of Wight Council" + ], + "source": [ + "SW" + ], + "promoter_works_ref": [ + "RF101BT0103000021728" + ], + "originator_ref": [ + "RF101BT0103000021728" + ], + "swa_org_ref": [ + 2114 + ], + "promoter_org_ref": [ + 2114 + ], + "publisher_orgref": [ + 2114 + ], + "publisher_organisation_id": [ + 1062 + ], + "promoter_organisation_id": [ + 1062 + ], + "organisation_id": [ + 1062 + ], + "lha_id": [ + 62 + ], + "start_date": [ + "{ts '2019-06-18 00:00:00'}" + ], + "end_date": [ + "{ts '2030-07-23 23:59:59'}" + ], + "works_state": [ + 4 + ], + "impact": [ + 1 + ], + "works_desc": [ + "Parapet improvement NEWPORT" + ], + "geom_type": [ + 1 + ], + "geojson_wgs84": [ + "{\"type\":\"Point\",\"coordinates\":[-1.295784,50.71086]}" + ], + "tm_cat": [ + "" + ] + } + }, + "reqtoken": "", + "filterimpact": "1,2,3,4", + "filterenddate": "16/10/2019", + "filterstartdate": "16/07/2019", + "timeperiod": "cw" +} diff --git a/.cypress/cypress/integration/isleofwight.js b/.cypress/cypress/integration/isleofwight.js new file mode 100644 index 000000000..daa69f36a --- /dev/null +++ b/.cypress/cypress/integration/isleofwight.js @@ -0,0 +1,35 @@ +describe('When you look at the Island Roads site', function() { + + beforeEach(function() { + cy.server(); + cy.fixture('roads.xml'); + cy.route('/report/new/ajax*').as('report-ajax'); + cy.visit('http://isleofwight.localhost:3001/'); + cy.contains('Island Roads'); + cy.get('[name=pc]').type('PO30 5XJ'); + cy.get('[name=pc]').parents('form').submit(); + }); + + it('uses the correct name', function() { + cy.get('#map_box').click(); + cy.wait('@report-ajax'); + cy.get('select:eq(4)').select('Potholes'); + cy.contains('sent to Island Roads'); + cy.get('select:eq(4)').select('Private'); + cy.contains('sent to Island Roads'); + cy.get('select:eq(4)').select('Extra'); + cy.contains('Help Island Roads'); + }); + + it('displays nearby roadworks', function() { + cy.fixture('iow_roadworks.json'); + cy.route('/data/**', 'fixture:iow_roadworks.json').as('roadworks'); + cy.visit('http://isleofwight.localhost:3001/'); + cy.get('[name=pc]').type('PO30 5XJ'); + cy.get('[name=pc]').parents('form').submit(); + cy.get('#map_box').click(); + cy.wait('@report-ajax'); + cy.wait('@roadworks'); + cy.contains('Roadworks are scheduled near this location'); + }); +}); diff --git a/bin/browser-tests b/bin/browser-tests index 013c0d8d3..0180db0b5 100755 --- a/bin/browser-tests +++ b/bin/browser-tests @@ -11,7 +11,7 @@ my ($cobrand, $coords, $area_id, $name, $mapit_url); BEGIN { $config_file = 'conf/general.yml-example'; - $cobrand = [ 'borsetshire', 'fixmystreet', 'northamptonshire', 'bathnes', 'buckinghamshire', 'hounslow' ]; + $cobrand = [ 'borsetshire', 'fixmystreet', 'northamptonshire', 'bathnes', 'buckinghamshire', 'hounslow', 'isleofwight' ]; $coords = '51.532851,-2.284277'; $area_id = 2608; $name = 'Borsetshire'; @@ -150,7 +150,7 @@ browser-tests [running options] [fixture options] [cypress options] --help this help message Fixture option: - --cobrand Cobrand(s) to use, default is fixmystreet,northamptonshire,bathnes,buckinghamshire + --cobrand Cobrand(s) to use, default is fixmystreet,northamptonshire,bathnes,buckinghamshire,isleofwight --coords Default co-ordinates for created reports --area_id Area ID to use for created body --name Name to use for created body diff --git a/bin/fixmystreet.com/fixture b/bin/fixmystreet.com/fixture index 760c76414..1f6063a47 100755 --- a/bin/fixmystreet.com/fixture +++ b/bin/fixmystreet.com/fixture @@ -99,6 +99,7 @@ if ($opt->test_fixtures) { { area_id => 2257, categories => ['Flytipping', 'Graffiti'], name => 'Chiltern District Council' }, { area_id => 2397, categories => [ 'Graffiti' ], name => 'Northampton Borough Council' }, { area_id => 2483, categories => [ 'Potholes', 'Other' ], name => 'Hounslow Borough Council' }, + { area_id => 2636, categories => [ 'Potholes', 'Private', 'Extra' ], name => 'Isle of Wight Council' }, ) { $bodies->{$_->{area_id}} = FixMyStreet::DB::Factory::Body->find_or_create($_); my $cats = join(', ', @{$_->{categories}}); @@ -144,6 +145,36 @@ if ($opt->test_fixtures) { $child_cat->update({ non_public => 1 }); + + $child_cat = FixMyStreet::DB->resultset("Contact")->find({ + body => $bodies->{2636}, + category => 'Potholes', + }); + $child_cat->update({ + send_method => 'Triage' + }); + $child_cat = FixMyStreet::DB->resultset("Contact")->find({ + body => $bodies->{2636}, + category => 'Private', + }); + $child_cat->update({ + non_public => 1, + send_method => 'Triage' + }); + $child_cat = FixMyStreet::DB->resultset("Contact")->find({ + body => $bodies->{2636}, + category => 'Extra', + }); + $child_cat->update({ + send_method => 'Triage' + }); + $child_cat->set_extra_fields({ + code => 'extra', + datatype => 'string', + order => 1, + variable => 'true', + }); + $child_cat->update; } FixMyStreet::DB::Factory::ResponseTemplate->create({ diff --git a/bin/fixmystreet.com/island_roads_setup_messages b/bin/fixmystreet.com/island_roads_setup_messages new file mode 100644 index 000000000..4969f18ba --- /dev/null +++ b/bin/fixmystreet.com/island_roads_setup_messages @@ -0,0 +1,73 @@ +#!/usr/bin/env perl +# +# One off script to transfer the hardcoded JS messages to the database + +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::DB; + +use Getopt::Long; + +my $commit; +GetOptions( + 'commit' => \$commit, +); + +if (!$commit) { + say "*** DRY RUN ***"; +} + +my $urgent = { + order => 0, + required => 'true', + protected => 'true', + code => 'urgent', + description => 'To ensure your report is dealt with effectively, please tell us the severity of the issue:-', + datatype_description => 'You have indicated that the issue requires an urgent response, please phone Island Roads on 01983 822440 so that we can respond to the issue appropriately.', + datatype => 'singlevaluelist', + variable => 'true', + values => [ + { + key => 'urgent', + name => 'The issue requires an urgent response/action', + disable => 1, + }, + { + key => 'not_urgent', + name => 'The issue requires a routine/non-urgent response/action', + } + ], +}; + +my $iow = FixMyStreet::DB->resultset("Body")->find({ name => 'Isle of Wight Council' }); +if ($iow) { + my @iow_contacts = $iow->contacts->search({ send_method => 'Triage' })->all; + foreach my $category (@iow_contacts) { + my $extra_fields = $category->get_extra_fields; + my $found = 0; + foreach (@$extra_fields) { + next unless $_->{code} eq 'urgent'; + $_ = $urgent; + $found = 1; + } + if (!$found) { + push @$extra_fields, $urgent; + } + $category->set_extra_fields(@$extra_fields); + say "Making emergency message disable form on " . $category->category . ", Isle of Wight"; + if ($commit) { + $category->update; + } + } +} else { + say STDERR "Could not find IoW"; +} diff --git a/bin/fixmystreet.com/setup_island_roads_triage_cats b/bin/fixmystreet.com/setup_island_roads_triage_cats new file mode 100755 index 000000000..b747fb99b --- /dev/null +++ b/bin/fixmystreet.com/setup_island_roads_triage_cats @@ -0,0 +1,133 @@ +#!/usr/bin/env perl + +=head1 NAME + +setup_island_roads_triage_cats - create/update triage contacts for Island Roads cobrand + +=head1 DESCRIPTION + +This script creates a set of contacts for Island Road to be used as part of their triage +system. The contacts created all have the `Triage` send_method and so will be displayed +to non staff users. + +It also adds the relevant extra fields to the contacts to enable asset details to be +added to a report created in the category. + +If a contact with the same category already exists that does not have a send_method of +`Triage` then the script will emit a warning. Any existing contacts with the `Triage` +send_method will have their state reset to `confirmed`. + +=cut + +use v5.14; +use warnings; + +BEGIN { + use File::Basename qw(dirname); + use File::Spec; + my $d = dirname(File::Spec->rel2abs($0)); + require "$d/../../setenv.pl"; +} + + +my @cat_list = ( + "Dog Fouling", + "Manholes", + "Trees & Hedges", + "Pavements/footpaths", + "Drainage", + "Car Parking", + "Street Lighting", + "Bus Stops", + "Flyposting", + "Potholes", + "Street Cleaning", + "Bridges & Walls", + "Traffic Lights", + "Street Furniture", + "Roads/Highways", + "Road Traffic Signs & Markings", + "Grass Verges & Weeds", + "Flytipping", + "Graffiti", + "Street Nameplates", + "Abandoned Vehicles" +); + +use FixMyStreet::DB; +use FixMyStreet::Cobrand::IsleOfWight; + +my $iow = FixMyStreet::DB->resultset("Body")->search({ + name => "Isle of Wight Council" +}); + +my $cobrand = FixMyStreet::Cobrand::IsleOfWight->new; + +if ($iow->count != 1) { + die "Could not find IoW body\n"; +} + +$iow = $iow->first; + +for my $cat (@cat_list) { + + my $existing = FixMyStreet::DB->resultset("Contact")->search({ + category => $cat, body_id => $iow->id + })->first; + + if ( $existing ) { + if (!$existing->send_method || $existing->send_method ne 'Triage') { + warn "$cat is not a Triage category\n"; + next; + } + # make sure category is not deleted + $existing->state('confirmed'); + } else { + $existing = FixMyStreet::DB->resultset('Contact')->create({ + name => $cat, + body => $iow, + send_method => 'Triage', + state => 'confirmed', + editor => $0, + note => 'created automatically by script', + }); + } + + my $extra_fields = $existing->get_extra_fields; + my @meta = grep { $_->{code} ne 'central_asset_id' && $_->{code} ne 'site_code'} @$extra_fields; + + push @meta, + { + code => 'central_asset_id', + datatype => 'string', + description => 'central asset id', + order => 100, + required => 'false', + variable => 'true', + automated => 'hidden_field', + }, + { + code => 'asset_details', + datatype => 'string', + description => 'asset details', + order => 100, + required => 'false', + variable => 'true', + automated => 'hidden_field', + }, + { + code => 'site_code', + datatype => 'string', + description => 'site code', + order => 100, + required => 'false', + variable => 'true', + automated => 'hidden_field', + }; + + $cobrand->call_hook( + open311_contact_meta_override => {}, $existing, \@meta); + + $existing->set_extra_fields(@meta); + $existing->update; +} 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..385248cd1 --- /dev/null +++ b/perllib/FixMyStreet/App/Controller/Admin/Triage.pm @@ -0,0 +1,163 @@ +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'; + my $comment = $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, + whensent => \'current_timestamp', + } ); + + my @alerts = FixMyStreet::DB->resultset('Alert')->search( { + alert_type => 'new_updates', + parameter => $problem->id, + confirmed => 1, + } ); + + for my $alert (@alerts) { + my $alerts_sent = FixMyStreet::DB->resultset('AlertSent')->find_or_create( { + alert_id => $alert->id, + parameter => $comment->id, + } ); + } + } +} + +1; diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index 203296c4d..ebb3d4839 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -234,12 +234,18 @@ sub check_and_stash_category : Private { my @list_of_names = map { $_->name } values %bodies; my $csv = Text::CSV->new(); $csv->combine(@list_of_names); + $c->stash->{bodies} = \@bodies; $c->{stash}->{list_of_names_as_string} = $csv->string; + my $where = { body_id => [ keys %bodies ], }; + + my $cobrand_where = $c->cobrand->call_hook('munge_around_category_where', $where ); + if ( $cobrand_where ) { + $where = $cobrand_where; + } + my @categories = $c->model('DB::Contact')->not_deleted->search( - { - body_id => [ keys %bodies ], - }, + $where, { columns => [ 'category', 'extra' ], order_by => [ 'category' ], @@ -252,6 +258,7 @@ sub check_and_stash_category : Private { my $categories = [ $c->get_param_list('filter_category', 1) ]; my %valid_categories = map { $_ => 1 } grep { $_ && $categories_mapped{$_} } @$categories; $c->stash->{filter_category} = \%valid_categories; + $c->cobrand->call_hook('munge_around_filter_category_list'); } sub map_features : Private { @@ -312,6 +319,7 @@ sub ajax : Path('/ajax') { my %valid_categories = map { $_ => 1 } $c->get_param_list('filter_category', 1); $c->stash->{filter_category} = \%valid_categories; + $c->cobrand->call_hook('munge_around_filter_category_list'); $c->forward('map_features', [ { bbox => $c->stash->{bbox} } ]); $c->forward('/reports/ajax', [ 'around/on_map_list_items.html' ]); 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/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 554fbc3b7..9b7a925b8 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -261,15 +261,20 @@ sub category_extras_ajax : Path('category_extras') : Args(0) { sub by_category_ajax_data : Private { my ($self, $c, $type, $category) = @_; - my $bodies = $c->forward('contacts_to_bodies', [ $category ]); - my $list_of_names = [ map { $_->name } ($category ? @$bodies : values %{$c->stash->{bodies_to_list}}) ]; - my $vars = { - $category ? (list_of_names => $list_of_names) : (), - }; + my @bodies; + my $bodies = []; + my $vars = {}; + if ($category) { + $bodies = $c->forward('contacts_to_bodies', [ $category ]); + @bodies = @$bodies; + $vars->{list_of_names} = [ map { $_->cobrand_name } @bodies ]; + } else { + @bodies = values %{$c->stash->{bodies_to_list}}; + } my $non_public = $c->stash->{non_public_categories}->{$category}; my $body = { - bodies => $list_of_names, + bodies => [ map { $_->name } @bodies ], $non_public ? ( non_public => JSON->true ) : (), }; @@ -743,7 +748,7 @@ sub setup_categories_and_bodies : Private { push @category_options, $seen{_('Other')} if $seen{_('Other')}; } - $c->cobrand->call_hook(munge_category_list => \@category_options, \@contacts, \%category_extras); + $c->cobrand->call_hook(munge_report_new_category_list => \@category_options, \@contacts, \%category_extras); # put results onto stash for display $c->stash->{bodies} = \%bodies; diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index d8d52a2d9..741cbb60f 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -165,10 +165,13 @@ sub ward : Path : Args(2) { $c->stash->{stats} = $c->cobrand->get_report_stats(); my @categories = $c->stash->{body}->contacts->not_deleted->search( undef, { - columns => [ 'id', 'category', 'extra' ], + columns => [ 'id', 'category', 'extra', 'body_id', 'send_method' ], distinct => 1, order_by => [ 'category' ], } )->all; + + $c->cobrand->call_hook('munge_reports_category_list', \@categories); + $c->stash->{filter_categories} = \@categories; $c->stash->{filter_category} = { map { $_ => 1 } $c->get_param_list('filter_category', 1) }; diff --git a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm index e5327b084..38e9e09a0 100644 --- a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm +++ b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm @@ -7,6 +7,7 @@ use warnings; use mySociety::Random; use constant COUNCIL_ID_BROMLEY => 2482; +use constant COUNCIL_ID_ISLEOFWIGHT => 2636; sub on_map_default_status { return 'open'; } @@ -37,13 +38,116 @@ sub restriction { return {}; } -sub munge_category_list { +sub munge_around_category_where { + my ($self, $where) = @_; + + my $user = $self->{c}->user; + my @iow = grep { $_->name eq 'Isle of Wight Council' } @{ $self->{c}->stash->{bodies} }; + return unless @iow; + + # display all the categories on Isle of Wight at the moment as there's no way to + # do the expand bit later as we fetch it using ajax which uses a bounding box so + # can't determine the body + $where->{send_method} = [ { '!=' => 'Triage' }, undef ]; + return $where; +} + +sub munge_reports_categories_list { + my ($self, $categories) = @_; + + my %bodies = map { $_->body->name => $_->body } @$categories; + if ( $bodies{'Isle of Wight Council'} ) { + my $user = $self->{c}->user; + my $b = $bodies{'Isle of Wight Council'}; + + if ( $user && ( $user->is_superuser || $user->belongs_to_body( $b->id ) ) ) { + @$categories = grep { !$_->send_method || $_->send_method ne 'Triage' } @$categories; + return @$categories; + } + + @$categories = grep { $_->send_method && $_->send_method eq 'Triage' } @$categories; + return @$categories; + } +} + +sub munge_report_new_category_list { my ($self, $options, $contacts, $extras) = @_; # No TfL Traffic Lights category in Hounslow - my %bodies = map { $_->body->name => 1 } @$contacts; - return unless $bodies{'Hounslow Borough Council'}; - @$options = grep { ($_->{category} || $_->category) !~ /^Traffic lights$/i } @$options; + my %bodies = map { $_->body->name => $_->body } @$contacts; + if ( $bodies{'Hounslow Borough Council'} ) { + @$options = grep { ($_->{category} || $_->category) !~ /^Traffic lights$/i } @$options; + } + + if ( $bodies{'Isle of Wight Council'} ) { + my $user = $self->{c}->user; + if ( $user && ( $user->is_superuser || $user->belongs_to_body( $bodies{'Isle of Wight Council'}->id ) ) ) { + @$contacts = grep { !$_->send_method || $_->send_method ne 'Triage' } @$contacts; + my $seen = { map { $_->category => 1 } @$contacts }; + @$options = grep { my $c = ($_->{category} || $_->category); $c =~ 'Pick a category' || $seen->{ $c } } @$options; + return; + } + + @$contacts = grep { $_->send_method && $_->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 munge_load_and_group_problems { + my ($self, $where, $filter) = @_; + + return unless $where->{category} && $self->{c}->stash->{body}->name eq 'Isle of Wight Council'; + + my $cat_names = $self->expand_triage_cat_list($where->{category}); + + $where->{category} = $cat_names; + my $problems = $self->problems->search($where, $filter); + return $problems; +} + +sub expand_triage_cat_list { + my ($self, $categories) = @_; + + my $b = $self->{c}->stash->{body}; + + my $all_cats = $self->{c}->model('DB::Contact')->not_deleted->search( + { + body_id => $b->id, + send_method => [{ '!=', 'Triage'}, undef] + } + ); + + my %group_to_category; + while ( my $cat = $all_cats->next ) { + next unless $cat->get_extra_metadata('group'); + my $groups = $cat->get_extra_metadata('group'); + $groups = ref $groups eq 'ARRAY' ? $groups : [ $groups ]; + for my $group ( @$groups ) { + $group_to_category{$group} //= []; + push @{ $group_to_category{$group} }, $cat->category; + } + } + + my $cats = $self->{c}->model('DB::Contact')->not_deleted->search( + { + body_id => $b->id, + category => $categories + } + ); + + my @cat_names; + while ( my $cat = $cats->next ) { + if ( $cat->send_method && $cat->send_method eq 'Triage' ) { + # include the category itself + push @cat_names, $cat->category; + push @cat_names, @{ $group_to_category{$cat->category} } if $group_to_category{$cat->category}; + } else { + push @cat_names, $cat->category; + } + } + + return \@cat_names; } sub title_list { diff --git a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm new file mode 100644 index 000000000..49356e3ae --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm @@ -0,0 +1,236 @@ +package FixMyStreet::Cobrand::IsleOfWight; +use parent 'FixMyStreet::Cobrand::Whitelabel'; + +use strict; +use warnings; + +sub council_area_id { 2636 } +sub council_area { 'Isle of Wight' } +sub council_name { 'Island Roads' } +sub council_url { 'isleofwight' } +sub all_reports_single_body { { name => "Isle of Wight Council" } } +sub link_to_council_cobrand { "Island Roads" } + +sub enter_postcode_text { + my ($self) = @_; + return 'Enter an ' . $self->council_area . ' postcode, or street name and area'; +} + +sub admin_user_domain { ('islandroads.com') } + +sub on_map_default_status { 'open' } + +sub send_questionnaires { 0 } + +sub report_sent_confirmation_email { 'external_id' } + +sub map_type { 'IsleOfWight' } + +sub disambiguate_location { + my $self = shift; + my $string = shift; + + return { + %{ $self->SUPER::disambiguate_location() }, + centre => '50.675761,-1.296571', + bounds => [ 50.574653, -1.591732, 50.767567, -1.062957 ], + }; +} + +sub updates_disallowed { + my ($self, $problem) = @_; + + my $c = $self->{c}; + return 0 if $c->user_exists && $c->user->id eq $problem->user->id; + return 1; +} + +sub get_geocoder { 'OSM' } + +sub open311_pre_send { + my ($self, $row, $open311) = @_; + + return unless $row->extra; + my $extra = $row->get_extra_fields; + if (@$extra) { + @$extra = grep { $_->{name} ne 'urgent' } @$extra; + $row->set_extra_fields(@$extra); + } +} + +sub open311_config { + my ($self, $row, $h, $params) = @_; + + my $extra = $row->get_extra_fields; + push @$extra, + { name => 'report_url', + value => $h->{url} }, + { name => 'title', + value => $row->title }, + { name => 'description', + value => $row->detail }; + + $row->set_extra_fields(@$extra); +} + +# Make sure fetched report description isn't shown. +sub filter_report_description { "" } + +sub open311_munge_update_params { + my ($self, $params, $comment, $body) = @_; + + if ($comment->mark_fixed) { + $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}; +} + +# this handles making sure the user sees the right categories on the new report page +sub munge_reports_category_list { + my ($self, $categories) = @_; + + my $user = $self->{c}->user; + my %bodies = map { $_->body->name => $_->body } @$categories; + my $b = $bodies{'Isle of Wight Council'}; + + if ( $user && ( $user->is_superuser || $user->belongs_to_body( $b->id ) ) ) { + @$categories = grep { !$_->send_method || $_->send_method ne 'Triage' } @$categories; + return @$categories; + } + + @$categories = grep { $_->send_method && $_->send_method eq 'Triage' } @$categories; + return @$categories; +} + +sub munge_report_new_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 ) ) ) { + @$contacts = grep { !$_->send_method || $_->send_method ne 'Triage' } @$contacts; + my $seen = { map { $_->category => 1 } @$contacts }; + @$options = grep { my $c = ($_->{category} || $_->category); $c =~ 'Pick a category' || $seen->{ $c } } @$options; + return; + } + + @$contacts = grep { $_->send_method && $_->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 munge_around_category_where { + my ($self, $where) = @_; + + my $user = $self->{c}->user; + my $b = $self->{c}->model('DB::Body')->for_areas( $self->council_area_id )->first; + if ( $user && ( $user->is_superuser || $user->belongs_to_body( $b->id ) ) ) { + $where->{send_method} = [ { '!=' => 'Triage' }, undef ]; + return $where; + } + + $where->{'send_method'} = 'Triage'; + return $where; +} + +sub munge_load_and_group_problems { + my ($self, $where, $filter) = @_; + + return unless $where->{category}; + + my $cat_names = $self->expand_triage_cat_list($where->{category}); + + $where->{category} = $cat_names; + my $problems = $self->problems->search($where, $filter); + return $problems; +} + +sub munge_around_filter_category_list { + my $self = shift; + + my $c = $self->{c}; + return unless $c->stash->{filter_category}; + + my $cat_names = $self->expand_triage_cat_list([ keys %{$c->stash->{filter_category}} ]); + $c->stash->{filter_category} = { map { $_ => 1 } @$cat_names }; +} + +# this assumes that each Triage category has the same name as a group +# and uses this to generate a list of categories that a triage category +# could be triaged to +sub expand_triage_cat_list { + my ($self, $categories) = @_; + + my $b = $self->{c}->model('DB::Body')->for_areas( $self->council_area_id )->first; + + my $all_cats = $self->{c}->model('DB::Contact')->not_deleted->search( + { + body_id => $b->id, + send_method => [{ '!=', 'Triage'}, undef] + } + ); + + my %group_to_category; + while ( my $cat = $all_cats->next ) { + next unless $cat->get_extra_metadata('group'); + my $groups = $cat->get_extra_metadata('group'); + $groups = ref $groups eq 'ARRAY' ? $groups : [ $groups ]; + for my $group ( @$groups ) { + $group_to_category{$group} //= []; + push @{ $group_to_category{$group} }, $cat->category; + } + } + + my $cats = $self->{c}->model('DB::Contact')->not_deleted->search( + { + body_id => $b->id, + category => $categories + } + ); + + my @cat_names; + while ( my $cat = $cats->next ) { + if ( $cat->send_method eq 'Triage' ) { + # include the category itself + push @cat_names, $cat->category; + push @cat_names, @{ $group_to_category{$cat->category} } if $group_to_category{$cat->category}; + } else { + push @cat_names, $cat->category; + } + } + + return \@cat_names; +} + +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/perllib/FixMyStreet/Cobrand/UKCouncils.pm b/perllib/FixMyStreet/Cobrand/UKCouncils.pm index 9f4610143..5f45609bb 100644 --- a/perllib/FixMyStreet/Cobrand/UKCouncils.pm +++ b/perllib/FixMyStreet/Cobrand/UKCouncils.pm @@ -179,7 +179,11 @@ sub reports_body_check { } # We want to make sure we're only on our page. - unless ( $self->council_name =~ /^\Q$code\E/ ) { + my $council_name = $self->council_name; + if (my $override = $self->all_reports_single_body) { + $council_name = $override->{name}; + } + unless ( $council_name =~ /^\Q$code\E/ ) { $c->res->redirect( 'https://www.fixmystreet.com' . $c->req->uri->path_query, 301 ); $c->detach(); } diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index 39f446549..1ffcc7b40 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -231,6 +231,8 @@ sub meta_line { $body = "$body <img src='/cobrands/greenwich/favicon.png' alt=''>"; } elsif ($body eq 'Hounslow Borough Council') { $body = 'Hounslow Highways'; + } elsif ($body eq 'Isle of Wight Council') { + $body = 'Island Roads'; } } my $cobrand_always_view_body_user = $c->cobrand->call_hook("always_view_body_contribute_details"); diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 8159d7251..08b768719 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -751,7 +751,7 @@ sub defect_types { # Note: this only makes sense when called on a problem that has been sent! sub can_display_external_id { my $self = shift; - if ($self->external_id && $self->to_body_named('Oxfordshire|Lincolnshire')) { + if ($self->external_id && $self->to_body_named('Oxfordshire|Lincolnshire|Isle of Wight')) { return 1; } return 0; diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index 51f959e0e..85fdc790b 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -317,7 +317,11 @@ sub body { sub moderating_user_name { my $self = shift; - return $self->body || _('an administrator'); + my $body = $self->body; + if ( $body && $body eq 'Isle of Wight Council' ) { + $body = 'Island Roads'; + } + return $body || _('an administrator'); } =head2 belongs_to_body diff --git a/perllib/FixMyStreet/Map/IsleOfWight.pm b/perllib/FixMyStreet/Map/IsleOfWight.pm new file mode 100644 index 000000000..2316e2939 --- /dev/null +++ b/perllib/FixMyStreet/Map/IsleOfWight.pm @@ -0,0 +1,63 @@ +# FixMyStreet:Map::IsleOfWight +# IsleOfWight use their own tiles on their cobrand + +package FixMyStreet::Map::IsleOfWight; +use base 'FixMyStreet::Map::UKCouncilWMTS'; + +use strict; + +sub default_zoom { 7; } + +sub urls { [ 'https://gis.ringway.co.uk/server/rest/services/Hosted/IOW_OS/MapServer/WMTS/tile' ] } + +sub layer_names { [ 'Hosted_IOW_OS' ] } + +sub scales { + my $self = shift; + my @scales = ( + # The first 5 levels don't load and are really zoomed-out, so + # they're not included here. + # '600000', + # '500000', + # '400000', + # '300000', + # '200000', + '100000', + '75000', + '50000', + '25000', + '10000', + '8000', + '6000', + '4000', + '2000', + '1000', + '400', + ); + return @scales; +} + +sub zoom_parameters { + my $self = shift; + my $params = { + zoom_levels => scalar $self->scales, + default_zoom => $self->default_zoom, + min_zoom_level => 0, + id_offset => 5, # see note above about zoom layers we've skipped + }; + return $params; +} + +sub copyright { + return 'Contains Ordnance Survey data © Crown copyright and database rights 2019 OS 100019229. Use of this data is subject to <a href="/about/mapterms">terms and conditions</a>.'; +} + + +sub map_javascript { [ + '/vendor/OpenLayers/OpenLayers.wmts.js', + '/js/map-OpenLayers.js', + '/js/map-wmts-base.js', + '/js/map-wmts-isleofwight.js', +] } + +1; diff --git a/perllib/FixMyStreet/SendReport/Triage.pm b/perllib/FixMyStreet/SendReport/Triage.pm new file mode 100644 index 000000000..38341f3ff --- /dev/null +++ b/perllib/FixMyStreet/SendReport/Triage.pm @@ -0,0 +1,20 @@ +package FixMyStreet::SendReport::Triage; + +use Moo; + +BEGIN { extends 'FixMyStreet::SendReport'; } + +sub send { + my $self = shift; + my ( $row, $h ) = @_; + + $row->update({ + state => 'for triage' + }); + + $self->success(1); + + return 0; +} + +1; diff --git a/perllib/Open311/PostServiceRequestUpdates.pm b/perllib/Open311/PostServiceRequestUpdates.pm index 1f080b168..14bebfcb7 100755 --- a/perllib/Open311/PostServiceRequestUpdates.pm +++ b/perllib/Open311/PostServiceRequestUpdates.pm @@ -14,6 +14,7 @@ use Open311; use constant SEND_METHOD_OPEN311 => 'Open311'; has verbose => ( is => 'ro', default => 0 ); +has current_open311 => ( is => 'rw' ); sub send { my $self = shift; @@ -26,6 +27,7 @@ sub send { while ( my $body = $bodies->next ) { my $cobrand = $body->get_cobrand_handler; next if $cobrand && $cobrand->call_hook('open311_post_update_skip'); + $self->current_open311(Open311->new($self->open311_params($body))); $self->process_body($body); } } @@ -51,8 +53,6 @@ sub open311_params { sub process_body { my ($self, $body) = @_; - my $o = Open311->new( $self->open311_params($body) ); - my $comments = FixMyStreet::DB->resultset('Comment')->to_body($body)->search( { 'me.whensent' => undef, 'me.external_id' => undef, @@ -82,12 +82,14 @@ sub process_body { next if !$self->verbose && $comment->send_fail_count && retry_timeout($comment); - $self->process_update($body, $o, $comment, $cobrand); + $self->process_update($body, $comment, $cobrand); } } sub process_update { - my ($self, $body, $o, $comment, $cobrand) = @_; + my ($self, $body, $comment, $cobrand) = @_; + + my $o = $self->current_open311; $cobrand->call_hook(open311_pre_send => $comment, $o); diff --git a/t/Mock/MapIt.pm b/t/Mock/MapIt.pm index b5cc694df..8b8bb5978 100644 --- a/t/Mock/MapIt.pm +++ b/t/Mock/MapIt.pm @@ -49,6 +49,7 @@ my @PLACES = ( [ 'TA1 1QP', 51.023569, -3.099055, 2239, 'Somerset County Council', 'CTY', 2429, 'Taunton Deane Borough Council', 'DIS' ], [ 'GU51 4AE', 51.279456, -0.846216, 2333, 'Hart District Council', 'DIS', 2227, 'Hampshire County Council', 'CTY' ], [ 'WS1 4NH', 52.563074, -1.991032, 2535, 'Sandwell Borough Council', 'MTD' ], + [ 'PO30 5XJ', 50.71086, -1.29573, 2636, 'Isle of Wight Council', 'UTA' ], [ 'OX28 4DS', 51.784721, -1.494453 ], [ 'E14 2DN', 51.508536, '0.000001' ], # Norway diff --git a/t/app/controller/admin/triage.t b/t/app/controller/admin/triage.t new file mode 100644 index 000000000..6c84d1ff4 --- /dev/null +++ b/t/app/controller/admin/triage.t @@ -0,0 +1,126 @@ +use FixMyStreet::TestMech; +use FixMyStreet::Script::Alerts; + +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', + } +); + +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); + + my $alert = FixMyStreet::App->model('DB::Alert')->create( + { + user => $user2, + alert_type => 'new_updates', + parameter => $report->id, + parameter2 => '', + confirmed => 1, + } + ); + + $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'; + ok $comment->whensent, 'comment is marked as sent'; + + $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'); + + $mech->clear_emails_ok; + FixMyStreet::Script::Alerts::send(); + $mech->email_count_is(0); + }; +}; + +done_testing(); diff --git a/t/app/controller/report_new_open311.t b/t/app/controller/report_new_open311.t index 52f1ddc6e..a0d122a7b 100644 --- a/t/app/controller/report_new_open311.t +++ b/t/app/controller/report_new_open311.t @@ -203,6 +203,8 @@ foreach my $test ( # check that we got the errors expected is_deeply $mech->page_errors, $test->{errors}, "check errors"; + $mech->content_contains('Help <strong>Borsetshire Council</strong> resolve your problem quicker'); + # check that fields have changed as expected my $new_values = { %{ $test->{fields} }, # values added to form diff --git a/t/cobrand/isleofwight.t b/t/cobrand/isleofwight.t new file mode 100644 index 000000000..67088bc54 --- /dev/null +++ b/t/cobrand/isleofwight.t @@ -0,0 +1,567 @@ +use CGI::Simple; +use DateTime; +use FixMyStreet::TestMech; +use Open311; +use Open311::GetServiceRequests; +use Open311::GetServiceRequestUpdates; +use Open311::PostServiceRequestUpdates; +use FixMyStreet::Script::Alerts; +use FixMyStreet::Script::Reports; + +ok( my $mech = FixMyStreet::TestMech->new, 'Created mech object' ); + +my $params = { + send_method => 'Open311', + send_comments => 1, + api_key => 'KEY', + endpoint => 'endpoint', + jurisdiction => 'home', + can_be_devolved => 1, +}; +my $isleofwight = $mech->create_body_ok(2636, 'Isle of Wight Council', $params); +my $contact = $mech->create_contact_ok( + body_id => $isleofwight->id, + category => 'Potholes', + email => 'pothole@example.org', +); +$contact->set_extra_fields( ( { + code => 'urgent', + datatype => 'string', + description => 'question', + variable => 'true', + required => 'false', + order => 1, + datatype_description => 'datatype', +} ) ); +$contact->update; + +my $user = $mech->create_user_ok('user@example.org', name => 'Test User'); +my $iow_user = $mech->create_user_ok('iow_user@example.org', name => 'IoW User', from_body => $isleofwight); +$iow_user->user_body_permissions->create({ + body => $isleofwight, + permission_type => 'moderate', +}); + +my $contact2 = $mech->create_contact_ok( + body_id => $isleofwight->id, + category => 'Roads', + email => 'roads@example.org', + send_method => 'Triage', +); + +my $admin_user = $mech->create_user_ok('admin-user@example.org', name => 'Admin User', from_body => $isleofwight); + +$admin_user->user_body_permissions->create({ + body => $isleofwight, + permission_type => 'triage' +}); + +my @reports = $mech->create_problems_for_body(1, $isleofwight->id, 'An Isle of wight report', { + confirmed => '2019-05-25 09:00', + lastupdate => '2019-05-25 09:00', + latitude => 50.7108, + longitude => -1.29573, + user => $user, + external_id => 101202303 +}); + +subtest "check clicking all reports link" => sub { + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => 'isleofwight', + }, sub { + $mech->get_ok('/'); + $mech->follow_link_ok({ text => 'All reports' }); + }; + + $mech->content_contains("An Isle of wight report", "Isle of Wight report there"); + $mech->content_contains("Island Roads", "is still on cobrand"); +}; + +subtest "use external id for reference number" => sub { + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => 'isleofwight', + }, sub { + $mech->get_ok('/report/' . $reports[0]->id); + }; + + $mech->content_contains("101202303", "Display external id as reference number"); +}; + +subtest "only original reporter can comment" => sub { + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => 'isleofwight', + }, sub { + $mech->get_ok('/report/' . $reports[0]->id); + $mech->content_contains('Only the original reporter may leave updates'); + + $mech->log_in_ok('user@example.org'); + $mech->get_ok('/report/' . $reports[0]->id); + $mech->content_lacks('Only the original reporter may leave updates'); + }; +}; + +subtest "check moderation label uses correct name" => sub { + my $REPORT_URL = '/report/' . $reports[0]->id; + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => ['isleofwight'], + }, sub { + $mech->log_out_ok; + $mech->log_in_ok( $iow_user->email ); + $mech->get_ok($REPORT_URL); + $mech->content_lacks('show-moderation'); + $mech->follow_link_ok({ text_regex => qr/^Moderate$/ }); + $mech->content_contains('show-moderation'); + $mech->submit_form_ok({ with_fields => { + problem_title => 'Good good', + problem_detail => 'Good good improved', + }}); + $mech->base_like( qr{\Q$REPORT_URL\E} ); + $mech->content_like(qr/Moderated by Island Roads/); + }; +}; + +$_->delete for @reports; + +my $system_user = $mech->create_user_ok('system_user@example.org'); + +for my $status ( qw/ CLOSED FIXED DUPLICATE NOT_COUNCILS_RESPONSIBILITY NO_FURTHER_ACTION / ) { + subtest "updates which mark report as $status close it to comments" => sub { + my $dt = DateTime->now(formatter => DateTime::Format::W3CDTF->new)->add( minutes => -5 ); + my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, '', { lastupdate => $dt }); + $p->update({ external_id => $p->id }); + + my $requests_xml = qq{<?xml version="1.0" encoding="utf-8"?> + <service_requests_updates> + <request_update> + <update_id>UPDATE_ID</update_id> + <service_request_id>SERVICE_ID</service_request_id> + <service_request_id_ext>ID_EXTERNAL</service_request_id_ext> + <status>STATUS</status> + <description>This is a note</description> + <updated_datetime>UPDATED_DATETIME</updated_datetime> + </request_update> + </service_requests_updates> + }; + + my $update_dt = DateTime->now(formatter => DateTime::Format::W3CDTF->new); + + $requests_xml =~ s/STATUS/$status/; + $requests_xml =~ s/UPDATE_ID/@{[$p->id]}/; + $requests_xml =~ s/SERVICE_ID/@{[$p->id]}/; + $requests_xml =~ s/ID_EXTERNAL/@{[$p->id]}/; + $requests_xml =~ s/UPDATED_DATETIME/$update_dt/; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'servicerequestupdates.xml' => $requests_xml } ); + + my $update = Open311::GetServiceRequestUpdates->new( + system_user => $system_user, + current_open311 => $o, + current_body => $isleofwight, + ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => 'isleofwight', + }, sub { + $update->process_body; + }; + + $mech->log_in_ok('user@example.org'); + $mech->get_ok('/report/' . $p->id); + $mech->content_lacks('Provide an update', "No update form on report"); + + $p->discard_changes; + is $p->get_extra_metadata('closed_updates'), 1, "report closed to updates"; + $p->comments->delete; + $p->delete; + }; +} + +subtest "fetched requests do not use the description text" => sub { + my $requests_xml = qq{<?xml version="1.0" encoding="utf-8"?> + <service_requests> + <request> + <service_request_id>638344</service_request_id> + <status>open</status> + <status_notes>This is a note.</status_notes> + <service_name>Potholes</service_name> + <service_code>potholes\@example.org</service_code> + <description>This the description of a pothole problem</description> + <agency_responsible></agency_responsible> + <service_notice></service_notice> + <requested_datetime>DATETIME</requested_datetime> + <updated_datetime>DATETIME</updated_datetime> + <expected_datetime>DATETIME</expected_datetime> + <lat>50.71086</lat> + <long>-1.29573</long> + </request> + </service_requests> + }; + + my $dt = DateTime->now(formatter => DateTime::Format::W3CDTF->new)->add( minutes => -5 ); + $requests_xml =~ s/DATETIME/$dt/gm; + + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'requests.xml' => $requests_xml } ); + + my $update = Open311::GetServiceRequests->new( + system_user => $iow_user, + ); + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => 'isleofwight', + }, sub { + $update->create_problems( $o, $isleofwight ); + }; + + my $p = FixMyStreet::DB->resultset('Problem')->search( + { external_id => 638344 } + )->first; + + ok $p, 'Found problem'; + is $p->title, 'Potholes problem', 'correct problem title'; + is $p->detail, 'Potholes problem', 'correct problem description'; + $p->delete; +}; + +subtest "fixing passes along the correct 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 => 'fixed - council', state => 'confirmed', mark_fixed => 0, + confirmed => DateTime->now(), + }); + + 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); + like $cgi->param('description'), qr/^FMS-Update:/, 'FMS update prefix included'; + unlike $cgi->param('description'), qr/The customer indicated that this issue had been fixed/, 'No fixed message included'; + + $c = $mech->create_comment_for_problem($p, $p->user, 'Name', 'Update text', 'f', 'confirmed', 'fixed - user', { confirmed => \'current_timestamp' }); + $c->discard_changes; # Otherwise cannot set_nanosecond + + $id = $o->post_service_request_update($c); + is $id, 248, 'correct update ID returned'; + $cgi = CGI::Simple->new($o->test_req_used->content); + like $cgi->param('description'), qr/^FMS-Update: \[The customer indicated that this issue had been fixed/, 'Fixed message included'; + $p->comments->delete; + $p->delete; + }; +}; + +subtest 'Check special Open311 request handling', sub { + $mech->clear_emails_ok; + my ($p) = $mech->create_problems_for_body(1, $isleofwight->id, 'Title', { category => 'Potholes', latitude => 50.7108, longitude => -1.29573, cobrand => 'isleofwight' }); + $p->set_extra_fields({ name => 'urgent', value => 'no'}); + $p->update; + + my $test_data; + FixMyStreet::override_config { + STAGING_FLAGS => { send_reports => 1 }, + ALLOWED_COBRANDS => ['isleofwight' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $test_data = FixMyStreet::Script::Reports::send(); + }; + + $p->discard_changes; + ok $p->whensent, 'Report marked as sent'; + is $p->send_method_used, 'Open311', 'Report sent via Open311'; + is $p->external_id, 248, 'Report has right external ID'; + + my $req = $test_data->{test_req_used}; + my $c = CGI::Simple->new($req->content); + is $c->param('attribute[urgent]'), undef, 'no urgent param sent'; + + $mech->email_count_is(1); + my $email = $mech->get_email; + ok $email, "got an email"; + like $mech->get_text_body_from_email($email), + qr/your enquiry has been received by Island Roads/, "correct report send email text"; +}; + +subtest "comment recording triage details is not sent" => 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></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', + { + category => 'Roads', + areas => 2636, + latitude => 50.71086, + longitude => -1.29573, + whensent => DateTime->now->add( minutes => -5 ), + send_method_used => 'Triage', + state => 'for triage', + external_id => 1, + }); + + $mech->log_out_ok; + $mech->log_in_ok($admin_user->email); + my $report_url = '/report/' . $p->id; + $mech->get_ok($report_url); + $mech->submit_form_ok( { + with_fields => { + category => 'Potholes', + include_update => 0, + } + }, + 'triage form submitted' + ); + + ok $p->comments->first, 'comment created for problem'; + + $p->update({ + send_method_used => 'Open311', + whensent => DateTime->now->add( minutes => -5 ), + }); + + my $updates = Open311::PostServiceRequestUpdates->new( + current_open311 => $o, + ); + my $id = $updates->process_body($isleofwight); + ok !$o->test_req_used, 'no open311 update sent'; + + $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, + alert_type => 'new_updates', + user => $user, + cobrand => 'isleofwight', +} )->confirm; + +subtest "sends branded alert emails" => sub { + $mech->create_comment_for_problem($p, $system_user, 'Other User', 'This is some update text', 'f', 'confirmed', undef, { confirmed => DateTime->now->add( minutes => 5 ) }); + $mech->clear_emails_ok; + + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => ['isleofwight','fixmystreet'], + }, sub { + FixMyStreet::Script::Alerts::send(); + }; + + $mech->email_count_is(1); + my $email = $mech->get_email; + ok $email, "got an email"; + like $mech->get_text_body_from_email($email), qr/Island Roads/, "emails are branded"; +}; + +$p->comments->delete; +$p->delete; + +subtest "sends branded confirmation emails" => sub { + $mech->log_out_ok; + $mech->clear_emails_ok; + $mech->get_ok('/around'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ 'isleofwight' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, + "submit location" ); + + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); + + $mech->submit_form_ok( + { + button => 'submit_register', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + name => 'Joe Bloggs', + username => 'test-1@example.com', + category => 'Roads', + } + }, + "submit good details" + ); + + $mech->email_count_is(1); + my $email = $mech->get_email; + ok $email, "got an email"; + like $mech->get_text_body_from_email($email), qr/Island Roads/, "emails are branded"; + + my $url = $mech->get_link_from_email($email); + $mech->get_ok($url); + $mech->clear_emails_ok; + }; +}; + +subtest "sends branded report sent emails" => sub { + $mech->clear_emails_ok; + FixMyStreet::override_config { + STAGING_FLAGS => { send_reports => 1 }, + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => ['isleofwight','fixmystreet'], + }, sub { + FixMyStreet::Script::Reports::send(); + }; + $mech->email_count_is(1); + my $email = $mech->get_email; + ok $email, "got an email"; + like $mech->get_text_body_from_email($email), qr/Island Roads/, "emails are branded"; +}; + +subtest "check category extra uses correct name" => sub { + my @extras = ( { + code => 'test', + datatype => 'string', + description => 'question', + variable => 'true', + required => 'false', + order => 1, + datatype_description => 'datatype', + } ); + $contact2->set_extra_fields( @extras ); + $contact2->update; + + my $extra_details; + + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => ['isleofwight','fixmystreet'], + }, sub { + $extra_details = $mech->get_ok_json('/report/new/category_extras?category=Roads&latitude=50.71086&longitude=-1.29573'); + }; + + like $extra_details->{category_extra}, qr/Island Roads/, 'correct name in category extras'; +}; + +subtest "reports are marked for triage upon submission" => sub { + $mech->log_out_ok; + $mech->clear_emails_ok; + $mech->log_in_ok($user->email); + $mech->get_ok('/around'); + FixMyStreet::override_config { + STAGING_FLAGS => { send_reports => 1, skip_checks => 0 }, + ALLOWED_COBRANDS => [ 'isleofwight' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, + "submit location" ); + + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); + + $mech->submit_form_ok( + { + button => 'submit_register', + with_fields => { + title => 'Test Report', + detail => 'Test report details.', + photo1 => '', + category => 'Roads', + } + }, + "submit good details" + ); + + my $report = $user->problems->first; + ok $report, "Found the report"; + is $report->state, 'confirmed', 'report confirmed'; + + $mech->clear_emails_ok; + + FixMyStreet::Script::Reports::send(); + $report->discard_changes; + is $report->state, 'for triage', 'report marked as for triage'; + ok $report->whensent, 'report marked as sent'; + + $mech->email_count_is(1); + my $email = $mech->get_email; + like $mech->get_text_body_from_email($email), + qr/submitted to Island Roads for review/, 'correct text for email sent for Triage'; + }; +}; + +for my $cobrand ( 'fixmystreet', 'isleofwight' ) { + subtest "only categories for Triage are displayed on " . $cobrand => sub { + $mech->log_out_ok; + $mech->get_ok('/around'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ $cobrand ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, + "submit location" ); + + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); + + my $f = $mech->form_name('mapSkippedForm'); + ok $f, 'found form'; + my $cats = $f->find_input('category'); + ok $cats, 'found category element'; + my @values = $cats->possible_values; + is_deeply \@values, [ '-- Pick a category --', 'Roads' ], 'correct category list'; + }; + }; + + subtest "staff user can see non Triage categories on " . $cobrand => sub { + $mech->log_out_ok; + $mech->log_in_ok($admin_user->email); + $mech->get_ok('/around'); + FixMyStreet::override_config { + ALLOWED_COBRANDS => [ $cobrand ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'PO30 5XJ', } }, + "submit location" ); + + # click through to the report page + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, + "follow 'skip this step' link" ); + + my $f = $mech->form_name('mapSkippedForm'); + ok $f, 'found form'; + my $cats = $f->find_input('category'); + ok $cats, 'found category element'; + my @values = $cats->possible_values; + is_deeply \@values, [ '-- Pick a category --', 'Potholes' ], 'correct category list'; + }; + }; +} + +done_testing(); diff --git a/t/map/tests.t b/t/map/tests.t index 1b152620e..f1f29dc06 100644 --- a/t/map/tests.t +++ b/t/map/tests.t @@ -12,6 +12,7 @@ my $requires = { 'Google' => 'map-google.js', 'GoogleOL' => 'map-google-ol.js', 'Hounslow' => 'map-wmts-hounslow.js', + 'IsleOfWight' => 'map-wmts-isleofwight.js', 'OSM' => 'OpenStreetMap.js', 'CycleMap' => 'OpenStreetMap.js', 'MapQuest' => 'OpenStreetMap.js', diff --git a/t/open311/getservicerequestupdates.t b/t/open311/getservicerequestupdates.t index 809fd3a19..6ed1d44fd 100644 --- a/t/open311/getservicerequestupdates.t +++ b/t/open311/getservicerequestupdates.t @@ -24,6 +24,7 @@ my $user = FixMyStreet::DB->resultset('User')->find_or_create( my %bodies = ( 2237 => FixMyStreet::DB->resultset("Body")->create({ name => 'Oxfordshire' }), 2494 => FixMyStreet::DB->resultset("Body")->create({ name => 'Bexley' }), + 2636 => FixMyStreet::DB->resultset("Body")->create({ name => 'Isle of Wight' }), 2482 => FixMyStreet::DB->resultset("Body")->create({ name => 'Bromley', send_method => 'Open311', @@ -36,6 +37,7 @@ my %bodies = ( ); $bodies{2237}->body_areas->create({ area_id => 2237 }); $bodies{2494}->body_areas->create({ area_id => 2494 }); +$bodies{2636}->body_areas->create({ area_id => 2636 }); my $response_template = $bodies{2482}->response_templates->create({ title => "investigating template", @@ -529,29 +531,33 @@ for my $test ( }; } -subtest 'Marking report as fixed closes it for updates (Bexley)' => sub { - my $local_requests_xml = setup_xml($problemB->external_id, $problemB->id, 'CLOSED'); - my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'servicerequestupdates.xml' => $local_requests_xml } ); +for ( + { id => 2494, cobrand => 'bexley' }, + { id => 2636, cobrand => 'isleofwight' } +) { + subtest "Marking report as fixed closes it for updates ($_->{cobrand})" => sub { + my $local_requests_xml = setup_xml($problemB->external_id, $problemB->id, 'CLOSED'); + my $o = Open311->new( jurisdiction => 'mysociety', endpoint => 'http://example.com', test_mode => 1, test_get_returns => { 'servicerequestupdates.xml' => $local_requests_xml } ); - $problemB->update( { bodies_str => $bodies{2494}->id } ); + $problemB->update( { bodies_str => $bodies{$_->{id}}->id } ); - my $update = Open311::GetServiceRequestUpdates->new( - system_user => $user, - current_open311 => $o, - current_body => $bodies{2494}, - current_cobrand => $bodies{2494}->get_cobrand_handler, - ); - FixMyStreet::override_config { - ALLOWED_COBRANDS => 'bexley', - }, sub { - $update->process_body; - }; + my $update = Open311::GetServiceRequestUpdates->new( + system_user => $user, + current_open311 => $o, + current_body => $bodies{$_->{id}}, + ); + FixMyStreet::override_config { + ALLOWED_COBRANDS => $_->{cobrand}, + }, sub { + $update->process_body; + }; - $problemB->discard_changes; - is $problemB->comments->count, 1, 'comment count'; - is $problemB->get_extra_metadata('closed_updates'), 1; - $problemB->comments->delete; -}; + $problemB->discard_changes; + is $problemB->comments->count, 1, 'comment count'; + is $problemB->get_extra_metadata('closed_updates'), 1; + $problemB->comments->delete; + }; +} subtest 'Update with media_url includes image in update' => sub { my $UPLOAD_DIR = tempdir( CLEANUP => 1 ); diff --git a/templates/email/isleofwight/_council_reference.html b/templates/email/isleofwight/_council_reference.html new file mode 100644 index 000000000..5b73ee7e7 --- /dev/null +++ b/templates/email/isleofwight/_council_reference.html @@ -0,0 +1,4 @@ +[% IF problem.external_id ~%] +<p style="[% p_style %]">The report's reference number is <strong>[% problem.external_id %]</strong>. + Please quote this if you need to contact Island Roads about this report.</p> +[%~ END %] diff --git a/templates/email/isleofwight/_council_reference.txt b/templates/email/isleofwight/_council_reference.txt new file mode 100644 index 000000000..d6922217d --- /dev/null +++ b/templates/email/isleofwight/_council_reference.txt @@ -0,0 +1,2 @@ +[% IF problem.external_id %]The report's reference number is [% problem.external_id %]. Please quote this if +you need to contact Island Roads about this report.[% END %] diff --git a/templates/email/isleofwight/_council_reference_alert_update.html b/templates/email/isleofwight/_council_reference_alert_update.html new file mode 100644 index 000000000..5b73ee7e7 --- /dev/null +++ b/templates/email/isleofwight/_council_reference_alert_update.html @@ -0,0 +1,4 @@ +[% IF problem.external_id ~%] +<p style="[% p_style %]">The report's reference number is <strong>[% problem.external_id %]</strong>. + Please quote this if you need to contact Island Roads about this report.</p> +[%~ END %] diff --git a/templates/email/isleofwight/_council_reference_alert_update.txt b/templates/email/isleofwight/_council_reference_alert_update.txt new file mode 100644 index 000000000..d6922217d --- /dev/null +++ b/templates/email/isleofwight/_council_reference_alert_update.txt @@ -0,0 +1,2 @@ +[% IF problem.external_id %]The report's reference number is [% problem.external_id %]. Please quote this if +you need to contact Island Roads about this report.[% END %] diff --git a/templates/email/isleofwight/_email_color_overrides.html b/templates/email/isleofwight/_email_color_overrides.html new file mode 100644 index 000000000..3d6218ca4 --- /dev/null +++ b/templates/email/isleofwight/_email_color_overrides.html @@ -0,0 +1,19 @@ +[% + +color_cyan = '#00aeef' +color_blue = '#00478c' +color_green = '#75c044' +color_white = '#fff' + +header_background_color = color_white +header_text_color = color_blue + +secondary_column_background_color = color_white + +button_background_color = color_green +button_text_color = color_white + +logo_width = "282" # pixel measurement, but without 'px' suffix +logo_height = "76" # pixel measurement, but without 'px' suffix + +%] diff --git a/templates/email/isleofwight/confirm_report_sent.html b/templates/email/isleofwight/confirm_report_sent.html new file mode 100644 index 000000000..8e85c5729 --- /dev/null +++ b/templates/email/isleofwight/confirm_report_sent.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 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/confirm_report_sent.txt b/templates/email/isleofwight/confirm_report_sent.txt new file mode 100644 index 000000000..a368bd95e --- /dev/null +++ b/templates/email/isleofwight/confirm_report_sent.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/email/isleofwight/problem-confirm.html b/templates/email/isleofwight/problem-confirm.html new file mode 100644 index 000000000..ccdefc7aa --- /dev/null +++ b/templates/email/isleofwight/problem-confirm.html @@ -0,0 +1,31 @@ +[% + +email_summary = "You need to confirm your " _ site_name _ " report before it can be sent to Island Roads."; +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 %]">Please confirm your report</h1> + <p style="[% p_style %]">Please click on the link below to confirm that you want to send your report to Island Roads. + +[% TRY %][% INCLUDE '_problem-confirm_extra.html' %][% CATCH file %][% END %] + </p> + [% UNLESS report.non_public %]<p style="[% p_style %]">Your report will also appear on the [% site_name %] website.</p>[% END %] + <p style="margin: 20px auto; text-align: center"> + <a style="[% button_style %]" href="[% token_url %]">Yes, send my report</a> + </p> + <p style="[% p_style %]">If you no longer wish to send this report, please take no further action.</p> + [% end_padded_box %] +</th> +[% WRAPPER '_email_sidebar.html' object = report, url = token_url %] + <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/problem-confirm.txt b/templates/email/isleofwight/problem-confirm.txt new file mode 100644 index 000000000..20a871453 --- /dev/null +++ b/templates/email/isleofwight/problem-confirm.txt @@ -0,0 +1,31 @@ +Subject: Confirm your report on [% site_name %] + +Hello [% report.name %], + +Please click on the link below to confirm that you want to send your report to +Island Roads.[% UNLESS report.non_public %] Note that your report will also +appear on the [% site_name %] website.[% END %] + +[% token_url %] + +If your email program does not let you click on this link, copy and paste it +into your web browser and press return. +[% TRY %][% INCLUDE '_problem-confirm_extra.txt' %][% CATCH file %][% END %] +Your problem had the title: + +[% report.title %] + +And details: + +[% report.detail %] + +If you no longer wish to send this report, please take no further action. + +Thank you for submitting a report through [% site_name %]. + + + +[% signature %] + +This email was sent automatically, from an unmonitored email account - so +please do not reply to it. diff --git a/templates/email/isleofwight/signature.txt b/templates/email/isleofwight/signature.txt new file mode 100644 index 000000000..e375a0402 --- /dev/null +++ b/templates/email/isleofwight/signature.txt @@ -0,0 +1,2 @@ + +Island Roads 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/new/category_extras.html b/templates/web/base/report/new/category_extras.html index c7bdad94d..8e5b02952 100644 --- a/templates/web/base/report/new/category_extras.html +++ b/templates/web/base/report/new/category_extras.html @@ -1,4 +1,4 @@ -[% SET default_list = [] %][% FOR b IN bodies_to_list.values %][% default_list.push(b.name) %][% END %] +[% SET default_list = [] %][% FOR b IN bodies_to_list.values %][% default_list.push(b.cobrand_name) %][% END %] [% DEFAULT list_of_names = default_list %] <div id="category_meta"> diff --git a/templates/web/base/report/new/councils_text_all.html b/templates/web/base/report/new/councils_text_all.html index 8b9abf1f3..63f4125f4 100644 --- a/templates/web/base/report/new/councils_text_all.html +++ b/templates/web/base/report/new/councils_text_all.html @@ -1,4 +1,4 @@ -[% SET default_list = [] %][% FOR b IN bodies_to_list.values %][% default_list.push(b.name) %][% END %] +[% SET default_list = [] %][% FOR b IN bodies_to_list.values %][% default_list.push(b.cobrand_name) %][% END %] [% DEFAULT list_of_names = default_list %] <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/base/reports/body.html b/templates/web/base/reports/body.html index 02791ba3b..23540104b 100755 --- a/templates/web/base/reports/body.html +++ b/templates/web/base/reports/body.html @@ -1,6 +1,8 @@ [% 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 %] [% IF ward %] diff --git a/templates/web/fixmystreet.com/footer_extra_js.html b/templates/web/fixmystreet.com/footer_extra_js.html index 3f3e61c52..6fcb5d59c 100644 --- a/templates/web/fixmystreet.com/footer_extra_js.html +++ b/templates/web/fixmystreet.com/footer_extra_js.html @@ -10,6 +10,7 @@ IF bodyclass.match('mappage'); scripts.push( version('/cobrands/bristol/assets.js') ); scripts.push( version('/cobrands/bromley/assets.js') ); scripts.push( version('/cobrands/buckinghamshire/assets.js') ); + scripts.push( version('/cobrands/isleofwight/assets.js') ); scripts.push( version('/cobrands/lincolnshire/assets.js') ); scripts.push( version('/cobrands/northamptonshire/assets.js') ); scripts.push( version('/cobrands/hounslow/assets.js') ); diff --git a/templates/web/hounslow/report/new/councils_text_all.html b/templates/web/hounslow/report/new/councils_text_all.html deleted file mode 100644 index c434813f8..000000000 --- a/templates/web/hounslow/report/new/councils_text_all.html +++ /dev/null @@ -1,8 +0,0 @@ -<p> - [% UNLESS non_public_categories.$category %] - These will be sent to <strong>Hounslow Highways</strong> and also published online for others to see, in accordance with our - <a href="[% c.cobrand.privacy_policy_url %]">privacy policy</a>. - [% ELSE %] - These will be sent to <strong>Hounslow Highways</strong> but not published online. - [% END %] -</p> diff --git a/templates/web/isleofwight/about/mapterms.html b/templates/web/isleofwight/about/mapterms.html new file mode 100644 index 000000000..04c0aeb09 --- /dev/null +++ b/templates/web/isleofwight/about/mapterms.html @@ -0,0 +1,24 @@ +[% INCLUDE header.html + title = 'Map Terms and Conditions' +%] + +<h1>Map Terms and Conditions</h1> + +<ul> + <li> + I. You are granted a non-exclusive, royalty free revocable licence + solely to view the licensed data for non-commercial purposes for the + period during which mySociety Ltd. makes it available; + </li> + <li> + II. You are not permitted to copy, sub-license, distribute, sell or + otherwise make available the Licensed Data to third parties in any form; + and + </li> + <li> + III. Third party rights to enforce the terms of this licence shall be + reserved to OS. + </li> +</ul> +[% INCLUDE footer.html %] + diff --git a/templates/web/isleofwight/footer_extra_js.html b/templates/web/isleofwight/footer_extra_js.html new file mode 100644 index 000000000..0ad3525e6 --- /dev/null +++ b/templates/web/isleofwight/footer_extra_js.html @@ -0,0 +1,10 @@ +[% IF bodyclass.match('mappage'); + scripts.push( + version('/vendor/OpenLayers.Projection.OrdnanceSurvey.js'), + version('/cobrands/fixmystreet/assets.js'), + version('/cobrands/fixmystreet-uk-councils/roadworks.js'), + version('/cobrands/fixmystreet-uk-councils/js.js'), + version('/cobrands/isleofwight/js.js'), + version('/cobrands/isleofwight/assets.js'), + ); +END %] diff --git a/templates/web/isleofwight/header_extra.html b/templates/web/isleofwight/header_extra.html new file mode 100644 index 000000000..8a977495f --- /dev/null +++ b/templates/web/isleofwight/header_extra.html @@ -0,0 +1 @@ +[% INCLUDE 'tracking_code.html' %] diff --git a/templates/web/isleofwight/header_logo.html b/templates/web/isleofwight/header_logo.html new file mode 100644 index 000000000..e563007a7 --- /dev/null +++ b/templates/web/isleofwight/header_logo.html @@ -0,0 +1,2 @@ +<a href="http://www.islandroads.com" id="site-logo">Island Roads</a> +<a href="/" id="report-cta" title="[%- loc('Report a problem') -%]">[%- loc('Report') -%]</a> 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 _ '&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 _ '&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/_updates_disallowed_message.html b/templates/web/isleofwight/report/_updates_disallowed_message.html new file mode 100644 index 000000000..af56f73b1 --- /dev/null +++ b/templates/web/isleofwight/report/_updates_disallowed_message.html @@ -0,0 +1,8 @@ +<p> + Only the original reporter may leave updates. + [% IF NOT c.user_exists %] + If you made the original report please + <a href="/auth?r=report/[% problem.id %]">log in</a> + to leave an update. + [% END %] +</p> 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/templates/web/isleofwight/report/new/_form_labels.html b/templates/web/isleofwight/report/new/_form_labels.html new file mode 100644 index 000000000..a666ab0a3 --- /dev/null +++ b/templates/web/isleofwight/report/new/_form_labels.html @@ -0,0 +1,3 @@ +[% +SET form_title = 'Summarise the problem and location'; +%] diff --git a/templates/web/isleofwight/tokens/_confirm_problem_council_id.html b/templates/web/isleofwight/tokens/_confirm_problem_council_id.html new file mode 100644 index 000000000..703fbd1b6 --- /dev/null +++ b/templates/web/isleofwight/tokens/_confirm_problem_council_id.html @@ -0,0 +1,2 @@ +<h2>Your issue has been sent.</h2> +<p>You will receive an email with a reference number for this report soon, please quote it in any enquiries.</p> diff --git a/templates/web/isleofwight/tracking_code.html b/templates/web/isleofwight/tracking_code.html new file mode 100644 index 000000000..26358b589 --- /dev/null +++ b/templates/web/isleofwight/tracking_code.html @@ -0,0 +1,16 @@ +[% IF c.config.BASE_URL == "https://www.fixmystreet.com" %] + +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-136557400-2', 'auto'); + ga('send', 'pageview'); + +</script> + +[% ELSE %] +<!-- Tracking code not inserted as "[% c.config.BASE_URL %]" not "https://www.fixmystreet.com" --> +[% END %] diff --git a/web/cobrands/fixmystreet/assets.js b/web/cobrands/fixmystreet/assets.js index fd18bb34a..0b2205076 100644 --- a/web/cobrands/fixmystreet/assets.js +++ b/web/cobrands/fixmystreet/assets.js @@ -71,7 +71,7 @@ OpenLayers.Layer.VectorAsset = OpenLayers.Class(OpenLayers.Layer.Vector, { if (!fixmystreet.markers.getVisibility() || !(this.getVisibility() && this.inRange)) { return; } - var threshold = 50; // metres + var threshold = this.fixmystreet.snap_threshold || 50; // metres var marker = fixmystreet.markers.features[0]; if (marker === undefined) { // No marker to be found so bail out @@ -375,7 +375,7 @@ function clear_fields_for_attributes(attributes) { } function check_zoom_message_visibility() { - if (this.fixmystreet.non_interactive) { + if (this.fixmystreet.non_interactive && !this.fixmystreet.display_zoom_message) { return; } var select = this.fixmystreet.asset_group ? 'category_group' : 'form_category'; @@ -419,6 +419,9 @@ function layer_visibilitychanged() { if (!this.getVisibility()) { this.road_not_found(); } + if (this.fixmystreet.display_zoom_message) { + check_zoom_message_visibility.call(this); + } return; } else if (!this.getVisibility()) { asset_unselected.call(this); @@ -1021,6 +1024,7 @@ fixmystreet.message_controller = (function() { var stopperId = 'js-category-stopper', stoppers = [], ignored_bodies = []; + msg_after_bodies = []; // This shows an error message because e.g. an asset isn't selected or a road hasn't been clicked function show_responsibility_error(id, asset_item, asset_type) { @@ -1119,6 +1123,14 @@ fixmystreet.message_controller = (function() { } } + function stopper_after(stopper) { + var body = fixmystreet.bodies[0]; + if (OpenLayers.Util.indexOf( msg_after_bodies, body) > -1 ) { + return true; + } + return false; + } + function check_for_stopper() { var only_send = fixmystreet.body_overrides.get_only_send(); if (only_send == 'Highways England') { @@ -1150,7 +1162,11 @@ fixmystreet.message_controller = (function() { if ($id.length) { $id.replaceWith($msg); } else { - $msg.insertBefore('#js-post-category-messages'); + if (stopper_after(stopper)) { + $msg.insertAfter('#js-post-category-messages'); + } else { + $msg.insertBefore('#js-post-category-messages'); + } } disable_report_form(stopper.keep_category_extras); } @@ -1225,6 +1241,10 @@ fixmystreet.message_controller = (function() { add_ignored_body: function(body) { ignored_bodies.push(body); + }, + + add_msg_after_bodies: function(body) { + msg_after_bodies.push(body); } }; diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js index 24b8a6d0f..cda196864 100644 --- a/web/cobrands/fixmystreet/fixmystreet.js +++ b/web/cobrands/fixmystreet/fixmystreet.js @@ -1510,7 +1510,12 @@ fixmystreet.display = { window.selected_problem_id = reportId; var marker = fixmystreet.maps.get_marker_by_id(reportId); - if (fixmystreet.map.panTo && ($('html').hasClass('mobile') || !marker.onScreen())) { + var el = document.querySelector('input[name=triage]'); + if (el) { + fixmystreet.map.setCenter( + marker.geometry.getBounds().getCenterLonLat(), + fixmystreet.map.getNumZoomLevels() - 1 ); + } else if (fixmystreet.map.panTo && ($('html').hasClass('mobile') || !marker.onScreen())) { fixmystreet.map.panTo( marker.geometry.getBounds().getCenterLonLat() ); @@ -1526,8 +1531,10 @@ fixmystreet.display = { if (fixmystreet.maps.setup_inspector) { fixmystreet.maps.setup_inspector(); + fixmystreet.map.updateSize(); } + if (typeof callback === 'function') { callback(); } 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/_colours.scss b/web/cobrands/isleofwight/_colours.scss new file mode 100644 index 000000000..688eb66e0 --- /dev/null +++ b/web/cobrands/isleofwight/_colours.scss @@ -0,0 +1,31 @@ +/* COLOURS */ + +$menu-image: 'menu-black'; + +$mappage-header-height: 124px; + +$blue: #00478c; +$cyan: #00aeef; // + 60 25 +$orange: #fcaf17; +$green: #75c044; + +//Any 20% tint of the above + +$primary_text: #fff; +$primary: $cyan; +$primary_b: #222; + +$base_bg: #fff; +$base_fg: #222; + +$nav_background_colour: #fff; +$nav_colour: $blue; +$nav_hover_background_colour: $cyan; + +$col_click_map: $green; + +$header-top-border: false; + +$body-font: Helvetica, Arial, sans-serif; +$heading-font: $body-font; +$meta-font: $body-font; diff --git a/web/cobrands/isleofwight/assets.js b/web/cobrands/isleofwight/assets.js new file mode 100644 index 000000000..a68b0418b --- /dev/null +++ b/web/cobrands/isleofwight/assets.js @@ -0,0 +1,311 @@ +(function(){ + +if (!fixmystreet.maps) { + return; +} + +var defaults = { + http_options: { + url: "https://tilma.staging.mysociety.org/mapserver/iow", + params: { + SERVICE: "WFS", + VERSION: "1.1.0", + REQUEST: "GetFeature", + SRSNAME: "urn:ogc:def:crs:EPSG::27700" + } + }, + format_class: OpenLayers.Format.GML.v3.MultiCurveFix, + max_resolution: { + 'isleofwight': 0.5291677250021167, + 'fixmystreet': 1.194328566789627 + }, + attributes: { + central_asset_id: 'central_asset_id', + site_code: 'site_code' + }, + min_resolution: 0.00001, + asset_id_field: 'asset_id', + geometryName: 'msGeometry', + srsName: "EPSG:27700", + strategy_class: OpenLayers.Strategy.FixMyStreet, + body: "Isle of Wight Council" +}; + +var pin_prefix = fixmystreet.pin_prefix || document.getElementById('js-map-data').getAttribute('data-pin_prefix'); + +var labeled_default = { + fillColor: "#FFFF00", + fillOpacity: 0.6, + strokeColor: "#000000", + strokeOpacity: 0.8, + strokeWidth: 2, + pointRadius: 6 +}; + +var labeled_select = { + externalGraphic: pin_prefix + "pin-spot.png", + fillColor: "#55BB00", + graphicWidth: 48, + graphicHeight: 64, + graphicXOffset: -24, + graphicYOffset: -56, + backgroundGraphic: pin_prefix + "pin-shadow.png", + backgroundWidth: 60, + backgroundHeight: 30, + backgroundXOffset: -7, + backgroundYOffset: -22, + popupYOffset: -40, + graphicOpacity: 1.0, + + label: "${asset_id}", + labelOutlineColor: "white", + labelOutlineWidth: 3, + labelYOffset: 65, + fontSize: '15px', + fontWeight: 'bold' +}; + +var labeled_stylemap = new OpenLayers.StyleMap({ + 'default': new OpenLayers.Style(labeled_default), + 'select': new OpenLayers.Style(labeled_select) +}); + +fixmystreet.assets.add($.extend(true, {}, defaults, { + http_options: { + params: { + TYPENAME: "streets" + } + }, + always_visible: true, + non_interactive: true, + asset_type: 'area', + max_resolution: { + 'isleofwight': 6.614596562526458, + 'fixmystreet': 4.777314267158508 + }, + usrn: { + attribute: 'SITE_CODE', + field: 'site_code' + }, + stylemap: new OpenLayers.StyleMap({ + 'default': new OpenLayers.Style({ + fill: false, + strokeColor: "#5555FF", + strokeOpacity: 0.1, + strokeWidth: 7 + }) + }) +})); + +function not_found_msg_update() { + $('.category_meta_message').html('Please select an item or a road/pavement/path on the map »'); + $('.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 { + not_found_msg_update(); + } +} + + +var point_asset_defaults = $.extend(true, {}, defaults, { + snap_threshold: 5, + select_action: true, + asset_type: 'spot', + asset_item: "item", + actions: { + asset_found: function(asset) { + found_item(this, asset); + }, + asset_not_found: function() { + not_found_msg_update(); + } + } + +}); + +var line_asset_defaults = $.extend(true, {}, defaults, { + display_zoom_message: true, + non_interactive: true, + road: true, + stylemap: fixmystreet.assets.stylemap_invisible, + usrn: { + attribute: 'site_code', + field: 'site_code' + }, + asset_item: 'road', + actions: { + found: function(layer, feature) { + if ( fixmystreet.assets.selectedFeature() ) { + return; + } + found_item(layer, feature); + }, + not_found: function(layer) { + if ( fixmystreet.assets.selectedFeature() ) { + return; + } + not_found_msg_update(); + } + } +}); + + +var point_category_list = [ + //"Dog Fouling", + "Manholes", + "Trees & Hedges", + //"Pavements/footpaths", + //"Drainage", + //"Car Parking", + "Street Lighting", + "Bus Stops", + //"Flyposting", + //"Potholes", + //"Street Cleaning", + "Bridges & Walls", + "Traffic Lights", + "Street Furniture", + //"Roads/Highways", + "Road Traffic Signs & Markings", + "Grass Verges & Weeds", + //"Flytipping", + //"Graffiti", + "Street Nameplates", + //"Abandoned Vehicles" +]; + +var line_category_list = [ + "Dog Fouling", + "Drainage", + "Car Parking", + "Pavements/footpaths", + "Potholes", + "Street Cleaning", + "Roads/Highways", + "Flytipping", + "Abandoned Vehicles" +]; + +var layer_map = { + "Dog Fouling": "Dog_Fouling", + "Drainage": "Drainage_line", + "Car Parking": "Car_Parks", + "Trees & Hedges": "Trees_Hedges", + "Pavements/footpaths": "Pavements_footpaths", + "Street Lighting": "Street_Lighting", + "Bus Stops": "Bus_Stops", + "Street Cleaning": "Street_Cleaning", + "Bridges & Walls": "Bridges_Walls", + "Traffic Lights": "Traffic_Lights", + "Street Furniture": "Street_Furniture", + "Roads/Highways": "Roads_Highways", + "Road Traffic Signs & Markings": "Road_Traffic_Signs_Markings", + "Grass Verges & Weeds": "Grass_Verges_Weeds", + "Street Nameplates": "Street_Nameplates", + "Abandoned Vehicles": "Abandoned_Vehicles" +}; + +for (i = 0; i < point_category_list.length; i++) { + cat = point_category_list[i]; + layer = layer_map[cat] || cat; + + fixmystreet.assets.add($.extend(true, {}, point_asset_defaults, { + asset_group: cat, + http_options: { + params: { + TYPENAME: layer + } + } + })); +} + +for (i = 0; i < line_category_list.length; i++) { + cat = line_category_list[i]; + layer = layer_map[cat] || cat; + + fixmystreet.assets.add($.extend(true, {}, line_asset_defaults, { + asset_group: cat, + asset_category: [ + cat + ], + http_options: { + params: { + TYPENAME: layer + } + } + })); +} + +// non union layers +fixmystreet.assets.add($.extend(true, {}, point_asset_defaults, { + asset_group: "Roads/Highways", + http_options: { + params: { + TYPENAME: "Fords" + } + } +})); + +fixmystreet.assets.add($.extend(true, {}, point_asset_defaults, { + asset_group: "Roads/Highways", + http_options: { + params: { + TYPENAME: "Furn-Grid_and_Stones" + } + } +})); + + +fixmystreet.assets.add($.extend(true, {}, point_asset_defaults, { + asset_group: "Drainage", + http_options: { + params: { + TYPENAME: "Drainage_spot" + } + } +})); + +fixmystreet.assets.add($.extend(true, {}, point_asset_defaults, { + asset_group: "Car Parking", + http_options: { + params: { + TYPENAME: "Car_Parking" + } + } +})); + +fixmystreet.assets.add($.extend(true, {}, line_asset_defaults, { + asset_group: "Grass Verges & Weeds", + asset_category: [ + "Grass Verges & Weeds" + ], + http_options: { + params: { + TYPENAME: "Verges-Natural" + } + } +})); + +fixmystreet.assets.add($.extend(true, {}, point_asset_defaults, { + asset_group: "Dog Fouling", + http_options: { + params: { + TYPENAME: "Furn-Bins" + } + } +})); + +fixmystreet.message_controller.add_msg_after_bodies(defaults.body); + + +})(); diff --git a/web/cobrands/isleofwight/base.scss b/web/cobrands/isleofwight/base.scss new file mode 100644 index 000000000..aae8e9f7c --- /dev/null +++ b/web/cobrands/isleofwight/base.scss @@ -0,0 +1,18 @@ +@import "../sass/h5bp"; +@import "./_colours"; +@import "../sass/mixins"; +@import "../sass/base"; + +/* Header */ + +#site-logo { + width: 141px; + height: 38px; + padding: 1em 0; + background: url(/cobrands/isleofwight/images/logo.gif) 0 50% no-repeat; + background-size: 141px 38px; +} + +#map_box #map { + background-color: white; +} diff --git a/web/cobrands/isleofwight/images/email-logo.gif b/web/cobrands/isleofwight/images/email-logo.gif new file mode 120000 index 000000000..94a12010e --- /dev/null +++ b/web/cobrands/isleofwight/images/email-logo.gif @@ -0,0 +1 @@ +logo.gif
\ No newline at end of file diff --git a/web/cobrands/isleofwight/images/logo.gif b/web/cobrands/isleofwight/images/logo.gif Binary files differnew file mode 100644 index 000000000..7238ca8a3 --- /dev/null +++ b/web/cobrands/isleofwight/images/logo.gif diff --git a/web/cobrands/isleofwight/js.js b/web/cobrands/isleofwight/js.js new file mode 100644 index 000000000..d43006fd5 --- /dev/null +++ b/web/cobrands/isleofwight/js.js @@ -0,0 +1,33 @@ +(function(){ + +if (!fixmystreet.maps) { + return; +} + +if (fixmystreet.cobrand == 'isleofwight') { + // We want the cobranded site to always display "Island Roads" + // as the destination for reports in the "Public details" section. + // This is OK because the cobranded site only shows categories which + // Island Roads actually handle. + // To achieve this we ignore the passed list of bodies and always + // use "Island Roads" when calling the original function. + // NB calling the original function is required so that private categories + // cause the correct text to be shown in the UI. + var original_update_public_councils_text = fixmystreet.update_public_councils_text; + fixmystreet.update_public_councils_text = function(text, bodies) { + original_update_public_councils_text.call(this, text, ['Island Roads']); + }; +} + +var org_id = '1062'; +var body = "Isle of Wight Council"; +fixmystreet.assets.add(fixmystreet.roadworks.layer_future, { + http_options: { params: { organisation_id: org_id } }, + body: body +}); +fixmystreet.assets.add(fixmystreet.roadworks.layer_planned, { + http_options: { params: { organisation_id: org_id } }, + body: body +}); + +})(); diff --git a/web/cobrands/isleofwight/layout.scss b/web/cobrands/isleofwight/layout.scss new file mode 100644 index 000000000..924092a86 --- /dev/null +++ b/web/cobrands/isleofwight/layout.scss @@ -0,0 +1,26 @@ +@import "_colours"; + +@import "../sass/layout"; + +body.twothirdswidthpage .content .sticky-sidebar aside { + top: 10em; +} + +#site-logo { + width: 282px; + height: 76px; + padding: 24px 0; + background-position: 0px 50%; + background-size: 282px 76px; +} + +.mappage #site-header { + box-sizing: border-box; + border-bottom: solid 5px $cyan; +} + +.meta-highlight { + margin: 1em -1em; + padding: 2em 2em; + background-color: mix(#fff, $primary, 70%); +} diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js index 4e4201d3a..1588bda2e 100644 --- a/web/js/map-OpenLayers.js +++ b/web/js/map-OpenLayers.js @@ -676,7 +676,7 @@ $.extend(fixmystreet.utils, { styleMap: pin_layer_style_map }; if (fixmystreet.page == 'around') { - fixmystreet.bbox_strategy = fixmystreet.bbox_strategy || new OpenLayers.Strategy.FixMyStreet(); + fixmystreet.bbox_strategy = fixmystreet.map_bbox_strategy || new OpenLayers.Strategy.FixMyStreet(); pin_layer_options.strategies = [ fixmystreet.bbox_strategy ]; } if (fixmystreet.page == 'reports') { diff --git a/web/js/map-wmts-isleofwight.js b/web/js/map-wmts-isleofwight.js new file mode 100644 index 000000000..464bee913 --- /dev/null +++ b/web/js/map-wmts-isleofwight.js @@ -0,0 +1,206 @@ +/* + * Maps for FMS using Island Roads' WMTS tile server + */ + +fixmystreet.maps.layer_bounds = new OpenLayers.Bounds( + 428576.1131782566, + 70608.46901095579, + 468137.51522498735, + 101069.6062942903 +); + +fixmystreet.maps.matrix_ids = [ + // The first 5 levels don't load and are really zoomed-out, so + // they're not included here. + //{ + //"identifier": 0, + //"scaleDenominator": 566965.4196450538, + //"supportedCRS": "urn:ogc:def:crs:EPSG:27700", + //"tileWidth": 256, + //"tileHeight": 256, + //"matrixWidth": 140, + //"matrixHeight": 109 + //}, + //{ + //"identifier": 1, + //"scaleDenominator": 472471.18303754483, + //"supportedCRS": "urn:ogc:def:crs:EPSG:27700", + //"tileWidth": 256, + //"tileHeight": 256, + //"matrixWidth": 168, + //"matrixHeight": 130 + //}, + //{ + //"identifier": 2, + //"scaleDenominator": 377976.9464300358, + //"supportedCRS": "urn:ogc:def:crs:EPSG:27700", + //"tileWidth": 256, + //"tileHeight": 256, + //"matrixWidth": 210, + //"matrixHeight": 163 + //}, + //{ + //"identifier": 3, + //"scaleDenominator": 283482.7098225269, + //"supportedCRS": "urn:ogc:def:crs:EPSG:27700", + //"tileWidth": 256, + //"tileHeight": 256, + //"matrixWidth": 280, + //"matrixHeight": 217 + //}, + //{ + //"identifier": 4, + //"scaleDenominator": 188988.4732150179, + //"supportedCRS": "urn:ogc:def:crs:EPSG:27700", + //"tileWidth": 256, + //"tileHeight": 256, + //"matrixWidth": 420, + //"matrixHeight": 325 + //}, + { + "identifier": 5, + "scaleDenominator": 94494.23660750895, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 840, + "matrixHeight": 650 + }, + { + "identifier": 6, + "scaleDenominator": 70870.67745563173, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 1120, + "matrixHeight": 867 + }, + { + "identifier": 7, + "scaleDenominator": 47247.118303754476, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 1680, + "matrixHeight": 1300 + }, + { + "identifier": 8, + "scaleDenominator": 23623.559151877238, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 3360, + "matrixHeight": 2599 + }, + { + "identifier": 9, + "scaleDenominator": 9449.423660750896, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 8399, + "matrixHeight": 6496 + }, + { + "identifier": 10, + "scaleDenominator": 7559.538928600717, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 10499, + "matrixHeight": 8120 + }, + { + "identifier": 11, + "scaleDenominator": 5669.654196450538, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 13998, + "matrixHeight": 10826 + }, + { + "identifier": 12, + "scaleDenominator": 3779.7694643003583, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 20997, + "matrixHeight": 16239 + }, + { + "identifier": 13, + "scaleDenominator": 1889.8847321501792, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 41993, + "matrixHeight": 32478 + }, + { + "identifier": 14, + "scaleDenominator": 944.9423660750896, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 83985, + "matrixHeight": 64955 + }, + { + "identifier": 15, + "scaleDenominator": 377.9769464300358, + "supportedCRS": "urn:ogc:def:crs:EPSG:27700", + "tileWidth": 256, + "tileHeight": 256, + "matrixWidth": 209961, + "matrixHeight": 162387 + } +]; + +/* + * maps.config() is called on dom ready in map-OpenLayers.js + * to setup the way the map should operate. + */ +fixmystreet.maps.config = function() { + var permalink_id; + if ($('#map_permalink').length) { + permalink_id = 'map_permalink'; + } + + fixmystreet.controls = [ + new OpenLayers.Control.ArgParserFMS(), + new OpenLayers.Control.Navigation(), + new OpenLayers.Control.PermalinkFMS(permalink_id), + new OpenLayers.Control.PanZoomFMS({id: 'fms_pan_zoom' }) + ]; + + /* Linking back to around from report page, keeping track of map moves */ + if ( fixmystreet.page == 'report' ) { + fixmystreet.controls.push( new OpenLayers.Control.PermalinkFMS('key-tool-problems-nearby', '/around') ); + } + + this.setup_wmts_base_map(); +}; + +fixmystreet.maps.marker_size = function() { + var zoom = fixmystreet.map.getZoom() + fixmystreet.zoomOffset; + if (zoom >= 7) { + return 'normal'; + } else if (zoom >= 4) { + return 'small'; + } else { + return 'mini'; + } +}; + +fixmystreet.maps.selected_marker_size = function() { + var zoom = fixmystreet.map.getZoom() + fixmystreet.zoomOffset; + if (zoom >= 7) { + return 'big'; + } else if (zoom >= 4) { + return 'normal'; + } else { + return 'small'; + } +}; |