diff options
Diffstat (limited to 'perllib/FixMyStreet/Cobrand')
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Barnet.pm | 80 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Default.pm | 936 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/EmptyHomes.pm | 178 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/FiksGataMi.pm | 259 | ||||
-rw-r--r-- | perllib/FixMyStreet/Cobrand/Southampton.pm | 80 |
5 files changed, 1533 insertions, 0 deletions
diff --git a/perllib/FixMyStreet/Cobrand/Barnet.pm b/perllib/FixMyStreet/Cobrand/Barnet.pm new file mode 100644 index 000000000..a12fa6d06 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Barnet.pm @@ -0,0 +1,80 @@ +package FixMyStreet::Cobrand::Barnet; +use base 'FixMyStreet::Cobrand::Default'; + +use strict; +use warnings; + +use Carp; +use URI::Escape; +use mySociety::VotingArea; + +sub site_restriction { + return ( "and council='2489'", 'barnet', { council => '2489' } ); +} + +sub problems_clause { + return { council => '2489' }; +} + +sub problems { + my $self = shift; + return $self->{c}->model('DB::Problem')->search( $self->problems_clause ); +} + +sub base_url { + my $base_url = mySociety::Config::get('BASE_URL'); + if ( $base_url !~ /barnet/ ) { + $base_url =~ s{http://(?!www\.)}{http://barnet.}g; + $base_url =~ s{http://www\.}{http://barnet.}g; + } + return $base_url; +} + +sub site_title { + my ($self) = @_; + return 'Barnet Council FixMyStreet'; +} + +sub enter_postcode_text { + my ($self) = @_; + return 'Enter a Barnet postcode, or street name and area'; +} + +sub council_check { + my ( $self, $params, $context ) = @_; + + my $councils = $params->{all_councils}; + my $council_match = defined $councils->{2489}; + if ($council_match) { + return 1; + } + my $url = 'http://www.fixmystreet.com/'; + $url .= 'alert' if $context eq 'alert'; + $url .= '?pc=' . URI::Escape::uri_escape( $self->{c}->req->param('pc') ) + if $self->{c}->req->param('pc'); + my $error_msg = "That location is not covered by Barnet. +Please visit <a href=\"$url\">the main FixMyStreet site</a>."; + return ( 0, $error_msg ); +} + +# All reports page only has the one council. +sub all_councils_report { + return 0; +} + +sub disambiguate_location { + return { + centre => '51.612832,-0.218169', + span => '0.0563,0.09', + bounds => [ '51.584682,-0.263169', '51.640982,-0.173169' ], + }; +} + +sub recent_photos { + my ( $self, $num, $lat, $lon, $dist ) = @_; + $num = 2 if $num == 3; + return $self->problems->recent_photos( $num, $lat, $lon, $dist ); +} + +1; + diff --git a/perllib/FixMyStreet/Cobrand/Default.pm b/perllib/FixMyStreet/Cobrand/Default.pm new file mode 100644 index 000000000..134111076 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Default.pm @@ -0,0 +1,936 @@ +package FixMyStreet::Cobrand::Default; + +use strict; +use warnings; +use FixMyStreet; +use URI; + +use Carp; +use mySociety::MaPit; +use mySociety::PostcodeUtil; + +=head2 new + + my $cobrand = $class->new; + my $cobrand = $class->new( { c => $c } ); + +Create a new cobrand object, optionally setting the context. + +You probably shouldn't need to do this and should get the cobrand object via a +method in L<FixMyStreet::Cobrand> instead. + +=cut + +sub new { + my $class = shift; + my $self = shift || {}; + return bless $self, $class; +} + +=head2 moniker + + $moniker = $cobrand_class->moniker(); + +Returns a moniker that can be used to identify this cobrand. By default this is +the last part of the class name lowercased - eg 'F::C::SomeCobrand' becomes +'somecobrand'. + +=cut + +sub moniker { + my $class = ref( $_[0] ) || $_[0]; # deal with object or class + my ($last_part) = $class =~ m{::(\w+)$}; + $last_part = lc($last_part); + return '' if $last_part eq 'default'; + return $last_part; +} + +=head2 is_default + + $bool = $cobrand->is_default(); + +Returns true if this is the default cobrand, false otherwise. + +=cut + +sub is_default { + my $self = shift; + return $self->moniker eq ''; +} + +=head2 path_to_web_templates + + $path = $cobrand->path_to_web_templates( ); + +Returns the path to the templates for this cobrand - by default +"templates/web/$moniker" + +=cut + +sub path_to_web_templates { + my $self = shift; + return FixMyStreet->path_to( 'templates/web', $self->moniker ); +} + +=head1 country + +Returns the country that this cobrand operates in, as an ISO3166-alpha2 code. + +=cut + +sub country { + return 'GB'; +} + +=head1 problems_clause + +Returns a hash for a query to be used by problems (and elsewhere in joined +queries) to restrict results for a cobrand. + +=cut + +sub problems_clause {} + +=head1 problems + +Returns a ResultSet of Problems, restricted to a subset if we're on a cobrand +that only wants some of the data. + +=cut + +sub problems { + my $self = shift; + return $self->{c}->model('DB::Problem'); +} + +=head1 site_restriction + +Return a site restriction clause and a site key if the cobrand uses a subset of +the FixMyStreet data. Parameter is any extra data the cobrand needs. Returns an +empty string and site key 0 if the cobrand uses all the data. + +=cut + +sub site_restriction { return ( "", 0, {} ) } + +=head2 contact_restriction + +Return a contact restriction clause if the cobrand uses a subset of the +FixMyStreet contact data. + +=cut + +sub contact_restriction { + {}; +} + +=head2 restriction + +Return a restriction to pull out data saved while using the cobrand site. + +=cut + +sub restriction { + my $self = shift; + + return $self->moniker ? { cobrand => $self->moniker } : {}; +} + +=head2 base_url_for_emails + +Return the base url to use in links in emails for the cobranded version of the +site, parameter is extra data. + +=cut + +sub base_url_for_emails { + my $self = shift; + return $self->base_url; +} + +=head2 base_url_with_lang + +=cut + +sub base_url_with_lang { + my $self = shift; + my $email = shift; + + if ($email) { + return $self->base_url_for_emails; + } else { + return $self->base_url; + } +} + +=head2 admin_base_url + +Base URL for the admin interface. + +=cut + +sub admin_base_url { 0 } + +=head2 writetothem_url + +URL for writetothem; parameter is COBRAND_DATA. + +=cut + +sub writetothem_url { 0 } + +=head2 base_url + +Return the base url for the cobranded version of the site + +=cut + +sub base_url { mySociety::Config::get('BASE_URL') } + +=head2 base_host + +Return the base host for the cobranded version of the site + +=cut + +sub base_host { + my $self = shift; + my $uri = URI->new( $self->base_url ); + return $uri->host; +} + +=head2 enter_postcode_text + +Return the text that prompts the user to enter their postcode/place name. +Parameter is QUERY + +=cut + +sub enter_postcode_text { '' } + +=head2 set_lang_and_domain + + my $set_lang = $cobrand->set_lang_and_domain( $lang, $unicode, $dir ) + +Set the language and domain of the site based on the cobrand and host. + +=cut + +sub set_lang_and_domain { + my ( $self, $lang, $unicode, $dir ) = @_; + my $set_lang = mySociety::Locale::negotiate_language( + 'en-gb,English,en_GB', $lang + ); + mySociety::Locale::gettext_domain( 'FixMyStreet', $unicode, $dir ); + mySociety::Locale::change(); + return $set_lang; +} + +=head2 alert_list_options + +Return HTML for a list of alert options for the cobrand, given QUERY and +OPTIONS. + +=cut + +sub alert_list_options { 0 } + +=head2 recent_photos + +Return N recent photos. If EASTING, NORTHING and DISTANCE are supplied, the +photos must be attached to problems within DISTANCE of the point defined by +EASTING and NORTHING. + +=cut + +sub recent_photos { + my $self = shift; + return $self->problems->recent_photos(@_); +} + +=head2 recent + +Return recent problems on the site. + +=cut + +sub recent { + my ( $self ) = @_; + return $self->problems->recent(); +} + +=item shorten_recency_if_new_greater_than_fixed + +By default we want to shorten the recency so that the numbers are more +attractive. + +=cut + +sub shorten_recency_if_new_greater_than_fixed { + return 1; +} + +=head2 front_stats_data + +Return a data structure containing the front stats information that a template +can then format. + +=cut + +sub front_stats_data { + my ( $self ) = @_; + + my $recency = '1 week'; + my $shorter_recency = '3 days'; + + my $fixed = $self->problems->recent_fixed(); + my $updates = $self->problems->number_comments(); + my $new = $self->problems->recent_new( $recency ); + + if ( $new > $fixed && $self->shorten_recency_if_new_greater_than_fixed ) { + $recency = $shorter_recency; + $new = $self->problems->recent_new( $recency ); + } + + my $stats = { + fixed => $fixed, + updates => $updates, + new => $new, + recency => $recency, + }; + + return $stats; +} + +=head2 disambiguate_location + +Returns disambiguating information available + +=cut + +sub disambiguate_location { + return { + country => 'uk', + }; +} + +=head2 form_elements + +Parameters are FORM_NAME, QUERY. Return HTML for any extra needed elements for +FORM_NAME + +=cut + +sub form_elements { '' } + +=head2 cobrand_data_for_generic_update + +Parameter is UPDATE_DATA, a reference to a hash of non-cobranded update data. +Return cobrand extra data for the update + +=cut + +sub cobrand_data_for_generic_update { '' } + +=head2 cobrand_data_for_generic_update + +Parameter is PROBLEM_DATA, a reference to a hash of non-cobranded problem data. +Return cobrand extra data for the problem + +=cut + +sub cobrand_data_for_generic_problem { '' } + +=head2 extra_problem_data + +Parameter is QUERY. Return a string of extra data to be stored with a problem + +=cut + +sub extra_problem_data { '' } + +=head2 extra_update_data + +Parameter is QUERY. Return a string of extra data to be stored with an update + +=cut + +sub extra_update_data { '' } + +=head2 extra_alert_data + +Parameter is QUERY. Return a string of extra data to be stored with an alert + +=cut + +sub extra_alert_data { '' } + +=head2 extra_data + +Given a QUERY, extract any extra data required by the cobrand + +=cut + +sub extra_data { '' } + +=head2 extra_problem_meta_text + +Returns any extra text to be displayed with a PROBLEM. + +=cut + +sub extra_problem_meta_text { '' } + +=head2 extra_update_meta_text + +Returns any extra text to be displayed with an UPDATE. + +=cut + +sub extra_update_meta_text { '' } + +=head2 uri + +Given a URL ($_[1]), QUERY, EXTRA_DATA, return a URL with any extra params +needed appended to it. + +In the default case, if we're using an OpenLayers map, we need to make +sure zoom is always present if lat/lon are, to stop OpenLayers defaulting +to null/0. + +=cut + +sub uri { + my ( $self, $uri ) = @_; + + (my $map_class = $FixMyStreet::Map::map_class) =~ s/^FixMyStreet::Map:://; + return $uri unless $map_class =~ /OSM|FMS/; + + $uri->query_param( zoom => 3 ) + if $uri->query_param('lat') && !$uri->query_param('zoom'); + + return $uri; +} + + +=head2 header_params + +Return any params to be added to responses + +=cut + +sub header_params { return {} } + +=head2 root_path_js + +Parameter is QUERY. Return some js to set the root path from which AJAX queries +should be made. + +=cut + +sub root_path_js { 'var root_path = "";' } + +=head2 site_title + +Return the title to be used in page heads. + +=cut + +sub site_title { 'FixMyStreet' } + +=head2 on_map_list_limit + +Return the maximum number of items to be given in the list of reports on the map + +=cut + +sub on_map_list_limit { return undef; } + +=head2 on_map_default_max_pin_age + +Return the default maximum age for pins. + +=cut + +sub on_map_default_max_pin_age { return '6 months'; } + +=head2 allow_photo_upload + +Return a boolean indicating whether the cobrand allows photo uploads + +=cut + +sub allow_photo_upload { return 1; } + +=head2 allow_crosssell_adverts + +Return a boolean indicating whether the cobrand allows the display of crosssell +adverts + +=cut + +sub allow_crosssell_adverts { return 1; } + +=head2 allow_photo_display + +Return a boolean indicating whether the cobrand allows photo display + +=cut + +sub allow_photo_display { return 1; } + +=head2 allow_update_reporting + +Return a boolean indication whether users should see links next to updates +allowing them to report them as offensive. + +=cut + +sub allow_update_reporting { return 0; } + +=head2 geocode_postcode + +Given a QUERY, return LAT/LON and/or ERROR. + +=cut + +sub geocode_postcode { + my ( $self, $s ) = @_; + + if ($s =~ /^\d+$/) { + return { + error => 'FixMyStreet is a UK-based website that currently works in England, Scotland, and Wales. Please enter either a postcode, or a Great British street name and area.' + }; + } elsif (mySociety::PostcodeUtil::is_valid_postcode($s)) { + my $location = mySociety::MaPit::call('postcode', $s); + if ($location->{error}) { + return { + error => $location->{code} =~ /^4/ + ? _('That postcode was not recognised, sorry.') + : $location->{error} + }; + } + my $island = $location->{coordsyst}; + if (!$island) { + return { + error => _("Sorry, that appears to be a Crown dependency postcode, which we don't cover.") + }; + } elsif ($island eq 'I') { + return { + error => _("We do not currently cover Northern Ireland, I'm afraid.") + }; + } + return { + latitude => $location->{wgs84_lat}, + longitude => $location->{wgs84_lon}, + }; + } + return {}; +} + +=head2 geocoded_string_check + +Parameters are LOCATION, QUERY. Return a boolean indicating whether the +string LOCATION passes the cobrands checks. + +=cut + +sub geocoded_string_check { return 1; } + +=head2 find_closest + +Used by send-reports to attach nearest things to the bottom of the report + +=cut + +sub find_closest { + my ( $self, $latitude, $longitude ) = @_; + my $str = ''; + + # Get nearest road-type thing from Bing + my $key = mySociety::Config::get('BING_MAPS_API_KEY', ''); + if ($key) { + my $url = "http://dev.virtualearth.net/REST/v1/Locations/$latitude,$longitude?c=en-GB&key=$key"; + my $j = LWP::Simple::get($url); + if ($j) { + $j = JSON->new->utf8->allow_nonref->decode($j); + if ($j->{resourceSets}[0]{resources}[0]{name}) { + $str .= sprintf(_("Nearest road to the pin placed on the map (automatically generated by Bing Maps): %s"), + $j->{resourceSets}[0]{resources}[0]{name}) . "\n\n"; + } + } + } + + # Get nearest postcode from Matthew's random gazetteer (put in MaPit? Or elsewhere?) + my $url = "http://gazetteer.dracos.vm.bytemark.co.uk/point/$latitude,$longitude.json"; + my $j = LWP::Simple::get($url); + if ($j) { + $j = JSON->new->utf8->allow_nonref->decode($j); + if ($j->{postcode}) { + $str .= sprintf(_("Nearest postcode to the pin placed on the map (automatically generated): %s (%sm away)"), + $j->{postcode}[0], $j->{postcode}[1]) . "\n\n"; + } + } + + return $str; +} + +=head2 council_check + +Paramters are COUNCILS, QUERY, CONTEXT. Return a boolean indicating whether +COUNCILS pass any extra checks. CONTEXT is where we are on the site. + +=cut + +sub council_check { return ( 1, '' ); } + +=head2 feed_xsl + +Return an XSL to be used in rendering feeds + +=cut + +sub feed_xsl { '/xsl.xsl' } + +=head2 all_councils_report + +Return a boolean indicating whether the cobrand displays a report of all +councils + +=cut + +sub all_councils_report { 1 } + +=head2 ask_ever_reported + +Return a boolean indicating whether people should be asked whether this is the +first time they' ve reported a problem + +=cut + +sub ask_ever_reported { 1 } + +=head2 admin_pages + +List of names of pages to display on the admin interface + +=cut + +sub admin_pages { 0 } + +=head2 admin_show_creation_graph + +Show the problem creation graph in the admin interface +=cut + +sub admin_show_creation_graph { 1 } + +=head2 area_types, area_min_generation + +The MaPit types this site handles + +=cut + +sub area_types { return qw(DIS LBO MTD UTA CTY COI); } +sub area_min_generation { 10 } + +=head2 contact_name, contact_email + +Return the contact name or email for the cobranded version of the site (to be +used in emails). + +=cut + +sub contact_name { $_[0]->get_cobrand_conf('CONTACT_NAME') } +sub contact_email { $_[0]->get_cobrand_conf('CONTACT_EMAIL') } + +=head2 get_cobrand_conf COBRAND KEY + +Get the value for KEY from the config file for COBRAND + +=cut + +sub get_cobrand_conf { + my ( $self, $key ) = @_; + my $value = undef; + my $cobrand_moniker = $self->moniker; + + my $cobrand_config_file = + FixMyStreet->path_to("conf/cobrands/$cobrand_moniker/general"); + my $normal_config_file = FixMyStreet->path_to('conf/general'); + + if ( -e $cobrand_config_file ) { + + # FIXME - don't rely on the config file name - should + # change mySociety::Config so that it can return values from a + # particular config file instead + mySociety::Config::set_file("$cobrand_config_file"); + my $config_key = $key . "_" . uc($cobrand_moniker); + $value = mySociety::Config::get( $config_key, undef ); + mySociety::Config::set_file("$normal_config_file"); + } + + # If we didn't find a value use one from normal config + if ( !defined($value) ) { + $value = mySociety::Config::get($key); + } + + return $value; +} + +=item email_host + +Return if we are the virtual host that sends email for this cobrand + +=cut + +sub email_host { + return 1; +} + +=item remove_redundant_councils + +Remove councils whose reports go to another council + +=cut + +sub remove_redundant_councils { + my $self = shift; + my $all_councils = shift; + + # Ipswich & St Edmundsbury are responsible for everything in their + # areas, not Suffolk + delete $all_councils->{2241} + if $all_councils->{2446} # + || $all_councils->{2443}; + + # Norwich is responsible for everything in its areas, not Norfolk + delete $all_councils->{2233} # + if $all_councils->{2391}; +} + +=item filter_all_council_ids_list + +Removes any council IDs that we don't need from an array and returns the +filtered array + +=cut + +sub filter_all_council_ids_list { + my $self = shift; + return @_; +} + +=item short_name + +Remove extra information from council names for tidy URIs + +=cut + +sub short_name { + my $self = shift; + my ($area, $info) = @_; + # Special case Durham as it's the only place with two councils of the same name + return 'Durham+County' if $area->{name} eq 'Durham County Council'; + return 'Durham+City' if $area->{name} eq 'Durham City Council'; + + my $name = $area->{name}; + $name =~ s/ (Borough|City|District|County) Council$//; + $name =~ s/ Council$//; + $name =~ s/ & / and /; + $name =~ s{/}{_}g; + $name = URI::Escape::uri_escape_utf8($name); + $name =~ s/%20/+/g; + return $name; + +} + +=item council_rss_alert_options + +Generate a set of options for council rss alerts. + +=cut + +sub council_rss_alert_options { + my $self = shift; + my $all_councils = shift; + my $c = shift; + + my %councils = map { $_ => 1 } $self->area_types(); + + my $num_councils = scalar keys %$all_councils; + + my ( @options, @reported_to_options ); + if ( $num_councils == 1 or $num_councils == 2 ) { + my ($council, $ward); + foreach (values %$all_councils) { + if ($councils{$_->{type}}) { + $council = $_; + $council->{short_name} = $self->short_name( $council ); + ( $council->{id_name} = $council->{short_name} ) =~ tr/+/_/; + } else { + $ward = $_; + $ward->{short_name} = $self->short_name( $ward ); + ( $ward->{id_name} = $ward->{short_name} ) =~ tr/+/_/; + } + } + + push @options, + { + type => 'council', + id => sprintf( 'council:%s:%s', $council->{id}, $council->{id_name} ), + text => sprintf( _('Problems within %s'), $council->{name}), + rss_text => sprintf( _('RSS feed of problems within %s'), $council->{name}), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} ), + }; + push @options, + { + type => 'ward', + id => sprintf( 'ward:%s:%s:%s:%s', $council->{id}, $ward->{id}, $council->{id_name}, $ward->{id_name} ), + rss_text => sprintf( _('RSS feed of problems within %s ward'), $ward->{name}), + text => sprintf( _('Problems within %s ward'), $ward->{name}), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ), + } if $ward; + } elsif ( $num_councils == 4 ) { +# # Two-tier council + my ($county, $district, $c_ward, $d_ward); + foreach (values %$all_councils) { + $_->{short_name} = $self->short_name( $_ ); + ( $_->{id_name} = $_->{short_name} ) =~ tr/+/_/; + if ($_->{type} eq 'CTY') { + $county = $_; + } elsif ($_->{type} eq 'DIS') { + $district = $_; + } elsif ($_->{type} eq 'CED') { + $c_ward = $_; + } elsif ($_->{type} eq 'DIW') { + $d_ward = $_; + } + } + my $district_name = $district->{name}; + my $d_ward_name = $d_ward->{name}; + my $county_name = $county->{name}; + my $c_ward_name = $c_ward->{name}; + + push @options, + { + type => 'area', + id => sprintf( 'area:%s:%s', $district->{id}, $district->{id_name} ), + text => $district_name, + rss_text => sprintf( _('RSS feed for %s'), $district_name ), + uri => $c->uri_for( '/rss/areas/' . $district->{short_name} ) + }, + { + type => 'area', + id => sprintf( 'area:%s:%s:%s:%s', $district->{id}, $d_ward->{id}, $district->{id_name}, $d_ward->{id_name} ), + text => sprintf( _('%s ward, %s'), $d_ward_name, $district_name ), + rss_text => sprintf( _('RSS feed for %s ward, %s'), $d_ward_name, $district_name ), + uri => $c->uri_for( '/rss/areas/' . $district->{short_name} . '/' . $d_ward->{short_name} ) + }, + { + type => 'area', + id => sprintf( 'area:%s:%s', $county->{id}, $county->{id_name} ), + text => $county_name, + rss_text => sprintf( _('RSS feed for %s'), $county_name ), + uri => $c->uri_for( '/rss/areas/' . $county->{short_name} ) + }, + { + type => 'area', + id => sprintf( 'area:%s:%s:%s:%s', $county->{id}, $c_ward->{id}, $county->{id_name}, $c_ward->{id_name} ), + text => sprintf( _('%s ward, %s'), $c_ward_name, $county_name ), + rss_text => sprintf( _('RSS feed for %s ward, %s'), $c_ward_name, $county_name ), + uri => $c->uri_for( '/rss/areas/' . $county->{short_name} . '/' . $c_ward->{short_name} ) + }; + + push @reported_to_options, + { + type => 'council', + id => sprintf( 'council:%s:%s', $district->{id}, $district->{id_name} ), + text => $district->{name}, + rss_text => sprintf( _('RSS feed of %s'), $district->{name}), + uri => $c->uri_for( '/rss/reports/' . $district->{short_name} ), + }, + { + type => 'ward', + id => sprintf( 'ward:%s:%s:%s:%s', $district->{id}, $d_ward->{id}, $district->{id_name}, $d_ward->{id_name} ), + rss_text => sprintf( _('RSS feed of %s, within %s ward'), $district->{name}, $d_ward->{name}), + text => sprintf( _('%s, within %s ward'), $district->{name}, $d_ward->{name}), + uri => $c->uri_for( '/rss/reports/' . $district->{short_name} . '/' . $d_ward->{short_name} ), + }, + { + type => 'council', + id => sprintf( 'council:%s:%s', $county->{id}, $county->{id_name} ), + text => $county->{name}, + rss_text => sprintf( _('RSS feed of %s'), $county->{name}), + uri => $c->uri_for( '/rss/reports/' . $county->{short_name} ), + }, + { + type => 'ward', + id => sprintf( 'ward:%s:%s:%s:%s', $county->{id}, $c_ward->{id}, $county->{id_name}, $c_ward->{id_name} ), + rss_text => sprintf( _('RSS feed of %s, within %s ward'), $county->{name}, $c_ward->{name}), + text => sprintf( _('%s, within %s ward'), $county->{name}, $c_ward->{name}), + uri => $c->uri_for( '/rss/reports/' . $county->{short_name} . '/' . $c_ward->{short_name} ), + }; + + + } else { + throw Error::Simple('An area with three tiers of council? Impossible! '. join('|',keys %$all_councils)); + } + + return ( \@options, @reported_to_options ? \@reported_to_options : undef ); +} + +=head2 generate_problem_banner + + my $banner = $c->cobrand->generate_problem_banner; + + <p id="[% banner.id %]:>[% banner.text %]</p> + +Generate id and text for banner that appears at top of problem page. + +=cut + +sub generate_problem_banner { + my ( $self, $problem ) = @_; + + my $banner = {}; + if ($problem->state eq 'confirmed' && time() - $problem->lastupdate_local->epoch > 8*7*24*60*60) { + $banner->{id} = 'unknown'; + $banner->{text} = _('This problem is old and of unknown status.'); + } + if ($problem->state eq 'fixed') { + $banner->{id} = 'fixed'; + $banner->{text} = _('This problem has been fixed') . '.'; + } + + return $banner; +} + +sub reports_council_check { + my ( $self, $c, $code ) = @_; + + if ($code =~ /^(\d\d)([a-z]{2})?([a-z]{2})?$/i) { + my $area = mySociety::MaPit::call( 'area', uc $code ); + $c->detach( 'redirect_index' ) if $area->{error}; # Given a bad/old ONS code + if (length($code) == 6) { + my $council = mySociety::MaPit::call( 'area', $area->{parent_area} ); + $c->stash->{ward} = $area; + $c->stash->{council} = $council; + } else { + $c->stash->{council} = $area; + } + $c->detach( 'redirect_area' ); + } +} + +=head2 default_photo_resize + +Size that photos are to be resized to for display. If photos aren't +to be resized then return 0; + +=cut + +sub default_photo_resize { return 0; } + +1; + diff --git a/perllib/FixMyStreet/Cobrand/EmptyHomes.pm b/perllib/FixMyStreet/Cobrand/EmptyHomes.pm new file mode 100644 index 000000000..eda0b2882 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/EmptyHomes.pm @@ -0,0 +1,178 @@ +package FixMyStreet::Cobrand::EmptyHomes; +use base 'FixMyStreet::Cobrand::Default'; + +use strict; +use warnings; + +use FixMyStreet; +use mySociety::Locale; +use Carp; + +=item + +Return the base url for this cobranded site + +=cut + +sub base_url { + my $base_url = FixMyStreet->config('BASE_URL'); + if ( $base_url !~ /emptyhomes/ ) { + $base_url =~ s/http:\/\//http:\/\/emptyhomes\./g; + } + return $base_url; +} + +sub admin_base_url { + return 'https://secure.mysociety.org/admin/emptyhomes/'; +} + +sub area_types { + return qw(DIS LBO MTD UTA LGD COI); # No CTY +} + + +sub base_url_with_lang { + my $self = shift; + my $email = shift; + + my $base = $self->base_url; + + if ($email) { + $base = $self->base_url_for_emails; + } + + my $lang = $mySociety::Locale::lang; + if ($lang eq 'cy') { + $base =~ s{http://}{$&cy.}; + } else { + $base =~ s{http://}{$&en.}; + } + return $base; +} + +=item set_lang_and_domain LANG UNICODE + +Set the language and text domain for the site based on the query and host. + +=cut + +sub set_lang_and_domain { + my ( $self, $lang, $unicode, $dir ) = @_; + my $set_lang = mySociety::Locale::negotiate_language( + 'en-gb,English,en_GB|cy,Cymraeg,cy_GB', $lang ); + mySociety::Locale::gettext_domain( 'FixMyStreet-EmptyHomes', $unicode, + $dir ); + mySociety::Locale::change(); + return $set_lang; +} + +=item site_title + +Return the title to be used in page heads + +=cut + +sub site_title { + my ($self) = @_; + return _('Report Empty Homes'); +} + +=item feed_xsl + +Return the XSL file path to be used for feeds' + +=cut + +sub feed_xsl { + my ($self) = @_; + return '/xsl.eha.xsl'; +} + +=item shorten_recency_if_new_greater_than_fixed + +For empty homes we don't want to shorten the recency + +=cut + +sub shorten_recency_if_new_greater_than_fixed { + return 0; +} + +=head2 generate_problem_banner + + my $banner = $c->cobrand->generate_problem_banner; + + <p id="[% banner.id %]:>[% banner.text %]</p> + +Generate id and text for banner that appears at top of problem page. + +=cut + +sub generate_problem_banner { + my ( $self, $problem ) = @_; + + my $banner = {}; + if ($problem->state eq 'fixed') { + $banner->{id} = 'fixed'; + $banner->{text} = _('This problem has been fixed') . '.'; + } + + return $banner; +} + +=head2 default_photo_resize + +Size that photos are to be resized to for display. If photos aren't +to be resized then return 0; + +=cut + +sub default_photo_resize { return '195x'; } + +=item council_rss_alert_options + +Generate a set of options for council rss alerts. + +=cut + +sub council_rss_alert_options { + my $self = shift; + my $all_councils = shift; + my $c = shift; + + my %councils = map { $_ => 1 } $self->area_types(); + + my $num_councils = scalar keys %$all_councils; + + my ( @options, @reported_to_options ); + my ($council, $ward); + foreach (values %$all_councils) { + $_->{short_name} = $self->short_name( $_ ); + ( $_->{id_name} = $_->{short_name} ) =~ tr/+/_/; + if ($councils{$_->{type}}) { + $council = $_; + } else { + $ward = $_; + } + } + + push @options, { + type => 'council', + id => sprintf( 'council:%s:%s', $council->{id}, $council->{id_name} ), + text => sprintf( _('Problems within %s'), $council->{name}), + rss_text => sprintf( _('RSS feed of problems within %s'), $council->{name}), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} ), + }; + push @options, { + type => 'ward', + id => sprintf( 'ward:%s:%s:%s:%s', $council->{id}, $ward->{id}, $council->{id_name}, $ward->{id_name} ), + rss_text => sprintf( _('RSS feed of problems within %s ward'), $ward->{name}), + text => sprintf( _('Problems within %s ward'), $ward->{name}), + uri => $c->uri_for( '/rss/reports/' . $council->{short_name} . '/' . $ward->{short_name} ), + }; + + return ( \@options, @reported_to_options ? \@reported_to_options : undef ); +} + +1; + diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm new file mode 100644 index 000000000..4f3b975b3 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm @@ -0,0 +1,259 @@ +package FixMyStreet::Cobrand::FiksGataMi; +use base 'FixMyStreet::Cobrand::Default'; + +use strict; +use warnings; + +use Carp; +use mySociety::MaPit; +use FixMyStreet::Geocode::OSM; + +sub country { + return 'NO'; +} + +sub set_lang_and_domain { + my ( $self, $lang, $unicode, $dir ) = @_; + my $set_lang = mySociety::Locale::negotiate_language( + 'en-gb,English,en_GB|nb,Norwegian,nb_NO', 'nb' + ); + mySociety::Locale::gettext_domain( 'FixMyStreet', $unicode, $dir ); + mySociety::Locale::change(); + return $set_lang; +} + +sub enter_postcode_text { + my ( $self ) = @_; + return _('Enter a nearby postcode, or street name and area'); +} + +# Is also adding language parameter +sub disambiguate_location { + return { + lang => 'no', + country => 'no', + }; +} + +sub area_types { + return ( 'NKO', 'NFY', 'NRA' ); +} + +sub area_min_generation { + return ''; +} + +sub admin_base_url { + return 'http://www.fiksgatami.no/admin/'; +} + +sub writetothem_url { + return 'http://www.norge.no/styresmakter/'; +} + +# 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 { + my ( $self, $uri ) = @_; + + $uri->query_param( zoom => 3 ) + if $uri->query_param('lat') && !$uri->query_param('zoom'); + + return $uri; +} + +sub geocode_postcode { + my ( $self, $s ) = @_; + + if ($s =~ /^\d{4}$/) { + my $location = mySociety::MaPit::call('postcode', $s); + if ($location->{error}) { + return { + error => $location->{code} =~ /^4/ + ? _('That postcode was not recognised, sorry.') + : $location->{error} + }; + } + return { + latitude => $location->{wgs84_lat}, + longitude => $location->{wgs84_lon}, + }; + } + return {}; +} + +sub geocoded_string_check { + my ( $self, $s ) = @_; + return 1 if $s =~ /, Norge/; + return 0; +} + +sub find_closest { + my ( $self, $latitude, $longitude ) = @_; + return FixMyStreet::Geocode::OSM::closest_road_text( $self, $latitude, $longitude ); +} + +# Used by send-reports, calling find_closest, calling OSM geocoding +sub guess_road_operator { + my ( $self, $inforef ) = @_; + + my $highway = $inforef->{highway} || "unknown"; + my $refs = $inforef->{ref} || "unknown"; + + return "Statens vegvesen" + if $highway eq "trunk" || $highway eq "primary"; + + for my $ref (split(/;/, $refs)) { + return "Statens vegvesen" + if $ref =~ m/E ?\d+/ || $ref =~ m/Fv\d+/i; + } + return ''; +} + +sub remove_redundant_councils { + my $self = shift; + my $all_councils = shift; + + # Oslo is both a kommune and a fylke, we only want to show it once + delete $all_councils->{301} # + if $all_councils->{3}; +} + +sub filter_all_council_ids_list { + my $self = shift; + my @all_councils_ids = @_; + + # as above we only want to show Oslo once + return grep { $_ != 301 } @all_councils_ids; +} + +sub short_name { + my $self = shift; + my ($area, $info) = @_; + + if ($area->{name} =~ /^(Os|Nes|V\xe5ler|Sande|B\xf8|Her\xf8y)$/) { + my $parent = $info->{$area->{parent_area}}->{name}; + return URI::Escape::uri_escape_utf8("$area->{name}, $parent"); + } + + my $name = $area->{name}; + $name =~ s/ & / and /; + $name = URI::Escape::uri_escape_utf8($name); + $name =~ s/%20/+/g; + return $name; +} + +sub council_rss_alert_options { + my $self = shift; + my $all_councils = shift; + my $c = shift; + + my ( @options, @reported_to_options, $fylke, $kommune ); + + foreach ( values %$all_councils ) { + if ( $_->{type} eq 'NKO' ) { + $kommune = $_; + } + else { + $fylke = $_; + } + } + + if ( $fylke->{id} == 3 ) { # Oslo + my $short_name = $self->short_name($fylke, $all_councils); + ( my $id_name = $short_name ) =~ tr/+/_/; + + push @options, + { + type => 'council', + id => sprintf( 'council:%s:%s', $fylke->{id}, $id_name ), + rss_text => + sprintf( _('RSS feed of problems within %s'), $fylke->{name} ), + text => sprintf( _('Problems within %s'), $fylke->{name} ), + uri => $c->uri_for( '/rss/reports', $short_name ), + }; + } + else { + my $short_kommune_name = $self->short_name($kommune, $all_councils); + ( my $id_kommune_name = $short_kommune_name ) =~ tr/+/_/; + + my $short_fylke_name = $self->short_name($fylke, $all_councils); + ( my $id_fylke_name = $short_fylke_name ) =~ tr/+/_/; + + push @options, + { + type => 'area', + id => sprintf( 'area:%s:%s', $kommune->{id}, $id_kommune_name ), + rss_text => + sprintf( _('RSS feed of %s'), $kommune->{name} ), + text => $kommune->{name}, + uri => $c->uri_for( '/rss/area', $short_kommune_name ), + }, + { + type => 'area', + id => sprintf( 'area:%s:%s', $fylke->{id}, $id_fylke_name ), + rss_text => + sprintf( _('RSS feed of %s'), $fylke->{name} ), + text => $fylke->{name}, + uri => $c->uri_for( '/rss/area', $short_fylke_name ), + }; + + push @reported_to_options, + { + type => 'council', + id => sprintf( 'council:%s:%s', $kommune->{id}, $id_kommune_name ), + rss_text => + sprintf( _('RSS feed of %s'), $kommune->{name} ), + text => $kommune->{name}, + uri => $c->uri_for( '/rss/reports', $short_kommune_name ), + }, + { + type => 'council', + id => sprintf( 'council:%s:%s', $fylke->{id}, $id_fylke_name ), + rss_text => + sprintf( _('RSS feed of %s'), $fylke->{name} ), + text => $fylke->{name}, + uri => $c->uri_for( '/rss/reports/', $short_fylke_name ), + }; + } + + return ( + \@options, @reported_to_options + ? \@reported_to_options + : undef + ); + +} + +sub reports_council_check { + my ( $self, $c, $council ) = @_; + + if ($council eq 'Oslo') { + + # There are two Oslos (kommune and fylke), we only want one of them. + $c->stash->{council} = mySociety::MaPit::call('area', 3); + return 1; + + } elsif ($council =~ /,/) { + + # Some kommunes have the same name, use the fylke name to work out which. + my ($kommune, $fylke) = split /\s*,\s*/, $council; + my @area_types = $c->cobrand->area_types; + my $areas_k = mySociety::MaPit::call('areas', $kommune, type => \@area_types); + my $areas_f = mySociety::MaPit::call('areas', $fylke, type => \@area_types); + if (keys %$areas_f == 1) { + ($fylke) = values %$areas_f; + foreach (values %$areas_k) { + if ($_->{name} eq $kommune && $_->{parent_area} == $fylke->{id}) { + $c->stash->{council} = $_; + return 1; + } + } + } + # If we're here, we've been given a bad name. + $c->detach( 'redirect_index' ); + + } +} + +1; diff --git a/perllib/FixMyStreet/Cobrand/Southampton.pm b/perllib/FixMyStreet/Cobrand/Southampton.pm new file mode 100644 index 000000000..bd461f5e2 --- /dev/null +++ b/perllib/FixMyStreet/Cobrand/Southampton.pm @@ -0,0 +1,80 @@ +package FixMyStreet::Cobrand::Southampton; +use base 'FixMyStreet::Cobrand::Default'; + +use strict; +use warnings; + +use Carp; +use URI::Escape; +use mySociety::VotingArea; + +sub site_restriction { + return ( "and council='2567'", 'southampton', { council => '2567' } ); +} + +sub problems_clause { + return { council => '2567' }; +} + +sub problems { + my $self = shift; + return $self->{c}->model('DB::Problem')->search( $self->problems_clause ); +} + +sub base_url { + my $base_url = mySociety::Config::get('BASE_URL'); + if ($base_url !~ /southampton/) { + $base_url =~ s{http://(?!www\.)}{http://southampton.}g; + $base_url =~ s{http://www\.}{http://southampton.}g; + } + return $base_url; +} + +sub site_title { + my ( $self ) = @_; + return 'Southampton City Council FixMyStreet'; +} + +sub enter_postcode_text { + my ( $self ) = @_; + return 'Enter a Southampton postcode, or street name and area'; +} + +sub council_check { + my ( $self, $params, $context ) = @_; + + my $councils = $params->{all_councils}; + my $council_match = defined $councils->{2567}; + if ($council_match) { + return 1; + } + my $url = 'http://www.fixmystreet.com/'; + $url .= 'alert' if $context eq 'alert'; + $url .= '?pc=' . URI::Escape::uri_escape_utf8($self->{c}->req->param('pc')) + if $self->{c}->req->param('pc'); + my $error_msg = "That location is not covered by Southampton. +Please visit <a href=\"$url\">the main FixMyStreet site</a>."; + return ( 0, $error_msg ); +} + +# All reports page only has the one council. +sub all_councils_report { + return 0; +} + +sub disambiguate_location { + return { + centre => '50.913822,-1.400493', + span => '0.084628,0.15701', + bounds => [ '50.871508,-1.478998', '50.956136,-1.321988' ], + }; +} + +sub recent_photos { + my ($self, $num, $lat, $lon, $dist) = @_; + $num = 2 if $num == 3; + return $self->problems->recent_photos( $num, $lat, $lon, $dist ); +} + +1; + |