diff options
Diffstat (limited to 'perllib/FixMyStreet')
32 files changed, 376 insertions, 353 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index b8f038ce3..f8ea84d08 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -195,6 +195,7 @@ sub display_location : Private { colour => $colour, id => $p->id, title => $p->title_safe, + problem => $p, } } @$on_map_all, @$around_map; } diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index b564a988c..ca4a2fc80 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -85,6 +85,9 @@ sub sign_in : Private { $c->set_session_cookie_expire(0) unless $remember_me; + # Regenerate CSRF token as session ID changed + $c->forward('get_csrf_token'); + return 1; } diff --git a/perllib/FixMyStreet/App/Controller/JS.pm b/perllib/FixMyStreet/App/Controller/JS.pm index 483c3c2cc..1ced9d43b 100755 --- a/perllib/FixMyStreet/App/Controller/JS.pm +++ b/perllib/FixMyStreet/App/Controller/JS.pm @@ -24,8 +24,6 @@ sub translation_strings : LocalRegex('^translation_strings\.(.*?)\.js$') : Args( $c->res->content_type( 'application/javascript' ); } -sub validation_rules : Path('validation_rules.js') : Args(0) { } - __PACKAGE__->meta->make_immutable; 1; diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 9779a5e2a..e81dc719f 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -143,22 +143,11 @@ sub report_new_ajax : Path('mobile') : Args(0) { $c->forward('save_user_and_report'); my $report = $c->stash->{report}; - my $data = $c->stash->{token_data} || {}; - my $token = $c->model("DB::Token")->create( { - scope => 'problem', - data => { - %$data, - id => $report->id - } - } ); if ( $report->confirmed ) { $c->forward( 'create_reporter_alert' ); $c->stash->{ json_response } = { success => 1, report => $report->id }; } else { - $c->stash->{token_url} = $c->uri_for_email( '/P', $token->token ); - $c->send_email( 'problem-confirm.txt', { - to => [ $report->name ? [ $report->user->email, $report->name ] : $report->user->email ], - } ); + $c->forward( 'send_problem_confirm_email' ); $c->stash->{ json_response } = { success => 1 }; } @@ -602,12 +591,12 @@ sub setup_categories_and_bodies : Private { my %bodies = map { $_->id => $_ } @bodies; my $first_body = ( values %bodies )[0]; - my @contacts # + my $contacts # = $c # ->model('DB::Contact') # ->not_deleted # - ->search( { body_id => [ keys %bodies ] } ) - ->all; + ->search( { body_id => [ keys %bodies ] } ); + my @contacts = $c->cobrand->categories_restriction($contacts)->all; # variables to populate my %bodies_to_list = (); # Bodies with categories assigned @@ -670,15 +659,6 @@ sub setup_categories_and_bodies : Private { $c->cobrand->munge_category_list(\@category_options, \@contacts, \%category_extras) if $c->cobrand->can('munge_category_list'); - if ($c->cobrand->can('hidden_categories')) { - my %hidden_categories = map { $_ => 1 } - $c->cobrand->hidden_categories; - - @category_options = grep { - !$hidden_categories{$_} - } @category_options; - } - # put results onto stash for display $c->stash->{bodies} = \%bodies; $c->stash->{all_body_names} = [ map { $_->name } values %bodies ]; @@ -1030,6 +1010,31 @@ sub tokenize_user : Private { if $c->get_param('oauth_need_email') && $c->session->{oauth}{twitter_id}; } +sub send_problem_confirm_email : Private { + my ( $self, $c ) = @_; + my $data = $c->stash->{token_data} || {}; + my $report = $c->stash->{report}; + my $token = $c->model("DB::Token")->create( { + scope => 'problem', + data => { + %$data, + id => $report->id + } + } ); + + my $template = 'problem-confirm.txt'; + $template = 'problem-confirm-not-sending.txt' unless $report->bodies_str; + + $c->stash->{token_url} = $c->uri_for_email( '/P', $token->token ); + if ($c->cobrand->can('problem_confirm_email_extras')) { + $c->cobrand->problem_confirm_email_extras($report); + } + + $c->send_email( $template, { + to => [ $report->name ? [ $report->user->email, $report->name ] : $report->user->email ], + } ); +} + =head2 save_user_and_report Save the user and the report. @@ -1187,30 +1192,13 @@ sub redirect_or_confirm_creation : Private { return 1; } - my $template = 'problem-confirm.txt'; - $template = 'problem-confirm-not-sending.txt' unless $report->bodies_str; - - # otherwise create a confirm token and email it to them. - my $data = $c->stash->{token_data} || {}; - my $token = $c->model("DB::Token")->create( { - scope => 'problem', - data => { - %$data, - id => $report->id - } - } ); - $c->stash->{token_url} = $c->uri_for_email( '/P', $token->token ); - if ($c->cobrand->can('problem_confirm_email_extras')) { - $c->cobrand->problem_confirm_email_extras($report); - } - $c->send_email( $template, { - to => [ $report->name ? [ $report->user->email, $report->name ] : $report->user->email ], - } ); + # otherwise email a confirm token to them. + $c->forward( 'send_problem_confirm_email' ); # tell user that they've been sent an email $c->stash->{template} = 'email_sent.html'; $c->stash->{email_type} = 'problem'; - $c->log->info($report->user->id . ' created ' . $report->id . ', email sent, ' . ($data->{password} ? 'password set' : 'password not set')); + $c->log->info($report->user->id . ' created ' . $report->id . ', email sent, ' . ($c->stash->{token_data}->{password} ? 'password set' : 'password not set')); } sub create_reporter_alert : Private { diff --git a/perllib/FixMyStreet/App/Controller/Root.pm b/perllib/FixMyStreet/App/Controller/Root.pm index 16f4aa491..88f480137 100644 --- a/perllib/FixMyStreet/App/Controller/Root.pm +++ b/perllib/FixMyStreet/App/Controller/Root.pm @@ -82,6 +82,13 @@ Display a 404 (not found) or 410 (gone) page. Pass in an optional error message sub page_error_404_not_found : Private { my ( $self, $c, $error_msg ) = @_; + + # Try getting static content that might be given under an admin proxy. + # First the special generated JavaScript file + $c->go('/js/translation_strings', [ $1 ], []) if $c->req->path =~ m{^admin/js/translation_strings\.(.*?)\.js$}; + # Then a generic static file + $c->serve_static_file("web/$1") && return if $c->req->path =~ m{^admin/(.*)}; + $c->stash->{template} = 'errors/page_error_404_not_found.html'; $c->stash->{error_msg} = $error_msg; $c->response->status(404); diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm index 38f344250..da017c57f 100644 --- a/perllib/FixMyStreet/App/Controller/Tokens.pm +++ b/perllib/FixMyStreet/App/Controller/Tokens.pm @@ -32,7 +32,7 @@ sub confirm_problem : Path('/P') { $c->stash->{report} = { id => 123, title => 'Title of Report', - bodies_str => 'True', + bodies_str => '1', url => '/report/123', service => $c->get_param('service'), }; @@ -195,7 +195,7 @@ sub confirm_update : Path('/C') { $c->stash->{problem} = { id => 123, title => 'Title of Report', - bodies_str => 'True', + bodies_str => '1', url => '/report/123', }; return; diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm index a92021f0c..d43d3af7e 100644 --- a/perllib/FixMyStreet/App/View/Web.pm +++ b/perllib/FixMyStreet/App/View/Web.pm @@ -176,7 +176,8 @@ sub version { $version_hash{$file} = ( stat( $path ) )[9]; } $version_hash{$file} ||= ''; - return "$file?$version_hash{$file}"; + my $admin = $self->template->context->stash->{admin} ? FixMyStreet->config('ADMIN_BASE_URL') : ''; + return "$admin$file?$version_hash{$file}"; } sub decode { diff --git a/perllib/FixMyStreet/Cobrand/Angus.pm b/perllib/FixMyStreet/Cobrand/Angus.pm index 23d0d2c58..0361c2d11 100644 --- a/perllib/FixMyStreet/Cobrand/Angus.pm +++ b/perllib/FixMyStreet/Cobrand/Angus.pm @@ -10,7 +10,8 @@ sub council_name { return 'Angus Council'; } sub council_url { return 'angus'; } sub base_url { - return FixMyStreet->config('BASE_URL') if FixMyStreet->config('STAGING_SITE'); + my $self = shift; + return $self->next::method() if FixMyStreet->config('STAGING_SITE'); return 'https://fix.angus.gov.uk'; } diff --git a/perllib/FixMyStreet/Cobrand/Bristol.pm b/perllib/FixMyStreet/Cobrand/Bristol.pm index a6bab287a..faf2b5f0a 100644 --- a/perllib/FixMyStreet/Cobrand/Bristol.pm +++ b/perllib/FixMyStreet/Cobrand/Bristol.pm @@ -55,4 +55,14 @@ sub send_questionnaires { return 0; } +sub categories_restriction { + my ($self, $rs) = @_; + # Categories covering the Bristol area have a mixture of Open311 and Email + # send methods. Bristol only want Open311 categories to be visible on their + # cobrand, not the email categories from FMS.com. We've set up the + # Email categories with a devolved send_method, so can identify Open311 + # categories as those which have a blank send_method. + return $rs->search( { send_method => undef } ); +} + 1; diff --git a/perllib/FixMyStreet/Cobrand/Bromley.pm b/perllib/FixMyStreet/Cobrand/Bromley.pm index c9f9a98be..2d0cb86f1 100644 --- a/perllib/FixMyStreet/Cobrand/Bromley.pm +++ b/perllib/FixMyStreet/Cobrand/Bromley.pm @@ -10,7 +10,8 @@ sub council_name { return 'Bromley Council'; } sub council_url { return 'bromley'; } sub base_url { - return FixMyStreet->config('BASE_URL') if FixMyStreet->config('STAGING_SITE'); + my $self = shift; + return $self->next::method() if FixMyStreet->config('STAGING_SITE'); return 'https://fix.bromley.gov.uk'; } diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm index 76d73d96e..36313cf63 100644 --- a/perllib/FixMyStreet/Cobrand/Default.pm +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -74,6 +74,18 @@ sub problems { return $self->problems_restriction($self->{c}->model('DB::Problem')); } +=head1 problems_on_map + +Returns a ResultSet of Problems to be shown on the /around map, potentially +restricted to a subset if we're on a cobrand that only wants some of the data. + +=cut + +sub problems_on_map { + my $self = shift; + return $self->problems_on_map_restriction($self->{c}->model('DB::Problem')); +} + =head1 updates Returns a ResultSet of Comments, potentially restricted to a subset if we're on @@ -103,6 +115,30 @@ sub updates_restriction { return $rs; } +=head1 categories_restriction + +Used to restrict categories available when making new report in a cobrand in a +particular way. Do nothing by default. + +=cut + +sub categories_restriction { + my ($self, $rs) = @_; + return $rs; +} + + +=head1 problems_on_map_restriction + +Used to restricts reports shown on the /around map in a cobrand in a particular way. Do +nothing by default. + +=cut + +sub problems_on_map_restriction { + my ($self, $rs) = @_; + return $rs; +} sub site_key { return 0; } @@ -133,7 +169,10 @@ Base URL for the admin interface. =cut -sub admin_base_url { FixMyStreet->config('ADMIN_BASE_URL') || '' } +sub admin_base_url { + my $self = shift; + return FixMyStreet->config('ADMIN_BASE_URL') || $self->base_url . "/admin"; +} =head2 base_url diff --git a/perllib/FixMyStreet/Cobrand/EastHerts.pm b/perllib/FixMyStreet/Cobrand/EastHerts.pm new file mode 100644 index 000000000..ea5ed7f55 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/EastHerts.pm @@ -0,0 +1,54 @@ +package FixMyStreet::Cobrand::EastHerts; +use parent 'FixMyStreet::Cobrand::UKCouncils'; + +use strict; +use warnings; + +sub council_id { return 2342; } +sub council_area { return 'East Hertfordshire'; } +sub council_name { return 'East Hertfordshire District Council'; } +sub council_url { return 'eastherts'; } + +sub base_url { + my $self = shift; + return $self->next::method() if FixMyStreet->config('STAGING_SITE'); + return 'https://fixmystreet.eastherts.gov.uk'; +} + + +sub enter_postcode_text { + my ($self) = @_; + return 'Enter an ' . $self->council_area . ' postcode, or street name and area'; +} + +sub example_places { + return ( 'SG14 2AP', "Mangrove Road" ); +} + +sub disambiguate_location { + my $self = shift; + my $string = shift; + + return { + %{ $self->SUPER::disambiguate_location() }, + town => 'Hertford', + centre => '51.8650537133491,-0.00819715483544082', + span => '0.26293637547365,0.379186581552513', + bounds => [ 51.7341800736161, -0.18359132013981, 51.9971164490897, 0.195595261412703 ], + }; +} + +sub pin_colour { + my ( $self, $p, $context ) = @_; + return 'grey' if $p->state eq 'not responsible'; + return 'green' if $p->is_fixed || $p->is_closed; + return 'red' if $p->state eq 'confirmed'; + return 'yellow'; +} + +sub contact_email { + my $self = shift; + return join( '@', 'enquiries', 'eastherts.gov.uk' ); +} + +1;
\ No newline at end of file diff --git a/perllib/FixMyStreet/Cobrand/EastSussex.pm b/perllib/FixMyStreet/Cobrand/EastSussex.pm index 2ba3a4f70..80a86706a 100644 --- a/perllib/FixMyStreet/Cobrand/EastSussex.pm +++ b/perllib/FixMyStreet/Cobrand/EastSussex.pm @@ -113,6 +113,8 @@ sub reports_per_page { return 20; } sub pin_colour { my ( $self, $p, $context ) = @_; + return 'grey' unless $self->owns_problem( $p ); + # TODO refactor to a Moo(se)? lazy attribute my $open_states = $self->{open_states} ||= $p->open_states; diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm index 7b175f371..ba26b7a2c 100644 --- a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm +++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm @@ -32,10 +32,6 @@ sub area_types { [ 'NKO', 'NFY', 'NRA' ]; } -sub admin_base_url { - return 'http://www.fiksgatami.no/admin'; -} - sub geocode_postcode { my ( $self, $s ) = @_; diff --git a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm index 61011a414..b321a21c4 100644 --- a/perllib/FixMyStreet/Cobrand/FixMyStreet.pm +++ b/perllib/FixMyStreet/Cobrand/FixMyStreet.pm @@ -23,10 +23,6 @@ sub restriction { return {}; } -sub admin_base_url { - return 'https://secure.mysociety.org/admin/bci'; -} - sub title_list { my $self = shift; my $areas = shift; diff --git a/perllib/FixMyStreet/Cobrand/FixaMinGata.pm b/perllib/FixMyStreet/Cobrand/FixaMinGata.pm index e6e54926f..9ffbf00b8 100644 --- a/perllib/FixMyStreet/Cobrand/FixaMinGata.pm +++ b/perllib/FixMyStreet/Cobrand/FixaMinGata.pm @@ -33,10 +33,6 @@ sub area_types { [ 'KOM' ]; } -sub admin_base_url { - return 'http://www.fixamingata.se/admin/'; -} - # If lat/lon are present in the URL, OpenLayers will use that to centre the map. # Need to specify a zoom to stop it defaulting to null/0. sub uri { diff --git a/perllib/FixMyStreet/Cobrand/Greenwich.pm b/perllib/FixMyStreet/Cobrand/Greenwich.pm index d23e62138..7777079a9 100644 --- a/perllib/FixMyStreet/Cobrand/Greenwich.pm +++ b/perllib/FixMyStreet/Cobrand/Greenwich.pm @@ -10,7 +10,8 @@ sub council_name { return 'Royal Borough of Greenwich'; } sub council_url { return 'greenwich'; } sub base_url { - return FixMyStreet->config('BASE_URL') if FixMyStreet->config('STAGING_SITE'); + my $self = shift; + return $self->next::method() if FixMyStreet->config('STAGING_SITE'); return 'https://fix.royalgreenwich.gov.uk'; } diff --git a/perllib/FixMyStreet/Cobrand/Harrogate.pm b/perllib/FixMyStreet/Cobrand/Harrogate.pm index 519521867..8f4a6e2ea 100644 --- a/perllib/FixMyStreet/Cobrand/Harrogate.pm +++ b/perllib/FixMyStreet/Cobrand/Harrogate.pm @@ -12,7 +12,8 @@ sub council_url { return 'harrogate'; } sub is_two_tier { return 1; } # with North Yorkshire CC 2235 sub base_url { - return FixMyStreet->config('BASE_URL') if FixMyStreet->config('STAGING_SITE'); + my $self = shift; + return $self->next::method() if FixMyStreet->config('STAGING_SITE'); return 'http://fix.harrogate.gov.uk'; } diff --git a/perllib/FixMyStreet/Cobrand/Hart.pm b/perllib/FixMyStreet/Cobrand/Hart.pm index 185539cb6..42c4a636e 100644 --- a/perllib/FixMyStreet/Cobrand/Hart.pm +++ b/perllib/FixMyStreet/Cobrand/Hart.pm @@ -30,10 +30,9 @@ sub example_places { return ( 'GU51 4JX', 'Primrose Drive' ); } -sub hidden_categories { - return ( - 'Graffiti on bridges/subways', - ); +sub categories_restriction { + my ($self, $rs) = @_; + return $rs->search( { category => { '!=' => 'Graffiti on bridges/subways' } } ); } sub send_questionnaires { diff --git a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm index 543dd431a..d127f5e13 100644 --- a/perllib/FixMyStreet/Cobrand/Oxfordshire.pm +++ b/perllib/FixMyStreet/Cobrand/Oxfordshire.pm @@ -11,8 +11,9 @@ sub council_url { return 'oxfordshire'; } sub is_two_tier { return 1; } sub base_url { - return FixMyStreet->config('BASE_URL') if FixMyStreet->config('STAGING_SITE'); - return 'http://fixmystreet.oxfordshire.gov.uk'; + my $self = shift; + return $self->next::method() if FixMyStreet->config('STAGING_SITE'); + return 'https://fixmystreet.oxfordshire.gov.uk'; } sub enter_postcode_text { @@ -97,6 +98,7 @@ sub reports_ordering { sub pin_colour { my ( $self, $p, $context ) = @_; + return 'grey' unless $self->owns_problem( $p ); return 'grey' if $p->state eq 'not responsible'; return 'green' if $p->is_fixed || $p->is_closed; return 'red' if $p->state eq 'confirmed'; diff --git a/perllib/FixMyStreet/Cobrand/Stevenage.pm b/perllib/FixMyStreet/Cobrand/Stevenage.pm index 1f90e0498..2c305d326 100644 --- a/perllib/FixMyStreet/Cobrand/Stevenage.pm +++ b/perllib/FixMyStreet/Cobrand/Stevenage.pm @@ -11,7 +11,8 @@ sub council_url { return 'stevenage'; } sub is_two_tier { return 1; } sub base_url { - return FixMyStreet->config('BASE_URL') if FixMyStreet->config('STAGING_SITE'); + my $self = shift; + return $self->next::method() if FixMyStreet->config('STAGING_SITE'); return 'http://fixmystreet.stevenage.gov.uk'; } diff --git a/perllib/FixMyStreet/Cobrand/UK.pm b/perllib/FixMyStreet/Cobrand/UK.pm index 0eff48b00..e60b673b4 100644 --- a/perllib/FixMyStreet/Cobrand/UK.pm +++ b/perllib/FixMyStreet/Cobrand/UK.pm @@ -336,5 +336,61 @@ sub report_check_for_errors { return %errors; } -1; +=head2 get_body_handler_for_problem + +Returns a cobrand for the body that a problem was logged against. + + my $handler = $cobrand->get_body_handler_for_problem($row); + my $handler = $cobrand_class->get_body_handler_for_problem($row); + +If the UK council in bodies_str has a FMS.com cobrand then an instance of that +cobrand class is returned, otherwise the default FixMyStreet cobrand is used. + +=cut + +sub get_body_handler_for_problem { + my ($self, $row) = @_; + + my @bodies = values %{$row->bodies}; + my %areas = map { %{$_->areas} } @bodies; + + foreach my $avail ( FixMyStreet::Cobrand->available_cobrand_classes ) { + my $class = FixMyStreet::Cobrand->get_class_for_moniker($avail->{moniker}); + my $cobrand = $class->new({}); + next unless $cobrand->can('council_id'); + return $cobrand if $areas{$cobrand->council_id}; + } + return ref $self ? $self : $self->new; +} + +=head2 link_to_council_cobrand + +If a problem was sent to a UK council who has a FMS cobrand and the report is +currently being viewed on a different cobrand, then link the council's name to +that problem on the council's cobrand. + +=cut + +sub link_to_council_cobrand { + my ( $self, $problem ) = @_; + # If the report was sent to a cobrand that we're not currently on, + # include a link to view it on the responsible cobrand. + # This only occurs if the report was sent to a single body and we're not already + # using the body name as a link to all problem reports. + my $handler = $self->get_body_handler_for_problem($problem); + $self->{c}->log->debug( sprintf "bodies: %s areas: %s self: %s handler: %s", $problem->bodies_str, $problem->areas, $self->moniker, $handler->moniker ); + my $bodies_str_ids = $problem->bodies_str_ids; + if ( !mySociety::Config::get('AREA_LINKS_FROM_PROBLEMS') && + scalar(@$bodies_str_ids) == 1 && $handler->is_council && + $handler->moniker ne $self->{c}->cobrand->moniker + ) { + my $url = sprintf("%s%s", $handler->base_url, $problem->url); + return sprintf("<a href='%s'>%s</a>", $url, $problem->body( $self->{c} )); + } else { + return $problem->body( $self->{c} ); + } +} + + +1; diff --git a/perllib/FixMyStreet/Cobrand/UKCouncils.pm b/perllib/FixMyStreet/Cobrand/UKCouncils.pm index 6bf70e091..6e98f4ae0 100644 --- a/perllib/FixMyStreet/Cobrand/UKCouncils.pm +++ b/perllib/FixMyStreet/Cobrand/UKCouncils.pm @@ -129,6 +129,13 @@ sub owns_problem { return $areas{$self->council_id} ? 1 : undef; } +# If the council is two-tier then show pins for the other council as grey +sub pin_colour { + my ( $self, $p, $context ) = @_; + return 'grey' if $self->is_two_tier && !$self->owns_problem( $p ); + return $self->next::method($p, $context); +} + # If we ever link to a county problem report, needs to be to main FixMyStreet sub base_url_for_report { my ( $self, $report ) = @_; diff --git a/perllib/FixMyStreet/Cobrand/Zurich.pm b/perllib/FixMyStreet/Cobrand/Zurich.pm index 2e4a167db..d13408321 100644 --- a/perllib/FixMyStreet/Cobrand/Zurich.pm +++ b/perllib/FixMyStreet/Cobrand/Zurich.pm @@ -1163,7 +1163,7 @@ sub admin_stats { $public_response =~ s{\r?\n}{ <br/> }g if $public_response; # Assemble photo URL, if report has a photo - my $media_url = @{$report->photos} ? ($c->cobrand->base_url . $report->photos->[0]->{url}) : ''; + my $media_url = ( @{$report->photos} && $c->cobrand->allow_photo_display($report) ) ? ($c->cobrand->base_url . $report->photos->[0]->{url}) : ''; my @columns = ( $report->id, diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index 628497233..bc72cf9da 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -558,47 +558,40 @@ sub meta_line { my $date_time = Utils::prettify_dt( $problem->confirmed ); my $meta = ''; + my $category = $problem->category; + if ($c->cobrand->can('change_category_text')) { + $category = $c->cobrand->change_category_text($category); + } + if ( $problem->anonymous ) { - if ( $problem->service - and $problem->category && $problem->category ne _('Other') ) - { + if ( $problem->service and $category && $category ne _('Other') ) { $meta = sprintf( _('Reported via %s in the %s category anonymously at %s'), - $problem->service, $problem->category, $date_time ); - } - elsif ( $problem->service ) { + $problem->service, $category, $date_time ); + } elsif ( $problem->service ) { $meta = sprintf( _('Reported via %s anonymously at %s'), $problem->service, $date_time ); - } - elsif ( $problem->category and $problem->category ne _('Other') ) { + } elsif ( $category and $category ne _('Other') ) { $meta = sprintf( _('Reported in the %s category anonymously at %s'), - $problem->category, $date_time ); - } - else { + $category, $date_time ); + } else { $meta = sprintf( _('Reported anonymously at %s'), $date_time ); } - } - else { - if ( $problem->service - and $problem->category && $problem->category ne _('Other') ) - { + } else { + if ( $problem->service and $category && $category ne _('Other') ) { $meta = sprintf( _('Reported via %s in the %s category by %s at %s'), - $problem->service, $problem->category, + $problem->service, $category, $problem->name, $date_time ); - } - elsif ( $problem->service ) { + } elsif ( $problem->service ) { $meta = sprintf( _('Reported via %s by %s at %s'), $problem->service, $problem->name, $date_time ); - } - elsif ( $problem->category and $problem->category ne _('Other') ) { + } elsif ( $category and $category ne _('Other') ) { $meta = sprintf( _('Reported in the %s category by %s at %s'), - $problem->category, $problem->name, $date_time ); - } - else { - $meta = - sprintf( _('Reported by %s at %s'), $problem->name, $date_time ); + $category, $problem->name, $date_time ); + } else { + $meta = sprintf( _('Reported by %s at %s'), $problem->name, $date_time ); } } @@ -665,7 +658,12 @@ sub can_display_external_id { sub duration_string { my ( $problem, $c ) = @_; - my $body = $problem->body( $c ); + my $body; + if ( $c->cobrand->can('link_to_council_cobrand') ) { + $body = $c->cobrand->link_to_council_cobrand($problem); + } else { + $body = $problem->body( $c ); + } return sprintf(_('Sent to %s %s later'), $body, Utils::prettify_duration($problem->whensent->epoch - $problem->confirmed->epoch, 'minute') ); @@ -675,7 +673,7 @@ sub local_coords { my $self = shift; my $cobrand = FixMyStreet::Cobrand->get_class_for_moniker($self->cobrand)->new; if ($cobrand->moniker eq 'zurich') { - my ($x, $y) = Geo::Coordinates::CH1903::from_latlon($self->latitude, $self->longitude); + my ($x, $y) = Geo::Coordinates::CH1903Plus::from_latlon($self->latitude, $self->longitude); return ( int($x+0.5), int($y+0.5) ); } elsif ($cobrand->country eq 'GB') { my $coordsyst = 'G'; @@ -875,4 +873,24 @@ sub get_time_spent { return $admin_logs ? $admin_logs->get_column('sum_time_spent') : 0; } +=head2 get_cobrand_logged + +Get a cobrand object for the cobrand the problem was logged for. + +e.g. if a problem was logged at www.fixmystreet.com, this will be a +FixMyStreet::Cobrand::FixMyStreet object. + +=cut + +has get_cobrand_logged => ( + is => 'ro', + lazy => 1, + default => sub { + my $self = shift; + my $cobrand_class = FixMyStreet::Cobrand->get_class_for_moniker( $self->cobrand ); + return $cobrand_class->new; + }, +); + + 1; diff --git a/perllib/FixMyStreet/Gaze.pm b/perllib/FixMyStreet/Gaze.pm index a072cd246..bccc81d8c 100644 --- a/perllib/FixMyStreet/Gaze.pm +++ b/perllib/FixMyStreet/Gaze.pm @@ -8,6 +8,9 @@ use mySociety::Gaze; sub get_radius_containing_population ($$) { my ($lat, $lon) = @_; + # Don't call out to a real gaze when testing. + return 10.0 if FixMyStreet->test_mode; + my $dist = eval { mySociety::Locale::in_gb_locale { mySociety::Gaze::get_radius_containing_population($lat, $lon, 200_000); diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm index 4470ecbca..257a8d4a3 100644 --- a/perllib/FixMyStreet/Geocode.pm +++ b/perllib/FixMyStreet/Geocode.pm @@ -72,7 +72,7 @@ sub cache { my $cache_dir = FixMyStreet->config('GEO_CACHE') . $type . '/'; my $cache_file = $cache_dir . md5_hex($url); my $js; - if (-s $cache_file && -M $cache_file <= 7) { + if (-s $cache_file && -M $cache_file <= 7 && !FixMyStreet->config('STAGING_SITE')) { $js = File::Slurp::read_file($cache_file); } else { $url .= '&' . $args if $args; @@ -80,7 +80,7 @@ sub cache { $js = LWP::Simple::get($url); $js = encode_utf8($js) if utf8::is_utf8($js); File::Path::mkpath($cache_dir); - if ($js && (!$re || $js !~ $re)) { + if ($js && (!$re || $js !~ $re) && !FixMyStreet->config('STAGING_SITE')) { File::Slurp::write_file($cache_file, $js); } } diff --git a/perllib/FixMyStreet/Geocode/Zurich.pm b/perllib/FixMyStreet/Geocode/Zurich.pm index 50a7c355e..671da9722 100644 --- a/perllib/FixMyStreet/Geocode/Zurich.pm +++ b/perllib/FixMyStreet/Geocode/Zurich.pm @@ -13,7 +13,7 @@ package FixMyStreet::Geocode::Zurich; use strict; use Digest::MD5 qw(md5_hex); use File::Path (); -use Geo::Coordinates::CH1903; +use Geo::Coordinates::CH1903Plus; use Storable; use Utils; @@ -46,7 +46,7 @@ sub setup_soap { ) ); $soap = SOAP::Lite->on_action( sub { $action . $_[1]; } )->proxy($url); - $method = SOAP::Data->name('getLocation')->attr({ xmlns => $attr }); + $method = SOAP::Data->name('getLocation95')->attr({ xmlns => $attr }); } # string STRING CONTEXT @@ -67,7 +67,7 @@ sub string { my $cache_dir = FixMyStreet->config('GEO_CACHE') . 'zurich/'; my $cache_file = $cache_dir . md5_hex($s); my $result; - if (-s $cache_file && -M $cache_file <= 7) { + if (-s $cache_file && -M $cache_file <= 7 && !FixMyStreet->config('STAGING_SITE')) { $result = retrieve($cache_file); } else { my $search = SOAP::Data->name('search' => $s)->type(''); @@ -81,7 +81,7 @@ sub string { } $result = $result->result; File::Path::mkpath($cache_dir); - store $result, $cache_file if $result; + store $result, $cache_file if $result && !FixMyStreet->config('STAGING_SITE'); } if (!$result || !$result->{Location}) { @@ -95,7 +95,7 @@ sub string { foreach (@$results) { ($latitude, $longitude) = map { Utils::truncate_coordinate($_) } - Geo::Coordinates::CH1903::to_latlon($_->{easting}, $_->{northing}); + Geo::Coordinates::CH1903Plus::to_latlon($_->{easting}, $_->{northing}); push (@$error, { address => $_->{text}, latitude => $latitude, diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm index 6d641331f..b8b128611 100644 --- a/perllib/FixMyStreet/Map.pm +++ b/perllib/FixMyStreet/Map.pm @@ -100,9 +100,9 @@ sub _map_features { my $around_limit = $c->cobrand->on_map_list_limit || undef; my @around_args = ( $min_lat, $max_lat, $min_lon, $max_lon, $interval ); - my $around_map = $c->cobrand->problems->around_map( @around_args, undef, $category, $states ); + my $around_map = $c->cobrand->problems_on_map->around_map( @around_args, undef, $category, $states ); my $around_map_list = $around_limit - ? $c->cobrand->problems->around_map( @around_args, $around_limit, $category, $states ) + ? $c->cobrand->problems_on_map->around_map( @around_args, $around_limit, $category, $states ) : $around_map; my $dist = FixMyStreet::Gaze::get_radius_containing_population( $lat, $lon ); diff --git a/perllib/FixMyStreet/Map/Bristol.pm b/perllib/FixMyStreet/Map/Bristol.pm index 7098ceb40..3b60d1acf 100644 --- a/perllib/FixMyStreet/Map/Bristol.pm +++ b/perllib/FixMyStreet/Map/Bristol.pm @@ -20,9 +20,9 @@ sub zoom_parameters { sub tile_parameters { my $self = shift; my $params = { - url => 'https://maps.bristol.gov.uk/arcgis/rest/services/base/2015_BCC_96dpi/MapServer/WMTS/tile', + urls => [ 'https://maps.bristol.gov.uk/arcgis/rest/services/base/2015_BCC_96dpi/MapServer/WMTS/tile' ], + layer_names => [ '2015_BCC_96dpi' ], wmts_version => '1.0.0', - layer_name => '2015_BCC_96dpi', layer_style => 'default', matrix_set => 'default028mm', suffix => '.png', # appended to tile URLs @@ -60,6 +60,10 @@ sub copyright { return '© BCC'; } +sub map_type { + return 'bristol'; +} + # Reproject a WGS84 lat/lon into BNG easting/northing sub reproject_from_latlon($$$) { my ($self, $lat, $lon) = @_; diff --git a/perllib/FixMyStreet/Map/WMTSBase.pm b/perllib/FixMyStreet/Map/WMTSBase.pm index 13b6d8091..e35ae13c9 100644 --- a/perllib/FixMyStreet/Map/WMTSBase.pm +++ b/perllib/FixMyStreet/Map/WMTSBase.pm @@ -42,7 +42,8 @@ sub zoom_parameters { # A hash of parameters used in calculations for map tiles sub tile_parameters { my $params = { - url => '', # URL of the map tiles, up to the /{z}/{x}/{y} part + urls => [ '' ], # URL of the map tiles, up to the /{z}/{x}/{y} part + layer_names => [ '' ], wmts_version => '1.0.0', layer_style => '', matrix_set => '', @@ -206,14 +207,14 @@ sub get_map_hash { numZoomLevels => $self->zoom_parameters->{default_zoom}, tile_size => $self->tile_parameters->{size}, tile_dpi => $self->tile_parameters->{dpi}, - tile_url => $self->tile_parameters->{url}, + tile_urls => encode_json $self->tile_parameters->{urls}, tile_suffix => $self->tile_parameters->{suffix}, - layer_name => $self->tile_parameters->{layer_name}, + layer_names => encode_json $self->tile_parameters->{layer_names}, layer_style => $self->tile_parameters->{layer_style}, matrix_set => $self->tile_parameters->{matrix_set}, map_projection => $self->tile_parameters->{projection}, - origin_x => $self->tile_parameters->{origin_x}, - origin_y => $self->tile_parameters->{origin_y}, + origin_x => force_float_format($self->tile_parameters->{origin_x}), + origin_y => force_float_format($self->tile_parameters->{origin_y}), scales => encode_json \@scales, }; } @@ -222,7 +223,7 @@ sub tile_base_url { my $self = shift; my $params = $self->tile_parameters; return sprintf '%s/%s/%s/%s/%s', - $params->{url}, $params->{wmts_version}, $params->{layer_name}, + $params->{urls}[0], $params->{wmts_version}, $params->{layer_names}[0], $params->{layer_style}, $params->{matrix_set}; } @@ -336,4 +337,11 @@ sub click_to_wgs84 { return ( $lat, $lon ); } +sub force_float_format { + my $in = shift; + return mySociety::Locale::in_gb_locale { + sprintf( '%f', $in ); + }; +} + 1; diff --git a/perllib/FixMyStreet/Map/Zurich.pm b/perllib/FixMyStreet/Map/Zurich.pm index 4c597c30b..3b97f947f 100644 --- a/perllib/FixMyStreet/Map/Zurich.pm +++ b/perllib/FixMyStreet/Map/Zurich.pm @@ -1,259 +1,89 @@ # FixMyStreet:Map::Zurich # Zurich have their own tileserver. -# -# Copyright (c) 2012 UK Citizens Online Democracy. All rights reserved. -# Email: steve@mysociety.org; WWW: http://www.mysociety.org/ package FixMyStreet::Map::Zurich; +use base 'FixMyStreet::Map::WMTSBase'; use strict; -use Geo::Coordinates::CH1903; -use Math::Trig; -use Utils; - -use constant ZOOM_LEVELS => 9; -use constant DEFAULT_ZOOM => 5; -use constant MIN_ZOOM_LEVEL => 0; -use constant ID_OFFSET => 2; -use constant TILE_SIZE => 512; - -sub map_tiles { - my ($self, %params) = @_; - my ($left_col, $top_row, $z) = @params{'x_left_tile', 'y_top_tile', 'matrix_id'}; - my $tile_url = $self->base_tile_url(); - my $cols = $params{cols}; - my $rows = $params{rows}; - - my @col_offsets = (0.. ($cols-1) ); - my @row_offsets = (0.. ($rows-1) ); - - return [ - map { - my $row_offset = $_; - [ - map { - my $col_offset = $_; - my $row = $top_row + $row_offset; - my $col = $left_col + $col_offset; - my $src = sprintf '%s/%d/%d/%d.jpg', - $tile_url, $z, $row, $col; - my $dotted_id = sprintf '%d.%d', $col, $row; - - # return the data structure for the cell - +{ - src => $src, - row_offset => $row_offset, - col_offset => $col_offset, - dotted_id => $dotted_id, - alt => "Map tile $dotted_id", # TODO "NW map tile"? - } - } - @col_offsets - ] - } - @row_offsets - ]; -} - -sub base_tile_url { - # use the new 512px maps as used by Javascript - return '//www.gis.stadt-zuerich.ch/maps/rest/services/tiled/LuftbildHybrid/MapServer/WMTS/tile/1.0.0/tiled_LuftbildHybrid/default/default028mm'; -} - -sub copyright { - return '© Stadt Zürich'; -} - -# display_map C PARAMS -# PARAMS include: -# latitude, longitude for the centre point of the map -# CLICKABLE is set if the map is clickable -# PINS is array of pins to show, location and colour -sub display_map { - my ($self, $c, %params) = @_; - - # Map centre may be overridden in the query string - $params{latitude} = Utils::truncate_coordinate($c->get_param('lat') + 0) - if defined $c->get_param('lat'); - $params{longitude} = Utils::truncate_coordinate($c->get_param('lon') + 0) - if defined $c->get_param('lon'); - - $params{rows} //= 2; # 2x2 square is default - $params{cols} //= 2; - - $params{zoom} = do { - my $zoom = defined $c->get_param('zoom') - ? $c->get_param('zoom') + 0 - : $c->stash->{page} eq 'report' - ? DEFAULT_ZOOM+1 - : DEFAULT_ZOOM; - $zoom = ZOOM_LEVELS - 1 if $zoom >= ZOOM_LEVELS; - $zoom = 0 if $zoom < 0; - $zoom; +use Geo::Coordinates::CH1903Plus; + +sub zoom_parameters { + my $self = shift; + my $params = { + zoom_levels => scalar $self->scales, + default_zoom => 5, + min_zoom_level => 0, + id_offset => 0, }; - - $c->stash->{map} = $self->get_map_hash( %params ); - - if ($params{print_report}) { - $params{zoom}++ unless $params{zoom} >= ZOOM_LEVELS; - $c->stash->{print_report_map} - = $self->get_map_hash( - %params, - img_type => 'img', - cols => 4, rows => 4, - ); - # NB: we can passthrough img_type as literal here, as only designed for print - - # NB we can do arbitrary size, including non-squares, however we'd have - # to modify .square-map style with padding-bottom percentage calculated in - # an inline style: - # <zarino> in which case, the only change that'd be required is - # removing { padding-bottom: 100% } from .square-map__outer, putting - # the percentage into an inline style on the element itself, and then - # probably renaming .square-map__* to .fixed-aspect-map__* or something - # since it's no longer necessarily square - } + return $params; } -sub get_map_hash { - my ($self, %params) = @_; - - @params{'x_centre_tile', 'y_centre_tile', 'matrix_id'} - = latlon_to_tile_with_adjust( - @params{'latitude', 'longitude', 'zoom', 'rows', 'cols'}); - - # centre_(row|col) is either in middle, or just to right. - # e.g. if centre is the number in parens: - # 1 (2) 3 => 2 - int( 3/2 ) = 1 - # 1 2 (3) 4 => 3 - int( 4/2 ) = 1 - $params{x_left_tile} = $params{x_centre_tile} - int($params{cols} / 2); - $params{y_top_tile} = $params{y_centre_tile} - int($params{rows} / 2); - - $params{pins} = [ - map { - my $pin = { %$_ }; # shallow clone - ($pin->{px}, $pin->{py}) - = latlon_to_px($pin->{latitude}, $pin->{longitude}, - @params{'x_left_tile', 'y_top_tile', 'zoom'}); - $pin; - } @{ $params{pins} } - ]; - - return { - %params, - type => 'zurich', - map_type => 'OpenLayers.Layer.WMTS', - tiles => $self->map_tiles( %params ), - copyright => $self->copyright(), - zoom => $params{zoom},, - zoomOffset => MIN_ZOOM_LEVEL, - numZoomLevels => ZOOM_LEVELS, - tile_size => TILE_SIZE, +sub tile_parameters { + my $self = shift; + my $params = { + urls => [ + 'https://www.gis.stadt-zuerich.ch/maps/rest/services/tiled95/LuftbildHybrid/MapServer/WMTS/tile', + 'https://www.gis.stadt-zuerich.ch/maps/rest/services/tiled95/Stadtplan3D/MapServer/WMTS/tile' + ], + layer_names => [ 'LuftbildHybrid', 'Stadtplan3D' ], + wmts_version => '1.0.0', + layer_style => 'default', + matrix_set => 'default028mm', + suffix => '', # appended to tile URLs + size => 512, # pixels + dpi => 96, + inches_per_unit => 39.3701, # BNG uses metres + projection => 'EPSG:2056', + # The original tile origin values from the getCapabilities call are + # -27386400.0/31814500.0, but this results in the map tile being offset + # slightly. These corrected values were figured out manually by + # trial and error... + origin_x => -27386322.5, + origin_y => 31814423.0, }; + return $params; } -# Given a lat/lon, convert it to Zurch tile co-ordinates (precise). -sub latlon_to_tile($$$) { - my ($lat, $lon, $zoom) = @_; - - my ($x, $y) = Geo::Coordinates::CH1903::from_latlon($lat, $lon); - - my $matrix_id = $zoom + ID_OFFSET; +sub scales { + my $self = shift; my @scales = ( - '250000', '125000', - '64000', '32000', - '16000', '8000', - '4000', '2000', - '1000', '500', - '250' + # The two highest zoom levels are pretty much useless so they're disabled. + # '256000', # resolution 67.73346880027094 + # '128000', # resolution 33.86673440013547 + '64000', # resolution 16.933367200067735 + '32000', # resolution 8.466683600033868 + '16000', # resolution 4.233341800016934 + '8000', # resolution 2.116670900008467 + '4000', # resolution 1.0583354500042335 + '2000', # resolution 0.5291677250021167 + '1000', # resolution 0.26458386250105836 + '500', # resolution 0.13229193125052918 + '250', # resolution 0.06614596562526459 ); - my $tileOrigin = { lat => 30814423, lon => -29386322 }; - my $res = $scales[$matrix_id] / (39.3701 * 96); - # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH - - my $fx = ( $x - $tileOrigin->{lon} ) / ($res * TILE_SIZE); - my $fy = ( $tileOrigin->{lat} - $y ) / ($res * TILE_SIZE); - - return ( $fx, $fy, $matrix_id ); + return @scales; } -# Given a lat/lon, convert it to OSM tile co-ordinates (nearest actual tile, -# adjusted so the point will be near the centre of a 2x2 tiled map). -# -# Takes parameter for rows/cols. For even sizes (2x2, 4x4 etc.) will -# do adjustment, but simply returns actual for odd sizes. -# -sub latlon_to_tile_with_adjust { - my ($lat, $lon, $zoom, $rows, $cols) = @_; - my ($x_tile, $y_tile, $matrix_id) - = my @ret - = latlon_to_tile($lat, $lon, $zoom); - - # Try and have point near centre of map, passing through if odd - unless ($cols % 2) { - if ($x_tile - int($x_tile) > 0.5) { - $x_tile += 1; - } - } - unless ($rows % 2) { - if ($y_tile - int($y_tile) > 0.5) { - $y_tile += 1; - } - } - - return ( int($x_tile), int($y_tile), $matrix_id ); -} - -sub tile_to_latlon { - my ($fx, $fy, $zoom) = @_; - - my $matrix_id = $zoom + ID_OFFSET; - my @scales = ( '250000', '125000', '64000', '32000', '16000', '8000', '4000', '2000', '1000', '500', '250' ); - my $tileOrigin = { lat => 30814423, lon => -29386322 }; - my $res = $scales[$matrix_id] / (39.3701 * 96); # OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH - - my $x = $fx * $res * TILE_SIZE + $tileOrigin->{lon}; - my $y = $tileOrigin->{lat} - $fy * $res * TILE_SIZE; - - my ($lat, $lon) = Geo::Coordinates::CH1903::to_latlon($x, $y); - - return ( $lat, $lon ); +sub copyright { + return '© Stadt Zürich'; } -# Given a lat/lon, convert it to pixel co-ordinates from the top left of the map -sub latlon_to_px($$$$$) { - my ($lat, $lon, $x_tile, $y_tile, $zoom) = @_; - my ($pin_x_tile, $pin_y_tile) = latlon_to_tile($lat, $lon, $zoom); - my $pin_x = tile_to_px($pin_x_tile, $x_tile); - my $pin_y = tile_to_px($pin_y_tile, $y_tile); - return ($pin_x, $pin_y); +sub map_type { + return 'zurich'; } -# Convert tile co-ordinates to pixel co-ordinates from top left of map -# C is centre tile reference of displayed map -sub tile_to_px { - my ($p, $c) = @_; - $p = TILE_SIZE * ($p - $c); - $p = int($p + .5 * ($p <=> 0)); - return $p; -} -sub click_to_tile { - my ($pin_tile, $pin) = @_; - $pin -= TILE_SIZE while $pin > TILE_SIZE; - $pin += TILE_SIZE while $pin < 0; - return $pin_tile + $pin / TILE_SIZE; +# Reproject a WGS84 lat/lon into Swiss easting/northing +sub reproject_from_latlon($$$) { + my ($self, $lat, $lon) = @_; + my ($x, $y) = Geo::Coordinates::CH1903Plus::from_latlon($lat, $lon); + return ($x, $y); } -# Given some click co-ords (the tile they were on, and where in the -# tile they were), convert to WGS84 and return. -sub click_to_wgs84 { - my ($self, $c, $pin_tile_x, $pin_x, $pin_tile_y, $pin_y) = @_; - my $tile_x = click_to_tile($pin_tile_x, $pin_x); - my $tile_y = click_to_tile($pin_tile_y, $pin_y); - my $zoom = (defined $c->get_param('zoom') ? $c->get_param('zoom') : DEFAULT_ZOOM); - my ($lat, $lon) = tile_to_latlon($tile_x, $tile_y, $zoom); - return ( $lat, $lon ); +# Reproject a Swiss easting/northing into WGS84 lat/lon +sub reproject_to_latlon($$$) { + my ($self, $x, $y) = @_; + my ($lat, $lon) = Geo::Coordinates::CH1903Plus::to_latlon($x, $y); + return ($lat, $lon); } 1; |