diff --git a/perllib/FixMyStreet/Cobrand/TfL.pm b/perllib/FixMyStreet/Cobrand/TfL.pm index 2de73f9c145..ba3a45b3d0f 100644 --- a/perllib/FixMyStreet/Cobrand/TfL.pm +++ b/perllib/FixMyStreet/Cobrand/TfL.pm @@ -1,3 +1,13 @@ +=head1 NAME + +FixMyStreet::Cobrand::TfL - code specific to the Transport for London cobrand + +=head1 SYNOPSIS + +Transport for London is an FMS integration. It covers multiple London Borough areas + +=cut + package FixMyStreet::Cobrand::TfL; use parent 'FixMyStreet::Cobrand::Whitelabel'; @@ -6,6 +16,15 @@ use warnings; use utf8; use Moo; + +=head1 DESCRIPTION + +TfL has features related to covering multiple London areas. It doesn't have an external +integration with a backend, but does have single sign-on with Azure for staff users - they +are assigned an FMS account and user role when logging in. + +=cut + with 'FixMyStreet::Roles::BoroughEmails'; use POSIX qw(strcoll); @@ -14,47 +33,33 @@ use FixMyStreet::MapIt; use mySociety::ArrayUtils; use Utils; -sub london_boroughs { ( - 2511, 2489, 2494, 2488, 2482, 2505, 2512, 2481, 2484, 2495, - 2493, 2508, 2502, 2509, 2487, 2485, 2486, 2483, 2507, 2503, - 2480, 2490, 2492, 2500, 2510, 2497, 2499, 2491, 2498, 2506, - 2496, 2501, 2504, -) } - -# Surrounding areas for bus stops which can be outside London -sub surrounding_london { ( - # Clockwise from top left: South Bucks, Three Rivers, Watford, Hertsmere, - # Welwyn Hatfield, Broxbourne, Epping Forest, Brentwood, Thurrock, - # Dartford, Sevenoaks, Tandridge, Reigate and Banstead, Epsom and Ewell, - # Mole Valley, Elmbridge, Spelthorne, Slough - 2256, 2338, 2346, 2339, 2344, 2340, 2311, 2309, 2615, - 2358, 2350, 2448, 2453, 2457, 2454, 2455, 2456, 2606, -) } - sub council_area_id { [ $_[0]->london_boroughs, $_[0]->surrounding_london ] } sub council_area { return 'TfL'; } sub council_name { return 'TfL'; } sub council_url { return 'tfl'; } sub area_types { [ 'LBO', 'UTA', 'DIS' ] } -sub is_council { 0 } -sub borough_for_report { - my ($self, $problem) = @_; +=head1 Defaults - # Get relevant area ID from report - my %areas = map { $_ => 1 } split ',', $problem->areas; - my ($council_match) = grep { $areas{$_} } @{ $self->council_area_id }; - return unless $council_match; +=over 4 - # Look up area names if not already fetched - my $areas = $self->{c}->stash->{children} ||= $self->fetch_area_children; - return $areas->{$council_match}{name}; -} +=item * TfL only allows contact form to report abusive reports + +=cut sub abuse_reports_only { 1 } + +=item * Reports made to TfL do not get followed up by a questionnaire + +=cut + sub send_questionnaires { 0 } +=item * TfL geocoding automatically sets town to London + +=cut + sub disambiguate_location { my $self = shift; my $string = shift; @@ -65,15 +70,37 @@ sub disambiguate_location { }; } +=item * We use OpenStreetMap for geocoding + +=cut + sub get_geocoder { 'OSM' } +=item * TfL is not a council so prevent council related versions of text appearing on the web and in emails + +=cut + +sub is_council { 0 } + +=item * TfL is set to resend a report if the category is updated + +=cut + sub category_change_force_resend { 1 } +=item * TfL has customised front page prompt for entering a search + +=cut + sub enter_postcode_text { my ($self) = @_; return 'Enter a London postcode, or street name and area, or a reference number of a problem previously reported'; } +=item * TfL has its own privacy policy, which we redirect to from /about/privacy + +=cut + sub privacy_policy_url { 'https://tfl.gov.uk/corporate/privacy-and-cookies/reporting-street-problems' } sub about_hook { @@ -86,14 +113,132 @@ sub about_hook { } } -# These need to be overridden so the method in UKCouncils doesn't create -# a fixmystreet.com link (because of the false-returning owns_problem call) +=item * TfL has green pins for closed/fixed, red for confirmed, and orange for everything else + +=cut + +sub pin_colour { + my ( $self, $p, $context ) = @_; + return 'green' if $p->is_closed; + return 'green' if $p->is_fixed; + return 'red' if $p->state eq 'confirmed'; + return 'orange'; # all the other `open_states` like "in progress" +} + +=item * Superusers and TfL staff can access the TfL admin + +=cut + +sub admin_allow_user { + my ( $self, $user ) = @_; + return 1 if $user->is_superuser; + return undef unless defined $user->from_body; + return $user->from_body->name eq 'TfL'; +} + + +=item * Restrict admin users to tfl.gov.uk email addresses + +=cut + +sub admin_user_domain { 'tfl.gov.uk' } + +=item * Sets anonymous reports to show a button for anonymous reporting + +=cut + +sub allow_anonymous_reports { 'button' } + +=item * TfL sends out confirmation emails when a report is made + +=cut + +sub report_sent_confirmation_email { 'id' } + +=item * TfL only shows reports on the map and in the report list for 6 weeks + +=cut + +sub report_age { '6 weeks' } + +=item * TfL doesn't show reports made before the go-live date (2019-12-09) + +=cut + +sub cut_off_date { '2019-12-09 12:00' } + +=back + +=head2 relative_url_for_report & base_url_for_report + +These are overridden so the method in UKCouncils doesn't create +a fixmystreet.com link (because of the false-returning owns_problem call) + +=cut + sub relative_url_for_report { "" } sub base_url_for_report { my $self = shift; return $self->base_url; } +=head1 Multi council features + +TfL covers a lot of areas and has features necessary to work across areas. +It sends emails in some categories to different addresses depending on Borough +using L. + +The cobrand covers London and its surrounding areas, as e.g. some bus stops are +outside London. + +=cut + +# A list of all the London Borough MapIt IDs +sub london_boroughs { ( + 2511, 2489, 2494, 2488, 2482, 2505, 2512, 2481, 2484, 2495, + 2493, 2508, 2502, 2509, 2487, 2485, 2486, 2483, 2507, 2503, + 2480, 2490, 2492, 2500, 2510, 2497, 2499, 2491, 2498, 2506, + 2496, 2501, 2504, +) } + +=pod + +These are Clockwise from top left: South Bucks, Three Rivers, Watford, Hertsmere, +Welwyn Hatfield, Broxbourne, Epping Forest, Brentwood, Thurrock, Dartford, Sevenoaks, +Tandridge, Reigate and Banstead, Epsom and Ewell, Mole Valley, Elmbridge, Spelthorne, Slough + +=cut + +sub surrounding_london { ( + 2256, 2338, 2346, 2339, 2344, 2340, 2311, 2309, 2615, + 2358, 2350, 2448, 2453, 2457, 2454, 2455, 2456, 2606, +) } + +=over 4 + +=item * For staff, the Borough the report is made in is shown when listing reports + +=cut + +sub borough_for_report { + my ($self, $problem) = @_; + + # Get relevant area ID from report + my %areas = map { $_ => 1 } split ',', $problem->areas; + my ($council_match) = grep { $areas{$_} } @{ $self->council_area_id }; + return unless $council_match; + + # Look up area names if not already fetched + my $areas = $self->{c}->stash->{children} ||= $self->fetch_area_children; + return $areas->{$council_match}{name}; +} + +=item * Extra bodies can appear from configuration (e.g. hire bike companies) + +=item * A few categories don't appear to admins as we don't want to be able to resend reports to them + +=cut + sub categories_restriction { my ($self, $rs) = @_; my $bodies = [ 'TfL', 'National Highways' ]; @@ -103,9 +248,15 @@ sub categories_restriction { return $rs->search( { category => { -not_in => $self->_tfl_no_resend_categories } } ); } -sub admin_user_domain { 'tfl.gov.uk' } +=back -sub allow_anonymous_reports { 'button' } +=head1 Problem display and searching + +=over 4 + +=item * You can search for reports from the front page using FMS[id]. + +=cut sub lookup_by_ref_regex { return qr/^\s*((?:FMS\s*)?\d+)\s*$/i; @@ -121,12 +272,9 @@ sub lookup_by_ref { return 0; } -sub report_sent_confirmation_email { 'id' } - -sub report_age { '6 weeks' } +=item * Limit problem listings history to problems that have been updated in the last 3 years -# We don't want any reports made before the go-live date visible -sub cut_off_date { '2019-12-09 12:00' } +=cut sub problems_restriction { my ($self, $rs) = @_; @@ -148,6 +296,10 @@ sub problems_sql_restriction { return $q; } +=item * Reports are deleted from TfL after 7 years if they are safety critical, or after 3 years if they are not + +=cut + sub inactive_reports_filter { my ($self, $time, $rs) = @_; if ($time < 7*12) { @@ -158,44 +310,53 @@ sub inactive_reports_filter { return $rs; } -sub password_expiry { - my ($self) = @_; - - my $email = $self->{c}->user->email; - my $domain_email = $self->admin_user_domain; - return if $email =~ /$domain_email$/; - - # uncoverable statement - 86400 * 365 -} - -sub pin_colour { - my ( $self, $p, $context ) = @_; - return 'green' if $p->is_closed; - return 'green' if $p->is_fixed; - return 'red' if $p->state eq 'confirmed'; - return 'orange'; # all the other `open_states` like "in progress" -} +=item * Include all reports in duplicate spotting, not just open ones -sub admin_allow_user { - my ( $self, $user ) = @_; - return 1 if $user->is_superuser; - return undef unless defined $user->from_body; - return $user->from_body->name eq 'TfL'; -} +=cut sub around_nearby_filter { my ($self, $params) = @_; - # Include all reports in duplicate spotting + delete $params->{states}; } +=back + +=head1 OIDC single sign on + +TfL staff users use an Azure single-sign on integration + +=over 4 + +=item * Single sign on is enabled if the configuration is set up + +=cut + sub social_auth_enabled { my $self = shift; return $self->feature('oidc_login') ? 1 : 0; } +=item * Non-staff users must change their passwords annually; staff users don't use passwords so are excluded + +=cut + +sub password_expiry { + my ($self) = @_; + + my $email = $self->{c}->user->email; + my $domain_email = $self->admin_user_domain; + return if $email =~ /$domain_email$/; + + # uncoverable statement + 86400 * 365 +} + +=item * Different single sign-ons send user details differently, user_from_oidc extracts the relevant parts + +=cut + sub user_from_oidc { my ($self, $payload) = @_; @@ -205,6 +366,10 @@ sub user_from_oidc { return ($name, $email); } +=item * TfL sends the user role in the single sign-on payload, which we use to set the FMS role + +=cut + sub roles_from_oidc { my ($self, $user, $roles) = @_; @@ -444,6 +609,8 @@ sub report_new_munge_before_insert { $report->set_extra_fields(@$extra); } +=back + =head2 report_validation Allow through "TfL" as a name if reporting as staff.