From 683b188b288fe43526e1649c784fa44435559655 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 30 Apr 2020 13:51:42 +0100 Subject: Move per-row Contact lookup to the database. On admin report lists, and in front-end lists when an inspector, each row was querying the database for `category_display`. We create a new relationship for this query, and join/prefetch it wherever we request this data. Include staff joins on /around page, copying what happens on /reports to prevent more lookups there too. Also add some joins for user email in admin report list. --- perllib/FixMyStreet/App/Controller/Admin.pm | 14 ++++++-- .../FixMyStreet/App/Controller/Admin/Reports.pm | 9 ++++- perllib/FixMyStreet/App/Controller/Dashboard.pm | 10 +++++- perllib/FixMyStreet/App/Controller/My.pm | 14 +++++--- perllib/FixMyStreet/App/Controller/Report.pm | 4 ++- .../FixMyStreet/App/Controller/Report/Update.pm | 16 ++++----- perllib/FixMyStreet/App/Controller/Reports.pm | 11 +++--- perllib/FixMyStreet/Cobrand/Bexley.pm | 2 +- perllib/FixMyStreet/Cobrand/Bromley.pm | 12 +++---- perllib/FixMyStreet/Cobrand/EastSussex.pm | 1 - perllib/FixMyStreet/Cobrand/FixMyStreet.pm | 4 +-- perllib/FixMyStreet/Cobrand/IsleOfWight.pm | 4 +-- perllib/FixMyStreet/Cobrand/Peterborough.pm | 2 +- perllib/FixMyStreet/DB/Result/Problem.pm | 42 +++++++++++----------- perllib/FixMyStreet/DB/ResultSet/Nearby.pm | 21 ++++++++--- perllib/FixMyStreet/DB/ResultSet/Problem.pm | 30 +++++++++++----- t/roles/translatable.t | 5 +-- .../web/isleofwight/report/_item_heading.html | 2 +- 18 files changed, 131 insertions(+), 72 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index 038cba9e5..8d6a41b9f 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -71,13 +71,16 @@ sub index : Path : Args(0) { } my @unsent = $c->cobrand->problems->search( { - state => [ FixMyStreet::DB::Result::Problem::open_states() ], + 'me.state' => [ FixMyStreet::DB::Result::Problem::open_states() ], whensent => undef, bodies_str => { '!=', undef }, # Ignore very recent ones that probably just haven't been sent yet confirmed => { '<', \"current_timestamp - '5 minutes'::interval" }, }, { + '+columns' => ['user.email'], + prefetch => 'contact', + join => 'user', order_by => 'confirmed', } )->all; $c->stash->{unsent_reports} = \@unsent; @@ -301,7 +304,14 @@ sub add_flags : Private { sub flagged : Path('flagged') : Args(0) { my ( $self, $c ) = @_; - my $problems = $c->cobrand->problems->search( { flagged => 1 } ); + my $problems = $c->cobrand->problems->search( + { 'me.flagged' => 1 }, + { + '+columns' => ['user.email'], + join => 'user', + prefetch => 'contact', + } + ); # pass in as array ref as using same template as search_reports # which has to use an array ref for sql quoting reasons diff --git a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm index 7300fe676..88c4380fc 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm @@ -110,6 +110,7 @@ sub index : Path { { join => 'user', '+columns' => 'user.email', + prefetch => 'contact', rows => 50, order_by => $order, } @@ -166,7 +167,13 @@ sub index : Path { my $problems = $c->cobrand->problems->search( $query, - { order_by => $order, rows => 50 } + { + '+columns' => ['user.email'], + join => 'user', + prefetch => 'contact', + order_by => $order, + rows => 50 + } )->page( $p_page ); $c->stash->{problems} = [ $problems->all ]; $c->stash->{problems_pager} = $problems->pager; diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index ad6c9ba98..f3b607ba8 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -591,7 +591,15 @@ sub heatmap_sidebar :Private { order_by => 'lastupdate', })->all ]; - my $params = { map { my $n = $_; s/me\./problem\./; $_ => $where->{$n} } keys %$where }; + my $params = { map { + my $v = $where->{$_}; + if (ref $v eq 'HASH') { + $v = { map { my $vv = $v->{$_}; s/me\./problem\./; $_ => $vv } keys %$v }; + } else { + s/me\./problem\./; + } + $_ => $v; + } keys %$where }; my $body = $c->stash->{body}; my @user; diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm index 3328caac0..316fe08b8 100644 --- a/perllib/FixMyStreet/App/Controller/My.pm +++ b/perllib/FixMyStreet/App/Controller/My.pm @@ -135,13 +135,14 @@ sub get_problems : Private { my $problems = []; my $states = $c->stash->{filter_problem_states}; + my $table = $c->action eq 'my/planned' ? 'report' : 'me'; my $params = { - state => [ keys %$states ], + "$table.state" => [ keys %$states ], }; my $categories = [ $c->get_param_list('filter_category', 1) ]; if ( @$categories ) { - $params->{category} = $categories; + $params->{"$table.category"} = $categories; $c->stash->{filter_category} = { map { $_ => 1 } @$categories }; } @@ -149,6 +150,7 @@ sub get_problems : Private { $rows = 5000 if $c->stash->{sort_key} eq 'shortlist'; # Want all reports my $rs = $c->stash->{problems_rs}->search( $params, { + prefetch => 'contact', order_by => $c->stash->{sort_order}, rows => $rows, } )->include_comment_counts->page( $p_page ); @@ -186,12 +188,14 @@ sub get_updates : Private { sub setup_page_data : Private { my ($self, $c) = @_; + my $table = $c->action eq 'my/planned' ? 'report' : 'me'; my @categories = $c->stash->{problems_rs}->search({ - state => [ FixMyStreet::DB::Result::Problem->visible_states() ], + "$table.state" => [ FixMyStreet::DB::Result::Problem->visible_states() ], }, { - columns => [ 'category', 'bodies_str', 'extra' ], + join => 'contact', + columns => [ "$table.category", 'contact.extra', 'contact.category' ], distinct => 1, - order_by => [ 'category' ], + order_by => [ "$table.category" ], } )->all; $c->stash->{filter_categories} = \@categories; $c->forward('/report/stash_category_groups', [ \@categories ]) if $c->cobrand->enable_category_groups; diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 72f96013a..058edebd8 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -133,11 +133,13 @@ sub support :Chained('id') :Args(0) { sub load_problem_or_display_error : Private { my ( $self, $c, $id ) = @_; + my $attrs = { prefetch => 'contact' }; + # try to load a report if the id is a number my $problem = ( !$id || $id =~ m{\D} ) # is id non-numeric? ? undef # ...don't even search - : $c->cobrand->problems->find( { id => $id } ) + : $c->cobrand->problems->find( { id => $id }, $attrs ) or $c->detach( '/page_error_404_not_found', [ _('Unknown problem ID') ] ); # check that the problem is suitable to show. diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index 41c42b8a1..c5d20a5da 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -121,7 +121,7 @@ sub process_user : Private { if ( $c->user_exists ) { { my $user = $c->user->obj; - if ($c->stash->{contributing_as_another_user} = $user->contributing_as('another_user', $c, $update->problem->bodies_str_ids)) { + if ($c->stash->{contributing_as_another_user} = $user->contributing_as('another_user', $c, $c->stash->{problem}->bodies_str_ids)) { # Act as if not logged in (and it will be auto-confirmed later on) last; } @@ -248,7 +248,7 @@ sub load_problem : Private { # Problem ID could come from existing update in token, or from query parameter my $problem_id = $update->problem_id || $c->get_param('id'); $c->forward( '/report/load_problem_or_display_error', [ $problem_id ] ); - $update->problem($c->stash->{problem}); + $update->problem_id($c->stash->{problem}->id); } =head2 check_form_submitted @@ -282,7 +282,8 @@ sub process_update : Private { my $name = Utils::trim_text( $params{name} ); - $params{reopen} = 0 unless $c->user && $c->user->id == $c->stash->{problem}->user->id; + my $problem = $c->stash->{problem}; + $params{reopen} = 0 unless $c->user && $c->user->id == $problem->user->id; my $update = $c->stash->{update}; $update->text($params{update}); @@ -290,11 +291,11 @@ sub process_update : Private { $update->mark_fixed($params{fixed} ? 1 : 0); $update->mark_open($params{reopen} ? 1 : 0); - $c->stash->{contributing_as_body} = $c->user_exists && $c->user->contributing_as('body', $c, $update->problem->bodies_str_ids); - $c->stash->{contributing_as_anonymous_user} = $c->user_exists && $c->user->contributing_as('anonymous_user', $c, $update->problem->bodies_str_ids); + $c->stash->{contributing_as_body} = $c->user_exists && $c->user->contributing_as('body', $c, $problem->bodies_str_ids); + $c->stash->{contributing_as_anonymous_user} = $c->user_exists && $c->user->contributing_as('anonymous_user', $c, $problem->bodies_str_ids); # This is also done in process_user, but is needed here for anonymous() just below - my $anon_button = $c->cobrand->allow_anonymous_reports($update->problem->category) eq 'button' && $c->get_param('report_anonymously'); + my $anon_button = $c->cobrand->allow_anonymous_reports($problem->category) eq 'button' && $c->get_param('report_anonymously'); if ($anon_button) { $c->stash->{contributing_as_anonymous_user} = 1; $c->stash->{contributing_as_body} = undef; @@ -323,7 +324,6 @@ sub process_update : Private { # then we are not changing the state of the problem so can use the current # problem state } else { - my $problem = $c->stash->{problem} || $update->problem; $update->problem_state( $problem->state ); } } @@ -332,7 +332,7 @@ sub process_update : Private { my @extra; # Next function fills this, but we don't need it here. # This is just so that the error checking for these extra fields runs. # TODO Use extra here as it is used on reports. - my $body = (values %{$update->problem->bodies})[0]; + my $body = (values %{$problem->bodies})[0]; $c->cobrand->process_open311_extras( $c, $body, \@extra ); if ( $c->get_param('fms_extra_title') ) { diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 97976ebe3..53f27eb62 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -620,6 +620,9 @@ sub load_problems_parameters : Private { }; if ($c->user_exists && $body) { my $prefetch = []; + if ($c->user->from_body || $c->user->is_superuser) { + push @$prefetch, 'contact'; + } if ($c->user->has_permission_to('planned_reports', $body->id)) { push @$prefetch, 'user_planned_reports'; } @@ -646,7 +649,7 @@ sub load_problems_parameters : Private { } if (@$category) { - $where->{category} = $category; + $where->{'me.category'} = $category; } if ($c->stash->{wards}) { @@ -687,12 +690,12 @@ sub check_non_public_reports_permission : Private { } if ( $user_has_permission ) { - $where->{non_public} = 1 if $c->stash->{only_non_public}; + $where->{'me.non_public'} = 1 if $c->stash->{only_non_public}; } else { - $where->{non_public} = 0; + $where->{'me.non_public'} = 0; } } else { - $where->{non_public} = 0; + $where->{'me.non_public'} = 0; } } diff --git a/perllib/FixMyStreet/Cobrand/Bexley.pm b/perllib/FixMyStreet/Cobrand/Bexley.pm index 481926e72..f37394794 100644 --- a/perllib/FixMyStreet/Cobrand/Bexley.pm +++ b/perllib/FixMyStreet/Cobrand/Bexley.pm @@ -54,7 +54,7 @@ sub open311_munge_update_params { $params->{service_request_id_ext} = $comment->problem->id; - my $contact = $comment->problem->category_row; + my $contact = $comment->problem->contact; $params->{service_code} = $contact->email; } diff --git a/perllib/FixMyStreet/Cobrand/Bromley.pm b/perllib/FixMyStreet/Cobrand/Bromley.pm index 8f82817a8..bd229f360 100644 --- a/perllib/FixMyStreet/Cobrand/Bromley.pm +++ b/perllib/FixMyStreet/Cobrand/Bromley.pm @@ -344,23 +344,23 @@ sub munge_load_and_group_problems { return unless $c->action eq 'dashboard/heatmap'; # Bromley subcategory stuff - if (!$where->{category}) { + if (!$where->{'me.category'}) { my $cats = $c->user->categories; my $subcats = $c->user->get_extra_metadata('subcategories') || []; - $where->{category} = [ @$cats, @$subcats ] if @$cats || @$subcats; + $where->{'me.category'} = [ @$cats, @$subcats ] if @$cats || @$subcats; } my %subcats = $self->subcategories; my $subcat; - my %chosen = map { $_ => 1 } @{$where->{category} || []}; + my %chosen = map { $_ => 1 } @{$where->{'me.category'} || []}; my @subcat = grep { $chosen{$_} } map { $_->{key} } map { @$_ } values %subcats; if (@subcat) { my %chosen = map { $_ => 1 } @subcat; $where->{'-or'} = { - category => [ grep { !$chosen{$_} } @{$where->{category}} ], - subcategory => \@subcat, + 'me.category' => [ grep { !$chosen{$_} } @{$where->{'me.category'}} ], + 'me.subcategory' => \@subcat, }; - delete $where->{category}; + delete $where->{'me.category'}; } } diff --git a/perllib/FixMyStreet/Cobrand/EastSussex.pm b/perllib/FixMyStreet/Cobrand/EastSussex.pm index e6c2da6c5..8d95980fe 100644 --- a/perllib/FixMyStreet/Cobrand/EastSussex.pm +++ b/perllib/FixMyStreet/Cobrand/EastSussex.pm @@ -11,7 +11,6 @@ sub open311_extra_data { $h->{es_original_detail} = $row->detail; - $contact = $row->category_row; my $fields = $contact->get_extra_fields; my $text = ''; for my $field ( @$fields ) { diff --git a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm index dfb511f39..f39558f45 100644 --- a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm +++ b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm @@ -139,10 +139,10 @@ sub munge_report_new_contacts { sub munge_load_and_group_problems { my ($self, $where, $filter) = @_; - return unless $where->{category} && $self->{c}->stash->{body}->name eq 'Isle of Wight Council'; + return unless $where->{'me.category'} && $self->{c}->stash->{body}->name eq 'Isle of Wight Council'; my $iow = FixMyStreet::Cobrand->get_class_for_moniker( 'isleofwight' )->new({ c => $self->{c} }); - $where->{category} = $iow->expand_triage_cat_list($where->{category}, $self->{c}->stash->{body}); + $where->{'me.category'} = $iow->expand_triage_cat_list($where->{'me.category'}, $self->{c}->stash->{body}); } sub title_list { diff --git a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm index db0a20b9c..3bcea4d3d 100644 --- a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm +++ b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm @@ -140,9 +140,9 @@ sub munge_around_category_where { sub munge_load_and_group_problems { my ($self, $where, $filter) = @_; - return unless $where->{category}; + return unless $where->{'me.category'}; - $where->{category} = $self->_expand_triage_cat_list($where->{category}); + $where->{'me.category'} = $self->_expand_triage_cat_list($where->{'me.category'}); } sub munge_around_filter_category_list { diff --git a/perllib/FixMyStreet/Cobrand/Peterborough.pm b/perllib/FixMyStreet/Cobrand/Peterborough.pm index 0ddaeacb6..133767fa7 100644 --- a/perllib/FixMyStreet/Cobrand/Peterborough.pm +++ b/perllib/FixMyStreet/Cobrand/Peterborough.pm @@ -85,7 +85,7 @@ sub open311_munge_update_params { # Send the FMS problem ID with the update. $params->{service_request_id_ext} = $comment->problem->id; - my $contact = $comment->problem->category_row; + my $contact = $comment->problem->contact; $params->{service_code} = $contact->email; } diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 37563d327..42983c9ad 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -193,6 +193,27 @@ __PACKAGE__->might_have( cascade_copy => 0, cascade_delete => 1 }, ); +# Add a possible join for the Contact object associated with +# this report (based on bodies_str and category). If the report +# was sent to multiple bodies, only returns the first. +__PACKAGE__->belongs_to( + contact => "FixMyStreet::DB::Result::Contact", + sub { + my $args = shift; + return { + "$args->{foreign_alias}.category" => { -ident => "$args->{self_alias}.category" }, + -and => [ + \[ "CAST($args->{foreign_alias}.body_id AS text) = (regexp_split_to_array($args->{self_alias}.bodies_str, ','))[1]" ], + ] + }; + }, + { + join_type => "LEFT", + on_delete => "NO ACTION", + on_update => "NO ACTION", + }, +); + __PACKAGE__->load_components("+FixMyStreet::DB::RABXColumn"); __PACKAGE__->rabx_column('extra'); __PACKAGE__->rabx_column('geocode'); @@ -407,30 +428,11 @@ sub confirm { sub category_display { my $self = shift; - my $contact = $self->category_row; + my $contact = $self->contact; return $self->category unless $contact; # Fallback; shouldn't happen, but some tests return $contact->category_display; } -=head2 category_row - -Returns the corresponding Contact object for this problem's category and body. -If the report was sent to multiple bodies, only returns the first. - -=cut - -sub category_row { - my $self = shift; - my $schema = $self->result_source->schema; - my $body_id = $self->bodies_str_ids->[0]; - return unless $body_id && $body_id =~ /^[0-9]+$/; - my $contact = $schema->resultset("Contact")->find({ - body_id => $body_id, - category => $self->category, - }); - return $contact; -} - sub bodies_str_ids { my $self = shift; return [] unless $self->bodies_str; diff --git a/perllib/FixMyStreet/DB/ResultSet/Nearby.pm b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm index 2ebe309e3..af1142c3a 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Nearby.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm @@ -17,16 +17,16 @@ sub nearby { } my $params = { - state => [ keys %{$args{states}} ], + 'problem.state' => [ keys %{$args{states}} ], }; - $params->{id} = { -not_in => $args{ids} } + $params->{problem_id} = { -not_in => $args{ids} } if $args{ids}; - $params->{category} = $args{categories} if $args{categories} && @{$args{categories}}; + $params->{'problem.category'} = $args{categories} if $args{categories} && @{$args{categories}}; $params->{$c->stash->{report_age_field}} = { '>=', \"current_timestamp-'$args{report_age}'::interval" } if $args{report_age}; - FixMyStreet::DB::ResultSet::Problem->non_public_if_possible($params, $c); + FixMyStreet::DB::ResultSet::Problem->non_public_if_possible($params, $c, 'problem'); $rs = $c->cobrand->problems_restriction($rs); @@ -34,11 +34,22 @@ sub nearby { $params = { %$params, %{$args{extra}} } if $args{extra}; my $attrs = { - prefetch => 'problem', + prefetch => { problem => [] }, bind => [ $args{latitude}, $args{longitude}, $args{distance} ], order_by => [ 'distance', { -desc => 'created' } ], rows => $args{limit}, }; + if ($c->user_exists) { + if ($c->user->from_body || $c->user->is_superuser) { + push @{$attrs->{prefetch}{problem}}, 'contact'; + } + if ($c->user->has_body_permission_to('planned_reports')) { + push @{$attrs->{prefetch}{problem}}, 'user_planned_reports'; + } + if ($c->user->has_body_permission_to('report_edit_priority') || $c->user->has_body_permission_to('report_inspect')) { + push @{$attrs->{prefetch}{problem}}, 'response_priority'; + } + } my @problems = mySociety::Locale::in_gb_locale { $rs->search( $params, $attrs )->all }; return \@problems; diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index e23cf78e1..2ce4c04be 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -26,30 +26,31 @@ sub body_query { # Edits PARAMS in place to either hide non_public reports, or show them # if user is superuser (all) or inspector (correct body) sub non_public_if_possible { - my ($rs, $params, $c) = @_; + my ($rs, $params, $c, $table) = @_; + $table ||= 'me'; if ($c->user_exists) { my $only_non_public = $c->stash->{only_non_public} ? 1 : 0; if ($c->user->is_superuser) { # See all reports, no restriction - $params->{non_public} = 1 if $only_non_public; + $params->{"$table.non_public"} = 1 if $only_non_public; } elsif ($c->user->has_body_permission_to('report_inspect') || $c->user->has_body_permission_to('report_mark_private')) { if ($only_non_public) { $params->{'-and'} = [ - non_public => 1, + "$table.non_public" => 1, $rs->body_query($c->user->from_body->id), ]; } else { $params->{'-or'} = [ - non_public => 0, + "$table.non_public" => 0, $rs->body_query($c->user->from_body->id), ]; } } else { - $params->{non_public} = 0; + $params->{"$table.non_public"} = 0; } } else { - $params->{non_public} = 0; + $params->{"$table.non_public"} = 0; } } @@ -175,22 +176,33 @@ sub around_map { my ( $rs, $c, %p) = @_; my $attr = { order_by => $p{order}, + rows => $c->cobrand->reports_per_page, }; - $attr->{rows} = $c->cobrand->reports_per_page; + if ($c->user_exists) { + if ($c->user->from_body || $c->user->is_superuser) { + push @{$attr->{prefetch}}, 'contact'; + } + if ($c->user->has_body_permission_to('planned_reports')) { + push @{$attr->{prefetch}}, 'user_planned_reports'; + } + if ($c->user->has_body_permission_to('report_edit_priority') || $c->user->has_body_permission_to('report_inspect')) { + push @{$attr->{prefetch}}, 'response_priority'; + } + } unless ( $p{states} ) { $p{states} = FixMyStreet::DB::Result::Problem->visible_states(); } my $q = { - state => [ keys %{$p{states}} ], + 'me.state' => [ keys %{$p{states}} ], latitude => { '>=', $p{min_lat}, '<', $p{max_lat} }, longitude => { '>=', $p{min_lon}, '<', $p{max_lon} }, }; $q->{$c->stash->{report_age_field}} = { '>=', \"current_timestamp-'$p{report_age}'::interval" } if $p{report_age}; - $q->{category} = $p{categories} if $p{categories} && @{$p{categories}}; + $q->{'me.category'} = $p{categories} if $p{categories} && @{$p{categories}}; $rs->non_public_if_possible($q, $c); diff --git a/t/roles/translatable.t b/t/roles/translatable.t index 9f8c67394..beddb7182 100644 --- a/t/roles/translatable.t +++ b/t/roles/translatable.t @@ -77,8 +77,9 @@ FixMyStreet::override_config { subtest 'Check display_name override' => sub { $contact->set_extra_metadata( display_name => 'Override name' ); $contact->update; - is $contact->category_display, "Override name"; - is $problem->category_display, "Override name"; + is $contact->category_display, "Override name", 'Contact uses display_name'; + $problem->discard_changes; + is $problem->category_display, "Override name", 'Problem uses display_name'; }; done_testing; diff --git a/templates/web/isleofwight/report/_item_heading.html b/templates/web/isleofwight/report/_item_heading.html index 90010a548..40f9c8ad2 100644 --- a/templates/web/isleofwight/report/_item_heading.html +++ b/templates/web/isleofwight/report/_item_heading.html @@ -1,7 +1,7 @@ [% SET title = problem.title; IF c.req.uri.path == '/'; - title = problem.category_display; + title = problem.category; # Not category_display to prevent unneeded DM lookup END; %]

[% title %]

-- cgit v1.2.3 From 730d25ae7218d731590b322d4f419a7df6d4e4fb Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 30 Apr 2020 13:56:31 +0100 Subject: Add ability to disallow updates in a category. Add a tickbox to the category admin, and do not allow updates on reports made in those selected categories. --- CHANGELOG.md | 1 + docs/_includes/admin-tasks-content.md | 6 ++-- perllib/FixMyStreet/App/Controller/Admin/Bodies.pm | 15 ++++------ perllib/FixMyStreet/Cobrand/Default.pm | 1 + t/app/controller/admin/bodies.t | 11 ++++++++ t/app/controller/report_updates.t | 33 ++++++++++++++-------- templates/web/base/admin/bodies/contact-form.html | 5 ++++ 7 files changed, 48 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad53ca547..e5fbb6ea8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Move stats from main admin index to stats index. - Speed up dashboard export and report search. - Allow a template to be an initial update on reports. #2973 + - Interface for disabling updates for certain categories. #2991 - Bugfixes - Application user in Docker container can't install packages. #2914 - Look at all categories when sending reports. diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index 7e3d47efe..a846b73cc 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -560,14 +560,14 @@ and staff users — can filter reports when viewing them on the site. From the Admin menu, click on ‘Categories’. You’ll see a table of existing categories, and below that, a form by which you can create new ones. - Input a title for the category, and the email address to which reports in that category should be forwarded. When creating a category, these are the only fields required. You can also choose a variety of options – whether to automatically hide any reports made in this category, whether to prevent form submission when this -category is selected, or what parent category or categories a particular -category is in. See below for information on creating/editing extra notices and questions for a category. diff --git a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm index 6ae068cd9..61b486f02 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm @@ -267,15 +267,12 @@ sub update_contact : Private { $contact->send_method( $c->get_param('send_method') ); # Set flags in extra to the appropriate values - if ( $c->get_param('photo_required') ) { - $contact->set_extra_metadata_if_undefined( photo_required => 1 ); - } else { - $contact->unset_extra_metadata( 'photo_required' ); - } - if ( $c->get_param('open311_protect') ) { - $contact->set_extra_metadata( open311_protect => 1 ); - } else { - $contact->unset_extra_metadata( 'open311_protect' ); + foreach (qw(photo_required open311_protect updates_disallowed)) { + if ( $c->get_param($_) ) { + $contact->set_extra_metadata( $_ => 1 ); + } else { + $contact->unset_extra_metadata($_); + } } if ( my @group = $c->get_param_list('group') ) { @group = grep { $_ } @group; diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 695487268..07e781479 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -530,6 +530,7 @@ or not. Default behaviour is disallowed if "closed_updates" metadata is set. sub updates_disallowed { my ($self, $problem) = @_; return 1 if $problem->get_extra_metadata('closed_updates'); + return 1 if $problem->contact && $problem->contact->get_extra_metadata('updates_disallowed'); return 0; } diff --git a/t/app/controller/admin/bodies.t b/t/app/controller/admin/bodies.t index c73a90da1..7ec7aed75 100644 --- a/t/app/controller/admin/bodies.t +++ b/t/app/controller/admin/bodies.t @@ -261,6 +261,17 @@ subtest 'open311 protection editing' => sub { is $contact->get_extra_metadata('open311_protect'), 1, 'Open311 protect flag set'; }; +subtest 'updates disabling' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->submit_form_ok( { with_fields => { + updates_disallowed => 1, + note => 'Disabling updates', + } } ); + $mech->content_contains('Values updated'); + my $contact = $body->contacts->find({ category => 'test category' }); + is $contact->get_extra_metadata('updates_disallowed'), 1, 'Updates disallowed flag set'; +}; + }; # END of override wrap diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index 07ee48587..e8ab1cc85 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -22,6 +22,8 @@ my $user2 = $mech->create_user_ok('commenter@example.com', name => 'Commenter'); my $body = $mech->create_body_ok(2504, 'Westminster City Council'); +my $contact = $mech->create_contact_ok( body_id => $body->id, category => 'Other', email => 'other' ); + my $dt = DateTime->new( year => 2011, month => 04, @@ -1893,6 +1895,18 @@ for my $test ( }; } +$mech->log_in_ok( $report->user->email ); + +my %standard_fields = ( + name => $report->user->name, + update => 'update text', + photo1 => '', + photo2 => '', + photo3 => '', + may_show_name => 1, + add_alert => 1, +); + for my $test ( { desc => 'update confirmed without marking as fixed leaves state unchanged', @@ -2094,18 +2108,6 @@ for my $test ( }, ) { subtest $test->{desc} => sub { - $mech->log_in_ok( $report->user->email ); - - my %standard_fields = ( - name => $report->user->name, - update => 'update text', - photo1 => '', - photo2 => '', - photo3 => '', - may_show_name => 1, - add_alert => 1, - ); - my %expected_fields = ( %standard_fields, %{ $test->{expected_form_fields} }, @@ -2178,4 +2180,11 @@ FixMyStreet::override_config { }; }; +subtest 'check disabling of updates per category' => sub { + $contact->set_extra_metadata( updates_disallowed => 1 ); + $contact->update; + $mech->get_ok("/report/$report_id"); + $mech->content_lacks('Provide an update'); +}; + done_testing(); diff --git a/templates/web/base/admin/bodies/contact-form.html b/templates/web/base/admin/bodies/contact-form.html index 35fab4541..ddee35020 100644 --- a/templates/web/base/admin/bodies/contact-form.html +++ b/templates/web/base/admin/bodies/contact-form.html @@ -63,6 +63,11 @@

+

+ + +

+ [% IF body.send_method == 'Open311' %]

-- cgit v1.2.3 From a2cc36b0d94143d1150a73993541eb829bd9b9fa Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 4 May 2020 16:33:01 +0100 Subject: Include group in CSV export if enabled. --- CHANGELOG.md | 1 + perllib/FixMyStreet/App/Controller/Dashboard.pm | 29 ++++++++++++++++++++----- perllib/FixMyStreet/Cobrand/Bexley.pm | 27 ----------------------- perllib/FixMyStreet/Cobrand/TfL.pm | 12 ---------- perllib/FixMyStreet/DB/ResultSet/Contact.pm | 9 -------- t/cobrand/bexley.t | 19 +++++++++------- t/cobrand/tfl.t | 1 + 7 files changed, 37 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7772d93e8..0877dcca0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Speed up dashboard export and report search. #2988 - Allow a template to be an initial update on reports. #2973 - Interface for disabling updates for certain categories. #2991 + - Include group in CSV export if enabled. #2994 - Bugfixes: - Application user in Docker container can't install packages. #2914 - Look at all categories when sending reports. diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index f3b607ba8..e5a526efc 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -152,14 +152,14 @@ sub index : Path : Args(0) { sub construct_rs_filter : Private { my ($self, $c, $updates) = @_; + my $table_name = $updates ? 'problem' : 'me'; + my %where; $where{areas} = [ map { { 'like', "%,$_,%" } } @{$c->stash->{ward}} ] if @{$c->stash->{ward}}; - $where{category} = $c->stash->{category} + $where{"$table_name.category"} = $c->stash->{category} if $c->stash->{category}; - my $table_name = $updates ? 'problem' : 'me'; - my $state = $c->stash->{q_state}; if ( FixMyStreet::DB::Result::Problem->fixed_states->{$state} ) { # Probably fixed - council $where{"$table_name.state"} = [ FixMyStreet::DB::Result::Problem->fixed_states() ]; @@ -347,10 +347,17 @@ sub export_as_csv_updates : Private { sub export_as_csv : Private { my ($self, $c) = @_; + my $groups = $c->cobrand->enable_category_groups ? 1 : 0; + my $join = ['comments']; + my $columns = ['comments.problem_state', 'comments.state', 'comments.confirmed', 'comments.mark_fixed']; + if ($groups) { + push @$join, 'contact'; + push @$columns, 'contact.extra'; + } my $csv = $c->stash->{csv} = { objects => $c->stash->{objects_rs}->search_rs({}, { - join => 'comments', - '+columns' => ['comments.problem_state', 'comments.state', 'comments.confirmed', 'comments.mark_fixed'], + join => $join, + '+columns' => $columns, order_by => ['me.confirmed', 'me.id'], cursor_page_size => 1000, }), @@ -360,6 +367,7 @@ sub export_as_csv : Private { 'Detail', 'User Name', 'Category', + $groups ? ('Subcategory') : (), 'Created', 'Confirmed', 'Acknowledged', @@ -381,6 +389,7 @@ sub export_as_csv : Private { 'detail', 'user_name_display', 'category', + $groups ? ('subcategory') : (), 'created', 'confirmed', 'acknowledged', @@ -481,6 +490,16 @@ sub generate_csv : Private { ($hashref->{local_coords_x}, $hashref->{local_coords_y}) = $obj->local_coords; } + + if ($asked_for{subcategory}) { + my $group = $obj->contact && $obj->contact->get_extra_metadata('group') || ''; + $group = join(',', ref $group ? @$group : $group); + if ($group) { + $hashref->{subcategory} = $obj->category; + $hashref->{category} = $group; + } + } + if ($obj->can('url')) { my $base = $c->cobrand->base_url_for_report($obj->can('problem') ? $obj->problem : $obj); $hashref->{url} = join '', $base, $obj->url; diff --git a/perllib/FixMyStreet/Cobrand/Bexley.pm b/perllib/FixMyStreet/Cobrand/Bexley.pm index f37394794..ad3036711 100644 --- a/perllib/FixMyStreet/Cobrand/Bexley.pm +++ b/perllib/FixMyStreet/Cobrand/Bexley.pm @@ -216,33 +216,6 @@ sub email_list { return @to; } -sub dashboard_export_problems_add_columns { - my $self = shift; - my $c = $self->{c}; - - my %groups; - if ($c->stash->{body}) { - %groups = FixMyStreet::DB->resultset('Contact')->search({ - body_id => $c->stash->{body}->id, - })->group_lookup; - } - - splice @{$c->stash->{csv}->{headers}}, 5, 0, 'Subcategory'; - splice @{$c->stash->{csv}->{columns}}, 5, 0, 'subcategory'; - - $c->stash->{csv}->{extra_data} = sub { - my $report = shift; - - if ($groups{$report->category}) { - return { - category => $groups{$report->category}, - subcategory => $report->category, - }; - } - return {}; - }; -} - sub _is_out_of_hours { my $time = localtime; return 1 if $time->hour > 16 || ($time->hour == 16 && $time->min >= 45); diff --git a/perllib/FixMyStreet/Cobrand/TfL.pm b/perllib/FixMyStreet/Cobrand/TfL.pm index b98ad1d8b..78b7c271a 100644 --- a/perllib/FixMyStreet/Cobrand/TfL.pm +++ b/perllib/FixMyStreet/Cobrand/TfL.pm @@ -245,16 +245,6 @@ sub dashboard_export_problems_add_columns { my $self = shift; my $c = $self->{c}; - my %groups; - if ($c->stash->{body}) { - %groups = FixMyStreet::DB->resultset('Contact')->search({ - body_id => $c->stash->{body}->id, - })->group_lookup; - } - - splice @{$c->stash->{csv}->{headers}}, 5, 0, 'Subcategory'; - splice @{$c->stash->{csv}->{columns}}, 5, 0, 'subcategory'; - $c->stash->{csv}->{headers} = [ map { $_ eq 'Ward' ? 'Borough' : $_ } @{ $c->stash->{csv}->{headers} }, "Agent responsible", @@ -315,8 +305,6 @@ sub dashboard_export_problems_add_columns { my $fields = { acknowledged => $report->whensent, agent_responsible => $agent ? $agent->name : '', - category => $groups{$report->category}, - subcategory => $report->category, user_name_display => $user_name_display, safety_critical => $safety_critical, delivered_to => join(',', @$delivered_to), diff --git a/perllib/FixMyStreet/DB/ResultSet/Contact.pm b/perllib/FixMyStreet/DB/ResultSet/Contact.pm index 801d20cc0..3aceee9e7 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Contact.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Contact.pm @@ -90,13 +90,4 @@ sub summary_count { ); } -sub group_lookup { - my $rs = shift; - map { - my $group = $_->get_extra_metadata('group') || ''; - $group = join(',', ref $group ? @$group : $group); - $_->category => $group - } $rs->all; -} - 1; diff --git a/t/cobrand/bexley.t b/t/cobrand/bexley.t index 352e61389..91e30ff50 100644 --- a/t/cobrand/bexley.t +++ b/t/cobrand/bexley.t @@ -50,14 +50,17 @@ FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'bexley' ], MAPIT_URL => 'http://mapit.uk/', STAGING_FLAGS => { send_reports => 1, skip_checks => 0 }, - COBRAND_FEATURES => { open311_email => { bexley => { - p1 => 'p1@bexley', - p1confirm => 'p1confirm@bexley', - lighting => 'thirdparty@notbexley.example.com,another@notbexley.example.com', - outofhours => 'outofhours@bexley,ooh2@bexley', - flooding => 'flooding@bexley', - eh => 'eh@bexley', - } } }, + COBRAND_FEATURES => { + open311_email => { bexley => { + p1 => 'p1@bexley', + p1confirm => 'p1confirm@bexley', + lighting => 'thirdparty@notbexley.example.com,another@notbexley.example.com', + outofhours => 'outofhours@bexley,ooh2@bexley', + flooding => 'flooding@bexley', + eh => 'eh@bexley', + } }, + category_groups => { bexley => 1 }, + }, }, sub { subtest 'cobrand displays council name' => sub { diff --git a/t/cobrand/tfl.t b/t/cobrand/tfl.t index 8ddc3d671..f8e0a11c8 100644 --- a/t/cobrand/tfl.t +++ b/t/cobrand/tfl.t @@ -188,6 +188,7 @@ FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'tfl', 'bromley', 'fixmystreet'], MAPIT_URL => 'http://mapit.uk/', COBRAND_FEATURES => { + category_groups => { tfl => 1 }, internal_ips => { tfl => [ '127.0.0.1' ] }, base_url => { tfl => 'https://street.tfl' -- cgit v1.2.3 From f6d807fd5217a19ac488f652d1f0853a7891231f Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 1 May 2020 15:42:01 +0100 Subject: Add way to disallow report reopening in a category Add a tickbox to the category admin, and do not allow reopening on reports made in the selected categories. --- CHANGELOG.md | 2 +- docs/_includes/admin-tasks-content.md | 1 + perllib/FixMyStreet/App/Controller/Admin/Bodies.pm | 2 +- perllib/FixMyStreet/Cobrand/Default.pm | 17 ++++++++++++++++- t/app/controller/admin/bodies.t | 11 +++++++++++ t/app/controller/report_updates.t | 11 +++++++++++ templates/web/base/admin/bodies/contact-form.html | 6 ++++++ .../web/base/report/update/form_state_checkbox.html | 2 ++ .../report/update/form_state_checkbox.html | 2 ++ 9 files changed, 51 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7772d93e8..ff8b11a7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ - Move stats from main admin index to stats index. #2982 - Speed up dashboard export and report search. #2988 - Allow a template to be an initial update on reports. #2973 - - Interface for disabling updates for certain categories. #2991 + - Interface for disabling updates/reopening for certain categories. #2991 #2992 - Bugfixes: - Application user in Docker container can't install packages. #2914 - Look at all categories when sending reports. diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index a846b73cc..765e96370 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -566,6 +566,7 @@ forwarded. When creating a category, these are the only fields required. You can also choose a variety of options – whether to automatically hide any reports made in this category, whether to prevent form submission when this category is selected, whether updates are allowed on reports in this category, +whether members of the public can reopen fixed/closed reports in this category, or what parent category or categories a particular category is in. See below for information on creating/editing extra notices and diff --git a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm index 61b486f02..bfa74ad4e 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm @@ -267,7 +267,7 @@ sub update_contact : Private { $contact->send_method( $c->get_param('send_method') ); # Set flags in extra to the appropriate values - foreach (qw(photo_required open311_protect updates_disallowed)) { + foreach (qw(photo_required open311_protect updates_disallowed reopening_disallowed)) { if ( $c->get_param($_) ) { $contact->set_extra_metadata( $_ => 1 ); } else { diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 07e781479..df7e333fb 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -523,7 +523,8 @@ sub allow_update_reporting { return 0; } =item updates_disallowed Returns a boolean indicating whether updates on a particular report are allowed -or not. Default behaviour is disallowed if "closed_updates" metadata is set. +or not. Default behaviour is disallowed if "closed_updates" metadata is set, or +if the report's category has its "updates_disallowed" flag set. =cut @@ -534,6 +535,20 @@ sub updates_disallowed { return 0; } +=item reopening_disallowed + +Returns a boolean indicating whether reopening of a particular report is +allowed or not. Default behaviour is allowed unless the report's category +has its reopening_disallowed flag set. + +=cut + +sub reopening_disallowed { + my ($self, $problem) = @_; + return 1 if $problem->contact && $problem->contact->get_extra_metadata('reopening_disallowed'); + return 0; +} + =item geocode_postcode Given a QUERY, return LAT/LON and/or ERROR. diff --git a/t/app/controller/admin/bodies.t b/t/app/controller/admin/bodies.t index 7ec7aed75..d3e4074f9 100644 --- a/t/app/controller/admin/bodies.t +++ b/t/app/controller/admin/bodies.t @@ -272,6 +272,17 @@ subtest 'updates disabling' => sub { is $contact->get_extra_metadata('updates_disallowed'), 1, 'Updates disallowed flag set'; }; +subtest 'reopen disabling' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->submit_form_ok( { with_fields => { + reopening_disallowed => 1, + note => 'Disabling reopening', + } } ); + $mech->content_contains('Values updated'); + my $contact = $body->contacts->find({ category => 'test category' }); + is $contact->get_extra_metadata('reopening_disallowed'), 1, 'Reopening disallowed flag set'; +}; + }; # END of override wrap diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index e8ab1cc85..e97b04f12 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -2145,6 +2145,17 @@ for my $test ( }; } +subtest 'check disabling of reopening' => sub { + $report->state('fixed - council'); + $report->update; + $mech->get_ok("/report/$report_id"); + $mech->content_contains('This problem has not been fixed'); + $contact->set_extra_metadata( reopening_disallowed => 1 ); + $contact->update; + $mech->get_ok("/report/$report_id"); + $mech->content_lacks('This problem has not been fixed'); +}; + subtest 'check have to be logged in for creator fixed questionnaire' => sub { $mech->log_out_ok(); diff --git a/templates/web/base/admin/bodies/contact-form.html b/templates/web/base/admin/bodies/contact-form.html index ddee35020..0224946cf 100644 --- a/templates/web/base/admin/bodies/contact-form.html +++ b/templates/web/base/admin/bodies/contact-form.html @@ -68,6 +68,12 @@

+

+ + + [% loc('Use this where you do not want problem reporters to be able to reopen their fixed or closed reports when leaving an update.') %] +

+ [% IF body.send_method == 'Open311' %]

diff --git a/templates/web/base/report/update/form_state_checkbox.html b/templates/web/base/report/update/form_state_checkbox.html index 5316affb9..b65df72b7 100644 --- a/templates/web/base/report/update/form_state_checkbox.html +++ b/templates/web/base/report/update/form_state_checkbox.html @@ -1,5 +1,7 @@ [% IF (problem.is_fixed OR problem.is_closed) AND ((c.user_exists AND c.user.id == problem.user_id) OR alert_to_reporter) %] + [% RETURN IF c.cobrand.reopening_disallowed(problem) ~%] + [% IF problem.is_closed %] diff --git a/templates/web/fixmystreet.com/report/update/form_state_checkbox.html b/templates/web/fixmystreet.com/report/update/form_state_checkbox.html index 16ae59bc8..41b8c4897 100644 --- a/templates/web/fixmystreet.com/report/update/form_state_checkbox.html +++ b/templates/web/fixmystreet.com/report/update/form_state_checkbox.html @@ -2,6 +2,8 @@ [% IF (problem.is_fixed OR problem.is_closed) AND ((c.user_exists AND c.user.id == problem.user_id) OR alert_to_reporter) %] + [% RETURN IF c.cobrand.reopening_disallowed(problem) ~%] + [% IF problem.is_closed %] -- cgit v1.2.3 From 5ce552af2b66613272dc9cfe2d46532e057f44f7 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 7 May 2020 12:44:23 +0100 Subject: Fix dashboard report CSV export. The change to join in e16054150 did not include a collapse that the previous prefetch was doing, so multiple rows per result were being returned. --- CHANGELOG.md | 2 ++ perllib/FixMyStreet/App/Controller/Dashboard.pm | 5 +++-- t/app/controller/dashboard.t | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b62915428..61a56ebdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## Releases * Unreleased + - Bugfixes: + - Fix issue with dashboard report CSV export. #3026 * v3.0.1 (6th May 2020) - New features: diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index e5a526efc..833d6d6b2 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -349,14 +349,15 @@ sub export_as_csv : Private { my $groups = $c->cobrand->enable_category_groups ? 1 : 0; my $join = ['comments']; - my $columns = ['comments.problem_state', 'comments.state', 'comments.confirmed', 'comments.mark_fixed']; + my $columns = ['comments.id', 'comments.problem_state', 'comments.state', 'comments.confirmed', 'comments.mark_fixed']; if ($groups) { push @$join, 'contact'; - push @$columns, 'contact.extra'; + push @$columns, 'contact.id', 'contact.extra'; } my $csv = $c->stash->{csv} = { objects => $c->stash->{objects_rs}->search_rs({}, { join => $join, + collapse => 1, '+columns' => $columns, order_by => ['me.confirmed', 'me.id'], cursor_page_size => 1000, diff --git a/t/app/controller/dashboard.t b/t/app/controller/dashboard.t index 72fc00128..c62ada89a 100644 --- a/t/app/controller/dashboard.t +++ b/t/app/controller/dashboard.t @@ -70,6 +70,7 @@ foreach my $problem (@fixed_problems) { foreach my $problem (@closed_problems) { $problem->update({ state => 'closed' }); + $mech->create_comment_for_problem($problem, $counciluser, 'Name', 'in progress text', 0, 'confirmed', 'in progress'); $mech->create_comment_for_problem($problem, $counciluser, 'Title', 'text', 0, 'confirmed', 'closed'); } @@ -214,7 +215,7 @@ FixMyStreet::override_config { subtest 'export updates as csv' => sub { $mech->get_ok('/dashboard?updates=1&export=1'); my @rows = $mech->content_as_csv; - is scalar @rows, 15, '1 (header) + 14 (updates) = 15 lines'; + is scalar @rows, 18, '1 (header) + 17 (updates) = 18 lines'; is scalar @{$rows[0]}, 8, '8 columns present'; is_deeply $rows[0], -- cgit v1.2.3 From 7cd75c95b5b910b44687b3b478bd0eadbf6f0549 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Thu, 7 May 2020 13:52:59 +0100 Subject: [TfL] Add new pavement overcrowding category TLRN-only. For https://github.com/mysociety/fixmystreet-commercial/issues/1883 --- perllib/FixMyStreet/Cobrand/TfL.pm | 1 + web/cobrands/tfl/assets.js | 1 + 2 files changed, 2 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/TfL.pm b/perllib/FixMyStreet/Cobrand/TfL.pm index 78b7c271a..da762e386 100644 --- a/perllib/FixMyStreet/Cobrand/TfL.pm +++ b/perllib/FixMyStreet/Cobrand/TfL.pm @@ -486,6 +486,7 @@ sub _tlrn_categories { [ "Mobile Crane Operation", "Other (TfL)", "Pavement Defect (uneven surface / cracked paving slab)", + "Pavement Overcrowding", "Pothole", "Pothole (minor)", "Roadworks", diff --git a/web/cobrands/tfl/assets.js b/web/cobrands/tfl/assets.js index ad58c5c8a..b47769b6f 100644 --- a/web/cobrands/tfl/assets.js +++ b/web/cobrands/tfl/assets.js @@ -64,6 +64,7 @@ var tlrn_categories = [ "Mobile Crane Operation", "Other (TfL)", "Pavement Defect (uneven surface / cracked paving slab)", + "Pavement Overcrowding", "Pothole", "Pothole (minor)", "Roadworks", -- cgit v1.2.3 From 725d7ccf1874e3404c6fa05b8269cb57bb2b2ea1 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 7 May 2020 17:08:49 +0100 Subject: [Buckinghamshire] Stop hiding staging banner. --- web/cobrands/buckinghamshire/base.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/cobrands/buckinghamshire/base.scss b/web/cobrands/buckinghamshire/base.scss index 0c7bb4bc2..8df2cab7b 100644 --- a/web/cobrands/buckinghamshire/base.scss +++ b/web/cobrands/buckinghamshire/base.scss @@ -5,10 +5,6 @@ @import "../sass/base"; -.dev-site-notice { - display: none; -} - .container { padding: 0 20px; } -- cgit v1.2.3 From dc1507989b865999a96b448aad46a65ccb468244 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 15 Apr 2020 17:02:44 +0100 Subject: In category admin, group already shown elsewhere. Groups have their own special admin section above, so no need to show it in the extra data list (especially as if it is in more than one group it only shows a reference). --- CHANGELOG.md | 1 + templates/web/base/admin/bodies/contact-form.html | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a56ebdd..c19e2ffa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Allow a template to be an initial update on reports. #2973 - Interface for disabling updates/reopening for certain categories. #2991 #2992 - Include group in CSV export if enabled. #2994 + - In category admin, group already shown elsewhere. - Bugfixes: - Application user in Docker container can't install packages. #2914 - Look at all categories when sending reports. diff --git a/templates/web/base/admin/bodies/contact-form.html b/templates/web/base/admin/bodies/contact-form.html index 0224946cf..d989b62aa 100644 --- a/templates/web/base/admin/bodies/contact-form.html +++ b/templates/web/base/admin/bodies/contact-form.html @@ -125,6 +125,7 @@

[% loc('Extra data:') %]

[% FOR pair IN contact.get_extra_metadata %] + [% NEXT IF pair.key == 'group' %]
[% pair.key %]
[% pair.value OR '-' %]
[% END %]
-- cgit v1.2.3 From 7d395d5ca8049a904473e90957115ce675e57db7 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 17 Apr 2020 12:43:57 +0100 Subject: Refactor report page permissions. Look up user's permissions once at the start, and use that throughout the report page templates. --- perllib/FixMyStreet/App/Controller/Report.pm | 28 ++++++++++------------ perllib/FixMyStreet/DB/Result/User.pm | 2 +- templates/web/base/admin/triage/_inspect.html | 1 - templates/web/base/report/_inspect.html | 1 - templates/web/base/report/display.html | 3 --- templates/web/base/report/display_tools.html | 2 +- templates/web/base/report/inspect/information.html | 2 +- .../web/base/report/update/form_user_loggedin.html | 6 ++--- 8 files changed, 18 insertions(+), 27 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 058edebd8..82e8b107f 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -85,14 +85,14 @@ sub display :PathPart('') :Chained('id') :Args(0) { $c->forward( 'load_updates' ); $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 triage/ ] ); - if (any { $_ } values %$permissions) { + my $permissions = $c->stash->{permissions} ||= $c->forward('fetch_permissions'); + + if (grep { $permissions->{$_} } qw/report_inspect report_edit_category report_edit_priority report_mark_private triage/) { $c->stash->{template} = 'report/inspect.html'; $c->forward('inspect'); } - if ($c->user_exists && $c->user->has_permission_to(contribute_as_another_user => $c->stash->{problem}->bodies_str_ids)) { + if ($permissions->{contribute_as_another_user}) { $c->stash->{email} = $c->user->email; } } @@ -160,8 +160,7 @@ sub load_problem_or_display_error : Private { } elsif ( $problem->non_public ) { # Creator, and inspection users can see non_public reports $c->stash->{problem} = $problem; - my $permissions = $c->stash->{_permissions} = $c->forward( 'check_has_permission_to', - [ qw/report_inspect report_edit_category report_edit_priority report_mark_private / ] ); + my $permissions = $c->stash->{permissions} = $c->forward('fetch_permissions'); # If someone has clicked a unique token link in an email to them my $from_email = $c->sessionid && $c->flash->{alert_to_reporter} && $c->flash->{alert_to_reporter} == $problem->id; @@ -386,7 +385,7 @@ sub delete :Chained('id') :Args(0) { sub inspect : Private { my ( $self, $c ) = @_; my $problem = $c->stash->{problem}; - my $permissions = $c->stash->{_permissions}; + my $permissions = $c->stash->{permissions}; $c->forward('/admin/reports/categories_for_point'); $c->stash->{report_meta} = { map { 'x' . $_->{name} => $_ } @{ $c->stash->{problem}->get_extra_fields() } }; @@ -668,22 +667,19 @@ sub _nearby_json :Private { } -=head2 check_has_permission_to +=head2 fetch_permissions -Ensure the currently logged-in user has any of the provided permissions applied -to the current Problem in $c->stash->{problem}. Shows the 403 page if not. +Returns a hash of the user's permissions, applied to the problem +in $c->stash->{problem}. =cut -sub check_has_permission_to : Private { - my ( $self, $c, @permissions ) = @_; +sub fetch_permissions : Private { + my ( $self, $c ) = @_; return {} unless $c->user_exists; - my $bodies = $c->stash->{problem}->bodies_str_ids; - my %permissions = map { $_ => $c->user->has_permission_to($_, $bodies) } @permissions; - return \%permissions; + return $c->user->permissions($c->stash->{problem}); }; - sub stash_category_groups : Private { my ( $self, $c, $contacts, $combine_multiple ) = @_; diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index b0a05d0b7..49338f245 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -444,7 +444,7 @@ sub permissions { my $body_id = $problem->bodies_str; - return unless $self->belongs_to_body($body_id); + return {} unless $self->belongs_to_body($body_id); my @permissions = grep { $_->{body_id} == $self->from_body->id } @{$self->body_permissions}; return { map { $_->{permission} => 1 } @permissions }; diff --git a/templates/web/base/admin/triage/_inspect.html b/templates/web/base/admin/triage/_inspect.html index 926197ceb..bd76a47f7 100644 --- a/templates/web/base/admin/triage/_inspect.html +++ b/templates/web/base/admin/triage/_inspect.html @@ -24,7 +24,6 @@ [% END %] -[% permissions = c.user.permissions(problem) %] [% second_column = BLOCK -%]
diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html index 771942b16..a8be342d0 100644 --- a/templates/web/base/report/_inspect.html +++ b/templates/web/base/report/_inspect.html @@ -1,4 +1,3 @@ -[% permissions = c.user.permissions(problem) %] [% second_column = BLOCK -%]
diff --git a/templates/web/base/report/display.html b/templates/web/base/report/display.html index f08df931d..af2282f66 100644 --- a/templates/web/base/report/display.html +++ b/templates/web/base/report/display.html @@ -38,9 +38,6 @@ [% SET shown_form = 1 %] [% END %] -[% IF c.user_exists %] - [% DEFAULT permissions = c.user.permissions(problem) %] -[%- END %] [% INCLUDE 'report/_main.html' %] [% IF problem.duplicate_of %] diff --git a/templates/web/base/report/display_tools.html b/templates/web/base/report/display_tools.html index e16ffcb2c..a09abd73b 100644 --- a/templates/web/base/report/display_tools.html +++ b/templates/web/base/report/display_tools.html @@ -46,7 +46,7 @@ [% loc('Receive email when updates are left on this problem.' ) %]

[% IF c.user_exists %] - [% IF c.user.has_permission_to("contribute_as_another_user", problem.bodies_str_ids) %] + [% IF permissions.contribute_as_another_user %]
diff --git a/templates/web/base/report/inspect/information.html b/templates/web/base/report/inspect/information.html index b81b37543..3abde9a98 100644 --- a/templates/web/base/report/inspect/information.html +++ b/templates/web/base/report/inspect/information.html @@ -6,7 +6,7 @@

[% loc('Report ID:') %] [% problem.id %] - [% IF c.user_exists AND c.cobrand.admin_allow_user(c.user) AND c.user.has_permission_to('report_edit', problem.bodies_str_ids) %] + [% IF c.user_exists AND c.cobrand.admin_allow_user(c.user) AND permissions.report_edit %] ([% loc('admin') %]) [% END %]

diff --git a/templates/web/base/report/update/form_user_loggedin.html b/templates/web/base/report/update/form_user_loggedin.html index bec783bb4..35c9beeff 100644 --- a/templates/web/base/report/update/form_user_loggedin.html +++ b/templates/web/base/report/update/form_user_loggedin.html @@ -4,9 +4,9 @@ [% PROCESS 'user/_anonymity.html' anonymous = update.anonymous %] - [% can_contribute_as_another_user = c.user.has_permission_to("contribute_as_another_user", problem.bodies_str_ids) %] - [% can_contribute_as_anonymous_user = c.user.has_permission_to("contribute_as_anonymous_user", problem.bodies_str_ids) %] - [% can_contribute_as_body = c.user.from_body AND c.user.has_permission_to("contribute_as_body", problem.bodies_str_ids) %] + [% can_contribute_as_another_user = permissions.contribute_as_another_user %] + [% can_contribute_as_anonymous_user = permissions.contribute_as_anonymous_user %] + [% can_contribute_as_body = c.user.from_body AND permissions.contribute_as_body %] [% IF can_contribute_as_another_user OR can_contribute_as_body OR can_contribute_as_anonymous_user %] -- cgit v1.2.3 From ecc5a7b9ca20418f1bdb45cdc3ce5b41a11f3593 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 15 Apr 2020 19:12:31 +0100 Subject: Add assigned_(users|categories)_only functionality Users with assigned_categories_only will only see staff features on a report page in their assigned categories. Users will only see staff features on a report page in a category with assigned_users_only if it is in their assigned categories. --- CHANGELOG.md | 1 + .../img/pro-user-guide/edit-category-page.png | Bin 65237 -> 42128 bytes perllib/FixMyStreet/App/Controller/Admin/Bodies.pm | 2 +- perllib/FixMyStreet/App/Controller/Admin/Users.pm | 5 ++ perllib/FixMyStreet/App/Controller/Report.pm | 18 +++++++ t/app/controller/admin/bodies.t | 10 ++++ t/app/controller/admin/users.t | 10 +++- t/app/controller/report_inspect.t | 52 +++++++++++++++++++-- templates/web/base/admin/bodies/contact-form.html | 8 +++- templates/web/base/admin/users/form.html | 15 ++++++ templates/web/base/report/display_tools.html | 2 +- templates/web/base/report/update/form_update.html | 4 +- 12 files changed, 118 insertions(+), 9 deletions(-) mode change 100644 => 100755 docs/assets/img/pro-user-guide/edit-category-page.png diff --git a/CHANGELOG.md b/CHANGELOG.md index c19e2ffa3..2b429624e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Interface for disabling updates/reopening for certain categories. #2991 #2992 - Include group in CSV export if enabled. #2994 - In category admin, group already shown elsewhere. + - Add assigned_(users|categories)_only functionality. - Bugfixes: - Application user in Docker container can't install packages. #2914 - Look at all categories when sending reports. diff --git a/docs/assets/img/pro-user-guide/edit-category-page.png b/docs/assets/img/pro-user-guide/edit-category-page.png old mode 100644 new mode 100755 index 8b029faa0..db84bbb70 Binary files a/docs/assets/img/pro-user-guide/edit-category-page.png and b/docs/assets/img/pro-user-guide/edit-category-page.png differ diff --git a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm index bfa74ad4e..07d058872 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Bodies.pm @@ -267,7 +267,7 @@ sub update_contact : Private { $contact->send_method( $c->get_param('send_method') ); # Set flags in extra to the appropriate values - foreach (qw(photo_required open311_protect updates_disallowed reopening_disallowed)) { + foreach (qw(photo_required open311_protect updates_disallowed reopening_disallowed assigned_users_only)) { if ( $c->get_param($_) ) { $contact->set_extra_metadata( $_ => 1 ); } else { diff --git a/perllib/FixMyStreet/App/Controller/Admin/Users.pm b/perllib/FixMyStreet/App/Controller/Admin/Users.pm index 046e19126..f4b9bd7dc 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Users.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Users.pm @@ -373,6 +373,11 @@ sub edit : Chained('user') : PathPart('') : Args(0) { my @live_contact_ids = map { $_->id } @live_contacts; my @new_contact_ids = grep { $c->get_param("contacts[$_]") } @live_contact_ids; $user->set_extra_metadata('categories', \@new_contact_ids); + if ($c->get_param('assigned_categories_only')) { + $user->set_extra_metadata(assigned_categories_only => 1); + } else { + $user->unset_extra_metadata('assigned_categories_only'); + } } else { $user->unset_extra_metadata('categories'); } diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 82e8b107f..3052b1015 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -87,6 +87,24 @@ sub display :PathPart('') :Chained('id') :Args(0) { my $permissions = $c->stash->{permissions} ||= $c->forward('fetch_permissions'); + my $staff_user = $c->user_exists && ($c->user->is_superuser || $c->user->belongs_to_body($c->stash->{problem}->bodies_str)); + + if ($staff_user) { + # Check assigned categories feature + my $okay = 1; + my $contact = $c->stash->{problem}->contact; + if ($contact && ($c->user->get_extra_metadata('assigned_categories_only') || $contact->get_extra_metadata('assigned_users_only'))) { + my $user_cats = $c->user->get_extra_metadata('categories') || []; + $okay = any { $contact->id eq $_ } @$user_cats; + } + if ($okay) { + $c->stash->{relevant_staff_user} = 1; + } else { + # Remove all staff permissions + $permissions = $c->stash->{permissions} = {}; + } + } + if (grep { $permissions->{$_} } qw/report_inspect report_edit_category report_edit_priority report_mark_private triage/) { $c->stash->{template} = 'report/inspect.html'; $c->forward('inspect'); diff --git a/t/app/controller/admin/bodies.t b/t/app/controller/admin/bodies.t index d3e4074f9..883386380 100644 --- a/t/app/controller/admin/bodies.t +++ b/t/app/controller/admin/bodies.t @@ -261,6 +261,16 @@ subtest 'open311 protection editing' => sub { is $contact->get_extra_metadata('open311_protect'), 1, 'Open311 protect flag set'; }; +subtest 'test assigned_users_only setting' => sub { + $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); + $mech->submit_form_ok( { with_fields => { + assigned_users_only => 1, + } } ); + $mech->content_contains('Values updated'); + my $contact = $body->contacts->find({ category => 'test category' }); + is $contact->get_extra_metadata('assigned_users_only'), 1; +}; + subtest 'updates disabling' => sub { $mech->get_ok('/admin/body/' . $body->id . '/test%20category'); $mech->submit_form_ok( { with_fields => { diff --git a/t/app/controller/admin/users.t b/t/app/controller/admin/users.t index 4f0298103..bc8d28e2d 100644 --- a/t/app/controller/admin/users.t +++ b/t/app/controller/admin/users.t @@ -299,6 +299,7 @@ FixMyStreet::override_config { flagged => undef, is_superuser => undef, area_ids => undef, + assigned_categories_only => undef, %default_perms, roles => $role->id, }, @@ -320,6 +321,7 @@ FixMyStreet::override_config { flagged => undef, is_superuser => undef, area_ids => undef, + assigned_categories_only => undef, %default_perms, roles => $role->id, }, @@ -341,6 +343,7 @@ FixMyStreet::override_config { flagged => undef, is_superuser => undef, area_ids => undef, + assigned_categories_only => undef, %default_perms, roles => $role->id, }, @@ -365,6 +368,7 @@ FixMyStreet::override_config { flagged => undef, is_superuser => undef, area_ids => undef, + assigned_categories_only => undef, %default_perms, }, changes => { @@ -385,6 +389,7 @@ FixMyStreet::override_config { flagged => 'on', is_superuser => undef, area_ids => undef, + assigned_categories_only => undef, %default_perms, }, changes => { @@ -394,7 +399,7 @@ FixMyStreet::override_config { log_entries => [qw/edit edit edit edit/], }, { - desc => 'edit user add is_superuser', + desc => 'edit user add is_superuser and assigned_categories_only', fields => { name => 'Changed User', email => 'changed@example.com', @@ -405,10 +410,12 @@ FixMyStreet::override_config { flagged => undef, is_superuser => undef, area_ids => undef, + assigned_categories_only => undef, %default_perms, }, changes => { is_superuser => 'on', + assigned_categories_only => 'on', }, removed => [ keys %default_perms, @@ -428,6 +435,7 @@ FixMyStreet::override_config { flagged => undef, is_superuser => 'on', area_ids => undef, + assigned_categories_only => 'on', }, changes => { is_superuser => undef, diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t index 8deb2667e..2852f8d18 100644 --- a/t/app/controller/report_inspect.t +++ b/t/app/controller/report_inspect.t @@ -822,7 +822,53 @@ FixMyStreet::override_config { }; }; +FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => 'oxfordshire', +}, sub { + subtest 'test relevant staff user display' => sub { + $user->user_body_permissions->create({ body => $oxon, permission_type => 'planned_reports' }); + $user->user_body_permissions->create({ body => $oxon, permission_type => 'moderate' }); + $mech->log_in_ok('body@example.com'); -END { - done_testing(); -} + # First, check user can see staff things on reports 2 and 3 + $mech->get_ok("/report/$report2_id"); + $mech->content_contains(''); + $mech->content_contains('
'); + + # User's categories are ["Cows"], which is currently report 2 + # So should be able to see staff things on 2, but no longer on 3 + $user->set_extra_metadata(assigned_categories_only => 1); + $user->update; + $mech->get_ok("/report/$report2_id"); + $mech->content_contains(''); + $mech->content_lacks('
'); + $mech->content_lacks('Moderate this report'); + $mech->content_lacks('shortlist'); + $user->unset_extra_metadata('assigned_categories_only'); + $user->update; + + # Contact 2 is "Sheep", which is currently report 3 + # So again, should be able to see staff things on 2, but no longer on 3 + $contact2->set_extra_metadata(assigned_users_only => 1); + $contact2->update; + $mech->get_ok("/report/$report2_id"); + $mech->content_contains(''); + $mech->content_lacks('
'); + $mech->content_lacks('Moderate this report'); + $mech->content_lacks('shortlist'); + $contact2->unset_extra_metadata('assigned_users_only'); + $contact2->update; + }; +}; + +done_testing(); diff --git a/templates/web/base/admin/bodies/contact-form.html b/templates/web/base/admin/bodies/contact-form.html index d989b62aa..85ed48fbf 100644 --- a/templates/web/base/admin/bodies/contact-form.html +++ b/templates/web/base/admin/bodies/contact-form.html @@ -81,6 +81,12 @@

[% END %] +

+ + + [% loc('Use this if you wish only users assigned to this category to see staff-related features (such as the inspector form) in the front end.') %] +

+ [% IF body.can_be_devolved %]

@@ -125,7 +131,7 @@

[% loc('Extra data:') %]

[% FOR pair IN contact.get_extra_metadata %] - [% NEXT IF pair.key == 'group' %] + [% NEXT IF pair.key == 'group' OR pair.key == 'assigned_users_only' %]
[% pair.key %]
[% pair.value OR '-' %]
[% END %]
diff --git a/templates/web/base/admin/users/form.html b/templates/web/base/admin/users/form.html index 495da8648..efe885908 100644 --- a/templates/web/base/admin/users/form.html +++ b/templates/web/base/admin/users/form.html @@ -101,6 +101,21 @@ [% END %] + [% IF user.from_body AND c.cobrand.moniker != 'zurich' %] +
  • +
    +

    + [% loc("This means the user will only see front end staff features (such as the inspector form) in their assigned categories.") %] +

    +
    + + +
  • + [% END %] + [% IF c.cobrand.moniker != 'zurich' %]
  • diff --git a/templates/web/base/report/display_tools.html b/templates/web/base/report/display_tools.html index a09abd73b..4c79e4b71 100644 --- a/templates/web/base/report/display_tools.html +++ b/templates/web/base/report/display_tools.html @@ -1,7 +1,7 @@
      [% IF c.user_exists OR NOT problem.non_public %] - [% IF c.user_exists AND c.cobrand.users_can_hide AND c.user.belongs_to_body( problem.bodies_str ) %] + [% IF c.cobrand.users_can_hide AND relevant_staff_user %]
    • diff --git a/templates/web/base/report/update/form_update.html b/templates/web/base/report/update/form_update.html index ff4612840..089d63f55 100644 --- a/templates/web/base/report/update/form_update.html +++ b/templates/web/base/report/update/form_update.html @@ -29,7 +29,7 @@ [% TRY %][% PROCESS 'report/update/before_update.html' %][% CATCH file %][% END %] -[% IF c.user AND (c.user.is_superuser OR c.user.belongs_to_body(problem.bodies_str)) %] +[% IF relevant_staff_user %] [% INCLUDE 'admin/response_templates_select.html' for='form_update' %] [% END %] [% IF field_errors.update %] @@ -37,7 +37,7 @@ [% END %] -[% IF c.user AND (c.user.is_superuser OR c.user.belongs_to_body(problem.bodies_str)) %] +[% IF relevant_staff_user %] [% INCLUDE 'report/inspect/state_groups_select.html' %] [% ELSE %] -- cgit v1.2.3 From d1622dc20c1623e3f7fb54d72d99370af796c197 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 17 Apr 2020 14:07:32 +0100 Subject: Extend assigned_*_only to report lists. Shortlist buttons and extended print information will not appear where not allowed. --- perllib/FixMyStreet/App/Controller/Around.pm | 2 ++ perllib/FixMyStreet/App/Controller/Dashboard.pm | 3 ++- perllib/FixMyStreet/App/Controller/Report.pm | 13 +++++++++++++ perllib/FixMyStreet/App/Controller/Reports.pm | 12 ++++++++++-- t/app/controller/around.t | 25 ++++++++++++++++++++++++- templates/web/base/report/_item.html | 16 +++++++++++++--- 6 files changed, 64 insertions(+), 7 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index af50f1883..0e87d1e70 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -262,6 +262,8 @@ sub check_and_stash_category : Private { my %valid_categories = map { $_ => 1 } grep { $_ && $categories_mapped{$_} } @$categories; $c->stash->{filter_category} = \%valid_categories; $c->cobrand->call_hook('munge_around_filter_category_list'); + + $c->forward('/report/assigned_users_only', [ \@categories ]); } sub map_features : Private { diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index 833d6d6b2..52af45940 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -564,7 +564,8 @@ sub heatmap : Local : Args(0) { $c->stash->{children} = $children; $c->stash->{ward_hash} = { map { $_->{id} => 1 } @{$c->stash->{wards}} } if $c->stash->{wards}; - $c->forward('/reports/setup_categories_and_map'); + $c->forward('/reports/setup_categories'); + $c->forward('/reports/setup_map'); } sub heatmap_filters :Private { diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 3052b1015..8a6047194 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -724,6 +724,19 @@ sub stash_category_groups : Private { $c->stash->{category_groups} = \@category_groups; } +sub assigned_users_only : Private { + my ($self, $c, $categories) = @_; + + # Assigned only category checking + if ($c->user_exists && $c->user->from_body) { + my @assigned_users_only = grep { $_->get_extra_metadata('assigned_users_only') } @$categories; + $c->stash->{assigned_users_only} = { map { $_->category => 1 } @assigned_users_only }; + $c->stash->{assigned_categories_only} = $c->user->get_extra_metadata('assigned_categories_only'); + + $c->stash->{user_categories} = { map { $_ => 1 } @{$c->user->categories} }; + } +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 53f27eb62..10902eab9 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -154,6 +154,8 @@ sub ward : Path : Args(2) { $c->forward('stash_report_sort', [ $c->cobrand->reports_ordering ]); $c->forward( 'load_and_group_problems' ); + $c->forward('setup_categories'); + if ($c->get_param('ajax')) { my $ajax_template = $c->stash->{ajax_template} || 'reports/_problem-list.html'; $c->detach('ajax', [ $ajax_template ]); @@ -165,7 +167,7 @@ sub ward : Path : Args(2) { $c->stash->{stats} = $c->cobrand->get_report_stats(); - $c->forward('setup_categories_and_map'); + $c->forward('setup_map'); # List of wards if ( !$c->stash->{wards} && $c->stash->{body}->id && $c->stash->{body}->body_areas->first ) { @@ -181,7 +183,7 @@ sub ward : Path : Args(2) { } } -sub setup_categories_and_map :Private { +sub setup_categories :Private { my ($self, $c) = @_; my @categories = $c->stash->{body}->contacts->not_deleted->search( undef, { @@ -191,9 +193,15 @@ sub setup_categories_and_map :Private { $c->cobrand->call_hook('munge_reports_category_list', \@categories); + $c->forward('/report/assigned_users_only', [ \@categories ]); + $c->stash->{filter_categories} = \@categories; $c->stash->{filter_category} = { map { $_ => 1 } $c->get_param_list('filter_category', 1) }; $c->forward('/report/stash_category_groups', [ \@categories ]) if $c->cobrand->enable_category_groups; +} + +sub setup_map :Private { + my ($self, $c) = @_; my $pins = $c->stash->{pins} || []; diff --git a/t/app/controller/around.t b/t/app/controller/around.t index 186b833fd..6e49c6f29 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -150,7 +150,7 @@ subtest 'check missing body message not shown when it does not need to be' => su }; for my $permission ( qw/ report_inspect report_mark_private/ ) { - subtest 'check non public reports are displayed on around page with $permission permission' => sub { + subtest "check non public reports are displayed on around page with $permission permission" => sub { my $body = FixMyStreet::DB->resultset('Body')->find( $body_edin_id ); my $body2 = FixMyStreet::DB->resultset('Body')->find( $body_west_id ); my $user = $mech->log_in_ok( 'test@example.com' ); @@ -218,6 +218,29 @@ for my $permission ( qw/ report_inspect report_mark_private/ ) { }; } +subtest 'check assigned-only list items do not display shortlist buttons' => sub { + my $body = FixMyStreet::DB->resultset('Body')->find( $body_edin_id ); + my $contact = $mech->create_contact_ok( category => 'Horses', body_id => $body->id, email => "horses\@example.org" ); + $edinburgh_problems[4]->update({ category => 'Horses' }); + + my $user = $mech->log_in_ok( 'test@example.com' ); + $user->set_extra_metadata(assigned_categories_only => 1); + $user->user_body_permissions->delete(); + $user->set_extra_metadata(categories => [ $contact->id ]); + $user->update({ from_body => $body }); + $user->user_body_permissions->find_or_create({ body => $body, permission_type => 'planned_reports' }); + + FixMyStreet::override_config { + ALLOWED_COBRANDS => 'fixmystreet', + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->get_ok('/around?pc=EH1+1BB'); + }; + $mech->content_contains('shortlist-add-' . $edinburgh_problems[4]->id); + $mech->content_lacks('shortlist-add-' . $edinburgh_problems[3]->id); + $mech->content_lacks('shortlist-add-' . $edinburgh_problems[1]->id); +}; + my $body = $mech->create_body_ok(2237, "Oxfordshire"); subtest 'check category, status and extra filtering works on /around' => sub { diff --git a/templates/web/base/report/_item.html b/templates/web/base/report/_item.html index 4d74b3be9..baba04d3e 100644 --- a/templates/web/base/report/_item.html +++ b/templates/web/base/report/_item.html @@ -2,7 +2,17 @@ [% PROCESS 'admin/report_blocks.html' ~%] [% END ~%] -[% IF c.user.has_permission_to('planned_reports', problem.bodies_str_ids) OR c.user.is_planned_report(problem) ~%] +[% + +SET relevant_staff = 1; +SET is_user_category = user_categories.${problem.category}; +IF (assigned_users_only.${problem.category} OR assigned_categories_only) AND NOT is_user_category; + SET relevant_staff = 0; +END; + +~%] + +[% IF relevant_staff AND (c.user.has_permission_to('planned_reports', problem.bodies_str_ids) OR c.user.is_planned_report(problem)) ~%] [% item_extra_class = "item-list__item--indented" ~%] [% item_action = BLOCK ~%] [% problem.detail | html %]
    [% END %] [% PROCESS 'report/_item_small.html' %] - [% IF c.user.has_permission_to('report_inspect', problem.bodies_str_ids) %] + [% IF relevant_staff AND c.user.has_permission_to('report_inspect', problem.bodies_str_ids) %] diff --git a/docs/_layouts/pro.html b/docs/_layouts/pro.html index d79163989..085c1ce44 100644 --- a/docs/_layouts/pro.html +++ b/docs/_layouts/pro.html @@ -110,7 +110,7 @@
  • Creating and editing category notices/questions
  • Creating and editing priorities
  • Creating and editing response templates
  • -
  • Viewing statistics
  • +
  • Viewing statistics/heatmap
  • -- cgit v1.2.3 From 8c6e3d87d94a34f741c02483f758e7f9d145b7f2 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 6 May 2020 16:27:28 +0100 Subject: [Docs] Document HTTPS dev server. --- docs/install/manual-install.md | 7 +++++++ docs/install/vagrant.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/docs/install/manual-install.md b/docs/install/manual-install.md index 5ddf8a30f..408b419cb 100644 --- a/docs/install/manual-install.md +++ b/docs/install/manual-install.md @@ -177,6 +177,13 @@ $ script/server The server will be accessible as , and will automatically restart if you update the code or `general.yml`. +If you need to run the server under HTTPS, to e.g. develop the service worker +or geolocation, then you can run `script/server --listen :3000:ssl +--ssl-cert=my.crt --ssl-key=my.key --Reload perllib,conf` where my.crt and +my.key point to a key and self-signed certificate you have generated using +something like `openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout +my.key -out my.crt -subj "/CN=My local CA" -days 3650`. + ### Post-install: Things you might want to change #### Next Steps diff --git a/docs/install/vagrant.md b/docs/install/vagrant.md index 55b7802ac..1c335ab5d 100644 --- a/docs/install/vagrant.md +++ b/docs/install/vagrant.md @@ -69,6 +69,13 @@ The username and password to access the admin (at address `http://fixmystreet.127.0.0.1.xip.io:3000/admin/`) will have been shown at the end of the `vagrant up` output. +If you need to run the server under HTTPS, to e.g. develop the service worker +or geolocation, then you can run `script/server --listen :3000:ssl +--ssl-cert=my.crt --ssl-key=my.key --Reload perllib,conf` where my.crt and +my.key point to a key and self-signed certificate you have generated using +something like `openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout +my.key -out my.crt -subj "/CN=My local CA" -days 3650`. + ## Editing a cobrand If you're working on a cobrand in an external repository, you will want that -- cgit v1.2.3 From 60b0ccde85228080b81387292f6c98c3293a0f57 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 6 May 2020 16:29:22 +0100 Subject: [Docs] Clarify a few different sections of manual. --- docs/_includes/admin-tasks-content.md | 23 ++++++++++++++--------- docs/pro-manual/admin-tasks.md | 6 ++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index 5b570aa27..91ba8f3d8 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -4,9 +4,11 @@ When you're logged in as an administrator the 'admin' button is in the navigation menu -If your Administrator has already given you staff permissions, you’ll see additional admin button -on the top menu when you’re logged in. If not, your Administrator can add these permissions at -any time: see ‘[Creating a Staff Account](#adminstrating-staff-accounts)’. +If your Administrator has already given you staff permissions, you may see +additional admin button on the top menu when you’re logged in. If not, your +access may be only in the frontend (e.g. moderation does not require any +backend admin access), or your Administrator can add these permissions at any +time: see ‘[Creating a Staff Account](#adminstrating-staff-accounts)’.
    @@ -18,14 +20,15 @@ any time: see ‘[Creating a Staff Account](#adminstrating-staff-accounts)’. If you want to view everything that’s been reported within the council area, here’s the quickest way. -Click on ‘all reports’ in the top menu. You’ll see the boundaries of your council area on the map, -and a list of all reports listed, with the most recent reports at the top, on the left hand panel. +Click on ‘all reports’ in the top menu. You’ll see the boundaries of your +council area on the map, and a list of all reports listed, with the most +recently updated reports at the top, in the left hand panel. Click all reports in the top menu to view all reports in an area Note that staff members can be allocated one or more category (e.g. potholes, streetlights, etc) by the Administrator — see ‘[Creating a Staff Account](#adminstrating-staff-accounts)’ for more details. When logged in, these staff -members will see reports within these categories by default. +members will only see reports within those categories by default.
    @@ -538,9 +541,11 @@ mySociety. #### Note -If your FixMyStreet Pro installation has been integrated with a CRM system, this -section does not apply. In such cases, categories are managed from within the CRM, and -FixMyStreet will be set up to work with these. +If your FixMyStreet Pro installation has been integrated with a CRM system, +this section might not fully apply; it is possible categories are managed from +within the CRM, and FixMyStreet will be set up to work with these. Some +installations only integrate on some categories, and some sections such as form +disabling can still be used on top of an integration.
    diff --git a/docs/pro-manual/admin-tasks.md b/docs/pro-manual/admin-tasks.md index d4f48a5eb..7beefab31 100644 --- a/docs/pro-manual/admin-tasks.md +++ b/docs/pro-manual/admin-tasks.md @@ -22,7 +22,5 @@ via a role) for you to be able to perform this task. - If you’re a member of council staff using FixMyStreet Pro, questions should be addressed to your Administrator. -- For technical problems and queries, Administrators may contact the FixMyStreet team on -[bettercities@mysociety.org](mailto:bettercities@mysociety.org). -- Is something not quite right, or missing from this documentation? Let us know on -[bettercities@mysociety.org](mailto:bettercities@mysociety.org). +- For any technical problems and queries, Administrators may contact the + FixMyStreet team by raising a ticket through their helpdesk system. -- cgit v1.2.3 From 8898170c9ac6127329a1f17d855dae8ea7472f86 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 6 May 2020 16:52:30 +0100 Subject: [Docs] Add info about user timeline. --- docs/_includes/admin-tasks-content.md | 21 ++++++--------------- docs/_layouts/pro.html | 3 +-- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index 91ba8f3d8..16329e7e8 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -114,25 +114,16 @@ to access the full report admin editing page.
    -### Searching for a user +### User admin Permissions required: User must be marked as staff; “Edit users' details/search for their reports” must be ticked -Click on ‘admin’ in the top menu bar. - -Enter any part of the user’s name or email address in the ‘Search users’ box. - -
    - -
    - -### Editing a user’s information - -Permissions required: User must be marked as staff; "Edit users' details/search for their reports" must be ticked. - -Click on ‘Edit’ beside any user to view or change their account details. -Or, click the user’s email address to view a list of all reports and updates made by that user. +In the admin, enter any part of the user’s name or email address in the ‘Search +users’ box. You may click on ‘Edit’ beside any user to view or change their +account details, click the user’s email address to view a list of all reports +and updates made by that user, or click ‘Timeline’ to see a timeline of that +user’s activities on the site.
    diff --git a/docs/_layouts/pro.html b/docs/_layouts/pro.html index 085c1ce44..d04446a6e 100644 --- a/docs/_layouts/pro.html +++ b/docs/_layouts/pro.html @@ -95,8 +95,7 @@
  • Filtering and sorting reports
  • Viewing a single report
  • Searching for a report
  • -
  • Searching for a user
  • -
  • Editing a user’s information
  • +
  • User search/edit
  • Viewing reports as a non-staff user
  • Creating reports on behalf of another user
  • Dealing with undesirable content
  • -- cgit v1.2.3 From 97e52222eccc441e4fcb2a444d04eb411c3a1eee Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 6 May 2020 16:52:40 +0100 Subject: [Docs] Add info about two-factor auth. --- docs/_includes/admin-tasks-content.md | 11 +++++++++++ docs/pro-manual/staff-user-accounts.md | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index 16329e7e8..98a2780bc 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -522,6 +522,17 @@ staff user only deals with reports of a specific type. To remove an account, the Administrator-level member of staff should make contact with mySociety. +#### Two-factor authentication + +Any staff user can choose to set up two-factor authentication on their account. +To do this, they will need an app on their phone that can generate time-based +authentication keys, such as Google Authenticator or a password manager. Then. +they can visit their account page on the site and select ‘Activate two-factor +authentication’ to receive a QR code to scan with the app. + +If you wish your organisation to have mandated two-factor authentication, +please get in touch. +
    diff --git a/docs/pro-manual/staff-user-accounts.md b/docs/pro-manual/staff-user-accounts.md index 37997ada2..3efa4f41f 100644 --- a/docs/pro-manual/staff-user-accounts.md +++ b/docs/pro-manual/staff-user-accounts.md @@ -35,6 +35,17 @@ Your password must contain 6 or more characters, and must not be one of the most used combinations (such as ‘Password’ or ‘123456’). Your council may also have password policies which you are expected to abide by. +### Two-factor authentication + +Any staff user can choose to set up two-factor authentication on their account. +To do this, they will need an app on their phone that can generate time-based +authentication keys, such as Google Authenticator or a password manager. Then. +they can visit their account page on the site and select ‘Activate two-factor +authentication’ to receive a QR code to scan with the app. + +If you wish your organisation to have mandated two-factor authentication, +please get in touch. + ### How to log out Click on ‘Your account’ in the top menu, then click ‘Sign out’. -- cgit v1.2.3 From 54c089dce3f02edbafba02161307fbe32f571cce Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 6 May 2020 16:54:31 +0100 Subject: [Docs] Bit on staff-only categories. --- docs/_includes/admin-tasks-content.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index 98a2780bc..b68a09d81 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -579,6 +579,11 @@ for information on creating/editing extra notices and questions for a category. +Categories can be made ‘staff only’, where they are only available to logged-in +members of staff, not members of the public. This could be used for e.g. +contact centre staff to leave reports in categories that you still want to be +phoned in. + You can set up as many or as few categories as you like, and each category can have its own email address attached to it, so for example, reports about potholes may go to one council email address, while reports about road signs go to another. More than one category may share the -- cgit v1.2.3 From b818109a546d48033f4a108d7070d77d1901b7f1 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 6 May 2020 17:05:37 +0100 Subject: [Docs] Tiny bit on web app. --- docs/pro-manual/citizens-experience.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/pro-manual/citizens-experience.md b/docs/pro-manual/citizens-experience.md index f47c2a124..5e7bfd0f7 100644 --- a/docs/pro-manual/citizens-experience.md +++ b/docs/pro-manual/citizens-experience.md @@ -22,13 +22,17 @@ When a citizen sees a problem and wishes to report it, they can do so in one of Make a report on a council website, fixmystreet.com, or via the FixMyStreet app - Through the council website, on the council’s branded FixMyStreet instance -- On FixMyStreet.com, the UK-wide site -- Via the FixMystreet app - +- On [FixMyStreet.com](https://www.fixmystreet.com), the UK-wide site +- Via the FixMyStreet app No matter which of these channels they use, the report will be visible in all three places. All FixMyStreet instances draw from the same database of reports. +The FixMyStreet website, and the council’s branded version, can also be +installed as a ‘web app’ – providing a logo on their homescreen that users can +treat like a native app without the hassle of app stores. If that’s something +that would be of interest for your installation, please get in touch. +

    What if a user makes a report on the council site, but it’s the responsibility of a different authority?

    @@ -101,4 +105,4 @@ and you’ll be offered a range of options: you can subscribe to every report ma council area; every report made within a particular ward; or within an area roughly covering a population of 200,000 people (the size of this area varies with population density). Staff need not normally do this, as they will be working in the reports interface daily, and will be -aware of issues as they arise. \ No newline at end of file +aware of issues as they arise. -- cgit v1.2.3 From bf179ad7dc57b371d45742c3df675bd1bc285a8d Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 13 May 2020 09:45:22 +0100 Subject: [Docs] document per category anonymous reporting Also update the category page screenshot to include the new options --- docs/_includes/admin-tasks-content.md | 4 ++++ .../img/pro-user-guide/edit-category-page.png | Bin 42128 -> 180703 bytes 2 files changed, 4 insertions(+) diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index b68a09d81..ac9e81f9e 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -579,6 +579,10 @@ for information on creating/editing extra notices and questions for a category. +You may also be able to configure categories to allow anonymous +reporting if you have this option available. If this is not available then +contact us to discuss it. + Categories can be made ‘staff only’, where they are only available to logged-in members of staff, not members of the public. This could be used for e.g. contact centre staff to leave reports in categories that you still want to be diff --git a/docs/assets/img/pro-user-guide/edit-category-page.png b/docs/assets/img/pro-user-guide/edit-category-page.png index db84bbb70..f78b00a1c 100755 Binary files a/docs/assets/img/pro-user-guide/edit-category-page.png and b/docs/assets/img/pro-user-guide/edit-category-page.png differ -- cgit v1.2.3 From c2b32091198d3ca3d8dc0da7302f96de9ce0acc9 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Thu, 14 May 2020 16:48:30 +0100 Subject: =?UTF-8?q?[Peterborough]=20Show=20lighting=20layer=20on=20?= =?UTF-8?q?=E2=80=98Lighting=20enquiry=E2=80=99=20category=20in=20all=20gr?= =?UTF-8?q?oups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/cobrands/peterborough/assets.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/cobrands/peterborough/assets.js b/web/cobrands/peterborough/assets.js index 58ec4e5a1..3d718116e 100644 --- a/web/cobrands/peterborough/assets.js +++ b/web/cobrands/peterborough/assets.js @@ -137,8 +137,11 @@ fixmystreet.assets.add(light_defaults, { } }, asset_group: 'Street lighting', + asset_category: 'Lighting enquiry', relevant: function(options) { - return options.group === 'Street lighting' && options.category !== UNKNOWN_LIGHT_CATEGORY_NAME; + return ( options.group === 'Street lighting' && + options.category !== UNKNOWN_LIGHT_CATEGORY_NAME + ) || options.category === "Lighting enquiry"; }, asset_item_message: 'You can pick a street light from the map »', select_action: true, -- cgit v1.2.3 From 66bcc3b0fde04b9c5d8360bd4113e6247538076a Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Tue, 28 Apr 2020 18:42:53 +0100 Subject: [Hart] Update front page suggested report types For https://github.com/mysociety/fixmystreet-commercial/issues/1875 --- templates/web/hart/around/intro.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/web/hart/around/intro.html b/templates/web/hart/around/intro.html index 7cc971041..fc5e2a844 100644 --- a/templates/web/hart/around/intro.html +++ b/templates/web/hart/around/intro.html @@ -1,4 +1,4 @@

    Report something in Hart that needs to be fixed

    -

    (like graffiti, fly tipping, broken paving slabs, or street lighting)

    +

    (like graffiti, fly tipping, broken paving slabs, dog fouling, or street lighting)

    -- cgit v1.2.3 From 4b5c27c8aeb904c3f89dc375d418a1e07d381e38 Mon Sep 17 00:00:00 2001 From: Zarino Zappia Date: Fri, 1 May 2020 12:41:32 +0100 Subject: Simplify #geolocate_link styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Much less opinionated styling for #geolocate_link in core, which means the link should look better, by default, for most cobrands. (In particular, the link looks much better on cobrands with the more modern style of white / off-white #front-main background, such as highwaysengland, which was the cobrand that started off this work to begin with.) I’ve also reduced the specificity of the #geolocate_link rulesets in both core and all the cobrands. While this commit means that fewer cobrands need to override styles on #geolocate_link, there are still 8 cobrands that have to define a custom text colour for their #geolocate_link, because they both: A) have a light coloured background for #front-main, and B) want their #geolocate_link to be coloured like a normal link, rather than inheriting the text colour of the parent element. We might want to revisit this handling of #geolocate_link colouring and the `#front-main a { color: inherit }` rule in _layout.scss at some point in the future. Fixes mysociety/fixmystreet-commercial#1835. --- templates/web/buckinghamshire/front/pre-steps.html | 2 +- web/cobrands/bexley/base.scss | 2 +- web/cobrands/bristol/base.scss | 4 --- web/cobrands/bristol/layout.scss | 8 +++--- web/cobrands/buckinghamshire/base.scss | 25 +++++++---------- web/cobrands/buckinghamshire/layout.scss | 7 +---- web/cobrands/cheshireeast/base.scss | 11 +++----- web/cobrands/cheshireeast/layout.scss | 7 +++-- web/cobrands/eastherts/layout.scss | 8 +++--- web/cobrands/fixamingata/layout.scss | 27 ++++++++---------- web/cobrands/fixmystreet.com/layout.scss | 31 +++++++++++---------- web/cobrands/greenwich/base.scss | 4 +++ web/cobrands/oxfordshire/base.scss | 28 +++++-------------- web/cobrands/oxfordshire/layout.scss | 15 ++++------ web/cobrands/peterborough/base.scss | 12 +------- web/cobrands/peterborough/layout.scss | 15 +++++----- web/cobrands/sass/_base.scss | 25 ++++------------- web/cobrands/sass/_layout.scss | 18 ++++-------- web/cobrands/tfl/base.scss | 17 ++++-------- web/cobrands/tfl/layout.scss | 11 ++++---- web/cobrands/warwickshire/base.scss | 32 ++++++++-------------- web/cobrands/warwickshire/layout.scss | 14 ++++------ web/cobrands/westminster/base.scss | 21 ++++---------- web/cobrands/westminster/layout.scss | 4 +-- 24 files changed, 129 insertions(+), 219 deletions(-) diff --git a/templates/web/buckinghamshire/front/pre-steps.html b/templates/web/buckinghamshire/front/pre-steps.html index 4728bff2f..ee7f83ec9 100644 --- a/templates/web/buckinghamshire/front/pre-steps.html +++ b/templates/web/buckinghamshire/front/pre-steps.html @@ -1,4 +1,4 @@ -

    +

    In light of the ongoing COVID 19 crisis an element of TfB workforce has reduced by the need for self-isolation. This regrettably means that for the immediate future some work will have to be delayed. We apologise for any possible delay diff --git a/web/cobrands/bexley/base.scss b/web/cobrands/bexley/base.scss index 7132f33ab..0f72a2e34 100644 --- a/web/cobrands/bexley/base.scss +++ b/web/cobrands/bexley/base.scss @@ -39,7 +39,7 @@ small { .mobile-map-banner { font-size: 0.89em; } -#front-main a#geolocate_link { +a#geolocate_link { font-size: 0.89em; } #front_stats div { diff --git a/web/cobrands/bristol/base.scss b/web/cobrands/bristol/base.scss index 836b27009..cbd5ee081 100644 --- a/web/cobrands/bristol/base.scss +++ b/web/cobrands/bristol/base.scss @@ -109,10 +109,6 @@ dl dt { } } -a#geolocate_link { - color: $b3; -} - label { @extend %bold-font; } diff --git a/web/cobrands/bristol/layout.scss b/web/cobrands/bristol/layout.scss index 07b39b08b..22fdcf60c 100644 --- a/web/cobrands/bristol/layout.scss +++ b/web/cobrands/bristol/layout.scss @@ -54,10 +54,6 @@ body.frontpage, body.twothirdswidthpage, body.fullwidthpage, body.authpage { } } - a#geolocate_link { - color: $b3; - } - h1 { font-size: 3em; } @@ -99,6 +95,10 @@ body.frontpage, body.twothirdswidthpage, body.fullwidthpage, body.authpage { } } +a#geolocate_link { + color: $b3; // override default `#front-main a` +} + body.mappage { // Add a red border-bottom *inside* the header #site-header { diff --git a/web/cobrands/buckinghamshire/base.scss b/web/cobrands/buckinghamshire/base.scss index 8df2cab7b..aefe76945 100644 --- a/web/cobrands/buckinghamshire/base.scss +++ b/web/cobrands/buckinghamshire/base.scss @@ -102,26 +102,15 @@ dl dt { } #postcodeForm { - margin-top: 1em; + margin: 1em 0 0 0; + padding: 0; background: #fff; + div input#sub { @include bucks-button(); box-shadow: 0; } } - a#geolocate_link { - background: transparent; - color: $bucks_links; - padding: 0; - font-size: 1em; - &:hover, - &:active, - &:focus { - background: transparent; - color: $link-hover-color; - text-decoration: underline; - } - } } .btn-primary, .green-btn, .btn--primary { @@ -137,7 +126,13 @@ dl dt { } a#geolocate_link { - color: $b3; + color: $bucks_links; + + &:hover, + &:active, + &:focus { + color: $link-hover-color; + } } label { diff --git a/web/cobrands/buckinghamshire/layout.scss b/web/cobrands/buckinghamshire/layout.scss index 5df31f9e2..bea2fadc0 100644 --- a/web/cobrands/buckinghamshire/layout.scss +++ b/web/cobrands/buckinghamshire/layout.scss @@ -90,19 +90,14 @@ body.twothirdswidthpage .content .sticky-sidebar aside { background-color: white; text-align: left; padding-top: 40px; + padding-bottom: 0; #postcodeForm { - margin-top: 0; - div { margin: 0; } } - a#geolocate_link { - color: $b3; - } - h1 { font-size: 2.5em; } diff --git a/web/cobrands/cheshireeast/base.scss b/web/cobrands/cheshireeast/base.scss index 8e771f0ad..c0d798c3a 100644 --- a/web/cobrands/cheshireeast/base.scss +++ b/web/cobrands/cheshireeast/base.scss @@ -90,15 +90,12 @@ a, background-color: #ecf3ec; } -#front-main a#geolocate_link { - color: #2e3191; - background: transparent; +a#geolocate_link { border-bottom: 1px solid #a6a7da; - padding: 0; - margin-top: 0.5em; - font-size: inherit; + padding: 0; // remove padding so that border-bottom looks like an underline + margin: 0 0 1em 0; + &:hover { - background: transparent; border-bottom: 1px solid #2e3191; transition: border-color 0.5s; } diff --git a/web/cobrands/cheshireeast/layout.scss b/web/cobrands/cheshireeast/layout.scss index 71861e13f..4505b3a26 100644 --- a/web/cobrands/cheshireeast/layout.scss +++ b/web/cobrands/cheshireeast/layout.scss @@ -29,8 +29,11 @@ body.frontpage .content { margin: 0; width: 30em; } -#front-main a#geolocate_link { - color: #2e3191; + +a#geolocate_link { + color: #2e3191; // override default `#front-main a` + margin-top: 1em; + &:hover { text-decoration: none; } diff --git a/web/cobrands/eastherts/layout.scss b/web/cobrands/eastherts/layout.scss index 964feb5ca..dbb33a3d2 100644 --- a/web/cobrands/eastherts/layout.scss +++ b/web/cobrands/eastherts/layout.scss @@ -116,10 +116,6 @@ } } - a#geolocate_link { - color: $eh_green; - } - h1 { font-size: 2.5em; } @@ -131,6 +127,10 @@ } } +a#geolocate_link { + color: $eh_green; // override default `#front-main a` +} + body.mappage { .eh-footer { display: none; diff --git a/web/cobrands/fixamingata/layout.scss b/web/cobrands/fixamingata/layout.scss index 6cf0dd47e..25e5b260f 100644 --- a/web/cobrands/fixamingata/layout.scss +++ b/web/cobrands/fixamingata/layout.scss @@ -122,26 +122,23 @@ body.mappage { .content footer .tablewrapper { background: #fff; } -#front-main { - a#geolocate_link { +a#geolocate_link { + background: url(images/locate-me.png) $left 0 no-repeat; + height: 34px; + padding-#{$left}: 24px; + margin-top: 0.25em; + @media ($high-dpi-screen) { + background-image: url(images/locate-me@2.png); + background-size: 22px 34px; + } + &:hover { + text-decoration:underline; background: url(images/locate-me.png) $left 0 no-repeat; - height: 34px; - padding-#{$left}: 24px; - margin-top: 0.25em; - font-size: 1em; + @media ($high-dpi-screen) { background-image: url(images/locate-me@2.png); background-size: 22px 34px; } - &:hover { - text-decoration:underline; - background: url(images/locate-me.png) $left 0 no-repeat; - - @media ($high-dpi-screen) { - background-image: url(images/locate-me@2.png); - background-size: 22px 34px; - } - } } } diff --git a/web/cobrands/fixmystreet.com/layout.scss b/web/cobrands/fixmystreet.com/layout.scss index f6d7c9096..e6a862f73 100644 --- a/web/cobrands/fixmystreet.com/layout.scss +++ b/web/cobrands/fixmystreet.com/layout.scss @@ -152,20 +152,23 @@ body.fullwidthpage { } } } - a#geolocate_link { - font-family: $body-font; - background: url(images/locate-me.png) $left 0 no-repeat; - height: 34px; - padding-#{$left}: 24px; - margin-top: 0.25em; - @media ($high-dpi-screen) { - background-image: url(images/locate-me@2.png); - background-size: 22px 34px; - } - } - a#geolocate_link.loading { - background: url("/cobrands/fixmystreet/images/spinner-yellow.gif") 100% 33% no-repeat - } + } +} + +a#geolocate_link { + font-family: $body-font; + background: url(images/locate-me.png) $left 0 no-repeat; + height: 34px; + padding-#{$left}: 24px; + margin-top: 0.25em; + + @media ($high-dpi-screen) { + background-image: url(images/locate-me@2.png); + background-size: 22px 34px; + } + + &.loading { + background: url("/cobrands/fixmystreet/images/spinner-yellow.gif") 100% 33% no-repeat } } diff --git a/web/cobrands/greenwich/base.scss b/web/cobrands/greenwich/base.scss index 0f990c865..bfbb0a62a 100644 --- a/web/cobrands/greenwich/base.scss +++ b/web/cobrands/greenwich/base.scss @@ -28,6 +28,10 @@ background-color: $greenwich_light_grey; } +#front-main #postcodeForm { + margin-top: 1em; +} + label[for=pc] { color: $greenwich_dark_red; } diff --git a/web/cobrands/oxfordshire/base.scss b/web/cobrands/oxfordshire/base.scss index 999321a7e..9fd4bc2ad 100644 --- a/web/cobrands/oxfordshire/base.scss +++ b/web/cobrands/oxfordshire/base.scss @@ -99,29 +99,15 @@ a:not([class]):focus { } } } +} - a#geolocate_link { - padding: 0; - background: transparent; - font-size: inherit; - color: $link-color; - margin-top: 0.5em; - - &:hover { - background-color: transparent; - color: $link-hover-color; - } - - &:focus { - background-color: $color-oxfordshire-bright-yellow; - outline: 2px solid $color-oxfordshire-bright-yellow; - } +a#geolocate_link { + padding: 0; + margin-top: 0.5em; - &.loading { - background: transparent url("/cobrands/fixmystreet/images/spinner-white.gif") 100% 50% no-repeat; - padding: 0 1.5em 0 0; - border: none; - } + &.loading { + background: transparent url("/cobrands/fixmystreet/images/spinner-white.gif") 100% 50% no-repeat; + padding: 0 1.5em 0 0; } } diff --git a/web/cobrands/oxfordshire/layout.scss b/web/cobrands/oxfordshire/layout.scss index 1b415903a..e482b1eaf 100644 --- a/web/cobrands/oxfordshire/layout.scss +++ b/web/cobrands/oxfordshire/layout.scss @@ -130,18 +130,13 @@ $mappage-header-height: 10em; } } } +} - a#geolocate_link { - color: $color-oxfordshire-link-blue; - - &:hover { - color: $color-oxfordshire-bright-yellow; - } +a#geolocate_link { + color: $color-oxfordshire-link-blue; // override default `#front-main a` - &:focus { - background-color: $color-oxfordshire-bright-yellow; - outline: 2px solid $color-oxfordshire-bright-yellow; - } + &:hover { + color: $color-oxfordshire-bright-yellow; // override default `#front-main a` } } diff --git a/web/cobrands/peterborough/base.scss b/web/cobrands/peterborough/base.scss index 67e199cca..32d883132 100644 --- a/web/cobrands/peterborough/base.scss +++ b/web/cobrands/peterborough/base.scss @@ -45,6 +45,7 @@ h1, h2 { } #postcodeForm { background-color: #fff; + padding-bottom: 0; div input#sub { background-color: $link-color; } @@ -53,17 +54,6 @@ h1, h2 { .form-hint { color: $primary_b; } - a#geolocate_link { - background-color: transparent; - padding: 0; - color: $link-color; - &:hover, - &:active, - &:focus { - background-color: transparent; - text-decoration: underline - } - } } .btn-primary, diff --git a/web/cobrands/peterborough/layout.scss b/web/cobrands/peterborough/layout.scss index 62486ab35..cd209f530 100644 --- a/web/cobrands/peterborough/layout.scss +++ b/web/cobrands/peterborough/layout.scss @@ -29,13 +29,14 @@ body.frontpage #front-main { font-weight: normal; font-size: 1.5em; } - a#geolocate_link { - color: $link-color; - &:hover, - &:active, - &:focus { - color: $link-hover-color; - } +} + +a#geolocate_link { + color: $link-color; // override default `#front-main a` + &:hover, + &:active, + &:focus { + color: $link-hover-color; // override default `#front-main a` } } diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss index b52b75f43..29b000fbc 100644 --- a/web/cobrands/sass/_base.scss +++ b/web/cobrands/sass/_base.scss @@ -2373,26 +2373,13 @@ label .muted { } } } - a#geolocate_link { - @include inline-block; - vertical-align:top; - background:#1a1a1a; - color:#C8C8C8; - padding:0.5em; - font-family: $meta-font; - font-size: 0.8125em; - @include border-radius(0 0 0.25em 0.25em); - &:hover { - text-decoration:none; - background:#2a2a2a; - } - } - a#geolocate_link.loading { - background: #1a1a1a url("/cobrands/fixmystreet/images/spinner-black.gif") flip(100%,0) 50% no-repeat; - border-#{$right}: solid 0.5em #1a1a1a; - padding-#{$right}: 1.5em; - } } + +a#geolocate_link { + display: inline-block; + padding: 0.5em; +} + .no-js #geolocate_link { display: none !important; } diff --git a/web/cobrands/sass/_layout.scss b/web/cobrands/sass/_layout.scss index 02ded6fc1..e02da2bcc 100644 --- a/web/cobrands/sass/_layout.scss +++ b/web/cobrands/sass/_layout.scss @@ -860,18 +860,12 @@ textarea.form-error { text-decoration: none; } } - a#geolocate_link { - color: inherit; - background:none; - text-decoration: none; - padding-bottom: 0; - &:hover { - text-decoration:underline; - background:none; - } - } - a#geolocate_link.loading { - border-#{$right}: none; +} + +a#geolocate_link { + text-decoration: none; // override `#front-main a` + &:hover { + text-decoration: underline; } } diff --git a/web/cobrands/tfl/base.scss b/web/cobrands/tfl/base.scss index 9c573f331..43ac90612 100644 --- a/web/cobrands/tfl/base.scss +++ b/web/cobrands/tfl/base.scss @@ -138,18 +138,6 @@ input.form-error, textarea.form-error { } } } - a#geolocate_link { - color: $beck-blue; - font-family: $heading-font; - text-decoration: underline; - font-size: 1.125em; - background: transparent; - &:hover, - &:active, - &:focus { - background: transparent; - } - } h2 { font-style: normal; font-family: $body-font; @@ -158,6 +146,11 @@ input.form-error, textarea.form-error { } } +a#geolocate_link { + font-family: $heading-font; + font-size: 1.125em; +} + .item-list__heading { font-family: $body-font; } diff --git a/web/cobrands/tfl/layout.scss b/web/cobrands/tfl/layout.scss index 8e3c6b5d8..6f63fa06d 100644 --- a/web/cobrands/tfl/layout.scss +++ b/web/cobrands/tfl/layout.scss @@ -66,12 +66,11 @@ h1 { #postcodeForm div { margin: 0; } - a#geolocate_link { - color: $beck-blue; - font-family: $heading-font; - text-decoration: underline; - font-size: 1.125em; - } +} + +a#geolocate_link { + color: $beck-blue; // override default `#front-main a` + text-decoration: underline; } .frontpage .content { diff --git a/web/cobrands/warwickshire/base.scss b/web/cobrands/warwickshire/base.scss index 224346195..53bae8431 100644 --- a/web/cobrands/warwickshire/base.scss +++ b/web/cobrands/warwickshire/base.scss @@ -79,31 +79,21 @@ } } } +} - a#geolocate_link { - padding: 0; - background: transparent; - font-size: 1em; - color: $link-color; - margin-top: 0.5em; - - &:hover { - background-color: transparent; - color: $link-hover-color; - } - - &:focus { - outline: 3px solid $warwickshire-yellow; - } +a#geolocate_link { + padding: 0; + margin-top: 0.5em; - &.loading, - &.loading:hover { - background: transparent url("/cobrands/warwickshire/images/spinner-f6f6f6-333333.gif") 100% 50% no-repeat; - padding: 0 1.5em 0 0; - border: none; - } + &:focus { + outline: 3px solid $warwickshire-yellow; } + &.loading, + &.loading:hover { + background: transparent url("/cobrands/warwickshire/images/spinner-f6f6f6-333333.gif") 100% 50% no-repeat; + padding: 0 1.5em 0 0; + } } .box-warning { diff --git a/web/cobrands/warwickshire/layout.scss b/web/cobrands/warwickshire/layout.scss index ac360f16d..37cd88b4e 100644 --- a/web/cobrands/warwickshire/layout.scss +++ b/web/cobrands/warwickshire/layout.scss @@ -43,17 +43,13 @@ margin: 1.5em 0 0.5em 0; } } +} - a#geolocate_link { - color: $link-color; - - &:hover { - color: $link-hover-color; - } +a#geolocate_link { + color: $link-color; // override default `#front-main a` - &:focus { - outline: 3px solid $warwickshire-yellow; - } + &:hover { + color: $link-hover-color; // override default `#front-main a` } } diff --git a/web/cobrands/westminster/base.scss b/web/cobrands/westminster/base.scss index c67a67f6d..01b7760e8 100644 --- a/web/cobrands/westminster/base.scss +++ b/web/cobrands/westminster/base.scss @@ -82,27 +82,16 @@ body.frontpage { } } - a#geolocate_link { - background: transparent; - display: block; - padding: 0; - margin-top: 0.5em; - font-family: inherit; - font-size: 1em; - border-radius: 0; - color: $westminster_blue; - - &:hover { - background: transparent; - text-decoration: underline; - } - } - .form-hint { color: inherit; } } +a#geolocate_link { + padding: 0; + margin-top: 0.5em; +} + #front-howto h2, #front-recently h2 { font-weight: bold; diff --git a/web/cobrands/westminster/layout.scss b/web/cobrands/westminster/layout.scss index 94483a523..eac7a8432 100644 --- a/web/cobrands/westminster/layout.scss +++ b/web/cobrands/westminster/layout.scss @@ -36,8 +36,8 @@ } } -#front-main a#geolocate_link { - color: $westminster-blue; +a#geolocate_link { + color: $westminster-blue; // override default `#front-main a` } body.mappage { -- cgit v1.2.3 From 5bf3d1b8784a025c3fa19a66f7d85d0fe0202c75 Mon Sep 17 00:00:00 2001 From: Zarino Zappia Date: Wed, 13 May 2020 12:19:06 +0100 Subject: Sass variables for customising links in #front-main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The colour and text decoration of links inside `#front-main` can now be customised via `$primary_link_*` Sass variables. Text decoration is set in _base.scss (like global link text decoration) while colour is handled in _layout.scss (which is where `#front-main` is given its background colour, so likely also the time you’ll want to set a contrasting colour for links inside it). The colour variables are set to `null` by default, meaning that no colour or rules for those links will be compiled, enabling the links to inherit the global link colour style, which previously wasn’t possible. The decoration variables are set to `underline` by default, because most cobrands set their #front-main links to be the same colour as the surrounding text, so adding an underline is a sensible default. You can disable the underlines by setting `$primary_link_decoration: none` in your cobrand’s _colours.scss file, as cheshireeast and oxfordshire do. Fixes #3007. --- CHANGELOG.md | 3 +++ web/cobrands/bathnes/_colours.scss | 6 ++++-- web/cobrands/bristol/layout.scss | 4 ---- web/cobrands/bromley/_colours.scss | 2 ++ web/cobrands/cheshireeast/_colours.scss | 1 + web/cobrands/cheshireeast/layout.scss | 5 ----- web/cobrands/eastherts/layout.scss | 4 ---- web/cobrands/fiksgatami/_colours.scss | 2 ++ web/cobrands/fixamingata/_colours.scss | 2 ++ web/cobrands/fixmystreet.com/_colours.scss | 2 ++ web/cobrands/hart/_colours.scss | 2 ++ web/cobrands/hounslow/_colours.scss | 2 ++ web/cobrands/isleofwight/_colours.scss | 4 +++- web/cobrands/oxfordshire/_colours.scss | 1 + web/cobrands/oxfordshire/layout.scss | 8 -------- web/cobrands/peterborough/layout.scss | 9 --------- web/cobrands/rutland/_colours.scss | 2 ++ web/cobrands/sass/_base.scss | 9 +++++++++ web/cobrands/sass/_layout.scss | 15 +++++---------- web/cobrands/tfl/layout.scss | 5 ----- web/cobrands/warwickshire/layout.scss | 8 -------- web/cobrands/westminster/layout.scss | 4 ---- 22 files changed, 40 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c576808d0..19429b6cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 + - Development improvements: + - `#geolocate_link` is now easier to re-style. #3006 + - Links inside `#front-main` can be customised using `$primary_link_*` Sass variables. #3007 * v3.0.1 (6th May 2020) - New features: diff --git a/web/cobrands/bathnes/_colours.scss b/web/cobrands/bathnes/_colours.scss index 0340b3eda..c9503ae04 100644 --- a/web/cobrands/bathnes/_colours.scss +++ b/web/cobrands/bathnes/_colours.scss @@ -18,9 +18,11 @@ $site-width: 60em; @import "pattern-lib/colours"; -$primary: #00728F; +$primary: $bathnes-primary; $primary_b: #0b0b0c; -$primary_text: #0b0c0c; +$primary_text: #fff; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $base_bg: white; $base_fg: #0b0c0c; diff --git a/web/cobrands/bristol/layout.scss b/web/cobrands/bristol/layout.scss index 22fdcf60c..c2ba57afe 100644 --- a/web/cobrands/bristol/layout.scss +++ b/web/cobrands/bristol/layout.scss @@ -95,10 +95,6 @@ body.frontpage, body.twothirdswidthpage, body.fullwidthpage, body.authpage { } } -a#geolocate_link { - color: $b3; // override default `#front-main a` -} - body.mappage { // Add a red border-bottom *inside* the header #site-header { diff --git a/web/cobrands/bromley/_colours.scss b/web/cobrands/bromley/_colours.scss index 9528f5403..53f6bc2c6 100644 --- a/web/cobrands/bromley/_colours.scss +++ b/web/cobrands/bromley/_colours.scss @@ -9,6 +9,8 @@ $bromley_dark_green: #505050; $primary: $bromley_blue; $primary_b: #ffffff; $primary_text: #ffffff; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $link-color: $bromley_green; $link-hover-color: $bromley_green; diff --git a/web/cobrands/cheshireeast/_colours.scss b/web/cobrands/cheshireeast/_colours.scss index 363214bd0..e0bc13b08 100644 --- a/web/cobrands/cheshireeast/_colours.scss +++ b/web/cobrands/cheshireeast/_colours.scss @@ -27,6 +27,7 @@ $col_button_hover: $green; $primary: $white; $primary_b: $green; $primary_text: $text_black; +$primary_link_decoration: none; $base_bg: $white; $base_fg: $text_black; diff --git a/web/cobrands/cheshireeast/layout.scss b/web/cobrands/cheshireeast/layout.scss index 4505b3a26..ad5c6580b 100644 --- a/web/cobrands/cheshireeast/layout.scss +++ b/web/cobrands/cheshireeast/layout.scss @@ -31,12 +31,7 @@ body.frontpage .content { } a#geolocate_link { - color: #2e3191; // override default `#front-main a` margin-top: 1em; - - &:hover { - text-decoration: none; - } } .ce-footer { diff --git a/web/cobrands/eastherts/layout.scss b/web/cobrands/eastherts/layout.scss index dbb33a3d2..d5fb3fc0d 100644 --- a/web/cobrands/eastherts/layout.scss +++ b/web/cobrands/eastherts/layout.scss @@ -127,10 +127,6 @@ } } -a#geolocate_link { - color: $eh_green; // override default `#front-main a` -} - body.mappage { .eh-footer { display: none; diff --git a/web/cobrands/fiksgatami/_colours.scss b/web/cobrands/fiksgatami/_colours.scss index 907065d36..cd5ed0c47 100644 --- a/web/cobrands/fiksgatami/_colours.scss +++ b/web/cobrands/fiksgatami/_colours.scss @@ -7,6 +7,8 @@ $bg: #1a4f7f; $primary: #99bfe1; $primary_b: #000000; $primary_text: #222222; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $base_bg: $bg; $base_fg: #fff; diff --git a/web/cobrands/fixamingata/_colours.scss b/web/cobrands/fixamingata/_colours.scss index dbdd92e5b..c718a102b 100644 --- a/web/cobrands/fixamingata/_colours.scss +++ b/web/cobrands/fixamingata/_colours.scss @@ -5,6 +5,8 @@ $menu-image: 'menu-black'; $primary: #00b1da; $primary_b: #0087a6; $primary_text: #222; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $base_bg: #eee url(images/tile.jpg) 0 0 repeat; $base_fg: $primary_text; diff --git a/web/cobrands/fixmystreet.com/_colours.scss b/web/cobrands/fixmystreet.com/_colours.scss index d28cf11e2..b0de8eacb 100644 --- a/web/cobrands/fixmystreet.com/_colours.scss +++ b/web/cobrands/fixmystreet.com/_colours.scss @@ -3,6 +3,8 @@ $primary: #FFD000; $primary_b: #F3B11E; // For the box around the front page postcode form only $primary_text: #222; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); // Tiled main body background $base_bg: #272727 url(images/tile.jpg) 0 0 repeat; diff --git a/web/cobrands/hart/_colours.scss b/web/cobrands/hart/_colours.scss index d79126bae..321b893ad 100644 --- a/web/cobrands/hart/_colours.scss +++ b/web/cobrands/hart/_colours.scss @@ -8,6 +8,8 @@ $col_fixed_label: $hart_primary; $primary_b: #000000; $primary_text: #ffffff; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $link-color: #369; $link-hover-color: #369; diff --git a/web/cobrands/hounslow/_colours.scss b/web/cobrands/hounslow/_colours.scss index 0a4476720..622055e82 100644 --- a/web/cobrands/hounslow/_colours.scss +++ b/web/cobrands/hounslow/_colours.scss @@ -21,6 +21,8 @@ $primary: $purple; //$primary: #dce6f2; // From bexley.gov.uk/services $primary_b: #222; $primary_text: $white; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $base_bg: $white; $base_fg: #222; diff --git a/web/cobrands/isleofwight/_colours.scss b/web/cobrands/isleofwight/_colours.scss index 688eb66e0..511fcef02 100644 --- a/web/cobrands/isleofwight/_colours.scss +++ b/web/cobrands/isleofwight/_colours.scss @@ -11,9 +11,11 @@ $green: #75c044; //Any 20% tint of the above -$primary_text: #fff; $primary: $cyan; $primary_b: #222; +$primary_text: #fff; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $base_bg: #fff; $base_fg: #222; diff --git a/web/cobrands/oxfordshire/_colours.scss b/web/cobrands/oxfordshire/_colours.scss index 7ef941660..d52571721 100644 --- a/web/cobrands/oxfordshire/_colours.scss +++ b/web/cobrands/oxfordshire/_colours.scss @@ -14,6 +14,7 @@ $color-oxfordshire-link-blue: #0a549d; $primary: $color-oxfordshire-bright-green; $primary_b: $color-oxfordshire-dark-green; $primary_text: #fff; +$primary_link_decoration: none; $link-color: $color-oxfordshire-link-blue; $link-hover-color: $color-oxfordshire-bright-yellow; diff --git a/web/cobrands/oxfordshire/layout.scss b/web/cobrands/oxfordshire/layout.scss index e482b1eaf..91ea8c4d1 100644 --- a/web/cobrands/oxfordshire/layout.scss +++ b/web/cobrands/oxfordshire/layout.scss @@ -132,14 +132,6 @@ $mappage-header-height: 10em; } } -a#geolocate_link { - color: $color-oxfordshire-link-blue; // override default `#front-main a` - - &:hover { - color: $color-oxfordshire-bright-yellow; // override default `#front-main a` - } -} - .frontpage { .content h2 { font-size: 2em; diff --git a/web/cobrands/peterborough/layout.scss b/web/cobrands/peterborough/layout.scss index cd209f530..de5c5f9a9 100644 --- a/web/cobrands/peterborough/layout.scss +++ b/web/cobrands/peterborough/layout.scss @@ -31,15 +31,6 @@ body.frontpage #front-main { } } -a#geolocate_link { - color: $link-color; // override default `#front-main a` - &:hover, - &:active, - &:focus { - color: $link-hover-color; // override default `#front-main a` - } -} - #postcodeForm .form-hint { color: $grey; } diff --git a/web/cobrands/rutland/_colours.scss b/web/cobrands/rutland/_colours.scss index aabedb0f1..e81ce2554 100755 --- a/web/cobrands/rutland/_colours.scss +++ b/web/cobrands/rutland/_colours.scss @@ -14,6 +14,8 @@ $RCCbg: #F1F1F1; $primary: $RCCGreen; $primary_b: #000000; $primary_text: #222222; +$primary_link_color: $primary_text; +$primary_link_hover_color: rgba($primary_text, 0.8); $base_bg: $RCCbg; $base_fg: #000; diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss index 29b000fbc..f86af2386 100644 --- a/web/cobrands/sass/_base.scss +++ b/web/cobrands/sass/_base.scss @@ -11,6 +11,9 @@ $link-visited-color: $link-color !default; $link-text-decoration: none !default; $link-hover-text-decoration: underline !default; +$primary_link_decoration: underline !default; +$primary_link_hover_decoration: $primary_link_decoration !default; + $itemlist_item_background: #f6f6f6 !default; $itemlist_item_background_hover: #e6e6e6 !default; $col_big_numbers: #666 !default; @@ -2373,6 +2376,12 @@ label .muted { } } } + a { + text-decoration: $primary_link_decoration; + &:hover { + text-decoration: $primary_link_hover_decoration; + } + } } a#geolocate_link { diff --git a/web/cobrands/sass/_layout.scss b/web/cobrands/sass/_layout.scss index e02da2bcc..8b3406d9d 100644 --- a/web/cobrands/sass/_layout.scss +++ b/web/cobrands/sass/_layout.scss @@ -13,6 +13,9 @@ $header-top-border: $header-top-border-width solid $primary !default; $container-max-width: 60em !default; +$primary_link_color: null !default; +$primary_link_hover_color: null !default; + .internal-link-fixed-header { display: block; position: relative; @@ -854,21 +857,13 @@ textarea.form-error { } } a { - color: inherit; - text-decoration: underline; + color: $primary_link_color; &:hover { - text-decoration: none; + color: $primary_link_hover_color; } } } -a#geolocate_link { - text-decoration: none; // override `#front-main a` - &:hover { - text-decoration: underline; - } -} - #front-howto { border-#{$right}: 1em solid transparent; } diff --git a/web/cobrands/tfl/layout.scss b/web/cobrands/tfl/layout.scss index 6f63fa06d..751a71672 100644 --- a/web/cobrands/tfl/layout.scss +++ b/web/cobrands/tfl/layout.scss @@ -68,11 +68,6 @@ h1 { } } -a#geolocate_link { - color: $beck-blue; // override default `#front-main a` - text-decoration: underline; -} - .frontpage .content { padding: 2em 0; } diff --git a/web/cobrands/warwickshire/layout.scss b/web/cobrands/warwickshire/layout.scss index 37cd88b4e..fdf3524cd 100644 --- a/web/cobrands/warwickshire/layout.scss +++ b/web/cobrands/warwickshire/layout.scss @@ -45,14 +45,6 @@ } } -a#geolocate_link { - color: $link-color; // override default `#front-main a` - - &:hover { - color: $link-hover-color; // override default `#front-main a` - } -} - .site-footer__section { @include box-sizing(border-box); float: left; diff --git a/web/cobrands/westminster/layout.scss b/web/cobrands/westminster/layout.scss index eac7a8432..6e5c30ced 100644 --- a/web/cobrands/westminster/layout.scss +++ b/web/cobrands/westminster/layout.scss @@ -36,10 +36,6 @@ } } -a#geolocate_link { - color: $westminster-blue; // override default `#front-main a` -} - body.mappage { #site-header { box-sizing: border-box; // count padding as part of height, so border-bottom is visible -- cgit v1.2.3 From 151197042f368b9c7f3b0267e0fc9bcb1fee04d3 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Thu, 14 May 2020 12:09:51 +0100 Subject: reset asset select message by id when asset unselected Resetting using the class could cause an incorrect "you can pick an $item from the map" message to be display when asset_groups were being used. This was happening when the unselect event was firing after the category change event and then updating the new message using the message from the unselected layer. Fixes mysociety/fixmystreet-commercial#1888 --- .cypress/cypress/integration/peterborough.js | 7 +++++++ bin/fixmystreet.com/fixture | 20 +++++++++++++++++++- web/cobrands/fixmystreet/assets.js | 14 +++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/.cypress/cypress/integration/peterborough.js b/.cypress/cypress/integration/peterborough.js index 67c468be8..be1515028 100644 --- a/.cypress/cypress/integration/peterborough.js +++ b/.cypress/cypress/integration/peterborough.js @@ -31,4 +31,11 @@ describe('new report form', function() { cy.get('.js-hide-if-invalid-category').should('be.visible'); }); + it('correctly changes the asset select message', function() { + cy.get('select:eq(4)').select('Street lighting'); + cy.get('.category_meta_message').should('contain', 'You can pick a street light from the map'); + cy.get('select:eq(4)').select('Trees'); + cy.get('.category_meta_message').should('contain', 'You can pick a tree from the map'); + }); + }); diff --git a/bin/fixmystreet.com/fixture b/bin/fixmystreet.com/fixture index 1062eb16b..082fc6309 100755 --- a/bin/fixmystreet.com/fixture +++ b/bin/fixmystreet.com/fixture @@ -100,7 +100,7 @@ if ($opt->test_fixtures) { { 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' }, - { area_id => 2566, categories => [ 'Fallen branch' ], name => 'Peterborough City Council' }, + { area_id => 2566, categories => [ 'Fallen branch', 'Light Out', 'Light Dim', 'Fallen Tree', 'Damaged Tree' ], name => 'Peterborough City Council' }, { area_id => 2498, categories => [ 'Incorrect timetable', 'Glass broken', 'Mobile Crane Operation' ], name => 'TfL' }, ) { $bodies->{$_->{area_id}} = FixMyStreet::DB::Factory::Body->find_or_create($_); @@ -108,6 +108,24 @@ if ($opt->test_fixtures) { say "Created body $_->{name} for MapIt area ID $_->{area_id}, categories $cats"; } + for my $cat_name ('Fallen Tree', 'Damaged Tree') { + my $cat = FixMyStreet::DB->resultset('Contact')->find({ + body => $bodies->{2566}, + category => $cat_name, + }); + $cat->set_extra_metadata( group => 'Trees' ); + $cat->update; + } + + for my $cat_name ('Light Out', 'Light Dim') { + my $cat = FixMyStreet::DB->resultset('Contact')->find({ + body => $bodies->{2566}, + category => $cat_name, + }); + $cat->set_extra_metadata( group => 'Street lighting' ); + $cat->update; + } + my $child_cat = FixMyStreet::DB->resultset("Contact")->find({ body => $bodies->{2234}, category => 'Very Urgent', diff --git a/web/cobrands/fixmystreet/assets.js b/web/cobrands/fixmystreet/assets.js index 88a098398..e816fb4e5 100644 --- a/web/cobrands/fixmystreet/assets.js +++ b/web/cobrands/fixmystreet/assets.js @@ -833,7 +833,19 @@ fixmystreet.assets = { named_select_action_not_found: function() { var message = this.fixmystreet.asset_item_message; message = message.replace('ITEM', this.fixmystreet.asset_item); - $('.category_meta_message').html(message); + if (this.fixmystreet.asset_group) { + var prefix = this.fixmystreet.asset_group.replace(/[^a-z]/gi, ''); + var id = "category_meta_message_" + prefix; + var $p = $('#' + id); + $p.html(message); + } else { + $.each(this.fixmystreet.asset_category, function(i, c) { + var prefix = c.replace(/[^a-z]/gi, ''), + id = "category_meta_message_" + prefix, + $p = $('#' + id); + $p.html(message); + }); + } }, selectedFeature: function() { -- cgit v1.2.3 From 1b988948aee3f4decff3252669c672149ebb1aef Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 18 May 2020 12:44:19 +0100 Subject: Add --coverage option to browser-tests. You need to install the @cypress/code-coverage package alongside cypress. --- .cypress/cypress/plugins/index.js | 6 +++++ .cypress/cypress/support/index.js | 2 ++ .gitignore | 4 +++ CHANGELOG.md | 1 + bin/browser-tests | 44 +++++++++++++++++++++++++++++-- web/vendor/accessible-autocomplete.min.js | 1 - web/vendor/html5sortable.min.js | 1 - 7 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 .cypress/cypress/plugins/index.js create mode 100644 .cypress/cypress/support/index.js diff --git a/.cypress/cypress/plugins/index.js b/.cypress/cypress/plugins/index.js new file mode 100644 index 000000000..218e6bfde --- /dev/null +++ b/.cypress/cypress/plugins/index.js @@ -0,0 +1,6 @@ +// jshint esversion: 6 + +module.exports = (on, config) => { + require('@cypress/code-coverage/task')(on, config); + return config; +}; diff --git a/.cypress/cypress/support/index.js b/.cypress/cypress/support/index.js new file mode 100644 index 000000000..227dd57f0 --- /dev/null +++ b/.cypress/cypress/support/index.js @@ -0,0 +1,2 @@ +// jshint esversion: 6 +import '@cypress/code-coverage/support'; diff --git a/.gitignore b/.gitignore index 7611c256a..a3a1a1b3f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,10 @@ _Inline/ *.pyc docker-compose.override.yml +# JS coverage +/.nyc_output +/coverage + # International /fixmystreet-international *[Mm]ake[Mm]y[Ii]sland* diff --git a/CHANGELOG.md b/CHANGELOG.md index 19429b6cb..ba34d428c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Development improvements: - `#geolocate_link` is now easier to re-style. #3006 - Links inside `#front-main` can be customised using `$primary_link_*` Sass variables. #3007 + - Add option to show front end testing code coverage. #3036 * v3.0.1 (6th May 2020) - New features: diff --git a/bin/browser-tests b/bin/browser-tests index c7e5df27c..32b844127 100755 --- a/bin/browser-tests +++ b/bin/browser-tests @@ -7,7 +7,7 @@ use lib '.'; # For the mock MapIt module use Getopt::Long ':config' => qw(pass_through auto_help); my ($run_server, $run_cypress, $vagrant, $wsl, $node, $config_file); -my ($cobrand, $coords, $area_id, $name, $mapit_url); +my ($cobrand, $coords, $area_id, $name, $mapit_url, $coverage); BEGIN { $config_file = 'conf/general.yml-example'; @@ -19,6 +19,7 @@ BEGIN { $node = 'C:\Program Files\nodejs\node.exe'; GetOptions( + 'coverage' => \$coverage, 'config=s' => \$config_file, 'server' => \$run_server, 'cypress' => \$run_cypress, @@ -38,6 +39,11 @@ BEGIN { exit 1; } + if ($coverage && (system('git', 'diff', '--quiet', 'web') >> 8)) { + print 'Do not run coverage with changes in web, they will be lost'; + exit 1; + } + if (!$run_server && !$run_cypress) { # If asked for neither, run both $run_server = $run_cypress = 1; @@ -75,6 +81,35 @@ if ($vagrant) { exit; } +BEGIN { + # setenv.pl above unloads File:: modules but we need them + use File::Path qw(remove_tree); +} + +sub coverage_setup { + # Add instrumentation to all JS files under web/ + if (system('nyc', 'instrument', '--exclude', 'vendor', '--compact', 'false', 'web', 'webO') >> 8) { + print 'Could not instrument JS files - are @cypress/code-coverage and nyc installed?'; + exit 1; + } + + # Move the instrumented files on top of the originals + while (glob("webO/js/*.js webO/cobrands/*/*.js")) { + (my $new = $_) =~ s/webO/web/; + rename $_, $new; + } + + remove_tree('webO', { safe => 1 }); # Remove anything else left +} + +sub coverage_teardown { + remove_tree('.nyc_output', '.cypress/coverage', { safe => 1 }); # Remove old data and incorrect report + rename '.cypress/.nyc_output', './.nyc_output'; # Move to top level so nyc can find JS files + system('git', 'checkout', 'web'); # Remove instrumented JS files + system('nyc', 'report', '--reporter=lcov'); # Generate correct report + print "The JS coverage report is at coverage/lcov-report/index.html\n"; +} + sub run { my $cmd = shift @ARGV; die "Must specify a cypress command\n" unless $cmd || !$run_cypress; @@ -113,8 +148,12 @@ sub run { if ($wsl) { @cypress = ('cmd.exe', '/c', $node, $wsl); } - my $exit = system(@cypress, $cmd, '--config', 'pluginsFile=false,supportFile=false', '--project', '.cypress', @ARGV); + my @config = $coverage ? () : ('--config', 'pluginsFile=false,supportFile=false'); + my $exit = system(@cypress, $cmd, @config, '--project', '.cypress', @ARGV); kill 'TERM', $pid if $pid; + + coverage_teardown() if $coverage; + exit $exit >> 8; } else { # Child, run the server on port 3001 @@ -128,6 +167,7 @@ sub run { } } +coverage_setup() if $coverage; run(); diff --git a/web/vendor/accessible-autocomplete.min.js b/web/vendor/accessible-autocomplete.min.js index 9eafe639f..d0780cb77 100644 --- a/web/vendor/accessible-autocomplete.min.js +++ b/web/vendor/accessible-autocomplete.min.js @@ -1,2 +1 @@ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.accessibleAutocomplete=t():e.accessibleAutocomplete=t()}(this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/",t(t.s=1)}([function(e,t,n){!function(){"use strict";function t(){}function n(e,n){var o,r,l,i,u=T;for(i=arguments.length;i-- >2;)D.push(arguments[i]);for(n&&null!=n.children&&(D.length||D.push(n.children),delete n.children);D.length;)if((r=D.pop())&&void 0!==r.pop)for(i=r.length;i--;)D.push(r[i]);else!0!==r&&!1!==r||(r=null),(l="function"!=typeof e)&&(null==r?r="":"number"==typeof r?r=String(r):"string"!=typeof r&&(l=!1)),l&&o?u[u.length-1]+=r:u===T?u=[r]:u.push(r),o=l;var s=new t;return s.nodeName=e,s.children=u,s.attributes=null==n?void 0:n,s.key=null==n?void 0:n.key,void 0!==A.vnode&&A.vnode(s),s}function o(e,t){for(var n in t)e[n]=t[n];return e}function r(e,t){return n(e.nodeName,o(o({},e.attributes),t),arguments.length>2?[].slice.call(arguments,2):e.children)}function l(e){!e.__d&&(e.__d=!0)&&1==V.push(e)&&(A.debounceRendering||setTimeout)(i)}function i(){var e,t=V;for(V=[];e=t.pop();)e.__d&&N(e)}function u(e,t,n){return"string"==typeof t||"number"==typeof t?void 0!==e.splitText:"string"==typeof t.nodeName?!e._componentConstructor&&s(e,t.nodeName):n||e._componentConstructor===t.nodeName}function s(e,t){return e.__n===t||e.nodeName.toLowerCase()===t.toLowerCase()}function a(e){var t=o({},e.attributes);t.children=e.children;var n=e.nodeName.defaultProps;if(void 0!==n)for(var r in n)void 0===t[r]&&(t[r]=n[r]);return t}function p(e,t){var n=t?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e);return n.__n=e,n}function c(e){e.parentNode&&e.parentNode.removeChild(e)}function d(e,t,n,o,r){if("className"===t&&(t="class"),"key"===t);else if("ref"===t)n&&n(null),o&&o(e);else if("class"!==t||r)if("style"===t){if(o&&"string"!=typeof o&&"string"!=typeof n||(e.style.cssText=o||""),o&&"object"==typeof o){if("string"!=typeof n)for(var l in n)l in o||(e.style[l]="");for(var l in o)e.style[l]="number"==typeof o[l]&&!1===L.test(l)?o[l]+"px":o[l]}}else if("dangerouslySetInnerHTML"===t)o&&(e.innerHTML=o.__html||"");else if("o"==t[0]&&"n"==t[1]){var i=t!==(t=t.replace(/Capture$/,""));t=t.toLowerCase().substring(2),o?n||e.addEventListener(t,h,i):e.removeEventListener(t,h,i),(e.__l||(e.__l={}))[t]=o}else if("list"!==t&&"type"!==t&&!r&&t in e)f(e,t,null==o?"":o),null!=o&&!1!==o||e.removeAttribute(t);else{var u=r&&t!==(t=t.replace(/^xlink\:?/,""));null==o||!1===o?u?e.removeAttributeNS("http://www.w3.org/1999/xlink",t.toLowerCase()):e.removeAttribute(t):"function"!=typeof o&&(u?e.setAttributeNS("http://www.w3.org/1999/xlink",t.toLowerCase(),o):e.setAttribute(t,o))}else e.className=o||""}function f(e,t,n){try{e[t]=n}catch(e){}}function h(e){return this.__l[e.type](A.event&&A.event(e)||e)}function m(){for(var e;e=j.pop();)A.afterMount&&A.afterMount(e),e.componentDidMount&&e.componentDidMount()}function _(e,t,n,o,r,l){P++||(q=null!=r&&void 0!==r.ownerSVGElement,B=null!=e&&!("__preactattr_"in e));var i=v(e,t,n,o,l);return r&&i.parentNode!==r&&r.appendChild(i),--P||(B=!1,l||m()),i}function v(e,t,n,o,r){var l=e,i=q;if(null==t&&(t=""),"string"==typeof t)return e&&void 0!==e.splitText&&e.parentNode&&(!e._component||r)?e.nodeValue!=t&&(e.nodeValue=t):(l=document.createTextNode(t),e&&(e.parentNode&&e.parentNode.replaceChild(l,e),b(e,!0))),l.__preactattr_=!0,l;if("function"==typeof t.nodeName)return I(e,t,n,o);if(q="svg"===t.nodeName||"foreignObject"!==t.nodeName&&q,(!e||!s(e,String(t.nodeName)))&&(l=p(String(t.nodeName),q),e)){for(;e.firstChild;)l.appendChild(e.firstChild);e.parentNode&&e.parentNode.replaceChild(l,e),b(e,!0)}var u=l.firstChild,a=l.__preactattr_||(l.__preactattr_={}),c=t.children;return!B&&c&&1===c.length&&"string"==typeof c[0]&&null!=u&&void 0!==u.splitText&&null==u.nextSibling?u.nodeValue!=c[0]&&(u.nodeValue=c[0]):(c&&c.length||null!=u)&&y(l,c,n,o,B||null!=a.dangerouslySetInnerHTML),O(l,t.attributes,a),q=i,l}function y(e,t,n,o,r){var l,i,s,a,p=e.childNodes,d=[],f={},h=0,m=0,_=p.length,y=0,g=t?t.length:0;if(0!==_)for(var O=0;O<_;O++){var w=p[O],C=w.__preactattr_,E=g&&C?w._component?w._component.__k:C.key:null;null!=E?(h++,f[E]=w):(C||(void 0!==w.splitText?!r||w.nodeValue.trim():r))&&(d[y++]=w)}if(0!==g)for(var O=0;O=_?e.appendChild(a):a!==p[O]&&(a===p[O+1]?c(p[O]):e.insertBefore(a,p[O]||null)))}if(h)for(var O in f)void 0!==f[O]&&b(f[O],!1);for(;m<=y;)void 0!==(a=d[y--])&&b(a,!1)}function b(e,t){var n=e._component;n?k(n):(null!=e.__preactattr_&&e.__preactattr_.ref&&e.__preactattr_.ref(null),!1!==t&&null!=e.__preactattr_||c(e),g(e))}function g(e){for(e=e.lastChild;e;){var t=e.previousSibling;b(e,!0),e=t}}function O(e,t,n){var o;for(o in n)t&&null!=t[o]||null==n[o]||d(e,o,n[o],n[o]=void 0,q);for(o in t)"children"===o||"innerHTML"===o||o in n&&t[o]===("value"===o||"checked"===o?e[o]:n[o])||d(e,o,n[o],n[o]=t[o],q)}function w(e){var t=e.constructor.name;(U[t]||(U[t]=[])).push(e)}function C(e,t,n){var o,r=U[e.name];if(e.prototype&&e.prototype.render?(o=new e(t,n),M.call(o,t,n)):(o=new M(t,n),o.constructor=e,o.render=E),r)for(var l=r.length;l--;)if(r[l].constructor===e){o.__b=r[l].__b,r.splice(l,1);break}return o}function E(e,t,n){return this.constructor(e,n)}function x(e,t,n,o,r){e.__x||(e.__x=!0,(e.__r=t.ref)&&delete t.ref,(e.__k=t.key)&&delete t.key,!e.base||r?e.componentWillMount&&e.componentWillMount():e.componentWillReceiveProps&&e.componentWillReceiveProps(t,o),o&&o!==e.context&&(e.__c||(e.__c=e.context),e.context=o),e.__p||(e.__p=e.props),e.props=t,e.__x=!1,0!==n&&(1!==n&&!1===A.syncComponentUpdates&&e.base?l(e):N(e,1,r)),e.__r&&e.__r(e))}function N(e,t,n,r){if(!e.__x){var l,i,u,s=e.props,p=e.state,c=e.context,d=e.__p||s,f=e.__s||p,h=e.__c||c,v=e.base,y=e.__b,g=v||y,O=e._component,w=!1;if(v&&(e.props=d,e.state=f,e.context=h,2!==t&&e.shouldComponentUpdate&&!1===e.shouldComponentUpdate(s,p,c)?w=!0:e.componentWillUpdate&&e.componentWillUpdate(s,p,c),e.props=s,e.state=p,e.context=c),e.__p=e.__s=e.__c=e.__b=null,e.__d=!1,!w){l=e.render(s,p,c),e.getChildContext&&(c=o(o({},c),e.getChildContext()));var E,I,M=l&&l.nodeName;if("function"==typeof M){var S=a(l);i=O,i&&i.constructor===M&&S.key==i.__k?x(i,S,1,c,!1):(E=i,e._component=i=C(M,S,c),i.__b=i.__b||y,i.__u=e,x(i,S,0,c,!1),N(i,1,n,!0)),I=i.base}else u=g,E=O,E&&(u=e._component=null),(g||1===t)&&(u&&(u._component=null),I=_(u,l,c,n||!v,g&&g.parentNode,!0));if(g&&I!==g&&i!==O){var D=g.parentNode;D&&I!==D&&(D.replaceChild(I,g),E||(g._component=null,b(g,!1)))}if(E&&k(E),e.base=I,I&&!r){for(var T=e,L=e;L=L.__u;)(T=L).base=I;I._component=T,I._componentConstructor=T.constructor}}if(!v||n?j.unshift(e):w||(m(),e.componentDidUpdate&&e.componentDidUpdate(d,f,h),A.afterUpdate&&A.afterUpdate(e)),null!=e.__h)for(;e.__h.length;)e.__h.pop().call(e);P||r||m()}}function I(e,t,n,o){for(var r=e&&e._component,l=r,i=e,u=r&&e._componentConstructor===t.nodeName,s=u,p=a(t);r&&!s&&(r=r.__u);)s=r.constructor===t.nodeName;return r&&s&&(!o||r._component)?(x(r,p,3,n,o),e=r.base):(l&&!u&&(k(l),e=i=null),r=C(t.nodeName,p,n),e&&!r.__b&&(r.__b=e,i=null),x(r,p,1,n,o),e=r.base,i&&e!==i&&(i._component=null,b(i,!1))),e}function k(e){A.beforeUnmount&&A.beforeUnmount(e);var t=e.base;e.__x=!0,e.componentWillUnmount&&e.componentWillUnmount(),e.base=null;var n=e._component;n?k(n):t&&(t.__preactattr_&&t.__preactattr_.ref&&t.__preactattr_.ref(null),e.__b=t,c(t),w(e),g(t)),e.__r&&e.__r(null)}function M(e,t){this.__d=!0,this.context=t,this.props=e,this.state=this.state||{}}function S(e,t,n){return _(n,e,{},!1,t,!1)}var A={},D=[],T=[],L=/acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i,V=[],j=[],P=0,q=!1,B=!1,U={};o(M.prototype,{setState:function(e,t){var n=this.state;this.__s||(this.__s=o({},n)),o(n,"function"==typeof e?e(n,this.props):e),t&&(this.__h=this.__h||[]).push(t),l(this)},forceUpdate:function(e){e&&(this.__h=this.__h||[]).push(e),N(this,2)},render:function(){}});var R={h:n,createElement:n,cloneElement:r,Component:M,render:S,rerender:i,options:A};e.exports=R}()},function(e,t,n){e.exports=n(2)},function(e,t,n){"use strict";function o(e){if(!e.element)throw new Error("element is not defined");if(!e.id)throw new Error("id is not defined");if(!e.source)throw new Error("source is not defined");Array.isArray(e.source)&&(e.source=s(e.source)),(0,l.render)((0,l.createElement)(u.default,e),e.element)}var r=Object.assign||function(e){for(var t=1;t47&&e<58||32===e||8===e||e>64&&e<91||e>95&&e<112||e>185&&e<193||e>218&&e<223}function a(e){return y?{onInput:e}:b?{onChange:e}:void 0}t.__esModule=!0,t.default=void 0;var p,c,d=Object.assign||function(e){for(var t=1;t=o;this.setState({query:u}),l||!s&&a&&p?r(u,function(e){var n=e.length>0;t.setState({menuOpen:n,options:e,selected:i&&n?0:-1})}):!s&&p||this.setState({menuOpen:!1,options:[]})},t.prototype.handleInputClick=function(e){this.handleInputChange(e)},t.prototype.handleInputFocus=function(e){this.setState({focused:-1})},t.prototype.handleOptionFocus=function(e){this.setState({focused:e,hovered:null,selected:e})},t.prototype.handleOptionMouseEnter=function(e,t){this.setState({hovered:t})},t.prototype.handleOptionMouseOut=function(e,t){this.setState({hovered:null})},t.prototype.handleOptionTouchEnd=function(e,t){this.handleOptionClick(e,t)},t.prototype.handleOptionClick=function(e,t){var n=this.state.options[t],o=this.templateInputValue(n);this.props.onConfirm(n),this.setState({focused:-1,menuOpen:!1,query:o,selected:-1})},t.prototype.handleOptionMouseDown=function(e){e.preventDefault()},t.prototype.handleUpArrow=function(e){e.preventDefault();var t=this.state,n=t.menuOpen,o=t.selected;-1!==o&&n&&this.handleOptionFocus(o-1)},t.prototype.handleDownArrow=function(e){var t=this;if(e.preventDefault(),this.props.showAllValues&&!1===this.state.menuOpen)e.preventDefault(),this.props.source("",function(e){t.setState({menuOpen:!0,options:e,selected:0,focused:0,hovered:null})});else if(!0===this.state.menuOpen){var n=this.state,o=n.menuOpen,r=n.options,l=n.selected,i=l!==r.length-1,u=i&&o;u&&this.handleOptionFocus(l+1)}},t.prototype.handleSpace=function(e){var t=this;this.props.showAllValues&&!1===this.state.menuOpen&&(e.preventDefault(),this.props.source("",function(e){t.setState({menuOpen:!0,options:e})}))},t.prototype.handleEnter=function(e){this.state.menuOpen&&(e.preventDefault(),this.state.selected>=0&&this.handleOptionClick(e,this.state.selected))},t.prototype.handlePrintableKey=function(e){var t=this.elementReferences[-1];e.target===t||t.focus()},t.prototype.handleKeyDown=function(e){switch(g[e.keyCode]){case"up":this.handleUpArrow(e);break;case"down":this.handleDownArrow(e);break;case"space":this.handleSpace(e);break;case"enter":this.handleEnter(e);break;case"escape":this.handleComponentBlur({query:this.state.query});break;default:s(e.keyCode)&&this.handlePrintableKey(e)}},t.prototype.render=function(){var e=this,t=this.props,n=t.cssNamespace,o=t.displayMenu,r=t.id,l=t.minLength,i=t.name,u=t.placeholder,s=t.required,p=t.showAllValues,c=this.state,h=c.focused,_=c.hovered,y=c.menuOpen,b=c.options,g=c.query,w=c.selected,C=this.hasAutoselect(),E=-1===h,x=0===b.length,N=0!==g.length,I=g.length>=l,k=this.props.showNoOptionsFound&&E&&x&&N&&I,M=n+"__wrapper",S=n+"__input",A=null!==h,D=A?" "+S+"--focused":"",T=this.props.showAllValues?" "+S+"--show-all-values":" "+S+"--default",L=n+"__dropdown-arrow-down",V=-1!==h&&null!==h,j=n+"__menu",P=j+"--"+o,q=y||k,B=j+"--"+(q?"visible":"hidden"),U=n+"__option",R=n+"__hint",F=this.templateInputValue(b[w]),W=F&&0===F.toLowerCase().indexOf(g.toLowerCase()),K=W&&C?g+F.substr(g.length):"",H=O&&K;return(0,f.createElement)("div",{className:M,onKeyDown:this.handleKeyDown},(0,f.createElement)(m.default,{length:b.length,queryLength:g.length,minQueryLength:l,selectedOption:this.templateInputValue(b[w])}),H&&(0,f.createElement)("span",null,(0,f.createElement)("input",{className:R,readonly:!0,tabIndex:"-1",value:K})),(0,f.createElement)("input",d({"aria-activedescendant":!!V&&r+"__option--"+h,"aria-expanded":y,"aria-owns":r+"__listbox",autoComplete:"off",className:""+S+D+T,id:r,onClick:function(t){return e.handleInputClick(t)},onBlur:this.handleInputBlur},a(this.handleInputChange),{onFocus:this.handleInputFocus,name:i,placeholder:u,ref:function(t){e.elementReferences[-1]=t},role:"combobox",type:"text",required:s,value:g})),p&&(0,f.createElement)(v.default,{className:L}),(0,f.createElement)("ul",{className:j+" "+P+" "+B,id:r+"__listbox",role:"listbox"},b.map(function(t,n){var o=-1===h?w===n:h===n,l=o&&null===_?" "+U+"--focused":"",i=n%2?" "+U+"--odd":"";return(0,f.createElement)("li",{"aria-selected":h===n,className:""+U+l+i,dangerouslySetInnerHTML:{__html:e.templateSuggestion(t)},id:r+"__option--"+n,key:n,onBlur:function(t){return e.handleOptionBlur(t,n)},onClick:function(t){return e.handleOptionClick(t,n)},onMouseDown:e.handleOptionMouseDown,onMouseEnter:function(t){return e.handleOptionMouseEnter(t,n)},onMouseOut:function(t){return e.handleOptionMouseOut(t,n)},onTouchEnd:function(t){return e.handleOptionTouchEnd(t,n)},ref:function(t){e.elementReferences[n]=t},role:"option",tabIndex:"-1"})}),k&&(0,f.createElement)("li",{className:U+" "+U+"--no-results"},"No results found")))},t}(f.Component),p.defaultProps={autoselect:!1,cssNamespace:"autocomplete",defaultValue:"",displayMenu:"inline",minLength:0,name:"input-autocomplete",placeholder:"",onConfirm:function(){},confirmOnBlur:!0,showNoOptionsFound:!0,showAllValues:!1,required:!1},c);t.default=w},function(e,t,n){"use strict";function o(e,t){}function r(e,t){if(e)return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function l(e,t){"function"!=typeof t&&null!==t||(e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t))}t.__esModule=!0,t.default=void 0;var i=n(0),u=function(e){function t(){var n,l,i;o(this,t);for(var u=arguments.length,s=Array(u),a=0;a':t=document.createElement("div")),"string"==typeof n&&(r=t.classList).add.apply(r,n.split(" ")),t},b=function(e){if(!(e instanceof HTMLElement))throw new Error("You must provide a valid dom element");var n=window.getComputedStyle(e);return["height","padding-top","padding-bottom"].map(function(e){var t=parseInt(n.getPropertyValue(e),10);return isNaN(t)?0:t}).reduce(function(e,t){return e+t})},s=function(e,t){if(!(e instanceof Array))throw new Error("You must provide a Array of HTMLElements to be filtered.");return"string"!=typeof t?e:e.filter(function(e){return e.querySelector(t)instanceof HTMLElement||e.shadowRoot&&e.shadowRoot.querySelector(t)instanceof HTMLElement}).map(function(e){return e.querySelector(t)||e.shadowRoot&&e.shadowRoot.querySelector(t)})},T=function(e){return e.composedPath&&e.composedPath()[0]||e.target},f=function(e,t,n){return{element:e,posX:n.pageX-t.left,posY:n.pageY-t.top}},L=function(e,t,n){if(!(e instanceof Event))throw new Error("setDragImage requires a DragEvent as the first argument.");if(!(t instanceof HTMLElement))throw new Error("setDragImage requires the dragged element as the second argument.");if(n||(n=f),e.dataTransfer&&e.dataTransfer.setDragImage){var r=n(t,m(t),e);if(!(r.element instanceof HTMLElement)||"number"!=typeof r.posX||"number"!=typeof r.posY)throw new Error("The customDragImage function you provided must return and object with the properties element[string], posX[integer], posY[integer].");e.dataTransfer.effectAllowed="copyMove",e.dataTransfer.setData("text/plain",T(e).id),e.dataTransfer.setDragImage(r.element,r.posX,r.posY)}},C=function(e,t){if(!0===e.isSortable){var n=p(e).getConfig("acceptFrom");if(null!==n&&!1!==n&&"string"!=typeof n)throw new Error('HTML5Sortable: Wrong argument, "acceptFrom" must be "null", "false", or a valid selector string.');if(null!==n)return!1!==n&&0=parseInt(r.maxItems)&&D.parentElement!==n||(e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect=!0===p(n).getConfig("copy")?"copy":"move",o(n,t,e.pageY))}};a(t.concat(s),"dragover",r),a(t.concat(s),"dragenter",r)}),e)}return X.destroy=function(e){var t,n,r,o;n=c(t=e,"opts")||{},r=d(t.children,n.items),o=s(r,n.handle),i(t,"dragover"),i(t,"dragenter"),i(t,"drop"),F(t),i(o,"mousedown"),P(r),N(r)},X.enable=function(e){z(e)},X.disable=function(e){var t,n,r,o;n=c(t=e,"opts"),r=d(t.children,n.items),o=s(r,n.handle),l(t,"aria-dropeffect","none"),c(t,"_disabled","true"),l(o,"draggable","false"),i(o,"mousedown")},X.__testing={_data:c,_removeItemEvents:P,_removeItemData:N,_removeSortableData:F},X}(); -//# sourceMappingURL=html5sortable.min.js.map -- cgit v1.2.3 From 36bdf3a8c6ce406d23453b9bb840cf94ca57a820 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 18 May 2020 12:44:26 +0100 Subject: [Borsetshire] Add front end tests. --- .cypress/cypress/integration/borsetshire.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .cypress/cypress/integration/borsetshire.js diff --git a/.cypress/cypress/integration/borsetshire.js b/.cypress/cypress/integration/borsetshire.js new file mode 100644 index 000000000..c2a2710fb --- /dev/null +++ b/.cypress/cypress/integration/borsetshire.js @@ -0,0 +1,21 @@ +it('loads the right front page', function() { + cy.visit('http://borsetshire.localhost:3001/'); + cy.contains('Borsetshire'); +}); + +it('logs in without fuss', function() { + cy.contains('Sign in').click(); + cy.contains('Customer service').click(); + cy.url().should('include', '/reports'); + + cy.visit('http://borsetshire.localhost:3001/auth'); + cy.contains('Inspector').click(); + cy.url().should('include', '/my/planned'); + + cy.visit('http://borsetshire.localhost:3001/auth'); + cy.get('[name=username]').type('super@example.org'); + cy.contains('Sign in with a password').click(); + cy.get('[name=password_sign_in]').type('password'); + cy.get('[name=sign_in_by_password]').last().click(); + cy.url().should('include', '/admin'); +}); -- cgit v1.2.3 From 38dce48b716c3f6fc1561b976d98eee94af778b3 Mon Sep 17 00:00:00 2001 From: Sam Pearson Date: Thu, 14 May 2020 10:18:16 +0100 Subject: Include updates in failure summary report. This includes details of any failing updates in the summary script. --- bin/send-failure-summary | 25 +++++++++++++++++++++++++ bin/send-reports-failure-summary | 20 -------------------- conf/crontab-example | 2 +- perllib/Open311/PostServiceRequestUpdates.pm | 22 ++++++++++++++++++++++ t/open311/post-service-request-updates.t | 4 +++- 5 files changed, 51 insertions(+), 22 deletions(-) create mode 100755 bin/send-failure-summary delete mode 100755 bin/send-reports-failure-summary diff --git a/bin/send-failure-summary b/bin/send-failure-summary new file mode 100755 index 000000000..1a435dc7c --- /dev/null +++ b/bin/send-failure-summary @@ -0,0 +1,25 @@ +#!/usr/bin/env perl + +# send-failure-summary: +# Prints a summary of report/update sending failures + +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::Script::Reports; +use Open311::PostServiceRequestUpdates; + +# report summary +my $manager = FixMyStreet::Script::Reports->new; +$manager->end_summary_failures; + +# updates summary +my $updates = Open311::PostServiceRequestUpdates->new; +$updates->summary_failures; diff --git a/bin/send-reports-failure-summary b/bin/send-reports-failure-summary deleted file mode 100755 index c7a5b4086..000000000 --- a/bin/send-reports-failure-summary +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env perl - -# send-reports-failure-summary: -# Prints a summary of report sending failures - -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::Script::Reports; - -my $manager = FixMyStreet::Script::Reports->new; -$manager->end_summary_failures; diff --git a/conf/crontab-example b/conf/crontab-example index 86fcee80c..568b07e2e 100644 --- a/conf/crontab-example +++ b/conf/crontab-example @@ -12,7 +12,7 @@ PATH=/usr/local/bin:/usr/bin:/bin # send-reports has three rows so that its 8am entry can be run with --verbose to send a morning summary of anything that's gone wrong */5 * * * * "$FMS/commonlib/bin/run-with-lockfile.sh" -n "$LOCK_DIR/send-reports.lock" "$FMS/bin/send-reports" || echo "stalled?" -0 8 * * * "$FMS/bin/send-reports-failure-summary" +0 8 * * * "$FMS/bin/send-failure-summary" 2 * * * * "$FMS/commonlib/bin/run-with-lockfile.sh" -n "$LOCK_DIR/send-alerts.lock" "$FMS/bin/send-alerts" || echo "stalled?" 22,52 * * * * "$FMS/commonlib/bin/run-with-lockfile.sh" -n "$LOCK_DIR/send-questionnaires.lock" "$FMS/bin/send-questionnaires" || echo "stalled?" diff --git a/perllib/Open311/PostServiceRequestUpdates.pm b/perllib/Open311/PostServiceRequestUpdates.pm index fadd063da..68823343c 100755 --- a/perllib/Open311/PostServiceRequestUpdates.pm +++ b/perllib/Open311/PostServiceRequestUpdates.pm @@ -141,6 +141,28 @@ sub process_update { } } +sub summary_failures { + my $self = shift; + my $bodies = $self->fetch_bodies; + my $params = $self->construct_query(1); + my $u = FixMyStreet::DB->resultset("Comment") + ->to_body([ keys %$bodies ]) + ->search({ "me.send_fail_count" => { '>', 0 } }) + ->search($params, { join => "problem" }); + + my $base_url = FixMyStreet->config('BASE_URL'); + my $sending_errors; + while (my $row = $u->next) { + my $url = $base_url . "/report/" . $row->problem_id; + $sending_errors .= "\n" . '=' x 80 . "\n\n" . "* $url, update " . $row->id . " failed " + . $row->send_fail_count . " times, last at " . $row->send_fail_timestamp + . ", reason " . $row->send_fail_reason . "\n"; + } + if ($sending_errors) { + print '=' x 80 . "\n\n" . "The following updates failed sending:\n$sending_errors"; + } +} + sub log { my ($self, $comment, $msg) = @_; return unless $self->verbose; diff --git a/t/open311/post-service-request-updates.t b/t/open311/post-service-request-updates.t index adfd4e3c5..71bba4af7 100644 --- a/t/open311/post-service-request-updates.t +++ b/t/open311/post-service-request-updates.t @@ -1,12 +1,13 @@ #!/usr/bin/env perl use FixMyStreet::TestMech; +use Test::Output; my $mech = FixMyStreet::TestMech->new; use_ok( 'Open311::PostServiceRequestUpdates' ); -my $o = Open311::PostServiceRequestUpdates->new( site => 'fixmystreet.com' ); +my $o = Open311::PostServiceRequestUpdates->new; my $params = { send_method => 'Open311', @@ -130,6 +131,7 @@ subtest 'Oxfordshire gets an ID' => sub { $o->send; $c2->discard_changes; is $c2->send_fail_count, 1, 'Oxfordshire update tried to send, failed'; + stdout_like { $o->summary_failures } qr/The following updates failed sending/; }; }; -- cgit v1.2.3 From 6a6354640abd976b76f5644367bf8018532e43ec Mon Sep 17 00:00:00 2001 From: Zarino Zappia Date: Wed, 20 May 2020 15:41:33 +0100 Subject: [fixmystreet.com] Update company details in footer And in couple of cobrand templates. --- docs/_includes/footer.html | 11 ++++++++--- docs/_layouts/pro.html | 9 +++++---- templates/web/fixmystreet.com/footer_extra.html | 7 ++++++- templates/web/hounslow/about/mapterms.html | 2 +- templates/web/isleofwight/about/mapterms.html | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html index 25ffa94a7..d7260aaa7 100644 --- a/docs/_includes/footer.html +++ b/docs/_includes/footer.html @@ -25,7 +25,7 @@

    @@ -37,14 +37,19 @@
    diff --git a/docs/_layouts/pro.html b/docs/_layouts/pro.html index d04446a6e..f1e764c8b 100644 --- a/docs/_layouts/pro.html +++ b/docs/_layouts/pro.html @@ -137,14 +137,15 @@
    diff --git a/templates/web/fixmystreet.com/footer_extra.html b/templates/web/fixmystreet.com/footer_extra.html index 699d7de8b..b1cb0572e 100644 --- a/templates/web/fixmystreet.com/footer_extra.html +++ b/templates/web/fixmystreet.com/footer_extra.html @@ -70,7 +70,12 @@
    diff --git a/templates/web/hounslow/about/mapterms.html b/templates/web/hounslow/about/mapterms.html index 04c0aeb09..fe08dcdd1 100644 --- a/templates/web/hounslow/about/mapterms.html +++ b/templates/web/hounslow/about/mapterms.html @@ -8,7 +8,7 @@
  • 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; + period during which SocietyWorks Ltd. makes it available;
  • II. You are not permitted to copy, sub-license, distribute, sell or diff --git a/templates/web/isleofwight/about/mapterms.html b/templates/web/isleofwight/about/mapterms.html index 04c0aeb09..fe08dcdd1 100644 --- a/templates/web/isleofwight/about/mapterms.html +++ b/templates/web/isleofwight/about/mapterms.html @@ -8,7 +8,7 @@
  • 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; + period during which SocietyWorks Ltd. makes it available;
  • II. You are not permitted to copy, sub-license, distribute, sell or -- cgit v1.2.3 From c6e723a2fee40f7009de3f04de5870693e12889e Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 26 May 2020 23:01:36 +0100 Subject: [Docs] Update Gemfile.lock to current GitHub pages --- docs/Gemfile.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index ed1962744..c6eb7aa83 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,12 +1,12 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.2.1) + activesupport (6.0.3.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - zeitwerk (~> 2.2) + zeitwerk (~> 2.2, >= 2.2.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) coffee-script (2.4.1) @@ -26,14 +26,14 @@ GEM ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (1.0.0) + faraday (1.0.1) multipart-post (>= 1.2, < 3) ffi (1.12.2) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (204) + github-pages (206) github-pages-health-check (= 1.16.1) - jekyll (= 3.8.5) + jekyll (= 3.8.7) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.6) @@ -72,7 +72,7 @@ GEM mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.13.0) + rouge (= 3.19.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -86,7 +86,7 @@ GEM http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) - jekyll (3.8.5) + jekyll (3.8.7) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) @@ -205,23 +205,23 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.14.0) + minitest (5.14.1) multipart-post (2.1.1) - nokogiri (1.10.8) + nokogiri (1.10.9) mini_portile2 (~> 2.4.0) - octokit (4.16.0) + octokit (4.18.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.1.1) - rb-fsevent (0.10.3) + rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) - rouge (3.13.0) - ruby-enum (0.7.2) + rouge (3.19.0) + ruby-enum (0.8.0) i18n - rubyzip (2.2.0) + rubyzip (2.3.0) safe_yaml (1.0.5) sass (3.7.4) sass-listen (~> 4.0.0) @@ -234,12 +234,12 @@ GEM terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) - typhoeus (1.3.1) + typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.6) + tzinfo (1.2.7) thread_safe (~> 0.1) - unicode-display_width (1.6.1) - zeitwerk (2.2.2) + unicode-display_width (1.7.0) + zeitwerk (2.3.0) PLATFORMS ruby -- cgit v1.2.3 From 72809e405e690c88d0e2011802820c9ba215ef61 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 11 May 2020 16:06:37 +0100 Subject: Consistent use of Contact groups. --- bin/import_categories | 2 +- bin/tfl/import_categories | 2 +- perllib/FixMyStreet/App/Controller/Admin/Templates.pm | 2 +- perllib/FixMyStreet/App/Controller/Admin/Triage.pm | 6 ++---- perllib/FixMyStreet/App/Controller/Admin/Users.pm | 2 +- perllib/FixMyStreet/App/Controller/Dashboard.pm | 4 ++-- perllib/FixMyStreet/App/Controller/My.pm | 10 +++++++++- perllib/FixMyStreet/App/Controller/Report.pm | 5 ++--- perllib/FixMyStreet/App/Controller/Report/New.pm | 2 +- perllib/FixMyStreet/Cobrand/Hounslow.pm | 4 ++-- perllib/FixMyStreet/Cobrand/IsleOfWight.pm | 5 +---- perllib/FixMyStreet/DB/Result/Contact.pm | 4 +++- templates/web/base/reports/_list-filters.html | 2 +- 13 files changed, 27 insertions(+), 23 deletions(-) diff --git a/bin/import_categories b/bin/import_categories index 490e2187f..744759f1f 100644 --- a/bin/import_categories +++ b/bin/import_categories @@ -81,7 +81,7 @@ sub make_categories { if ($group) { my $groups = $child_cat->groups; - my %groups = map { $_ => 1} @$groups; + my %groups = map { $_ => 1 } grep { $_ } @$groups; $groups{$group} = 1; my @groups = keys %groups; $child_cat->set_extra_metadata(group => \@groups); diff --git a/bin/tfl/import_categories b/bin/tfl/import_categories index bb48ddc4e..d3b99547c 100755 --- a/bin/tfl/import_categories +++ b/bin/tfl/import_categories @@ -65,7 +65,7 @@ for my $group (keys %$groups) { $child_cat->note($child_cat->in_storage ? 'Updated by import_categories' : 'Created by import_categories'); say colored("WARNING", 'red') . " " . $child_cat->category . " already exists" if $child_cat->in_storage and $child_cat->category ne 'Other (TfL)'; my $groups = $child_cat->groups; - my %groups = map { $_ => 1} @$groups; + my %groups = map { $_ => 1 } grep { $_ } @$groups; $groups{$group} = 1; my @groups = keys %groups; $child_cat->extra(undef) if $child_cat->in_storage; diff --git a/perllib/FixMyStreet/App/Controller/Admin/Templates.pm b/perllib/FixMyStreet/App/Controller/Admin/Templates.pm index efff1b488..f933c5f8a 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Templates.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Templates.pm @@ -69,7 +69,7 @@ sub edit : Path : Args(2) { category => $_->category_display, active => $active_contacts{$_->id}, email => $_->email, - group => $_->get_extra_metadata('group') // '', + group => $_->groups, } } @live_contacts; $c->stash->{contacts} = \@all_contacts; $c->forward('/report/stash_category_groups', [ \@all_contacts, 1 ]) if $c->cobrand->enable_category_groups; diff --git a/perllib/FixMyStreet/App/Controller/Admin/Triage.pm b/perllib/FixMyStreet/App/Controller/Admin/Triage.pm index 428c35073..7cfcc93dd 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Triage.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Triage.pm @@ -90,10 +90,8 @@ sub setup_categories : Private { 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 $groups = $category->groups; + push( @{$category_groups{$_}}, $category ) for @$groups; } my @category_groups = (); for my $group ( grep { $_ ne _('Other') } sort keys %category_groups ) { diff --git a/perllib/FixMyStreet/App/Controller/Admin/Users.pm b/perllib/FixMyStreet/App/Controller/Admin/Users.pm index f4b9bd7dc..5bb91341d 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Users.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Users.pm @@ -401,7 +401,7 @@ sub edit : Chained('user') : PathPart('') : Args(0) { id => $_->id, category => $_->category, active => $active_contacts{$_->id}, - group => $_->get_extra_metadata('group') // '', + group => $_->groups, } } @live_contacts; $c->stash->{contacts} = \@all_contacts; $c->forward('/report/stash_category_groups', [ \@all_contacts, 1 ]) if $c->cobrand->enable_category_groups; diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index 52af45940..abb2e0988 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -493,8 +493,8 @@ sub generate_csv : Private { } if ($asked_for{subcategory}) { - my $group = $obj->contact && $obj->contact->get_extra_metadata('group') || ''; - $group = join(',', ref $group ? @$group : $group); + my $group = $obj->contact && $obj->contact->groups; + $group = join(',', @$group); if ($group) { $hashref->{subcategory} = $obj->category; $hashref->{category} = $group; diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm index 316fe08b8..f5c06d926 100644 --- a/perllib/FixMyStreet/App/Controller/My.pm +++ b/perllib/FixMyStreet/App/Controller/My.pm @@ -198,7 +198,15 @@ sub setup_page_data : Private { order_by => [ "$table.category" ], } )->all; $c->stash->{filter_categories} = \@categories; - $c->forward('/report/stash_category_groups', [ \@categories ]) if $c->cobrand->enable_category_groups; + + if ($c->cobrand->enable_category_groups) { + my @contacts = map { { + category => $_->category, + category_display => $_->category_display, + group => [''], + } } @categories; + $c->forward('/report/stash_category_groups', [ \@contacts ]); + } my $pins = $c->stash->{pins}; FixMyStreet::Map::display_map( diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 8a6047194..4d8794c59 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -703,9 +703,8 @@ sub stash_category_groups : Private { my %category_groups = (); for my $category (@$contacts) { - 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); + my $group = $category->{group} // $category->groups; + my @groups = @$group; if (scalar @groups > 1 && $combine_multiple) { @groups = sort @groups; $category->{group} = \@groups; diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index fc1a78cd5..abe37887e 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -766,7 +766,7 @@ sub setup_categories_and_bodies : Private { if (@category_options) { # If there's an Other category present, put it at the bottom @category_options = ( - { category => _('-- Pick a category --'), category_display => _('-- Pick a category --'), group => '' }, + { category => _('-- Pick a category --'), category_display => _('-- Pick a category --'), group => [''] }, grep { $_->category ne _('Other') } @category_options ); push @category_options, $seen{_('Other')} if $seen{_('Other')}; } diff --git a/perllib/FixMyStreet/Cobrand/Hounslow.pm b/perllib/FixMyStreet/Cobrand/Hounslow.pm index 2fc949546..bc1c80571 100644 --- a/perllib/FixMyStreet/Cobrand/Hounslow.pm +++ b/perllib/FixMyStreet/Cobrand/Hounslow.pm @@ -138,8 +138,8 @@ sub setup_general_enquiries_stash { } )->all; @contacts = grep { - my $group = $_->get_extra_metadata('group') || ''; - $group eq 'Other' || $group eq 'General Enquiries'; + my $groups = $_->groups; + grep { $_ eq 'Other' || $_ eq 'General Enquiries' } @$groups; } @contacts; $self->{c}->stash->{bodies} = \%bodies; $self->{c}->stash->{bodies_to_list} = \%bodies; diff --git a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm index 3bcea4d3d..ee1a25465 100644 --- a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm +++ b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm @@ -176,10 +176,7 @@ sub expand_triage_cat_list { 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 ) { + for my $group ( @{$cat->groups} ) { $group_to_category{$group} //= []; push @{ $group_to_category{$group} }, $cat->category; } diff --git a/perllib/FixMyStreet/DB/Result/Contact.pm b/perllib/FixMyStreet/DB/Result/Contact.pm index 2941683d1..1fa38e8a2 100644 --- a/perllib/FixMyStreet/DB/Result/Contact.pm +++ b/perllib/FixMyStreet/DB/Result/Contact.pm @@ -113,9 +113,11 @@ sub category_display { $self->get_extra_metadata('display_name') || $self->translate_column('category'); } +# Returns an arrayref of groups this Contact is in; if it is +# not in any group, returns an arrayref of the empty string. sub groups { my $self = shift; - my $groups = $self->get_extra_metadata('group') || []; + my $groups = $self->get_extra_metadata('group') || ['']; $groups = [ $groups ] unless ref $groups eq 'ARRAY'; return $groups; } diff --git a/templates/web/base/reports/_list-filters.html b/templates/web/base/reports/_list-filters.html index f5d1faa65..c5a8eaa75 100644 --- a/templates/web/base/reports/_list-filters.html +++ b/templates/web/base/reports/_list-filters.html @@ -2,7 +2,7 @@ [% BLOCK category_options %] [% FOR cat IN categories %] - -- cgit v1.2.3 From 901834444846905c85e56528b020bd1a667cb5d2 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 11 May 2020 16:56:46 +0100 Subject: Make sure category shown in all its groups. When compiling the reporting category dropdown, only one instance of each value was used. But if a value appears twice, from two different bodies, in different groups, it would then not appear in all the groups it would be expected to. Make sure we update the list of groups of the category if we come across another with the same value. --- CHANGELOG.md | 1 + perllib/FixMyStreet/App/Controller/JSON.pm | 1 - perllib/FixMyStreet/App/Controller/Report/New.pm | 13 +++++++++---- t/app/controller/report_new.t | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba34d428c..11f7cbf29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Unreleased - Bugfixes: - Fix issue with dashboard report CSV export. #3026 + - Make sure category shown in all its groups when reporting. - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/perllib/FixMyStreet/App/Controller/JSON.pm b/perllib/FixMyStreet/App/Controller/JSON.pm index ccc5b31dc..a7913bb4b 100644 --- a/perllib/FixMyStreet/App/Controller/JSON.pm +++ b/perllib/FixMyStreet/App/Controller/JSON.pm @@ -7,7 +7,6 @@ BEGIN { extends 'Catalyst::Controller'; } use JSON::MaybeXS; use DateTime; use DateTime::Format::ISO8601; -use List::MoreUtils 'uniq'; use FixMyStreet::DateRange; =head1 NAME diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index abe37887e..ad324fe60 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -6,8 +6,7 @@ BEGIN { extends 'Catalyst::Controller'; } use utf8; use Encode; -use List::MoreUtils qw(uniq); -use List::Util 'first'; +use List::Util qw(first uniq); use HTML::Entities; use Path::Class; use Utils; @@ -759,8 +758,14 @@ sub setup_categories_and_bodies : Private { if !$c->stash->{unresponsive}{ALL} && ($contact->email =~ /^REFUSED$/i || $body_send_method eq 'Refused'); - push @category_options, $contact unless $seen{$contact->category}; - $seen{$contact->category} = $contact; + if (my $cat = $seen{$contact->category}) { + # Make sure the category is listed in all its groups, not just the first set + my @groups = uniq @{$cat->groups}, @{$contact->groups}; + $cat->set_extra_metadata(group => \@groups); + } else { + push @category_options, $contact; + $seen{$contact->category} = $contact; + } } if (@category_options) { diff --git a/t/app/controller/report_new.t b/t/app/controller/report_new.t index d2da75f2c..6af709c2c 100644 --- a/t/app/controller/report_new.t +++ b/t/app/controller/report_new.t @@ -641,10 +641,10 @@ subtest "category groups" => sub { } }, sub { $contact2->update( { extra => { group => ['Roads','Pavements'] } } ); - $contact9->update( { extra => { group => 'Roads' } } ); + $contact9->update( { extra => { group => 'Pavements' } } ); $contact10->update( { extra => { group => 'Roads' } } ); $mech->get_ok("/report/new?lat=$saved_lat&lon=$saved_lon"); - $mech->content_like(qr{\s*}); + $mech->content_like(qr{\s*\s*}); $mech->content_like(qr{\s*\s*}); }; }; -- cgit v1.2.3 From 7b7e00c16f6d31ff77b9f28e63aac3b608f2bfbb Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Tue, 26 May 2020 13:34:34 +0100 Subject: Fix update-schema constraint check on Postgres 12 pg_constraint.consrc has been removed in Postgres 12, and this is the recommended replacement. See https://www.postgresql.org/docs/12/release-12.html --- CHANGELOG.md | 1 + bin/update-schema | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba34d428c..e0afca449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Unreleased - Bugfixes: - Fix issue with dashboard report CSV export. #3026 + - bin/update-schema PostgreSQL 12 compatibility. #3043 - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/bin/update-schema b/bin/update-schema index 8f31085c9..44e299b91 100755 --- a/bin/update-schema +++ b/bin/update-schema @@ -319,7 +319,7 @@ sub constraint_exists { # Returns true if a check constraint contains a certain string sub constraint_contains { my ( $constraint, $check ) = @_; - my ($consrc) = $db->dbh->selectrow_array('select consrc from pg_constraint where conname = ?', {}, $constraint); + my ($consrc) = $db->dbh->selectrow_array('select pg_get_expr(conbin, conrelid) from pg_constraint where conname = ?', {}, $constraint); return unless $consrc; return $consrc =~ /$check/; } -- cgit v1.2.3 From 363e9b29df531494a01726b3d1d12acb37ac9c8a Mon Sep 17 00:00:00 2001 From: Chris Mytton Date: Fri, 29 May 2020 13:32:33 +0100 Subject: [TfL] Update COVID-19 homepage messaging --- templates/web/tfl/front/pre-steps.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/web/tfl/front/pre-steps.html b/templates/web/tfl/front/pre-steps.html index 9a740c692..ee22f4978 100644 --- a/templates/web/tfl/front/pre-steps.html +++ b/templates/web/tfl/front/pre-steps.html @@ -8,6 +8,4 @@
    We will continue to serve you the best that we can but urge everyone not to make journeys unless absolutely necessary. -
    - We will be able to give you a further update on this by the end of May.

    -- cgit v1.2.3 From f8859e29f5bcf75c1b2acb934bd4353f83c5a2c1 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Fri, 29 May 2020 16:21:57 +0100 Subject: [UK] Remove Somerset West and Taunton special destination handling --- perllib/FixMyStreet/SendReport/Email.pm | 20 -------------------- t/Mock/MapIt.pm | 1 - t/app/sendreport/email.t | 16 ---------------- 3 files changed, 37 deletions(-) diff --git a/perllib/FixMyStreet/SendReport/Email.pm b/perllib/FixMyStreet/SendReport/Email.pm index 72cd42952..81a25f896 100644 --- a/perllib/FixMyStreet/SendReport/Email.pm +++ b/perllib/FixMyStreet/SendReport/Email.pm @@ -16,9 +16,6 @@ sub build_recipient_list { my ($body_email, $state, $note) = ( $contact->email, $contact->state, $contact->note ); - $body_email = swandt_contact($row->latitude, $row->longitude) - if $body->name eq 'Somerset West and Taunton Council' && $body_email eq 'SPECIAL'; - unless ($state eq 'confirmed') { $all_confirmed = 0; $note = 'Body ' . $row->bodies_str . ' deleted' @@ -128,21 +125,4 @@ sub email_list { return \@list; } -# SW&T has different contact addresses depending upon the old district -sub swandt_contact { - my $district = _get_district_for_contact(@_); - my $email; - $email = ['customerservices', 'westsomerset'] if $district == 2427; - $email = ['enquiries', 'tauntondeane'] if $district == 2429; - return join('@', $email->[0], $email->[1] . '.gov.uk'); -} - -sub _get_district_for_contact { - my ( $lat, $lon ) = @_; - my $district = - FixMyStreet::MapIt::call( 'point', "4326/$lon,$lat", type => 'DIS', generation => 34 ); - ($district) = keys %$district; - return $district; -} - 1; diff --git a/t/Mock/MapIt.pm b/t/Mock/MapIt.pm index d49294a6a..b54ba0ddb 100644 --- a/t/Mock/MapIt.pm +++ b/t/Mock/MapIt.pm @@ -50,7 +50,6 @@ my @PLACES = ( [ '?', 51.345714, -0.227959, 2457, 'Epsom and Ewell Borough Council', 'DIS' ], [ 'CW11 1HZ', 53.145324, -2.370437, 21069, 'Cheshire East Council', 'UTA', 135301, 'Sandbach Town', 'UTW' ], [ '?', 50.78301, -0.646929 ], - [ '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' ], diff --git a/t/app/sendreport/email.t b/t/app/sendreport/email.t index cfd70a097..c9363b787 100644 --- a/t/app/sendreport/email.t +++ b/t/app/sendreport/email.t @@ -20,8 +20,6 @@ my $contact = $mech->create_contact_ok( ); my $row = FixMyStreet::DB->resultset('Problem')->new( { - latitude => 51.023569, - longitude => -3.099055, bodies_str => '1000', category => 'category', cobrand => '', @@ -70,18 +68,4 @@ foreach my $test ( { }; } -$body->update({ name => 'Somerset West and Taunton Council' }); - -subtest 'Test special behaviour' => sub { - my $e = FixMyStreet::SendReport::Email->new; - $contact->update( { state => 'confirmed', email => 'SPECIAL' } ); - $e->add_body( $body ); - FixMyStreet::override_config { - MAPIT_URL => 'http://mapit.uk/' - }, sub { - my ($e) = $e->build_recipient_list( $row, {} ); - like $e->[0], qr/tauntondeane/, 'correct recipient'; - }; -}; - done_testing(); -- cgit v1.2.3 From a7541a181013378da0090da53847c882881825d0 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 1 Jun 2020 10:08:55 +0100 Subject: Add user_country call that uses Gaze. --- CHANGELOG.md | 1 + perllib/FixMyStreet/App.pm | 5 +++++ perllib/FixMyStreet/Gaze.pm | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a2466913..096c07bd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - `#geolocate_link` is now easier to re-style. #3006 - Links inside `#front-main` can be customised using `$primary_link_*` Sass variables. #3007 - Add option to show front end testing code coverage. #3036 + - Add function to fetch user's country from Gaze. * v3.0.1 (6th May 2020) - New features: diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm index 6a41d93a9..4ca6f23cb 100644 --- a/perllib/FixMyStreet/App.pm +++ b/perllib/FixMyStreet/App.pm @@ -536,6 +536,11 @@ sub check_2fa { return 0; } +sub user_country { + my $c = shift; + return FixMyStreet::Gaze::get_country_from_ip($c->req->address); +} + =head1 SEE ALSO L, L diff --git a/perllib/FixMyStreet/Gaze.pm b/perllib/FixMyStreet/Gaze.pm index bccc81d8c..e2b2e0e08 100644 --- a/perllib/FixMyStreet/Gaze.pm +++ b/perllib/FixMyStreet/Gaze.pm @@ -3,6 +3,7 @@ package FixMyStreet::Gaze; use strict; use warnings; +use FixMyStreet; use mySociety::Gaze; sub get_radius_containing_population ($$) { @@ -24,4 +25,11 @@ sub get_radius_containing_population ($$) { return $dist; } +sub get_country_from_ip { + my ($ip) = @_; + return 'GB' if FixMyStreet->test_mode; + # uncoverable statement + return mySociety::Gaze::get_country_from_ip($ip); +} + 1; -- cgit v1.2.3 From 5412a569e233b82c8f971e07c57c73d2ef3f6733 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 27 May 2020 21:01:10 +0100 Subject: Mark user as active when sent an email alert. --- CHANGELOG.md | 2 ++ perllib/FixMyStreet/Script/Alerts.pm | 4 ++++ t/app/controller/alert_new.t | 2 ++ 3 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a2466913..05663b366 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## Releases * Unreleased + - Changes: + - Mark user as active when sent an email alert. - Bugfixes: - Fix issue with dashboard report CSV export. #3026 - bin/update-schema PostgreSQL 12 compatibility. #3043 diff --git a/perllib/FixMyStreet/Script/Alerts.pm b/perllib/FixMyStreet/Script/Alerts.pm index cb1f022fa..d07728092 100644 --- a/perllib/FixMyStreet/Script/Alerts.pm +++ b/perllib/FixMyStreet/Script/Alerts.pm @@ -307,6 +307,10 @@ sub _send_aggregated_alert_email(%) { # Ignore phone-only users return unless $data{alert_user}->email_verified; + # Mark user as active as they're being sent an alert + $data{alert_user}->set_last_active; + $data{alert_user}->update; + my $email = $data{alert_user}->email; my ($domain) = $email =~ m{ @ (.*) \z }x; return if $data{schema}->resultset('Abuse')->search( { diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t index 7eba90530..d968b56b1 100644 --- a/t/app/controller/alert_new.t +++ b/t/app/controller/alert_new.t @@ -523,6 +523,8 @@ subtest "Test alerts are not sent for no-text updates" => sub { }; $mech->email_count_is(1); + $user2->discard_changes; + isnt $user2->last_active, undef, 'Last active has been set'; $mech->delete_user($user1); $mech->delete_user($user2); -- cgit v1.2.3 From 7068bba405c905753bd3b41ebf16c399b9d3213f Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 27 May 2020 21:01:29 +0100 Subject: [fixmystreet.com] State account deletion policy. --- templates/web/fixmystreet.com/about/privacy.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/templates/web/fixmystreet.com/about/privacy.html b/templates/web/fixmystreet.com/about/privacy.html index 8ed953cc9..665b0968b 100755 --- a/templates/web/fixmystreet.com/about/privacy.html +++ b/templates/web/fixmystreet.com/about/privacy.html @@ -287,8 +287,10 @@ When you make a report

    + Personal details will automatically be removed from our database after two + years of inactivity of the associated account. Please contact us if you would like your details to be removed from our admin - database. + database sooner than that.

    @@ -297,7 +299,7 @@ When you make a report

    If you contact FixMyStreet via our support email address we keep your message for two - years at which point they will be automatically deleted.. This is to aid continuity + years at which point it will be automatically deleted. This is to aid continuity and so that we can view any historic context which may have bearing on subsequent support mail, even if members of the support staff change. Support staff adhere to internal privacy policies which may be viewed on request. -- cgit v1.2.3 From f2ef45fb4ed8e167a0dea3bba1a9cd1ad640a31c Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 1 Jun 2020 11:06:55 +0100 Subject: Ignore body users when anonymizing. --- perllib/FixMyStreet/Script/Inactive.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/perllib/FixMyStreet/Script/Inactive.pm b/perllib/FixMyStreet/Script/Inactive.pm index 8dd524ce1..4d28057d4 100644 --- a/perllib/FixMyStreet/Script/Inactive.pm +++ b/perllib/FixMyStreet/Script/Inactive.pm @@ -158,8 +158,14 @@ sub delete_reports { sub anonymize_users { my $self = shift; + my $body_users = FixMyStreet::DB->resultset("Body")->search({ + comment_user_id => { '!=' => undef }, + }, { + columns => 'comment_user_id', + }); my $users = FixMyStreet::DB->resultset("User")->search({ last_active => { '<', interval($self->anonymize) }, + id => { -not_in => $body_users->as_query }, email => { -not_like => 'removed-%@' . FixMyStreet->config('EMAIL_DOMAIN') }, }); -- cgit v1.2.3 From e210a3576aa4fe247feafe4f7c0d27e308faba86 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 3 Jun 2020 17:23:32 +0100 Subject: Store staff user when staff make anonymous update. Match the identical code for anonymous reports. --- perllib/FixMyStreet/App/Controller/Report/Update.pm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index c5d20a5da..8ffba3dcf 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -484,6 +484,13 @@ sub save_update : Private { $update->confirm(); } elsif ($c->stash->{contributing_as_anonymous_user}) { $update->set_extra_metadata( contributed_as => 'anonymous_user' ); + if ( $c->user_exists && $c->user->from_body ) { + # If a staff user has clicked the 'report anonymously' button then + # there would be no record of who that staff member was as we've + # used the cobrand's anonymous_account for the report. In this case + # record the staff user ID in the report metadata. + $update->set_extra_metadata( contributed_by => $c->user->id ); + } $update->confirm(); } elsif ( !$update->user->in_storage ) { # User does not exist. -- cgit v1.2.3 From 766f0ed07864ac89a81d9356a8960c3c0744fc99 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 1 Jun 2020 10:47:27 +0100 Subject: [fixmystreet.com] Add option for recaptcha. We are getting spam submissions of e.g. alert subscribe form, which causes people to get a confirmation email. --- CHANGELOG.md | 2 ++ perllib/FixMyStreet/App/Controller/Auth.pm | 3 ++ perllib/FixMyStreet/Cobrand/UK.pm | 33 ++++++++++++++++++++++ t/app/controller/alert.t | 22 +++++++++++++++ templates/web/base/alert/_list.html | 1 + templates/web/base/alert/updates.html | 2 ++ templates/web/base/auth/form_extra.html | 0 templates/web/base/report/display_tools.html | 1 + .../base/report/form/user_loggedout_by_email.html | 2 ++ templates/web/fixmystreet.com/auth/form_extra.html | 4 +++ 10 files changed, 70 insertions(+) create mode 100644 templates/web/base/auth/form_extra.html create mode 100644 templates/web/fixmystreet.com/auth/form_extra.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 096c07bd8..6830bfbb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - Links inside `#front-main` can be customised using `$primary_link_*` Sass variables. #3007 - Add option to show front end testing code coverage. #3036 - Add function to fetch user's country from Gaze. + - UK: + - Add option for recaptcha. #3050 * v3.0.1 (6th May 2020) - New features: diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index cecfa318c..beba6b235 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -448,6 +448,9 @@ sub check_csrf_token : Private { unless $time && $time > time() - 3600 && $token eq $gen_token; + + # Also check recaptcha if needed + $c->cobrand->call_hook('check_recaptcha'); } sub no_csrf_token : Private { diff --git a/perllib/FixMyStreet/Cobrand/UK.pm b/perllib/FixMyStreet/Cobrand/UK.pm index a42ff58a6..4c62dd538 100644 --- a/perllib/FixMyStreet/Cobrand/UK.pm +++ b/perllib/FixMyStreet/Cobrand/UK.pm @@ -3,6 +3,7 @@ use base 'FixMyStreet::Cobrand::Default'; use strict; use JSON::MaybeXS; +use LWP::UserAgent; use mySociety::MaPit; use mySociety::VotingArea; use Utils; @@ -422,4 +423,36 @@ sub report_new_munge_before_insert { } } +# To use recaptcha, add a RECAPTCHA key to your config, with subkeys secret and +# site_key, taken from the recaptcha site. This shows it to non-UK IP addresses +# on alert and report pages. + +sub requires_recaptcha { + my $self = shift; + my $c = $self->{c}; + + return 0 if $c->user_exists; + return 0 if !FixMyStreet->config('RECAPTCHA'); + return 0 if $c->user_country eq 'GB'; + return 0 unless $c->action =~ /^(alert|report)/; + return 1; +} + +sub check_recaptcha { + my $self = shift; + my $c = $self->{c}; + + return unless $self->requires_recaptcha; + + my $url = 'https://www.google.com/recaptcha/api/siteverify'; + my $res = LWP::UserAgent->new->post($url, { + secret => FixMyStreet->config('RECAPTCHA')->{secret}, + response => $c->get_param('g-recaptcha-response'), + remoteip => $c->req->address, + }); + $res = decode_json($res->content); + $c->detach('/page_error_400_bad_request', ['Bad recaptcha']) + unless $res->{success}; +} + 1; diff --git a/t/app/controller/alert.t b/t/app/controller/alert.t index 41aee5bbc..34e68177c 100644 --- a/t/app/controller/alert.t +++ b/t/app/controller/alert.t @@ -1,6 +1,7 @@ use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; +use Test::MockModule; use t::Mock::Nominatim; # check that we can get the page @@ -73,4 +74,25 @@ FixMyStreet::override_config { is $mech->uri->path, '/rss/reports/Cheltenham/Lansdown'; }; +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'fixmystreet', + MAPIT_URL => 'http://mapit.uk/', + GEOCODER => '', + RECAPTCHA => { secret => 'secret', site_key => 'site_key' }, +}, sub { + subtest 'recaptcha' => sub { + $mech->get_ok('/alert/list?pc=EH11BB'); + $mech->content_lacks('g-recaptcha'); # GB is default test country + + my $mod_app = Test::MockModule->new('FixMyStreet::App'); + $mod_app->mock('user_country', sub { 'FR' }); + my $mod_lwp = Test::MockModule->new('LWP::UserAgent'); + $mod_lwp->mock('post', sub { HTTP::Response->new(200, 'OK', [], '{ "success": true }') }); + + $mech->get_ok('/alert/list?pc=EH11BB'); + $mech->content_contains('g-recaptcha'); + $mech->submit_form_ok({ with_fields => { rznvy => 'someone@example.org' } }); + }; +}; + done_testing(); diff --git a/templates/web/base/alert/_list.html b/templates/web/base/alert/_list.html index 782989a5c..e6c5183e4 100644 --- a/templates/web/base/alert/_list.html +++ b/templates/web/base/alert/_list.html @@ -62,6 +62,7 @@

  • [% END %] + [% PROCESS 'auth/form_extra.html' %]

    [% loc('Subscribe by email') %]

    diff --git a/templates/web/base/alert/updates.html b/templates/web/base/alert/updates.html index 0fbed2254..40380849f 100644 --- a/templates/web/base/alert/updates.html +++ b/templates/web/base/alert/updates.html @@ -15,6 +15,8 @@

    + [% PROCESS 'auth/form_extra.html' %] +
    [% IF c.user_exists %] [% IF c.user.has_permission_to("contribute_as_another_user", problem.bodies_str_ids) %] diff --git a/templates/web/base/auth/form_extra.html b/templates/web/base/auth/form_extra.html new file mode 100644 index 000000000..e69de29bb diff --git a/templates/web/base/report/display_tools.html b/templates/web/base/report/display_tools.html index 4c79e4b71..532c78ec8 100644 --- a/templates/web/base/report/display_tools.html +++ b/templates/web/base/report/display_tools.html @@ -44,6 +44,7 @@ [% loc('RSS feed of updates to this problem' ) %] [% loc('Receive email when updates are left on this problem.' ) %]

    + [% PROCESS 'auth/form_extra.html' %]
    [% IF c.user_exists %] [% IF permissions.contribute_as_another_user %] diff --git a/templates/web/base/report/form/user_loggedout_by_email.html b/templates/web/base/report/form/user_loggedout_by_email.html index 33526cc46..459428059 100644 --- a/templates/web/base/report/form/user_loggedout_by_email.html +++ b/templates/web/base/report/form/user_loggedout_by_email.html @@ -62,6 +62,8 @@ + [% PROCESS 'auth/form_extra.html' %] +
    diff --git a/templates/web/fixmystreet.com/auth/form_extra.html b/templates/web/fixmystreet.com/auth/form_extra.html new file mode 100644 index 000000000..8e96b447c --- /dev/null +++ b/templates/web/fixmystreet.com/auth/form_extra.html @@ -0,0 +1,4 @@ +[% IF c.cobrand.requires_recaptcha %] + +
    +[% END %] -- cgit v1.2.3 From 1c8f7efec8e04919ad43c63ce9bcf148ee72a2cc Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 3 Jun 2020 15:31:15 +0100 Subject: [Open311] Do not remove any devolved contacts. --- CHANGELOG.md | 1 + perllib/Open311/PopulateServiceList.pm | 6 ++-- t/open311/populate-service-list.t | 64 ++++++++++++++++++++++------------ 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a2466913..d9fe1e282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Fix issue with dashboard report CSV export. #3026 - bin/update-schema PostgreSQL 12 compatibility. #3043 - Make sure category shown in all its groups when reporting. + - Do not remove any devolved contacts. - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/perllib/Open311/PopulateServiceList.pm b/perllib/Open311/PopulateServiceList.pm index 9be17946e..20fca90b3 100644 --- a/perllib/Open311/PopulateServiceList.pm +++ b/perllib/Open311/PopulateServiceList.pm @@ -350,10 +350,10 @@ sub _delete_contacts_not_in_service_list { ); if ($self->_current_body->can_be_devolved) { - # If the body has can_be_devolved switched on, it's most likely a - # combination of Open311/email, so ignore any email addresses. + # If the body has can_be_devolved switched on, ignore any + # contact with its own send method $found_contacts = $found_contacts->search( - { email => { -not_like => '%@%' } } + { send_method => [ "", undef ] }, ); } diff --git a/t/open311/populate-service-list.t b/t/open311/populate-service-list.t index bd837f203..20f092da4 100644 --- a/t/open311/populate-service-list.t +++ b/t/open311/populate-service-list.t @@ -175,33 +175,53 @@ subtest "set multiple groups with groups element" => sub { is_deeply $contact->get_extra->{group}, ['sanitation & cleaning','street'], "groups set correctly"; }; -subtest 'check non open311 contacts marked as deleted' => sub { - FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete(); +$body->update({ can_be_devolved => 1 }); +for my $test ( + { + test => 'check non open311 contacts marked as deleted', + contact_params => { + email => 'contact@example.com', + }, + deleted => 1, + }, + { + test => 'check devolved non open311 contacts not marked as deleted', + contact_params => { + email => 'contact', + send_method => 'Open311', + }, + deleted => 0, + }, +) { + subtest $test->{test} => sub { + FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete(); - my $contact = FixMyStreet::DB->resultset('Contact')->create( - { - body_id => $body->id, - email => 'contact@example.com', - category => 'An old category', - state => 'confirmed', - editor => $0, - whenedited => \'current_timestamp', - note => 'test contact', - } - ); + my $contact = FixMyStreet::DB->resultset('Contact')->create( + { + body_id => $body->id, + category => 'An old category', + state => 'confirmed', + editor => $0, + whenedited => \'current_timestamp', + note => 'test contact', + %{$test->{contact_params}}, + } + ); - my $service_list = get_xml_simple_object( get_standard_xml() ); + my $service_list = get_xml_simple_object( get_standard_xml() ); - my $processor = Open311::PopulateServiceList->new(); - $processor->_current_body( $body ); - $processor->process_services( $service_list ); + my $processor = Open311::PopulateServiceList->new(); + $processor->_current_body( $body ); + $processor->process_services( $service_list ); - my $contact_count = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->count(); - is $contact_count, 4, 'correct number of contacts'; + my $contact_count = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->count(); + is $contact_count, 4, 'correct number of contacts'; - $contact_count = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id, state => 'deleted' } )->count(); - is $contact_count, 1, 'correct number of deleted contacts'; -}; + $contact_count = FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id, state => 'deleted' } )->count(); + is $contact_count, $test->{deleted}, 'correct number of deleted contacts'; + }; +} +$body->update({ can_be_devolved => 0 }); subtest 'check email changed if matching category' => sub { FixMyStreet::DB->resultset('Contact')->search( { body_id => $body->id } )->delete(); -- cgit v1.2.3 From ce5588038dfafb2db53c7de7e0d34e8c59bed8ba Mon Sep 17 00:00:00 2001 From: M Somerville Date: Fri, 29 May 2020 11:09:09 +0100 Subject: Small refactor of around.t, add "lat,lon" test. --- t/app/controller/around.t | 95 ++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 63 deletions(-) diff --git a/t/app/controller/around.t b/t/app/controller/around.t index 6e49c6f29..be8b0df9e 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -53,6 +53,11 @@ foreach my $test ( }; } +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'fixmystreet', + MAPIT_URL => 'http://mapit.uk/', +}, sub { + # check that exact queries result in the correct lat,lng foreach my $test ( { @@ -69,16 +74,18 @@ foreach my $test ( { subtest "check lat/lng for '$test->{pc}'" => sub { $mech->get_ok('/'); - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, - "good location" ); - }; + $mech->submit_form_ok( { with_fields => { pc => $test->{pc} } }, + "good location" ); is_deeply $mech->page_errors, [], "no errors for pc '$test->{pc}'"; is_deeply $mech->extract_location, $test, "got expected location for pc '$test->{pc}'"; + $mech->get_ok('/'); + my $pc = "$test->{latitude},$test->{longitude}"; + $mech->submit_form_ok( { with_fields => { pc => $pc } }, + "good location" ); + is_deeply $mech->page_errors, [], "no errors for pc '$pc'"; + is_deeply $mech->extract_location, { %$test, pc => $pc }, + "got expected location for pc '$pc'"; }; } @@ -93,10 +100,10 @@ my @edinburgh_problems = $mech->create_problems_for_body( 5, $body_edin_id, 'Aro subtest 'check lookup by reference' => sub { $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => 'ref:12345' } }, 'bad ref'); + $mech->submit_form_ok( { with_fields => { pc => '12345' } }, 'bad ref'); $mech->content_contains('Searching found no reports'); my $id = $edinburgh_problems[0]->id; - $mech->submit_form_ok( { with_fields => { pc => "ref:$id" } }, 'good ref'); + $mech->submit_form_ok( { with_fields => { pc => $id } }, 'good ref'); is $mech->uri->path, "/report/$id", "redirected to report page"; }; @@ -106,19 +113,14 @@ subtest 'check lookup by reference does not show non_public reports' => sub { }); my $id = $edinburgh_problems[0]->id; $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => "ref:$id" } }, 'non_public ref'); + $mech->submit_form_ok( { with_fields => { pc => $id } }, 'non_public ref'); $mech->content_contains('Searching found no reports'); }; subtest 'check non public reports are not displayed on around page' => sub { $mech->get_ok('/'); - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, - "good location" ); - }; + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, + "good location" ); $mech->content_contains( "Around page Test 3 for $body_edin_id", 'problem to be marked non public visible' ); @@ -126,26 +128,16 @@ subtest 'check non public reports are not displayed on around page' => sub { ok $private->update( { non_public => 1 } ), 'problem marked non public'; $mech->get_ok('/'); - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, - "good location" ); - }; + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, + "good location" ); $mech->content_lacks( "Around page Test 3 for $body_edin_id", 'problem marked non public is not visible' ); }; subtest 'check missing body message not shown when it does not need to be' => sub { $mech->get_ok('/'); - FixMyStreet::override_config { - ALLOWED_COBRANDS => 'fixmystreet', - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, - "good location" ); - }; + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, + "good location" ); $mech->content_lacks('yet have details for the other councils that cover this location'); }; @@ -162,24 +154,14 @@ for my $permission ( qw/ report_inspect report_mark_private/ ) { }); $mech->get_ok('/'); - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, - "good location" ); - }; + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, + "good location" ); $mech->content_contains( "Around page Test 3 for $body_edin_id", 'problem marked non public is visible' ); $mech->content_contains( "Around page Test 2 for $body_edin_id", 'problem marked public is visible' ); - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->get_ok('/around?pc=EH1+1BB&status=non_public'); - }; + $mech->get_ok('/around?pc=EH1+1BB&status=non_public'); $mech->content_contains( "Around page Test 3 for $body_edin_id", 'problem marked non public is visible' ); $mech->content_lacks( "Around page Test 2 for $body_edin_id", @@ -193,24 +175,14 @@ for my $permission ( qw/ report_inspect report_mark_private/ ) { }); $mech->get_ok('/'); - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, - "good location" ); - }; + $mech->submit_form_ok( { with_fields => { pc => 'EH1 1BB' } }, + "good location" ); $mech->content_lacks( "Around page Test 3 for $body_edin_id", 'problem marked non public is not visible' ); $mech->content_contains( "Around page Test 2 for $body_edin_id", 'problem marked public is visible' ); - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ { 'fixmystreet' => '.' } ], - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->get_ok('/around?pc=EH1+1BB&status=non_public'); - }; + $mech->get_ok('/around?pc=EH1+1BB&status=non_public'); $mech->content_lacks( "Around page Test 3 for $body_edin_id", 'problem marked non public is not visible' ); $mech->content_lacks( "Around page Test 2 for $body_edin_id", @@ -230,17 +202,14 @@ subtest 'check assigned-only list items do not display shortlist buttons' => sub $user->update({ from_body => $body }); $user->user_body_permissions->find_or_create({ body => $body, permission_type => 'planned_reports' }); - FixMyStreet::override_config { - ALLOWED_COBRANDS => 'fixmystreet', - MAPIT_URL => 'http://mapit.uk/', - }, sub { - $mech->get_ok('/around?pc=EH1+1BB'); - }; + $mech->get_ok('/around?pc=EH1+1BB'); $mech->content_contains('shortlist-add-' . $edinburgh_problems[4]->id); $mech->content_lacks('shortlist-add-' . $edinburgh_problems[3]->id); $mech->content_lacks('shortlist-add-' . $edinburgh_problems[1]->id); }; +}; # End big override_config + my $body = $mech->create_body_ok(2237, "Oxfordshire"); subtest 'check category, status and extra filtering works on /around' => sub { -- cgit v1.2.3 From abc63a85b47bbc6b9e81c8256a226864ac7fe0ce Mon Sep 17 00:00:00 2001 From: M Somerville Date: Fri, 29 May 2020 11:09:36 +0100 Subject: Add Open Location Codes support to search box. --- CHANGELOG.md | 2 ++ cpanfile | 1 + cpanfile.snapshot | 9 +++++++++ perllib/FixMyStreet/App/Controller/Location.pm | 25 +++++++++++++++++++++++++ t/Mock/Nominatim.pm | 5 +++++ t/app/controller/around.t | 25 +++++++++++++++++++++++++ 6 files changed, 67 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9fe1e282..1bd70243c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## Releases * Unreleased + - New features: + - Add Open Location Codes support to search box. #3047 - Bugfixes: - Fix issue with dashboard report CSV export. #3026 - bin/update-schema PostgreSQL 12 compatibility. #3043 diff --git a/cpanfile b/cpanfile index 0474f4691..1415a7af5 100644 --- a/cpanfile +++ b/cpanfile @@ -80,6 +80,7 @@ requires 'Error'; requires 'FCGI'; # Required by e.g. Plack::Handler::FCGI requires 'File::Find'; requires 'File::Path'; +requires 'Geo::OLC'; requires 'Geography::NationalGrid', mirror => 'https://cpan.metacpan.org/'; requires 'Getopt::Long::Descriptive', '0.105'; diff --git a/cpanfile.snapshot b/cpanfile.snapshot index 53ce1f120..edc125960 100644 --- a/cpanfile.snapshot +++ b/cpanfile.snapshot @@ -3116,6 +3116,15 @@ DISTRIBUTIONS perl 5.014000 strict 0 warnings 0 + Geo-OLC-1 + pathname: J/JG/JGREELY/Geo-OLC-1.tar.gz + provides: + Geo::OLC 1 + requirements: + ExtUtils::MakeMaker 0 + List::Util 1 + Test::More 0 + perl 5.010001 Geography-NationalGrid-1.6 pathname: P/PK/PKENT/Geography-NationalGrid-1.6.tar.gz provides: diff --git a/perllib/FixMyStreet/App/Controller/Location.pm b/perllib/FixMyStreet/App/Controller/Location.pm index 416fb942a..3869ef8d3 100644 --- a/perllib/FixMyStreet/App/Controller/Location.pm +++ b/perllib/FixMyStreet/App/Controller/Location.pm @@ -6,6 +6,7 @@ BEGIN {extends 'Catalyst::Controller'; } use Encode; use FixMyStreet::Geocode; +use Geo::OLC; use Try::Tiny; use Utils; @@ -74,6 +75,30 @@ sub determine_location_from_pc : Private { map { Utils::truncate_coordinate($_) } ($1, $2); return $c->forward( 'check_location' ); } + + if (Geo::OLC::is_full($pc)) { + my $ref = Geo::OLC::decode($pc); + ($c->stash->{latitude}, $c->stash->{longitude}) = + map { Utils::truncate_coordinate($_) } @{$ref->{center}}; + return $c->forward( 'check_location' ); + } + + if ($pc =~ /^\s*([2-9CFGHJMPQRVWX]{4,6}\+[2-9CFGHJMPQRVWX]{2,3})\s+(.*)$/i) { + my ($code, $rest) = ($1, $2); + my ($lat, $lon, $error) = FixMyStreet::Geocode::lookup($rest, $c); + if (ref($error) eq 'ARRAY') { # Just take the first result + $lat = $error->[0]{latitude}; + $lon = $error->[0]{longitude}; + } + if (defined $lat && defined $lon) { + $code = Geo::OLC::recover_nearest($code, $lat, $lon); + my $ref = Geo::OLC::decode($code); + ($c->stash->{latitude}, $c->stash->{longitude}) = + map { Utils::truncate_coordinate($_) } @{$ref->{center}}; + return $c->forward( 'check_location' ); + } + } + if ( $c->cobrand->country eq 'GB' && $pc =~ /^([A-Z])([A-Z])([\d\s]{4,})$/i) { if (my $convert = gridref_to_latlon( $1, $2, $3 )) { ($c->stash->{latitude}, $c->stash->{longitude}) = diff --git a/t/Mock/Nominatim.pm b/t/Mock/Nominatim.pm index 806ebbfd3..d108256f5 100644 --- a/t/Mock/Nominatim.pm +++ b/t/Mock/Nominatim.pm @@ -1,6 +1,7 @@ package t::Mock::Nominatim; use JSON::MaybeXS; +use LWP::Protocol::PSGI; use Web::Simple; has json => ( @@ -35,6 +36,10 @@ sub query { {"osm_type"=>"way","osm_id"=>"4684282","lat"=>"55.9504009","lon"=>"-3.1858425","display_name"=>"High Street, Old Town, City of Ed\x{ed}nburgh, Scotland, EH1 1SP, United Kingdom","class"=>"highway","type"=>"tertiary","importance"=>0.55892577838734}, {"osm_type"=>"node","osm_id"=>"27424410","lat"=>"55.8596449","lon"=>"-4.240377","display_name"=>"High Street, Collegelands, Merchant City, Glasgow, Glasgow City, Scotland, G, United Kingdom","class"=>"railway","type"=>"station","importance"=>0.53074299592768} ]; + } elsif ($q eq 'edinburgh') { + return [ + {"osm_type"=>"node","osm_id"=>"17898859","lat"=>"55.9533456","lon"=>"-3.1883749","display_name"=>"Edinburgh","class"=>"place","type"=>"place:city","importance"=>0.676704} + ]; } return []; } diff --git a/t/app/controller/around.t b/t/app/controller/around.t index be8b0df9e..784801517 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -9,6 +9,7 @@ use constant MIN_ZOOM_LEVEL => 88; package main; use Test::MockModule; +use t::Mock::Nominatim; use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; @@ -89,6 +90,30 @@ foreach my $test ( }; } +subtest "check lat/lng for full plus code" => sub { + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => "9C7RXR26+R5" } } ); + is_deeply $mech->page_errors, [], "no errors for plus code"; + is_deeply $mech->extract_location, { + pc => "9C7RXR26+R5", + latitude => 55.952063, + longitude => -3.189562, + }, + "got expected location for full plus code"; +}; + +subtest "check lat/lng for short plus code" => sub { + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => "XR26+R5 Edinburgh" } } ); + is_deeply $mech->page_errors, [], "no errors for plus code"; + is_deeply $mech->extract_location, { + pc => "XR26+R5 Edinburgh", + latitude => 55.952063, + longitude => -3.189562, + }, + "got expected location for short plus code"; +}; + my $body_edin_id = $mech->create_body_ok(2651, 'City of Edinburgh Council')->id; my $body_west_id = $mech->create_body_ok(2504, 'Westminster City Council')->id; -- cgit v1.2.3 From f706b89ef794ed3d16c015842d6052d03a5d39ef Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 9 Jun 2020 13:46:59 +0100 Subject: Fix double encoding of per-category templates. encode_json encodes to UTF-8, and then he output was being encoded again. --- CHANGELOG.md | 1 + perllib/FixMyStreet/Roles/ContactExtra.pm | 2 +- t/app/model/responsetemplate.t | 8 +++++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd70243c..733ad04c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - bin/update-schema PostgreSQL 12 compatibility. #3043 - Make sure category shown in all its groups when reporting. - Do not remove any devolved contacts. + - Fix double encoding of per-category templates. - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/perllib/FixMyStreet/Roles/ContactExtra.pm b/perllib/FixMyStreet/Roles/ContactExtra.pm index e78d9b53f..73b1d3e6a 100644 --- a/perllib/FixMyStreet/Roles/ContactExtra.pm +++ b/perllib/FixMyStreet/Roles/ContactExtra.pm @@ -46,7 +46,7 @@ sub by_categories { || (grep { $_->contact_id == $contact->get_column('id') } $_->$join_table) } @results; @ts = $rs->map_extras(@ts); - $extras{$contact->category} = encode_json(\@ts); + $extras{$contact->category} = JSON::XS->new->encode(\@ts); } return \%extras; diff --git a/t/app/model/responsetemplate.t b/t/app/model/responsetemplate.t index 9efc7e3b4..29c41a0e0 100644 --- a/t/app/model/responsetemplate.t +++ b/t/app/model/responsetemplate.t @@ -1,3 +1,4 @@ +use utf8; use FixMyStreet::TestMech; use JSON::MaybeXS; @@ -7,7 +8,7 @@ my $area_id = 2651; my $body = $mech->create_body_ok($area_id, 'Edinburgh Council'); my $c1 = $mech->create_contact_ok(category => 'Potholes', body_id => $body->id, email => 'p'); my $c2 = $mech->create_contact_ok(category => 'Graffiti', body_id => $body->id, email => 'g'); -my $t1 = FixMyStreet::DB->resultset('ResponseTemplate')->create({ body_id => $body->id, title => "Title 1", text => "Text 1" }); +my $t1 = FixMyStreet::DB->resultset('ResponseTemplate')->create({ body_id => $body->id, title => "Title 1", text => "Text 1 ⛄" }); my $t2 = FixMyStreet::DB->resultset('ResponseTemplate')->create({ body_id => $body->id, title => "Title 2", text => "Text 2", state => 'investigating' }); my $t3 = FixMyStreet::DB->resultset('ResponseTemplate')->create({ body_id => $body->id, title => "Title 3", text => "Text 3" }); $t1->add_to_contacts($c1); @@ -17,12 +18,13 @@ my @contacts = FixMyStreet::DB->resultset('Contact')->not_deleted->search( { bod subtest 'by_categories returns allresponse templates grouped by category' => sub { my $templates = FixMyStreet::DB->resultset('ResponseTemplate')->by_categories(\@contacts, body_id => $body->id); - my $potholes = decode_json($templates->{Potholes}); - my $graffiti = decode_json($templates->{Graffiti}); + my $potholes = JSON::MaybeXS->new->decode($templates->{Potholes}); + my $graffiti = JSON::MaybeXS->new->decode($templates->{Graffiti}); is scalar @$potholes, 2, 'Potholes have 2 templates'; is scalar @$graffiti, 2, 'Graffiti has 2 templates'; is $graffiti->[0]->{state}, 'investigating', 'Graffiti first template has right state'; + is $potholes->[0]->{id}, 'Text 1 ⛄', 'Pothole first template has right text'; }; done_testing; -- cgit v1.2.3 From d56c3d7396ddd71b5cbf8e69d8cb0c3d7c400466 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 9 Jun 2020 14:43:30 +0100 Subject: Use problem name in reporting to match display. --- templates/web/base/contact/index.html | 2 +- templates/web/bathnes/contact/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/web/base/contact/index.html b/templates/web/base/contact/index.html index 05a24160f..4e65ce674 100644 --- a/templates/web/base/contact/index.html +++ b/templates/web/base/contact/index.html @@ -63,7 +63,7 @@ [% IF problem.anonymous %] [% tprintf( loc('Reported anonymously at %s'), prettify_dt( problem.confirmed ) ) %] [% ELSE %] - [% tprintf( loc('Reported by %s at %s'), problem.user.name, prettify_dt( problem.confirmed ) ) | html %] + [% tprintf( loc('Reported by %s at %s'), problem.name, prettify_dt( problem.confirmed ) ) | html %] [% END %] diff --git a/templates/web/bathnes/contact/index.html b/templates/web/bathnes/contact/index.html index f0589bd9e..aaa0d3c57 100644 --- a/templates/web/bathnes/contact/index.html +++ b/templates/web/bathnes/contact/index.html @@ -63,7 +63,7 @@ [% IF problem.anonymous %] [% tprintf( loc('Reported anonymously at %s'), prettify_dt( problem.confirmed ) ) %] [% ELSE %] - [% tprintf( loc('Reported by %s at %s'), problem.user.name, prettify_dt( problem.confirmed ) ) | html %] + [% tprintf( loc('Reported by %s at %s'), problem.name, prettify_dt( problem.confirmed ) ) | html %] [% END %] -- cgit v1.2.3 From 20cc280dd9f4cd17dadd2f540c6e8c0127ea350d Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 9 Jun 2020 14:57:08 +0100 Subject: [Open311] Use indexed way of searching by body. --- perllib/FixMyStreet/App/Controller/Open311.pm | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm index b4b5d5e3a..b87d3d11c 100644 --- a/perllib/FixMyStreet/App/Controller/Open311.pm +++ b/perllib/FixMyStreet/App/Controller/Open311.pm @@ -210,7 +210,7 @@ sub output_requests : Private { }; # Look up categories for this council or councils - my $problems = $c->cobrand->problems->search( $criteria, $attr ); + my $problems = $c->stash->{rs}->search( $criteria, $attr ); my %statusmap = ( map( { $_ => 'open' } FixMyStreet::DB::Result::Problem->open_states() ), @@ -325,22 +325,6 @@ sub get_requests : Private { 'open' => [ FixMyStreet::DB::Result::Problem->open_states() ], 'closed' => [ FixMyStreet::DB::Result::Problem->fixed_states(), 'closed' ], }->{$value}; - } elsif ( 'agency_responsible' eq $param ) { - my @valuelist; - for my $agency (split(/\|/, $value)) { - unless ($agency =~ m/^(\d+)$/) { - $c->detach( 'error', [ - sprintf(_('Invalid agency_responsible value %s'), - $value) - ] ); - } - my $agencyid = $1; - # FIXME This seem to match the wrong entries - # some times. Not sure when or why - my $re = "(\\y$agencyid\\y|^$agencyid\\y|\\y$agencyid\$)"; - push(@valuelist, $re); - } - $value = \@valuelist; } elsif ( 'has_photo' eq $param ) { $value = undef; $op = '!=' if 'true' eq $value; @@ -363,6 +347,11 @@ sub get_requests : Private { $criteria->{confirmed} = { '<', $c->get_param('end_date') }; } + $c->stash->{rs} = $c->cobrand->problems; + if (my $bodies = $c->get_param('agency_responsible')) { + $c->stash->{rs} = $c->stash->{rs}->to_body([ split(/\|/, $bodies) ]); + } + if ('rss' eq $c->stash->{format}) { $c->stash->{type} = 'new_problems'; $c->forward( '/rss/lookup_type' ); @@ -384,7 +373,7 @@ sub rss_query : Private { rows => $limit }; - my $problems = $c->cobrand->problems->search( $criteria, $attr ); + my $problems = $c->stash->{rs}->search( $criteria, $attr ); $c->stash->{problems} = $problems; } @@ -411,6 +400,7 @@ sub get_request : Private { id => $id, non_public => 0, }; + $c->stash->{rs} = $c->cobrand->problems; $c->forward( 'output_requests', [ $criteria ] ); } -- cgit v1.2.3 From e1c8f0dadbcdf1f8511ec673c2e6892207fca328 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 9 Jun 2020 14:57:22 +0100 Subject: [Open311] Return reports in all closed states. --- CHANGELOG.md | 1 + perllib/FixMyStreet/App/Controller/Open311.pm | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bd70243c..6edef1831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - bin/update-schema PostgreSQL 12 compatibility. #3043 - Make sure category shown in all its groups when reporting. - Do not remove any devolved contacts. + - Return reports in all closed states through Open311. - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm index b87d3d11c..31cf84874 100644 --- a/perllib/FixMyStreet/App/Controller/Open311.pm +++ b/perllib/FixMyStreet/App/Controller/Open311.pm @@ -323,7 +323,7 @@ sub get_requests : Private { if ( 'status' eq $param ) { $value = { 'open' => [ FixMyStreet::DB::Result::Problem->open_states() ], - 'closed' => [ FixMyStreet::DB::Result::Problem->fixed_states(), 'closed' ], + 'closed' => [ FixMyStreet::DB::Result::Problem->fixed_states(), FixMyStreet::DB::Result::Problem->closed_states() ], }->{$value}; } elsif ( 'has_photo' eq $param ) { $value = undef; -- cgit v1.2.3 From 6088d10642da56cbea96446da9d9cd87c922ede5 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 9 Jun 2020 15:18:16 +0100 Subject: [Highways England] Anonymize all names on cobrand. --- perllib/FixMyStreet/App/Controller/JSON.pm | 1 + perllib/FixMyStreet/App/Controller/Open311.pm | 2 ++ perllib/FixMyStreet/App/Controller/Report.pm | 2 ++ perllib/FixMyStreet/Cobrand/HighwaysEngland.pm | 9 +++++++++ t/cobrand/highwaysengland.t | 18 +++++++++++------- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/JSON.pm b/perllib/FixMyStreet/App/Controller/JSON.pm index a7913bb4b..4657fcf2c 100644 --- a/perllib/FixMyStreet/App/Controller/JSON.pm +++ b/perllib/FixMyStreet/App/Controller/JSON.pm @@ -101,6 +101,7 @@ sub problems : Local { } ); foreach my $problem (@problems) { + $c->cobrand->call_hook(munge_problem_list => $problem); $problem->name( '' ) if $problem->anonymous == 1; $problem->service( 'Web interface' ) if $problem->service eq ''; my $body_names = $problem->body_names; diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm index 31cf84874..73a91a62a 100644 --- a/perllib/FixMyStreet/App/Controller/Open311.pm +++ b/perllib/FixMyStreet/App/Controller/Open311.pm @@ -220,6 +220,8 @@ sub output_requests : Private { my @problemlist; while ( my $problem = $problems->next ) { + $c->cobrand->call_hook(munge_problem_list => $problem); + my $id = $problem->id; $problem->service( 'Web interface' ) unless $problem->service; diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 4d8794c59..4f9825cae 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -197,6 +197,7 @@ sub load_problem_or_display_error : Private { } } + $c->cobrand->call_hook(munge_problem_list => $problem); $c->stash->{problem} = $problem; if ( $c->user_exists && $c->user->can_moderate($problem) ) { $c->stash->{problem_original} = $problem->find_or_new_related( @@ -251,6 +252,7 @@ sub load_updates : Private { my @combined; my %questionnaires_with_updates; while (my $update = $updates->next) { + $c->cobrand->call_hook(munge_update_list => $update); push @combined, [ $update->confirmed, $update ]; if (my $qid = $update->get_extra_metadata('questionnaire_id')) { $questionnaires_with_updates{$qid} = $update; diff --git a/perllib/FixMyStreet/Cobrand/HighwaysEngland.pm b/perllib/FixMyStreet/Cobrand/HighwaysEngland.pm index ed58eb4f7..c282ac5ea 100644 --- a/perllib/FixMyStreet/Cobrand/HighwaysEngland.pm +++ b/perllib/FixMyStreet/Cobrand/HighwaysEngland.pm @@ -29,6 +29,15 @@ sub users_restriction { FixMyStreet::Cobrand::UKCouncils::users_restriction($_[0 sub updates_restriction { FixMyStreet::Cobrand::UKCouncils::updates_restriction($_[0], $_[1]) } sub base_url { FixMyStreet::Cobrand::UKCouncils::base_url($_[0]) } +sub munge_problem_list { + my ($self, $problem) = @_; + $problem->anonymous(1); +} +sub munge_update_list { + my ($self, $update) = @_; + $update->anonymous(1); +} + sub admin_allow_user { my ( $self, $user ) = @_; return 1 if $user->is_superuser; diff --git a/t/cobrand/highwaysengland.t b/t/cobrand/highwaysengland.t index f6400ea7c..0b71c613f 100644 --- a/t/cobrand/highwaysengland.t +++ b/t/cobrand/highwaysengland.t @@ -29,12 +29,11 @@ my $highways = $mech->create_body_ok(2234, 'Highways England'); $mech->create_contact_ok(email => 'highways@example.com', body_id => $highways->id, category => 'Pothole'); -# Br1 3UH -subtest "check where heard from saved" => sub { - FixMyStreet::override_config { - ALLOWED_COBRANDS => 'highwaysengland', - MAPIT_URL => 'http://mapit.uk/', - }, sub { +FixMyStreet::override_config { + ALLOWED_COBRANDS => 'highwaysengland', + MAPIT_URL => 'http://mapit.uk/', +}, sub { + subtest "check where heard from saved" => sub { $mech->get_ok('/around'); $mech->submit_form_ok( { with_fields => { pc => 'M1, J16', } }, "submit location" ); $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, @@ -64,7 +63,12 @@ subtest "check where heard from saved" => sub { like $mech->get_text_body_from_email($email), qr/Heard from: Facebook/, 'where hear included in email' }; -}; + subtest "check anonymous display" => sub { + my ($problem) = $mech->create_problems_for_body(1, $highways->id, 'Title'); + $mech->get_ok('/report/' . $problem->id); + $mech->content_lacks('Reported by Test User at'); + }; +}; done_testing(); -- cgit v1.2.3 From 931f391ddc521a5dc76a2747a1cf3663431631e8 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 9 Jun 2020 17:17:36 +0100 Subject: Add recaptcha config option to example file. --- conf/general.yml-example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/conf/general.yml-example b/conf/general.yml-example index 91507b03d..26399bb81 100644 --- a/conf/general.yml-example +++ b/conf/general.yml-example @@ -244,6 +244,9 @@ TESTING_COUNCILS: '' # if you're using Message Manager, include the URL here (see https://github.com/mysociety/message-manager/) MESSAGE_MANAGER_URL: '' +# If you set up using recaptcha, you can put its configuration keys here +RECAPTCHA: '' + # If you want to use SMS login or 'report as' with just a phone number, you'll # need to set the site's two-digit ISO 3166 country code (e.g. GB) here. PHONE_COUNTRY: '' -- cgit v1.2.3 From 6b2af7944bb7d07411fe9375fef535c8248b46d1 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 9 Jun 2020 17:26:10 +0100 Subject: [fixmystreet.com] Include script nonce in new script element. --- templates/web/fixmystreet.com/auth/form_extra.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/web/fixmystreet.com/auth/form_extra.html b/templates/web/fixmystreet.com/auth/form_extra.html index 8e96b447c..92cc31050 100644 --- a/templates/web/fixmystreet.com/auth/form_extra.html +++ b/templates/web/fixmystreet.com/auth/form_extra.html @@ -1,4 +1,4 @@ [% IF c.cobrand.requires_recaptcha %] - +
    [% END %] -- cgit v1.2.3 From 476781624d03b8ae57f456206465fd3fb2e45e97 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 10 Jun 2020 14:31:10 +0100 Subject: [TfL] Update front page text. --- templates/web/tfl/front/pre-steps.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/templates/web/tfl/front/pre-steps.html b/templates/web/tfl/front/pre-steps.html index ee22f4978..1dead5c89 100644 --- a/templates/web/tfl/front/pre-steps.html +++ b/templates/web/tfl/front/pre-steps.html @@ -6,6 +6,7 @@ safety critical issues.
    - We will continue to serve you the best that we can but urge everyone not to - make journeys unless absolutely necessary. + If you are concerned about pavement overcrowding during social distancing + on the Transport for London Road Network, you can now report that to us + here.

    -- cgit v1.2.3 From fa49a5ec148e975d98df1138e0688410cf81d9b3 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 2 Jun 2020 13:05:25 +0100 Subject: [Bath,Bristol] No need for special hidden overrides. --- perllib/FixMyStreet/Cobrand/BathNES.pm | 8 -------- perllib/FixMyStreet/Cobrand/Bristol.pm | 2 ++ perllib/FixMyStreet/Cobrand/UK.pm | 6 ------ 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/BathNES.pm b/perllib/FixMyStreet/Cobrand/BathNES.pm index 06095734b..10e74056f 100644 --- a/perllib/FixMyStreet/Cobrand/BathNES.pm +++ b/perllib/FixMyStreet/Cobrand/BathNES.pm @@ -90,14 +90,6 @@ sub send_questionnaires { 0 } sub default_map_zoom { 3 } -sub category_extra_hidden { - my ($self, $meta) = @_; - my $code = $meta->{code}; - # These two are used in the non-Open311 'Street light fault' category. - return 1 if $code eq 'unitid' || $code eq 'asset_details'; - return $self->SUPER::category_extra_hidden($meta); -} - sub available_permissions { my $self = shift; diff --git a/perllib/FixMyStreet/Cobrand/Bristol.pm b/perllib/FixMyStreet/Cobrand/Bristol.pm index 6e3160c89..7e85a3315 100644 --- a/perllib/FixMyStreet/Cobrand/Bristol.pm +++ b/perllib/FixMyStreet/Cobrand/Bristol.pm @@ -68,8 +68,10 @@ sub open311_contact_meta_override { $service->{group} = []; my %server_set = (easting => 1, northing => 1); + my %hidden_field = (usrn => 1, asset_id => 1); foreach (@$meta) { $_->{automated} = 'server_set' if $server_set{$_->{code}}; + $_->{automated} = 'hidden_field' if $hidden_field{$_->{code}}; } } diff --git a/perllib/FixMyStreet/Cobrand/UK.pm b/perllib/FixMyStreet/Cobrand/UK.pm index 4c62dd538..322b257a7 100644 --- a/perllib/FixMyStreet/Cobrand/UK.pm +++ b/perllib/FixMyStreet/Cobrand/UK.pm @@ -408,12 +408,6 @@ sub lookup_by_ref_regex { return qr/^\s*(\d+)\s*$/; } -sub category_extra_hidden { - my ($self, $meta) = @_; - return 1 if $meta->{code} eq 'usrn' || $meta->{code} eq 'asset_id'; - return $self->SUPER::category_extra_hidden($meta); -} - sub report_new_munge_before_insert { my ($self, $report) = @_; -- cgit v1.2.3 From 4d0a57b172c44d4f880bb560f7a876aff3ccae27 Mon Sep 17 00:00:00 2001 From: Martin Wright Date: Fri, 20 Mar 2020 14:07:54 +0000 Subject: [Bexley] Redesign. Make sure spans in navbar are legible at all times. --- web/cobrands/bexley/_colours.scss | 6 ++--- web/cobrands/bexley/layout.scss | 47 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/web/cobrands/bexley/_colours.scss b/web/cobrands/bexley/_colours.scss index 615ef65a4..453623fb5 100644 --- a/web/cobrands/bexley/_colours.scss +++ b/web/cobrands/bexley/_colours.scss @@ -40,9 +40,9 @@ $primary_text: $text_black; $base_bg: $white; $base_fg: $text_black; -$nav_background_colour: $fuchsia; -$nav_colour: $white; -$nav_hover_background_colour: darken($fuchsia, 10%); +$nav_background_colour: $white; +$nav_colour: $text_black; +$nav_hover_background_colour: $fuchsia_lt; $col_click_map: $aqua; diff --git a/web/cobrands/bexley/layout.scss b/web/cobrands/bexley/layout.scss index 948b01443..c18574bd5 100644 --- a/web/cobrands/bexley/layout.scss +++ b/web/cobrands/bexley/layout.scss @@ -6,6 +6,10 @@ body.twothirdswidthpage .content .sticky-sidebar aside { top: 10em; } +#site-header { + border-top: 7px solid $fuchsia; +} + #site-logo { width: 208px; height: 80px; @@ -18,10 +22,47 @@ body.twothirdswidthpage .content .sticky-sidebar aside { color: $fuchsia_lt; } +.frontpage { + .nav-menu--main { + margin: 3em 0 0 0; + } +} + .nav-menu--main { - span { - background-color: $nav_hover_background_colour; - } + a { + &:hover, + &:active, + &:focus { + color: $text_black; + } + } + a.report-a-problem-btn, + span.report-a-problem-btn, + span { + background-color: $nav_hover_background_colour; + color: $text_black; + border-radius: 0; + &:hover { + color: $fuchsia; + cursor: pointer; + } + } + a[href='/auth'] { + position: absolute; + top: 0; + right: 1em; + .frontpage & { + right: 0; + } + background-color: $fuchsia; + color: $white; + padding: 0 0.75em; + &:hover, + &:active, + &:focus { + color: $white; + } + } } .general-notes { -- cgit v1.2.3 From 368d1513eca9423edaba3ddff20af2364d54b7c2 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 11 Jun 2020 16:42:34 +0100 Subject: [TfL] Add new Streetspace category, TLRN-only. --- perllib/FixMyStreet/Cobrand/TfL.pm | 1 + web/cobrands/tfl/assets.js | 1 + 2 files changed, 2 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/TfL.pm b/perllib/FixMyStreet/Cobrand/TfL.pm index da762e386..c37b9a0e5 100644 --- a/perllib/FixMyStreet/Cobrand/TfL.pm +++ b/perllib/FixMyStreet/Cobrand/TfL.pm @@ -494,6 +494,7 @@ sub _tlrn_categories { [ "Single Light out (street light)", "Standing water", "Street Light - Equipment damaged, pole leaning", + "Streetspace Suggestions and Feedback", "Unstable hoardings", "Unstable scaffolding", "Worn out road markings", diff --git a/web/cobrands/tfl/assets.js b/web/cobrands/tfl/assets.js index b47769b6f..9eb197da8 100644 --- a/web/cobrands/tfl/assets.js +++ b/web/cobrands/tfl/assets.js @@ -72,6 +72,7 @@ var tlrn_categories = [ "Single Light out (street light)", "Standing water", "Street Light - Equipment damaged, pole leaning", + "Streetspace Suggestions and Feedback", "Unstable hoardings", "Unstable scaffolding", "Worn out road markings" -- cgit v1.2.3 From 251acf7799e78fd46a39154f0381c7470d5542a9 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 11 Jun 2020 17:25:49 +0100 Subject: Skip accounts without email for inactive emails. --- CHANGELOG.md | 1 + perllib/FixMyStreet/Script/Inactive.pm | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf3bfae5d..2413892c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Do not remove any devolved contacts. - Fix double encoding of per-category templates. - Return reports in all closed states through Open311. + - Skip accounts without email when sending inactive emails. - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/perllib/FixMyStreet/Script/Inactive.pm b/perllib/FixMyStreet/Script/Inactive.pm index 4d28057d4..6b3372a2b 100644 --- a/perllib/FixMyStreet/Script/Inactive.pm +++ b/perllib/FixMyStreet/Script/Inactive.pm @@ -185,6 +185,7 @@ sub email_inactive_users { }); while (my $user = $users->next) { next if $user->get_extra_metadata('inactive_email_sent'); + next unless $user->email && $user->email_verified; say "Emailing user #" . $user->id if $self->verbose; next if $self->dry_run; -- cgit v1.2.3 From b39b8dd5d579b195da2bb2e81bbacea552e09216 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Mon, 2 Mar 2020 14:14:56 +0000 Subject: [UK Councils] Enable private photo uploading on cobrands whose backend supports it For https://github.com/mysociety/fixmystreet-commercial/issues/1778 --- perllib/FixMyStreet/Cobrand/IsleOfWight.pm | 7 +++++++ perllib/FixMyStreet/Cobrand/Lincolnshire.pm | 7 +++++++ perllib/FixMyStreet/Cobrand/Peterborough.pm | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm index ee1a25465..08741639c 100644 --- a/perllib/FixMyStreet/Cobrand/IsleOfWight.pm +++ b/perllib/FixMyStreet/Cobrand/IsleOfWight.pm @@ -77,6 +77,13 @@ sub open311_pre_send { # Make sure fetched report description isn't shown. sub filter_report_description { "" } +around 'open311_config' => sub { + my ($orig, $self, $row, $h, $params) = @_; + + $params->{upload_files} = 1; + $self->$orig($row, $h, $params); +}; + sub open311_munge_update_params { my ($self, $params, $comment, $body) = @_; diff --git a/perllib/FixMyStreet/Cobrand/Lincolnshire.pm b/perllib/FixMyStreet/Cobrand/Lincolnshire.pm index ee40bb173..d1fe319e1 100644 --- a/perllib/FixMyStreet/Cobrand/Lincolnshire.pm +++ b/perllib/FixMyStreet/Cobrand/Lincolnshire.pm @@ -77,4 +77,11 @@ sub pin_colour { return 'yellow'; } +around 'open311_config' => sub { + my ($orig, $self, $row, $h, $params) = @_; + + $params->{upload_files} = 1; + $self->$orig($row, $h, $params); +}; + 1; diff --git a/perllib/FixMyStreet/Cobrand/Peterborough.pm b/perllib/FixMyStreet/Cobrand/Peterborough.pm index 133767fa7..1bd0c83fb 100644 --- a/perllib/FixMyStreet/Cobrand/Peterborough.pm +++ b/perllib/FixMyStreet/Cobrand/Peterborough.pm @@ -89,4 +89,11 @@ sub open311_munge_update_params { $params->{service_code} = $contact->email; } +around 'open311_config' => sub { + my ($orig, $self, $row, $h, $params) = @_; + + $params->{upload_files} = 1; + $self->$orig($row, $h, $params); +}; + 1; -- cgit v1.2.3 From 9553c8daac73f06af704a87ace7294f751a77b8f Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Sat, 23 May 2020 11:38:25 +0100 Subject: Add lazy image loading on list items. --- CHANGELOG.md | 2 + templates/web/base/common_scripts.html | 2 + templates/web/base/report/_item.html | 4 +- templates/web/base/report/_item_expandable.html | 4 +- web/js/loading-attribute-polyfill.js | 213 ++++++++++++++++++++++++ web/js/map-OpenLayers.js | 3 + 6 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 web/js/loading-attribute-polyfill.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2413892c1..0ae55f306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ * Unreleased - New features: - Add Open Location Codes support to search box. #3047 + - Front end improvements: + - Add lazy image loading on list items. - Changes: - Mark user as active when sent an email alert. - Bugfixes: diff --git a/templates/web/base/common_scripts.html b/templates/web/base/common_scripts.html index b8c4db343..32ec9b00f 100644 --- a/templates/web/base/common_scripts.html +++ b/templates/web/base/common_scripts.html @@ -13,6 +13,7 @@ IF bodyclass.match('frontpage'); scripts.push( version('/js/front.js'), version('/js/geolocation.js'), + version('/js/loading-attribute-polyfill.js'), ); ELSIF bodyclass.match('alertpage'); scripts.push( @@ -64,6 +65,7 @@ IF bodyclass.match('mappage'); version('/cobrands/fixmystreet/map.js'), version('/vendor/dropzone.min.js'), version('/vendor/fancybox/jquery.fancybox-1.3.4.pack.js'), + version('/js/loading-attribute-polyfill.js'), ); ELSE; scripts.push( diff --git a/templates/web/base/report/_item.html b/templates/web/base/report/_item.html index baba04d3e..6f567de01 100644 --- a/templates/web/base/report/_item.html +++ b/templates/web/base/report/_item.html @@ -50,7 +50,9 @@ END; [% PROCESS 'report/_item_photo_title.html' ~%] [% CATCH file ~%] [% IF problem.photo %] - + [% END %] [% TRY %] [% PROCESS 'report/_item_heading.html' %] diff --git a/templates/web/base/report/_item_expandable.html b/templates/web/base/report/_item_expandable.html index fad935407..ab363482a 100644 --- a/templates/web/base/report/_item_expandable.html +++ b/templates/web/base/report/_item_expandable.html @@ -18,7 +18,9 @@ [% IF problem.photo %] - + [% END %] diff --git a/web/js/loading-attribute-polyfill.js b/web/js/loading-attribute-polyfill.js new file mode 100644 index 000000000..f11397985 --- /dev/null +++ b/web/js/loading-attribute-polyfill.js @@ -0,0 +1,213 @@ +/* + * Loading attribute polyfill - https://github.com/mfranzke/loading-attribute-polyfill + * @license Copyright(c) 2019 by Maximilian Franzke + * Credits for the initial kickstarter / script to @Sora2455, and supported by @cbirdsong, @eklingen, @DaPo, @nextgenthemes, @diogoterremoto, @dracos, @Flimm, @TomS- and @vinyfc93 - many thanks for that ! + */ +/* + * A minimal and dependency-free vanilla JavaScript loading attribute polyfill. + * Supports standard's functionality and tests for native support upfront. + * Elsewhere the functionality gets emulated with the support of noscript wrapper tags. + * Use an IntersectionObserver polyfill in case of IE11 support necessary. + * + * MS - Removed iframe/picture/srcset parts, unneeded at present, and added external API + */ + +(function () { + 'use strict'; + + var config = { + // Start download if the item gets within 256px in the Y axis + rootMargin: '256px 0px', + threshold: 0.01 + }; + + // Device/browser capabilities object + var capabilities = { + loading: 'loading' in HTMLImageElement.prototype, + scrolling: 'onscroll' in window + }; + + // Nodelist foreach polyfill / source: https://stackoverflow.com/a/46929259 + if ( + typeof NodeList !== 'undefined' && + NodeList.prototype && + !NodeList.prototype.forEach + ) { + // Yes, there's really no need for `Object.defineProperty` here + NodeList.prototype.forEach = Array.prototype.forEach; + } + + // Define according to browsers support of the IntersectionObserver feature (missing e.g. on IE11 or Safari 11) + var intersectionObserver; + + if ('IntersectionObserver' in window) { + intersectionObserver = new IntersectionObserver(onIntersection, config); + } + + // On using a browser w/o requestAnimationFrame support (IE9, Opera Mini), just run the passed function + var rAFWrapper; + + if ('requestAnimationFrame' in window) { + rAFWrapper = window.requestAnimationFrame; + } else { + rAFWrapper = function (func) { + func(); + }; + } + + /** + * Put the source back where it belongs - now that the elements content is attached to the document, it will load now + * @param {Object} lazyItem Current item to be restored after lazy loading. + */ + function restoreSource(lazyItem) { + lazyItem.setAttribute('src', lazyItem.getAttribute('data-lazy-src')); + lazyItem.removeAttribute('data-lazy-src'); // Not using delete .dataset here for compatibility down to IE9 + } + + /** + * Handle IntersectionObservers callback + * @param {Object} entries Target elements Intersection observed changes + * @param {Object} observer IntersectionObserver instance reference + */ + function onIntersection(entries, observer) { + entries.forEach(function (entry) { + // Mitigation for EDGE lacking support of .isIntersecting until v15, compare to e.g. https://github.com/w3c/IntersectionObserver/issues/211#issuecomment-309144669 + if (entry.intersectionRatio === 0) { + return; + } + + // If the item is visible now, load it and stop watching it + var lazyItem = entry.target; + + observer.unobserve(lazyItem); + + restoreSource(lazyItem); + }); + } + + /** + * Handle printing the page + */ + function onPrinting() { + if (typeof window.matchMedia === 'undefined') { + return; + } + + var mediaQueryList = window.matchMedia('print'); + + mediaQueryList.addListener(function (mql) { + if (mql.matches) { + document + .querySelectorAll('img[loading="lazy"][data-lazy-src]') + .forEach(function (lazyItem) { + restoreSource(lazyItem); + }); + } + }); + } + + /** + * Get and prepare the HTML code depending on feature detection, + * and if not scrolling supported, because it's a Google or Bing Bot + * @param {String} lazyAreaHtml Noscript inner HTML code that src-urls need to get rewritten + */ + function getAndPrepareHTMLCode(noScriptTag) { + // The contents of a
    '),this.element.appendChild(e));if(f=e.getElementsByTagName("span")[0])null!=f.textContent?f.textContent=this.options.dictFallbackMessage:null!=f.innerText&&(f.innerText=this.options.dictFallbackMessage);return this.element.appendChild(this.getFallbackForm())},resize:function(a,c,g,b){var e={srcX:0,srcY:0,srcWidth:a.width, +srcHeight:a.height};var f=a.width/a.height;null==c&&null==g?(c=e.srcWidth,g=e.srcHeight):null==c?c=g*f:null==g&&(g=c/f);c=Math.min(c,e.srcWidth);g=Math.min(g,e.srcHeight);var x=c/g;if(e.srcWidth>c||e.srcHeight>g)if("crop"===b)f>x?(e.srcHeight=a.height,e.srcWidth=e.srcHeight*x):(e.srcWidth=a.width,e.srcHeight=e.srcWidth/x);else if("contain"===b)f>x?g=c/f:c=g*f;else throw Error("Unknown resizeMethod '"+b+"'");e.srcX=(a.width-e.srcWidth)/2;e.srcY=(a.height-e.srcHeight)/2;e.trgWidth=c;e.trgHeight=g;return e}, +transformFile:function(a,c){return(this.options.resizeWidth||this.options.resizeHeight)&&a.type.match(/image.*/)?this.resizeImage(a,this.options.resizeWidth,this.options.resizeHeight,this.options.resizeMethod,c):c(a)},previewTemplate:'
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n Check\n \n \n \n \n \n
    \n
    \n \n Error\n \n \n \n \n \n \n \n
    \n
    ', +drop:function(a){return this.element.classList.remove("dz-drag-hover")},dragstart:l,dragend:function(a){return this.element.classList.remove("dz-drag-hover")},dragenter:function(a){return this.element.classList.add("dz-drag-hover")},dragover:function(a){return this.element.classList.add("dz-drag-hover")},dragleave:function(a){return this.element.classList.remove("dz-drag-hover")},paste:l,reset:function(){return this.element.classList.remove("dz-started")},addedfile:function(a){var c;this.element=== +this.previewsContainer&&this.element.classList.add("dz-started");if(this.previewsContainer){a.previewElement=b.createElement(this.options.previewTemplate.trim());a.previewTemplate=a.previewElement;this.previewsContainer.appendChild(a.previewElement);var g=a.previewElement.querySelectorAll("[data-dz-name]");var f=0;for(c=g.length;f'+this.options.dictRemoveFile+""),a.previewElement.appendChild(a._removeLink));g=function(c){return function(g){g.preventDefault();g.stopPropagation();return a.status===b.UPLOADING?b.confirm(c.options.dictCancelUploadConfirmation,function(){return c.removeFile(a)}):c.options.dictRemoveFileConfirmation?b.confirm(c.options.dictRemoveFileConfirmation,function(){return c.removeFile(a)}):c.removeFile(a)}}(this); +c=a.previewElement.querySelectorAll("[data-dz-remove]");var p=[];e=0;for(f=c.length;e'+this.options.dictDefaultMessage+"
    "));if(this.clickableElements.length){var g=function(a){return function(){a.hiddenFileInput&&a.hiddenFileInput.parentNode.removeChild(a.hiddenFileInput);a.hiddenFileInput=document.createElement("input");a.hiddenFileInput.setAttribute("type","file");(null==a.options.maxFiles||1");a+='
    ';a=b.createElement(a);if("FORM"!==this.element.tagName){var c=b.createElement('
    ');c.appendChild(a)}else this.element.setAttribute("enctype","multipart/form-data"),this.element.setAttribute("method",this.options.method);return null!=c?c:a};b.prototype.getExistingFallback=function(){var a;var c=function(a){var c;var b=0;for(c=a.length;b=h){f=a/Math.pow(this.options.filesizeBase,4-k);e=r;break}}f=Math.round(10*f)/10}return""+f+" "+this.options.dictFileSizeUnits[e]};b.prototype._updateMaxFilesReachedClass=function(){return null!=this.options.maxFiles&&this.getAcceptedFiles().length>= +this.options.maxFiles?(this.getAcceptedFiles().length===this.options.maxFiles&&this.emit("maxfilesreached",this.files),this.element.classList.add("dz-max-files-reached")):this.element.classList.remove("dz-max-files-reached")};b.prototype.drop=function(a){if(a.dataTransfer){this.emit("drop",a);var c=a.dataTransfer.files;this.emit("addedfiles",c);c.length&&((a=a.dataTransfer.items)&&a.length&&null!=a[0].webkitGetAsEntry?this._addFilesFromItems(a):this.handleFiles(c))}};b.prototype.paste=function(a){var c; +if(null!=(null!=a?null!=(c=a.clipboardData)?c.items:void 0:void 0)&&(this.emit("paste",a),a=a.clipboardData.items,a.length))return this._addFilesFromItems(a)};b.prototype.handleFiles=function(a){var c;var b=[];var f=0;for(c=a.length;f1048576*this.options.maxFilesize?c(this.options.dictFileTooBig.replace("{{filesize}}",Math.round(a.size/1024/10.24)/100).replace("{{maxFilesize}}",this.options.maxFilesize)):b.isValidFile(a,this.options.acceptedFiles)?null!=this.options.maxFiles&&this.getAcceptedFiles().length>=this.options.maxFiles? +(c(this.options.dictMaxFilesExceeded.replace("{{maxFiles}}",this.options.maxFiles)),this.emit("maxfilesexceeded",a)):this.options.accept.call(this,a,c):c(this.options.dictInvalidFileType)};b.prototype.addFile=function(a){a.upload={progress:0,total:a.size,bytesSent:0,filename:this._renameFile(a)};this.files.push(a);a.status=b.ADDED;this.emit("addedfile",a);this._enqueueThumbnail(a);return this.accept(a,function(c){return function(b){b?(a.accepted=!1,c._errorProcessing([a],b)):(a.accepted=!0,c.options.autoQueue&& +c.enqueueFile(a));return c._updateMaxFilesReachedClass()}}(this))};b.prototype.enqueueFiles=function(a){var c;var b=0;for(c=a.length;b=b)){var f=this.getQueuedFiles();if(0f?c._finished(a, +l,d):m()}}}(this);e.onerror=function(c){return function(){if(a[0].status!==b.CANCELED)return m()}}(this);(null!=(g=e.upload)?g:e).onprogress=n;d={Accept:"application/json","Cache-Control":"no-cache","X-Requested-With":"XMLHttpRequest"};this.options.headers&&r(d,this.options.headers);for(u in d)(g=d[u])&&e.setRequestHeader(u,g);var q=new FormData;if(this.options.params){var u=this.options.params;for(w in u)g=u[w],q.append(w,g)}var w=0;for(u=a.length;w= +g;w=0<=g?++u:--u)c=function(b){return function(c,d,f){return function(c){q.append(d,c,f);if(++t===a.length)return b.submitRequest(e,q,a)}}}(this),d.push(this.options.transformFile.call(this,a[w],c(a[w],this._getParamName(w),a[w].upload.filename)));return d};b.prototype.submitRequest=function(a,b,d){return a.send(b)};b.prototype._finished=function(a,c,d){var f;var e=0;for(f=a.length;e= +h;g=0<=h?++b:--b)c[g]=k.charCodeAt(g);return new Blob([d],{type:a})};var q=function(d,b){var h;var k=[];var a=0;for(h=d.length;ac;)d=a[4*(h-1)+3],0===d?b=h:c=h,h=b+c>>1;k=h/k;return 0===k?1:k};var C=function(d,b,h,k,a,c,g,f,e,l){var p=E(b);return d.drawImage(b,h,k,a,c,g,f,e,l/p)};h._autoDiscoverFunction=function(){if(h.autoDiscover)return h.discover()};(function(d,b){var h=!1;var k=!0;var a=d.document;var c=a.documentElement;var g=a.addEventListener?"addEventListener":"attachEvent";var f=a.addEventListener?"removeEventListener": +"detachEvent";var e=a.addEventListener?"":"on";var l=function(c){if("readystatechange"!==c.type||"complete"===a.readyState)if(("load"===c.type?d:a)[f](e+c.type,l,!1),!h&&(h=!0))return b.call(d,c.type||c)};var m=function(){try{c.doScroll("left")}catch(B){setTimeout(m,50);return}return l("poll")};if("complete"!==a.readyState){if(a.createEventObject&&c.doScroll){try{k=!d.frameElement}catch(B){}k&&m()}a[g](e+"DOMContentLoaded",l,!1);a[g](e+"readystatechange",l,!1);return d[g](e+"load",l,!1)}})(window, +h._autoDiscoverFunction)}).call(this); -- cgit v1.2.3 From 00ad623313b17c9fbf70d0896574cbca599b6dc9 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Thu, 11 Jun 2020 13:32:11 +0100 Subject: [Northamptonshire] Factor out code to add multiple Alloy asset layers --- web/cobrands/fixmystreet-uk-councils/alloy.js | 22 ++++++++++++++++++++++ web/cobrands/northamptonshire/assets.js | 21 +-------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/web/cobrands/fixmystreet-uk-councils/alloy.js b/web/cobrands/fixmystreet-uk-councils/alloy.js index ace0062de..f11a3e7bf 100644 --- a/web/cobrands/fixmystreet-uk-councils/alloy.js +++ b/web/cobrands/fixmystreet-uk-councils/alloy.js @@ -133,4 +133,26 @@ fixmystreet.alloy_defaults = { strategy_class: OpenLayers.Strategy.Alloy }; +fixmystreet.alloy_add_layers = function(defaults, layers) { + $.each(layers, function(index, layer) { + if ( layer.categories ) { + var options = { + http_options: { + layerid: layer.layer, + layerVersion: layer.version, + }, + asset_type: layer.asset_type || 'spot', + asset_category: layer.categories, + asset_item: layer.item_name || layer.layer_name.toLowerCase(), + }; + if (layer.max_resolution) { + options.max_resolution = layer.max_resolution; + } + if (layer.snap_threshold || layer.snap_threshold === 0) { + options.snap_threshold = layer.snap_threshold; + } + fixmystreet.assets.add(northants_defaults, options); + } + }); +}; })(); diff --git a/web/cobrands/northamptonshire/assets.js b/web/cobrands/northamptonshire/assets.js index 2b6cdeb69..377e1091c 100644 --- a/web/cobrands/northamptonshire/assets.js +++ b/web/cobrands/northamptonshire/assets.js @@ -439,26 +439,7 @@ var northants_defaults = $.extend(true, {}, fixmystreet.alloy_defaults, { } }); -$.each(layers, function(index, layer) { - if ( layer.categories ) { - var options = { - http_options: { - layerid: layer.layer, - layerVersion: layer.version, - }, - asset_type: layer.asset_type || 'spot', - asset_category: layer.categories, - asset_item: layer.item_name || layer.layer_name.toLowerCase(), - }; - if (layer.max_resolution) { - options.max_resolution = layer.max_resolution; - } - if (layer.snap_threshold || layer.snap_threshold === 0) { - options.snap_threshold = layer.snap_threshold; - } - fixmystreet.assets.add(northants_defaults, options); - } -}); +fixmystreet.alloy_add_layers(northants_defaults, layers); // NCC roads layers which prevent report submission unless we have selected // an asset. -- cgit v1.2.3 From 603cc8c79564cada62a4dfa8d5c12bdd489f02f0 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Thu, 7 Nov 2019 11:46:19 +0000 Subject: [Alloy] asset layers for Alloy V2 Mostly inherits from original Alloy code, with addition of a new format as Alloy no longer returns a native geoJSON collection. --- web/cobrands/fixmystreet-uk-councils/alloy.js | 93 +++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/web/cobrands/fixmystreet-uk-councils/alloy.js b/web/cobrands/fixmystreet-uk-councils/alloy.js index f11a3e7bf..88d0b017f 100644 --- a/web/cobrands/fixmystreet-uk-councils/alloy.js +++ b/web/cobrands/fixmystreet-uk-councils/alloy.js @@ -3,6 +3,7 @@ OpenLayers.Protocol.Alloy = OpenLayers.Class(OpenLayers.Protocol.HTTP, { currentRequests: [], + tileSize: 512, abort: function() { if (this.currentRequests.length) { for (var j = 0; j < this.currentRequests.length; j++) { @@ -63,8 +64,8 @@ OpenLayers.Protocol.Alloy = OpenLayers.Class(OpenLayers.Protocol.HTTP, { var adjustY = reverse ? 0 : 0.5; var xFromOrigin = Math.floor((bounds[0] - origin.lon) / resolution + adjustX); var yFromOrigin = Math.floor((bounds[1] - origin.lat) / resolution + adjustY); - var tileCoordX = Math.floor(xFromOrigin / 512); - var tileCoordY = Math.floor(yFromOrigin / 512) * -1; + var tileCoordX = Math.floor(xFromOrigin / this.tileSize); + var tileCoordY = Math.floor(yFromOrigin / this.tileSize) * -1; if (reverse) { tileCoordX -= 1; @@ -88,13 +89,18 @@ OpenLayers.Strategy.Alloy = OpenLayers.Class(OpenLayers.Strategy.FixMyStreet, { this.failCount = 0; this.layer.destroyFeatures(); }, + // allow sub classes to override the remote projection for converting the geometry + // of the features + getRemoteProjection: function() { + return this.layer.projection; + }, merge: function(resp) { this.count++; // This if/else clause lifted from OpenLayers.Strategy.BBOX if (resp.success()) { var features = resp.features; if(features && features.length > 0) { - var remote = this.layer.projection; + var remote = this.getRemoteProjection(); var local = this.layer.map.getProjectionObject(); if(!local.equals(remote)) { var geom; @@ -133,26 +139,87 @@ fixmystreet.alloy_defaults = { strategy_class: OpenLayers.Strategy.Alloy }; +/* for Alloy V2 */ +OpenLayers.Format.AlloyV2 = OpenLayers.Class(OpenLayers.Format.GeoJSON, { + read: function(json, type, filter) { + var results = null; + var obj = null; + if (typeof json == "string") { + obj = OpenLayers.Format.JSON.prototype.read.apply(this, [json, filter]); + } else { + obj = json; + } + + if(!obj) { + OpenLayers.Console.error("Bad JSON: " + json); + } else { + results = []; + for(var i=0, len=obj.results.length; i Date: Thu, 23 Apr 2020 15:47:59 +0100 Subject: =?UTF-8?q?Don=E2=80=99t=20show=20`sent=5Fto`=20on=20moderation=20?= =?UTF-8?q?diff?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm | 2 +- t/app/controller/moderate.t | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm b/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm index 1805e1fd2..6d14e6a9f 100644 --- a/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm +++ b/perllib/FixMyStreet/DB/Result/ModerationOriginalData.pm @@ -163,7 +163,7 @@ sub compare_extra { my $new = $other->get_extra_metadata; my $both = { %$old, %$new }; - my @all_keys = sort keys %$both; + my @all_keys = grep { $_ ne 'sent_to' } sort keys %$both; my @s; foreach (@all_keys) { if ($old->{$_} && $new->{$_}) { diff --git a/t/app/controller/moderate.t b/t/app/controller/moderate.t index 8e84bd392..43ae1c980 100644 --- a/t/app/controller/moderate.t +++ b/t/app/controller/moderate.t @@ -51,7 +51,7 @@ sub create_report { longitude => '0.007831', user_id => $user2->id, photo => '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg', - extra => { moon => 'waxing full' }, + extra => { moon => 'waxing full', sent_to => [ 'authority@example.org' ] }, }); } my $report = create_report(); @@ -115,6 +115,7 @@ subtest 'Problem moderation' => sub { }}); $mech->base_like( qr{\Q$REPORT_URL\E} ); $mech->content_like(qr/Moderated by Bromley Council/); + $mech->content_lacks('sent_to = ARRAY(0x'); $report->discard_changes; is $report->title, 'Good good'; -- cgit v1.2.3 From 3e9277ed0e81b5d09656bff0aa5dacd87da1e55e Mon Sep 17 00:00:00 2001 From: Martin Wright Date: Wed, 9 Oct 2019 14:02:12 +0100 Subject: [Hackney] Initial cobrand design & privacy policy amendment Co-Authored-By: Struan Donald For https://github.com/mysociety/fixmystreet-commercial/issues/1808 --- .cypress/cypress/integration/hackney.js | 19 ++ bin/browser-tests | 4 +- bin/fixmystreet.com/fixture | 1 + perllib/FixMyStreet/Cobrand/Hackney.pm | 24 +++ t/Mock/MapIt.pm | 1 + t/cobrand/hackney.t | 223 +++++++++++++++++++++ templates/email/hackney/_email_bottom.html | 28 +++ .../email/hackney/_email_color_overrides.html | 25 +++ .../email/hackney/_email_setting_overrides.html | 7 + templates/email/hackney/signature.txt | 2 + templates/email/hackney/site-name.txt | 1 + .../web/fixmystreet-uk-councils/about/privacy.html | 6 + templates/web/hackney/around/intro.html | 5 + templates/web/hackney/footer_extra.html | 10 + templates/web/hackney/header_extra.html | 2 + templates/web/hackney/site-name.html | 1 + templates/web/hackney/tracking_code.html | 1 + web/cobrands/hackney/_colours.scss | 45 +++++ web/cobrands/hackney/base.scss | 217 ++++++++++++++++++++ web/cobrands/hackney/hackney-search-icon.png | Bin 0 -> 464 bytes web/cobrands/hackney/images/hackney-logo-white.png | Bin 0 -> 18214 bytes web/cobrands/hackney/layout.scss | 143 +++++++++++++ 22 files changed, 763 insertions(+), 2 deletions(-) create mode 100644 .cypress/cypress/integration/hackney.js create mode 100644 perllib/FixMyStreet/Cobrand/Hackney.pm create mode 100644 t/cobrand/hackney.t create mode 100644 templates/email/hackney/_email_bottom.html create mode 100644 templates/email/hackney/_email_color_overrides.html create mode 100644 templates/email/hackney/_email_setting_overrides.html create mode 100644 templates/email/hackney/signature.txt create mode 100644 templates/email/hackney/site-name.txt create mode 100644 templates/web/hackney/around/intro.html create mode 100644 templates/web/hackney/footer_extra.html create mode 100644 templates/web/hackney/header_extra.html create mode 100644 templates/web/hackney/site-name.html create mode 100644 templates/web/hackney/tracking_code.html create mode 100644 web/cobrands/hackney/_colours.scss create mode 100644 web/cobrands/hackney/base.scss create mode 100644 web/cobrands/hackney/hackney-search-icon.png create mode 100644 web/cobrands/hackney/images/hackney-logo-white.png create mode 100644 web/cobrands/hackney/layout.scss diff --git a/.cypress/cypress/integration/hackney.js b/.cypress/cypress/integration/hackney.js new file mode 100644 index 000000000..a4293b028 --- /dev/null +++ b/.cypress/cypress/integration/hackney.js @@ -0,0 +1,19 @@ +describe('When you look at the Hackney site', function() { + + beforeEach(function() { + cy.server(); + cy.route('/report/new/ajax*').as('report-ajax'); + cy.visit('http://hackney.localhost:3001/'); + cy.contains('Hackney Council'); + cy.should('not.contain', 'Hackney Borough'); + cy.get('[name=pc]').type('E8 1DY'); + 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 Hackney Council'); + }); +}); diff --git a/bin/browser-tests b/bin/browser-tests index 32b844127..e0b9bedfe 100755 --- a/bin/browser-tests +++ b/bin/browser-tests @@ -11,7 +11,7 @@ my ($cobrand, $coords, $area_id, $name, $mapit_url, $coverage); BEGIN { $config_file = 'conf/general.yml-example'; - $cobrand = [ 'borsetshire', 'fixmystreet', 'northamptonshire', 'bathnes', 'buckinghamshire', 'hounslow', 'isleofwight', 'peterborough', 'tfl' ]; + $cobrand = [ 'borsetshire', 'fixmystreet', 'northamptonshire', 'bathnes', 'buckinghamshire', 'hounslow', 'isleofwight', 'peterborough', 'tfl', 'hackney' ]; $coords = '51.532851,-2.284277'; $area_id = 2608; $name = 'Borsetshire'; @@ -190,7 +190,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,isleofwight,peterborough,tfl + --cobrand Cobrand(s) to use, default is fixmystreet,northamptonshire,bathnes,buckinghamshire,isleofwight,peterborough,tfl,hackney --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 082fc6309..e8dd3f364 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 => 2508, categories => [ 'Potholes', 'Other' ], name => 'Hackney Council' }, { area_id => 2636, categories => [ 'Potholes', 'Private', 'Extra' ], name => 'Isle of Wight Council' }, { area_id => 2566, categories => [ 'Fallen branch', 'Light Out', 'Light Dim', 'Fallen Tree', 'Damaged Tree' ], name => 'Peterborough City Council' }, { area_id => 2498, categories => [ 'Incorrect timetable', 'Glass broken', 'Mobile Crane Operation' ], name => 'TfL' }, diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm new file mode 100644 index 000000000..e726154b0 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -0,0 +1,24 @@ +package FixMyStreet::Cobrand::Hackney; +use parent 'FixMyStreet::Cobrand::Whitelabel'; + +use strict; +use warnings; + +sub council_area_id { return 2508; } +sub council_area { return 'Hackney'; } +sub council_name { return 'Hackney Council'; } +sub council_url { return 'hackney'; } +sub send_questionnaires { 0 } + +sub disambiguate_location { + my $self = shift; + my $string = shift; + + return { + %{ $self->SUPER::disambiguate_location() }, + centre => '51.552267,-0.063316', + bounds => [ 51.519814, -0.104511, 51.577784, -0.016527 ], + }; +} + +1; diff --git a/t/Mock/MapIt.pm b/t/Mock/MapIt.pm index b54ba0ddb..ed95e71fc 100644 --- a/t/Mock/MapIt.pm +++ b/t/Mock/MapIt.pm @@ -44,6 +44,7 @@ my @PLACES = ( [ 'NN1 2NS', 52.238301, -0.889992, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ], [ '?', 52.238827, -0.894970, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ], [ '?', 52.23025, -1.015826, 2234, 'Northamptonshire County Council', 'CTY', 2397, 'Northampton Borough Council', 'DIS' ], + [ 'E8 1DY', 51.552267, -0.063316, 2508, 'Hackney Borough Council', 'LBO' ], [ 'TW7 5JN', 51.482286, -0.328163, 2483, 'Hounslow Borough Council', 'LBO' ], [ '?', 51.48111, -0.327219, 2483, 'Hounslow Borough Council', 'LBO' ], [ '?', 51.482045, -0.327219, 2483, 'Hounslow Borough Council', 'LBO' ], diff --git a/t/cobrand/hackney.t b/t/cobrand/hackney.t new file mode 100644 index 000000000..519b1b3b8 --- /dev/null +++ b/t/cobrand/hackney.t @@ -0,0 +1,223 @@ +use utf8; +use CGI::Simple; +use DateTime; +use Test::MockModule; +use FixMyStreet::TestMech; +use Open311; +use Open311::GetServiceRequests; +use Open311::GetServiceRequestUpdates; +use Open311::PostServiceRequestUpdates; +use FixMyStreet::Script::Alerts; +use FixMyStreet::Script::Reports; + +# disable info logs for this test run +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + +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 $hackney = $mech->create_body_ok(2508, 'Hackney Council', $params); +my $contact = $mech->create_contact_ok( + body_id => $hackney->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 $hackney_user = $mech->create_user_ok('hackney_user@example.org', name => 'Hackney User', from_body => $hackney); +$hackney_user->user_body_permissions->create({ + body => $hackney, + permission_type => 'moderate', +}); + +my $contact2 = $mech->create_contact_ok( + body_id => $hackney->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 => $hackney); + +$admin_user->user_body_permissions->create({ + body => $hackney, + permission_type => 'triage' +}); + +my @reports = $mech->create_problems_for_body(1, $hackney->id, 'A Hackney report', { + confirmed => '2019-10-25 09:00', + lastupdate => '2019-10-25 09:00', + latitude => 51.552267, + longitude => -0.063316, + user => $user, + external_id => 101202303 +}); + +subtest "check clicking all reports link" => sub { + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => ['hackney'], + }, sub { + $mech->get_ok('/'); + $mech->follow_link_ok({ text => 'All reports' }); + }; + + $mech->content_contains("A Hackney report", "Hackney report there"); + $mech->content_contains("Hackney Council", "is still on cobrand"); +}; + +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 => ['hackney'], + }, sub { + $mech->log_out_ok; + $mech->log_in_ok( $hackney_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 Hackney Council/); + }; +}; + +$_->delete for @reports; + +my $system_user = $mech->create_user_ok('system_user@example.org'); + +my ($p) = $mech->create_problems_for_body(1, $hackney->id, '', { cobrand => 'hackney' }); +my $alert = FixMyStreet::DB->resultset('Alert')->create( { + parameter => $p->id, + alert_type => 'new_updates', + user => $user, + cobrand => 'hackney', +} )->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); + $mech->clear_emails_ok; + + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => ['hackney','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/Hackney Council/, "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 => [ 'hackney' ], + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $mech->submit_form_ok( { with_fields => { pc => 'E8 1DY', } }, + "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/Hackney Council/, "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 => ['hackney','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/Hackney Council/, "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 => ['hackney','fixmystreet'], + }, sub { + $extra_details = $mech->get_ok_json('/report/new/category_extras?category=Roads&latitude=51.552267&longitude=-0.063316'); + }; + + like $extra_details->{category_extra}, qr/Hackney Council/, 'correct name in category extras'; +}; + + +done_testing(); diff --git a/templates/email/hackney/_email_bottom.html b/templates/email/hackney/_email_bottom.html new file mode 100644 index 000000000..64936c470 --- /dev/null +++ b/templates/email/hackney/_email_bottom.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + +
    + [%~ IF email_footer %] + [% email_footer | safe %] + [%~ ELSE %] + This email was sent automatically, from an unmonitored email account. Please do not reply to it. + [%~ END %] +
    + Powered by FixMyStreet +
    + + diff --git a/templates/email/hackney/_email_color_overrides.html b/templates/email/hackney/_email_color_overrides.html new file mode 100644 index 000000000..1af818eca --- /dev/null +++ b/templates/email/hackney/_email_color_overrides.html @@ -0,0 +1,25 @@ +[% + +color_green = '#00b341' +color_black = '#000000' +color_white = '#FFFFFF' +color_hackney_pale_green = '#f2f7f0' +color_hackney_dark_green = '#00664f' + +body_background_color = color_hackney_pale_green +body_text_color = color_black + +header_background_color = color_black +header_text_color = color_white + +secondary_column_background_color = color_white + +button_background_color = color_hackney_dark_green +button_text_color = color_white + +logo_file = 'hackney-logo-white.png' +logo_width = "200" # pixel measurement, but without 'px' suffix +logo_height = "36" # pixel measurement, but without 'px' suffix +header_padding = "20px 30px" + +%] diff --git a/templates/email/hackney/_email_setting_overrides.html b/templates/email/hackney/_email_setting_overrides.html new file mode 100644 index 000000000..00eeed9cc --- /dev/null +++ b/templates/email/hackney/_email_setting_overrides.html @@ -0,0 +1,7 @@ +[% + +only_column_style = "$only_column_style border: 1px solid $column_divider_color; border-top: none;" +primary_column_style = "$primary_column_style border: 1px solid $column_divider_color; border-top: none;" +secondary_column_style = "vertical-align: top; width: 50%; background-color: $secondary_column_background_color; color: $secondary_column_text_color; border: 1px solid $column_divider_color; border-top: none; border-left: none;" + +%] diff --git a/templates/email/hackney/signature.txt b/templates/email/hackney/signature.txt new file mode 100644 index 000000000..78a02659f --- /dev/null +++ b/templates/email/hackney/signature.txt @@ -0,0 +1,2 @@ + +Hackney Council diff --git a/templates/email/hackney/site-name.txt b/templates/email/hackney/site-name.txt new file mode 100644 index 000000000..29d7f1480 --- /dev/null +++ b/templates/email/hackney/site-name.txt @@ -0,0 +1 @@ +Report A Problem diff --git a/templates/web/fixmystreet-uk-councils/about/privacy.html b/templates/web/fixmystreet-uk-councils/about/privacy.html index 4a5ed9d2c..1290ee7fb 100644 --- a/templates/web/fixmystreet-uk-councils/about/privacy.html +++ b/templates/web/fixmystreet-uk-councils/about/privacy.html @@ -231,6 +231,11 @@ When you make a report

    +[% IF c.cobrand.moniker == 'hackney' %] + In using FixMyStreet for any of the functions listed above (sending a + report, leaving an update, email alerts or site registration), we are + processing your data under the legal basis 6(1)(a) – consent. +[% ELSE %] In using FixMyStreet for any of the functions listed above (sending a report, leaving an update, email alerts or site registration), we are processing your data under the legal basis 6(1)(f) – legitimate interests. @@ -243,6 +248,7 @@ When you make a report communities, so it's easy to see what the common problems are in a given area, and how quickly they get fixed. Other local residents can browse, read and comment on problems – and perhaps even offer a solution. +[% END %]

    diff --git a/templates/web/hackney/around/intro.html b/templates/web/hackney/around/intro.html new file mode 100644 index 000000000..d4510ac9a --- /dev/null +++ b/templates/web/hackney/around/intro.html @@ -0,0 +1,5 @@ +
    +

    Report, view, or discuss local problems

    +

    (like potholes, fly tipping, broken paving slabs, or street lighting)

    +
    + diff --git a/templates/web/hackney/footer_extra.html b/templates/web/hackney/footer_extra.html new file mode 100644 index 000000000..1e7c53aad --- /dev/null +++ b/templates/web/hackney/footer_extra.html @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/templates/web/hackney/header_extra.html b/templates/web/hackney/header_extra.html new file mode 100644 index 000000000..73d214ae0 --- /dev/null +++ b/templates/web/hackney/header_extra.html @@ -0,0 +1,2 @@ +[% INCLUDE 'tracking_code.html' %] + diff --git a/templates/web/hackney/site-name.html b/templates/web/hackney/site-name.html new file mode 100644 index 000000000..29d7f1480 --- /dev/null +++ b/templates/web/hackney/site-name.html @@ -0,0 +1 @@ +Report A Problem diff --git a/templates/web/hackney/tracking_code.html b/templates/web/hackney/tracking_code.html new file mode 100644 index 000000000..b01a0178c --- /dev/null +++ b/templates/web/hackney/tracking_code.html @@ -0,0 +1 @@ +[%# awaiting analytics information from Hackney %] diff --git a/web/cobrands/hackney/_colours.scss b/web/cobrands/hackney/_colours.scss new file mode 100644 index 000000000..4c0af7b03 --- /dev/null +++ b/web/cobrands/hackney/_colours.scss @@ -0,0 +1,45 @@ +/* COLOURS */ + +$menu-image: 'menu-black'; + +// Primary +$white: #fff; +$green: #00b341; +$grey: #666664; + + +// Secondary +$yellow: #ffc845; +$blue: #0072ce; +$pale_green: #f2f7f0; +$alt_green: #328b15; +$light_green: #84bd00; +$dark_green: #00664f; +$teal :#1e98a7; +$black: #000; +$red: #be3a34; + +$primary: $green; +$primary_b: #000; +$primary_text: $black; + +$base_bg: $white; +$base_fg: $black; + +$link-color: $blue; +$link-visited_color: $dark-green; +$link-hover-color: $green; + +$nav_background_colour: $black; +$nav_colour: $white; +$nav_hover_background_colour: $black; + +$col_click_map: $green; + +$header-top-border: false; + +$montserrat: 'Montserrat', Arial, sans-serif; + +$heading-font: $montserrat; +$body-font: $montserrat; +$meta-font: $montserrat; \ No newline at end of file diff --git a/web/cobrands/hackney/base.scss b/web/cobrands/hackney/base.scss new file mode 100644 index 000000000..a3e41c5d0 --- /dev/null +++ b/web/cobrands/hackney/base.scss @@ -0,0 +1,217 @@ +@import "../sass/h5bp"; +@import "./_colours"; +@import "../sass/mixins"; + +@import "../sass/base"; + + +#site-header { + box-shadow: + 0 0 0 6px $white, + 0 0 0 10px $dark-green, + 0 0 0 13px $white, + 0 0 0 16px $green, + 0 0 0 19px $white, + 0 0 0 21px $light-green; +} + +#site-logo { + background: transparent url('images/hackney-logo-white.png') 0 50% no-repeat; + background-size: 200px 36px; + width: 200px; + &:focus { + outline: 4px solid $yellow; + } +} + +.nav-menu--mysoc { + a { + color: $primary_text; + background-color: $primary; + } +} + +#front-main { + background-color: $white; + margin: 0; + padding: 1em; + text-align: inherit; + + h1 { + color: $black; + } + + #postcodeForm { + margin: 0; + padding: 0; + background: transparent; + color: inherit; + + div { + border: none; + background: transparent; + position: relative; + background: #fff; + box-shadow: 1px 1px 5px 1px rgba(104, 104, 104, 0.4); + + input#pc { + box-sizing: border-box; + padding: 10px 22px; + } + + input#sub { + width: 0; + padding-left: 50px; + overflow: hidden; + @include flex(0 0 auto); + background: $black url('hackney-search-icon.png') no-repeat 50% 50%; + background-size: 25px 25px; + color: $black; + &:hover, + &:focus { + background: $dark-green url('hackney-search-icon.png') no-repeat 50% 50%; + background-size: 25px 25px; + color: $dark_green; + } + &:focus { + outline: 4px solid $yellow; + } + } + } + } + + a#geolocate_link { + background: transparent; + display: block; + padding: 0; + margin-top: 0.5em; + font-family: inherit; + font-size: 1em; + border-radius: 0; + color: $dark_green; + + &:hover { + background: transparent; + text-decoration: underline; + } + } + + .form-hint { + color: inherit; + } +} + +.hackney-footer { + background-color: $dark_green; + color: #fff; + padding: 1em 0; +} + +.hackney-footer__logo { + background: transparent url('images/hackney-logo-white.png') 0 50% no-repeat; + background-size: 200px 36px; + width: 200px; + height: 54px; + text-indent: -999999px; + display: inline-block; + &:focus { + outline: 4px solid $yellow; + } +} + +a.platform-logo { + color: #fff; +} + +#front_stats { + background-color: $dark_green; + color: #fff; +} + +.green-btn, +.btn { + border-radius: 4px; + font-size: 1.1875em; + font-weight: 400; + line-height: 1.4375em; + vertical-align: top; + display: inline-block; + position: relative; + border: 1px solid $dark_green; + box-shadow: inset #003d2f 0 -2px 0 0; + &:hover { + background: none; + background-color: #005a30; + color: #fff !important; + } + + &:focus { + border-color:#ffdd00; + color:#0b0c0c !important; + background: none; + background-color:#ffdd00; + box-shadow: 0 2px 0#0b0c0c; + } +} + +.btn--primary, +.btn-primary, +.green-btn { + background: $dark_green; + border: 2px solid transparent; + color: #ffffff; + box-shadow: 0 2px 0 #002d18; +} + +textarea.form-control, +.dropzone, +input.form-control { + border: 2px solid #0b0c0c; + border-radius: 0; + box-shadow: none; + &:focus { + outline: 3px solid#ffdd00; + outline-offset: 0; + box-shadow: inset 0 0 0 2px; + } +} + +label, legend, .label { + font-weight: 300; +} + +.big-green-banner { + background-color: $dark_green; + text-transform: none; +} + +div.form-error, p.form-error { + color: $red; + background-color: transparent; + padding: 0; + width: 100%; + + a { + color: $red; + text-decoration: underline; + } +} + + +input.form-error, textarea.form-error, +select.form-error { + border-color: $red; + border-radius: 0 !important; +} + +.box-warning { + background-color: rgba(164, 214, 94, 0.5); +} + +.dz-clickable:hover, .dz-drag-hover { + border-color: $light-green; +} + +.dz-clickable:hover .dz-message u, .dz-drag-hover .dz-message u { + color: $green; +} \ No newline at end of file diff --git a/web/cobrands/hackney/hackney-search-icon.png b/web/cobrands/hackney/hackney-search-icon.png new file mode 100644 index 000000000..99305f24a Binary files /dev/null and b/web/cobrands/hackney/hackney-search-icon.png differ diff --git a/web/cobrands/hackney/images/hackney-logo-white.png b/web/cobrands/hackney/images/hackney-logo-white.png new file mode 100644 index 000000000..3a41df786 Binary files /dev/null and b/web/cobrands/hackney/images/hackney-logo-white.png differ diff --git a/web/cobrands/hackney/layout.scss b/web/cobrands/hackney/layout.scss new file mode 100644 index 000000000..8b6fa6278 --- /dev/null +++ b/web/cobrands/hackney/layout.scss @@ -0,0 +1,143 @@ +@import "_colours"; +@import "../sass/layout"; + +#main-nav { + display: block; // remove flex so nav touches top of parent + min-height: 0; // no vertical align, so no need for a height + margin-top: 0; // don't bother overlapping the border + height: auto; + margin-top: 0.5em; + .frontpage & { + margin-top: 1em; + } + .ie9 & { + position: static; + float: $right; + + & > * { + position: static; + -ms-transform: none; + } + } + + a { + font-weight: 600; + } +} + +.nav-menu--mysoc { + padding: 0em 0.5em; + margin-#{$left}: 0.25em; + background-color: $primary; + @include border-radius(0 0 0.375em 0.375em); + a { + background-color: transparent; + color: $primary_text; + text-transform: uppercase; + font-size: 0.6875em; + padding: 1.3em 0.7em 1em; + &:hover { + color: #fff; + background-color: transparent; + } + } +} + +body.frontpage { + #site-logo { + margin: 0.5em 0 0.5em; + width: 200px; + height: 54px; + background: transparent url('images/hackney-logo-white.png') 0 50% no-repeat; + background-size: 200px 36px; + } +} + +#site-header { + padding: 4px 0; + margin-bottom: 2em; + .mappage & { + margin-bottom: 0; + } +} + +#front-main { + background-color: $white; + padding: 50px 0; + border-bottom: 3px solid $light_green; + label { + font-size: 18px; + font-weight: 600; + } + + h1 { + font-size: 40px; + font-weight: 600; + } + + #postcodeForm { + overflow: visible; + margin: 0; + + div { + margin: 0; + } + } + + #front-main-container { + padding: 0 1em; + } +} + +ol.big-numbers>li:before { + color: $dark_green; +} + +.nav-menu--main span { + color: $light_green; + font-weight: 600; +} + +#front_stats { + border-color: $dark_green; + big { + color: $dark_green; + } +} + +.nav-menu--main a.report-a-problem-btn { + background-color: transparent; + color: white; + &:hover, + &:active, + &:focus { + background-color: transparent; + color: white; + } +} + +#front-howto h2, +#front-recently h2 { + font-weight: 600; +} + +#front_stats { + background-color: transparent; +} + +.big-green-banner { + background-color: $dark_green; +} + +.footer-powered-by { + position: relative; + top: -40px; + right: 0; + text-align: right; + +} + +a.platform-logo { + text-align: left; + +} \ No newline at end of file -- cgit v1.2.3 From 49805dd6a3ed38f06e355ad3603eb411d9e6dd15 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Fri, 28 Feb 2020 14:52:46 +0000 Subject: [Hackney] set extra open311 parameters --- perllib/FixMyStreet/Cobrand/Hackney.pm | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index e726154b0..a888224d7 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -21,4 +21,26 @@ sub disambiguate_location { }; } +sub open311_config { + my ($self, $row, $h, $params) = @_; + + $params->{multi_photos} = 1; +} + +sub open311_extra_data { + my ($self, $row, $h, $extra) = @_; + + my $open311_only = [ + { name => 'report_url', + value => $h->{url} }, + { name => 'title', + value => $row->title }, + { name => 'description', + value => $row->detail }, + { name => 'category', + value => $row->category }, + ]; + + return $open311_only; +} 1; -- cgit v1.2.3 From 4237159458ce724c42c3952ec48a111f71050beb Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Wed, 11 Mar 2020 15:26:47 +0000 Subject: [Hackney] Use OpenStreetMap base map Also bumps up default zoom level a couple of notches For https://github.com/mysociety/fixmystreet-commercial/issues/1760 --- perllib/FixMyStreet/Cobrand/Hackney.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index a888224d7..6af147a16 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -43,4 +43,9 @@ sub open311_extra_data { return $open311_only; } + +sub map_type { 'OSM' } + +sub default_map_zoom { 5 } + 1; -- cgit v1.2.3 From 002057bfedb00ff3cef14783d165a1c5fc3d0c56 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Thu, 23 Apr 2020 15:13:08 +0100 Subject: [Hackney] Switch to OSM geocoder --- perllib/FixMyStreet/Cobrand/Hackney.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index 6af147a16..eeca31186 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -16,11 +16,16 @@ sub disambiguate_location { return { %{ $self->SUPER::disambiguate_location() }, + town => 'Hackney', centre => '51.552267,-0.063316', bounds => [ 51.519814, -0.104511, 51.577784, -0.016527 ], }; } +sub get_geocoder { + return 'OSM'; # default of Bing gives poor results, let's try overriding. +} + sub open311_config { my ($self, $row, $h, $params) = @_; -- cgit v1.2.3 From 761afd95fdc7cc98adb87c0ccfbb850e91dccc25 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Thu, 7 May 2020 11:32:20 +0100 Subject: [Hackney] Add admin_user_domain --- perllib/FixMyStreet/Cobrand/Hackney.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index eeca31186..deb408ab1 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -53,4 +53,6 @@ sub map_type { 'OSM' } sub default_map_zoom { 5 } +sub admin_user_domain { 'hackney.gov.uk' } + 1; -- cgit v1.2.3 From 6a013ddcfeb590863e16989652a0393acceb6e29 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Mon, 1 Jun 2020 16:12:57 +0100 Subject: [Hackney] Enable anonymous reporting --- perllib/FixMyStreet/Cobrand/Hackney.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index deb408ab1..76cd3f1dd 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -55,4 +55,12 @@ sub default_map_zoom { 5 } sub admin_user_domain { 'hackney.gov.uk' } +sub anonymous_account { + my $self = shift; + return { + email => $self->feature('anonymous_account') . '@' . $self->admin_user_domain, + name => 'Anonymous user', + }; +} + 1; -- cgit v1.2.3 From 6d07fab1527a18512c560d0b5608dea1143bdb69 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Mon, 2 Mar 2020 17:45:33 +0000 Subject: [Hackney] Add initial asset layers & config --- templates/web/hackney/footer_extra_js.html | 7 + web/cobrands/hackney/assets.js | 246 +++++++++++++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 templates/web/hackney/footer_extra_js.html create mode 100644 web/cobrands/hackney/assets.js diff --git a/templates/web/hackney/footer_extra_js.html b/templates/web/hackney/footer_extra_js.html new file mode 100644 index 000000000..fbd33dd11 --- /dev/null +++ b/templates/web/hackney/footer_extra_js.html @@ -0,0 +1,7 @@ +[% +IF bodyclass.match('mappage'); + scripts.push( + version('/cobrands/fixmystreet-uk-councils/alloy.js'), + ); +END %] +[% PROCESS 'footer_extra_js_base.html' highways=1 cobrand_js=1 validation=1 %] diff --git a/web/cobrands/hackney/assets.js b/web/cobrands/hackney/assets.js new file mode 100644 index 000000000..9941594f0 --- /dev/null +++ b/web/cobrands/hackney/assets.js @@ -0,0 +1,246 @@ +(function(){ + +if (!fixmystreet.maps) { + return; +} + +/** These layers are from the Hackney WFS feed, for non-Alloy categories: */ +var wfs_defaults = { + http_options: { + url: "https://map.hackney.gov.uk/geoserver/wfs", + params: { + SERVICE: "WFS", + VERSION: "1.1.0", + REQUEST: "GetFeature", + SRSNAME: "urn:ogc:def:crs:EPSG::27700" + } +}, + asset_type: 'spot', + max_resolution: 2.388657133579254, + asset_id_field: 'id', + attributes: {}, + geometryName: 'geom', + srsName: "EPSG:27700", + strategy_class: OpenLayers.Strategy.FixMyStreet, + body: "Hackney Council", + asset_item: "item" +}; + + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "greenspaces:park_bin", + } + }, + asset_category: "Overflowing bin", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "amenity:public_toilet", + } + }, + asset_category: ["Public toilets", "Toilets"], + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "parking:pay_display_machine_liberator", + } + }, + asset_category: "Pay & Display Machines", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "recycling:estate_recycling_bin", + } + }, + asset_category: "Bin Contamination", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "amenity:sport_facility", + } + }, + asset_category: "Sport Area", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "greenspaces:park_bench", + } + }, + asset_category: "Park Furniture (bench)", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "greenspaces:park_bin", + } + }, + asset_category: "Park Furniture (bin)", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "greenspaces:park_picnic_bench", + } + }, + asset_category: "Park Furniture (picnic bench)", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "transport:bike_hangar_scheme", + } + }, + asset_category: "Cycle Hangars", + attributes: {} +}); + +fixmystreet.assets.add(wfs_defaults, { + http_options: { + params: { + TYPENAME: "greenspaces:park_bench", + } + }, + asset_category: "Benches", + attributes: {} +}); + + +/** These layers are served directly from Alloy: */ + +// View all layers with something like: +// curl https://tilma.staging.mysociety.org/resource-proxy/proxy.php\?https://hackney.assets/ | jq '.results[] | .layer.code, ( .layer.styles[] | { id, name } ) ' +var layers = [ + { + "categories": ["Street Lighting", "Lamposts"], + "item_name": "street light", + "layer_name": "Street Lights", + "styleid": "5d308d57fe2ad8046c67cdb5", + "layerid": "layers_streetLightingAssets" + }, + { + "categories": ["Illuminated Bollards", "Non-illuminated Bollards"], + "item_name": "bollard", + "layer_name": "Bollards", + "styleid": "5d308d57fe2ad8046c67cdb9", + "layerid": "layers_streetLightingAssets" + }, + { + "categories": ["Benches"], + "item_name": "bench", + "layer_name": "Bench", + "styleid": "5e8b16f0ca31500f60b3f589", + "layerid": "layers_bench_5e8b15f0ca31500f60b3f568" + }, + { + "categories": ["Potholes"], + "item_name": "road", + "layer_name": "Carriageway", + "styleid": "5d53d28bfe2ad80fc4573184", + "layerid": "layers_carriageway_5d53cc74fe2ad80c3403b77d" + }, + { + "categories": ["Road Markings / Lines"], + "item_name": "road", + "layer_name": "Markings", + "styleid": "5d308dd7fe2ad8046c67da33", + "layerid": "layers_highwayAssets" + }, + { + "categories": ["Pavement"], + "item_name": "pavement", + "layer_name": "Footways", + "styleid": "5d308dd6fe2ad8046c67da2a", + "layerid": "layers_highwayAssets" + }, + { + "categories": ["Cycle Tracks"], + "item_name": "cycle track", + "layer_name": "Cycle Tracks", + "styleid": "5d308dd6fe2ad8046c67da29", + "layerid": "layers_highwayAssets" + }, + { + "categories": ["Drains and gutters"], + "item_name": "drain", + "layer_name": "Gullies", + "styleid": "5d308dd6fe2ad8046c67da2e", + "layerid": "layers_highwayAssets" + }, + { + "categories": ["Verges"], + "item_name": "verge", + "layer_name": "Verges", + "styleid": "5d308dd7fe2ad8046c67da36", + "layerid": "layers_highwayAssets" + }, + { + "categories": ["Road Hump Fault / Damage"], + "item_name": "road hump", + "layer_name": "Traffic Calming", + "styleid": "5d308dd7fe2ad8046c67da35", + "layerid": "layers_highwayAssets" + }, + { + "categories": ["Broken or Faulty Barrier Gates"], + "item_name": "barrier gate", + "layer_name": "Gates", + "styleid": "5d308dd6fe2ad8046c67da2c", + "layerid": "layers_highwayAssets" + }, + { + "categories": ["Belisha Beacon"], + "item_name": "beacon", + "layer_name": "Belisha Beacon", + "styleid": "5d308d57fe2ad8046c67cdb6", + "layerid": "layers_streetLightingAssets" + }, + { + "categories": ["Loose or Damaged Kerb Stones"], + "item_name": "kerb", + "layer_name": "Kerbs", + "styleid": "5d308dd6fe2ad8046c67da30", + "layerid": "layers_highwayAssets" + } +]; + +var hackney_defaults = $.extend(true, {}, fixmystreet.alloyv2_defaults, { + class: OpenLayers.Layer.NCCVectorAsset, + protocol_class: OpenLayers.Protocol.AlloyV2, + http_options: { + base: "https://tilma.staging.mysociety.org/resource-proxy/proxy.php?https://hackney.assets/${layerid}/${x}/${y}/${z}/cluster?styleIds=${styleid}" + }, + non_interactive: false, + body: "Hackney Council", + attributes: { + asset_resource_id: function() { + return this.fid; + } + } +}); + +fixmystreet.alloy_add_layers(hackney_defaults, layers); + +})(); -- cgit v1.2.3 From e8627c718732b2cb325530daf7a5b82998e61512 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Wed, 13 May 2020 17:02:49 +0100 Subject: [Hackney] Include TfL assets on map --- templates/web/hackney/footer_extra_js.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/web/hackney/footer_extra_js.html b/templates/web/hackney/footer_extra_js.html index fbd33dd11..61b8dacea 100644 --- a/templates/web/hackney/footer_extra_js.html +++ b/templates/web/hackney/footer_extra_js.html @@ -4,4 +4,4 @@ IF bodyclass.match('mappage'); version('/cobrands/fixmystreet-uk-councils/alloy.js'), ); END %] -[% PROCESS 'footer_extra_js_base.html' highways=1 cobrand_js=1 validation=1 %] +[% PROCESS 'footer_extra_js_base.html' highways=1 tfl=1 cobrand_js=1 validation=1 %] -- cgit v1.2.3 From aa9d3cce479c7b28ba3f5f09cac0231111aeebf8 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Mon, 8 Jun 2020 13:05:40 +0100 Subject: [Hackney] Extend open311_protect flag to entire contact, not just name/group --- perllib/FixMyStreet/Cobrand/Hackney.pm | 17 +++++++++++++++++ perllib/Open311/PopulateServiceList.pm | 8 +++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index 76cd3f1dd..f08cd0dd9 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -63,4 +63,21 @@ sub anonymous_account { }; } +sub open311_skip_existing_contact { + my ($self, $contact) = @_; + + # For Hackney we want the 'protected' flag to prevent any changes to this + # contact at all. + return $contact->get_extra_metadata("open311_protect") ? 1 : 0; +} + +sub open311_filter_contacts_for_deletion { + my ($self, $contacts) = @_; + + # Don't delete open311 protected contacts when importing + return $contacts->search({ + extra => { -not_like => '%T15:open311_protect,I1:1%' }, + }); +} + 1; diff --git a/perllib/Open311/PopulateServiceList.pm b/perllib/Open311/PopulateServiceList.pm index 20fca90b3..a3672770c 100644 --- a/perllib/Open311/PopulateServiceList.pm +++ b/perllib/Open311/PopulateServiceList.pm @@ -145,6 +145,8 @@ sub _handle_existing_contact { my $service_name = $self->_normalize_service_name; my $protected = $contact->get_extra_metadata("open311_protect"); + return if $self->_current_body_cobrand && $self->_current_body_cobrand->call_hook(open311_skip_existing_contact => $contact); + print $self->_current_body->id . " already has a contact for service code " . $self->_current_service->{service_code} . "\n" if $self->verbose >= 2; if ( $contact->state eq 'deleted' || $service_name ne $contact->category || $self->_current_service->{service_code} ne $contact->email ) { @@ -370,7 +372,11 @@ sub _delete_contacts_not_in_service_list { sub _delete_contacts_not_in_service_list_cobrand_overrides { my ( $self, $found_contacts ) = @_; - return $found_contacts; + if ($self->_current_body_cobrand && $self->_current_body_cobrand->can('open311_filter_contacts_for_deletion')) { + return $self->_current_body_cobrand->open311_filter_contacts_for_deletion($found_contacts); + } else { + return $found_contacts; + } } 1; -- cgit v1.2.3 From c3b4b23b378f6d14881675556571e912d1a52ee0 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Fri, 13 Mar 2020 13:07:47 +0000 Subject: Improve category import script Adds ability for categories to be marked as devolved with their own send_method, and for the open311_protect flag to be set. --- bin/import_categories | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/import_categories b/bin/import_categories index 744759f1f..23a1089f5 100644 --- a/bin/import_categories +++ b/bin/import_categories @@ -46,11 +46,10 @@ if (!$opt->commit) { my $config = decode_json(path($ARGV[0])->slurp_utf8); my $body = FixMyStreet::DB->resultset('Body')->find({ name => $opt->body }); +die "Couldn't find body " . $opt->body unless $body; $body->contacts->delete_all if $opt->delete; -die "Couldn't find body" unless $body; - my $groups = $config->{groups}; if ($groups) { for my $group (keys %$groups) { @@ -72,12 +71,14 @@ sub make_categories { category => $cat->{category} }); $child_cat->email($cat->{email}); + $child_cat->send_method($cat->{devolved}) if $cat->{devolved}; $child_cat->state('confirmed'); $child_cat->editor($0); $child_cat->whenedited(\'current_timestamp'); $child_cat->note($child_cat->in_storage ? 'Updated by import_categories' : 'Created by import_categories'); say colored("WARNING", 'red') . " " . $child_cat->category . " already exists" if $child_cat->in_storage and $child_cat->category ne 'Other (TfL)'; $child_cat->extra(undef) if $child_cat->in_storage; + $child_cat->set_extra_metadata(open311_protect => 1) if $cat->{open311_protect}; if ($group) { my $groups = $child_cat->groups; -- cgit v1.2.3 From 53b9f82cbb7fe81484b8bbf434f0b0acd925b454 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 11 May 2020 12:43:02 +0100 Subject: [Hackney] Special destination handling. --- perllib/FixMyStreet/Cobrand/Default.pm | 3 +- perllib/FixMyStreet/Cobrand/Hackney.pm | 62 +++++++++++++++++++++++- perllib/FixMyStreet/Cobrand/UKCouncils.pm | 2 +- perllib/FixMyStreet/Cobrand/Zurich.pm | 2 +- perllib/FixMyStreet/Queue/Item/Report.pm | 2 +- perllib/Open311/PostServiceRequestUpdates.pm | 2 +- t/cobrand/get_body_sender.t | 18 ++----- t/cobrand/hackney.t | 71 ++++++++++++++++++++++++---- 8 files changed, 133 insertions(+), 29 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 73340338b..bd8b7e4b4 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -957,9 +957,10 @@ Get stats to display on the council reports page sub get_report_stats { return 0; } sub get_body_sender { - my ( $self, $body, $category ) = @_; + my ( $self, $body, $problem ) = @_; # look up via category + my $category = $problem->category; my $contact = $body->contacts->search( { category => $category } )->first; if ( $body->can_be_devolved && $contact && $contact->send_method ) { return { method => $contact->send_method, config => $contact, contact => $contact }; diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index f08cd0dd9..dbf070628 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -3,6 +3,7 @@ use parent 'FixMyStreet::Cobrand::Whitelabel'; use strict; use warnings; +use mySociety::EmailUtil qw(is_valid_email); sub council_area_id { return 2508; } sub council_area { return 'Hackney'; } @@ -33,7 +34,7 @@ sub open311_config { } sub open311_extra_data { - my ($self, $row, $h, $extra) = @_; + my ($self, $row, $h, $extra, $contact) = @_; my $open311_only = [ { name => 'report_url', @@ -46,6 +47,13 @@ sub open311_extra_data { value => $row->category }, ]; + # Make sure contact 'email' set correctly for Open311 + if (my $sent_to = $row->get_extra_metadata('sent_to')) { + $row->unset_extra_metadata('sent_to'); + my $code = $sent_to->{$contact->email}; + $contact->email($code) if $code; + } + return $open311_only; } @@ -80,4 +88,56 @@ sub open311_filter_contacts_for_deletion { }); } +sub lookup_site_code_config { + my ($self, $type) = @_; + my $property_map = { + park => "greenspaces:hackney_park", + estate => "housing:lbh_estate", + }; + { + buffer => 3, # metres + url => "https://map.hackney.gov.uk/geoserver/wfs", + srsname => "urn:ogc:def:crs:EPSG::27700", + typename => $property_map->{$type}, + property => ( $type eq "park" ? "park_id" : "id" ), + accept_feature => sub { 1 }, + accept_types => { Polygon => 1 }, + outputformat => "json", + } +} + +sub get_body_sender { + my ( $self, $body, $problem ) = @_; + + my $contact = $body->contacts->search( { category => $problem->category } )->first; + + my $parts = join '\s*', qw(^ park : (.*?) ; estate : (.*?) ; other : (.*?) $); + my $regex = qr/$parts/i; + if (my ($park, $estate, $other) = $contact->email =~ $regex) { + my $to = $other; + if (my $park_id = $self->lookup_site_code($problem, 'park')) { + $to = $park; + } elsif (my $estate_id = $self->lookup_site_code($problem, 'estate')) { + $to = $estate; + } + $problem->set_extra_metadata(sent_to => { $contact->email => $to }); + if (is_valid_email($to)) { + return { method => 'Email', contact => $contact }; + } + } + return $self->SUPER::get_body_sender($body, $problem); +} + +# Translate email address to actual delivery address +sub munge_sendreport_params { + my ($self, $row, $h, $params) = @_; + + my $sent_to = $row->get_extra_metadata('sent_to') or return; + $row->unset_extra_metadata('sent_to'); + for my $recip (@{$params->{To}}) { + my ($email, $name) = @$recip; + $recip->[0] = $sent_to->{$email} if $sent_to->{$email}; + } +} + 1; diff --git a/perllib/FixMyStreet/Cobrand/UKCouncils.pm b/perllib/FixMyStreet/Cobrand/UKCouncils.pm index 21dd2d455..7456d9ddf 100644 --- a/perllib/FixMyStreet/Cobrand/UKCouncils.pm +++ b/perllib/FixMyStreet/Cobrand/UKCouncils.pm @@ -392,7 +392,7 @@ sub _fetch_features_url { SRSNAME => $cfg->{srsname}, TYPENAME => $cfg->{typename}, VERSION => "1.1.0", - outputformat => "geojson", + outputformat => $cfg->{outputformat} || "geojson", $cfg->{filter} ? ( Filter => $cfg->{filter} ) : ( BBOX => $cfg->{bbox} ), ); diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index 3cf678f9c..42932ec41 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -217,7 +217,7 @@ sub allow_photo_display { } sub get_body_sender { - my ( $self, $body, $category ) = @_; + my ( $self, $body, $problem ) = @_; return { method => 'Zurich' }; } diff --git a/perllib/FixMyStreet/Queue/Item/Report.pm b/perllib/FixMyStreet/Queue/Item/Report.pm index e38987838..60e9ad3dc 100644 --- a/perllib/FixMyStreet/Queue/Item/Report.pm +++ b/perllib/FixMyStreet/Queue/Item/Report.pm @@ -172,7 +172,7 @@ sub _create_reporters { my @dear; my %reporters = (); while (my $body = $bodies->next) { - my $sender_info = $self->cobrand->get_body_sender( $body, $row->category ); + my $sender_info = $self->cobrand_handler->get_body_sender( $body, $row ); my $sender = "FixMyStreet::SendReport::" . $sender_info->{method}; if ( ! exists $self->senders->{ $sender } ) { diff --git a/perllib/Open311/PostServiceRequestUpdates.pm b/perllib/Open311/PostServiceRequestUpdates.pm index d7345ea4d..a31bca8f7 100755 --- a/perllib/Open311/PostServiceRequestUpdates.pm +++ b/perllib/Open311/PostServiceRequestUpdates.pm @@ -50,7 +50,7 @@ sub open311_params { my $conf = $body; if ($comment) { my $cobrand_logged = $comment->get_cobrand_logged; - my $sender = $cobrand_logged->get_body_sender($body, $comment->problem->category); + my $sender = $cobrand_logged->get_body_sender($body, $comment->problem); $conf = $sender->{config}; } diff --git a/t/cobrand/get_body_sender.t b/t/cobrand/get_body_sender.t index 06ffb42a5..a1e8f2320 100644 --- a/t/cobrand/get_body_sender.t +++ b/t/cobrand/get_body_sender.t @@ -6,31 +6,21 @@ use_ok 'FixMyStreet::Cobrand'; my $c = FixMyStreet::Cobrand::FixMyStreet->new(); -FixMyStreet::DB->resultset('BodyArea')->search( { body_id => 1000 } )->delete; -FixMyStreet::DB->resultset('Body')->search( { name => 'Body of a Thousand' } )->delete; - my $body = FixMyStreet::DB->resultset('Body')->find_or_create({ id => 1000, name => 'Body of a Thousand', }); -my $body_area = $body->body_areas->find_or_create({ area_id => 1000 }); + +my $problem = FixMyStreet::DB->resultset('Problem')->new({}); FixMyStreet::override_config { MAPIT_TYPES => [ 'LBO' ], MAPIT_URL => 'http://mapit.uk/', # Not actually used as no special casing at present }, sub { - is_deeply $c->get_body_sender( $body ), { method => 'Email', contact => undef }, 'defaults to email'; - $body_area->update({ area_id => 2481 }); # Croydon LBO - is_deeply $c->get_body_sender( $body ), { method => 'Email', contact => undef }, 'still email if London borough'; + is_deeply $c->get_body_sender( $body, $problem ), { method => 'Email', contact => undef }, 'defaults to email'; }; $body->send_method( 'TestMethod' ); -is $c->get_body_sender( $body )->{ method }, 'TestMethod', 'uses send_method in preference to London'; - -$body_area->update({ area_id => 1000 }); # Nothing -is $c->get_body_sender( $body )->{ method }, 'TestMethod', 'uses send_method in preference to Email'; - -$body_area->delete; -$body->delete; +is $c->get_body_sender( $body, $problem )->{ method }, 'TestMethod', 'uses send_method in preference to Email'; done_testing(); diff --git a/t/cobrand/hackney.t b/t/cobrand/hackney.t index 519b1b3b8..82fefc15b 100644 --- a/t/cobrand/hackney.t +++ b/t/cobrand/hackney.t @@ -53,16 +53,11 @@ my $contact2 = $mech->create_contact_ok( body_id => $hackney->id, category => 'Roads', email => 'roads@example.org', - send_method => 'Triage', + send_method => 'Email', ); my $admin_user = $mech->create_user_ok('admin-user@example.org', name => 'Admin User', from_body => $hackney); -$admin_user->user_body_permissions->create({ - body => $hackney, - permission_type => 'triage' -}); - my @reports = $mech->create_problems_for_body(1, $hackney->id, 'A Hackney report', { confirmed => '2019-10-25 09:00', lastupdate => '2019-10-25 09:00', @@ -129,7 +124,6 @@ subtest "sends branded alert emails" => 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/Hackney Council/, "emails are branded"; @@ -168,7 +162,6 @@ subtest "sends branded confirmation emails" => sub { "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/Hackney Council/, "emails are branded"; @@ -179,6 +172,67 @@ subtest "sends branded confirmation emails" => sub { }; }; +FixMyStreet::override_config { + STAGING_FLAGS => { send_reports => 1 }, + MAPIT_URL => 'http://mapit.uk/', + ALLOWED_COBRANDS => ['hackney', 'fixmystreet'], +}, sub { + subtest "special send handling" => sub { + my $cbr = Test::MockModule->new('FixMyStreet::Cobrand::Hackney'); + my $p = FixMyStreet::DB->resultset("Problem")->search(undef, { order_by => { -desc => 'id' } })->first; + $contact2->update({ email => 'park:parks@example;estate:estates@example;other:OTHER', send_method => '' }); + + subtest 'in a park' => sub { + $cbr->mock('_fetch_features', sub { + my ($self, $cfg, $x, $y) = @_; + return [{ + properties => { park_id => 'park' }, + geometry => { + type => 'Polygon', + coordinates => [ [ [ $x-1, $y-1 ], [ $x+1, $y+1 ] ] ], + } + }] if $cfg->{typename} eq 'greenspaces:hackney_park'; + }); + FixMyStreet::Script::Reports::send(); + my $email = $mech->get_email; + is $email->header('To'), '"Hackney Council" '; + $mech->clear_emails_ok; + $p->discard_changes; + $p->update({ whensent => undef }); + }; + + subtest 'in an estate' => sub { + $cbr->mock('_fetch_features', sub { + my ($self, $cfg, $x, $y) = @_; + return [{ + properties => { id => 'estate' }, + geometry => { + type => 'Polygon', + coordinates => [ [ [ $x-1, $y-1 ], [ $x+1, $y+1 ] ] ], + } + }] if $cfg->{typename} eq 'housing:lbh_estate'; + }); + FixMyStreet::Script::Reports::send(); + my $email = $mech->get_email; + is $email->header('To'), '"Hackney Council" '; + $mech->clear_emails_ok; + $p->discard_changes; + $p->update({ whensent => undef }); + }; + + subtest 'elsewhere' => sub { + $cbr->mock('_fetch_features', sub { + my ($self, $cfg, $x, $y) = @_; + return []; # Not in park or estate + }); + my $test_data = FixMyStreet::Script::Reports::send(); + my $req = $test_data->{test_req_used}; + my $c = CGI::Simple->new($req->content); + is $c->param('service_code'), 'OTHER'; + }; + }; +}; + #subtest "sends branded report sent emails" => sub { #$mech->clear_emails_ok; #FixMyStreet::override_config { @@ -188,7 +242,6 @@ subtest "sends branded confirmation emails" => sub { #}, 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/Hackney Council/, "emails are branded"; -- cgit v1.2.3 From 3b958bc30c5ccb6ea3143c08d1ca65dc0bf4b9bc Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Wed, 13 May 2020 16:38:09 +0100 Subject: Rename O::L::C::W::Azure to O::L::C::W::AuthCodeFlow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns out there’s nothing strictly Azure-specific about it. --- perllib/FixMyStreet/App/Controller/Auth/Social.pm | 4 +-- perllib/OIDC/Lite/Client/WebServer/AuthCodeFlow.pm | 42 ++++++++++++++++++++++ perllib/OIDC/Lite/Client/WebServer/Azure.pm | 39 -------------------- 3 files changed, 44 insertions(+), 41 deletions(-) create mode 100644 perllib/OIDC/Lite/Client/WebServer/AuthCodeFlow.pm delete mode 100644 perllib/OIDC/Lite/Client/WebServer/Azure.pm diff --git a/perllib/FixMyStreet/App/Controller/Auth/Social.pm b/perllib/FixMyStreet/App/Controller/Auth/Social.pm index 06e67573f..54cf35315 100644 --- a/perllib/FixMyStreet/App/Controller/Auth/Social.pm +++ b/perllib/FixMyStreet/App/Controller/Auth/Social.pm @@ -6,7 +6,7 @@ BEGIN { extends 'Catalyst::Controller'; } use Net::Facebook::Oauth2; use Net::Twitter::Lite::WithAPIv1_1; -use OIDC::Lite::Client::WebServer::Azure; +use OIDC::Lite::Client::WebServer::AuthCodeFlow; use URI::Escape; use mySociety::AuthToken; @@ -167,7 +167,7 @@ sub oidc : Private { my $config = $c->cobrand->feature('oidc_login'); - OIDC::Lite::Client::WebServer::Azure->new( + OIDC::Lite::Client::WebServer::AuthCodeFlow->new( id => $config->{client_id}, secret => $config->{secret}, authorize_uri => $config->{auth_uri}, diff --git a/perllib/OIDC/Lite/Client/WebServer/AuthCodeFlow.pm b/perllib/OIDC/Lite/Client/WebServer/AuthCodeFlow.pm new file mode 100644 index 000000000..33a9a788f --- /dev/null +++ b/perllib/OIDC/Lite/Client/WebServer/AuthCodeFlow.pm @@ -0,0 +1,42 @@ +package OIDC::Lite::Client::WebServer::AuthCodeFlow; + +use strict; +use warnings; +use parent 'OIDC::Lite::Client::WebServer'; + +use OIDC::Lite::Client::IDTokenResponseParser; + +=head1 NAME + +OIDC::Lite::Client::WebServer::AuthCodeFlow - extension to auth against an +identity provider using the authorization code flow, such as Azure AD B2C or +Google OAuth 2.0. +More info: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowSteps + +OIDC::Lite doesn't appear to support the authorisation code flow to get an +ID token - only an access token. This flow returns all its claims in the id_token +(and may not support a UserInfo endpoint e.g. Azure AD B2C), so this extension +adds support for parsing the id_token when calling get_access_token. + +=cut + +=head2 new + +Overrides response_parser so that get_access_token returns a +L object. + +NB this does not perform any verification of the id_token. It's assumed to be +safe as it's come directly from the OpenID IdP and not an untrusted user's +browser. + +=cut + +sub new { + my $self = shift->next::method(@_); + + $self->{response_parser} = OIDC::Lite::Client::IDTokenResponseParser->new; + + return $self; +} + +1; diff --git a/perllib/OIDC/Lite/Client/WebServer/Azure.pm b/perllib/OIDC/Lite/Client/WebServer/Azure.pm deleted file mode 100644 index b19dce90e..000000000 --- a/perllib/OIDC/Lite/Client/WebServer/Azure.pm +++ /dev/null @@ -1,39 +0,0 @@ -package OIDC::Lite::Client::WebServer::Azure; - -use strict; -use warnings; -use parent 'OIDC::Lite::Client::WebServer'; - -use OIDC::Lite::Client::IDTokenResponseParser; - -=head1 NAME - -OIDC::Lite::Client::WebServer::Azure - extension to auth against Azure AD B2C - -OIDC::Lite doesn't appear to support the authorisation code flow to get an -ID token - only an access token. Azure returns all its claims in the id_token -and doesn't support a UserInfo endpoint, so this extension adds support for -parsing the id_token when calling get_access_token. - -=cut - -=head2 new - -Overrides response_parser so that get_access_token returns a -L object. - -NB this does not perform any verification of the id_token. It's assumed to be -safe as it's come directly from the OpenID IdP and not an untrusted user's -browser. - -=cut - -sub new { - my $self = shift->next::method(@_); - - $self->{response_parser} = OIDC::Lite::Client::IDTokenResponseParser->new; - - return $self; -} - -1; -- cgit v1.2.3 From c505cbca2576f9e658692d8e1c512f645069985a Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Wed, 13 May 2020 16:41:26 +0100 Subject: OIDC scope/token parsing improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Cobrand config can now specify custom scope and other params e.g. G Suite supports per-domain customisation and the ‘prompt’ param to always ask the user to select the account they want to login with. - Token may have an ‘name’ claim instead of needing to concat given_/family_name claims --- CHANGELOG.md | 1 + perllib/FixMyStreet/App/Controller/Auth/Social.pm | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93292785c..e22d3cc63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - Skip accounts without email when sending inactive emails. - Include file extensions in Dropzone accepted photo config. - Fix photo orientation in modern browsers. + - Improve compatibility with G Suite OpenID Connect authentication. #3032 - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/perllib/FixMyStreet/App/Controller/Auth/Social.pm b/perllib/FixMyStreet/App/Controller/Auth/Social.pm index 54cf35315..2468cfad2 100644 --- a/perllib/FixMyStreet/App/Controller/Auth/Social.pm +++ b/perllib/FixMyStreet/App/Controller/Auth/Social.pm @@ -179,7 +179,9 @@ sub oidc_sign_in : Private { my ( $self, $c ) = @_; $c->detach( '/page_error_403_access_denied', [] ) if FixMyStreet->config('SIGNUPS_DISABLED'); - $c->detach( '/page_error_400_bad_request', [] ) unless $c->cobrand->feature('oidc_login'); + + my $cfg = $c->cobrand->feature('oidc_login'); + $c->detach( '/page_error_400_bad_request', [] ) unless $cfg; my $oidc = $c->forward('oidc'); my $nonce = $self->generate_nonce(); @@ -190,6 +192,15 @@ sub oidc_sign_in : Private { extra => { response_mode => 'form_post', nonce => $nonce, + # auth_extra_params provides a way to pass custom parameters + # to the OIDC endpoint for the intial authentication request. + # This allows, for example, a custom scope to be used, + # or the `hd` parameter which customises the appearance of + # the login form. + # This is primarily useful for Google G Suite authentication - see + # available parameters here: + # https://developers.google.com/identity/protocols/oauth2/openid-connect#authenticationuriparameters + %{ $cfg->{auth_extra_params} || {} } , }, ); @@ -201,14 +212,14 @@ sub oidc_sign_in : Private { # The OIDC endpoint may require a specific URI to be called to log the user # out when they log out of FMS. - if ( my $redirect_uri = $c->cobrand->feature('oidc_login')->{logout_uri} ) { + if ( my $redirect_uri = $cfg->{logout_uri} ) { $redirect_uri .= "?post_logout_redirect_uri="; $redirect_uri .= URI::Escape::uri_escape( $c->uri_for('/auth/sign_out') ); $oauth{logout_redirect_uri} = $redirect_uri; } # The OIDC endpoint may provide a specific URI for changing the user's password. - if ( my $password_change_uri = $c->cobrand->feature('oidc_login')->{password_change_uri} ) { + if ( my $password_change_uri = $cfg->{password_change_uri} ) { $oauth{change_password_uri} = $oidc->uri_to_redirect( uri => $password_change_uri, redirect_uri => $c->uri_for('/auth/OIDC'), @@ -295,9 +306,9 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) { $c->detach('/page_error_500_internal_error', ['invalid id_token']) unless $id_token->payload->{nonce} eq $c->session->{oauth}{nonce}; # Some claims need parsing into a friendlier format - # XXX check how much of this is Westminster/Azure-specific - my $name = join(" ", $id_token->payload->{given_name}, $id_token->payload->{family_name}); + my $name = $id_token->payload->{name} || join(" ", $id_token->payload->{given_name}, $id_token->payload->{family_name}); my $email = $id_token->payload->{email}; + # WCC Azure provides a single email address as an array for some reason my $emails = $id_token->payload->{emails}; if ($emails && @$emails) { -- cgit v1.2.3 From 7e4ebd6568a836683f5f374ce6f0d9ed97d38c54 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Wed, 13 May 2020 16:42:04 +0100 Subject: Include redirect URI when fetching OIDC access token --- perllib/FixMyStreet/App/Controller/Auth/Social.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/perllib/FixMyStreet/App/Controller/Auth/Social.pm b/perllib/FixMyStreet/App/Controller/Auth/Social.pm index 2468cfad2..c9e0381a4 100644 --- a/perllib/FixMyStreet/App/Controller/Auth/Social.pm +++ b/perllib/FixMyStreet/App/Controller/Auth/Social.pm @@ -290,6 +290,7 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) { eval { $id_token = $oidc->get_access_token( code => $c->get_param('code'), + redirect_uri => $c->uri_for('/auth/OIDC') ); }; if ($@) { -- cgit v1.2.3 From 41ed9db574ecb77504992f3836ced7f25fe1768d Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Wed, 13 May 2020 16:42:47 +0100 Subject: [Hackney] Enable OIDC social login --- perllib/FixMyStreet/Cobrand/Hackney.pm | 6 ++++ t/Mock/OpenIDConnect.pm | 48 ++++++++++++++++++++++++++ t/app/controller/auth_social.t | 62 +++++++++++++++++++++++++++++----- 3 files changed, 108 insertions(+), 8 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/Hackney.pm b/perllib/FixMyStreet/Cobrand/Hackney.pm index dbf070628..b0c815d70 100644 --- a/perllib/FixMyStreet/Cobrand/Hackney.pm +++ b/perllib/FixMyStreet/Cobrand/Hackney.pm @@ -63,6 +63,12 @@ sub default_map_zoom { 5 } sub admin_user_domain { 'hackney.gov.uk' } +sub social_auth_enabled { + my $self = shift; + + return $self->feature('oidc_login') ? 1 : 0; +} + sub anonymous_account { my $self = shift; return { diff --git a/t/Mock/OpenIDConnect.pm b/t/Mock/OpenIDConnect.pm index ba7d03b1d..61a67f329 100644 --- a/t/Mock/OpenIDConnect.pm +++ b/t/Mock/OpenIDConnect.pm @@ -27,6 +27,11 @@ sub dispatch_request { return [ 200, [ 'Content-Type' => 'text/html' ], [ 'OpenID Connect login page' ] ]; }, + sub (GET + /oauth2/v2.0/authorize_google + ?*) { + my ($self) = @_; + return [ 200, [ 'Content-Type' => 'text/html' ], [ 'OpenID Connect login page' ] ]; + }, + sub (GET + /oauth2/v2.0/logout + ?*) { my ($self) = @_; return [ 200, [ 'Content-Type' => 'text/html' ], [ 'OpenID Connect logout page' ] ]; @@ -72,6 +77,49 @@ sub dispatch_request { my $json = $self->json->encode($data); return [ 200, [ 'Content-Type' => 'application/json' ], [ $json ] ]; }, + + sub (POST + /oauth2/v2.0/token_google + ?*) { + my ($self) = @_; + my $header = { + typ => "JWT", + alg => "RS256", + kid => "XXXfakeKEY1234", + }; + my $now = DateTime->now->epoch; + my $payload = { + exp => $now + 3600, + nbf => $now, + locale => 'en-GB', + ver => "1.0", + iss => 'https://accounts.google.com', + sub => "my_google_user_id", + aud => "example_client_id", + iat => $now, + auth_time => $now, + given_name => "Andy", + family_name => "Dwyer", + name => "Andy Dwyer", + nonce => 'MyAwesomeRandomValue', + hd => 'example.org', + }; + $payload->{email} = 'pkg-tappcontrollerauth_socialt-oidc_google@example.org' if $self->returns_email; + $payload->{email_verified} = JSON->true if $self->returns_email; + my $signature = "dummy"; + my $id_token = join(".", ( + encode_base64($self->json->encode($header), ''), + encode_base64($self->json->encode($payload), ''), + encode_base64($signature, '') + )); + my $data = { + id_token => $id_token, + token_type => "Bearer", + not_before => $now, + id_token_expires_in => 3600, + profile_info => encode_base64($self->json->encode({}), ''), + }; + my $json = $self->json->encode($data); + return [ 200, [ 'Content-Type' => 'application/json' ], [ $json ] ]; + }, } __PACKAGE__->run_if_script; diff --git a/t/app/controller/auth_social.t b/t/app/controller/auth_social.t index 200863029..e56fcda98 100644 --- a/t/app/controller/auth_social.t +++ b/t/app/controller/auth_social.t @@ -15,9 +15,12 @@ FixMyStreet::App->log->disable('info'); END { FixMyStreet::App->log->enable('info'); } my $body = $mech->create_body_ok(2504, 'Westminster City Council'); +my $body2 = $mech->create_body_ok(2508, 'Hackney Council'); my ($report) = $mech->create_problems_for_body(1, $body->id, 'My Test Report'); my $test_email = $report->user->email; +my ($report2) = $mech->create_problems_for_body(1, $body2->id, 'My Test Report'); +my $test_email2 = $report->user->email; my $contact = $mech->create_contact_ok( body_id => $body->id, category => 'Damaged bin', email => 'BIN', @@ -26,11 +29,21 @@ my $contact = $mech->create_contact_ok( { code => 'bin_service', description => 'Service needed', required => 'False' }, ] ); +$mech->create_contact_ok( + body_id => $body2->id, category => 'Damaged bin', email => 'BIN', + extra => [ + { code => 'bin_type', description => 'Type of bin', required => 'True' }, + { code => 'bin_service', description => 'Service needed', required => 'False' }, + ] +); # Two options, incidentally, so that the template "Only one option, select it" # code doesn't kick in and make the tests pass my $contact2 = $mech->create_contact_ok( body_id => $body->id, category => 'Whatever', email => 'WHATEVER', ); +$mech->create_contact_ok( + body_id => $body2->id, category => 'Whatever', email => 'WHATEVER', +); my $resolver = Test::MockModule->new('Email::Valid'); my $social = Test::MockModule->new('FixMyStreet::App::Controller::Auth::Social'); @@ -88,6 +101,38 @@ for my $test ( user_extras => [ [westminster_account_id => "1c304134-ef12-c128-9212-123908123901"], ], +}, { + type => 'oidc', + config => { + ALLOWED_COBRANDS => 'hackney', + MAPIT_URL => 'http://mapit.uk/', + COBRAND_FEATURES => { + anonymous_account => { + hackney => 'test', + }, + oidc_login => { + hackney => { + client_id => 'example_client_id', + secret => 'example_secret_key', + auth_uri => 'http://oidc.example.org/oauth2/v2.0/authorize_google', + token_uri => 'http://oidc.example.org/oauth2/v2.0/token_google', + allowed_domains => [ 'example.org' ], + } + } + } + }, + email => $mech->uniquify_email('oidc_google@example.org'), + uid => "hackney:example_client_id:my_google_user_id", + mock => 't::Mock::OpenIDConnect', + mock_hosts => ['oidc.example.org'], + host => 'oidc.example.org', + error_callback => '/auth/OIDC?error=ERROR', + success_callback => '/auth/OIDC?code=response-code&state=login', + redirect_pattern => qr{oidc\.example\.org/oauth2/v2\.0/authorize_google}, + pc => 'E8 1DY', + # Need to use a different report that's within Hackney + report => $report2, + report_email => $test_email2, } ) { @@ -100,6 +145,7 @@ for my $state ( 'refused', 'no email', 'existing UID', 'okay' ) { next if $page eq 'update' && !$test->{update}; subtest "test $test->{type} '$state' login for page '$page'" => sub { + my $test_report = $test->{report} || $report; # Lots of user changes happening here, make sure we don't confuse # Catalyst with a cookie session user that no longer exists $mech->log_out_ok; @@ -115,9 +161,9 @@ for my $state ( 'refused', 'no email', 'existing UID', 'okay' ) { $mech->delete_user($test->{email}); } if ($page eq 'my' && $state eq 'existing UID') { - $report->update({ user_id => FixMyStreet::DB->resultset( 'User' )->find( { email => $test->{email} } )->id }); + $test_report->update({ user_id => FixMyStreet::DB->resultset( 'User' )->find( { email => $test->{email} } )->id }); } else { - $report->update({ user_id => FixMyStreet::DB->resultset( 'User' )->find( { email => $test_email } )->id }); + $test_report->update({ user_id => FixMyStreet::DB->resultset( 'User' )->find( { email => ($report->{test_email} || $test_email) } )->id }); } # Set up a mock to catch (most, see below) requests to the OAuth API @@ -139,7 +185,7 @@ for my $state ( 'refused', 'no email', 'existing UID', 'okay' ) { $mech->get_ok('/my'); } elsif ($page eq 'report') { $mech->get_ok('/'); - $mech->submit_form_ok( { with_fields => { pc => 'SW1A1AA' } }, "submit location" ); + $mech->submit_form_ok( { with_fields => { pc => $test->{pc} || 'SW1A1AA' } }, "submit location" ); $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); $mech->submit_form(with_fields => { category => 'Damaged bin', @@ -150,7 +196,7 @@ for my $state ( 'refused', 'no email', 'existing UID', 'okay' ) { bin_type => 'Salt bin', }; } else { - $mech->get_ok('/report/' . $report->id); + $mech->get_ok('/report/' . $test_report->id); $fields = { update => 'Test update', }; @@ -243,17 +289,17 @@ for my $state ( 'refused', 'no email', 'existing UID', 'okay' ) { } } if ($state eq 'existing UID') { - my $report_id = $report->id; - $mech->content_contains( $report->title ); + my $report_id = $test_report->id; + $mech->content_contains( $test_report->title ); $mech->content_contains( "/report/$report_id" ); } - if ($test->{type} eq 'oidc') { + if ($test->{type} eq 'oidc' && $test->{password_change_pattern}) { ok $mech->find_link( text => 'Change password', url_regex => $test->{password_change_pattern} ); } } $mech->get('/auth/sign_out'); - if ($test->{type} eq 'oidc' && $state ne 'refused' && $state ne 'no email') { + if ($test->{type} eq 'oidc' && $test->{logout_redirect_pattern} && $state ne 'refused' && $state ne 'no email') { # XXX the 'no email' situation is skipped because of some confusion # with the hosts/sessions that I've not been able to get to the bottom of. # The code does behave as expected when testing manually, however. -- cgit v1.2.3 From c5b0d088eb76dcf7290dae5d4d2d208f7031d13d Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Tue, 19 May 2020 08:39:42 +0100 Subject: [Hackney] Add staff sign-in link to login forms --- templates/web/hackney/auth/general.html | 88 +++++++++++++++++++++++++++++ templates/web/hackney/report/form/user.html | 29 ++++++++++ web/cobrands/hackney/base.scss | 7 ++- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 templates/web/hackney/auth/general.html create mode 100644 templates/web/hackney/report/form/user.html diff --git a/templates/web/hackney/auth/general.html b/templates/web/hackney/auth/general.html new file mode 100644 index 000000000..1a9e4a060 --- /dev/null +++ b/templates/web/hackney/auth/general.html @@ -0,0 +1,88 @@ +[% INCLUDE 'header.html', bodyclass='authpage', title = loc('Sign in or create an account') %] + +

    + [% loc('Sign in') %] + + [% tprintf(loc('or create an account'), '/auth/create') %] + +

    + +[% TRY %][% INCLUDE 'auth/_general_top.html' %][% CATCH file %][% END %] + +[% IF oauth_need_email %] +

    [% loc('We need your email address, please give it below.') %]

    +[% END %] +[% IF oauth_failure %] +

    [% loc('Sorry, we could not log you in. Please fill in the form below.') %]

    +[% END %] + +
    +
    + + + + [% loc_username_error = INCLUDE 'auth/_username_error.html' default='email' %] + +[% IF c.config.SMS_AUTHENTICATION %] + [% SET username_label = loc('Your email or mobile') %] +[% ELSE %] + [% SET username_label = loc('Your email') %] +[% END %] + + + [% IF loc_username_error %] +
    [% loc_username_error %]
    + [% END %] + + +
    + [% IF oauth_need_email %] + [% INCLUDE form_sign_in_no %] + + [% ELSE %] + [% INCLUDE form_sign_in_yes %] + [% INCLUDE form_sign_in_no %] + [% INCLUDE form_sign_in_hackney_staff %] + [% END %] +
    +
    +
    + +[% INCLUDE 'footer.html' %] + +[% BLOCK form_sign_in_yes %] + + +[% END %] + +[% BLOCK form_sign_in_no %] +

    +[% END %] + +[% BLOCK form_sign_in_hackney_staff %] + [% IF c.cobrand.feature('oidc_login') %] + + [% END %] +[% END %] \ No newline at end of file diff --git a/templates/web/hackney/report/form/user.html b/templates/web/hackney/report/form/user.html new file mode 100644 index 000000000..bced8c189 --- /dev/null +++ b/templates/web/hackney/report/form/user.html @@ -0,0 +1,29 @@ + +
    +

    + [% loc('Next:') %] [% loc('Tell us about you') %] +

    + +[% IF NOT c.user_exists AND c.cobrand.feature('oidc_login') %] + +[% END %] +
    + [% loc('or') %] + + [% loc('No personal details will be stored, and you will not receive updates about this report.') %] +
    +
    + +[% IF (c.user_exists OR NOT c.cobrand.social_auth_enabled) AND type == 'report' AND c.cobrand.allow_anonymous_reports == 'button' %] +
    + + [% loc('No personal details will be stored, and you will not receive updates about this report.') %] + [% loc('or') %] +
    +[% END %] + + diff --git a/web/cobrands/hackney/base.scss b/web/cobrands/hackney/base.scss index a3e41c5d0..4bc3fc8b1 100644 --- a/web/cobrands/hackney/base.scss +++ b/web/cobrands/hackney/base.scss @@ -214,4 +214,9 @@ select.form-error { .dz-clickable:hover .dz-message u, .dz-drag-hover .dz-message u { color: $green; -} \ No newline at end of file +} + +.sso-staff-sign-in { + font-size: 0.9em; + margin: 1em 0; +} -- cgit v1.2.3 From aad79063d6e01f7596da564b274b3bf78413fdfa Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Mon, 1 Jun 2020 13:49:52 +0100 Subject: Add allowed_domains OIDC config to limit logins to specific domains --- perllib/FixMyStreet/App/Controller/Auth/Social.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/perllib/FixMyStreet/App/Controller/Auth/Social.pm b/perllib/FixMyStreet/App/Controller/Auth/Social.pm index c9e0381a4..ce94fe256 100644 --- a/perllib/FixMyStreet/App/Controller/Auth/Social.pm +++ b/perllib/FixMyStreet/App/Controller/Auth/Social.pm @@ -306,6 +306,14 @@ sub oidc_callback: Path('/auth/OIDC') : Args(0) { # check that the nonce matches what we set in the user session $c->detach('/page_error_500_internal_error', ['invalid id_token']) unless $id_token->payload->{nonce} eq $c->session->{oauth}{nonce}; + if (my $domains = $c->cobrand->feature('oidc_login')->{allowed_domains}) { + # Check that the hd payload is present in the token and matches the + # list of allowed domains from the config + my $hd = $id_token->payload->{hd}; + my %allowed_domains = map { $_ => 1} @$domains; + $c->detach('oauth_failure') unless $allowed_domains{$hd}; + } + # Some claims need parsing into a friendlier format my $name = $id_token->payload->{name} || join(" ", $id_token->payload->{given_name}, $id_token->payload->{family_name}); my $email = $id_token->payload->{email}; -- cgit v1.2.3 From 832dd8e367b9c83619981bf9828dea405d15ce02 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Tue, 16 Jun 2020 16:23:03 +0100 Subject: Display contents of lists in report extra data in admin --- t/app/controller/admin/report_edit.t | 20 ++++++++++++++++---- templates/web/base/admin/reports/edit.html | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/t/app/controller/admin/report_edit.t b/t/app/controller/admin/report_edit.t index 01f091412..f8101ab76 100644 --- a/t/app/controller/admin/report_edit.t +++ b/t/app/controller/admin/report_edit.t @@ -686,16 +686,28 @@ subtest "Test display of fields extra data" => sub { $mech->get_ok("/admin/report_edit/$report_id"); $mech->content_contains('Extra data: No'); - $report->push_extra_fields( { - name => 'report_url', - value => 'http://example.com', - }); + $report->push_extra_fields( + { + name => 'report_url', + value => 'http://example.com', + }, + { + name => 'sent_to', + value => [ 'onerecipient@example.org' ], + }, + { + name => 'sent_too', + value => [ 'onemorerecipient@example.org', 'another@example.org' ], + }, + ); $report->update; $report->discard_changes; $mech->get_ok("/admin/report_edit/$report_id"); $mech->content_contains('report_url: http://example.com'); + $mech->content_contains('sent_to: onerecipient@example.org'); + $mech->content_contains('sent_too: onemorerecipient@example.org, another@example.org'); $report->set_extra_fields( { description => 'Report URL', diff --git a/templates/web/base/admin/reports/edit.html b/templates/web/base/admin/reports/edit.html index d2b866d01..6e7cff4cf 100644 --- a/templates/web/base/admin/reports/edit.html +++ b/templates/web/base/admin/reports/edit.html @@ -131,7 +131,20 @@ class="admin-offsite-link">[% problem.latitude %], [% problem.longitude %]
  • [% INCLUDE 'admin/report-category.html' %]
  • -
  • [% loc('Extra data:') %] [% IF extra_fields.size %]
      [% FOREACH field IN extra_fields %]
    • [% field.name %]: [% field.val %]
    • [% END %]
    [% ELSE %]No[% END %]
  • +
  • [% loc('Extra data:') ~%] + [%~ IF extra_fields.size ~%] +
      + [%~ FOREACH field IN extra_fields ~%] +
    • [%~ field.name ~%]: [% IF field.val.0.defined ~%] + [%~ field.val.list.join(", ") ~%] + [%~ ELSE ~%] + [%~ field.val ~%] + [%~ END ~%] +
    • + [%~ END ~%] +
    + [%~ ELSE %] No[% END ~%] +
  • + [% TRY %][% INCLUDE admin/bodies/_updates_disallowed_hint.html %][% CATCH file %][% END %]

    diff --git a/templates/web/fixmystreet-uk-councils/admin/bodies/_updates_disallowed_hint.html b/templates/web/fixmystreet-uk-councils/admin/bodies/_updates_disallowed_hint.html new file mode 100644 index 000000000..fc9ef4867 --- /dev/null +++ b/templates/web/fixmystreet-uk-councils/admin/bodies/_updates_disallowed_hint.html @@ -0,0 +1,9 @@ +[% cfg = c.cobrand.feature('updates_allowed') ~%] + + This site’s configuration means even if this is unticked, only + [%~ IF cfg == 'staff' %] staff will be able to leave updates. + [%~ ELSIF cfg == 'reporter' %] the problem reporter will be able to leave updates. + [%~ ELSIF cfg == 'reporter-open' %] the problem reporter will be able to leave updates on open reports. + [%~ ELSIF cfg == 'open' %] open reports can have updates left on them. + [%~ END %] + -- cgit v1.2.3 From 67823bc788ce744e1228a8602b9a5aa805771ced Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 10 Jul 2020 14:38:17 +0100 Subject: Centralise update creation to include fields. Given the user, we can infer the name if not provided, and the extra data if a staff user. We can also provide defaults for various other fields. Always have superuser take precedence over from_body. --- CHANGELOG.md | 1 + bin/fixmystreet.com/banes-close-reports | 1 - bin/fixmystreet.com/buckinghamshire-flytipping | 1 - bin/one-off-update-staff | 38 ++++++++++++++++++++++ .../FixMyStreet/App/Controller/Admin/Reports.pm | 24 ++------------ perllib/FixMyStreet/App/Controller/Admin/Triage.pm | 17 ++-------- perllib/FixMyStreet/App/Controller/Moderate.pm | 9 ++--- .../FixMyStreet/App/Controller/Questionnaire.pm | 4 --- perllib/FixMyStreet/App/Controller/Report.pm | 7 +--- perllib/FixMyStreet/DB/Result/Comment.pm | 35 ++++++++++++++++++++ perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm | 10 +----- perllib/Open311/UpdatesBase.pm | 5 --- t/app/controller/admin/report_edit.t | 22 ++----------- t/app/controller/admin/update_edit.t | 4 +-- t/app/controller/report_updates.t | 6 ++-- t/app/model/comment.t | 25 +++++++++----- t/open311.t | 1 + 17 files changed, 107 insertions(+), 103 deletions(-) create mode 100755 bin/one-off-update-staff diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a500d688..28d37d847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 - Better sort admin user table. + - Centralise update creation to include fields. - Development improvements: - `#geolocate_link` is now easier to re-style. #3006 - Links inside `#front-main` can be customised using `$primary_link_*` Sass variables. #3007 diff --git a/bin/fixmystreet.com/banes-close-reports b/bin/fixmystreet.com/banes-close-reports index bba4c88e0..79c1c44b9 100755 --- a/bin/fixmystreet.com/banes-close-reports +++ b/bin/fixmystreet.com/banes-close-reports @@ -50,7 +50,6 @@ my $q = FixMyStreet::DB->resultset("Problem")->search({ # Provide some variables to the archiving script FixMyStreet::Script::ArchiveOldEnquiries::update_options({ user => $body->comment_user->id, - user_name => $body->comment_user->name, closure_text => CLOSURE_TEXT, retain_alerts => 1, commit => $opts->commit, diff --git a/bin/fixmystreet.com/buckinghamshire-flytipping b/bin/fixmystreet.com/buckinghamshire-flytipping index a312f9fbe..65e87d30e 100755 --- a/bin/fixmystreet.com/buckinghamshire-flytipping +++ b/bin/fixmystreet.com/buckinghamshire-flytipping @@ -79,7 +79,6 @@ sub find_problems { # Provide some variables to the archiving script FixMyStreet::Script::ArchiveOldEnquiries::update_options({ user => $body->comment_user->id, - user_name => $body->comment_user->name, closure_text => $template->text, retain_alerts => $retain_alerts, commit => $opts->commit, diff --git a/bin/one-off-update-staff b/bin/one-off-update-staff new file mode 100755 index 000000000..d94d6b961 --- /dev/null +++ b/bin/one-off-update-staff @@ -0,0 +1,38 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +BEGIN { + use File::Basename qw(dirname); + use File::Spec; + my $d = dirname(File::Spec->rel2abs($0)); + require "$d/../setenv.pl"; +} + +use FixMyStreet::DB; + +my $rs = FixMyStreet::DB->resultset("Comment")->search({ + 'user.from_body' => { '!=', undef }, + 'user.is_superuser' => 0, + 'me.extra' => [ undef, { -not_like => '%is_body_user%' } ], +}, { + "+columns" => ["user.from_body"], + join => 'user', +}); +while (my $row = $rs->next) { + my $id = $row->user->{_column_data}->{from_body}; # Avoid DB lookups + $row->set_extra_metadata( is_body_user => $id ); + $row->update; +} + +$rs = FixMyStreet::DB->resultset("Comment")->search({ + 'user.is_superuser' => 1, + 'me.extra' => [ undef, { -not_like => '%is_superuser%' } ], +}, { + join => 'user', +}); +while (my $row = $rs->next) { + $row->set_extra_metadata( is_superuser => 1 ); + $row->update; +} diff --git a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm index 4866a657e..48386cf3e 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm @@ -368,24 +368,10 @@ sub edit : Path('/admin/report_edit') : Args(1) { if ( $problem->state ne $old_state ) { $c->forward( '/admin/log_edit', [ $id, 'problem', 'state_change' ] ); - 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; - } - my $timestamp = \'current_timestamp'; $problem->add_to_comments( { text => $c->stash->{update_text} || '', - created => $timestamp, - confirmed => $timestamp, - user_id => $c->user->id, - name => $name, - mark_fixed => 0, - anonymous => 0, - state => 'confirmed', + user => $c->user->obj, problem_state => $problem->state, - extra => $extra } ); } $c->forward( '/admin/log_edit', [ $id, 'problem', 'edit' ] ); @@ -444,13 +430,7 @@ sub edit_category : Private { } else { $problem->add_to_comments({ text => $update_text, - created => \'current_timestamp', - confirmed => \'current_timestamp', - user_id => $c->user->id, - name => $c->user->from_body ? $c->user->from_body->name : $c->user->name, - state => 'confirmed', - mark_fixed => 0, - anonymous => 0, + user => $c->user->obj, }); } $c->forward( '/admin/log_edit', [ $problem->id, 'problem', 'category_change' ] ); diff --git a/perllib/FixMyStreet/App/Controller/Admin/Triage.pm b/perllib/FixMyStreet/App/Controller/Admin/Triage.pm index 7cfcc93dd..c5bb6628d 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Triage.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Triage.pm @@ -117,27 +117,14 @@ sub update : Private { $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; - } - + my $extra; $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', + user => $c->user->obj, problem_state => $problem->state, extra => $extra, whensent => \'current_timestamp', diff --git a/perllib/FixMyStreet/App/Controller/Moderate.pm b/perllib/FixMyStreet/App/Controller/Moderate.pm index f4143f0b4..c936f13c0 100644 --- a/perllib/FixMyStreet/App/Controller/Moderate.pm +++ b/perllib/FixMyStreet/App/Controller/Moderate.pm @@ -340,13 +340,8 @@ sub moderate_state : Private { $problem->state($new_state); $problem->add_to_comments( { text => $c->stash->{moderation_reason}, - created => \'current_timestamp', - confirmed => \'current_timestamp', - user_id => $c->user->id, - name => $c->user->from_body ? $c->user->from_body->name : $c->user->name, - state => 'confirmed', - mark_fixed => 0, - anonymous => $c->user->from_body ? 0 : 1, + user => $c->user->obj, + anonymous => $c->user->is_superuser || $c->user->from_body ? 0 : 1, problem_state => $new_state, } ); return 'state'; diff --git a/perllib/FixMyStreet/App/Controller/Questionnaire.pm b/perllib/FixMyStreet/App/Controller/Questionnaire.pm index ab6117ae4..ef6152c30 100755 --- a/perllib/FixMyStreet/App/Controller/Questionnaire.pm +++ b/perllib/FixMyStreet/App/Controller/Questionnaire.pm @@ -206,16 +206,12 @@ sub submit_standard : Private { $update = $c->model('DB::Comment')->new( { problem => $problem, - name => $problem->name, user => $problem->user, text => $update, - state => 'confirmed', mark_fixed => $c->stash->{new_state} eq 'fixed - user' ? 1 : 0, mark_open => $c->stash->{new_state} eq 'confirmed' ? 1 : 0, lang => $c->stash->{lang_code}, cobrand => $c->cobrand->moniker, - cobrand_data => '', - confirmed => \'current_timestamp', anonymous => $problem->anonymous, } ); diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 4f9825cae..dd7abc563 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -569,16 +569,11 @@ sub inspect : Private { epoch => $saved_at ); } - my $name = $c->user->from_body ? $c->user->from_body->name : $c->user->name; $problem->add_to_comments( { text => $update_text, created => $timestamp, confirmed => $timestamp, - user_id => $c->user->id, - name => $name, - state => 'confirmed', - mark_fixed => 0, - anonymous => 0, + user => $c->user->obj, %update_params, } ); } diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index 68e267d00..c876f3459 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -110,6 +110,41 @@ with 'FixMyStreet::Roles::Abuser', 'FixMyStreet::Roles::Moderation', 'FixMyStreet::Roles::PhotoSet'; +=head2 FOREIGNBUILDARGS + +Make sure that when creating a new Comment object, certain +other fields are set based upon the supplied data. + +=cut + +sub FOREIGNBUILDARGS { + my ($class, $opts) = @_; + + if (my $user = $opts->{user}) { + my $name; + if ($user->is_superuser) { + $opts->{extra}->{is_superuser} = 1; + $name = _('an administrator'); + } elsif (my $body = $user->from_body) { + $opts->{extra}->{is_body_user} = $body->id; + $name = $body->name; + $name = 'Island Roads' if $name eq 'Isle of Wight Council'; + } else { + $name = $user->name; + } + $opts->{name} //= $name; + } + + $opts->{anonymous} //= 0; + $opts->{mark_fixed} //= 0; + $opts->{state} //= 'confirmed'; # it's only public updates that need to be unconfirmed + if ($opts->{state} eq 'confirmed') { + $opts->{confirmed} //= \'current_timestamp'; + } + + return $opts; +}; + =head2 get_cobrand_logged Get a cobrand object for the cobrand the update was made on. diff --git a/perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm b/perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm index 7ba763515..7c183ecbc 100644 --- a/perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm +++ b/perllib/FixMyStreet/Script/ArchiveOldEnquiries.pm @@ -141,7 +141,6 @@ sub close_problems { my $problems = shift; my $extra = { auto_closed_by_script => 1 }; - $extra->{is_superuser} = 1 if !$opts->{user_name}; my $cobrand; while (my $problem = $problems->next) { @@ -152,16 +151,9 @@ sub close_problems { $cobrand->set_lang_and_domain($problem->lang, 1); } - my $timestamp = \'current_timestamp'; my $comment = $problem->add_to_comments( { text => $opts->{closure_text} || '', - created => $timestamp, - confirmed => $timestamp, - user_id => $opts->{user}, - name => $opts->{user_name} || _('an administrator'), - mark_fixed => 0, - anonymous => 0, - state => 'confirmed', + user => FixMyStreet::DB->resultset("User")->find($opts->{user}), problem_state => $opts->{closed_state}, extra => $extra, } ); diff --git a/perllib/Open311/UpdatesBase.pm b/perllib/Open311/UpdatesBase.pm index 0be324773..c2f3fae0d 100644 --- a/perllib/Open311/UpdatesBase.pm +++ b/perllib/Open311/UpdatesBase.pm @@ -154,13 +154,8 @@ sub process_update { $request, $p, $state, $old_state, $external_status_code, $old_external_status_code ), - mark_fixed => 0, - mark_open => 0, - anonymous => 0, - name => $self->system_user->name, confirmed => $request->{comment_time}, created => $request->{comment_time}, - state => 'confirmed', } ); diff --git a/t/app/controller/admin/report_edit.t b/t/app/controller/admin/report_edit.t index f8101ab76..e041154db 100644 --- a/t/app/controller/admin/report_edit.t +++ b/t/app/controller/admin/report_edit.t @@ -329,7 +329,6 @@ foreach my $test ( closed_updates => undef, }, expect_comment => 1, - user_body => $oxfordshire, changes => { state => 'investigating' }, log_entries => [ qw/edit state_change edit edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ @@ -351,7 +350,6 @@ foreach my $test ( }, expect_comment => 1, expected_text => '*Category changed from ‘Other’ to ‘Potholes’*', - user_body => $oxfordshire, changes => { state => 'in progress', category => 'Potholes' }, log_entries => [ qw/edit state_change category_change edit state_change edit edit resend edit state_change edit state_change edit state_change edit state_change edit state_change edit edit edit edit edit/ @@ -364,11 +362,6 @@ foreach my $test ( $report->comments->delete; $log_entries->reset; - if ( $test->{user_body} ) { - $superuser->from_body( $test->{user_body}->id ); - $superuser->update; - } - $mech->get_ok("/admin/report_edit/$report_id"); @{$test->{fields}}{'external_id', 'external_body', 'external_team', 'category'} = (13, "", "", "Other"); @@ -440,21 +433,12 @@ foreach my $test ( } else { is $comment->text, '', 'comment has no text'; } - if ( $test->{user_body} ) { - ok $comment->get_extra_metadata('is_body_user'), 'body user metadata set'; - ok !$comment->get_extra_metadata('is_superuser'), 'superuser metadata not set'; - is $comment->name, $test->{user_body}->name, 'comment name is body name'; - } else { - ok !$comment->get_extra_metadata('is_body_user'), 'body user metadata not set'; - ok $comment->get_extra_metadata('is_superuser'), 'superuser metadata set'; - is $comment->name, _('an administrator'), 'comment name is admin'; - } + ok !$comment->get_extra_metadata('is_body_user'), 'body user metadata not set'; + ok $comment->get_extra_metadata('is_superuser'), 'superuser metadata set'; + is $comment->name, _('an administrator'), 'comment name is admin'; } else { is $report->comments->count, 0, 'report has no comments'; } - - $superuser->from_body(undef); - $superuser->update; }; } diff --git a/t/app/controller/admin/update_edit.t b/t/app/controller/admin/update_edit.t index 57c8973d4..8650e7771 100644 --- a/t/app/controller/admin/update_edit.t +++ b/t/app/controller/admin/update_edit.t @@ -81,7 +81,7 @@ for my $test ( fields => { text => 'this is an update', state => 'confirmed', - name => '', + name => 'Test User', anonymous => 1, username => $update->user->email, }, @@ -96,7 +96,7 @@ for my $test ( fields => { text => 'this is a changed update', state => 'confirmed', - name => '', + name => 'Test User', anonymous => 1, username => $update->user->email, }, diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index e97b04f12..3198cf70c 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -1096,10 +1096,10 @@ subtest $test->{desc} => sub { unlike $update_meta->[1], qr/Commenter/, 'commenter name not included'; like $update_meta->[0], qr/investigating/i, 'update meta includes state change'; - if ($test->{body} || $test->{bodyuser}) { - like $update_meta->[1], qr/Westminster/, 'body user update uses body name'; - } elsif ($test->{superuser}) { + if ($test->{superuser}) { like $update_meta->[1], qr/an administrator/, 'superuser update says an administrator'; + } elsif ($test->{body} || $test->{bodyuser}) { + like $update_meta->[1], qr/Westminster/, 'body user update uses body name'; } ok $user->user_body_permissions->create({ diff --git a/t/app/model/comment.t b/t/app/model/comment.t index 3f30b3a1e..dd9af9eb9 100644 --- a/t/app/model/comment.t +++ b/t/app/model/comment.t @@ -1,20 +1,27 @@ use FixMyStreet::Test; -my $comment_rs = FixMyStreet::DB->resultset('Comment'); +my $user = FixMyStreet::DB->resultset('User')->new({ name => 'Test User', is_superuser => 1 }); +my $comment_rs = FixMyStreet::DB->resultset('Comment'); my $comment = $comment_rs->new( { - user_id => 1, + user => $user, problem_id => 1, text => '', - state => 'confirmed', - anonymous => 0, - mark_fixed => 0, - cobrand => 'default', - cobrand_data => '', } ); -is $comment->confirmed, undef, 'inflating null confirmed ok'; -is $comment->created, undef, 'inflating null confirmed ok'; +is $comment->created, undef, 'inflating null created ok'; +is $comment->mark_fixed, 0, 'mark fixed default set'; +is $comment->state, 'confirmed', 'state default is confirmed'; +is $comment->name, 'an administrator'; + +$user->is_superuser(0); +$comment = $comment_rs->new({ + user => $user, + problem_id => 1, + text => '', +}); +is $comment->name, 'Test User'; + done_testing(); diff --git a/t/open311.t b/t/open311.t index 9524006b8..941e35f7e 100644 --- a/t/open311.t +++ b/t/open311.t @@ -402,6 +402,7 @@ subtest 'Hounslow update description is correct for a different user' => sub { my $comment = make_comment('hounslow'); $comment->user($user2); + $comment->name($user2->name); my $results; FixMyStreet::override_config { ALLOWED_COBRANDS => 'hounslow', -- cgit v1.2.3 From 8bee582c74f9db05d08c682803987e6b324fb61f Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Tue, 14 Jul 2020 16:32:12 +0100 Subject: [HighwaysEngland] add notice about offensive language Below description. --- templates/web/highwaysengland/report/new/inline-tips.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/templates/web/highwaysengland/report/new/inline-tips.html b/templates/web/highwaysengland/report/new/inline-tips.html index ff0ef42f8..73f70a3d0 100644 --- a/templates/web/highwaysengland/report/new/inline-tips.html +++ b/templates/web/highwaysengland/report/new/inline-tips.html @@ -1,3 +1,9 @@ +

    +

    + Content of reports shall be monitored and any language that could be deemed to cause offense or be inappropriate shall be removed. +

    +
    + [%~ SET where_hear = report.get_extra_metadata('where_hear') %] -- cgit v1.2.3 From 1243e701620d71dfcf0eedb880f40e7fef59415d Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 13 Jul 2020 13:55:10 +0100 Subject: Do not have bootstrap run sudo commands. Your site user may not have access to sudo, and your admin user may cause permission issues if used to do the git checkout, or the Perl module installation. Document the separate script to install system packages instead. Co-authored-by: Sam Pearson --- CHANGELOG.md | 1 + Vagrantfile | 3 +++ bin/docker.preinit | 1 + bin/install_packages | 2 +- bin/site-specific-install.sh | 2 -- docker-compose.yml | 1 - docs/install/manual-install.md | 2 +- docs/updating/ami.md | 4 +++- docs/updating/index.md | 5 +++-- script/bootstrap | 13 ------------- 10 files changed, 13 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28d37d847..2af6f052c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - Improve compatibility with G Suite OpenID Connect authentication. #3032 - Fix duplicate asset message after dismissing duplicate suggestions. - Improve moderation diff display in a few small ways. #3105 + - Do not have bootstrap run sudo commands. #2930 - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/Vagrantfile b/Vagrantfile index 17eebe4b5..a69b58b11 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -60,6 +60,9 @@ EOS # This just runs our update script, used on our offical box. $update = <<-EOS chown -R vagrant:vagrant /home/vagrant/.cpanm + # Ensure any new packages are installed + /home/vagrant/fixmystreet/bin/install_packages + # Run setup script su vagrant -c '/home/vagrant/fixmystreet/script/setup ; exit $?' if [ $? -eq 0 ]; then touch /tmp/success diff --git a/bin/docker.preinit b/bin/docker.preinit index 459e89de2..501878185 100644 --- a/bin/docker.preinit +++ b/bin/docker.preinit @@ -34,6 +34,7 @@ fi su $FMS_DB_USER -c "${FMS_ROOT}/bin/docker-cobrand" # Ensure things are up to date - schema, CSS, etc +${FMS_ROOT}/bin/install_packages docker su $FMS_DB_USER -c "${FMS_ROOT}/script/update" # Update reports diff --git a/bin/install_packages b/bin/install_packages index 7b4bfd8e1..c7f1b9446 100755 --- a/bin/install_packages +++ b/bin/install_packages @@ -3,7 +3,7 @@ set -e cd "$(dirname "${BASH_SOURCE[0]}")/.." -PACKAGE_FILE=conf/packages +PACKAGE_FILE=conf/packages.generic [ -n "$1" ] && PACKAGE_FILE="conf/packages.${1}" diff --git a/bin/site-specific-install.sh b/bin/site-specific-install.sh index af52dbdbc..2b773fecb 100644 --- a/bin/site-specific-install.sh +++ b/bin/site-specific-install.sh @@ -54,8 +54,6 @@ fi install_website_packages -su -l -c "touch '$DIRECTORY/admin-htpasswd'" "$UNIX_USER" - if [ $INSTALL_DB = true ]; then add_postgresql_user fi diff --git a/docker-compose.yml b/docker-compose.yml index cc653c922..80f0b56f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,7 +30,6 @@ services: FMS_ROOT: '/var/www/fixmystreet/fixmystreet' SUPERUSER_EMAIL: 'superuser@example.org' SUPERUSER_PASSWORD: '5up3r53cr3t' - SKIP_PACKAGES_INSTALL: networks: default: aliases: diff --git a/docs/install/manual-install.md b/docs/install/manual-install.md index 408b419cb..35c654cf5 100644 --- a/docs/install/manual-install.md +++ b/docs/install/manual-install.md @@ -40,7 +40,7 @@ dependencies are listed in `conf/packages.generic`. To install all of them you can run e.g.: {% highlight bash %} -$ sudo xargs -a conf/packages.generic apt-get install +$ sudo bin/install_packages {% endhighlight %} A similar list of packages should work for other Debian-based distributions. diff --git a/docs/updating/ami.md b/docs/updating/ami.md index ff83e3aec..9f2f612ed 100644 --- a/docs/updating/ami.md +++ b/docs/updating/ami.md @@ -41,10 +41,12 @@ make sure they are all committed to your local branch and fork first, see to try checking out your repository elsewhere and trying the merge there first, to see if it there are any problems. -After updating the code, you should run the following command to update any +After updating the code, you should run the following commands to update any needed dependencies and any schema changes to your database. It's a good idea to take a backup of your database first. + admin@ip-10-58-191-98:/var/www/fixmystreet/fixmystreet$ sudo bin/install_packages + fms@ip-10-58-191-98:~/fixmystreet$ script/update If you have made changes to the schema yourself, this may not work, diff --git a/docs/updating/index.md b/docs/updating/index.md index 2ab8ed6f5..ea1ce72cd 100644 --- a/docs/updating/index.md +++ b/docs/updating/index.md @@ -53,12 +53,13 @@ you are too worried to merge in case it breaks something. ## Subsequent dependency updates -After updating the code, you should run the following command to update any +After updating the code, you should run the following commands to update any needed dependencies and any schema changes to your database. It's a good idea to take a backup of your database first. {% highlight bash %} -script/update +sudo bin/install_packages # (as your admin user) +script/update # (as the fms user) {% endhighlight %} Of course, if you have made changes to the database schema yourself, this may diff --git a/script/bootstrap b/script/bootstrap index f40e97b18..dfebd7449 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -4,17 +4,4 @@ set -e cd "$(dirname "$0")/.." git submodule --quiet update --init --recursive --rebase - -if [ -z ${SKIP_PACKAGES_INSTALL:+x} ] ; then - # Let's see if we can't work out where we might be running. - if cut -d/ -f2 /proc/self/cgroup | sort -u | grep -q docker ; then - echo "==> Installing Docker packages..." - sudo bin/install_packages docker - else - # Fallback - echo "==> Installing generic packages..." - sudo bin/install_packages generic - fi -fi - bin/install_perl_modules -- cgit v1.2.3 From c3899dc611ade1410adc7682f2ed196e341ee577 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 15 Jul 2020 14:23:27 +0100 Subject: [HighwaysEngland] change where heard from options --- templates/web/highwaysengland/report/new/inline-tips.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/web/highwaysengland/report/new/inline-tips.html b/templates/web/highwaysengland/report/new/inline-tips.html index 73f70a3d0..730b482e5 100644 --- a/templates/web/highwaysengland/report/new/inline-tips.html +++ b/templates/web/highwaysengland/report/new/inline-tips.html @@ -14,6 +14,6 @@ - + -- cgit v1.2.3 From 2915ec9562bcb43f981d08ed2fa64a13c78a6a58 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 16 Jul 2020 12:59:38 +0100 Subject: Tidy up map_sub_links in templates. --- templates/web/base/maps/_sub_links.html | 6 ------ templates/web/base/maps/bing.html | 2 +- templates/web/base/maps/fms.html | 2 +- templates/web/base/maps/google-ol.html | 2 +- templates/web/base/maps/openlayers.html | 6 ++++++ templates/web/zurich/maps/zurich.html | 5 ----- 6 files changed, 9 insertions(+), 14 deletions(-) delete mode 100644 templates/web/base/maps/_sub_links.html diff --git a/templates/web/base/maps/_sub_links.html b/templates/web/base/maps/_sub_links.html deleted file mode 100644 index d58ecf97c..000000000 --- a/templates/web/base/maps/_sub_links.html +++ /dev/null @@ -1,6 +0,0 @@ -[% INCLUDE maps/openlayers.html %] -[% UNLESS around_page %] - -[% END %] diff --git a/templates/web/base/maps/bing.html b/templates/web/base/maps/bing.html index 7d2c379c4..d30fc2600 100644 --- a/templates/web/base/maps/bing.html +++ b/templates/web/base/maps/bing.html @@ -6,4 +6,4 @@ [% END %] [% END %] -[% map_html = INCLUDE maps/_sub_links.html %] +[% map_html = INCLUDE maps/openlayers.html %] diff --git a/templates/web/base/maps/fms.html b/templates/web/base/maps/fms.html index 739c00b4e..2df3bd11b 100644 --- a/templates/web/base/maps/fms.html +++ b/templates/web/base/maps/fms.html @@ -6,4 +6,4 @@ [% END %] [% END %] -[% map_html = INCLUDE maps/_sub_links.html include_key = 1 %] +[% map_html = INCLUDE maps/openlayers.html include_key = 1 %] diff --git a/templates/web/base/maps/google-ol.html b/templates/web/base/maps/google-ol.html index eb68ae804..9103ba42b 100644 --- a/templates/web/base/maps/google-ol.html +++ b/templates/web/base/maps/google-ol.html @@ -2,4 +2,4 @@ [% loc('Satellite') %] [% END %] -[% map_html = INCLUDE maps/_sub_links.html %] +[% map_html = INCLUDE maps/openlayers.html %] diff --git a/templates/web/base/maps/openlayers.html b/templates/web/base/maps/openlayers.html index f4c78767c..60d7acfde 100644 --- a/templates/web/base/maps/openlayers.html +++ b/templates/web/base/maps/openlayers.html @@ -46,3 +46,9 @@
    [% map.copyright | safe %]
    [% END %] + +[% IF NOT around_page AND map_sub_links %] + +[% END %] diff --git a/templates/web/zurich/maps/zurich.html b/templates/web/zurich/maps/zurich.html index f3a9bdc7f..8310b0758 100644 --- a/templates/web/zurich/maps/zurich.html +++ b/templates/web/zurich/maps/zurich.html @@ -5,9 +5,4 @@ [% map_html = BLOCK %] [% INCLUDE maps/openlayers.html %] [% INCLUDE maps/wmts_config.html %] -[% UNLESS around_page %] - -[% END %] [% END %] -- cgit v1.2.3 From 4770393b3de8d38649a66b9608048c15ec73347a Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 16 Jul 2020 13:08:56 +0100 Subject: Add aerial option to OSM maps. --- CHANGELOG.md | 2 +- perllib/FixMyStreet/Map/Bing.pm | 10 ---------- perllib/FixMyStreet/Map/OSM.pm | 4 ++++ templates/web/base/maps/bing.html | 9 --------- templates/web/base/maps/osm.html | 8 ++++++++ web/js/map-OpenStreetMap.js | 7 +++++++ 6 files changed, 20 insertions(+), 20 deletions(-) delete mode 100644 templates/web/base/maps/bing.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 2af6f052c..9f7dc615b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - Add lazy image loading on list items. - Improve Bing geocoder results. - Add option of checking passwords against Have I Been Pwned. - - Add aerial maps option to Bing maps. + - Add aerial maps option to Bing and OSM maps. - Changes: - Mark user as active when sent an email alert. - Bugfixes: diff --git a/perllib/FixMyStreet/Map/Bing.pm b/perllib/FixMyStreet/Map/Bing.pm index 723dcd6dc..17bdc3a53 100644 --- a/perllib/FixMyStreet/Map/Bing.pm +++ b/perllib/FixMyStreet/Map/Bing.pm @@ -8,8 +8,6 @@ use strict; sub map_type { '' } -sub map_template { 'bing' } - sub map_javascript { [ '/vendor/OpenLayers/OpenLayers.fixmystreet.js', '/js/map-OpenLayers.js', @@ -31,14 +29,6 @@ sub get_quadkey { return $key; } -sub display_map { - my ($self, $c, %params) = @_; - - $params{aerial} = $c->get_param("aerial") ? 1 : 0; - - $self->SUPER::display_map($c, %params); -} - my $road_base = '//%s.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/%s?mkt=en-US&it=G,L&src=t&shading=hill&og=969&n=z'; my $aerial_base = '//%s.ssl.ak.dynamic.tiles.virtualearth.net/comp/ch/%s?mkt=en-US&it=A,G,L&src=t&og=969&n=z'; diff --git a/perllib/FixMyStreet/Map/OSM.pm b/perllib/FixMyStreet/Map/OSM.pm index 417fdca0b..47c1c8535 100644 --- a/perllib/FixMyStreet/Map/OSM.pm +++ b/perllib/FixMyStreet/Map/OSM.pm @@ -21,11 +21,13 @@ sub map_template { 'osm' } sub map_javascript { [ '/vendor/OpenLayers/OpenLayers.wfs.js', '/js/map-OpenLayers.js', + FixMyStreet->config('BING_MAPS_API_KEY') ? ('/js/map-bing-ol.js') : (), '/js/map-OpenStreetMap.js', ] } sub map_tiles { my ( $self, %params ) = @_; + return FixMyStreet::Map::Bing->map_tiles(%params) if $params{aerial}; my ( $x, $y, $z ) = ( $params{x_tile}, $params{y_tile}, $params{zoom_act} ); my $tile_url = $self->base_tile_url(); return [ @@ -59,6 +61,8 @@ sub display_map { if defined $c->get_param('lon'); $params{zoomToBounds} = $params{any_zoom} && !defined $c->get_param('zoom'); + $params{aerial} = $c->get_param("aerial") && FixMyStreet->config('BING_MAPS_API_KEY') ? 1 : 0; + my %data; $data{cobrand} = $c->cobrand; $data{distance} = $c->stash->{distance}; diff --git a/templates/web/base/maps/bing.html b/templates/web/base/maps/bing.html deleted file mode 100644 index d30fc2600..000000000 --- a/templates/web/base/maps/bing.html +++ /dev/null @@ -1,9 +0,0 @@ -[% map_sub_links = BLOCK %] - [% IF c.req.params.aerial %] - [% loc('Roads') %] - [% ELSE %] - [% loc('Aerial') %] - [% END %] -[% END %] - -[% map_html = INCLUDE maps/openlayers.html %] diff --git a/templates/web/base/maps/osm.html b/templates/web/base/maps/osm.html index 70d05929f..560055f3b 100644 --- a/templates/web/base/maps/osm.html +++ b/templates/web/base/maps/osm.html @@ -1,3 +1,11 @@ +[% map_sub_links = BLOCK %][% IF c.config.BING_MAPS_API_KEY %] + [% IF c.req.params.aerial %] + [% loc('Roads') %] + [% ELSE %] + [% loc('Aerial') %] + [% END %] +[% END %][% END %] + [% map.copyright = ''; # This is handled with OpenLayers.Control.Attribution map_html = INCLUDE maps/openlayers.html diff --git a/web/js/map-OpenStreetMap.js b/web/js/map-OpenStreetMap.js index 9ed3a2ee3..bd3497343 100644 --- a/web/js/map-OpenStreetMap.js +++ b/web/js/map-OpenStreetMap.js @@ -7,6 +7,13 @@ fixmystreet.maps.config = function() { new OpenLayers.Control.PermalinkFMS('map'), new OpenLayers.Control.PanZoomFMS({id: 'fms_pan_zoom' }) ]; + + if (OpenLayers.Layer.BingAerial) { + fixmystreet.layer_options = [ + { map_type: fixmystreet.map_type }, + { map_type: OpenLayers.Layer.BingAerial } + ]; + } }; // http://www.openstreetmap.org/openlayers/OpenStreetMap.js (added maxResolution) -- cgit v1.2.3 From 83b77947e61525ad2a18aa4511a513abdfef25eb Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Thu, 16 Jul 2020 11:27:15 +0100 Subject: [TfL] remove COVID-19 front page text Fixes mysociety/fimystreet-commercial#1939 --- templates/web/tfl/front/pre-steps.html | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 templates/web/tfl/front/pre-steps.html diff --git a/templates/web/tfl/front/pre-steps.html b/templates/web/tfl/front/pre-steps.html deleted file mode 100644 index 1dead5c89..000000000 --- a/templates/web/tfl/front/pre-steps.html +++ /dev/null @@ -1,12 +0,0 @@ -

    - - All issues reported on Streetcare continue to be monitored by our teams at - Transport for London. We are not able to carry out all of our normal - maintenance works at the moment but we are working hard to deal with all - safety critical issues. - -
    - If you are concerned about pavement overcrowding during social distancing - on the Transport for London Road Network, you can now report that to us - here. -

    -- cgit v1.2.3 From d55461e6d05ab0c1b398f756a687107a5ce6d49e Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 16 Jul 2020 14:26:13 +0100 Subject: Default to aerial map in JS if aerial given in URL. --- web/js/map-bing-ol.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/js/map-bing-ol.js b/web/js/map-bing-ol.js index b0e5dfc6a..179238633 100644 --- a/web/js/map-bing-ol.js +++ b/web/js/map-bing-ol.js @@ -25,6 +25,10 @@ fixmystreet.maps.config = function() { fixmystreet.map.setBaseLayer(fixmystreet.map.layers[0]); } }); + // If page loaded with Aerial as starting, rather than default road + if ($('#map_layer_toggle').text() == translation_strings.map_roads) { + fixmystreet.map.setBaseLayer(fixmystreet.map.layers[1]); + } }); })(); -- cgit v1.2.3 From 88c607fa378951491c7ec0f3384bc8f27fbca467 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 15 Jul 2020 16:02:21 +0100 Subject: Fix lookups in templates of categories with &s. A hash lookup in a template is escaping the key used, meaning the lookup is failing anywhere we are using a category containing an ampersand as a key. A continuation of the changes made in 527d763b. --- CHANGELOG.md | 1 + t/app/controller/around.t | 4 ++-- t/app/controller/report_inspect.t | 23 +++++++++++----------- t/cobrand/hackney.t | 7 +++++-- templates/web/base/admin/report-category.html | 3 ++- templates/web/base/admin/triage/_list-filters.html | 5 +++-- templates/web/base/report/_inspect.html | 7 ++++--- templates/web/base/report/_item.html | 5 +++-- .../web/base/report/new/form_user_loggedin.html | 5 +++-- templates/web/base/reports/_list-filters.html | 3 ++- 10 files changed, 37 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2af6f052c..f724b8f98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - Fix duplicate asset message after dismissing duplicate suggestions. - Improve moderation diff display in a few small ways. #3105 - Do not have bootstrap run sudo commands. #2930 + - Fix lookups in templates of categories with &s. - Admin improvements: - Display user name/email for contributed as reports. #2990 - Interface for enabling anonymous reports for certain categories. #2989 diff --git a/t/app/controller/around.t b/t/app/controller/around.t index 784801517..e35406bb6 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -217,8 +217,8 @@ for my $permission ( qw/ report_inspect report_mark_private/ ) { subtest 'check assigned-only list items do not display shortlist buttons' => sub { my $body = FixMyStreet::DB->resultset('Body')->find( $body_edin_id ); - my $contact = $mech->create_contact_ok( category => 'Horses', body_id => $body->id, email => "horses\@example.org" ); - $edinburgh_problems[4]->update({ category => 'Horses' }); + my $contact = $mech->create_contact_ok( category => 'Horses & Ponies', body_id => $body->id, email => "horses\@example.org" ); + $edinburgh_problems[4]->update({ category => 'Horses & Ponies' }); my $user = $mech->log_in_ok( 'test@example.com' ); $user->set_extra_metadata(assigned_categories_only => 1); diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t index 2852f8d18..de9d689cd 100644 --- a/t/app/controller/report_inspect.t +++ b/t/app/controller/report_inspect.t @@ -7,7 +7,7 @@ my $brum = $mech->create_body_ok(2514, 'Birmingham City Council'); my $oxon = $mech->create_body_ok(2237, 'Oxfordshire County Council', { can_be_devolved => 1 } ); my $contact = $mech->create_contact_ok( body_id => $oxon->id, category => 'Cows', email => 'cows@example.net' ); my $contact2 = $mech->create_contact_ok( body_id => $oxon->id, category => 'Sheep', email => 'SHEEP', send_method => 'Open311' ); -my $contact3 = $mech->create_contact_ok( body_id => $oxon->id, category => 'Badgers', email => 'badgers@example.net' ); +my $contact3 = $mech->create_contact_ok( body_id => $oxon->id, category => 'Badgers & Voles', email => 'badgers@example.net' ); my $rp = FixMyStreet::DB->resultset("ResponsePriority")->create({ body => $oxon, name => 'High Priority', @@ -683,19 +683,20 @@ FixMyStreet::override_config { FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', - ALLOWED_COBRANDS => 'fixmystreet', + ALLOWED_COBRANDS => 'oxfordshire', }, sub { subtest "test category not updated if fail to include public update" => sub { $mech->get_ok("/report/$report_id"); - $mech->submit_form(button => 'save', with_fields => { category => 'Badgers' }); + $mech->submit_form(button => 'save', with_fields => { category => 'Badgers & Voles' }); $report->discard_changes; is $report->category, "Cows", "Report in correct category"; - $mech->content_contains('Badgers" selected', 'Changed category still selected'); + $mech->content_contains('Badgers & Voles" selected', 'Changed category still selected'); }; subtest "test invalid form maintains Category and priority" => sub { $mech->get_ok("/report/$report_id"); + $mech->content_like(qr/data-priorities='[^']*?Low Priority/); my $expected_fields = { state => 'action scheduled', category => 'Cows', @@ -709,9 +710,9 @@ FixMyStreet::override_config { my $values = $mech->visible_form_values('report_inspect_form'); is_deeply $values, $expected_fields, 'correct form fields present'; - $mech->submit_form(button => 'save', with_fields => { category => 'Badgers', priority => $rp2->id }); + $mech->submit_form(button => 'save', with_fields => { category => 'Badgers & Voles', priority => $rp2->id }); - $expected_fields->{category} = 'Badgers'; + $expected_fields->{category} = 'Badgers & Voles'; $expected_fields->{priority} = $rp2->id; my $new_values = $mech->visible_form_values('report_inspect_form'); @@ -724,15 +725,15 @@ FixMyStreet::override_config { $mech->submit_form( button => 'save', with_fields => { - category => 'Badgers', + category => 'Badgers & Voles', include_update => 1, public_update => 'This is a public update', }); $report->discard_changes; - is $report->category, "Badgers", "Report in correct category"; + is $report->category, "Badgers & Voles", "Report in correct category"; is $report->comments->count, 1, "Only leaves one update"; - like $report->comments->first->text, qr/Category changed.*Badgers/, 'update text included category change'; + like $report->comments->first->text, qr/Category changed.*Badgers & Voles/, 'update text included category change'; }; subtest "test non-public changing" => sub { @@ -779,10 +780,10 @@ FixMyStreet::override_config { }); subtest "test report not resent when category changes if send_method doesn't change" => sub { $mech->get_ok("/report/$report3_id"); - $mech->submit_form(button => 'save', with_fields => { category => 'Badgers', include_update => undef, }); + $mech->submit_form(button => 'save', with_fields => { category => 'Badgers & Voles', include_update => undef, }); $report3->discard_changes; - is $report3->category, "Badgers", "Report in correct category"; + is $report3->category, "Badgers & Voles", "Report in correct category"; isnt $report3->whensent, undef, "Report not marked as unsent"; is $report3->bodies_str, $oxon->id, "Reported to OCC"; }; diff --git a/t/cobrand/hackney.t b/t/cobrand/hackney.t index b5a629e33..374bf2ea9 100644 --- a/t/cobrand/hackney.t +++ b/t/cobrand/hackney.t @@ -28,7 +28,7 @@ my $params = { my $hackney = $mech->create_body_ok(2508, 'Hackney Council', $params); my $contact = $mech->create_contact_ok( body_id => $hackney->id, - category => 'Potholes', + category => 'Potholes & stuff', email => 'pothole@example.org', ); $contact->set_extra_fields( ( { @@ -143,7 +143,7 @@ $p->delete; subtest "sends branded confirmation emails" => sub { $mech->log_out_ok; $mech->clear_emails_ok; - $mech->get_ok('/around'); + $mech->get_ok('/?filter_category=Potholes+%26+stuff'); FixMyStreet::override_config { ALLOWED_COBRANDS => [ 'hackney' ], MAPIT_URL => 'http://mapit.uk/', @@ -159,6 +159,9 @@ subtest "sends branded confirmation emails" => sub { $mech->submit_form_ok( { with_fields => { pc => 'E8 1DY', } }, "submit location" ); + # While we're here, check the category with an ampersand (regression test) + $mech->content_contains(' [% END %] diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html index a8be342d0..88fbdd70b 100644 --- a/templates/web/base/report/_inspect.html +++ b/templates/web/base/report/_inspect.html @@ -18,13 +18,14 @@ [% FOREACH category IN category_options %] [% cat_name = category.category; + cat_safe = mark_safe(cat_name); cat_prefix = cat_name | lower | replace('[^a-z]', ''); cat_prefix = "category_" _ cat_prefix _ "_" %] [% END %] diff --git a/templates/web/base/report/_item.html b/templates/web/base/report/_item.html index 6f567de01..9a5168986 100644 --- a/templates/web/base/report/_item.html +++ b/templates/web/base/report/_item.html @@ -5,8 +5,9 @@ [% SET relevant_staff = 1; -SET is_user_category = user_categories.${problem.category}; -IF (assigned_users_only.${problem.category} OR assigned_categories_only) AND NOT is_user_category; +SET category_safe = mark_safe(problem.category); +SET is_user_category = user_categories.$category_safe; +IF (assigned_users_only.$category_safe OR assigned_categories_only) AND NOT is_user_category; SET relevant_staff = 0; END; diff --git a/templates/web/base/report/new/form_user_loggedin.html b/templates/web/base/report/new/form_user_loggedin.html index 0d259e695..d26948fc2 100644 --- a/templates/web/base/report/new/form_user_loggedin.html +++ b/templates/web/base/report/new/form_user_loggedin.html @@ -64,8 +64,9 @@ [% IF c.user.has_permission_to("report_inspect", bodies_ids) OR c.user.has_permission_to("report_mark_private", bodies_ids) %]
    + [%~ SET category_safe = mark_safe(category) %] + [%~ ' checked' IF report.non_public OR non_public_categories.$category_safe %] + [%~ ' disabled' IF non_public_categories.$category_safe %]>
    [% END %] diff --git a/templates/web/base/reports/_list-filters.html b/templates/web/base/reports/_list-filters.html index c5a8eaa75..ee9815157 100644 --- a/templates/web/base/reports/_list-filters.html +++ b/templates/web/base/reports/_list-filters.html @@ -2,7 +2,8 @@ [% BLOCK category_options %] [% FOR cat IN categories %] - -- cgit v1.2.3 From 90afa8d790808979d1b1fd9c327e0139013b3b90 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 15 Jul 2020 15:17:13 +0100 Subject: Select matches for both filter category and group. If both are specified, we want to treat it as an AND, not an OR. --- CHANGELOG.md | 1 + t/app/controller/around.t | 8 +++++++- templates/web/base/report/new/_category_select.html | 1 + templates/web/base/report/new/category.html | 2 +- templates/web/base/reports/_list-filters.html | 3 ++- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f724b8f98..d7be16b11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Improve Bing geocoder results. - Add option of checking passwords against Have I Been Pwned. - Add aerial maps option to Bing maps. + - Select matches for both filter category and group. #3110 - Changes: - Mark user as active when sent an email alert. - Bugfixes: diff --git a/t/app/controller/around.t b/t/app/controller/around.t index e35406bb6..ba5f8c48a 100644 --- a/t/app/controller/around.t +++ b/t/app/controller/around.t @@ -250,7 +250,10 @@ subtest 'check category, status and extra filtering works on /around' => sub { # Create one open and one fixed report in each category foreach my $category ( @$categories ) { my $contact = $mech->create_contact_ok( category => $category, body_id => $body->id, email => "$category\@example.org" ); - if ($category ne 'Pothole') { + if ($category eq 'Vegetation') { + $contact->set_extra_metadata(group => ['Environment', 'Green']); + $contact->update; + } elsif ($category eq 'Flytipping') { $contact->set_extra_metadata(group => ['Environment']); $contact->update; } @@ -281,6 +284,9 @@ subtest 'check category, status and extra filtering works on /around' => sub { $mech->get_ok( '/around?filter_group=Environment&bbox=' . $bbox ); $mech->content_contains('.*?.*?[% END %] diff --git a/templates/web/base/report/new/category.html b/templates/web/base/report/new/category.html index b5bfd0251..37479e4a5 100644 --- a/templates/web/base/report/new/category.html +++ b/templates/web/base/report/new/category.html @@ -10,7 +10,7 @@ END [% IF category_options.size OR category_groups.size ~%] [%~ BLOCK category_option ~%] [% cat_lc = cat.category | lower =%] - diff --git a/templates/web/base/reports/_list-filters.html b/templates/web/base/reports/_list-filters.html index ee9815157..9973a0c1f 100644 --- a/templates/web/base/reports/_list-filters.html +++ b/templates/web/base/reports/_list-filters.html @@ -1,9 +1,10 @@ [% select_status = PROCESS 'reports/_list-filter-status.html' %] +[%# We want to only select things that match all filters, if filters are provided ~%] [% BLOCK category_options %] [% FOR cat IN categories %] [% SET cat_safe = mark_safe(cat.category) %] - -- cgit v1.2.3 From 859fe1336c653bc3a533fd7f1be23cc56b61c03c Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 16 Jul 2020 11:09:48 +0100 Subject: Make correct selection from single filter choice. If a single filter item, that was in multiple groups, was selected, then the entry in the last group was being selected in the category dropdown when starting a new report. Make sure we check for a match in the group first, the same behaviour as when there is an existing category. --- web/cobrands/fixmystreet/fixmystreet.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js index d79f17049..ca36d5b46 100644 --- a/web/cobrands/fixmystreet/fixmystreet.js +++ b/web/cobrands/fixmystreet/fixmystreet.js @@ -1310,6 +1310,20 @@ fixmystreet.update_pin = function(lonlat, savePushState) { } }; +(function() { // fetch_reporting_data closure + +function re_select(group, category) { + var cat_in_group = $("#form_category optgroup[label=\"" + group + "\"] option[value=\"" + category + "\"]"); + if (cat_in_group.length) { + cat_in_group.prop({selected:true}); + return true; + } else if ($("#form_category option[value=\"" + category + "\"]").length) { + $("#form_category").val(category); + return true; + } + return false; +} + fixmystreet.fetch_reporting_data = function() { $.getJSON('/report/new/ajax', { latitude: $('#fixmystreet\\.latitude').val(), @@ -1364,16 +1378,12 @@ fixmystreet.fetch_reporting_data = function() { } $('#form_category_row').html(data.category); - var cat_in_group = $("#form_category optgroup[label=\"" + old_category_group + "\"] option[value=\"" + old_category + "\"]"); - if (cat_in_group.length) { - cat_in_group.prop({selected:true}); - } else if ($("#form_category option[value=\"" + old_category + "\"]").length) { - $("#form_category").val(old_category); - } else if (filter_category !== undefined && $("#form_category option[value='" + filter_category + "']").length) { + var reselected = re_select(old_category_group, old_category); + if (!reselected && filter_category !== undefined) { // If the category filter appears on the map and the user has selected // something from it, then pre-fill the category field in the report, // if it's a value already present in the drop-down. - $("#form_category").val(filter_category); + re_select(old_category_group, filter_category); } fixmystreet.set_up.category_groups(old_category_group); @@ -1408,6 +1418,8 @@ fixmystreet.fetch_reporting_data = function() { }); }; +})(); // fetch_reporting_data closure + fixmystreet.display = { begin_report: function(lonlat, saveHistoryState) { lonlat = fixmystreet.maps.begin_report(lonlat); -- cgit v1.2.3 From 60af4a782721257e2647cc37cff8234779361e43 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 16 Jul 2020 16:49:05 +0100 Subject: Spot user set on update not at object creation. The changes in 67823bc78 create the necessary metadata at the creation of a Comment object, but if the user is not included at that point but later (as is done by the normal public update flow), then it would not be created. Wrap the user accessor so we can make sure it is set. --- perllib/FixMyStreet/DB/Result/Comment.pm | 20 ++++++++++++++++++++ t/app/controller/report_updates.t | 1 + 2 files changed, 21 insertions(+) diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index c876f3459..2ef8925e6 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -145,6 +145,26 @@ sub FOREIGNBUILDARGS { return $opts; }; +=head2 around user + +Also make sure we catch the setting of a user on an object at a time other than +object creation, to set the extra field needed. + +=cut + +around user => sub { + my ( $orig, $self ) = ( shift, shift ); + my $res = $self->$orig(@_); + if (@_) { + if ($_[0]->is_superuser) { + $self->set_extra_metadata( is_superuser => 1 ); + } elsif (my $body = $_[0]->from_body) { + $self->set_extra_metadata( is_body_user => $body->id ); + } + } + return $res; +}; + =head2 get_cobrand_logged Get a cobrand object for the cobrand the update was made on. diff --git a/t/app/controller/report_updates.t b/t/app/controller/report_updates.t index 3198cf70c..0b60ac02b 100644 --- a/t/app/controller/report_updates.t +++ b/t/app/controller/report_updates.t @@ -907,6 +907,7 @@ subtest "check comment with no status change has not status in meta" => sub { is $report->state, 'investigating', 'correct report state'; is $update->problem_state, 'investigating', 'correct update state'; + is $update->get_extra_metadata('is_body_user'), $body->id, 'correct metadata'; $update_meta = $mech->extract_update_metas; like $update_meta->[0], qr/fixed/i, 'first update meta says fixed'; unlike $update_meta->[2], qr/State changed to/, 'second update meta does not include state change'; -- cgit v1.2.3 From 5cff522976b840167b0ed7a1226c16b272238d06 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Mon, 20 Jul 2020 11:11:55 +0100 Subject: [Hounslow] skip disabled contacts in enquiry form This is largely to resolve an issue with the TfL other category appearing in the contact form even though it is disabled, and hence has no contact details. --- t/app/controller/contact_enquiry.t | 10 ++++++++++ templates/web/base/contact/enquiry/index.html | 1 + 2 files changed, 11 insertions(+) diff --git a/t/app/controller/contact_enquiry.t b/t/app/controller/contact_enquiry.t index af249ec6c..feb2a0545 100644 --- a/t/app/controller/contact_enquiry.t +++ b/t/app/controller/contact_enquiry.t @@ -40,9 +40,18 @@ my $contact4 = $mech->create_contact_ok( category => 'Carriageway Defect', email => 'potholes@example.com', ); +my $contact5 = $mech->create_contact_ok( + body_id => $body->id, + category => 'Other (disabled)', + email => 'other@example.com', +); $contact->update( { extra => { group => 'General Enquiries' } } ); $contact2->update( { extra => { group => 'General Enquiries' } } ); $contact3->update( { extra => { group => 'Other' } } ); +$contact5->update( { extra => { group => 'Other' } } ); + +$contact5->push_extra_fields({ code => '_fms_disable_', 'disable_form' => 'true', description => 'form_disabled' }); +$contact5->update; FixMyStreet::override_config { ALLOWED_COBRANDS => ['bromley'], }, sub { subtest 'redirected to / if general enquiries not enabled' => sub { @@ -60,6 +69,7 @@ FixMyStreet::override_config { subtest 'Non-general enquiries category not shown' => sub { $mech->get_ok( '/contact/enquiry' ); $mech->content_lacks('Carriageway Defect'); + $mech->content_lacks('Other (disabled)'); $mech->content_contains('FOI Request'); }; diff --git a/templates/web/base/contact/enquiry/index.html b/templates/web/base/contact/enquiry/index.html index c88ca221e..1180c08b4 100644 --- a/templates/web/base/contact/enquiry/index.html +++ b/templates/web/base/contact/enquiry/index.html @@ -34,6 +34,7 @@ -- cgit v1.2.3 From 82e7b51deb93d83618e99d9e092951695f221906 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 29 Jul 2020 12:52:43 +0100 Subject: Include textarea in alert email form copy. The recaptcha used by fixmystreet.com uses a textarea. --- web/cobrands/fixmystreet/fixmystreet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js index ca36d5b46..032140298 100644 --- a/web/cobrands/fixmystreet/fixmystreet.js +++ b/web/cobrands/fixmystreet/fixmystreet.js @@ -1117,7 +1117,7 @@ $.extend(fixmystreet.set_up, { e.preventDefault(); var form = $('
    ').attr({ method:'post', action:"/alert/subscribe" }); form.append($('')); - $(this).closest('.js-alert-list').find('input[type=email], input[type=text], input[type=hidden], input[type=radio]:checked').each(function() { + $(this).closest('.js-alert-list').find('textarea, input[type=email], input[type=text], input[type=hidden], input[type=radio]:checked').each(function() { var $v = $(this); $('').attr({ name:$v.attr('name'), value:$v.val(), type:'hidden' }).appendTo(form); }); -- cgit v1.2.3 From 5bef5a64577601c01f875264911a882b7794ebca Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 29 Jul 2020 18:43:42 +0100 Subject: [fixmystreet.com] Show recaptcha on /around. The hidden embedded new report form on /around needs to be included, otherwise report submission could fail. --- perllib/FixMyStreet/Cobrand/UK.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perllib/FixMyStreet/Cobrand/UK.pm b/perllib/FixMyStreet/Cobrand/UK.pm index 322b257a7..772310545 100644 --- a/perllib/FixMyStreet/Cobrand/UK.pm +++ b/perllib/FixMyStreet/Cobrand/UK.pm @@ -427,8 +427,8 @@ sub requires_recaptcha { return 0 if $c->user_exists; return 0 if !FixMyStreet->config('RECAPTCHA'); + return 0 unless $c->action =~ /^(alert|report|around)/; return 0 if $c->user_country eq 'GB'; - return 0 unless $c->action =~ /^(alert|report)/; return 1; } -- cgit v1.2.3 From 34dd7c253d337ea922049390dc6ba44b8686e516 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 29 Jul 2020 15:19:58 +0100 Subject: Prefetch user bodies in admin user search. --- perllib/FixMyStreet/App/Controller/Admin/Users.pm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Admin/Users.pm b/perllib/FixMyStreet/App/Controller/Admin/Users.pm index c73f1429b..9fd4cd32d 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Users.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Users.pm @@ -64,7 +64,7 @@ sub index :Path : Args(0) { -or => [ email => { ilike => $isearch }, phone => { ilike => $isearch }, - name => { ilike => $isearch }, + 'me.name' => { ilike => $isearch }, from_body => $search_n, ] } @@ -87,7 +87,10 @@ sub index :Path : Args(0) { $users = $users->search({ from_body => { '!=', undef } }); } - $users = $users->search(undef, { order_by => [ \"name = ''", 'name' ] }); + $users = $users->search(undef, { + prefetch => 'from_body', + order_by => [ \"me.name = ''", 'me.name' ], + }); my @users = $users->all; $c->stash->{users} = \@users; if ($search) { -- cgit v1.2.3 From bd42d6a1fcb6c8e6d89413e0ee22617625d95bad Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 29 Jul 2020 09:03:56 +0100 Subject: Add database index for full text search. --- CHANGELOG.md | 1 + bin/update-schema | 4 ++ conf/general.yml-example | 4 ++ db/downgrade_0073---0072.sql | 2 + db/schema.sql | 12 ++++ db/schema_0073-add-full-text-search-index.sql | 13 ++++ perllib/DBIx/Class/Helper/ResultSet/Me.pm | 78 ++++++++++++++++++++++ .../FixMyStreet/App/Controller/Admin/Reports.pm | 46 +++++-------- perllib/FixMyStreet/DB/ResultSet/Comment.pm | 7 +- perllib/FixMyStreet/DB/ResultSet/Contact.pm | 2 +- perllib/FixMyStreet/DB/ResultSet/Problem.pm | 6 ++ perllib/FixMyStreet/Roles/FullTextSearch.pm | 22 ++++++ 12 files changed, 164 insertions(+), 33 deletions(-) create mode 100644 db/downgrade_0073---0072.sql create mode 100644 db/schema_0073-add-full-text-search-index.sql create mode 100644 perllib/DBIx/Class/Helper/ResultSet/Me.pm create mode 100644 perllib/FixMyStreet/Roles/FullTextSearch.pm diff --git a/CHANGELOG.md b/CHANGELOG.md index df449a480..e7f42fdc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - Interface for enabling anonymous reports for certain categories. #2989 - Better sort admin user table. - Centralise update creation to include fields. + - Add full text index to speed up admin search. - Development improvements: - `#geolocate_link` is now easier to re-style. #3006 - Links inside `#front-main` can be customised using `$primary_link_*` Sass variables. #3007 diff --git a/bin/update-schema b/bin/update-schema index 44e299b91..90876cf76 100755 --- a/bin/update-schema +++ b/bin/update-schema @@ -78,6 +78,8 @@ print "= Dry run =\n" unless $commit; my $upgrade = !$downgrade; +my $db_fts_config = FixMyStreet->config('DB_FULL_TEXT_SEARCH_CONFIG') || 'english'; + sub get_statements { my $path = shift; open(my $FP, '<', $path) or die $!; @@ -91,6 +93,7 @@ sub get_statements { $in_function = 1 if /create (or replace )?function/i; $in_function = 0 if /language (sql|'plpgsql')/i; if ($s =~ /;/ && !$in_function) { + $s =~ s/DB_FULL_TEXT_SEARCH_CONFIG/$db_fts_config/g; push @statements, $s; $s = ''; } @@ -212,6 +215,7 @@ else { # (assuming schema change files are never half-applied, which should be the case) sub get_db_version { return 'EMPTY' if ! table_exists('problem'); + return '0073' if index_exists('problem_fulltext_idx'); return '0072' if constraint_contains('contacts_state_check', 'staff'); return '0071' if table_exists('manifest_theme'); return '0070' if column_like('alert_type', "ref='new_problems'", 'head_title', '{{SITE_NAME}}'); diff --git a/conf/general.yml-example b/conf/general.yml-example index e42a1433e..ece50d0f3 100644 --- a/conf/general.yml-example +++ b/conf/general.yml-example @@ -66,6 +66,10 @@ EXAMPLE_PLACES: [ 'High Street', 'Main Street' ] LANGUAGES: - 'en-gb,English,en_GB' +# A postgresql text search configuration value - it should match the +# configuration used by the problem_fulltext_idx index. +DB_FULL_TEXT_SEARCH_CONFIG: 'english' + # If you're running an installation that is being used in a different time zone # from the server, you can set the time zone here (standard time zone string) TIME_ZONE: "" diff --git a/db/downgrade_0073---0072.sql b/db/downgrade_0073---0072.sql new file mode 100644 index 000000000..1ae19b9d7 --- /dev/null +++ b/db/downgrade_0073---0072.sql @@ -0,0 +1,2 @@ +DROP INDEX problem_fulltext_idx; +DROP INDEX comment_fulltext_idx; diff --git a/db/schema.sql b/db/schema.sql index ed21aded6..f198a287b 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -246,6 +246,12 @@ create index problem_user_id_idx on problem ( user_id ); create index problem_external_body_idx on problem(lower(external_body)); create index problem_radians_latitude_longitude_idx on problem(radians(latitude), radians(longitude)); create index problem_bodies_str_array_idx on problem USING gin(regexp_split_to_array(bodies_str, ',')); +create index problem_fulltext_idx on problem USING GIN( + to_tsvector( + 'english', + translate(id || ' ' || coalesce(external_id,'') || ' ' || coalesce(bodies_str,'') || ' ' || name || ' ' || title || ' ' || detail, '/.', ' ') + ) +); create table questionnaire ( id serial not null primary key, @@ -354,6 +360,12 @@ create table comment ( create index comment_user_id_idx on comment(user_id); create index comment_problem_id_idx on comment(problem_id); create index comment_problem_id_created_idx on comment(problem_id, created); +create index comment_fulltext_idx on comment USING GIN( + to_tsvector( + 'english', + translate(id || ' ' || problem_id || ' ' || coalesce(name,'') || ' ' || text, '/.', ' ') + ) +); -- Tokens for confirmations create table token ( diff --git a/db/schema_0073-add-full-text-search-index.sql b/db/schema_0073-add-full-text-search-index.sql new file mode 100644 index 000000000..9871386bd --- /dev/null +++ b/db/schema_0073-add-full-text-search-index.sql @@ -0,0 +1,13 @@ +CREATE INDEX CONCURRENTLY problem_fulltext_idx ON problem USING GIN( + to_tsvector( + 'DB_FULL_TEXT_SEARCH_CONFIG', + translate(id || ' ' || coalesce(external_id,'') || ' ' || coalesce(bodies_str,'') || ' ' || name || ' ' || title || ' ' || detail, '/.', ' ') + ) +); + +CREATE INDEX CONCURRENTLY comment_fulltext_idx on comment USING GIN( + to_tsvector( + 'DB_FULL_TEXT_SEARCH_CONFIG', + translate(id || ' ' || problem_id || ' ' || coalesce(name,'') || ' ' || text, '/.', ' ') + ) +); diff --git a/perllib/DBIx/Class/Helper/ResultSet/Me.pm b/perllib/DBIx/Class/Helper/ResultSet/Me.pm new file mode 100644 index 000000000..6077cffe7 --- /dev/null +++ b/perllib/DBIx/Class/Helper/ResultSet/Me.pm @@ -0,0 +1,78 @@ +package DBIx::Class::Helper::ResultSet::Me; +$DBIx::Class::Helper::ResultSet::Me::VERSION = '2.036000'; +# ABSTRACT: Define predefined searches more nicely + +use strict; +use warnings; + +use parent 'DBIx::Class::ResultSet'; + +sub me { join('.', shift->current_source_alias, shift || q{}) } + +1; + +__END__ + +=pod + +=head1 NAME + +DBIx::Class::Helper::ResultSet::Me - Define predefined searches more nicely + +=head1 SYNOPSIS + + # note that this is normally a component for a ResultSet + package MySchema::ResultSet::Bar; + + use strict; + use warnings; + + use parent 'DBIx::Class::ResultSet'; + + use constant CANDY => 1; + + __PACKAGE__->load_components('Helper::ResultSet::Me'); + + sub candy { + $_[0]->search({ $_[0]->me.'type' => CANDY }) + } + + sub cake { + $_[0]->search({ $_[0]->me('type') => CAKE }) + } + + # in code using resultset: + my $candy_bars = $schema->resultset('Bar')->candy; + my $cake_bars = $schema->resultset('Bar')->cake; + +=head1 DESCRIPTION + +This component allows slightly nicer predefined search definition. See +L for a nice way to apply it to your +entire schema. + +It defines a single method that is shorter and (to most) clearer than +L, which is what it uses +for the L method. + +=head1 METHODS + +=head2 me + +Merely returns the SQL namespace for the current search with a C<.> at the end, +allowing internal resultset methods to be defined with C<< $self->me >> instead +of C<< $self->current_source_alias . q(.) >>. Also, if you pass it a single +argument it will append that to the returned string. + +=head1 AUTHOR + +Arthur Axel "fREW" Schmidt + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2020 by Arthur Axel "fREW" Schmidt. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +=cut diff --git a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm index 48386cf3e..052230643 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm @@ -50,6 +50,8 @@ sub index : Path { return if $c->cobrand->call_hook(report_search_query => $query, $p_page, $u_page, $order); + my $problems = $c->cobrand->problems; + if (my $search = $c->get_param('search')) { $search = $self->trim($search); @@ -62,9 +64,6 @@ sub index : Path { $c->stash->{searched} = $search; - my $search_n = 0; - $search_n = int($search) if $search =~ /^\d+$/; - my $like_search = "%$search%"; my $parsed = FixMyStreet::SMS->parse_username($search); @@ -92,20 +91,10 @@ sub index : Path { 'me.external_id' => { like => "%$1%" } ]; } else { - $query->{'-or'} = [ - 'me.id' => $search_n, - 'user.email' => { ilike => $like_search }, - 'user.phone' => { ilike => $like_search }, - 'me.external_id' => { ilike => $like_search }, - 'me.name' => { ilike => $like_search }, - 'me.title' => { ilike => $like_search }, - detail => { ilike => $like_search }, - bodies_str => { like => $like_search }, - cobrand_data => { like => $like_search }, - ]; + $problems = $problems->search_text($search); } - my $problems = $c->cobrand->problems->search( + $problems = $problems->search( $query, { join => 'user', @@ -119,6 +108,7 @@ sub index : Path { $c->stash->{problems} = [ $problems->all ]; $c->stash->{problems_pager} = $problems->pager; + my $updates = $c->cobrand->updates; if ($valid_email) { $query = [ 'user.email' => { ilike => $like_search }, @@ -133,24 +123,18 @@ sub index : Path { 'me.problem_id' => int($1), ]; } elsif ($search =~ /^area:(\d+)$/) { - $query = []; + $query = 0; } else { - $query = [ - 'me.id' => $search_n, - 'problem.id' => $search_n, - 'user.email' => { ilike => $like_search }, - 'user.phone' => { ilike => $like_search }, - 'me.name' => { ilike => $like_search }, - text => { ilike => $like_search }, - 'me.cobrand_data' => { ilike => $like_search }, - ]; + $updates = $updates->search_text($search); + $query = 1; } - if (@$query) { - my $updates = $c->cobrand->updates->search( - { - -or => $query, - }, + $query = { -or => $query } if ref $query; + + if ($query) { + $query = undef unless ref $query; + $updates = $updates->search( + $query, { '+columns' => ['user.email'], join => 'user', @@ -165,7 +149,7 @@ sub index : Path { } else { - my $problems = $c->cobrand->problems->search( + $problems = $problems->search( $query, { '+columns' => ['user.email'], diff --git a/perllib/FixMyStreet/DB/ResultSet/Comment.pm b/perllib/FixMyStreet/DB/ResultSet/Comment.pm index 034b86a40..0a4403e52 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Comment.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Comment.pm @@ -4,12 +4,17 @@ use base 'DBIx::Class::ResultSet'; use strict; use warnings; +use Moo; +with 'FixMyStreet::Roles::FullTextSearch'; +__PACKAGE__->load_components('Helper::ResultSet::Me'); +sub text_search_columns { qw(id problem_id name text) } +sub text_search_nulls { qw(name) } + sub to_body { my ($rs, $bodies) = @_; return FixMyStreet::DB::ResultSet::Problem::to_body($rs, $bodies, 1); } - sub timeline { my ( $rs ) = @_; diff --git a/perllib/FixMyStreet/DB/ResultSet/Contact.pm b/perllib/FixMyStreet/DB/ResultSet/Contact.pm index 3aceee9e7..accdbb7de 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Contact.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Contact.pm @@ -5,7 +5,7 @@ use strict; use warnings; use POSIX qw(strcoll); -sub me { join('.', shift->current_source_alias, shift || q{}) } +__PACKAGE__->load_components('Helper::ResultSet::Me'); =head2 not_deleted diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index 9f7c035ed..3ac9187ee 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -8,6 +8,12 @@ use Memcached; use mySociety::Locale; use FixMyStreet::DB; +use Moo; +with 'FixMyStreet::Roles::FullTextSearch'; +__PACKAGE__->load_components('Helper::ResultSet::Me'); +sub text_search_columns { qw(id external_id bodies_str name title detail) } +sub text_search_nulls { qw(external_id bodies_str) } + my $site_key; sub set_restriction { diff --git a/perllib/FixMyStreet/Roles/FullTextSearch.pm b/perllib/FixMyStreet/Roles/FullTextSearch.pm new file mode 100644 index 000000000..78dbd5c18 --- /dev/null +++ b/perllib/FixMyStreet/Roles/FullTextSearch.pm @@ -0,0 +1,22 @@ +package FixMyStreet::Roles::FullTextSearch; + +use Moo::Role; +use FixMyStreet; + +requires 'text_search_columns'; +requires 'text_search_nulls'; + +sub search_text { + my ($rs, $query) = @_; + my %nulls = map { $_ => 1 } $rs->text_search_nulls; + my @cols = map { + my $col = $rs->me($_); + $nulls{$_} ? "coalesce($col, '')" : $col; + } $rs->text_search_columns; + my $vector = "translate(" . join(" || ' ' || ", @cols) . ", '/.', ' ')"; + my $config = FixMyStreet->config('DB_FULL_TEXT_SEARCH_CONFIG') || 'english'; + $rs->search(\[ "to_tsvector('$config', $vector) @@ plainto_tsquery('$config', ?)", $query ]); +} + +1; + -- cgit v1.2.3 From 6c472bbf73bc3de5124a7eccd220a2a6a6b181d6 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 23 Jan 2020 11:23:13 +0000 Subject: [Zurich] Fix admin search box on some admin pages. --- templates/web/zurich/header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/web/zurich/header.html b/templates/web/zurich/header.html index 347bf6b70..49901ebb9 100644 --- a/templates/web/zurich/header.html +++ b/templates/web/zurich/header.html @@ -66,7 +66,7 @@
  • [% END %]
  • +
    +

    + [% loc( + "The user's name is displayed publicly on reports that have not been marked anonymous. + Names are not necessarily unique.") + %] +

    +
    + + +
  • + +
  • + +[% IF user %] + +[% END %] +
  • + +
  • + + +
  • +
  • +
  • + + +[% IF username_in_abuse %] +
  • +

    [% loc('User in abuse table') %]

    +
  • +[% END %] diff --git a/templates/web/base/admin/users/form.html b/templates/web/base/admin/users/form.html index efe885908..c782b7172 100644 --- a/templates/web/base/admin/users/form.html +++ b/templates/web/base/admin/users/form.html @@ -7,38 +7,9 @@ [% INCLUDE 'errors.html' errors = field_errors.values %]
      -
    • -
      -

      - [% loc( - "The user's name is displayed publicly on reports that have not been marked anonymous. - Names are not necessarily unique.") - %] -

      -
      - - -
    • -
    • - - [% IF user %] - - [% END %] -
    • -
    • - -
    • -
    • -
    • - + [% PROCESS 'admin/users/_form_details.html' %] - [% IF username_in_abuse %] -
    • -

      [% loc('User in abuse table') %]

      -
    • - [% END %] - - [% IF c.user.is_superuser || c.cobrand.moniker == 'zurich' %] + [% IF c.user.is_superuser %]
    • @@ -72,7 +43,7 @@

    • [% END %] - [% IF areas AND c.cobrand.moniker != 'zurich' %] + [% IF areas %]
    • @@ -94,14 +65,14 @@

    • [% END %] - [% IF contacts AND c.cobrand.moniker != 'zurich'%] + [% IF contacts %]
    • [% INCLUDE 'admin/category-checkboxes.html' hint=loc("Authorised staff users can be associated with the categories in which they operate.") %]
    • [% END %] - [% IF user.from_body AND c.cobrand.moniker != 'zurich' %] + [% IF user.from_body %]
    • @@ -116,7 +87,6 @@

    • [% END %] - [% IF c.cobrand.moniker != 'zurich' %]
    • @@ -197,7 +167,7 @@

    [% END %] - [% END %] + [% TRY %][% INCLUDE 'admin/users/form-extra-fields.html' %][% CATCH file %][% END %]

    diff --git a/templates/web/zurich/admin/users/form.html b/templates/web/zurich/admin/users/form.html new file mode 100644 index 000000000..5182d943e --- /dev/null +++ b/templates/web/zurich/admin/users/form.html @@ -0,0 +1,36 @@ +

    + + + + [% INCLUDE 'errors.html' errors = field_errors.values %] +
      + [% PROCESS 'admin/users/_form_details.html' %] + +
    • + + +
    • + +
    +

    + +

    + + [% IF user AND NOT user.from_body %] +
      +
    • +
    • +
    • +
    • +
    + [% END %] + +
    -- cgit v1.2.3 From 9756c8470d02bcfa36c77bd422181273727fa3aa Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Wed, 22 Jan 2020 10:51:13 +0000 Subject: [Zurich] Factor out admin index list table. --- templates/web/zurich/admin/_index_table.html | 20 ++++++++++++++++++++ templates/web/zurich/admin/index-dm.html | 27 +++------------------------ templates/web/zurich/admin/index-sdm.html | 26 +++----------------------- templates/web/zurich/admin/reports/index.html | 17 ++--------------- 4 files changed, 28 insertions(+), 62 deletions(-) create mode 100644 templates/web/zurich/admin/_index_table.html diff --git a/templates/web/zurich/admin/_index_table.html b/templates/web/zurich/admin/_index_table.html new file mode 100644 index 000000000..d523f4a20 --- /dev/null +++ b/templates/web/zurich/admin/_index_table.html @@ -0,0 +1,20 @@ + + + + + [% FOREACH col IN [ [ 'category', loc('Category') ], [ 'created', loc('Submitted') ], [ 'lastupdate', loc('Updated') ], [ 'state', loc('Status') ] ] %] + + [% END %] + [% IF include_subdiv %] + + [% END %] + + [% IF NOT no_edit %] + + [% END %] + + + + +[% INCLUDE 'admin/problem_row.html' %] +
    [% loc('ID') %][% loc('Description') %][% col.1 %] [% INCLUDE sort_arrow choice = col.0 %][% loc('Subdivision/Body') %][% loc('Photo') %]*
    diff --git a/templates/web/zurich/admin/index-dm.html b/templates/web/zurich/admin/index-dm.html index 39fa34dcb..eb791e8d2 100644 --- a/templates/web/zurich/admin/index-dm.html +++ b/templates/web/zurich/admin/index-dm.html @@ -6,34 +6,13 @@

    [% loc('Submitted') %]

    -[% INCLUDE list, problems = submitted.all, hash = 'submitted' %] +[% INCLUDE 'admin/_index_table.html' problems=submitted.all hash='submitted' %]

    Rückmeldung ausstehend

    -[% INCLUDE list, problems = approval.all, hash = 'feedback_pending' %] +[% INCLUDE 'admin/_index_table.html' problems=approval.all hash='feedback_pending' %]

    [% loc('All reports') %]

    -[% INCLUDE list, problems = other.all, include_subdiv = 1, hash = 'alle' %] +[% INCLUDE 'admin/_index_table.html' problems=other.all include_subdiv=1 hash='alle' %] [% INCLUDE 'pagination.html', admin = 1, param = 'p', hash = 'alle' %] [% INCLUDE 'admin/footer.html' %] - -[% BLOCK list %] - - - - - [% FOREACH col IN [ [ 'category', loc('Category') ], [ 'created', loc('Submitted') ], [ 'lastupdate', loc('Updated') ], [ 'state', loc('Status') ] ] %] - - [% END %] - [% IF include_subdiv %] - - [% END %] - - - - - - -[% INCLUDE 'admin/problem_row.html' %] -
    [% loc('ID') %][% loc('Description') %][% col.1 %] [% INCLUDE sort_arrow choice = col.0 %][% loc('Subdivision/Body') %][% loc('Photo') %]*
    -[% END %] diff --git a/templates/web/zurich/admin/index-sdm.html b/templates/web/zurich/admin/index-sdm.html index 68a2fcf62..6155f6a39 100644 --- a/templates/web/zurich/admin/index-sdm.html +++ b/templates/web/zurich/admin/index-sdm.html @@ -2,33 +2,13 @@ [% PROCESS 'admin/report_blocks.html' %]

    [% loc('New reports') %]

    -[% INCLUDE list, problems = reports_new.all, hash = 'new' %] +[% INCLUDE 'admin/_index_table.html' problems=reports_new.all hash='new' %]

    [% loc('Reports awaiting approval') %]

    -[% INCLUDE list, problems = reports_unpublished.all, hash = 'wait' %] +[% INCLUDE 'admin/_index_table.html' problems=reports_unpublished.all hash='wait' %]

    [% loc('Reports published') %]

    -[% INCLUDE list, problems = reports_published.all, no_edit = 1, hash = 'alle' %] +[% INCLUDE 'admin/_index_table.html' problems=reports_published.all no_edit=1 hash='alle' %] [% INCLUDE 'pagination.html', admin = 1, param = 'p', hash = 'alle' %] [% INCLUDE 'admin/footer.html' %] - -[% BLOCK list %] - - - - - [% FOREACH col IN [ [ 'category', loc('Category') ], [ 'created', loc('Submitted') ], [ 'lastupdate', loc('Updated') ], [ 'state', loc('Status') ] ] %] - - [% END %] - - [% IF NOT no_edit %] - - [% END %] - - - - -[% INCLUDE 'admin/problem_row.html' %] -
    [% loc('ID') %][% loc('Description') %][% col.1 %] [% INCLUDE sort_arrow choice = col.0 %][% loc('Photo') %]*
    -[% END %] diff --git a/templates/web/zurich/admin/reports/index.html b/templates/web/zurich/admin/reports/index.html index 481dfb49d..ecb7c9519 100644 --- a/templates/web/zurich/admin/reports/index.html +++ b/templates/web/zurich/admin/reports/index.html @@ -6,21 +6,8 @@ [% IF problems.size %] - - - - - [% FOREACH col IN [ [ 'category', loc('Category') ], [ 'created', loc('Submitted') ], [ 'lastupdate', loc('Updated') ], [ 'state', loc('Status') ] ] %] - - [% END %] - - - - [% INCLUDE 'admin/problem_row.html' %] -
    [% loc('ID') %][% loc('Description') %][% col.1 %] [% INCLUDE sort_arrow choice = col.0 %][% loc('Photo') %]*
    - -[% INCLUDE 'pagination.html', admin = 1, param = 'p', pager = problems_pager %] - + [% PROCESS 'admin/_index_table.html' %] + [% INCLUDE 'pagination.html', admin = 1, param = 'p', pager = problems_pager %] [% END %] [% INCLUDE 'admin/list_updates.html' %] -- cgit v1.2.3 From 8e9c3fb35f86f328bd31710af519a5e6b65f4c7f Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 23 Jan 2020 09:37:36 +0000 Subject: [Zurich] Factor out admin index ordering function. --- perllib/FixMyStreet/Cobrand/Zurich.pm | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index 42932ec41..7930ff6b1 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -454,6 +454,16 @@ sub admin_type { return $type; } +sub _admin_index_order { + my $self = shift; + my $c = $self->{c}; + my $order = $c->get_param('o') || 'created'; + my $dir = defined $c->get_param('d') ? $c->get_param('d') : 1; + $c->stash->{order} = $order; + $c->stash->{dir} = $dir; + return $dir ? { -desc => $order } : $order; +} + sub admin { my $self = shift; my $c = $self->{c}; @@ -466,13 +476,9 @@ sub admin { my @children = map { $_->id } $body->bodies->all; my @all = (@children, $body->id); - my $order = $c->get_param('o') || 'created'; - my $dir = defined $c->get_param('d') ? $c->get_param('d') : 1; - $c->stash->{order} = $order; - $c->stash->{dir} = $dir; - $order = { -desc => $order } if $dir; + my $order = $self->_admin_index_order; - # XXX No multiples or missing bodies + # No multiples or missing bodies $c->stash->{submitted} = $c->cobrand->problems->search({ state => [ 'submitted', 'confirmed' ], bodies_str => $c->stash->{body}->id, @@ -499,14 +505,9 @@ sub admin { $c->stash->{template} = 'admin/index-sdm.html'; my $body = $c->stash->{body}; + my $order = $self->_admin_index_order; - my $order = $c->get_param('o') || 'created'; - my $dir = defined $c->get_param('d') ? $c->get_param('d') : 1; - $c->stash->{order} = $order; - $c->stash->{dir} = $dir; - $order = { -desc => $order } if $dir; - - # XXX No multiples or missing bodies + # No multiples or missing bodies $c->stash->{reports_new} = $c->cobrand->problems->search( { state => 'in progress', bodies_str => $body->id, -- cgit v1.2.3 From fb0c5f39f46275e45074c8707f122a8c4e824ab5 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 23 Jan 2020 11:39:47 +0000 Subject: [Zurich] Remove unused filter-row. --- templates/web/zurich/admin/_index_table.html | 3 --- web/cobrands/sass/_admin.scss | 16 ---------------- 2 files changed, 19 deletions(-) diff --git a/templates/web/zurich/admin/_index_table.html b/templates/web/zurich/admin/_index_table.html index d523f4a20..232d4dc80 100644 --- a/templates/web/zurich/admin/_index_table.html +++ b/templates/web/zurich/admin/_index_table.html @@ -13,8 +13,5 @@ * [% END %] - - - [% INCLUDE 'admin/problem_row.html' %] diff --git a/web/cobrands/sass/_admin.scss b/web/cobrands/sass/_admin.scss index 4ae019776..e78820ae9 100644 --- a/web/cobrands/sass/_admin.scss +++ b/web/cobrands/sass/_admin.scss @@ -43,22 +43,6 @@ $button_bg_col: #a1a1a1; // also search bar (tables) text-align: center; font-weight: bold; } - tr.filter-row td { - display: none; /* TODO: reveal when filtering is implemented */ - padding: flip(4px 4px 4px 40px, 4px 40px 4px 4px); - background-color: $button_bg_col; - background-image: url('/cobrands/zurich/search-icon-white.png'); - background-position: flip(14px, right) center; - background-repeat: no-repeat; - border-bottom: 2px solid $table_border_color; - } - tr.filter-row td input[type=text] { - background-color: #e1e1e1; - width: 16em; - @include border-radius(4px); - border: none; - padding: 3px 0.5em; - } tr.is-deleted { background-color: #ffdddd; img { -- cgit v1.2.3 From 04ce3fe145b50107eb83a9dcdd349ac1f110cae6 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 24 Jan 2020 13:07:02 +0000 Subject: [Zurich] Remove a few test log out/ins. --- t/cobrand/zurich.t | 6 ------ 1 file changed, 6 deletions(-) diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index 8c5acddca..d8e39c304 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -747,26 +747,20 @@ subtest "superuser and dm can see stats" => sub { $user = $mech->log_in_ok( 'dm1@example.org' ); $mech->get( '/admin/stats' ); is $mech->res->code, 200, "dm can now also see stats page"; - $mech->log_out_ok; }; subtest "only superuser can edit bodies" => sub { - $user = $mech->log_in_ok( 'dm1@example.org' ); $mech->get( '/admin/body/' . $zurich->id ); is $mech->res->code, 403, "only superuser should be able to edit bodies"; - $mech->log_out_ok; }; subtest "only superuser can see 'Add body' form" => sub { - $user = $mech->log_in_ok( 'dm1@example.org' ); $mech->get_ok( '/admin/bodies' ); $mech->content_contains('External Body'); $mech->content_lacks( '
    log_out_ok; }; subtest "phone number is mandatory" => sub { - $user = $mech->log_in_ok( 'dm1@example.org' ); $mech->get_ok( '/report/new?lat=47.381817&lon=8.529156' ); $mech->submit_form( with_fields => { phone => "" } ); $mech->content_contains( 'Diese Information wird benötigt' ); -- cgit v1.2.3 From e0b0c1650aab6ed688fd32a2b2917d90d1bae604 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 21 Jan 2020 17:01:30 +0000 Subject: [Zurich] Add marking users as government-internal. --- t/cobrand/zurich.t | 11 ++++++++++- templates/web/zurich/admin/users/form.html | 7 +++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index d8e39c304..8def10d4f 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -98,7 +98,7 @@ my $superuser; subtest "set up superuser" => sub { $superuser = $mech->log_in_ok( 'super@example.org' ); # a user from body $zurich is a superuser, as $zurich has no parent id! - $superuser->update({ from_body => $zurich->id }); + $superuser->update({ name => 'Superuser', from_body => $zurich->id }); $EXISTING_REPORT_COUNT = get_export_rows_count($mech); $mech->log_out_ok; }; @@ -989,6 +989,15 @@ $mech->log_out_ok; subtest 'users at the top level can be edited' => sub { $mech->log_in_ok( $superuser->email ); $mech->get_ok('/admin/users/' . $superuser->id ); + $mech->content_contains('name="flagged">'); + $mech->submit_form_ok({ with_fields => { flagged => 1 } }); + $superuser->discard_changes; + is $superuser->flagged, 1, 'Marked as flagged'; + $mech->get_ok('/admin/users/' . $superuser->id ); + $mech->content_contains('name="flagged" checked'); + $mech->submit_form_ok({ with_fields => { flagged => 0 } }); + $superuser->discard_changes; + is $superuser->flagged, 0, 'Unmarked'; }; subtest 'A visit to /reports is okay' => sub { diff --git a/templates/web/zurich/admin/users/form.html b/templates/web/zurich/admin/users/form.html index 5182d943e..ec670a28a 100644 --- a/templates/web/zurich/admin/users/form.html +++ b/templates/web/zurich/admin/users/form.html @@ -19,6 +19,13 @@ +
  • + +
  • +

    -- cgit v1.2.3 From 8faf42a8d77f5e95840ed633bd21c32923ab2c51 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 23 Jan 2020 09:36:29 +0000 Subject: [Zurich] Mark reports by flagged users non-public. --- perllib/FixMyStreet/Cobrand/Zurich.pm | 9 +++++++++ t/cobrand/zurich.t | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index 7930ff6b1..e9fec8fda 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -1390,4 +1390,13 @@ sub hook_report_filter_status { } @$status; } +# If report is made by a flagged user, mark as non-public +sub report_new_munge_before_insert { + my ($self, $report) = @_; + + if ($report->user->flagged) { + $report->non_public(1); + } +} + 1; diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index 8def10d4f..97ac19761 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -14,6 +14,9 @@ use FixMyStreet::Script::Reports; use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; +FixMyStreet::App->log->disable('info'); +END { FixMyStreet::App->log->enable('info'); } + # Check that you have the required locale installed - the following # should return a line with de_CH.utf8 in. If not install that locale. # @@ -764,6 +767,14 @@ subtest "phone number is mandatory" => sub { $mech->get_ok( '/report/new?lat=47.381817&lon=8.529156' ); $mech->submit_form( with_fields => { phone => "" } ); $mech->content_contains( 'Diese Information wird benötigt' ); +}; + +subtest 'test flagged users make internal reports' => sub { + $user->update({ flagged => 1 }); + $mech->submit_form( with_fields => { phone => "01234", category => 'Cat1', detail => 'Details' } ); + my $report = FixMyStreet::DB->resultset('Problem')->search(undef, { order_by => { -desc => 'id' }, rows => 1 })->single; + is $report->non_public, 1; + $report->delete; $mech->log_out_ok; }; -- cgit v1.2.3 From 7e3742d37dbdab4e5be39230e4738457fecb34a4 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 23 Jan 2020 12:20:19 +0000 Subject: [Zurich] Show internal reports on their own page. The new internal reports is displayed the same as the main summary, and all reports shows both internal/non-internal. --- bin/zurich/overdue-alert | 1 + perllib/FixMyStreet/Cobrand/Zurich.pm | 7 +++++++ t/cobrand/zurich.t | 14 +++++++++++--- templates/web/zurich/admin/problem_row.html | 3 ++- templates/web/zurich/admin/reports/index.html | 2 +- templates/web/zurich/header.html | 11 ++++++++++- 6 files changed, 32 insertions(+), 6 deletions(-) diff --git a/bin/zurich/overdue-alert b/bin/zurich/overdue-alert index 7689c172f..b052d94d3 100755 --- a/bin/zurich/overdue-alert +++ b/bin/zurich/overdue-alert @@ -45,6 +45,7 @@ sub loop_through { state => $states, created => { '<', $date_threshold }, bodies_str => { '!=', undef }, + non_public => 0, } ); my %to_send = (); diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index e9fec8fda..be73ed9c1 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -468,6 +468,7 @@ sub admin { my $self = shift; my $c = $self->{c}; my $type = $c->stash->{admin_type}; + my $internal = $c->get_param('internal'); if ($type eq 'dm') { $c->stash->{template} = 'admin/index-dm.html'; @@ -482,12 +483,14 @@ sub admin { $c->stash->{submitted} = $c->cobrand->problems->search({ state => [ 'submitted', 'confirmed' ], bodies_str => $c->stash->{body}->id, + non_public => $internal ? 1 : 0, }, { order_by => $order, }); $c->stash->{approval} = $c->cobrand->problems->search({ state => 'feedback pending', bodies_str => $c->stash->{body}->id, + non_public => $internal ? 1 : 0, }, { order_by => $order, }); @@ -496,6 +499,7 @@ sub admin { $c->stash->{other} = $c->cobrand->problems->search({ state => { -not_in => [ 'submitted', 'confirmed', 'feedback pending' ] }, bodies_str => \@all, + non_public => $internal ? 1 : 0, }, { order_by => $order, })->page( $page ); @@ -511,12 +515,14 @@ sub admin { $c->stash->{reports_new} = $c->cobrand->problems->search( { state => 'in progress', bodies_str => $body->id, + non_public => $internal ? 1 : 0, }, { order_by => $order } ); $c->stash->{reports_unpublished} = $c->cobrand->problems->search( { state => 'feedback pending', bodies_str => $body->parent->id, + non_public => $internal ? 1 : 0, }, { order_by => $order } ); @@ -525,6 +531,7 @@ sub admin { $c->stash->{reports_published} = $c->cobrand->problems->search( { state => 'fixed - council', bodies_str => $body->parent->id, + non_public => $internal ? 1 : 0, }, { order_by => $order } )->page( $page ); diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index 97ac19761..ce38f65d4 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -769,12 +769,20 @@ subtest "phone number is mandatory" => sub { $mech->content_contains( 'Diese Information wird benötigt' ); }; +my $internal; subtest 'test flagged users make internal reports' => sub { $user->update({ flagged => 1 }); $mech->submit_form( with_fields => { phone => "01234", category => 'Cat1', detail => 'Details' } ); - my $report = FixMyStreet::DB->resultset('Problem')->search(undef, { order_by => { -desc => 'id' }, rows => 1 })->single; - is $report->non_public, 1; - $report->delete; + $internal = FixMyStreet::DB->resultset('Problem')->search(undef, { order_by => { -desc => 'id' }, rows => 1 })->single; + is $internal->non_public, 1; +}; + +subtest 'internal report admin display' => sub { + $mech->get_ok('/admin/summary'); + $mech->content_lacks('href="report_edit/' . $internal->id); + $mech->get_ok('/admin/summary?internal=1'); + $mech->content_contains('href="report_edit/' . $internal->id); + $internal->delete; $mech->log_out_ok; }; diff --git a/templates/web/zurich/admin/problem_row.html b/templates/web/zurich/admin/problem_row.html index 502a7bc39..a138339e6 100644 --- a/templates/web/zurich/admin/problem_row.html +++ b/templates/web/zurich/admin/problem_row.html @@ -21,7 +21,8 @@ [% prettify_state(problem.state) %] [% IF problem.state == 'feedback pending'; SET cs=problem.get_extra_metadata('closure_status'); - IF cs %] ([% prettify_state(cs) %]) [% END; END %] + IF cs %] ([% prettify_state(cs) %]) [% END; END %] + [% IF problem.non_public %]
    Interne Meldung[% END %] [% IF include_subdiv %] diff --git a/templates/web/zurich/admin/reports/index.html b/templates/web/zurich/admin/reports/index.html index ecb7c9519..e2790309b 100644 --- a/templates/web/zurich/admin/reports/index.html +++ b/templates/web/zurich/admin/reports/index.html @@ -1,4 +1,4 @@ -[% PROCESS 'admin/header.html' title=loc('Search Reports') %] +[% PROCESS 'admin/header.html' title=loc('All Reports') %] [% PROCESS 'admin/report_blocks.html' %] diff --git a/templates/web/zurich/header.html b/templates/web/zurich/header.html index 49901ebb9..c9343d8d6 100644 --- a/templates/web/zurich/header.html +++ b/templates/web/zurich/header.html @@ -41,11 +41,20 @@ [% pagename = c.req.uri.path %] [% pagename = pagename.replace('/admin/?(\w*).*', '$1') %] + [% IF admin_type == 'super' %]

  • [% loc('Summary') %]
  • + [% ELSE %] +
  • + Öffentliche +
  • +
  • + Interne +
  • + [% END %]
  • - [% loc('Reports') %] + [% loc('All') %]
  • [% IF admin_type == 'dm' OR admin_type == 'super' %]
  • -- cgit v1.2.3 From 8fd5db0cb1e873895564486f32d918de385d2f12 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 24 Jan 2020 14:20:29 +0000 Subject: [Zurich] No admin emails on internal reports. --- perllib/FixMyStreet/Cobrand/Zurich.pm | 1 + t/cobrand/zurich.t | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index be73ed9c1..695d10439 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -1061,6 +1061,7 @@ sub _admin_send_email { my ( $c, $template, $problem ) = @_; return unless $problem->get_extra_metadata('email_confirmed'); + return if $problem->non_public; my $to = $problem->name ? [ $problem->user->email, $problem->name ] diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index ce38f65d4..fb2c08550 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -775,6 +775,7 @@ subtest 'test flagged users make internal reports' => sub { $mech->submit_form( with_fields => { phone => "01234", category => 'Cat1', detail => 'Details' } ); $internal = FixMyStreet::DB->resultset('Problem')->search(undef, { order_by => { -desc => 'id' }, rows => 1 })->single; is $internal->non_public, 1; + $mech->clear_emails_ok; }; subtest 'internal report admin display' => sub { @@ -782,6 +783,24 @@ subtest 'internal report admin display' => sub { $mech->content_lacks('href="report_edit/' . $internal->id); $mech->get_ok('/admin/summary?internal=1'); $mech->content_contains('href="report_edit/' . $internal->id); +}; + +subtest 'test no email sent if closed' => sub { + $internal->state('feedback pending'); + $internal->set_extra_metadata('email_confirmed' => 1); + $internal->update; + + $mech->get_ok( '/admin/report_edit/' . $internal->id ); + $mech->submit_form_ok( { + button => 'publish_response', + with_fields => { + status_update => 'Testing no email sent', + } }); + + $internal->discard_changes; + is $internal->state, 'fixed - council'; + $mech->email_count_is(0); + $internal->delete; $mech->log_out_ok; }; -- cgit v1.2.3 From 1b275ad4e454d772a9874bf3b8aa2506d214a27d Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 23 Jan 2020 12:53:27 +0000 Subject: [Zurich] Add button to remove internal flag. --- perllib/FixMyStreet/Cobrand/Zurich.pm | 46 ++++++++++++++++++------- t/cobrand/zurich.t | 5 +++ templates/web/zurich/admin/report_edit-sdm.html | 6 ++++ templates/web/zurich/admin/reports/edit.html | 4 +++ 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index 695d10439..f3cfe8801 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -552,6 +552,18 @@ sub category_options { $c->stash->{category_options} = \@categories; } +sub report_remove_internal_flag { + my $self = shift; + my $c = $self->{c}; + my $problem = $c->stash->{problem}; + $c->forward('/auth/check_csrf_token'); + $problem->non_public(0); + $problem->update; + $c->forward('/admin/log_edit', [ $problem->id, 'problem', 'Intern Flag entfernt' ]); + # Make sure the problem's time_spent is updated + $self->update_admin_log($c, $problem); +} + sub admin_report_edit { my $self = shift; my $c = $self->{c}; @@ -631,6 +643,10 @@ sub admin_report_edit { } } + if ( ($type eq 'super' || $type eq 'dm') && $c->get_param('stop_internal') ) { + $self->report_remove_internal_flag; + return $self->admin_report_edit_done; + } # Problem updates upon submission if ( ($type eq 'super' || $type eq 'dm') && $c->get_param('submit') ) { @@ -871,12 +887,7 @@ sub admin_report_edit { $c->go('index'); } - $c->stash->{updates} = [ $c->model('DB::Comment') - ->search( { problem_id => $problem->id }, { order_by => 'created' } ) - ->all ]; - - $self->stash_states($problem); - return 1; + return $self->admin_report_edit_done; } if ($type eq 'sdm') { @@ -913,6 +924,8 @@ sub admin_report_edit { # Make sure the problem's time_spent is updated $self->update_admin_log($c, $problem); $c->res->redirect( '/admin/summary' ); + } elsif ($editable && $c->get_param('stop_internal')) { + $self->report_remove_internal_flag; } elsif ($editable && $c->get_param('submit')) { $c->forward('/auth/check_csrf_token'); @@ -953,13 +966,7 @@ sub admin_report_edit { } } - $c->stash->{updates} = [ $c->model('DB::Comment') - ->search( { problem_id => $problem->id }, { order_by => 'created' } ) - ->all ]; - - $self->stash_states($problem); - return 1; - + return $self->admin_report_edit_done; } $self->stash_states($problem); @@ -967,6 +974,19 @@ sub admin_report_edit { } +sub admin_report_edit_done { + my $self = shift; + my $c = $self->{c}; + my $problem = $c->stash->{problem}; + $c->stash->{updates} = [ $c->model('DB::Comment') + ->search( { problem_id => $problem->id }, { order_by => 'created' } ) + ->all ]; + + $self->stash_states($problem); + return 1; +} + + sub admin_district_lookup { my ($self, $row) = @_; FixMyStreet::Geocode::Zurich::admin_district($row->local_coords); diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index fb2c08550..cab7a3fef 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -800,7 +800,12 @@ subtest 'test no email sent if closed' => sub { $internal->discard_changes; is $internal->state, 'fixed - council'; $mech->email_count_is(0); +}; +subtest 'remove internal flag' => sub { + $mech->submit_form_ok( { form_number => 2, button => 'stop_internal' } ); + $internal->discard_changes; + is $internal->non_public, 0; $internal->delete; $mech->log_out_ok; }; diff --git a/templates/web/zurich/admin/report_edit-sdm.html b/templates/web/zurich/admin/report_edit-sdm.html index b468bc7a0..9d4afef20 100644 --- a/templates/web/zurich/admin/report_edit-sdm.html +++ b/templates/web/zurich/admin/report_edit-sdm.html @@ -85,6 +85,12 @@
    +[% IF problem.non_public %] +

    + +

    +[% END %] +

    [% status_message | safe %] diff --git a/templates/web/zurich/admin/reports/edit.html b/templates/web/zurich/admin/reports/edit.html index 147022981..c88c63307 100644 --- a/templates/web/zurich/admin/reports/edit.html +++ b/templates/web/zurich/admin/reports/edit.html @@ -119,6 +119,10 @@ [% status_message | safe %] +[% IF problem.non_public %] +

    +[% END %] +
    -- cgit v1.2.3 From 015a2411734f9c7230cdb71893fd6d1af2fb76f5 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Thu, 23 Jan 2020 13:23:12 +0000 Subject: [Zurich] SDMs immediately close internal reports. --- perllib/FixMyStreet/Cobrand/Zurich.pm | 13 +++++++++---- t/cobrand/zurich.t | 11 +++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index f3cfe8801..0e4c1d907 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -957,10 +957,15 @@ sub admin_report_edit { # If they clicked the no more updates button, we're done. if ($c->get_param('no_more_updates')) { - $problem->set_extra_metadata( subdiv_overdue => $self->overdue( $problem ) ); - $problem->bodies_str( $body->parent->id ); - $problem->whensent( undef ); - $self->set_problem_state($c, $problem, 'feedback pending'); + if ($problem->non_public) { + $problem->bodies_str( $body->parent->id ); + $self->set_problem_state($c, $problem, 'fixed - council'); + } else { + $problem->set_extra_metadata( subdiv_overdue => $self->overdue( $problem ) ); + $problem->bodies_str( $body->parent->id ); + $problem->whensent( undef ); + $self->set_problem_state($c, $problem, 'feedback pending'); + } $problem->update; $c->res->redirect( '/admin/summary' ); } diff --git a/t/cobrand/zurich.t b/t/cobrand/zurich.t index cab7a3fef..2992a37e7 100644 --- a/t/cobrand/zurich.t +++ b/t/cobrand/zurich.t @@ -802,7 +802,18 @@ subtest 'test no email sent if closed' => sub { $mech->email_count_is(0); }; +subtest 'SDM closing internal report' => sub { + $mech->log_in_ok('sdm1@example.org'); + $internal->update({ bodies_str => $subdivision->id, state => 'confirmed' }); + $mech->get_ok('/admin/report_edit/' . $internal->id); + $mech->submit_form_ok( { form_number => 2, button => 'no_more_updates' } ); + $internal->discard_changes; + is $internal->state, 'fixed - council', 'State updated'; +}; + subtest 'remove internal flag' => sub { + $internal->update({ bodies_str => $subdivision->id, state => 'confirmed' }); + $mech->get_ok('/admin/report_edit/' . $internal->id); $mech->submit_form_ok( { form_number => 2, button => 'stop_internal' } ); $internal->discard_changes; is $internal->non_public, 0; -- cgit v1.2.3 From b9cfc7b5a8366be9b24af2003051371623262d42 Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Mon, 2 Mar 2020 15:43:49 +0000 Subject: [Zurich] SDM can do fewer with internal reports. --- perllib/FixMyStreet/Cobrand/Zurich.pm | 7 +++++-- templates/web/zurich/admin/report_edit-sdm.html | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index 0e4c1d907..4551fcc4a 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -892,8 +892,7 @@ sub admin_report_edit { if ($type eq 'sdm') { - my $editable = $type eq 'sdm' && $body->id eq $problem->bodies_str; - $c->stash->{sdm_disabled} = $editable ? '' : 'disabled'; + my $editable = $body->id eq $problem->bodies_str; # Has cut-down edit template for adding update and sending back up only $c->stash->{template} = 'admin/report_edit-sdm.html'; @@ -971,6 +970,10 @@ sub admin_report_edit { } } + $c->stash->{sdm_disabled} = $editable ? '' : 'disabled'; + $c->stash->{sdm_disabled_internal} = $problem->non_public ? 'disabled' : ''; + $c->stash->{sdm_disabled_fixed} = $problem->is_fixed ? 'disabled' : ''; + return $self->admin_report_edit_done; } diff --git a/templates/web/zurich/admin/report_edit-sdm.html b/templates/web/zurich/admin/report_edit-sdm.html index 9d4afef20..8d1fa464c 100644 --- a/templates/web/zurich/admin/report_edit-sdm.html +++ b/templates/web/zurich/admin/report_edit-sdm.html @@ -87,15 +87,15 @@ [% IF problem.non_public %]

    - +

    [% END %] -

    +

    [% status_message | safe %] -

    +

    • @@ -104,7 +104,7 @@
    • - +
    @@ -115,7 +115,7 @@

    - +

    [% INCLUDE 'admin/list_updates.html' %] -- cgit v1.2.3 From c01d0a50cb9d57ba1c1d53a82d52487f4d5c0e3d Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Tue, 14 Apr 2020 16:39:00 +0100 Subject: [Zurich] SDM link internal reports to admin page. The reports are not visible on the public site, so SDMs need a way to see them. --- templates/web/zurich/admin/problem_row.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/web/zurich/admin/problem_row.html b/templates/web/zurich/admin/problem_row.html index a138339e6..08857f165 100644 --- a/templates/web/zurich/admin/problem_row.html +++ b/templates/web/zurich/admin/problem_row.html @@ -1,3 +1,5 @@ +[% no_edit = no_edit AND NOT c.req.params.internal ~%] + [%- FOR problem IN problems %] [% SET p_body = problem.bodies.values.0 %] Date: Mon, 6 Jul 2020 13:02:08 +0100 Subject: Make sure disabled inputs greyed out. --- web/cobrands/sass/_base.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss index 07bb2baa1..dbd357646 100644 --- a/web/cobrands/sass/_base.scss +++ b/web/cobrands/sass/_base.scss @@ -313,6 +313,9 @@ legend, box-shadow: none; color: #888888; } + &[disabled] { + background-color: #eee; + } } select.form-control { -- cgit v1.2.3 From 2e761c6825d76944fd579c98cd0a222d420ea02d Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 31 Jul 2020 10:56:12 +0100 Subject: Lowercase contact email when looking for user. --- perllib/FixMyStreet/App/Controller/Contact.pm | 2 +- t/app/controller/contact.t | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm index 9ce89a9e2..f919cbeff 100644 --- a/perllib/FixMyStreet/App/Controller/Contact.pm +++ b/perllib/FixMyStreet/App/Controller/Contact.pm @@ -199,7 +199,7 @@ sub prepare_params_for_email : Private { my $base_url = $c->cobrand->base_url(); my $admin_url = $c->cobrand->admin_base_url; - my $user = $c->cobrand->users->find( { email => $c->stash->{em} } ); + my $user = $c->cobrand->users->find( { email => lc $c->stash->{em} } ); if ( $user ) { $c->stash->{user_admin_url} = $admin_url . '/users/' . $user->id; $c->stash->{user_reports_admin_url} = $admin_url . '/reports?search=' . $user->email; diff --git a/t/app/controller/contact.t b/t/app/controller/contact.t index d6e56e7cc..01e8b0886 100644 --- a/t/app/controller/contact.t +++ b/t/app/controller/contact.t @@ -382,7 +382,7 @@ for my $test ( $mech->clear_emails_ok; $mech->get_ok('/contact'); - $test->{fields}{em} = $user->email; + $test->{fields}{em} = ucfirst $user->email; # Check case $mech->submit_form_ok( { with_fields => $test->{fields} } ); my $email = $mech->get_email; -- cgit v1.2.3 From 361bf0bc0d8b5e86ae2cde5f428129ae23af5999 Mon Sep 17 00:00:00 2001 From: Zarino Zappia Date: Mon, 3 Aug 2020 17:01:54 +0100 Subject: [fixmystreet.org] Update legal wording in footer --- docs/_includes/footer.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html index d7260aaa7..5f215cf8f 100644 --- a/docs/_includes/footer.html +++ b/docs/_includes/footer.html @@ -47,8 +47,10 @@

    mySociety is a registered charity in England and Wales (1076346) - and a limited company (03277032). Through our trading subsidiary - SocietyWorks (05798215) we also provide commercial services. + and a limited company (03277032). We provide commercial + services through our wholly owned subsidiary + SocietyWorks Ltd + (05798215).

  • -- cgit v1.2.3 From 994093c4675393bca6b84747e87a63e10cb063d9 Mon Sep 17 00:00:00 2001 From: M Somerville Date: Tue, 4 Aug 2020 15:05:55 +0100 Subject: Do not error in export if report has bad category. If category groups are enabled, the CSV export includes a subcategory, but it was only fetching a valid groups value if the row's contact was found. --- perllib/FixMyStreet/App/Controller/Dashboard.pm | 2 +- t/app/controller/dashboard.t | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index abb2e0988..f7273ca0e 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -493,7 +493,7 @@ sub generate_csv : Private { } if ($asked_for{subcategory}) { - my $group = $obj->contact && $obj->contact->groups; + my $group = $obj->contact ? $obj->contact->groups : []; $group = join(',', @$group); if ($group) { $hashref->{subcategory} = $obj->category; diff --git a/t/app/controller/dashboard.t b/t/app/controller/dashboard.t index 0f07bcae0..526b1a4c3 100644 --- a/t/app/controller/dashboard.t +++ b/t/app/controller/dashboard.t @@ -83,6 +83,7 @@ my $categories = scraper { FixMyStreet::override_config { ALLOWED_COBRANDS => 'no2fa', + COBRAND_FEATURES => { category_groups => { no2fa => 1 } }, MAPIT_URL => 'http://mapit.uk/', }, sub { @@ -174,13 +175,14 @@ FixMyStreet::override_config { subtest 'export as csv' => sub { $mech->create_problems_for_body(1, $body->id, 'Title', { detail => "this report\nis split across\nseveral lines", + category => 'Problem one', areas => ",$alt_area_id,2651,", }); $mech->get_ok('/dashboard?export=1'); my @rows = $mech->content_as_csv; is scalar @rows, 19, '1 (header) + 18 (reports) = 19 lines'; - is scalar @{$rows[0]}, 20, '20 columns present'; + is scalar @{$rows[0]}, 21, '21 columns present'; is_deeply $rows[0], [ @@ -189,6 +191,7 @@ FixMyStreet::override_config { 'Detail', 'User Name', 'Category', + 'Subcategory', 'Created', 'Confirmed', 'Acknowledged', @@ -207,9 +210,9 @@ FixMyStreet::override_config { ], 'Column headers look correct'; - is $rows[5]->[14], 'Trowbridge', 'Ward column is name not ID'; - is $rows[5]->[15], '529025', 'Correct Easting conversion'; - is $rows[5]->[16], '179716', 'Correct Northing conversion'; + is $rows[5]->[15], 'Trowbridge', 'Ward column is name not ID'; + is $rows[5]->[16], '529025', 'Correct Easting conversion'; + is $rows[5]->[17], '179716', 'Correct Northing conversion'; }; subtest 'export updates as csv' => sub { -- cgit v1.2.3 From 30de263a85621fcad51991661475cdc422730c12 Mon Sep 17 00:00:00 2001 From: Chris Mytton Date: Mon, 20 Apr 2020 11:31:07 +0100 Subject: Extract identical photo upload forms into a partial --- templates/web/base/questionnaire/index.html | 25 +---------------------- templates/web/base/report/form/photo_upload.html | 24 ++++++++++++++++++++++ templates/web/base/report/update/form_update.html | 25 +---------------------- 3 files changed, 26 insertions(+), 48 deletions(-) create mode 100644 templates/web/base/report/form/photo_upload.html diff --git a/templates/web/base/questionnaire/index.html b/templates/web/base/questionnaire/index.html index 36123013f..7a08d461d 100644 --- a/templates/web/base/questionnaire/index.html +++ b/templates/web/base/questionnaire/index.html @@ -74,30 +74,7 @@

    -[% IF c.cobrand.allow_photo_upload %] - - - - [% IF field_errors.photo %] -

    [% field_errors.photo %]

    - [% END %] - -
    - [% IF upload_fileid %] -

    [% loc('You have already attached photos to this update. Note that you can attach a maximum of 3 to this update (if you try to upload more, the oldest will be removed).') %]

    - [% FOREACH id IN upload_fileid.split(',') %] - - [% END %] - [% END %] - - - - - -
    -[% END %] +[% INCLUDE report/form/photo_upload.html %]

    4. [% loc('Would you like to receive another questionnaire in 4 weeks, reminding you to check the status?') %]

    diff --git a/templates/web/base/report/form/photo_upload.html b/templates/web/base/report/form/photo_upload.html new file mode 100644 index 000000000..960141210 --- /dev/null +++ b/templates/web/base/report/form/photo_upload.html @@ -0,0 +1,24 @@ +[% IF c.cobrand.allow_photo_upload %] + + + + [% IF field_errors.photo %] +

    [% field_errors.photo %]

    + [% END %] + +
    + [% IF upload_fileid %] +

    [% loc('You have already attached photos to this update. Note that you can attach a maximum of 3 to this update (if you try to upload more, the oldest will be removed).') %]

    + [% FOREACH id IN upload_fileid.split(',') %] + + [% END %] + [% END %] + + + + + +
    +[% END %] diff --git a/templates/web/base/report/update/form_update.html b/templates/web/base/report/update/form_update.html index 089d63f55..7132331bc 100644 --- a/templates/web/base/report/update/form_update.html +++ b/templates/web/base/report/update/form_update.html @@ -1,30 +1,7 @@ -[% IF c.cobrand.allow_photo_upload %] - - - - [% IF field_errors.photo %] -

    [% field_errors.photo %]

    - [% END %] - -
    - [% IF upload_fileid %] -

    [% loc('You have already attached photos to this update. Note that you can attach a maximum of 3 to this update (if you try to upload more, the oldest will be removed).') %]

    - [% FOREACH id IN upload_fileid.split(',') %] - - [% END %] - [% END %] - - - - - -
    -[% END %] +[% INCLUDE report/form/photo_upload.html %] [% TRY %][% PROCESS 'report/update/before_update.html' %][% CATCH file %][% END %] -- cgit v1.2.3 From ed0002b0d76560cc3d5b1e62f8395bbfae74403c Mon Sep 17 00:00:00 2001 From: Chris Mytton Date: Wed, 8 Apr 2020 14:39:19 +0100 Subject: Add photo upload field to inspector form This adds the code for photo uploads from the regular update form to the inspector form, and adds details to the documentation. --- CHANGELOG.md | 1 + docs/_includes/admin-tasks-content.md | 3 ++ .../img/pro-user-guide/report-public-update.png | Bin 10740 -> 39301 bytes perllib/FixMyStreet/App/Controller/Report.pm | 7 +++++ t/app/controller/report_inspect.t | 31 +++++++++++++++++++-- templates/web/base/report/_inspect.html | 2 +- .../web/base/report/inspect/public_update.html | 3 ++ web/cobrands/fixmystreet/fixmystreet.js | 11 ++++---- web/cobrands/fixmystreet/staff.js | 8 ++++-- 9 files changed, 55 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7f42fdc0..de9b71d38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ - Disable category rename on Open311 categories when unprotected. #2957 - In category admin, group is already shown elsewhere. - Add assigned_(users|categories)_only functionality. + - Inspectors can now include a photo with public updates. #2902 - Bugfixes: - Application user in Docker container can't install packages. #2914 - Look at all categories when sending reports. diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index ac9e81f9e..cd6043c25 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -350,6 +350,9 @@ If ‘Create reports/updates as the council’ is ticked in the staff user’s a updates will be labeled as from the council rather than the staff member’s name. For the sake of staff privacy, this is usually preferable. +Staff with appropriate permissions can also attach an image to the update, which will then be +displayed publicly alongside the text update. + This option is also available via the general 'Update' box under the main report. #### Requests for more information diff --git a/docs/assets/img/pro-user-guide/report-public-update.png b/docs/assets/img/pro-user-guide/report-public-update.png index e6b4ac587..d4b390a70 100644 Binary files a/docs/assets/img/pro-user-guide/report-public-update.png and b/docs/assets/img/pro-user-guide/report-public-update.png differ diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index dd7abc563..991064f55 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -554,6 +554,12 @@ sub inspect : Private { $c->cobrand->call_hook(report_inspect_update_extra => $problem); + $c->forward('/photo/process_photo'); + if ( my $photo_error = delete $c->stash->{photo_error} ) { + $valid = 0; + push @{ $c->stash->{errors} }, $photo_error; + } + if ($valid) { $problem->lastupdate( \'current_timestamp' ); $problem->update; @@ -574,6 +580,7 @@ sub inspect : Private { created => $timestamp, confirmed => $timestamp, user => $c->user->obj, + photo => $c->stash->{upload_fileid} || undef, %update_params, } ); } diff --git a/t/app/controller/report_inspect.t b/t/app/controller/report_inspect.t index de9d689cd..48dceaf04 100644 --- a/t/app/controller/report_inspect.t +++ b/t/app/controller/report_inspect.t @@ -1,5 +1,6 @@ use FixMyStreet::TestMech; use Test::MockModule; +use Path::Class; my $mech = FixMyStreet::TestMech->new; @@ -42,6 +43,9 @@ my $user = $mech->log_in_ok('body@example.com'); $user->set_extra_metadata('categories', [ $contact->id ]); $user->update( { from_body => $oxon } ); +my $sample_file = file(__FILE__)->parent->file("sample.jpg")->stringify; +ok -e $sample_file, "sample file $sample_file exists"; + FixMyStreet::override_config { MAPIT_URL => 'http://mapit.uk/', ALLOWED_COBRANDS => 'fixmystreet', @@ -589,7 +593,27 @@ FixMyStreet::override_config { $mech->get_ok("/report/$report_id"); $mech->content_contains('Nearest calculated address', 'Address displayed'); $mech->content_contains('Constitution Hill, London, SW1A', 'Correct address displayed'); - } + }; + + subtest "test upload photo with public updates" => sub { + $user->user_body_permissions->delete; + $user->user_body_permissions->create({ body => $oxon, permission_type => 'report_inspect' }); + + $report->state('confirmed'); + $report->update; + $mech->get_ok("/report/$report_id"); + $mech->submit_form_ok({ button => 'save', with_fields => { + public_update => "This is a public update.", include_update => "1", + state => 'action scheduled', + photo1 => [ [ $sample_file, undef, Content_Type => 'image/jpeg' ], 1 ], + } }); + $report->discard_changes; + my $comment = $report->comments(undef, { rows => 1, order_by => { -desc => "id" }})->first; + is $comment->photo, '74e3362283b6ef0c48686fb0e161da4043bbcc97.jpeg', 'photo added to comment'; + $mech->get_ok("/report/$report_id"); + $mech->content_contains("/photo/c/" . $comment->id . ".0.jpeg"); + }; + }; foreach my $test ( @@ -705,7 +729,10 @@ FixMyStreet::override_config { priority => $rp->id, include_update => '1', detailed_information => 'XXX164XXXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - traffic_information => '' + traffic_information => '', + photo1 => '', + photo2 => '', + photo3 => '', }; my $values = $mech->visible_form_values('report_inspect_form'); is_deeply $values, $expected_fields, 'correct form fields present'; diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html index 88fbdd70b..30848b5d4 100644 --- a/templates/web/base/report/_inspect.html +++ b/templates/web/base/report/_inspect.html @@ -5,7 +5,7 @@ [% INCLUDE 'errors.html' %] - + [% INCLUDE 'report/inspect/information.html' %] diff --git a/templates/web/base/report/inspect/public_update.html b/templates/web/base/report/inspect/public_update.html index be07d7b89..31940b6ee 100644 --- a/templates/web/base/report/inspect/public_update.html +++ b/templates/web/base/report/inspect/public_update.html @@ -7,8 +7,11 @@ [% loc('Save with a public update') %]

    +
    + [% INCLUDE report/form/photo_upload.html %]

    [% INCLUDE 'admin/response_templates_select.html' for='public_update' %]

    +
    diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js index 032140298..8073530c6 100644 --- a/web/cobrands/fixmystreet/fixmystreet.js +++ b/web/cobrands/fixmystreet/fixmystreet.js @@ -660,6 +660,11 @@ $.extend(fixmystreet.set_up, { }, dropzone: function($context) { + if ('Dropzone' in window) { + Dropzone.autoDiscover = false; + } else { + return; + } // Pass a jQuery element, eg $('.foobar'), into this function // to limit all the selectors to that element. Handy if you want @@ -670,12 +675,6 @@ $.extend(fixmystreet.set_up, { $context = $(document); } - if ('Dropzone' in window) { - Dropzone.autoDiscover = false; - } else { - return; - } - var forms = $('[for="form_photo"], .js-photo-label', $context).closest('form'); forms.each(function() { // Internal $context is the individual form with the photo upload inside diff --git a/web/cobrands/fixmystreet/staff.js b/web/cobrands/fixmystreet/staff.js index 89d7cab39..e62ae6ba7 100644 --- a/web/cobrands/fixmystreet/staff.js +++ b/web/cobrands/fixmystreet/staff.js @@ -137,6 +137,10 @@ fixmystreet.staff_set_up = { document.getElementById('side-inspect').scrollIntoView(); } + // make sure dropzone is set up, otherwise loading problem with + // JS leaves this uninitialized. + fixmystreet.set_up.dropzone($inspect_form); + function updateTemplates(opts) { opts.category = opts.category || $inspect_form.find('[name=category]').val(); opts.state = opts.state || $inspect_form.find('[name=state]').val(); @@ -212,9 +216,9 @@ fixmystreet.staff_set_up = { var $checkbox = $(this); var toggle_public_update = function() { if ($checkbox.prop('checked')) { - $('#public_update').parents('p').show(); + $('#public_update_form_fields').show(); } else { - $('#public_update').parents('p').hide(); + $('#public_update_form_fields').hide(); } }; $checkbox.on('change', function() { -- cgit v1.2.3 From ae687e6dd042982f2423d4e7e79e41c54d729e7b Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Mon, 4 May 2020 17:38:39 +0100 Subject: Handle multipart inspector form in service worker. This manually reconstructs the POST as there is no support for formData in safari, plus our storage mechanism does not handle formData as it's not a simple object. --- templates/web/base/offline/service_worker.html | 27 ++++++++++--- web/cobrands/fixmystreet/fixmystreet.js | 6 +++ web/cobrands/fixmystreet/offline.js | 52 ++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/templates/web/base/offline/service_worker.html b/templates/web/base/offline/service_worker.html index 3c480f9b1..71e5c282a 100644 --- a/templates/web/base/offline/service_worker.html +++ b/templates/web/base/offline/service_worker.html @@ -52,15 +52,30 @@ addEventListener('fetch', fetchEvent => { } catch { fetchEvent.waitUntil(async function() { - var text = await request.text(); - let formData = new URLSearchParams(text); - formData.set('save', 2); - formData.set('saved_at', Math.floor(+new Date() / 1000)); - formData = formData.toString(); + var request_buffer = await request.arrayBuffer(); + var headers = request.headers; + let formData = {}; + formData.contentType = headers.get('Content-Type'); + let boundary_re = /.*boundary=(.*)/; + let bound = formData.contentType.match(boundary_re); + + let saved = '--' + bound[1] + "\r\nContent-Disposition: form-data; name=\"saved_at\"\r\n\r\n" + Math.floor(+new Date() / 1000) + "\r\n"; + var savedBuffer = new ArrayBuffer(saved.length); + var bufView = new Uint8Array(savedBuffer); + for (var i=0; i Date: Thu, 6 Aug 2020 09:43:53 +0100 Subject: [Open311] use staff categories when fetching reports At the moment if you fetch a report in a staff category it will still be created but will be in the Other category. Given that some staff categories are marked as private this can allow reports that should be private to be put in a public category. --- perllib/Open311/GetServiceRequests.pm | 2 +- t/open311/getservicerequests.t | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/perllib/Open311/GetServiceRequests.pm b/perllib/Open311/GetServiceRequests.pm index 2545f6f29..eea7c0f26 100644 --- a/perllib/Open311/GetServiceRequests.pm +++ b/perllib/Open311/GetServiceRequests.pm @@ -81,7 +81,7 @@ sub create_problems { } my $contacts = $self->schema->resultset('Contact') - ->active + ->not_deleted_admin ->search( { body_id => $body->id } ); for my $request (@$requests) { diff --git a/t/open311/getservicerequests.t b/t/open311/getservicerequests.t index 672459f3f..7a69d0ff6 100644 --- a/t/open311/getservicerequests.t +++ b/t/open311/getservicerequests.t @@ -534,6 +534,46 @@ subtest "non_public contacts result in non_public reports" => sub { }; +subtest "staff and non_public contacts result in non_public reports" => sub { + + $contact->update({ + non_public => 1, + state => 'staff' + }); + my $o = Open311->new( + jurisdiction => 'mysociety', + endpoint => 'http://example.com', + test_mode => 1, + test_get_returns => { 'requests.xml' => prepare_xml( {} ) } + ); + + my $update = Open311::GetServiceRequests->new( + system_user => $user, + start_date => $start_date, + end_date => $end_date + ); + + FixMyStreet::override_config { + MAPIT_URL => 'http://mapit.uk/', + }, sub { + $update->create_problems( $o, $body ); + }; + + my $p = FixMyStreet::DB->resultset('Problem')->search( + { external_id => 123456, category => $contact->category } + )->first; + + ok $p, 'problem created'; + is $p->non_public, 1, "report non_public is set correctly"; + + $p->delete; + $contact->update({ + non_public => 0, + state => 'confirmed' + }); + +}; + for my $test ( { test_desc => 'filters out phone numbers', -- cgit v1.2.3 From 52b1410f91669fab5f22cae9a9d9a040a0144c9a Mon Sep 17 00:00:00 2001 From: Matthew Somerville Date: Fri, 31 Jul 2020 15:27:42 +0100 Subject: Add extra ordering to search query to force full text index use. --- perllib/FixMyStreet/App/Controller/Admin/Reports.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm index 052230643..e3a425632 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Reports.pm @@ -92,6 +92,9 @@ sub index : Path { ]; } else { $problems = $problems->search_text($search); + # The below is added so that PostgreSQL does not try and use other indexes + # besides the full text search. It should have no impact on results shown. + $order = [ $order, { -desc => "me.id" }, { -desc => "me.created" } ]; } $problems = $problems->search( @@ -109,6 +112,7 @@ sub index : Path { $c->stash->{problems_pager} = $problems->pager; my $updates = $c->cobrand->updates; + $order = { -desc => 'me.id' }; if ($valid_email) { $query = [ 'user.email' => { ilike => $like_search }, @@ -126,6 +130,7 @@ sub index : Path { $query = 0; } else { $updates = $updates->search_text($search); + $order = [ $order, { -desc => "me.created" } ]; $query = 1; } @@ -140,7 +145,7 @@ sub index : Path { join => 'user', prefetch => [qw/problem/], rows => 50, - order_by => { -desc => 'me.id' } + order_by => $order, } )->page( $u_page ); $c->stash->{updates} = [ $updates->all ]; -- cgit v1.2.3 From 21efead606530cb754bb7b54586dbb8c1a1502ca Mon Sep 17 00:00:00 2001 From: M Somerville Date: Wed, 5 Aug 2020 15:55:29 +0100 Subject: Perform translate on full text search query too. --- perllib/FixMyStreet/Roles/FullTextSearch.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/perllib/FixMyStreet/Roles/FullTextSearch.pm b/perllib/FixMyStreet/Roles/FullTextSearch.pm index 78dbd5c18..dc7730798 100644 --- a/perllib/FixMyStreet/Roles/FullTextSearch.pm +++ b/perllib/FixMyStreet/Roles/FullTextSearch.pm @@ -13,9 +13,11 @@ sub search_text { my $col = $rs->me($_); $nulls{$_} ? "coalesce($col, '')" : $col; } $rs->text_search_columns; - my $vector = "translate(" . join(" || ' ' || ", @cols) . ", '/.', ' ')"; + my $vector = join(" || ' ' || ", @cols); + $vector = "translate($vector, '/.', ' ')"; + my $bind = "translate(?, '/.', ' ')"; my $config = FixMyStreet->config('DB_FULL_TEXT_SEARCH_CONFIG') || 'english'; - $rs->search(\[ "to_tsvector('$config', $vector) @@ plainto_tsquery('$config', ?)", $query ]); + $rs->search(\[ "to_tsvector('$config', $vector) @@ plainto_tsquery('$config', $bind)", $query ]); } 1; -- cgit v1.2.3 From 7af4f2cc87cd6ff55501bb2856193a03fe72158c Mon Sep 17 00:00:00 2001 From: M Somerville Date: Wed, 5 Aug 2020 15:56:10 +0100 Subject: Add database index for user full text search. --- bin/update-schema | 1 + db/downgrade_0074---0073.sql | 1 + db/schema.sql | 6 ++++++ db/schema_0074-add-users-full-text-search-index.sql | 6 ++++++ perllib/FixMyStreet/App/Controller/Admin/Users.pm | 17 +---------------- perllib/FixMyStreet/DB/ResultSet/Comment.pm | 1 + perllib/FixMyStreet/DB/ResultSet/Problem.pm | 1 + perllib/FixMyStreet/DB/ResultSet/User.pm | 5 +++++ perllib/FixMyStreet/Roles/FullTextSearch.pm | 9 +++++++-- t/app/controller/admin/users.t | 2 -- 10 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 db/downgrade_0074---0073.sql create mode 100644 db/schema_0074-add-users-full-text-search-index.sql diff --git a/bin/update-schema b/bin/update-schema index 90876cf76..1cfb59f52 100755 --- a/bin/update-schema +++ b/bin/update-schema @@ -215,6 +215,7 @@ else { # (assuming schema change files are never half-applied, which should be the case) sub get_db_version { return 'EMPTY' if ! table_exists('problem'); + return '0074' if index_exists('users_fulltext_idx'); return '0073' if index_exists('problem_fulltext_idx'); return '0072' if constraint_contains('contacts_state_check', 'staff'); return '0071' if table_exists('manifest_theme'); diff --git a/db/downgrade_0074---0073.sql b/db/downgrade_0074---0073.sql new file mode 100644 index 000000000..f900cc1d7 --- /dev/null +++ b/db/downgrade_0074---0073.sql @@ -0,0 +1 @@ +DROP INDEX users_fulltext_idx; diff --git a/db/schema.sql b/db/schema.sql index f198a287b..2c0cedb8d 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -41,6 +41,12 @@ create table users ( ); CREATE UNIQUE INDEX users_email_verified_unique ON users (email) WHERE email_verified; CREATE UNIQUE INDEX users_phone_verified_unique ON users (phone) WHERE phone_verified; +create index users_fulltext_idx on users USING GIN( + to_tsvector( + 'english', + translate(id || ' ' || coalesce(name,'') || ' ' || coalesce(email,'') || ' ' || coalesce(phone,''), '@.', ' ') + ) +); -- Record details of reporting bodies, including open311 configuration details create table body ( diff --git a/db/schema_0074-add-users-full-text-search-index.sql b/db/schema_0074-add-users-full-text-search-index.sql new file mode 100644 index 000000000..dc743bd47 --- /dev/null +++ b/db/schema_0074-add-users-full-text-search-index.sql @@ -0,0 +1,6 @@ +CREATE INDEX CONCURRENTLY users_fulltext_idx on users USING GIN( + to_tsvector( + 'DB_FULL_TEXT_SEARCH_CONFIG', + translate(id || ' ' || coalesce(name,'') || ' ' || coalesce(email,'') || ' ' || coalesce(phone,''), '@.', ' ') + ) +); diff --git a/perllib/FixMyStreet/App/Controller/Admin/Users.pm b/perllib/FixMyStreet/App/Controller/Admin/Users.pm index 9fd4cd32d..a05e737ab 100644 --- a/perllib/FixMyStreet/App/Controller/Admin/Users.pm +++ b/perllib/FixMyStreet/App/Controller/Admin/Users.pm @@ -49,26 +49,11 @@ sub index :Path : Args(0) { my $role = $c->get_param('role'); my $users = $c->cobrand->users; if ($search || $role) { - my $isearch; if ($search) { $search = $self->trim($search); $search =~ s/^<(.*)>$/$1/; # In case email wrapped in <...> $c->stash->{searched} = $search; - - $isearch = '%' . $search . '%'; - my $search_n = 0; - $search_n = int($search) if $search =~ /^\d+$/; - - $users = $users->search( - { - -or => [ - email => { ilike => $isearch }, - phone => { ilike => $isearch }, - 'me.name' => { ilike => $isearch }, - from_body => $search_n, - ] - } - ); + $users = $users->search_text($search); } if ($role) { $c->stash->{role_selected} = $role; diff --git a/perllib/FixMyStreet/DB/ResultSet/Comment.pm b/perllib/FixMyStreet/DB/ResultSet/Comment.pm index 0a4403e52..ea38b3e14 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Comment.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Comment.pm @@ -9,6 +9,7 @@ with 'FixMyStreet::Roles::FullTextSearch'; __PACKAGE__->load_components('Helper::ResultSet::Me'); sub text_search_columns { qw(id problem_id name text) } sub text_search_nulls { qw(name) } +sub text_search_translate { '/.' } sub to_body { my ($rs, $bodies) = @_; diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index 3ac9187ee..a7c365c1e 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -13,6 +13,7 @@ with 'FixMyStreet::Roles::FullTextSearch'; __PACKAGE__->load_components('Helper::ResultSet::Me'); sub text_search_columns { qw(id external_id bodies_str name title detail) } sub text_search_nulls { qw(external_id bodies_str) } +sub text_search_translate { '/.' } my $site_key; diff --git a/perllib/FixMyStreet/DB/ResultSet/User.pm b/perllib/FixMyStreet/DB/ResultSet/User.pm index 9a8a50559..baae024bf 100644 --- a/perllib/FixMyStreet/DB/ResultSet/User.pm +++ b/perllib/FixMyStreet/DB/ResultSet/User.pm @@ -5,6 +5,11 @@ use strict; use warnings; use Moo; +with 'FixMyStreet::Roles::FullTextSearch'; +__PACKAGE__->load_components('Helper::ResultSet::Me'); +sub text_search_columns { qw(id name email phone) } +sub text_search_nulls { qw(name email phone) } +sub text_search_translate { '@.' } # The database has a partial unique index on email (when email_verified is # true), and phone (when phone_verified is true). In the code, we can only diff --git a/perllib/FixMyStreet/Roles/FullTextSearch.pm b/perllib/FixMyStreet/Roles/FullTextSearch.pm index dc7730798..871b1d185 100644 --- a/perllib/FixMyStreet/Roles/FullTextSearch.pm +++ b/perllib/FixMyStreet/Roles/FullTextSearch.pm @@ -5,6 +5,7 @@ use FixMyStreet; requires 'text_search_columns'; requires 'text_search_nulls'; +requires 'text_search_translate'; sub search_text { my ($rs, $query) = @_; @@ -14,8 +15,12 @@ sub search_text { $nulls{$_} ? "coalesce($col, '')" : $col; } $rs->text_search_columns; my $vector = join(" || ' ' || ", @cols); - $vector = "translate($vector, '/.', ' ')"; - my $bind = "translate(?, '/.', ' ')"; + my $bind = '?'; + if (my $trans = $rs->text_search_translate) { + my $replace = ' ' x length $trans; + $vector = "translate($vector, '$trans', '$replace')"; + $bind = "translate(?, '$trans', '$replace')"; + } my $config = FixMyStreet->config('DB_FULL_TEXT_SEARCH_CONFIG') || 'english'; $rs->search(\[ "to_tsvector('$config', $vector) @@ plainto_tsquery('$config', $bind)", $query ]); } diff --git a/t/app/controller/admin/users.t b/t/app/controller/admin/users.t index bc8d28e2d..a3bd4a784 100644 --- a/t/app/controller/admin/users.t +++ b/t/app/controller/admin/users.t @@ -84,8 +84,6 @@ subtest 'user search' => sub { permissions => ['moderate', 'user_edit'], }); $user->add_to_roles($role); - $mech->get_ok('/admin/users?search=' . $haringey->id ); - $mech->content_contains('test@example.com'); $mech->get_ok('/admin/users?role=' . $role->id); $mech->content_contains('selected>Role A'); $mech->content_contains('test@example.com'); -- cgit v1.2.3 From 615ddb00265386849f32405426c5232b9127669a Mon Sep 17 00:00:00 2001 From: M Somerville Date: Wed, 5 Aug 2020 17:36:18 +0100 Subject: Remove use of $c from various functions. This means these functions can more easily be used in a non-web context. --- perllib/FixMyStreet/App/Controller/Around.pm | 2 +- perllib/FixMyStreet/App/Controller/Dashboard.pm | 12 ++++--- perllib/FixMyStreet/App/Controller/My.pm | 2 +- perllib/FixMyStreet/App/Controller/Report.pm | 8 ++--- perllib/FixMyStreet/App/Controller/Reports.pm | 2 +- perllib/FixMyStreet/Cobrand/BathNES.pm | 37 +++++++++----------- perllib/FixMyStreet/Cobrand/Buckinghamshire.pm | 15 ++++---- perllib/FixMyStreet/Cobrand/Default.pm | 4 +-- perllib/FixMyStreet/Cobrand/Oxfordshire.pm | 9 +++-- perllib/FixMyStreet/Cobrand/TfL.pm | 21 ++++++------ perllib/FixMyStreet/Cobrand/UK.pm | 4 +-- perllib/FixMyStreet/Cobrand/Zurich.pm | 8 ++--- perllib/FixMyStreet/DB/Result/Comment.pm | 26 ++++++++------ perllib/FixMyStreet/DB/Result/Problem.pm | 40 ++++++++++++---------- templates/web/base/my/anonymize.html | 2 +- templates/web/base/questionnaire/index.html | 2 +- templates/web/base/report/_council_sent_info.html | 2 +- templates/web/base/report/_report_meta_info.html | 2 +- templates/web/base/report/_update_state.html | 2 +- templates/web/base/report/updates.html | 2 +- .../web/bathnes/report/_council_sent_info.html | 2 +- .../cheshireeast/report/_council_sent_info.html | 2 +- .../fixmystreet.com/report/_report_meta_info.html | 2 +- .../web/hounslow/report/_council_sent_info.html | 2 +- .../web/isleofwight/report/_council_sent_info.html | 2 +- .../report/_council_sent_info.html | 2 +- .../web/oxfordshire/report/_main_sent_info.html | 2 +- .../peterborough/report/_council_sent_info.html | 2 +- templates/web/tfl/report/_council_sent_info.html | 2 +- templates/web/zurich/admin/problem_row.html | 2 +- templates/web/zurich/admin/reports/edit.html | 2 +- templates/web/zurich/report/banner.html | 2 +- 32 files changed, 114 insertions(+), 112 deletions(-) diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index 0e87d1e70..4c5c36106 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -295,7 +295,7 @@ sub map_features : Private { @pins = map { # Here we might have a DB::Problem or a DB::Result::Nearby, we always want the problem. my $p = (ref $_ eq 'FixMyStreet::DB::Result::Nearby') ? $_->problem : $_; - $p->pin_data($c, 'around'); + $p->pin_data('around'); } @$on_map, @$nearby; } diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index f7273ca0e..6fd4154b9 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -339,8 +339,9 @@ sub export_as_csv_updates : Private { 'text', 'user_name_display', 'reported_as', ], filename => $self->csv_filename($c, 1), + user => $c->user_exists ? $c->user->obj : undef, }; - $c->cobrand->call_hook("dashboard_export_updates_add_columns"); + $c->cobrand->call_hook(dashboard_export_updates_add_columns => $csv); $c->forward('generate_csv'); } @@ -407,8 +408,11 @@ sub export_as_csv : Private { 'reported_as', ], filename => $self->csv_filename($c, 0), + user => $c->user_exists ? $c->user->obj : undef, + category => $c->stash->{category}, + contacts => $c->stash->{contacts}, }; - $c->cobrand->call_hook("dashboard_export_problems_add_columns"); + $c->cobrand->call_hook(dashboard_export_problems_add_columns => $csv); $c->forward('generate_csv'); } @@ -460,7 +464,7 @@ sub generate_csv : Private { my $objects = $c->stash->{csv}->{objects}; while ( my $obj = $objects->next ) { - my $hashref = $obj->as_hashref($c, \%asked_for); + my $hashref = $obj->as_hashref(\%asked_for); $hashref->{user_name_display} = $obj->anonymous ? '(anonymous)' : $obj->name; @@ -554,7 +558,7 @@ sub heatmap : Local : Args(0) { if ($c->get_param('ajax')) { my @pins; while ( my $problem = $problems->next ) { - push @pins, $problem->pin_data($c, 'reports'); + push @pins, $problem->pin_data('reports'); } $c->stash->{pins} = \@pins; $c->detach('/reports/ajax', [ 'dashboard/heatmap-list.html' ]); diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm index f5c06d926..9d57c5e1d 100644 --- a/perllib/FixMyStreet/App/Controller/My.pm +++ b/perllib/FixMyStreet/App/Controller/My.pm @@ -157,7 +157,7 @@ sub get_problems : Private { while ( my $problem = $rs->next ) { $c->stash->{has_content}++; - push @$pins, $problem->pin_data($c, 'my', private => 1); + push @$pins, $problem->pin_data('my', private => 1); push @$problems, $problem; } diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index 991064f55..81d5b18b5 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -325,7 +325,7 @@ sub format_problem_for_display : Private { $c->res->content_type('application/json; charset=utf-8'); # encode_json doesn't like DateTime objects, so strip them out - my $report_hashref = $c->cobrand->problem_as_hashref( $problem, $c ); + my $report_hashref = $c->cobrand->problem_as_hashref( $problem ); delete $report_hashref->{created}; delete $report_hashref->{confirmed}; @@ -333,7 +333,7 @@ sub format_problem_for_display : Private { my $content = $json->encode( { report => $report_hashref, - updates => $c->cobrand->updates_as_hashref( $problem, $c ), + updates => $c->cobrand->updates_as_hashref( $problem ), } ); $c->res->body( $content ); @@ -354,7 +354,7 @@ sub generate_map_tags : Private { latitude => $problem->latitude, longitude => $problem->longitude, pins => $problem->used_map - ? [ $problem->pin_data($c, 'report', type => 'big', draggable => 1) ] + ? [ $problem->pin_data('report', type => 'big', draggable => 1) ] : [], ); @@ -670,7 +670,7 @@ sub _nearby_json :Private { # Want to treat these as if they were on map $nearby = [ map { $_->problem } @$nearby ]; my @pins = map { - my $p = $_->pin_data($c, 'around'); + my $p = $_->pin_data('around'); [ $p->{latitude}, $p->{longitude}, $p->{colour}, $p->{id}, $p->{title}, $pin_size, JSON->false ] diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 10902eab9..d0f2a9394 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -826,7 +826,7 @@ sub stash_report_sort : Private { sub add_row { my ( $c, $problem, $body, $problems, $pins ) = @_; push @{$problems->{$body}}, $problem; - push @$pins, $problem->pin_data($c, 'reports'); + push @$pins, $problem->pin_data('reports'); } sub ajax : Private { diff --git a/perllib/FixMyStreet/Cobrand/BathNES.pm b/perllib/FixMyStreet/Cobrand/BathNES.pm index 10e74056f..2dc1a4297 100644 --- a/perllib/FixMyStreet/Cobrand/BathNES.pm +++ b/perllib/FixMyStreet/Cobrand/BathNES.pm @@ -163,9 +163,8 @@ sub categories_restriction { # Do a manual prefetch of all staff users for contributed_by lookup sub _dashboard_user_lookup { my $self = shift; - my $c = $self->{c}; - my @user_ids = $c->model('DB::User')->search( + my @user_ids = FixMyStreet::DB->resultset('User')->search( { from_body => { '!=' => undef } }, { columns => [ 'id', 'email' ] })->all; @@ -174,23 +173,22 @@ sub _dashboard_user_lookup { } sub dashboard_export_updates_add_columns { - my $self = shift; - my $c = $self->{c}; + my ($self, $csv) = @_; - return unless $c->user->has_body_permission_to('export_extra_columns'); + return unless $csv->{user}->has_body_permission_to('export_extra_columns'); - push @{$c->stash->{csv}->{headers}}, "Staff User"; - push @{$c->stash->{csv}->{headers}}, "User Email"; - push @{$c->stash->{csv}->{columns}}, "staff_user"; - push @{$c->stash->{csv}->{columns}}, "user_email"; + push @{$csv->{headers}}, "Staff User"; + push @{$csv->{headers}}, "User Email"; + push @{$csv->{columns}}, "staff_user"; + push @{$csv->{columns}}, "user_email"; - $c->stash->{csv}->{objects} = $c->stash->{csv}->{objects}->search(undef, { + $csv->{objects} = $csv->{objects}->search(undef, { '+columns' => ['user.email'], join => 'user', }); my $user_lookup = $self->_dashboard_user_lookup; - $c->stash->{csv}->{extra_data} = sub { + $csv->{extra_data} = sub { my $report = shift; my $staff_user = ''; @@ -206,34 +204,33 @@ sub dashboard_export_updates_add_columns { } sub dashboard_export_problems_add_columns { - my $self = shift; - my $c = $self->{c}; + my ($self, $csv) = @_; - return unless $c->user->has_body_permission_to('export_extra_columns'); + return unless $csv->{user}->has_body_permission_to('export_extra_columns'); - $c->stash->{csv}->{headers} = [ - @{ $c->stash->{csv}->{headers} }, + $csv->{headers} = [ + @{ $csv->{headers} }, "User Email", "User Phone", "Staff User", "Attribute Data", ]; - $c->stash->{csv}->{columns} = [ - @{ $c->stash->{csv}->{columns} }, + $csv->{columns} = [ + @{ $csv->{columns} }, "user_email", "user_phone", "staff_user", "attribute_data", ]; - $c->stash->{csv}->{objects} = $c->stash->{csv}->{objects}->search(undef, { + $csv->{objects} = $csv->{objects}->search(undef, { '+columns' => ['user.email', 'user.phone'], join => 'user', }); my $user_lookup = $self->_dashboard_user_lookup; - $c->stash->{csv}->{extra_data} = sub { + $csv->{extra_data} = sub { my $report = shift; my $staff_user = ''; diff --git a/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm b/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm index 00264afea..75356dc87 100644 --- a/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm +++ b/perllib/FixMyStreet/Cobrand/Buckinghamshire.pm @@ -124,19 +124,18 @@ sub map_type { 'Buckinghamshire' } sub default_map_zoom { 3 } sub _dashboard_export_add_columns { - my $self = shift; - my $c = $self->{c}; + my ($self, $csv) = @_; - push @{$c->stash->{csv}->{headers}}, "Staff User"; - push @{$c->stash->{csv}->{columns}}, "staff_user"; + push @{$csv->{headers}}, "Staff User"; + push @{$csv->{columns}}, "staff_user"; # All staff users, for contributed_by lookup - my @user_ids = $c->model('DB::User')->search( + my @user_ids = FixMyStreet::DB->resultset('User')->search( { from_body => $self->body->id }, { columns => [ 'id', 'email', ] })->all; my %user_lookup = map { $_->id => $_->email } @user_ids; - $c->stash->{csv}->{extra_data} = sub { + $csv->{extra_data} = sub { my $report = shift; my $staff_user = ''; if (my $contributed_by = $report->get_extra_metadata('contributed_by')) { @@ -149,11 +148,11 @@ sub _dashboard_export_add_columns { } sub dashboard_export_updates_add_columns { - shift->_dashboard_export_add_columns; + shift->_dashboard_export_add_columns(@_); } sub dashboard_export_problems_add_columns { - shift->_dashboard_export_add_columns; + shift->_dashboard_export_add_columns(@_); } # Enable adding/editing of parish councils in the admin diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index bd8b7e4b4..82b3b7f45 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -1248,15 +1248,13 @@ sub get_geocoder { sub problem_as_hashref { my $self = shift; my $problem = shift; - my $ctx = shift; - return $problem->as_hashref( $ctx ); + return $problem->as_hashref; } sub updates_as_hashref { my $self = shift; my $problem = shift; - my $ctx = shift; return {}; } diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm index aa6c9a5a5..31db0fd4e 100644 --- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -203,13 +203,12 @@ sub available_permissions { } sub dashboard_export_problems_add_columns { - my $self = shift; - my $c = $self->{c}; + my ($self, $csv) = @_; - push @{$c->stash->{csv}->{headers}}, "HIAMS/Exor Ref"; - push @{$c->stash->{csv}->{columns}}, "external_ref"; + push @{$csv->{headers}}, "HIAMS/Exor Ref"; + push @{$csv->{columns}}, "external_ref"; - $c->stash->{csv}->{extra_data} = sub { + $csv->{extra_data} = sub { my $report = shift; # Try and get a HIAMS reference first of all my $ref = $report->get_extra_metadata('customer_reference'); diff --git a/perllib/FixMyStreet/Cobrand/TfL.pm b/perllib/FixMyStreet/Cobrand/TfL.pm index 9620c6b9e..e085b8bd6 100644 --- a/perllib/FixMyStreet/Cobrand/TfL.pm +++ b/perllib/FixMyStreet/Cobrand/TfL.pm @@ -242,11 +242,10 @@ sub available_permissions { } sub dashboard_export_problems_add_columns { - my $self = shift; - my $c = $self->{c}; + my ($self, $csv) = @_; - $c->stash->{csv}->{headers} = [ - map { $_ eq 'Ward' ? 'Borough' : $_ } @{ $c->stash->{csv}->{headers} }, + $csv->{headers} = [ + map { $_ eq 'Ward' ? 'Borough' : $_ } @{ $csv->{headers} }, "Agent responsible", "Safety critical", "Delivered to", @@ -255,8 +254,8 @@ sub dashboard_export_problems_add_columns { "Reassigned by", ]; - $c->stash->{csv}->{columns} = [ - @{ $c->stash->{csv}->{columns} }, + $csv->{columns} = [ + @{ $csv->{columns} }, "agent_responsible", "safety_critical", "delivered_to", @@ -265,18 +264,18 @@ sub dashboard_export_problems_add_columns { "reassigned_by", ]; - if ($c->stash->{category}) { - my ($contact) = grep { $_->category eq $c->stash->{category} } @{$c->stash->{contacts}}; + if ($csv->{category}) { + my ($contact) = grep { $_->category eq $csv->{category} } @{$csv->{contacts}}; if ($contact) { foreach (@{$contact->get_metadata_for_storage}) { next if $_->{code} eq 'safety_critical'; - push @{$c->stash->{csv}->{columns}}, "extra.$_->{code}"; - push @{$c->stash->{csv}->{headers}}, $_->{description}; + push @{$csv->{columns}}, "extra.$_->{code}"; + push @{$csv->{headers}}, $_->{description}; } } } - $c->stash->{csv}->{extra_data} = sub { + $csv->{extra_data} = sub { my $report = shift; my $agent = $report->shortlisted_user; diff --git a/perllib/FixMyStreet/Cobrand/UK.pm b/perllib/FixMyStreet/Cobrand/UK.pm index 772310545..8d464ca7b 100644 --- a/perllib/FixMyStreet/Cobrand/UK.pm +++ b/perllib/FixMyStreet/Cobrand/UK.pm @@ -398,9 +398,9 @@ sub link_to_council_cobrand { $handler->moniker ne $self->{c}->cobrand->moniker ) { my $url = sprintf("%s%s", $handler->base_url, $problem->url); - return sprintf("%s", $url, $problem->body( $self->{c} )); + return sprintf("%s", $url, $problem->body); } else { - return $problem->body( $self->{c} ); + return $problem->body; } } diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index 4551fcc4a..e33edc792 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -131,9 +131,8 @@ sub problem_has_user_response { sub problem_as_hashref { my $self = shift; my $problem = shift; - my $ctx = shift; - my $hashref = $problem->as_hashref( $ctx ); + my $hashref = $problem->as_hashref; if ( $problem->state eq 'submitted' ) { for my $var ( qw( photo is_fixed meta ) ) { @@ -171,7 +170,6 @@ sub problem_as_hashref { sub updates_as_hashref { my $self = shift; my $problem = shift; - my $ctx = shift; my $hashref = {}; @@ -182,7 +180,7 @@ sub updates_as_hashref { $hashref->{details} = FixMyStreet::App::View::Web::add_links( $problem->get_extra_metadata('public_response') || '' ); } else { - $hashref->{details} = sprintf( _('Assigned to %s'), $problem->body($ctx)->name ); + $hashref->{details} = sprintf( _('Assigned to %s'), $problem->body->name ); } } @@ -1319,7 +1317,7 @@ sub export_as_csv { my $report = shift; my $body_name = ""; - if ( my $external_body = $report->body($c) ) { + if ( my $external_body = $report->body ) { $body_name = $external_body->name || '[Unknown body]'; } diff --git a/perllib/FixMyStreet/DB/Result/Comment.pm b/perllib/FixMyStreet/DB/Result/Comment.pm index 2ef8925e6..a20c336a5 100644 --- a/perllib/FixMyStreet/DB/Result/Comment.pm +++ b/perllib/FixMyStreet/DB/Result/Comment.pm @@ -262,7 +262,8 @@ about an update. Can include HTML. =cut sub meta_line { - my ( $self, $c ) = @_; + my ( $self, $user ) = @_; + my $cobrand = $self->result_source->schema->cobrand; my $meta = ''; @@ -292,9 +293,9 @@ sub meta_line { $body = 'Island Roads'; } } - my $cobrand_always_view_body_user = $c->cobrand->call_hook("always_view_body_contribute_details"); + my $cobrand_always_view_body_user = $cobrand->call_hook("always_view_body_contribute_details"); my $can_view_contribute = $cobrand_always_view_body_user || - ($c->user_exists && $c->user->has_permission_to('view_body_contribute_details', $self->problem->bodies_str_ids)); + ($user && $user->has_permission_to('view_body_contribute_details', $self->problem->bodies_str_ids)); if ($self->text) { if ($can_view_contribute) { $meta = sprintf( _( 'Posted by %s (%s) at %s' ), $body, $user_name, Utils::prettify_dt( $self->confirmed ) ); @@ -320,21 +321,23 @@ sub meta_line { }; sub problem_state_processed { - my ( $self, $c ) = @_; + my $self = shift; return 'fixed - user' if $self->mark_fixed; return 'confirmed' if $self->mark_open; - my $cobrand_state = $c->cobrand->call_hook(problem_state_processed => $self); + my $cobrand = $self->result_source->schema->cobrand; + my $cobrand_state = $cobrand->call_hook(problem_state_processed => $self); return $cobrand_state || $self->problem_state; } sub problem_state_display { - my ( $self, $c ) = @_; + my $self = shift; - my $state = $self->problem_state_processed($c); + my $state = $self->problem_state_processed; return '' unless $state; - my $cobrand_name = $c->cobrand->moniker; + my $cobrand = $self->result_source->schema->cobrand; + my $cobrand_name = $cobrand->moniker; my $names = join(',,', @{$self->problem->body_names}); if ($names =~ /(Bromley|Isle of Wight|TfL)/) { ($cobrand_name = lc $1) =~ s/ //g; @@ -370,7 +373,8 @@ sub hide { } sub as_hashref { - my ($self, $c, $cols) = @_; + my ($self, $cols) = @_; + my $cobrand = $self->result_source->schema->cobrand; my $out = { id => $self->id, @@ -380,13 +384,13 @@ sub as_hashref { created => $self->created, }; - $out->{problem_state} = $self->problem_state_processed($c); + $out->{problem_state} = $self->problem_state_processed; $out->{photos} = [ map { $_->{url} } @{$self->photos} ] if !$cols || $cols->{photos}; if ($self->confirmed) { $out->{confirmed} = $self->confirmed if !$cols || $cols->{confirmed}; - $out->{confirmed_pp} = $c->cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; + $out->{confirmed_pp} = $cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; } return $out; diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 0653de32b..705f07f79 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -631,13 +631,14 @@ meta data about the report. =cut sub meta_line { - my ( $problem, $c ) = @_; + my ( $problem, $user ) = @_; + my $cobrand = $problem->result_source->schema->cobrand; my $date_time = Utils::prettify_dt( $problem->confirmed ); my $meta = ''; my $category = $problem->category_display; - $category = $c->cobrand->call_hook(change_category_text => $category) || $category; + $category = $cobrand->call_hook(change_category_text => $category) || $category; if ( $problem->anonymous ) { if ( $problem->service and $category && $category ne _('Other') ) { @@ -656,8 +657,8 @@ sub meta_line { } else { my $problem_name = $problem->name; - if ($c->user_exists and - $c->user->has_permission_to('view_body_contribute_details', $problem->bodies_str_ids) and + if ($user and + $user->has_permission_to('view_body_contribute_details', $problem->bodies_str_ids) and $problem->name ne $problem->user->name) { $problem_name = sprintf('%s (%s)', $problem->name, $problem->user->name ); } @@ -692,12 +693,12 @@ sub nearest_address { } sub body { - my ( $problem, $c ) = @_; + my ( $problem, $link ) = @_; my $body; if ($problem->external_body) { if ($problem->cobrand eq 'zurich') { my $cache = $problem->result_source->schema->cache; - return $cache->{bodies}{$problem->external_body} //= $c->model('DB::Body')->find({ id => $problem->external_body }); + return $cache->{bodies}{$problem->external_body} //= FixMyStreet::DB->resultset('Body')->find({ id => $problem->external_body }); } else { $body = FixMyStreet::Template::html_filter($problem->external_body); } @@ -705,7 +706,7 @@ sub body { my $bodies = $problem->bodies; my @body_names = sort map { my $name = $_->name; - if ($c and FixMyStreet->config('AREA_LINKS_FROM_PROBLEMS')) { + if ($link and FixMyStreet->config('AREA_LINKS_FROM_PROBLEMS')) { '' . FixMyStreet::Template::html_filter($name) . ''; } else { FixMyStreet::Template::html_filter($name); @@ -810,9 +811,10 @@ sub can_display_external_id { # This can return HTML and is safe, so returns a FixMyStreet::Template::SafeString sub duration_string { - my ( $problem, $c ) = @_; - my $body = $c->cobrand->call_hook(link_to_council_cobrand => $problem) || $problem->body($c); - my $handler = $c->cobrand->call_hook(get_body_handler_for_problem => $problem); + my $problem = shift; + my $cobrand = $problem->result_source->schema->cobrand; + my $body = $cobrand->call_hook(link_to_council_cobrand => $problem) || $problem->body(1); + my $handler = $cobrand->call_hook(get_body_handler_for_problem => $problem); if ( $handler && $handler->call_hook('is_council_with_case_management') ) { my $s = sprintf(_('Received by %s moments later'), $body); return FixMyStreet::Template::SafeString->new($s); @@ -896,7 +898,8 @@ sub resend { } sub as_hashref { - my ($self, $c, $cols) = @_; + my ($self, $cols) = @_; + my $cobrand = $self->result_source->schema->cobrand; my $state_t = FixMyStreet::DB->resultset("State")->display($self->state); @@ -916,11 +919,11 @@ sub as_hashref { }; $out->{is_fixed} = $self->fixed_states->{ $self->state } ? 1 : 0 if !$cols || $cols->{is_fixed}; $out->{photos} = [ map { $_->{url} } @{$self->photos} ] if !$cols || $cols->{photos}; - $out->{meta} = $self->confirmed ? $self->meta_line( $c ) : '' if !$cols || $cols->{meta}; - $out->{created_pp} = $c->cobrand->prettify_dt( $self->created ) if !$cols || $cols->{created_pp}; + $out->{meta} = $self->confirmed ? $self->meta_line : '' if !$cols || $cols->{meta}; + $out->{created_pp} = $cobrand->prettify_dt( $self->created ) if !$cols || $cols->{created_pp}; if ($self->confirmed) { $out->{confirmed} = $self->confirmed if !$cols || $cols->{confirmed}; - $out->{confirmed_pp} = $c->cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; + $out->{confirmed_pp} = $cobrand->prettify_dt( $self->confirmed ) if !$cols || $cols->{confirmed_pp}; } return $out; } @@ -973,10 +976,11 @@ has get_cobrand_logged => ( sub pin_data { - my ($self, $c, $page, %opts) = @_; - my $colour = $c->cobrand->pin_colour($self, $page); + my ($self, $page, %opts) = @_; + my $cobrand = $self->result_source->schema->cobrand; + my $colour = $cobrand->pin_colour($self, $page); my $title = $opts{private} ? $self->title : $self->title_safe; - $title = $c->cobrand->call_hook(pin_hover_title => $self, $title) || $title; + $title = $cobrand->call_hook(pin_hover_title => $self, $title) || $title; { latitude => $self->latitude, longitude => $self->longitude, @@ -986,7 +990,7 @@ sub pin_data { problem => $self, draggable => $opts{draggable}, type => $opts{type}, - base_url => $c->cobrand->relative_url_for_report($self), + base_url => $cobrand->relative_url_for_report($self), } }; diff --git a/templates/web/base/my/anonymize.html b/templates/web/base/my/anonymize.html index e82a03ce0..1159d4ebc 100644 --- a/templates/web/base/my/anonymize.html +++ b/templates/web/base/my/anonymize.html @@ -10,7 +10,7 @@ [% ELSIF problem %] [% IF problem.bodies_str %] -

    [% tprintf(loc('Your name has already been sent to %s, but we can hide it on this page:'), problem.body(c)) %]

    +

    [% tprintf(loc('Your name has already been sent to %s, but we can hide it on this page:'), problem.body) %]

    [% END %] diff --git a/templates/web/base/questionnaire/index.html b/templates/web/base/questionnaire/index.html index 7a08d461d..00911c5b1 100644 --- a/templates/web/base/questionnaire/index.html +++ b/templates/web/base/questionnaire/index.html @@ -49,7 +49,7 @@

    [% problem.title | html %]

    -

    [% problem.meta_line(c) | html %]

    +

    [% problem.meta_line(c.user) | html %]

    [% IF updates.size %]

    [% loc('Last update') %] diff --git a/templates/web/base/report/_council_sent_info.html b/templates/web/base/report/_council_sent_info.html index dca74f994..ae0f80a2e 100644 --- a/templates/web/base/report/_council_sent_info.html +++ b/templates/web/base/report/_council_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || problem.can_display_external_id %]

    [%- IF problem.can_display_external_id %] diff --git a/templates/web/base/report/_report_meta_info.html b/templates/web/base/report/_report_meta_info.html index e2a6412a3..73e897e34 100644 --- a/templates/web/base/report/_report_meta_info.html +++ b/templates/web/base/report/_report_meta_info.html @@ -1,4 +1,4 @@ -[% problem.meta_line(c) | html %] +[% problem.meta_line(c.user) | html %] [% IF c.user_exists AND c.user.id == problem.user_id AND !problem.anonymous %] ([% loc('Hide your name?') %]) [% END %] diff --git a/templates/web/base/report/_update_state.html b/templates/web/base/report/_update_state.html index 0274d7695..0bc4bfb52 100644 --- a/templates/web/base/report/_update_state.html +++ b/templates/web/base/report/_update_state.html @@ -4,7 +4,7 @@ OR update.mark_fixed OR update.mark_open %] -

    [% loc('State changed to:') %] [% update.problem_state_display(c) %]

    +

    [% loc('State changed to:') %] [% update.problem_state_display %]

    [%- global.last_state = update_state %] [%- IF update_state == "" AND update.mark_fixed %][% global.last_state = 'fixed - user' %][% END %] [%- IF update_state == "" AND update.mark_open %][% global.last_state = 'confirmed' %][% END %] diff --git a/templates/web/base/report/updates.html b/templates/web/base/report/updates.html index 93bae4d64..d65f1a287 100644 --- a/templates/web/base/report/updates.html +++ b/templates/web/base/report/updates.html @@ -27,6 +27,6 @@ [% RETURN %] [% END %] - [% update.meta_line(c) %] + [% update.meta_line(c.user) %] [% END %] diff --git a/templates/web/bathnes/report/_council_sent_info.html b/templates/web/bathnes/report/_council_sent_info.html index 1963020bc..c7e1eaf1d 100644 --- a/templates/web/bathnes/report/_council_sent_info.html +++ b/templates/web/bathnes/report/_council_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || problem.whensent %]

    [%- IF problem.whensent %] diff --git a/templates/web/cheshireeast/report/_council_sent_info.html b/templates/web/cheshireeast/report/_council_sent_info.html index 1963020bc..c7e1eaf1d 100644 --- a/templates/web/cheshireeast/report/_council_sent_info.html +++ b/templates/web/cheshireeast/report/_council_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || problem.whensent %]

    [%- IF problem.whensent %] diff --git a/templates/web/fixmystreet.com/report/_report_meta_info.html b/templates/web/fixmystreet.com/report/_report_meta_info.html index 2eab84c9e..82d4b289a 100644 --- a/templates/web/fixmystreet.com/report/_report_meta_info.html +++ b/templates/web/fixmystreet.com/report/_report_meta_info.html @@ -1,4 +1,4 @@ -[% problem.meta_line(c) | html %] +[% problem.meta_line(c.user) | html %] [% IF c.cobrand.moniker != problem.get_cobrand_logged.moniker AND problem.get_cobrand_logged.is_council %] using FixMyStreet Pro [% END %] diff --git a/templates/web/hounslow/report/_council_sent_info.html b/templates/web/hounslow/report/_council_sent_info.html index 921f17f5f..97a7c4ca5 100644 --- a/templates/web/hounslow/report/_council_sent_info.html +++ b/templates/web/hounslow/report/_council_sent_info.html @@ -1,5 +1,5 @@ [% SET can_display_external_id = problem.external_id AND c.user_exists AND c.user.belongs_to_body(problem.bodies_str) %] -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || can_display_external_id %]

    [% duration_clause %] diff --git a/templates/web/isleofwight/report/_council_sent_info.html b/templates/web/isleofwight/report/_council_sent_info.html index d9edac902..a2933d81e 100644 --- a/templates/web/isleofwight/report/_council_sent_info.html +++ b/templates/web/isleofwight/report/_council_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || problem.can_display_external_id %]

    [%- IF problem.can_display_external_id %] diff --git a/templates/web/northamptonshire/report/_council_sent_info.html b/templates/web/northamptonshire/report/_council_sent_info.html index 6329c51ad..5a53f2034 100644 --- a/templates/web/northamptonshire/report/_council_sent_info.html +++ b/templates/web/northamptonshire/report/_council_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || problem.whensent %]

    [%- IF problem.whensent AND NOT c.cobrand.is_defect(problem) %] diff --git a/templates/web/oxfordshire/report/_main_sent_info.html b/templates/web/oxfordshire/report/_main_sent_info.html index 9865153d3..7066ebe5d 100644 --- a/templates/web/oxfordshire/report/_main_sent_info.html +++ b/templates/web/oxfordshire/report/_main_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause %]

    [% duration_clause %]

    [% END %] diff --git a/templates/web/peterborough/report/_council_sent_info.html b/templates/web/peterborough/report/_council_sent_info.html index 1963020bc..c7e1eaf1d 100644 --- a/templates/web/peterborough/report/_council_sent_info.html +++ b/templates/web/peterborough/report/_council_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || problem.whensent %]

    [%- IF problem.whensent %] diff --git a/templates/web/tfl/report/_council_sent_info.html b/templates/web/tfl/report/_council_sent_info.html index ef6cca079..472c6ce10 100644 --- a/templates/web/tfl/report/_council_sent_info.html +++ b/templates/web/tfl/report/_council_sent_info.html @@ -1,4 +1,4 @@ -[% SET duration_clause = problem.duration_string(c) %] +[% SET duration_clause = problem.duration_string %] [% IF duration_clause || problem.whensent %]

    [%- IF problem.whensent %] diff --git a/templates/web/zurich/admin/problem_row.html b/templates/web/zurich/admin/problem_row.html index 08857f165..a2805d6dc 100644 --- a/templates/web/zurich/admin/problem_row.html +++ b/templates/web/zurich/admin/problem_row.html @@ -31,7 +31,7 @@ [%- IF p_body.parent.parent.id -%][%# ...it's a subdivision %] [% PROCESS value_or_nbsp value=p_body.name %] [%- ELSIF problem.external_body %] - [% PROCESS value_or_nbsp value=problem.body(c).name %] + [% PROCESS value_or_nbsp value=problem.body.name %] [%- ELSE -%]   [%- END -%] diff --git a/templates/web/zurich/admin/reports/edit.html b/templates/web/zurich/admin/reports/edit.html index c88c63307..4a35192f0 100644 --- a/templates/web/zurich/admin/reports/edit.html +++ b/templates/web/zurich/admin/reports/edit.html @@ -272,7 +272,7 @@ [% ELSE %] [% loc('Message to competent body:') %] [% END %] - [% problem.body(c).endpoint %] + [% problem.body.endpoint %]

    [% problem.extra.external_message | html_para %] diff --git a/templates/web/zurich/report/banner.html b/templates/web/zurich/report/banner.html index 8814dc986..b8ba7fa43 100644 --- a/templates/web/zurich/report/banner.html +++ b/templates/web/zurich/report/banner.html @@ -1,5 +1,5 @@ [% USE date %] -[% problem_hashref = c.cobrand.problem_as_hashref(problem, c) %] +[% problem_hashref = c.cobrand.problem_as_hashref(problem) %] -- cgit v1.2.3 From 530ced2b6ba9ca394567572e66a50917ae49586c Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Thu, 30 Jul 2020 19:13:28 +0100 Subject: =?UTF-8?q?[Docs]=20Remove=20jekyll-livereload,=20it=E2=80=99s=20n?= =?UTF-8?q?ow=20included=20in=20Jekyll=20itself?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Gemfile | 4 ---- docs/Gemfile.lock | 6 +----- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/Gemfile b/docs/Gemfile index 62a36141d..53aaf9102 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -2,7 +2,3 @@ source 'https://rubygems.org' gem 'github-pages' -# https://github.com/RobertDeRose/jekyll-livereload/ -group :jekyll_plugins do - gem 'jekyll-livereload' -end diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index c6eb7aa83..c99d41c26 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -120,9 +120,6 @@ GEM jekyll-github-metadata (2.13.0) jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-livereload (0.2.2) - em-websocket (~> 0.5) - jekyll (~> 3.0) jekyll-mentions (1.5.1) html-pipeline (~> 2.3) jekyll (>= 3.7, < 5.0) @@ -246,7 +243,6 @@ PLATFORMS DEPENDENCIES github-pages - jekyll-livereload BUNDLED WITH - 1.16.1 + 1.17.2 -- cgit v1.2.3 From 5a0dd3f573f7aba771fe916a65138dfbbb258982 Mon Sep 17 00:00:00 2001 From: Dave Arter Date: Thu, 30 Jul 2020 19:14:42 +0100 Subject: [Docs] Add instructions & example for including HTML in notice fields For https://github.com/mysociety/fixmystreet-commercial/issues/1905 --- docs/_includes/admin-tasks-content.md | 44 +++++++++++++++++++++ .../img/pro-user-guide/blocked-drains-guidance.png | Bin 0 -> 378727 bytes 2 files changed, 44 insertions(+) create mode 100644 docs/assets/img/pro-user-guide/blocked-drains-guidance.png diff --git a/docs/_includes/admin-tasks-content.md b/docs/_includes/admin-tasks-content.md index cd6043c25..13ee4aa51 100644 --- a/docs/_includes/admin-tasks-content.md +++ b/docs/_includes/admin-tasks-content.md @@ -656,6 +656,50 @@ selected. This can be used to provide extra information about the category, point specific cases elsewhere (e.g. a phone number for urgent enquiries), or anything else you think the reporter may find useful. +##### HTML content in notices + +Sometimes it can be helpful to use different text formatting or include images +in the notices shown to users. You could, for example, include photos that +illustrate the sorts of issues users should report in a particular category. + +Screenshot illustrating how the notice can include guidance for the user as well as a comparison photo. + +The **Notice text** field allows you to include HTML tags to apply text +formatting, add links, or include images in the notice shown to the user. The +above notice was generated from this HTML: + +```html +We take different action depending on how a drain is blocked.
    + +A blockage on the surface of a drain could be caused by leaves or litter. These blockages can usually be cleared by carrying out street sweeping.
    + +Compared with a surface blockage, an internal blockage could be caused by leaves, mud, or stones and need specialist equipment. + +``` + +The following HTML tags are allowed for use: + + - `p` + - `ul` + - `ol` + - `li` + - `br` + - `b` + - `i` + - `strong` + - `em` + - `a` + - The following attributes are allowed: `href`, `style`, `target="_blank"`, `title` + - `img` + - The following attributes are allowed: `src`, `alt`, `width`, `height`, `hspace`, `vspace`, `align`, `sizes`, `srcset` + - **NB** The `alt` attribute is required if you're giving information only present in the image. + - `span` + - The following attributes are allowed: `style` + +Any other tags or attributes not allowed by the above list will be stripped out +when you save your changes on the form. + #### Questions A question can ask the user to provide some specific additional information diff --git a/docs/assets/img/pro-user-guide/blocked-drains-guidance.png b/docs/assets/img/pro-user-guide/blocked-drains-guidance.png new file mode 100644 index 000000000..95cb052a1 Binary files /dev/null and b/docs/assets/img/pro-user-guide/blocked-drains-guidance.png differ -- cgit v1.2.3 From 00fe7b548cee48c9683e4fa99ed148cdf911cc84 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Thu, 16 Jan 2020 18:30:10 +0000 Subject: [Oxfordshire] add asset layers Trees, street lights, traffic lights, grit bins and gullies --- templates/web/fixmystreet.com/footer_extra_js.html | 1 + templates/web/oxfordshire/footer_extra_js.html | 2 +- web/cobrands/fixmystreet/assets.js | 20 +-- web/cobrands/oxfordshire/assets.js | 138 +++++++++++++++++++++ web/cobrands/oxfordshire/base.scss | 4 + 5 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 web/cobrands/oxfordshire/assets.js diff --git a/templates/web/fixmystreet.com/footer_extra_js.html b/templates/web/fixmystreet.com/footer_extra_js.html index 289e230e7..2a4f9ed9c 100644 --- a/templates/web/fixmystreet.com/footer_extra_js.html +++ b/templates/web/fixmystreet.com/footer_extra_js.html @@ -16,6 +16,7 @@ IF bodyclass.match('mappage'); scripts.push( version('/cobrands/lincolnshire/assets.js') ); scripts.push( version('/cobrands/northamptonshire/assets.js') ); scripts.push( version('/cobrands/hounslow/assets.js') ); + scripts.push( version('/cobrands/oxfordshire/assets.js') ); scripts.push( version('/cobrands/westminster/assets.js') ); scripts.push( version('/cobrands/peterborough/assets.js') ); scripts.push( version('/cobrands/tfl/assets.js') ); diff --git a/templates/web/oxfordshire/footer_extra_js.html b/templates/web/oxfordshire/footer_extra_js.html index 12a42c0d2..bb65cec8b 100644 --- a/templates/web/oxfordshire/footer_extra_js.html +++ b/templates/web/oxfordshire/footer_extra_js.html @@ -1 +1 @@ -[% PROCESS 'footer_extra_js_base.html' highways=1 validation=1 roadworks=1 %] +[% PROCESS 'footer_extra_js_base.html' highways=1 validation=1 roadworks=1 cobrand_js=1 %] diff --git a/web/cobrands/fixmystreet/assets.js b/web/cobrands/fixmystreet/assets.js index 56055ec52..c42d900fc 100644 --- a/web/cobrands/fixmystreet/assets.js +++ b/web/cobrands/fixmystreet/assets.js @@ -549,15 +549,7 @@ function get_asset_stylemap() { return new OpenLayers.StyleMap({ 'default': fixmystreet.assets.style_default, 'select': fixmystreet.assets.style_default_select, - 'hover': new OpenLayers.Style({ - fillColor: "#55BB00", - fillOpacity: 0.8, - strokeColor: "#000000", - strokeOpacity: 1, - strokeWidth: 2, - pointRadius: 8, - cursor: 'pointer' - }) + 'hover': fixmystreet.assets.style_default_hover }); } @@ -824,6 +816,16 @@ fixmystreet.assets = { pointRadius: 6 }), + style_default_hover: new OpenLayers.Style({ + fillColor: "#55BB00", + fillOpacity: 0.8, + strokeColor: "#000000", + strokeOpacity: 1, + strokeWidth: 2, + pointRadius: 8, + cursor: 'pointer' + }), + style_default_select: new OpenLayers.Style({ externalGraphic: pin_prefix + "pin-spot.png", fillColor: "#55BB00", diff --git a/web/cobrands/oxfordshire/assets.js b/web/cobrands/oxfordshire/assets.js new file mode 100644 index 000000000..9bd38acac --- /dev/null +++ b/web/cobrands/oxfordshire/assets.js @@ -0,0 +1,138 @@ +(function(){ + +if (!fixmystreet.maps) { + return; +} + +var wfs_host = fixmystreet.staging ? 'tilma.staging.mysociety.org' : 'tilma.mysociety.org'; +var tilma_url = "https://" + wfs_host + "/mapserver/oxfordshire"; +var proxy_base_url = "https://" + wfs_host + "/proxy/occ/"; + +var defaults = { + wfs_url: tilma_url, + asset_type: 'spot', + max_resolution: 4.777314267158508, + geometryName: 'msGeometry', + srsName: "EPSG:3857", + body: "Oxfordshire County Council", + strategy_class: OpenLayers.Strategy.FixMyStreet +}; + +var occ_default = $.extend({}, fixmystreet.assets.style_default.defaultStyle, { + fillColor: "#007258" +}); + +var occ_hover = new OpenLayers.Style({ + pointRadius: 8, + cursor: 'pointer' +}); + +var occ_stylemap = new OpenLayers.StyleMap({ + 'default': occ_default, + 'select': fixmystreet.assets.style_default_select, + 'hover': occ_hover +}); + +fixmystreet.assets.add(defaults, { + stylemap: occ_stylemap, + wfs_feature: "Trees", + asset_id_field: 'Ref', + attributes: { + feature_id: 'Ref' + }, + asset_category: ["Trees"], + asset_item: 'tree' +}); + +fixmystreet.assets.add(defaults, { + stylemap: occ_stylemap, + wfs_feature: "Traffic_Lights", + asset_id_field: 'Site', + attributes: { + feature_id: 'Site' + }, + asset_category: ["Traffic Lights (permanent only)"], + asset_item: 'traffic light' +}); + +var streetlight_select = $.extend({ + label: "${UNITNO}", + fontColor: "#FFD800", + labelOutlineColor: "black", + labelOutlineWidth: 3, + labelYOffset: 69, + fontSize: '18px', + fontWeight: 'bold' +}, fixmystreet.assets.style_default_select.defaultStyle); + +var streetlight_stylemap = new OpenLayers.StyleMap({ + 'default': occ_default, + 'select': new OpenLayers.Style(streetlight_select), + 'hover': occ_hover +}); + +fixmystreet.assets.add(defaults, { + select_action: true, + stylemap: streetlight_stylemap, + wfs_feature: "Street_Lights", + asset_id_field: 'UNITID', + attributes: { + feature_id: 'UNITID', + column_no: 'UNITNO' + }, + asset_category: ["Street lighting"], + asset_item: 'street light', + feature_code: 'UNITNO', + actions: { + asset_found: fixmystreet.assets.named_select_action_found, + asset_not_found: fixmystreet.assets.named_select_action_not_found + } +}); + +var owned_defaults = $.extend({}, defaults, { + stylemap: occ_stylemap, + // have to do this by hand rather than using wfs_* options + // as the server does not like being POSTed xml with application/xml + // as the Content-Type which is what using those options results in. + http_options: { + headers: { + 'Content-Type': 'text/plain' + }, + params: { + SERVICE: "WFS", + VERSION: "1.1.0", + REQUEST: "GetFeature", + SRSNAME: "urn:ogc:def:crs:EPSG::27700", + propertyName: 'id,maintained_by,msGeometry' + } + }, + srsName: "EPSG:27700", + asset_id_field: 'id', + attributes: { + feature_id: 'id' + } +}); + +fixmystreet.assets.add(owned_defaults, { + http_options: { + url: proxy_base_url + 'drains/wfs', + params: { + TYPENAME: "junctions" + } + }, + asset_category: ["Gully and Catchpits"], + asset_item: 'drain' +}); + +fixmystreet.assets.add(owned_defaults, { + http_options: { + url: proxy_base_url + 'grit/wfs', + params: { + TYPENAME: "Grit_bins" + } + }, + asset_category: ["Ice/Snow"], + asset_item: 'grit bin' +}); + +})(); diff --git a/web/cobrands/oxfordshire/base.scss b/web/cobrands/oxfordshire/base.scss index 9fd4bc2ad..a5f7a43d6 100644 --- a/web/cobrands/oxfordshire/base.scss +++ b/web/cobrands/oxfordshire/base.scss @@ -279,6 +279,10 @@ textarea { color: $color-oxfordshire-link-blue; } +.asset-spot:before { + background-color: #007258; +} + @media print { body { background-color: #fff !important; -- cgit v1.2.3 From 2b6d5e5dac59b6a8a9af89cc4a51a9c3dff7fee8 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 22 Jan 2020 17:54:44 +0000 Subject: [Oxfordshire] include asset id in council problem emails --- templates/email/oxfordshire/submit.html | 77 +++++++++++++++++++++++++++++++++ templates/email/oxfordshire/submit.txt | 52 ++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 templates/email/oxfordshire/submit.html create mode 100644 templates/email/oxfordshire/submit.txt diff --git a/templates/email/oxfordshire/submit.html b/templates/email/oxfordshire/submit.html new file mode 100644 index 000000000..d26f3d44e --- /dev/null +++ b/templates/email/oxfordshire/submit.html @@ -0,0 +1,77 @@ +[% + +PROCESS '_email_settings.html'; + +email_summary = "A new problem in your area has been reported by a " _ site_name _ " user."; +email_footer = PROCESS '_submit_footer.html'; +email_columns = 2; + +INCLUDE '_email_top.html'; + +%] + + + [% start_padded_box | safe %] +

    New problem in your area

    +

    [% missing %][% multiple %]A user of [% site_name %] has submitted the following report +of a local problem that they believe might require your attention.

    + +

    + Show full report +

    +

    Reported by:

    + + + + + + + + + + [%~ IF report.user.phone %] + + + + + [%~ END %] +
    Name[% report.name | html %]
    Email + [%~ IF report.user.email ~%] + [% report.user.email | html %] + [%~ ELSE ~%] + No email address provided, only phone number + [%~ END ~%] +
    Phone[% report.user.phone | html %]
    +

    Replies to this message will go directly to [% report.name | html %], the user who reported the problem.

    + [% end_padded_box | safe %] + +[% WRAPPER '_email_sidebar.html' object = report %] +

    [% report.title | html %]

    +

    Category: [% report.category | html %]

    +

    [% report.detail | html %]

    + [%~ IF additional_information %] +

    [% additional_information %]

    + [%~ END %] +

    + Location: +
    Easting/Northing + [%~ " (IE)" IF coordsyst == "I" ~%] + : [% easting %]/[% northing %] + ( + [%~ report.latitude %], [% report.longitude ~%] + ) + [% IF closest_address %]
    [% closest_address | trim | replace("\n\n", "
    ") %][% END %] +

    + [% IF report.get_extra_field_value('feature_id') %] +

    + Asset id: [% report.get_extra_field_value('feature_id') %] +

    + [% END %] + [% IF report.get_extra_field_value('column_no') %] +

    + Column Number: [% report.get_extra_field_value('column_no') %] +

    + [% END %] +[% END %] + +[% INCLUDE '_email_bottom.html' %] diff --git a/templates/email/oxfordshire/submit.txt b/templates/email/oxfordshire/submit.txt new file mode 100644 index 000000000..90de2e60c --- /dev/null +++ b/templates/email/oxfordshire/submit.txt @@ -0,0 +1,52 @@ +Subject: Problem Report: [% report.title %] + +Dear [% bodies_name %], + +[% missing %][% multiple %]A user of +[% site_name %] has submitted the following report +of a local problem that they believe might require your attention. + +[% fuzzy %], or to provide an update on the problem, +please visit the following link: + + [% url %] + +[% has_photo %]---------- + +Name: [% report.name %] + +Email: [% report.user.email OR 'None provided' %] + +Phone: [% report.user.phone OR 'None provided' %] + +Category: [% report.category %] + +Subject: [% report.title %] + +Details: [% report.detail %] + +[% additional_information %] + +[%- IF report.get_extra_field_value('feature_id') %] +Asset id: [% report.get_extra_field_value('feature_id') %] +[%- END %] + +[%- IF report.get_extra_field_value('column_no') %] +Column number: [% report.get_extra_field_value('column_no') %] +[%- END %] + +Easting/Northing +[%- " (IE)" IF coordsyst == "I" -%] +: [% easting %]/[% northing %] + +Latitude: [% report.latitude %] + +Longitude: [% report.longitude %] + +View OpenStreetMap of this location: [% osm_url %] + +[% closest_address %]---------- + +Replies to this email will go to the user who submitted the problem. + +[% signature %] -- cgit v1.2.3 From a86acd036ad52a15ee3cf546f7f496f212be3cf5 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Thu, 23 Jan 2020 16:01:57 +0000 Subject: [Oxfordshire] include asset id in open311 description --- perllib/FixMyStreet/Cobrand/Bromley.pm | 5 +++ perllib/FixMyStreet/Cobrand/Oxfordshire.pm | 17 ++++++++++ perllib/Open311/PostServiceRequestUpdates.pm | 2 +- t/cobrand/oxfordshire.t | 47 ++++++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/perllib/FixMyStreet/Cobrand/Bromley.pm b/perllib/FixMyStreet/Cobrand/Bromley.pm index e63d722fd..90e5895d5 100644 --- a/perllib/FixMyStreet/Cobrand/Bromley.pm +++ b/perllib/FixMyStreet/Cobrand/Bromley.pm @@ -232,6 +232,11 @@ sub open311_pre_send { } } +sub open311_pre_send_updates { + my ($self, $row) = @_; + return $self->open311_pre_send($row); +} + sub open311_munge_update_params { my ($self, $params, $comment, $body) = @_; delete $params->{update_id}; diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm index 31db0fd4e..cf88495f8 100644 --- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -138,6 +138,23 @@ sub open311_config_updates { $params->{use_customer_reference} = 1; } +sub open311_pre_send { + my ($self, $row, $open311) = @_; + + $self->{ox_original_detail} = $row->detail; + + if (my $fid = $row->get_extra_field_value('feature_id')) { + my $text = $row->detail . "\n\nAsset Id: $fid\n"; + $row->detail($text); + } +} + +sub open311_post_send { + my ($self, $row, $h, $contact) = @_; + + $row->detail($self->{ox_original_detail}); +} + sub should_skip_sending_update { my ($self, $update ) = @_; diff --git a/perllib/Open311/PostServiceRequestUpdates.pm b/perllib/Open311/PostServiceRequestUpdates.pm index a31bca8f7..b9aa9ed50 100755 --- a/perllib/Open311/PostServiceRequestUpdates.pm +++ b/perllib/Open311/PostServiceRequestUpdates.pm @@ -126,7 +126,7 @@ sub process_update { my $o = $self->construct_open311($body, $comment); - $cobrand->call_hook(open311_pre_send => $comment, $o); + $cobrand->call_hook(open311_pre_send_updates => $comment); my $id = $o->post_service_request_update( $comment ); diff --git a/t/cobrand/oxfordshire.t b/t/cobrand/oxfordshire.t index 65c6a3864..16b7e5714 100644 --- a/t/cobrand/oxfordshire.t +++ b/t/cobrand/oxfordshire.t @@ -1,7 +1,9 @@ use Test::MockModule; +use CGI::Simple; use FixMyStreet::TestMech; use FixMyStreet::Script::Alerts; +use FixMyStreet::Script::Reports; my $mech = FixMyStreet::TestMech->new; my $oxon = $mech->create_body_ok(2237, 'Oxfordshire County Council'); @@ -50,6 +52,7 @@ subtest 'check /around?ajax defaults to open reports only' => sub { my @problems = FixMyStreet::DB->resultset('Problem')->search({}, { rows => 3, order_by => 'id' })->all; FixMyStreet::override_config { + STAGING_FLAGS => { send_reports => 1, skip_checks => 1 }, ALLOWED_COBRANDS => 'oxfordshire', MAPIT_URL => 'http://mapit.uk/', }, sub { @@ -63,10 +66,10 @@ FixMyStreet::override_config { is $mech->uri->path, '/report/' . $problem->id, 'redirects to report'; }; - subtest 'check unable to fix label' => sub { - my $user = $mech->create_user_ok( 'user@example.com', name => 'Test User' ); - my $user2 = $mech->create_user_ok( 'user2@example.com', name => 'Test User2' ); + my $user = $mech->create_user_ok( 'user@example.com', name => 'Test User' ); + my $user2 = $mech->create_user_ok( 'user2@example.com', name => 'Test User2' ); + subtest 'check unable to fix label' => sub { my $problem = $problems[0]; $problem->state( 'unable to fix' ); $problem->update; @@ -128,6 +131,44 @@ FixMyStreet::override_config { is $rows[2]->[20], '', 'Report without HIAMS ref has empty ref field'; is $rows[3]->[20], '123098123', 'Older Exor report has correct ref'; }; + + $oxon->update({ + send_method => 'Open311', + endpoint => 'endpoint', + api_key => 'key', + jurisdiction => 'home', + }); + my $contact = $mech->create_contact_ok( body_id => $oxon->id, category => 'Gullies and Catchpits', email => 'GC' ); + $contact->set_extra_fields( ( + { code => 'feature_id', datatype => 'hidden', variable => 'true' }, + ) ); + $contact->update; + FixMyStreet::Script::Reports::send(); # Make sure no waiting reports + + subtest 'Check special Open311 request handling', sub { + my ($p) = $mech->create_problems_for_body( 1, $oxon->id, 'Test', { + cobrand => 'oxfordshire', + category => 'Gullies and Catchpits', + user => $user, + latitude => 51.754926, + longitude => -1.256179, + }); + $p->set_extra_fields({ name => 'feature_id', value => 9875432}); + $p->update; + + my $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'; + unlike $p->detail, qr/Asset Id:/, 'asset id not saved to report detail'; + + my $req = $test_data->{test_req_used}; + my $c = CGI::Simple->new($req->content); + like $c->param('description'), qr/Asset Id: 9875432/, 'asset id included in body'; + }; + }; done_testing(); -- cgit v1.2.3 From cb257d9b2f4dcb9c9fa54836e6e08378194ff393 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Wed, 5 Feb 2020 15:04:07 +0000 Subject: [Oxfordshire] Add NSG roads layer. Prevent reports on areas not on a road for relevant categories, and do not allow reporting on private roads. --- perllib/FixMyStreet/Cobrand/Oxfordshire.pm | 5 ++ t/cobrand/oxfordshire.t | 60 +++++++++++++--------- .../web/oxfordshire/report/new/roads_message.html | 7 +++ web/cobrands/oxfordshire/assets.js | 50 ++++++++++++++++++ 4 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 templates/web/oxfordshire/report/new/roads_message.html diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm index cf88495f8..6021827cb 100644 --- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -147,6 +147,11 @@ sub open311_pre_send { my $text = $row->detail . "\n\nAsset Id: $fid\n"; $row->detail($text); } + + if (my $usrn = $row->get_extra_field_value('usrn')) { + my $text = $row->detail . "\n\nUSRN: $usrn\n"; + $row->detail($text); + } } sub open311_post_send { diff --git a/t/cobrand/oxfordshire.t b/t/cobrand/oxfordshire.t index 16b7e5714..5207ba916 100644 --- a/t/cobrand/oxfordshire.t +++ b/t/cobrand/oxfordshire.t @@ -141,33 +141,47 @@ FixMyStreet::override_config { my $contact = $mech->create_contact_ok( body_id => $oxon->id, category => 'Gullies and Catchpits', email => 'GC' ); $contact->set_extra_fields( ( { code => 'feature_id', datatype => 'hidden', variable => 'true' }, + { code => 'usrn', datatype => 'hidden', variable => 'true' }, ) ); $contact->update; FixMyStreet::Script::Reports::send(); # Make sure no waiting reports - subtest 'Check special Open311 request handling', sub { - my ($p) = $mech->create_problems_for_body( 1, $oxon->id, 'Test', { - cobrand => 'oxfordshire', - category => 'Gullies and Catchpits', - user => $user, - latitude => 51.754926, - longitude => -1.256179, - }); - $p->set_extra_fields({ name => 'feature_id', value => 9875432}); - $p->update; - - my $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'; - unlike $p->detail, qr/Asset Id:/, 'asset id not saved to report detail'; - - my $req = $test_data->{test_req_used}; - my $c = CGI::Simple->new($req->content); - like $c->param('description'), qr/Asset Id: 9875432/, 'asset id included in body'; - }; + for my $test ( + { + field => 'usrn', + value => '12345', + text => 'USRN', + }, + { + field => 'feature_id', + value => '12345', + text => 'Asset Id', + }, + ) { + subtest 'Check special Open311 request handling of ' . $test->{text}, sub { + my ($p) = $mech->create_problems_for_body( 1, $oxon->id, 'Test', { + cobrand => 'oxfordshire', + category => 'Gullies and Catchpits', + user => $user, + latitude => 51.754926, + longitude => -1.256179, + }); + $p->set_extra_fields({ name => $test->{field}, value => $test->{value}}); + $p->update; + + my $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'; + unlike $p->detail, qr/$test->{text}:/, $test->{text} . ' not saved to report detail'; + + my $req = $test_data->{test_req_used}; + my $c = CGI::Simple->new($req->content); + like $c->param('description'), qr/$test->{text}: $test->{value}/, $test->{text} . ' included in body'; + }; + } }; diff --git a/templates/web/oxfordshire/report/new/roads_message.html b/templates/web/oxfordshire/report/new/roads_message.html new file mode 100644 index 000000000..1c1dd66b0 --- /dev/null +++ b/templates/web/oxfordshire/report/new/roads_message.html @@ -0,0 +1,7 @@ + diff --git a/web/cobrands/oxfordshire/assets.js b/web/cobrands/oxfordshire/assets.js index 9bd38acac..1f7a7808d 100644 --- a/web/cobrands/oxfordshire/assets.js +++ b/web/cobrands/oxfordshire/assets.js @@ -135,4 +135,54 @@ fixmystreet.assets.add(owned_defaults, { asset_item: 'grit bin' }); +var road_occ_maintainable = 'Maintainable at Public Expense'; + +function road_owned(f) { + return f && + f.attributes && + f.attributes.STREET_MAINTENANCE_RESPONSIBILITY_NAME && + f.attributes.STREET_MAINTENANCE_RESPONSIBILITY_NAME.lastIndexOf(road_occ_maintainable, 0) === 0; +} + +fixmystreet.assets.add(defaults, { + stylemap: fixmystreet.assets.stylemap_invisible, + wfs_url: proxy_base_url + 'nsg/', + wfs_feature: "WFS_STREETS_CLASSIFIED_ROADS_INTERESTS", + propertyNames: ['TYPE1_2_USRN', 'STREET_MAINTENANCE_RESPONSIBILITY_NAME', 'SHAPE_GEOMETRY'], + srsName: "EPSG:27700", + geometryName: null, + usrn: { + attribute: 'TYPE1_2_USRN', + field: 'usrn' + }, + non_interactive: true, + road: true, + no_asset_msg_id: '#js-not-a-road', + asset_item: 'road', + asset_type: 'road', + actions: { + found: function(layer, feature) { + fixmystreet.message_controller.road_found(layer, feature, road_owned, '#js-not-a-road'); + }, + not_found: fixmystreet.message_controller.road_not_found + }, + asset_category: [ + "Bridges", + "Carriageway Defect", + "Current Roadworks", + "Drainage", + "Gully and Catchpits", + "Highway Schemes", + "Ice/Snow", + "Manhole", + "Pavements", + "Pothole", + "Road Traffic Signs and Road Markings", + "Roads/highways", + "Street lighting", + "Traffic Lights (permanent only)", + "Trees" + ] +}); + })(); -- cgit v1.2.3 From 07c053c4b5d49c20f24024154b5e154bb0239347 Mon Sep 17 00:00:00 2001 From: Struan Donald Date: Mon, 6 Apr 2020 17:25:28 +0100 Subject: [Oxfordshire] prevent reports on non OCC assets also improve contrast between assets and labels --- .../web/oxfordshire/report/new/roads_message.html | 6 ++ web/cobrands/oxfordshire/assets.js | 67 +++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/templates/web/oxfordshire/report/new/roads_message.html b/templates/web/oxfordshire/report/new/roads_message.html index 1c1dd66b0..4572b07bd 100644 --- a/templates/web/oxfordshire/report/new/roads_message.html +++ b/templates/web/oxfordshire/report/new/roads_message.html @@ -4,4 +4,10 @@ County Council and therefore we are unable to accept reports in this area / street.

    +
    diff --git a/web/cobrands/oxfordshire/assets.js b/web/cobrands/oxfordshire/assets.js index 1f7a7808d..caa4d5e2c 100644 --- a/web/cobrands/oxfordshire/assets.js +++ b/web/cobrands/oxfordshire/assets.js @@ -33,6 +33,58 @@ var occ_stylemap = new OpenLayers.StyleMap({ 'hover': occ_hover }); +var occ_ownernames = [ + "LocalAuthority", "CountyCouncil", 'ODS' +]; + +function occ_owns_feature(f) { + return f && + f.attributes && + f.attributes.maintained_by && + OpenLayers.Util.indexOf(occ_ownernames, f.attributes.maintained_by) > -1; +} + +function occ_does_not_own_feature(f) { + return !occ_owns_feature(f); +} + +var owned_default_style = new OpenLayers.Style({ + fillColor: "#868686", + fillOpacity: 0.6, + strokeColor: "#000000", + strokeOpacity: 0.6, + strokeWidth: 2, + pointRadius: 4, + title: 'Not maintained by Oxfordshire County Council. Maintained by ${maintained_by}.' +}); + +var rule_owned = new OpenLayers.Rule({ + filter: new OpenLayers.Filter.FeatureId({ + type: OpenLayers.Filter.Function, + evaluate: occ_owns_feature + }), + symbolizer: { + fillColor: "#007258", + pointRadius: 6, + title: '' + } +}); + +var rule_not_owned = new OpenLayers.Rule({ + filter: new OpenLayers.Filter.FeatureId({ + type: OpenLayers.Filter.Function, + evaluate: occ_does_not_own_feature + }) +}); + +owned_default_style.addRules([rule_owned, rule_not_owned]); + +var owned_stylemap = new OpenLayers.StyleMap({ + 'default': owned_default_style, + 'select': fixmystreet.assets.style_default_select, + 'hover': occ_hover +}); + fixmystreet.assets.add(defaults, { stylemap: occ_stylemap, wfs_feature: "Trees", @@ -90,7 +142,8 @@ fixmystreet.assets.add(defaults, { }); var owned_defaults = $.extend({}, defaults, { - stylemap: occ_stylemap, + stylemap: owned_stylemap, + select_action: true, // have to do this by hand rather than using wfs_* options // as the server does not like being POSTed xml with application/xml // as the Content-Type which is what using those options results in. @@ -110,6 +163,18 @@ var owned_defaults = $.extend({}, defaults, { asset_id_field: 'id', attributes: { feature_id: 'id' + }, + actions: { + asset_found: function(asset) { + var is_occ = occ_owns_feature(asset); + if (!is_occ) { + fixmystreet.message_controller.asset_not_found.call(this); + } else { + fixmystreet.message_controller.asset_found.call(this); + } + }, + // Not a typo, asset selection is not mandatory + asset_not_found: fixmystreet.message_controller.asset_found } }); -- cgit v1.2.3 From 045f362b3a37bd1c5f446674be4033852a7b4b22 Mon Sep 17 00:00:00 2001 From: M Somerville Date: Mon, 10 Aug 2020 15:37:49 +0100 Subject: [Oxfordshire] Switch to our own server for NSG. --- web/cobrands/oxfordshire/assets.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/web/cobrands/oxfordshire/assets.js b/web/cobrands/oxfordshire/assets.js index caa4d5e2c..fc9f87064 100644 --- a/web/cobrands/oxfordshire/assets.js +++ b/web/cobrands/oxfordshire/assets.js @@ -211,11 +211,9 @@ function road_owned(f) { fixmystreet.assets.add(defaults, { stylemap: fixmystreet.assets.stylemap_invisible, - wfs_url: proxy_base_url + 'nsg/', - wfs_feature: "WFS_STREETS_CLASSIFIED_ROADS_INTERESTS", - propertyNames: ['TYPE1_2_USRN', 'STREET_MAINTENANCE_RESPONSIBILITY_NAME', 'SHAPE_GEOMETRY'], + wfs_feature: "OCCRoads", + propertyNames: ['TYPE1_2_USRN', 'STREET_MAINTENANCE_RESPONSIBILITY_NAME', 'msGeometry'], srsName: "EPSG:27700", - geometryName: null, usrn: { attribute: 'TYPE1_2_USRN', field: 'usrn' -- cgit v1.2.3 From ca0868c438832ea1611c0a00599f9240306ebd8b Mon Sep 17 00:00:00 2001 From: Zarino Zappia Date: Fri, 7 Aug 2020 11:04:37 +0100 Subject: [fixmystreet.org] Update "pro" layout to latest SocietyWorks content The Pro manual at `/pro-manual` uses a separate jekyll layout that mimics the output of the WordPress site at societyworks.org. Our copy had fallen out of date. This commit: * Updates the Google Fonts requested (was Rubik, now Lato and Lora). * Updates the header nav menu content to match the live site. * Replaces fixmystreet.com/pro URLs with societyworks.org URLs. * Updates the footer legal text to match the live site. --- docs/_layouts/pro.html | 159 +++++++++++++++++++++++++++---------------------- 1 file changed, 89 insertions(+), 70 deletions(-) diff --git a/docs/_layouts/pro.html b/docs/_layouts/pro.html index f1e764c8b..e01d61688 100644 --- a/docs/_layouts/pro.html +++ b/docs/_layouts/pro.html @@ -2,21 +2,22 @@ - {{ page.title }} | FixMyStreet Platform | mySociety + + {{ page.title }} – SocietyWorks + + - + - - - - + + @@ -36,36 +37,50 @@