diff options
author | Struan Donald <struan@exo.org.uk> | 2011-08-17 22:56:37 +0100 |
---|---|---|
committer | Struan Donald <struan@exo.org.uk> | 2011-08-17 22:56:37 +0100 |
commit | 3b5d561700b184c20e63111d4cbc1daab78e16c6 (patch) | |
tree | f0c53c6bf8d4ebf12e5388053ad856fb0d0e7b86 /perllib/FixMyStreet | |
parent | 390f8e8ad1e10f832c4323c39bed2c883744a03f (diff) | |
parent | f38b8e985697c35a62374a2f02dce2d681ef58cd (diff) |
Merge branch 'master' of ssh://git.mysociety.org/data/git/public/fixmystreet into new_statuses
Conflicts:
db/schema.sql
perllib/FixMyStreet/App/Controller/Admin.pm
perllib/FixMyStreet/DB/Result/User.pm
t/app/controller/admin.t
templates/web/default/admin/update_edit.html
web/css/core.css
Diffstat (limited to 'perllib/FixMyStreet')
25 files changed, 950 insertions, 135 deletions
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm index 68bfc728b..beea8bb11 100644 --- a/perllib/FixMyStreet/App.pm +++ b/perllib/FixMyStreet/App.pm @@ -296,6 +296,10 @@ sub send_email { $email->header_set( ucfirst($_), $vars->{$_} ) for grep { $vars->{$_} } qw( to from subject); + $email->header_set( 'Message-ID', sprintf('<fms-%s-%s@%s>', + time(), unpack('h*', random_bytes(5, 1)), $c->config->{EMAIL_DOMAIN} + ) ); + # pass the email into mySociety::Email to construct the on the wire 7bit # format - this should probably happen in the transport instead but hohum. my $email_text = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email( diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index 63d892fc4..e5c0133cf 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -175,7 +175,7 @@ sub questionnaire : Path('questionnaire') : Args(0) { ); - my %questionnaire_counts = map { $_->get_column( 'reported' ) => $_->get_column( 'questionnaire_count' ) } $questionnaires->all; + my %questionnaire_counts = map { ( $_->get_column( 'reported' ) || -1 ) => $_->get_column( 'questionnaire_count' ) } $questionnaires->all; $questionnaire_counts{1} ||= 0; $questionnaire_counts{0} ||= 0; $questionnaire_counts{total} = $questionnaire_counts{0} + $questionnaire_counts{1}; @@ -450,9 +450,11 @@ sub search_reports : Path('search_reports') { } ); + # we need to pass this in as an array as we can't + # query the object in the template as the quoting + # will have been turned off $c->stash->{problems} = [ $problems->all ]; - $c->stash->{edit_council_contacts} = 1 if ( grep {$_ eq 'councilcontacts'} keys %{$c->stash->{allowed_pages}}); @@ -501,6 +503,7 @@ sub report_edit : Path('report_edit') : Args(1) { $c->forward('get_token'); $c->forward('check_page_allowed'); + $c->forward('check_email_for_abuse', [ $problem->user->email ] ); $c->stash->{updates} = [ $c->model('DB::Comment') @@ -517,6 +520,17 @@ sub report_edit : Path('report_edit') : Args(1) { $c->forward( 'log_edit', [ $id, 'problem', 'resend' ] ); } + elsif ( $c->req->param('flaguser') ) { + $c->forward('flag_user'); + $c->stash->{problem}->discard_changes; + } + elsif ( $c->req->param('removeuserflag') ) { + $c->forward('remove_user_flag'); + $c->stash->{problem}->discard_changes; + } + elsif ( $c->req->param('banuser') ) { + $c->forward('ban_user'); + } elsif ( $c->req->param('submit') ) { $c->forward('check_token'); @@ -536,12 +550,15 @@ sub report_edit : Path('report_edit') : Args(1) { $done = 1; } + my $flagged = $c->req->param('flagged') ? 1 : 0; + # do this here so before we update the values in problem if ( $c->req->param('anonymous') ne $problem->anonymous || $c->req->param('name') ne $problem->name || $c->req->param('email') ne $problem->user->email || $c->req->param('title') ne $problem->title - || $c->req->param('detail') ne $problem->detail ) + || $c->req->param('detail') ne $problem->detail + || $flagged != $problem->flagged ) { $edited = 1; } @@ -551,6 +568,7 @@ sub report_edit : Path('report_edit') : Args(1) { $problem->detail( $c->req->param('detail') ); $problem->state( $c->req->param('state') ); $problem->name( $c->req->param('name') ); + $problem->flagged( $flagged ); if ( $c->req->param('email') ne $problem->user->email ) { my $user = $c->model('DB::User')->find_or_create( @@ -598,7 +616,6 @@ sub report_edit : Path('report_edit') : Args(1) { return 1; } - sub search_users: Path('search_users') : Args(0) { my ( $self, $c ) = @_; @@ -629,6 +646,129 @@ sub search_users: Path('search_users') : Args(0) { return 1; } +sub update_edit : Path('update_edit') : Args(1) { + my ( $self, $c, $id ) = @_; + + my ( $site_res_sql, $site_key, $site_restriction ) = + $c->cobrand->site_restriction; + my $update = $c->model('DB::Comment')->search( + { + id => $id, + %{$site_restriction}, + } + )->first; + + $c->detach( '/page_error_404_not_found', + [ _('The requested URL was not found on this server.') ] ) + unless $update; + + $c->forward('get_token'); + $c->forward('check_page_allowed'); + + $c->stash->{update} = $update; + + $c->forward('check_email_for_abuse', [ $update->user->email ] ); + + if ( $c->req->param('banuser') ) { + $c->forward('ban_user'); + } + elsif ( $c->req->param('flaguser') ) { + $c->forward('flag_user'); + $c->stash->{update}->discard_changes; + } + elsif ( $c->req->param('removeuserflag') ) { + $c->forward('remove_user_flag'); + $c->stash->{update}->discard_changes; + } + elsif ( $c->req->param('submit') ) { + $c->forward('check_token'); + + my $old_state = $update->state; + my $new_state = $c->req->param('state'); + + my $edited = 0; + + # $update->name can be null which makes ne unhappy + my $name = $update->name || ''; + + if ( $c->req->param('name') ne $name + || $c->req->param('email') ne $update->user->email + || $c->req->param('anonymous') ne $update->anonymous + || $c->req->param('text') ne $update->text ){ + $edited = 1; + } + + if ( $c->req->param('remove_photo') ) { + $update->photo(undef); + } + + $update->name( $c->req->param('name') || '' ); + $update->text( $c->req->param('text') ); + $update->anonymous( $c->req->param('anonymous') ); + $update->state( $c->req->param('state') ); + + if ( $c->req->param('email') ne $update->user->email ) { + my $user = + $c->model('DB::User') + ->find_or_create( { email => $c->req->param('email') } ); + + $user->insert unless $user->in_storage; + $update->user($user); + } + + if ( $new_state eq 'confirmed' and $old_state eq 'unconfirmed' ) { + $update->confirmed( \'ms_current_timestamp()' ); + } + + $update->update; + + $c->stash->{status_message} = '<p><em>' . _('Updated!') . '</em></p>'; + + # If we're hiding an update, see if it marked as fixed and unfix if so + if ( $new_state eq 'hidden' && $update->mark_fixed ) { + if ( $update->problem->state eq 'fixed' ) { + $update->problem->state('confirmed'); + $update->problem->update; + } + + $c->stash->{status_message} .= + '<p><em>' . _('Problem marked as open.') . '</em></p>'; + } + + if ( $new_state ne $old_state ) { + $c->forward( 'log_edit', + [ $update->id, 'update', 'state_change' ] ); + } + + if ($edited) { + $c->forward( 'log_edit', [ $update->id, 'update', 'edit' ] ); + } + + } + + return 1; +} + +sub search_abuse : Path('search_abuse') : Args(0) { + my ( $self, $c ) = @_; + + $c->forward('check_page_allowed'); + + my $search = $c->req->param('search'); + + if ($search) { + my $emails = $c->model('DB::Abuse')->search( + { + email => { ilike => "\%$search\%" } + } + ); + + $c->stash->{emails} = [ $emails->all ]; + } + + return 1; +} + sub user_edit : Path('user_edit') : Args(1) { my ( $self, $c, $id ) = @_; @@ -674,6 +814,89 @@ sub user_edit : Path('user_edit') : Args(1) { return 1; } +sub list_flagged : Path('list_flagged') : Args(0) { + my ( $self, $c ) = @_; + + $c->forward('check_page_allowed'); + + my $problems = $c->model('DB::Problem')->search( { flagged => 1 } ); + + # pass in as array ref as using same template as search_reports + # which has to use an array ref for sql quoting reasons + $c->stash->{problems} = [ $problems->all ]; + + my $users = $c->model('DB::User')->search( { flagged => 1 } ); + + $c->stash->{users} = $users; + + return 1; +} + +sub stats : Path('stats') : Args(0) { + my ( $self, $c ) = @_; + + $c->forward('check_page_allowed'); + + if ( $c->req->param('getcounts') ) { + + my ( $start_date, $end_date, @errors ); + + eval { + $start_date = DateTime->new( + year => $c->req->param('start_date_year'), + month => $c->req->param('start_date_month'), + day => $c->req->param('start_date_day'), + ); + }; + + push @errors, _('Invalid start date') if $@; + + eval { + $end_date = DateTime->new( + year => $c->req->param('end_date_year'), + month => $c->req->param('end_date_month'), + day => $c->req->param('end_date_day'), + ); + }; + + push @errors, _('Invalid end date') if $@; + + $c->stash->{errors} = \@errors; + $c->stash->{start_date} = $start_date; + $c->stash->{end_date} = $end_date; + + $c->stash->{unconfirmed} = $c->req->param('unconfirmed') eq 'on' ? 1 : 0; + + return 1 if @errors; + + my $field = 'confirmed'; + + $field = 'created' if $c->req->param('unconfirmed'); + + my $one_day = DateTime::Duration->new( days => 1 ); + + my $p = $c->model('DB::Problem')->search( + { + -AND => [ + $field => { '>=', $start_date}, + $field => { '<=', $end_date + $one_day }, + ], + }, + { + select => [ 'state', { 'count' => 'me.id' } ], + as => [qw/state count/], + group_by => [ 'state' ], + order_by => [ 'state' ], + } + ); + + # in case the total_report count is 0 + $c->stash->{show_count} = 1; + $c->stash->{states} = $p; + } + + return 1; +} =head2 set_allowed_pages @@ -695,11 +918,15 @@ sub set_allowed_pages : Private { 'timeline' => [_('Timeline'), 3], 'questionnaire' => [_('Survey Results'), 4], 'search_users' => [_('Search Users'), 5], - 'council_contacts' => [undef, undef], - 'council_edit' => [undef, undef], - 'report_edit' => [undef, undef], - 'update_edit' => [undef, undef], + 'search_abuse' => [_('Search Abuse'), 5], + 'list_flagged' => [_('List Flagged'), 6], + 'stats' => [_('Stats'), 6], 'user_edit' => [undef, undef], + 'council_contacts' => [undef, undef], + 'council_edit' => [undef, undef], + 'report_edit' => [undef, undef], + 'update_edit' => [undef, undef], + 'abuse_edit' => [undef, undef], } } @@ -769,94 +996,115 @@ sub log_edit : Private { )->insert(); } -sub update_edit : Path('update_edit') : Args(1) { - my ( $self, $c, $id ) = @_; +=head2 ban_user - my ( $site_res_sql, $site_key, $site_restriction ) = - $c->cobrand->site_restriction; - my $update = $c->model('DB::Comment')->search( - { - id => $id, - %{$site_restriction}, - } - )->first; +Add the email address in the email param of the request object to +the abuse table if they are not already in there and sets status_message +accordingly - $c->detach( '/page_error_404_not_found', - [ _('The requested URL was not found on this server.') ] ) - unless $update; +=cut - $c->forward('get_token'); - $c->forward('check_page_allowed'); +sub ban_user : Private { + my ( $self, $c ) = @_; - $c->stash->{update} = $update; + my $email = $c->req->param('email'); - my $status_message = ''; - if ( $c->req->param('submit') ) { - $c->forward('check_token'); + return unless $email; - my $old_state = $update->state; - my $new_state = $c->req->param('state'); + my $abuse = $c->model('DB::Abuse')->find_or_new({ email => $email }); - my $edited = 0; + if ( $abuse->in_storage ) { + $c->stash->{status_message} = _('Email already in abuse list'); + } else { + $abuse->insert; + $c->stash->{status_message} = _('Email added to abuse list'); + } - # $update->name can be null which makes ne unhappy - my $name = $update->name || ''; + $c->stash->{email_in_abuse} = 1; - if ( $c->req->param('name') ne $name - || $c->req->param('email') ne $update->user->email - || $c->req->param('anonymous') ne $update->anonymous - || $c->req->param('text') ne $update->text ){ - $edited = 1; - } + return 1; +} - if ( $c->req->param('remove_photo') ) { - $update->photo(undef); - } +=head2 flag_user - $update->name( $c->req->param('name') || '' ); - $update->text( $c->req->param('text') ); - $update->anonymous( $c->req->param('anonymous') ); - $update->state( $c->req->param('state') ); +Sets the flag on a user with the given email - if ( $c->req->param('email') ne $update->user->email ) { - my $user = - $c->model('DB::User') - ->find_or_create( { email => $c->req->param('email') } ); +=cut - $user->insert unless $user->in_storage; - $update->user($user); - } +sub flag_user : Private { + my ( $self, $c ) = @_; - $update->update; + my $email = $c->req->param('email'); - $status_message = '<p><em>' . _('Updated!') . '</em></p>'; + return unless $email; - # If we're hiding an update, see if it marked as fixed and unfix if so - if ( $new_state eq 'hidden' && $update->mark_fixed ) { - if ( $update->problem->is_fixed ) { - $update->problem->state('confirmed'); - $update->problem->update; - } + my $user = $c->model('DB::User')->find({ email => $email }); - $status_message .= - '<p><em>' . _('Problem marked as open.') . '</em></p>'; - } + if ( !$user ) { + $c->stash->{status_message} = _('Could not find user'); + } else { + $user->flagged(1); + $user->update; + $c->stash->{status_message} = _('User flagged'); + } - if ( $new_state ne $old_state ) { - $c->forward( 'log_edit', - [ $update->id, 'update', 'state_change' ] ); - } + $c->stash->{user_flagged} = 1; - if ($edited) { - $c->forward( 'log_edit', [ $update->id, 'update', 'edit' ] ); - } + return 1; +} + +=head2 remove_user_flag + +Remove the flag on a user with the given email + +=cut +sub remove_user_flag : Private { + my ( $self, $c ) = @_; + + my $email = $c->req->param('email'); + + return unless $email; + + my $user = $c->model('DB::User')->find({ email => $email }); + + if ( !$user ) { + $c->stash->{status_message} = _('Could not find user'); + } else { + $user->flagged(0); + $user->update; + $c->stash->{status_message} = _('User flag removed'); } - $c->stash->{status_message} = $status_message; return 1; } + +=head2 check_email_for_abuse + + $c->forward('check_email_for_abuse', [ $email ] ); + +Checks if $email is in the abuse table and sets email_in_abuse accordingly + +=cut + +sub check_email_for_abuse : Private { + my ( $self, $c, $email ) =@_; + + my $is_abuse = $c->model('DB::Abuse')->find({ email => $email }); + + $c->stash->{email_in_abuse} = 1 if $is_abuse; + + return 1; +} + +=head2 check_page_allowed + +Checks if the current catalyst action is in the list of allowed pages and +if not then redirects to 404 error page. + +=cut + sub check_page_allowed : Private { my ( $self, $c ) = @_; diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index 660585454..d3a4500c6 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -172,7 +172,7 @@ sub display_location : Private { # get the map features my ( $on_map_all, $on_map, $around_map, $distance ) = - FixMyStreet::Map::map_features( $c, $latitude, $longitude, + FixMyStreet::Map::map_features( $c, $short_latitude, $short_longitude, $interval ); # copy the found reports to the stash @@ -199,8 +199,8 @@ sub display_location : Private { $c->stash->{page} = 'around'; # So the map knows to make clickable pins, update on pan FixMyStreet::Map::display_map( $c, - latitude => $latitude, - longitude => $longitude, + latitude => $short_latitude, + longitude => $short_longitude, clickable => 1, pins => \@pins, ); @@ -235,14 +235,23 @@ the map. sub ajax : Path('/ajax') { my ( $self, $c ) = @_; + $c->res->content_type('text/javascript; charset=utf-8'); + + unless ( $c->req->param('bbox') ) { + $c->res->status(404); + $c->res->body(''); + return; + } + + # assume this is not cacheable - may need to be more fine-grained later + $c->res->header( 'Cache_Control' => 'max-age=0' ); + # how far back should we go? my $all_pins = $c->req->param('all_pins') ? 1 : undef; my $interval = $all_pins ? undef : $c->cobrand->on_map_default_max_pin_age; # Need to be the class that can handle it - if ($c->req->param('bbox')) { - FixMyStreet::Map::set_map_class( 'OSM' ); - } + FixMyStreet::Map::set_map_class( 'OSM' ); # extract the data from the map my ( $pins, $on_map, $around_map, $dist ) = @@ -260,7 +269,7 @@ sub ajax : Path('/ajax') { ); # JSON encode the response - my $body = JSON->new->utf8(1)->pretty(1)->encode( + my $body = JSON->new->utf8(1)->encode( { pins => $pins, current => $on_map_list_html, @@ -268,16 +277,7 @@ sub ajax : Path('/ajax') { } ); - # assume this is not cacheable - may need to be more fine-grained later - $c->res->content_type('text/javascript; charset=utf-8'); - $c->res->header( 'Cache_Control' => 'max-age=0' ); - - if ( $c->req->param('bbox') ) { - $c->res->body($body); - } else { - # The JS needs the surrounding brackets for Tilma - $c->res->body("($body)"); - } + $c->res->body($body); } __PACKAGE__->meta->make_immutable; diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm index 88ac4987f..f28d37989 100644 --- a/perllib/FixMyStreet/App/Controller/Contact.pm +++ b/perllib/FixMyStreet/App/Controller/Contact.pm @@ -39,6 +39,8 @@ Handle contact us form submission sub submit : Path('submit') : Args(0) { my ( $self, $c ) = @_; + $c->res->redirect( '/contact' ) and return unless $c->req->method eq 'POST'; + return unless $c->forward('setup_request') && $c->forward('determine_contact_type') @@ -162,6 +164,10 @@ sub prepare_params_for_email : Private { $c->stash->{problem}->id, $problem_url, $admin_url ); + + # flag this so it's automatically listed in the admin interface + $c->stash->{problem}->flagged(1); + $c->stash->{problem}->update; } return 1; diff --git a/perllib/FixMyStreet/App/Controller/Council.pm b/perllib/FixMyStreet/App/Controller/Council.pm index 35e3d0d11..48248e4fe 100644 --- a/perllib/FixMyStreet/App/Controller/Council.pm +++ b/perllib/FixMyStreet/App/Controller/Council.pm @@ -48,12 +48,15 @@ sub load_and_check_councils : Private { @area_types = $c->cobrand->area_types(); } + my $short_latitude = Utils::truncate_coordinate($latitude); + my $short_longitude = Utils::truncate_coordinate($longitude); + # TODO: I think we want in_gb_locale around the MaPit line, needs testing my $all_councils; if ( $c->stash->{fetch_all_areas} ) { my %area_types = map { $_ => 1 } @area_types; my $all_areas = - mySociety::MaPit::call( 'point', "4326/$longitude,$latitude" ); + mySociety::MaPit::call( 'point', "4326/$short_longitude,$short_latitude" ); $c->stash->{all_areas} = $all_areas; $all_councils = { map { $_ => $all_areas->{$_} } @@ -62,7 +65,7 @@ sub load_and_check_councils : Private { }; } else { $all_councils = - mySociety::MaPit::call( 'point', "4326/$longitude,$latitude", + mySociety::MaPit::call( 'point', "4326/$short_longitude,$short_latitude", type => \@area_types ); } diff --git a/perllib/FixMyStreet/App/Controller/Location.pm b/perllib/FixMyStreet/App/Controller/Location.pm index 9f8260768..df8a090c2 100644 --- a/perllib/FixMyStreet/App/Controller/Location.pm +++ b/perllib/FixMyStreet/App/Controller/Location.pm @@ -76,12 +76,12 @@ sub determine_location_from_pc : Private { # $error doubles up to return multiple choices by being an array if ( ref($error) eq 'ARRAY' ) { - @$error = map { - decode_utf8($_); - s/, United Kingdom//; - s/, UK//; - $_; - } @$error; + foreach (@$error) { + my $a = decode_utf8($_->{address}); + $a =~ s/, United Kingdom//; + $a =~ s/, UK//; + $_->{address} = $a; + } $c->stash->{possible_location_matches} = $error; return; } diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm index 19b3ffee0..1021f7056 100644 --- a/perllib/FixMyStreet/App/Controller/My.pm +++ b/perllib/FixMyStreet/App/Controller/My.pm @@ -30,8 +30,12 @@ sub my : Path : Args(0) { my $pins = []; my $problems = {}; - my $rs = $c->user->problems->search( undef, - { rows => 50 } )->page( $p_page ); + my $rs = $c->user->problems->search( { + state => [ 'confirmed', 'fixed' ], + }, { + order_by => { -desc => 'confirmed' }, + rows => 50 + } )->page( $p_page ); while ( my $problem = $rs->next ) { push @$pins, { @@ -48,7 +52,10 @@ sub my : Path : Args(0) { $rs = $c->user->comments->search( { state => 'confirmed' }, - { rows => 50 } )->page( $u_page ); + { + order_by => { -desc => 'confirmed' }, + rows => 50 + } )->page( $u_page ); my @updates = $rs->all; $c->stash->{updates} = \@updates; $c->stash->{updates_pager} = $rs->pager; diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm new file mode 100644 index 000000000..459ce12c9 --- /dev/null +++ b/perllib/FixMyStreet/App/Controller/Open311.pm @@ -0,0 +1,458 @@ +package FixMyStreet::App::Controller::Open311; + +use utf8; +use Moose; +use namespace::autoclean; + +use JSON; +use XML::Simple; +use DateTime::Format::W3CDTF; + +BEGIN { extends 'Catalyst::Controller'; } + +=head1 NAME + +FixMyStreet::App::Controller::Open311 - Catalyst Controller + +=head1 DESCRIPTION + +Open311 server API + +Open311 server API for Open311 clients + +http://open311.org/ +http://wiki.open311.org/GeoReport_v2 +http://fixmystreet.org.nz/api +http://seeclickfix.com/open311/ + +Issues with Open311 + * no way to specify which languages are understood by the + recipients. some lang=nb,nn setting should be available. + * not obvious how to handle generic requests (ie without lat/lon + values). + * should service IDs be numeric or not? Spec do not say, and all + examples I find use numbers. + * missing way to search for reports near a location using lat/lon + * report attributes lack title field. + * missing way to provide updates information for a request + * should support GeoRSS output as well as json and home made XML + +=head1 METHODS + +=cut + +=head2 index + +Displays some summary information for the requests. + +=cut + +sub index : Path : Args(0) { + my ( $self, $c ) = @_; + # don't need to do anything here - should just pass through. +} + +sub old_uri : Regex('^open311\.cgi') : Args(0) { + my ( $self, $c ) = @_; + ( my $new = $c->req->path ) =~ s/open311.cgi/open311/; + $c->res->redirect( "/$new", 301); +} + +=head2 discovery + +http://search.cpan.org/~bobtfish/Catalyst-Manual-5.8007/lib/Catalyst/Manual/Intro.pod + +=cut + +sub discovery_v2 : LocalRegex('^v2/discovery.(xml|json|html)$') : Args(0) { + my ( $self, $c ) = @_; + $c->stash->{format} = $c->req->captures->[0]; + $c->forward( 'get_discovery' ); +} + +sub services_v2 : LocalRegex('^v2/services.(xml|json|html)$') : Args(0) { + my ( $self, $c ) = @_; + $c->stash->{format} = $c->req->captures->[0]; + $c->forward( 'get_services' ); +} + +sub requests_v2 : LocalRegex('^v2/requests.(xml|json|html|rss)$') : Args(0) { + my ( $self, $c ) = @_; + $c->stash->{format} = $c->req->captures->[0]; + $c->forward( 'get_requests' ); +} + +sub request_v2 : LocalRegex('^v2/requests/(\d+).(xml|json|html)$') : Args(0) { + my ( $self, $c ) = @_; + $c->stash->{id} = $c->req->captures->[0]; + $c->stash->{format} = $c->req->captures->[1]; + $c->forward( 'get_request' ); +} + +sub error : Private { + my ( $self, $c, $error ) = @_; + $c->stash->{error} = "ERROR: $error"; + $c->stash->{template} = 'open311/index.html'; +} + +# Example +# http://sandbox.georeport.org/tools/discovery/discovery.xml +sub get_discovery : Private { + my ( $self, $c ) = @_; + + my $contact_email = $c->config->{CONTACT_EMAIL}; + my $prod_url = 'http://www.fiksgatami.no/open311'; + my $test_url = 'http://fiksgatami-dev.nuug.no/open311'; + my $prod_changeset = '2011-04-08T00:00:00Z'; + my $test_changeset = $prod_changeset; + my $spec_url = 'http://wiki.open311.org/GeoReport_v2'; + my $info = + { + 'contact' => ["Send email to $contact_email."], + 'changeset' => [$prod_changeset], + # XXX rewrite to match + 'key_service' => ["Read access is open to all according to our \u003Ca href='/open_data' target='_blank'\u003Eopen data license\u003C/a\u003E. For write access either: 1. return the 'guid' cookie on each call (unique to each client) or 2. use an api key from a user account which can be generated here: http://seeclickfix.com/register The unversioned url will always point to the latest supported version."], + 'max_requests' => [ $c->config->{RSS_LIMIT} ], + 'endpoints' => [ + { + 'endpoint' => [ + { + 'formats' => [ + {'format' => [ 'text/xml', + 'application/json', + 'text/html' ] + } + ], + 'specification' => [ $spec_url ], + 'changeset' => [ $prod_changeset ], + 'url' => [ $prod_url ], + 'type' => [ 'production' ] + }, + { + 'formats' => [ + { + 'format' => [ 'text/xml', + 'application/json', + 'text/html' ] + } + ], + 'specification' => [ $spec_url ], + 'changeset' => [ $test_changeset ], + 'url' => [ $test_url ], + 'type' => [ 'test' ] + } + ] + } + ] + }; + $c->forward( 'format_output', [ { + 'discovery' => $info + } ] ); +} + +# Example +# http://seeclickfix.com/open311/services.html?lat=32.1562864999991&lng=-110.883806 +sub get_services : Private { + my ( $self, $c ) = @_; + + my $jurisdiction_id = $c->req->param('jurisdiction_id') || ''; + my $lat = $c->req->param('lat') || ''; + my $lon = $c->req->param('long') || ''; + + # Look up categories for this council or councils + my $categories = $c->model('DB::Contact')->not_deleted; + + if ($lat || $lon) { + my @area_types = $c->cobrand->area_types; + my $all_councils = mySociety::MaPit::call('point', + "4326/$lon,$lat", + type => \@area_types); + $categories = $categories->search( { + area_id => [ keys %$all_councils ], + } ); + } + + my @categories = $categories->search( undef, { + columns => [ 'category' ], + distinct => 1, + } )->all; + + my @services; + for my $categoryref ( sort { $a->category cmp $b->category } + @categories) { + my $categoryname = $categoryref->category; + push(@services, + { + # FIXME Open311 v2 seem to require all three, and we + # only have one value. + 'service_name' => [ $categoryname ], + 'description' => [ $categoryname ], + 'service_code' => [ $categoryname ], + 'metadata' => [ 'false' ], + 'type' => [ 'realtime' ], +# 'group' => [ '' ], +# 'keywords' => [ '' ], + } + ); + } + $c->forward( 'format_output', [ { + 'services' => [ { + 'service' => \@services + } ] + } ] ); +} + + +sub output_requests : Private { + my ( $self, $c, $criteria, $limit ) = @_; + $limit = $c->config->{RSS_LIMIT} + unless $limit && $limit <= $c->config->{RSS_LIMIT}; + + my $attr = { + order_by => { -desc => 'confirmed' }, + rows => $limit + }; + + # Look up categories for this council or councils + my $problems = $c->cobrand->problems->search( $criteria, $attr ); + + my %statusmap = ( 'fixed' => 'closed', + 'confirmed' => 'open'); + + my @problemlist; + my @councils; + while ( my $problem = $problems->next ) { + my $id = $problem->id; + + $problem->service( 'Web interface' ) unless $problem->service; + + if ($problem->council) { + (my $council = $problem->council) =~ s/\|.*//g; + my @council_ids = split(/,/, $council); + push(@councils, @council_ids); + $problem->council( \@council_ids ); + } + + $problem->state( $statusmap{$problem->state} ); + + my $request = + { + 'service_request_id' => [ $id ], + 'title' => [ $problem->title ], # Not in Open311 v2 + 'detail' => [ $problem->detail ], # Not in Open311 v2 + 'description' => [ $problem->title .': ' . $problem->detail ], + 'lat' => [ $problem->latitude ], + 'long' => [ $problem->longitude ], + 'status' => [ $problem->state ], +# 'status_notes' => [ {} ], + 'requested_datetime' => [ w3date($problem->confirmed_local) ], + 'updated_datetime' => [ w3date($problem->lastupdate_local) ], +# 'expected_datetime' => [ {} ], +# 'address' => [ {} ], +# 'address_id' => [ {} ], + 'service_code' => [ $problem->category ], + 'service_name' => [ $problem->category ], +# 'service_notice' => [ {} ], + 'agency_responsible' => $problem->council , # FIXME Not according to Open311 v2 +# 'zipcode' => [ {} ], + 'interface_used' => [ $problem->service ], # Not in Open311 v2 + }; + + if ( !$problem->anonymous ) { + # Not in Open311 v2 + $request->{'requestor_name'} = [ $problem->name ]; + } + if ( $problem->whensent ) { + # Not in Open311 v2 + $request->{'agency_sent_datetime'} = + [ w3date($problem->whensent_local) ]; + } + + # Extract number of updates + my $updates = $problem->comments->search( + { state => 'confirmed' }, + )->count; + if ($updates) { + # Not in Open311 v2 + $request->{'comment_count'} = [ $updates ]; + } + + my $display_photos = $c->cobrand->allow_photo_display; + if ($display_photos && $problem->photo) { + my $url = $c->cobrand->base_url(); + my $imgurl = $url . "/photo?id=$id"; + $request->{'media_url'} = [ $imgurl ]; + } + push(@problemlist, $request); + } + my $areas_info = mySociety::MaPit::call('areas', \@councils); + foreach my $request (@problemlist) { + if ($request->{agency_responsible}) { + my @council_names = map { $areas_info->{$_}->{name} } @{$request->{agency_responsible}} ; + $request->{agency_responsible} = + [ {'recipient' => [ @council_names ] } ]; + } + } + $c->forward( 'format_output', [ { + 'requests' => [ { + 'request' => \@problemlist + } ] + } ] ); +} + +sub get_requests : Private { + my ( $self, $c ) = @_; + + $c->forward( 'is_jurisdiction_id_ok' ); + + my $max_requests = $c->req->param('max_requests') || 0; + + # Only provide access to the published reports + my $criteria = { + state => [ 'fixed', 'confirmed' ] + }; + + my %rules = ( + service_request_id => [ '=', 'id' ], + service_code => [ '=', 'category' ], + status => [ '=', 'state' ], + start_date => [ '>=', 'confirmed' ], + end_date => [ '<', 'confirmed' ], + agency_responsible => [ '~', 'council' ], + interface_used => [ '=', 'service' ], + has_photo => [ '=', 'photo' ], + ); + for my $param (keys %rules) { + my $value = $c->req->param($param); + next unless $value; + my $op = $rules{$param}[0]; + my $key = $rules{$param}[1]; + if ( 'status' eq $param ) { + $value = { + 'open' => 'confirmed', + 'closed' => 'fixed' + }->{$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; + $c->detach( 'error', [ + sprintf(_('Incorrect has_photo value "%s"'), + $value) + ] ) + unless 'true' eq $value || 'false' eq $value; + } elsif ( 'interface_used' eq $param ) { + $value = undef if 'Web interface' eq $value; + } + $criteria->{$key} = { $op, $value }; + } + + if ('rss' eq $c->stash->{format}) { + $c->stash->{type} = 'new_problems'; + $c->forward( '/rss/lookup_type' ); + $c->forward( 'rss_query', [ $criteria, $max_requests ] ); + $c->forward( '/rss/generate' ); + } else { + $c->forward( 'output_requests', [ $criteria, $max_requests ] ); + } +} + +sub rss_query : Private { + my ( $self, $c, $criteria, $limit ) = @_; + $limit = $c->config->{RSS_LIMIT} + unless $limit && $limit <= $c->config->{RSS_LIMIT}; + + my $attr = { + result_class => 'DBIx::Class::ResultClass::HashRefInflator', + order_by => { -desc => 'confirmed' }, + rows => $limit + }; + + my $problems = $c->cobrand->problems->search( $criteria, $attr ); + $c->stash->{problems} = $problems; +} + +# Example +# http://seeclickfix.com/open311/requests/1.xml?jurisdiction_id=sfgov.org +sub get_request : Private { + my ( $self, $c ) = @_; + my $format = $c->stash->{format}; + my $id = $c->stash->{id}; + + $c->forward( 'is_jurisdiction_id_ok' ); + + if ('html' eq $format) { + my $base_url = $c->cobrand->base_url(); + $c->res->redirect($base_url . "/report/$id"); + return; + } + + my $criteria = { + state => [ 'fixed', 'confirmed' ], + id => $id, + }; + $c->forward( 'output_requests', [ $criteria ] ); +} + +sub format_output : Private { + my ( $self, $c, $hashref ) = @_; + my $format = $c->stash->{format}; + if ('json' eq $format) { + $c->res->content_type('application/json; charset=utf-8'); + $c->res->body( encode_json($hashref) ); + } elsif ('xml' eq $format) { + $c->res->content_type('application/xml; charset=utf-8'); + $c->res->body( XMLout($hashref, RootName => undef) ); + } else { + $c->detach( 'error', [ + sprintf(_('Invalid format %s specified.'), $format) + ] ); + } +} + +sub is_jurisdiction_id_ok : Private { + my ( $self, $c ) = @_; + unless (my $jurisdiction_id = $c->req->param('jurisdiction_id')) { + $c->detach( 'error', [ _('Missing jurisdiction_id') ] ); + } +} + +# Input: DateTime object +# Output: 2011-04-23T10:28:55+02:00 +# FIXME Need generic solution to find time zone +sub w3date : Private { + my $datestr = shift; + return unless $datestr; + return DateTime::Format::W3CDTF->format_datetime($datestr); +} + +=head1 AUTHOR + +Copyright (c) 2011 Petter Reinholdtsen, some rights reserved. +Email: pere@hungry.com + +=head1 LICENSE + +This library is free software. You can redistribute it and/or modify +it under the GPL v2 or later. + +=cut + +__PACKAGE__->meta->make_immutable; + +1; diff --git a/perllib/FixMyStreet/App/Controller/Questionnaire.pm b/perllib/FixMyStreet/App/Controller/Questionnaire.pm index d9bdb7108..a8bdca7a4 100755 --- a/perllib/FixMyStreet/App/Controller/Questionnaire.pm +++ b/perllib/FixMyStreet/App/Controller/Questionnaire.pm @@ -261,7 +261,7 @@ sub process_questionnaire : Private { } # Sent here from email token action. Simply load and display questionnaire. -sub index : Private { +sub show : Private { my ( $self, $c ) = @_; $c->forward( 'check_questionnaire' ); $c->forward( 'display' ); diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 346dfb377..ffbb5a161 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -98,6 +98,33 @@ sub report_new : Path : Args(0) { $c->forward('redirect_or_confirm_creation'); } +sub report_form_ajax : Path('ajax') : Args(0) { + my ( $self, $c ) = @_; + + $c->forward('initialize_report'); + + # work out the location for this report and do some checks + # XXX We don't want to do this here if this actually happens! + return $c->forward('redirect_to_around') + unless $c->forward('determine_location'); + + $c->forward('setup_categories_and_councils'); + + # render templates to get the html + my $category = $c->view('Web')->render( $c, 'report/new/category.html'); + my $councils_text = $c->view('Web')->render( $c, 'report/new/councils_text.html'); + + my $body = JSON->new->utf8(1)->encode( + { + councils_text => $councils_text, + category => $category, + } + ); + + $c->res->content_type('application/json; charset=utf-8'); + $c->res->body($body); +} + =head2 report_import Action to accept report creations from iPhones and other mobile apps. URL is @@ -198,7 +225,7 @@ sub report_import : Path('/import') { # find or create the user my $report_user = $c->model('DB::User')->find_or_create( { - email => $input{email}, + email => lc $input{email}, name => $input{name}, phone => $input{phone} } @@ -907,7 +934,7 @@ sub generate_map : Private { ( $c->stash->{latitude}, $c->stash->{longitude} ); # Don't do anything if the user skipped the map - unless ( $c->req->param('skipped') ) { + if ( $c->stash->{report}->used_map ) { $c->stash->{page} = 'new'; FixMyStreet::Map::display_map( $c, diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 821b650ed..bf270a3b2 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -124,7 +124,9 @@ sub ward : Path : Args(2) { # List of wards unless ($c->stash->{ward}) { - my $children = mySociety::MaPit::call('area/children', $c->stash->{council}->{id} ); + my $children = mySociety::MaPit::call('area/children', $c->stash->{council}->{id}, + type => $mySociety::VotingArea::council_child_types, + ); foreach (values %$children) { $_->{url} = $c->uri_for( $c->stash->{council_url} . '/' . $c->cobrand->short_name( $_ ) @@ -269,9 +271,9 @@ sub ward_check : Private { type => $mySociety::VotingArea::council_child_types, min_generation => $c->cobrand->area_min_generation ); - foreach my $id (sort keys %$qw) { - if ($qw->{$id}->{parent_area} == $council->{id}) { - $c->stash->{ward} = $qw->{$id}; + foreach my $area (sort { $a->{name} cmp $b->{name} } values %$qw) { + if ($area->{parent_area} == $council->{id}) { + $c->stash->{ward} = $area; return; } } diff --git a/perllib/FixMyStreet/App/Controller/Rss.pm b/perllib/FixMyStreet/App/Controller/Rss.pm index 78793d9c1..45a16a9dd 100755 --- a/perllib/FixMyStreet/App/Controller/Rss.pm +++ b/perllib/FixMyStreet/App/Controller/Rss.pm @@ -151,12 +151,21 @@ sub local_problems_ll : Private { sub output : Private { my ( $self, $c ) = @_; + $c->forward( 'lookup_type' ); + $c->forward( 'query_main' ); + $c->forward( 'generate' ); +} + +sub lookup_type : Private { + my ( $self, $c ) = @_; $c->stash->{alert_type} = $c->model('DB::AlertType')->find( { ref => $c->stash->{type} } ); $c->detach( '/page_error_404_not_found', [ _('Unknown alert type') ] ) unless $c->stash->{alert_type}; +} - $c->forward( 'query_main' ); +sub generate : Private { + my ( $self, $c ) = @_; # Do our own encoding $c->stash->{rss} = new XML::RSS( @@ -170,8 +179,15 @@ sub output : Private { uri => 'http://www.georss.org/georss' ); - while (my $row = $c->stash->{query_main}->fetchrow_hashref) { - $c->forward( 'add_row', [ $row ] ); + my $problems = $c->stash->{problems}; + if ( $problems->can('fetchrow_hashref') ) { + while ( my $row = $problems->fetchrow_hashref ) { + $c->forward( 'add_row', [ $row ] ); + } + } else { + while ( my $row = $problems->next ) { + $c->forward( 'add_row', [ $row ] ); + } } $c->forward( 'add_parameters' ); @@ -210,7 +226,7 @@ sub query_main : Private { } else { $q->execute(); } - $c->stash->{query_main} = $q; + $c->stash->{problems} = $q; } sub add_row : Private { diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm index 9abef591d..26a1a1459 100644 --- a/perllib/FixMyStreet/App/Controller/Tokens.pm +++ b/perllib/FixMyStreet/App/Controller/Tokens.pm @@ -33,11 +33,17 @@ sub confirm_problem : Path('/P') { # Load the problem my $data = $auth_token->data; - my $problem_id = $data->{id}; + my $problem_id = ref $data ? $data->{id} : $data; my $problem = $c->cobrand->problems->find( { id => $problem_id } ) || $c->detach('token_error'); $c->stash->{problem} = $problem; + if ( $problem->state eq 'unconfirmed' && $auth_token->created < DateTime->now->subtract( months => 1 ) ) { + $c->stash->{template} = 'errors/generic.html'; + $c->stash->{message} = _("I'm afraid we couldn't validate that token, as the report was made too long ago."); + return; + } + # check that this email or domain are not the cause of abuse. If so hide it. if ( $problem->is_from_abuser ) { $problem->update( @@ -47,6 +53,7 @@ sub confirm_problem : Path('/P') { } # We have a problem - confirm it if needed! + my $old_state = $problem->state; $problem->update( { state => 'confirmed', @@ -60,7 +67,7 @@ sub confirm_problem : Path('/P') { $c->forward( '/report/new/create_reporter_alert' ); # log the problem creation user in to the site - if ( $data->{name} || $data->{password} ) { + if ( ref($data) && ( $data->{name} || $data->{password} ) ) { $problem->user->name( $data->{name} ) if $data->{name}; $problem->user->password( $data->{password}, 1 ) if $data->{password}; $problem->user->update; @@ -68,6 +75,11 @@ sub confirm_problem : Path('/P') { $c->authenticate( { email => $problem->user->email }, 'no_password' ); $c->set_session_cookie_expire(0); + if ( $old_state eq 'confirmed' || $old_state eq 'fixed' ) { + my $report_uri = $c->uri_for( '/report', $problem->id ); + $c->res->redirect($report_uri); + } + return 1; } @@ -169,10 +181,6 @@ sub confirm_update : Path('/C') { sub load_questionnaire : Private { my ( $self, $c, $token_code ) = @_; - # Set up error handling - $c->stash->{error_template} = 'errors/generic.html'; - $c->stash->{message} = _("I'm afraid we couldn't validate that token. If you've copied the URL from an email, please check that you copied it exactly.\n"); - my $auth_token = $c->forward( 'load_auth_token', [ $token_code, 'questionnaire' ] ); $c->stash->{id} = $auth_token->data; $c->stash->{token} = $token_code; @@ -191,7 +199,7 @@ sub questionnaire : Path('/Q') : Args(1) { $c->authenticate( { email => $c->stash->{questionnaire}->problem->user->email }, 'no_password' ); $c->set_session_cookie_expire(0); - $c->forward( '/questionnaire/index'); + $c->forward( '/questionnaire/show' ); } =head2 load_auth_token @@ -218,21 +226,26 @@ sub load_auth_token : Private { scope => $scope, token => $token_code, } - ) || $c->detach('token_error'); + ); + + unless ( $token ) { + $c->stash->{template} = 'errors/generic.html'; + $c->stash->{message} = _("I'm afraid we couldn't validate that token. If you've copied the URL from an email, please check that you copied it exactly.\n"); + $c->detach; + } return $token; } =head2 token_error -Display an error page saying that there is something wrong with the token. +Display an error page saying that there is something wrong with the token (our end). =cut sub token_error : Private { my ( $self, $c ) = @_; - $c->stash->{template} = $c->stash->{error_template} || 'tokens/error.html'; - $c->detach; + $c->stash->{template} = 'tokens/error.html'; } __PACKAGE__->meta->make_immutable; diff --git a/perllib/FixMyStreet/App/View/Web.pm b/perllib/FixMyStreet/App/View/Web.pm index df2d0ac20..5579d0d53 100644 --- a/perllib/FixMyStreet/App/View/Web.pm +++ b/perllib/FixMyStreet/App/View/Web.pm @@ -23,7 +23,10 @@ __PACKAGE__->config( ], FILTERS => { escape_js => \&escape_js, + html => \&html_filter, }, + COMPILE_EXT => '.ttc', + STAT_TTL => FixMyStreet->config('STAGING_SITE') ? 1 : 86400, ); =head1 NAME @@ -142,5 +145,25 @@ sub escape_js { return $text; } +=head2 html_filter + +Same as Template Toolkit's html_filter, but escapes ' too, as we don't (and +shouldn't have to) know whether we'll be used inbetween single or double +quotes. + +=cut + +sub html_filter { + my $text = shift; + for ($text) { + s/&/&/g; + s/</</g; + s/>/>/g; + s/"/"/g; + s/'/'/g; + } + return $text; +} + 1; diff --git a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm index 4f3b975b3..8ff5e3656 100644 --- a/perllib/FixMyStreet/Cobrand/FiksGataMi.pm +++ b/perllib/FixMyStreet/Cobrand/FiksGataMi.pm @@ -22,6 +22,11 @@ sub set_lang_and_domain { return $set_lang; } +sub site_title { + my ($self) = @_; + return 'FiksGataMi'; +} + sub enter_postcode_text { my ( $self ) = @_; return _('Enter a nearby postcode, or street name and area'); diff --git a/perllib/FixMyStreet/Cobrand/Lichfield.pm b/perllib/FixMyStreet/Cobrand/LichfieldDC.pm index 9865bfa7b..12882faee 100644 --- a/perllib/FixMyStreet/Cobrand/Lichfield.pm +++ b/perllib/FixMyStreet/Cobrand/LichfieldDC.pm @@ -1,4 +1,4 @@ -package FixMyStreet::Cobrand::Lichfield; +package FixMyStreet::Cobrand::LichfieldDC; use base 'FixMyStreet::Cobrand::Default'; use strict; @@ -23,21 +23,21 @@ sub problems { sub base_url { my $base_url = mySociety::Config::get('BASE_URL'); - if ( $base_url !~ /lichfield/ ) { - $base_url =~ s{http://(?!www\.)}{http://lichfield.}g; - $base_url =~ s{http://www\.}{http://lichfield.}g; + if ( $base_url !~ /lichfielddc/ ) { + $base_url =~ s{http://(?!www\.)}{http://lichfielddc.}g; + $base_url =~ s{http://www\.}{http://lichfielddc.}g; } return $base_url; } sub site_title { my ($self) = @_; - return 'Lichfield Council FixMyStreet'; + return 'Lichfield District Council FixMyStreet'; } sub enter_postcode_text { my ($self) = @_; - return 'Enter a Lichfield postcode, or street name and area'; + return 'Enter a Lichfield district postcode, or street name and area'; } sub council_check { @@ -52,7 +52,7 @@ sub council_check { $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 Lichfield. + my $error_msg = "That location is not covered by Lichfield District Council. Please visit <a href=\"$url\">the main FixMyStreet site</a>."; return ( 0, $error_msg ); } diff --git a/perllib/FixMyStreet/DB/Result/Problem.pm b/perllib/FixMyStreet/DB/Result/Problem.pm index d5d4b8102..987c92c64 100644 --- a/perllib/FixMyStreet/DB/Result/Problem.pm +++ b/perllib/FixMyStreet/DB/Result/Problem.pm @@ -78,6 +78,8 @@ __PACKAGE__->add_columns( { data_type => "timestamp", is_nullable => 1 }, "send_questionnaire", { data_type => "boolean", default_value => \"true", is_nullable => 0 }, + "flagged", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->has_many( @@ -104,7 +106,7 @@ __PACKAGE__->has_many( # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3sw/1dqxlTvcWEI/eJTm4w # Add fake relationship to stored procedure table -__PACKAGE__->has_many( +__PACKAGE__->has_one( "nearby", "FixMyStreet::DB::Result::Nearby", { "foreign.problem_id" => "self.id" }, diff --git a/perllib/FixMyStreet/DB/Result/User.pm b/perllib/FixMyStreet/DB/Result/User.pm index dba182db5..d2e43b5b0 100644 --- a/perllib/FixMyStreet/DB/Result/User.pm +++ b/perllib/FixMyStreet/DB/Result/User.pm @@ -28,6 +28,8 @@ __PACKAGE__->add_columns( { data_type => "text", default_value => "", is_nullable => 0 }, "from_council", { data_type => "integer", is_nullable => 1 }, + "flagged", + { data_type => "boolean", default_value => \"false", is_nullable => 0 }, ); __PACKAGE__->set_primary_key("id"); __PACKAGE__->add_unique_constraint("users_email_key", ["email"]); diff --git a/perllib/FixMyStreet/Geocode.pm b/perllib/FixMyStreet/Geocode.pm index 4ae3df368..d7c416fb5 100644 --- a/perllib/FixMyStreet/Geocode.pm +++ b/perllib/FixMyStreet/Geocode.pm @@ -43,6 +43,7 @@ sub string { if FixMyStreet->config('BING_MAPS_API_KEY'); return FixMyStreet::Geocode::Google::string($s, $c, $params) if FixMyStreet->config('GOOGLE_MAPS_API_KEY'); + die "No geocoding provider configured"; } 1; diff --git a/perllib/FixMyStreet/Geocode/Bing.pm b/perllib/FixMyStreet/Geocode/Bing.pm index cfeffc856..90d7f98bd 100644 --- a/perllib/FixMyStreet/Geocode/Bing.pm +++ b/perllib/FixMyStreet/Geocode/Bing.pm @@ -57,7 +57,7 @@ sub string { my $address = $_->{name}; next unless $_->{address}->{countryRegion} eq 'United Kingdom'; # FIXME This is UK only ( $latitude, $longitude ) = @{ $_->{point}->{coordinates} }; - push (@$error, $address); + push (@$error, { address => $address, latitude => $latitude, longitude => $longitude }); push (@valid_locations, $_); } return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1; diff --git a/perllib/FixMyStreet/Geocode/Google.pm b/perllib/FixMyStreet/Geocode/Google.pm index c37a750a2..83b36dbcd 100644 --- a/perllib/FixMyStreet/Geocode/Google.pm +++ b/perllib/FixMyStreet/Geocode/Google.pm @@ -75,7 +75,7 @@ sub string { my $address = $_->{address}; next unless $c->cobrand->geocoded_string_check( $address ); ( $longitude, $latitude ) = @{ $_->{Point}->{coordinates} }; - push (@$error, $address); + push (@$error, { address => $address, latitude => $latitude, longitude => $longitude }); push (@valid_locations, $_); } return { latitude => $latitude, longitude => $longitude } if scalar @valid_locations == 1; diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm index 825e1cd19..2bccf1584 100644 --- a/perllib/FixMyStreet/Map.pm +++ b/perllib/FixMyStreet/Map.pm @@ -21,7 +21,6 @@ my @ALL_MAP_CLASSES = allowed_maps(); use mySociety::Config; use mySociety::Gaze; use mySociety::Locale; -use mySociety::Web qw(ent); use Utils; =head2 allowed_maps diff --git a/perllib/FixMyStreet/Map/Bing.pm b/perllib/FixMyStreet/Map/Bing.pm index 54979eba1..676e70bf6 100644 --- a/perllib/FixMyStreet/Map/Bing.pm +++ b/perllib/FixMyStreet/Map/Bing.pm @@ -9,7 +9,6 @@ package FixMyStreet::Map::Bing; use strict; -use mySociety::Web qw(ent); # display_map C PARAMS # PARAMS include: diff --git a/perllib/FixMyStreet/Map/Google.pm b/perllib/FixMyStreet/Map/Google.pm index ceb3a53ed..c0d83e35a 100644 --- a/perllib/FixMyStreet/Map/Google.pm +++ b/perllib/FixMyStreet/Map/Google.pm @@ -9,7 +9,6 @@ package FixMyStreet::Map::Google; use strict; -use mySociety::Web qw(ent); # display_map C PARAMS # PARAMS include: diff --git a/perllib/FixMyStreet/Map/OSM.pm b/perllib/FixMyStreet/Map/OSM.pm index 1fc90c8f0..be185c35c 100644 --- a/perllib/FixMyStreet/Map/OSM.pm +++ b/perllib/FixMyStreet/Map/OSM.pm @@ -59,7 +59,8 @@ sub display_map { } # Adjust zoom level dependent upon population density - my $dist = mySociety::Gaze::get_radius_containing_population( $params{latitude}, $params{longitude}, 200_000 ); + my $dist = $c->stash->{distance} + || mySociety::Gaze::get_radius_containing_population( $params{latitude}, $params{longitude}, 200_000 ); my $default_zoom = $numZoomLevels - 3; $default_zoom = $numZoomLevels - 2 if $dist < 10; |