diff options
author | Matthew Somerville <matthew@mysociety.org> | 2013-02-01 15:28:08 +0000 |
---|---|---|
committer | Matthew Somerville <matthew@mysociety.org> | 2013-02-04 06:53:02 +0000 |
commit | 488a8f21a0c1bc2b9501c9d94d69db56cccd80ae (patch) | |
tree | 68becf501e9492466a80015e8ace9a7fb1557cfa /perllib/FixMyStreet/App/Controller | |
parent | 3e0d12e8584d132b573f536ab5cd01e24241827b (diff) | |
parent | 28aa1dd7fb1c9bc93aa204afae67cf68fe36ee6b (diff) |
Merge remote branch 'origin/zurich'
Conflicts:
bin/open311-populate-service-list
bin/send-comments
bin/update-all-reports
conf/crontab.ugly
db/schema.sql
perllib/FixMyStreet/App/Controller/Admin.pm
perllib/FixMyStreet/App/Controller/Report/New.pm
perllib/FixMyStreet/App/Controller/Reports.pm
perllib/FixMyStreet/Cobrand/Default.pm
perllib/FixMyStreet/Cobrand/LichfieldDC.pm
perllib/FixMyStreet/DB/Result/Open311conf.pm
perllib/FixMyStreet/DB/Result/Problem.pm
perllib/FixMyStreet/DB/ResultSet/Problem.pm
perllib/FixMyStreet/SendReport.pm
perllib/FixMyStreet/SendReport/Email.pm
perllib/FixMyStreet/SendReport/Open311.pm
perllib/Open311/GetServiceRequestUpdates.pm
perllib/Open311/PopulateServiceList.pm
t/app/controller/report_new.t
t/app/controller/rss.t
templates/web/bromley/report/display.html
templates/web/default/admin/council_contacts.html
templates/web/default/common_header_tags.html
templates/web/default/dashboard/index.html
templates/web/default/front/stats.html
templates/web/default/report/_main.html
templates/web/default/report/update-form.html
templates/web/emptyhomes/index.html
templates/web/emptyhomes/report/display.html
templates/web/emptyhomes/report/new/councils_text_all.html
templates/web/emptyhomes/reports/body.html
templates/web/emptyhomes/reports/index.html
templates/web/fixmystreet/report/new/fill_in_details_form.html
templates/web/fixmystreet/report/update-form.html
web/cobrands/fixmystreet/fixmystreet.js
web/js/fixmystreet.js
Diffstat (limited to 'perllib/FixMyStreet/App/Controller')
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Admin.pm | 473 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Alert.pm | 6 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Around.pm | 73 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Council.pm | 52 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Dashboard.pm | 21 | ||||
-rwxr-xr-x | perllib/FixMyStreet/App/Controller/JS.pm | 4 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/JSON.pm | 20 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Open311.pm | 24 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Photo.pm | 3 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report.pm | 41 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/New.pm | 122 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/Update.pm | 8 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Reports.pm | 360 | ||||
-rwxr-xr-x | perllib/FixMyStreet/App/Controller/Rss.pm | 11 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Tokens.pm | 22 |
16 files changed, 727 insertions, 515 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index e14c7dc66..a3a241590 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -4,8 +4,9 @@ use namespace::autoclean; BEGIN { extends 'Catalyst::Controller'; } +use Path::Class; use POSIX qw(strftime strcoll); -use Digest::MD5 qw(md5_hex); +use Digest::SHA1 qw(sha1_hex); use mySociety::EmailUtil qw(is_valid_email); use if !$ENV{TRAVIS}, 'Image::Magick'; @@ -23,23 +24,32 @@ Admin pages =cut -=head2 summary - -Redirect to index page. There to make the allowed pages stuff neater - -=cut - sub begin : Private { my ( $self, $c ) = @_; $c->uri_disposition('relative'); - if ( $c->cobrand->moniker eq 'seesomething' ) { + if ( $c->cobrand->moniker eq 'zurich' || $c->cobrand->moniker eq 'seesomething' ) { $c->detach( '/auth/redirect' ) unless $c->user_exists; - $c->detach( '/auth/redirect' ) unless $c->user->from_council; + $c->detach( '/auth/redirect' ) unless $c->user->from_body; + } + if ( $c->cobrand->moniker eq 'zurich' ) { + $c->cobrand->admin_type(); } } +sub auto : Private { + my ( $self, $c ) = @_; + + $c->forward('check_page_allowed'); +} + +=head2 summary + +Redirect to index page. There to make the allowed pages stuff neater + +=cut + sub summary : Path( 'summary' ) : Args(0) { my ( $self, $c ) = @_; $c->go( 'index' ); @@ -54,7 +64,9 @@ Displays some summary information for the requests. sub index : Path : Args(0) { my ( $self, $c ) = @_; - $c->forward('check_page_allowed'); + if ($c->cobrand->moniker eq 'zurich' && $c->stash->{admin_type} ne 'super') { + return $c->cobrand->admin(); + } my $site_restriction = $c->cobrand->site_restriction(); @@ -124,8 +136,6 @@ sub index : Path : Args(0) { sub timeline : Path( 'timeline' ) : Args(0) { my ($self, $c) = @_; - $c->forward('check_page_allowed'); - my $site_restriction = $c->cobrand->site_restriction(); my %time; @@ -175,8 +185,6 @@ sub timeline : Path( 'timeline' ) : Args(0) { sub questionnaire : Path('questionnaire') : Args(0) { my ( $self, $c ) = @_; - $c->forward('check_page_allowed'); - my $questionnaires = $c->model('DB::Questionnaire')->search( { whenanswered => { '!=', undef } }, { group_by => [ 'ever_reported' ], @@ -207,10 +215,10 @@ sub questionnaire : Path('questionnaire') : Args(0) { return 1; } -sub council_list : Path('council_list') : Args(0) { +sub bodies : Path('bodies') : Args(0) { my ( $self, $c ) = @_; - $c->forward('check_page_allowed'); + $c->forward( 'get_token' ); my $edit_activity = $c->model('DB::ContactsHistory')->search( undef, @@ -224,54 +232,74 @@ sub council_list : Path('council_list') : Args(0) { $c->stash->{edit_activity} = $edit_activity; - # Not London, as treated separately - my $area_types = $c->cobrand->moniker eq 'emptyhomes' - ? $c->cobrand->area_types - : [ grep { $_ ne 'LBO' } @{ $c->cobrand->area_types } ]; - my $areas = mySociety::MaPit::call('areas', $area_types); + my $posted = $c->req->param('posted') || ''; + if ( $posted eq 'body' ) { + $c->forward('check_token'); - my @councils_ids = sort { strcoll($areas->{$a}->{name}, $areas->{$b}->{name}) } keys %$areas; - @councils_ids = $c->cobrand->filter_all_council_ids_list( @councils_ids ); + my $params = $c->forward('body_params'); + my $body = $c->model('DB::Body')->create( $params ); + my $area_ids = $c->req->params->{area_ids}; + if ($area_ids) { + $area_ids = [ $area_ids ] unless ref $area_ids; + foreach (@$area_ids) { + $c->model('DB::BodyArea')->create( { body => $body, area_id => $_ } ); + } + } + + $c->stash->{updated} = _('New body added'); + } + + $c->forward( 'fetch_all_bodies' ); + + # XXX For fixmystreet.com, need to exclude bodies that are covering London. + # But soon, this means just don't have bodies covering London. my $contacts = $c->model('DB::Contact')->search( undef, { - select => [ 'area_id', { count => 'id' }, { count => \'case when deleted then 1 else null end' }, + select => [ 'body_id', { count => 'id' }, { count => \'case when deleted then 1 else null end' }, { count => \'case when confirmed then 1 else null end' } ], - as => [qw/area_id c deleted confirmed/], - group_by => [ 'area_id' ], + as => [qw/body_id c deleted confirmed/], + group_by => [ 'body_id' ], result_class => 'DBIx::Class::ResultClass::HashRefInflator' } ); - my %council_info = map { $_->{area_id} => $_ } $contacts->all; + my %council_info = map { $_->{body_id} => $_ } $contacts->all; - my @no_info = grep { !$council_info{$_} } @councils_ids; - my @one_plus_deleted = grep { $council_info{$_} && $council_info{$_}->{deleted} } @councils_ids; - my @unconfirmeds = grep { $council_info{$_} && !$council_info{$_}->{deleted} && $council_info{$_}->{confirmed} != $council_info{$_}->{c} } @councils_ids; - my @all_confirmed = grep { $council_info{$_} && !$council_info{$_}->{deleted} && $council_info{$_}->{confirmed} == $council_info{$_}->{c} } @councils_ids; - - $c->stash->{areas} = $areas; $c->stash->{counts} = \%council_info; - $c->stash->{no_info} = \@no_info; - $c->stash->{one_plus_deleted} = \@one_plus_deleted; - $c->stash->{unconfirmeds} = \@unconfirmeds; - $c->stash->{all_confirmed} = \@all_confirmed; + + $c->forward( 'body_form_dropdowns' ); return 1; } -sub council_contacts : Path('council_contacts') : Args(1) { - my ( $self, $c, $area_id ) = @_; +sub body_form_dropdowns : Private { + my ( $self, $c ) = @_; - $c->forward('check_page_allowed'); + my $areas; + if ($c->cobrand->moniker eq 'zurich') { + $areas = mySociety::MaPit::call('areas', 274456); + } else { + $areas = mySociety::MaPit::call('areas', $c->cobrand->area_types); + } + $c->stash->{areas} = [ sort { strcoll($a->{name}, $b->{name}) } values %$areas ]; - my $posted = $c->req->param('posted') || ''; - $c->stash->{area_id} = $area_id; + my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } keys %{ FixMyStreet::SendReport->get_senders }; + $c->stash->{send_methods} = \@methods; +} + +sub body : Path('body') : Args(1) { + my ( $self, $c, $body_id ) = @_; + + $c->stash->{body_id} = $body_id; $c->forward( 'get_token' ); + $c->forward( 'lookup_body' ); + $c->forward( 'fetch_all_bodies' ); + $c->forward( 'body_form_dropdowns' ); - if ( $posted ) { + if ( $c->req->param('posted') ) { $c->log->debug( 'posted' ); $c->forward('update_contacts'); } @@ -285,7 +313,7 @@ sub update_contacts : Private { my ( $self, $c ) = @_; my $posted = $c->req->param('posted'); - my $editor = $c->req->remote_user || _('*unknown*'); + my $editor = $c->forward('get_user'); if ( $posted eq 'new' ) { $c->forward('check_token'); @@ -297,7 +325,7 @@ sub update_contacts : Private { my $contact = $c->model('DB::Contact')->find_or_new( { - area_id => $c->stash->{area_id}, + body_id => $c->stash->{body_id}, category => $category, } ); @@ -331,7 +359,7 @@ sub update_contacts : Private { my $contacts = $c->model('DB::Contact')->search( { - area_id => $c->stash->{area_id}, + body_id => $c->stash->{body_id}, category => { -in => \@categories }, } ); @@ -346,69 +374,50 @@ sub update_contacts : Private { ); $c->stash->{updated} = _('Values updated'); - } elsif ( $posted eq 'open311' ) { + } elsif ( $posted eq 'body' ) { $c->forward('check_token'); - my %params = map { $_ => $c->req->param($_) || '' } qw/open311_id endpoint jurisdiction api_key area_id send_method send_comments suppress_alerts extended_statuses comment_user_id devolved/; - - if ( $params{open311_id} ) { - my $conf = $c->model('DB::Open311Conf')->find( { id => $params{open311_id} } ); - - $conf->endpoint( $params{endpoint} ); - $conf->jurisdiction( $params{jurisdiction} ); - $conf->api_key( $params{api_key} ); - $conf->send_method( $params{send_method} ); - $conf->send_comments( $params{send_comments} || 0); - $conf->suppress_alerts( $params{suppress_alerts} || 0); - $conf->comment_user_id( $params{comment_user_id} || undef ); - $conf->can_be_devolved( $params{devolved} || 0 ); - $conf->send_extended_statuses( $params{extended_statuses} || 0 ); - - $conf->update(); - - $c->stash->{updated} = _('Configuration updated'); - } else { - my $conf = $c->model('DB::Open311Conf')->find_or_new( { area_id => $params{area_id} } ); - - $conf->endpoint( $params{endpoint} ); - $conf->jurisdiction( $params{jurisdiction} ); - $conf->api_key( $params{api_key} ); - $conf->send_method( $params{send_method} ); - $conf->send_comments( $params{send_comments} || 0); - $conf->suppress_alerts( $params{suppress_alerts} || 0); - $conf->comment_user_id( $params{comment_user_id} || undef ); - $conf->can_be_devolved( $params{devolved} || 0 ); - $conf->send_extended_statuses( $params{extended_statuses} || 0 ); - - $conf->insert(); - - $c->stash->{updated} = _('Configuration updated - contacts will be generated automatically later'); + my $params = $c->forward( 'body_params' ); + $c->stash->{body}->update( $params ); + my @current = $c->stash->{body}->body_areas->all; + my %current = map { $_->area_id => 1 } @current; + my $area_ids = $c->req->params->{area_ids}; + if ($area_ids) { + $area_ids = [ $area_ids ] unless ref $area_ids; + foreach (@$area_ids) { + $c->model('DB::BodyArea')->find_or_create( { body => $c->stash->{body}, area_id => $_ } ); + delete $current{$_}; + } } + # Remove any others + $c->stash->{body}->body_areas->search( { area_id => [ keys %current ] } )->delete; + + $c->stash->{updated} = _('Configuration updated - contacts will be generated automatically later'); } } -sub display_contacts : Private { +sub body_params : Private { my ( $self, $c ) = @_; - $c->forward('setup_council_details'); - - my $area_id = $c->stash->{area_id}; - - my $contacts = $c->model('DB::Contact')->search( - { area_id => $area_id }, - { order_by => ['category'] } + my @fields = qw/name endpoint jurisdiction api_key send_method send_comments suppress_alerts send_extended_statuses comment_user_id can_be_devolved parent/; + my %defaults = map { $_ => '' } @fields; + %defaults = ( %defaults, + send_comments => 0, + suppress_alerts => 0, + comment_user_id => undef, + send_extended_statuses => 0, + can_be_devolved => 0, + parent => undef, ); + my %params = map { $_ => $c->req->param($_) || $defaults{$_} } @fields; + return \%params; +} - $c->stash->{contacts} = $contacts; - - my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } keys %{ FixMyStreet::SendReport->get_senders }; - $c->stash->{send_methods} = \@methods; - - my $open311 = $c->model('DB::Open311Conf')->search( - { area_id => $area_id } - ); +sub display_contacts : Private { + my ( $self, $c ) = @_; - $c->stash->{open311} = $open311; + my $contacts = $c->stash->{body}->contacts->search(undef, { order_by => [ 'category' ] } ); + $c->stash->{contacts} = $contacts; if ( $c->req->param('text') && $c->req->param('text') == 1 ) { $c->stash->{template} = 'admin/council_contacts.txt'; @@ -419,59 +428,52 @@ sub display_contacts : Private { return 1; } -sub setup_council_details : Private { +sub lookup_body : Private { my ( $self, $c ) = @_; - my $area_id = $c->stash->{area_id}; - - my $mapit_data = mySociety::MaPit::call('area', $area_id); - - $c->stash->{council_name} = $mapit_data->{name}; - - my $example_postcode = mySociety::MaPit::call('area/example_postcode', $area_id); - - if ($example_postcode && ! ref $example_postcode) { - $c->stash->{example_pc} = $example_postcode; + my $body_id = $c->stash->{body_id}; + my $body = $c->model('DB::Body')->find($body_id); + $c->detach( '/page_error_404_not_found' ) + unless $body; + $c->stash->{body} = $body; + + if ($body->body_areas->first) { + my $example_postcode = mySociety::MaPit::call('area/example_postcode', $body->body_areas->first->area_id); + if ($example_postcode && ! ref $example_postcode) { + $c->stash->{example_pc} = $example_postcode; + } } return 1; } -sub council_edit_all : Path('council_edit') { - my ( $self, $c, $area_id, @category ) = @_; +# This is for if the category name contains a '/' +sub body_edit_all : Path('body_edit') { + my ( $self, $c, $body_id, @category ) = @_; my $category = join( '/', @category ); - $c->go( 'council_edit', [ $area_id, $category ] ); + $c->go( 'body_edit', [ $body_id, $category ] ); } -sub council_edit : Path('council_edit') : Args(2) { - my ( $self, $c, $area_id, $category ) = @_; +sub body_edit : Path('body_edit') : Args(2) { + my ( $self, $c, $body_id, $category ) = @_; - $c->forward('check_page_allowed'); - - $c->stash->{area_id} = $area_id; + $c->stash->{body_id} = $body_id; $c->forward( 'get_token' ); - $c->forward('setup_council_details'); - - my $contact = $c->model('DB::Contact')->search( - { - area_id => $area_id, - category => $category - } - )->first; + $c->forward( 'lookup_body' ); + my $contact = $c->stash->{body}->contacts->search( { category => $category } )->first; $c->stash->{contact} = $contact; my $history = $c->model('DB::ContactsHistory')->search( { - area_id => $area_id, + body_id => $body_id, category => $category }, { order_by => ['contacts_history_id'] }, ); - $c->stash->{history} = $history; my @methods = map { $_ =~ s/FixMyStreet::SendReport:://; $_ } keys %{ FixMyStreet::SendReport->get_senders }; @@ -480,13 +482,11 @@ sub council_edit : Path('council_edit') : Args(2) { return 1; } -sub search_reports : Path('search_reports') { +sub reports : Path('reports') { my ( $self, $c ) = @_; - $c->forward('check_page_allowed'); - if (my $search = $c->req->param('search')) { - $c->stash->{searched} = 1; + $c->stash->{searched} = $search; my $site_restriction = $c->cobrand->site_restriction; @@ -523,7 +523,7 @@ sub search_reports : Path('search_reports') { 'me.name' => { ilike => $like_search }, 'me.title' => { ilike => $like_search }, detail => { ilike => $like_search }, - council => { like => $like_search }, + bodies_str => { like => $like_search }, cobrand_data => { like => $like_search }, ]; } @@ -542,8 +542,8 @@ sub search_reports : Path('search_reports') { # 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}}); + $c->stash->{edit_body_contacts} = 1 + if ( grep {$_ eq 'body'} keys %{$c->stash->{allowed_pages}}); if (is_valid_email($search)) { $query = [ @@ -574,7 +574,7 @@ sub search_reports : Path('search_reports') { -or => $query, }, { - -select => [ 'me.*', qw/problem.council problem.state/ ], + -select => [ 'me.*', qw/problem.bodies_str problem.state/ ], prefetch => [qw/user problem/], order_by => [\"(me.state='hidden')",\"(problem.state='hidden')",'me.created'] } @@ -592,11 +592,7 @@ sub report_edit : Path('report_edit') : Args(1) { my $site_restriction = $c->cobrand->site_restriction; - my $problem = $c->cobrand->problems->search( - { - id => $id, - } - )->first; + my $problem = $c->cobrand->problems->search( { id => $id } )->first; $c->detach( '/page_error_404_not_found' ) unless $problem; @@ -604,7 +600,32 @@ sub report_edit : Path('report_edit') : Args(1) { $c->stash->{problem} = $problem; $c->forward('get_token'); - $c->forward('check_page_allowed'); + + if ( $c->req->param('rotate_photo') ) { + $c->forward('rotate_photo'); + return 1; + } + + if ( $c->cobrand->moniker eq 'zurich' ) { + + FixMyStreet::Map::display_map( + $c, + latitude => $problem->latitude, + longitude => $problem->longitude, + pins => $problem->used_map + ? [ { + latitude => $problem->latitude, + longitude => $problem->longitude, + colour => 'yellow', + type => 'big', + } ] + : [], + ); + + my $done = $c->cobrand->admin_report_edit(); + return if $done; + } + $c->forward('check_email_for_abuse', [ $problem->user->email ] ); $c->stash->{updates} = @@ -633,9 +654,6 @@ sub report_edit : Path('report_edit') : Args(1) { elsif ( $c->req->param('banuser') ) { $c->forward('ban_user'); } - elsif ( $c->req->param('rotate_photo') ) { - $c->forward('rotate_photo'); - } elsif ( $c->req->param('submit') ) { $c->forward('check_token'); @@ -664,6 +682,7 @@ sub report_edit : Path('report_edit') : Args(1) { || $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('body') && $c->req->param('body') ne $problem->bodies_str) || $flagged != $problem->flagged || $non_public != $problem->non_public ) { @@ -673,8 +692,10 @@ sub report_edit : Path('report_edit') : Args(1) { $problem->anonymous( $c->req->param('anonymous') ); $problem->title( $c->req->param('title') ); $problem->detail( $c->req->param('detail') ); - $problem->state( $c->req->param('state') ); + $problem->state( $new_state ); $problem->name( $c->req->param('name') ); + $problem->bodies_str( $c->req->param('body') ) if $c->req->param('body'); + $problem->flagged( $flagged ); $problem->non_public( $non_public ); @@ -721,26 +742,22 @@ sub report_edit : Path('report_edit') : Args(1) { return 1; } -sub search_users: Path('search_users') : Args(0) { +sub users: Path('users') : Args(0) { my ( $self, $c ) = @_; - $c->forward('check_page_allowed'); - if (my $search = $c->req->param('search')) { - $c->stash->{searched} = 1; + $c->stash->{searched} = $search; - my $search = $c->req->param('search'); my $isearch = '%' . $search . '%'; - my $search_n = 0; $search_n = int($search) if $search =~ /^\d+$/; my $users = $c->model('DB::User')->search( { -or => [ - email => { ilike => $isearch }, - name => { ilike => $isearch }, - from_council => $search_n, + email => { ilike => $isearch }, + name => { ilike => $isearch }, + from_body => $search_n, ] } ); @@ -762,6 +779,9 @@ sub search_users: Path('search_users') : Args(0) { } } + } else { + $c->forward('get_token'); + $c->forward('fetch_all_bodies'); } return 1; @@ -782,7 +802,6 @@ sub update_edit : Path('update_edit') : Args(1) { unless $update; $c->forward('get_token'); - $c->forward('check_page_allowed'); $c->stash->{update} = $update; @@ -837,6 +856,11 @@ sub update_edit : Path('update_edit') : Args(1) { if ( $new_state eq 'confirmed' and $old_state eq 'unconfirmed' ) { $update->confirmed( \'ms_current_timestamp()' ); + if ( $update->problem_state && $update->created > $update->problem->lastupdate ) { + $update->problem->state( $update->problem_state ); + $update->problem->lastupdate( \'ms_current_timestamp()' ); + $update->problem->update; + } } $update->update; @@ -868,16 +892,44 @@ sub update_edit : Path('update_edit') : Args(1) { return 1; } +sub user_add : Path('user_edit') : Args(0) { + my ( $self, $c ) = @_; + + $c->stash->{template} = 'admin/user_edit.html'; + $c->forward('get_token'); + $c->forward('fetch_all_bodies'); + + return 1 unless $c->req->param('submit'); + + $c->forward('check_token'); + + my $user = $c->model('DB::User')->find_or_create( { + name => $c->req->param('name'), + email => $c->req->param('email'), + from_body => $c->req->param('body') || undef, + flagged => $c->req->param('flagged') || 0, + }, { + key => 'users_email_key' + } ); + $c->stash->{user} = $user; + + $c->forward( 'log_edit', [ $user->id, 'user', 'edit' ] ); + + $c->stash->{status_message} = + '<p><em>' . _('Updated!') . '</em></p>'; + + return 1; +} + sub user_edit : Path('user_edit') : Args(1) { my ( $self, $c, $id ) = @_; - $c->forward('check_page_allowed'); $c->forward('get_token'); my $user = $c->model('DB::User')->find( { id => $id } ); $c->stash->{user} = $user; - $c->forward('set_up_council_details'); + $c->forward('fetch_all_bodies'); if ( $c->req->param('submit') ) { $c->forward('check_token'); @@ -886,13 +938,15 @@ sub user_edit : Path('user_edit') : Args(1) { if ( $user->email ne $c->req->param('email') || $user->name ne $c->req->param('name' ) || - $user->from_council != $c->req->param('council') ) { + ($user->from_body && $user->from_body->id ne $c->req->param('body')) || + (!$user->from_body && $c->req->param('body')) + ) { $edited = 1; } $user->name( $c->req->param('name') ); $user->email( $c->req->param('email') ); - $user->from_council( $c->req->param('council') || undef ); + $user->from_body( $c->req->param('body') || undef ); $user->flagged( $c->req->param('flagged') || 0 ); $user->update; @@ -907,11 +961,9 @@ sub user_edit : Path('user_edit') : Args(1) { return 1; } -sub list_flagged : Path('list_flagged') : Args(0) { +sub flagged : Path('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 @@ -928,9 +980,7 @@ sub list_flagged : Path('list_flagged') : Args(0) { sub stats : Path('stats') : Args(0) { my ( $self, $c ) = @_; - $c->forward('check_page_allowed'); - - $c->forward('set_up_council_details'); + $c->forward('fetch_all_bodies'); if ( $c->cobrand->moniker eq 'seesomething' ) { return $c->cobrand->admin_stats(); @@ -970,11 +1020,11 @@ sub stats : Path('stats') : Args(0) { my $bymonth = $c->req->param('bymonth'); $c->stash->{bymonth} = $bymonth; - my ( %council, %dates ); - $council{council} = { like => $c->req->param('council') } - if $c->req->param('council'); + my ( %body, %dates ); + $body{bodies_str} = { like => $c->req->param('body') } + if $c->req->param('body'); - $c->stash->{selected_council} = $c->req->param('council'); + $c->stash->{selected_body} = $c->req->param('body'); my $field = 'confirmed'; @@ -1009,7 +1059,7 @@ sub stats : Path('stats') : Args(0) { $field => { '>=', $start_date}, $field => { '<=', $end_date + $one_day }, ], - %council, + %body, %dates, }, \%select, @@ -1038,16 +1088,16 @@ sub set_allowed_pages : Private { if( !$pages ) { $pages = { 'summary' => [_('Summary'), 0], - 'council_list' => [_('Bodies'), 1], - 'search_reports' => [_('Reports'), 2], + 'bodies' => [_('Bodies'), 1], + 'reports' => [_('Reports'), 2], 'timeline' => [_('Timeline'), 3], 'questionnaire' => [_('Survey'), 4], - 'search_users' => [_('Users'), 5], - 'list_flagged' => [_('Flagged'), 6], + 'users' => [_('Users'), 5], + 'flagged' => [_('Flagged'), 6], 'stats' => [_('Stats'), 6], 'user_edit' => [undef, undef], - 'council_contacts' => [undef, undef], - 'council_edit' => [undef, undef], + 'body' => [undef, undef], + 'body_edit' => [undef, undef], 'report_edit' => [undef, undef], 'update_edit' => [undef, undef], 'abuse_edit' => [undef, undef], @@ -1062,6 +1112,16 @@ sub set_allowed_pages : Private { return 1; } +sub get_user : Private { + my ( $self, $c ) = @_; + + my $user = $c->req->remote_user(); + $user ||= ($c->user && $c->user->name); + $user ||= ''; + + return $user; +} + =item get_token Generate a token based on user and secret @@ -1072,12 +1132,8 @@ sub get_token : Private { my ( $self, $c ) = @_; my $secret = $c->model('DB::Secret')->search()->first; - - my $user = $c->req->remote_user(); - $user ||= ''; - - my $token = md5_hex(($user . $secret->secret)); - + my $user = $c->forward('get_user'); + my $token = sha1_hex($user . $secret->secret); $c->stash->{token} = $token; return 1; @@ -1104,7 +1160,7 @@ sub check_token : Private { $c->forward( 'log_edit', [ $object_id, $object_type, $action_performed ] ); -Adds an entry into the admin_log table using the current remote_user. +Adds an entry into the admin_log table using the current user. =cut @@ -1112,7 +1168,7 @@ sub log_edit : Private { my ( $self, $c, $id, $object_type, $action ) = @_; $c->model('DB::AdminLog')->create( { - admin_user => ( $c->req->remote_user() || '' ), + admin_user => $c->forward('get_user'), object_type => $object_type, action => $action, object_id => $id, @@ -1232,21 +1288,42 @@ sub rotate_photo : Private { my ( $self, $c ) =@_; my $direction = $c->req->param('rotate_photo'); - return unless $direction =~ /Left/ or $direction =~ /Right/; - my $photo = _rotate_image( $c->stash->{problem}->photo, $direction =~ /Left/ ? -90 : 90 ); + my $photo = $c->stash->{problem}->photo; + my $file; + + # If photo field contains a hash + if ( length($photo) == 40 ) { + $file = file( $c->config->{UPLOAD_DIR}, "$photo.jpeg" ); + $photo = $file->slurp; + } - if ( $photo ) { - $c->stash->{rotated} = 1; - $c->stash->{problem}->photo( $photo ); + $photo = _rotate_image( $photo, $direction =~ /Left/ ? -90 : 90 ); + return unless $photo; + + my $fileid; + if ( !$file ) { + $fileid = sha1_hex($photo); + $file = file( $c->config->{UPLOAD_DIR}, "$fileid.jpeg" ); + } + + $c->stash->{rotated} = 1; + + my $fh = $file->open('w'); + print $fh $photo; + close $fh; + + unlink glob FixMyStreet->path_to( 'web', 'photo', $c->stash->{problem}->id . '.*' ); + + if ( $fileid ) { + $c->stash->{problem}->photo( $fileid ); $c->stash->{problem}->update(); } return 1; } - =head2 check_page_allowed Checks if the current catalyst action is in the list of allowed pages and @@ -1270,16 +1347,16 @@ sub check_page_allowed : Private { return 1; } -sub set_up_council_details : Private { +sub fetch_all_bodies : Private { my ($self, $c ) = @_; - my $areas = mySociety::MaPit::call('areas', $c->cobrand->area_types); - - my @councils_ids = sort { strcoll($areas->{$a}->{name}, $areas->{$b}->{name}) } keys %$areas; - @councils_ids = $c->cobrand->filter_all_council_ids_list( @councils_ids ); - - $c->stash->{council_ids} = \@councils_ids; - $c->stash->{council_details} = $areas; + my @bodies = $c->model('DB::Body')->all; + if ( $c->cobrand->moniker eq 'zurich' ) { + @bodies = $c->cobrand->admin_fetch_all_bodies( @bodies ); + } else { + @bodies = sort { strcoll($a->name, $b->name) } @bodies; + } + $c->stash->{bodies} = \@bodies; return 1; } diff --git a/perllib/FixMyStreet/App/Controller/Alert.pm b/perllib/FixMyStreet/App/Controller/Alert.pm index 91ea61fbc..e821b7467 100644 --- a/perllib/FixMyStreet/App/Controller/Alert.pm +++ b/perllib/FixMyStreet/App/Controller/Alert.pm @@ -407,13 +407,13 @@ Generate the details required to display the council/ward/area RSS feeds sub setup_council_rss_feeds : Private { my ( $self, $c ) = @_; - $c->stash->{council_check_action} = 'alert'; - unless ( $c->forward('/council/load_and_check_councils_and_wards') ) { + $c->stash->{area_check_action} = 'alert'; + unless ( $c->forward('/council/load_and_check_areas_and_wards') ) { $c->go('index'); } ( $c->stash->{options}, $c->stash->{reported_to_options} ) = - $c->cobrand->council_rss_alert_options( $c->stash->{all_councils}, $c ); + $c->cobrand->council_rss_alert_options( $c->stash->{all_areas}, $c ); return 1; } diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm index f2bb23350..9a754f063 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -44,7 +44,7 @@ sub around_index : Path : Args(0) { unless $c->forward('/location/determine_location_from_coords') || $c->forward('/location/determine_location_from_pc'); - # Check to see if the spot is covered by a council - if not show an error. + # Check to see if the spot is covered by a area - if not show an error. return unless $c->cobrand->moniker eq 'fixmybarangay' || $c->forward('check_location_is_acceptable'); # If we have a partial - redirect to /report/new so that it can be @@ -192,7 +192,7 @@ sub display_location : Private { longitude => $p->longitude, colour => $colour, id => $p->id, - title => $p->title, + title => $p->title_safe, } } @$on_map_all, @$around_map; } @@ -212,7 +212,7 @@ sub display_location : Private { =head2 check_location_is_acceptable -Find the lat and lon in stash and check that they are acceptable to the council, +Find the lat and lon in stash and check that they are acceptable to the area, and that they are in UK (if we are in UK). =cut @@ -220,10 +220,10 @@ and that they are in UK (if we are in UK). sub check_location_is_acceptable : Private { my ( $self, $c ) = @_; - # check that there are councils that can accept this location - $c->stash->{council_check_action} = 'submit_problem'; - $c->stash->{remove_redundant_councils} = 1; - return $c->forward('/council/load_and_check_councils'); + # check that there are areas that can accept this location + $c->stash->{area_check_action} = 'submit_problem'; + $c->stash->{remove_redundant_areas} = 1; + return $c->forward('/council/load_and_check_areas'); } =head2 /ajax @@ -281,6 +281,65 @@ sub ajax : Path('/ajax') { $c->res->body($body); } + +sub location_autocomplete : Path('/ajax/geocode') { + my ( $self, $c ) = @_; + $c->res->content_type('application/json; charset=utf-8'); + unless ( $c->req->param('term') ) { + $c->res->status(404); + $c->res->body(''); + return; + } + # we want the match even if there's no ambiguity, so recommendation doesn't + # disappear when it's the last choice being offered in the autocomplete. + $c->stash->{allow_single_geocode_match_strings} = 1; + return $self->_geocode( $c, $c->req->param('term') ); +} + +sub location_lookup : Path('/ajax/lookup_location') { + my ( $self, $c ) = @_; + $c->res->content_type('application/json; charset=utf-8'); + unless ( $c->req->param('term') ) { + $c->res->status(404); + $c->res->body(''); + return; + } + + return $self->_geocode( $c, $c->req->param('term') ); +} + +sub _geocode : Private { + my ( $self, $c, $term ) = @_; + + my ( $lat, $long, $suggestions ) = + FixMyStreet::Geocode::lookup( $c->req->param('term'), $c ); + + my ($response, @addresses); + + if ( $lat && $long ) { + $response = { latitude => $lat, longitude => $long }; + } else { + if ( ref($suggestions) eq 'ARRAY' ) { + foreach (@$suggestions) { + push @addresses, decode_utf8($_->{address}); + } + $response = { suggestions => \@addresses }; + } else { + $response = { error => $suggestions }; + } + } + + if ( $c->stash->{allow_single_geocode_match_strings} ) { + $response = \@addresses; + } + + my $body = JSON->new->utf8(1)->encode( + $response + ); + $c->res->body($body); + +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index 3dc25dedf..f4d6d86e4 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -31,7 +31,7 @@ sub general : Path : Args(0) { my $req = $c->req; $c->detach( 'redirect_on_signin', [ $req->param('r') ] ) - if $c->user && $req->param('r'); + if $c->user && $req->param('r') && $req->param('r') !~ /admin/; # all done unless we have a form posted to us return unless $req->method eq 'POST'; diff --git a/perllib/FixMyStreet/App/Controller/Council.pm b/perllib/FixMyStreet/App/Controller/Council.pm index cb9e78421..5d95c2538 100644 --- a/perllib/FixMyStreet/App/Controller/Council.pm +++ b/perllib/FixMyStreet/App/Controller/Council.pm @@ -14,34 +14,34 @@ Catalyst Controller. =head1 METHODS -=head2 load_and_check_councils_and_wards +=head2 load_and_check_areas_and_wards -Try to load councils and wards for this location and check that we have at least one. If -there are no councils then return false. +Try to load areas and wards for this location and check that we have at least one. If +there are no areas then return false. =cut -sub load_and_check_councils_and_wards : Private { +sub load_and_check_areas_and_wards : Private { my ( $self, $c ) = @_; my $area_types = [ @{$c->cobrand->area_types}, @{$c->cobrand->area_types_children} ]; $c->stash->{area_types} = $area_types; - $c->forward('load_and_check_councils'); + $c->forward('load_and_check_areas'); } -=head2 load_and_check_councils +=head2 load_and_check_areas -Try to load councils for this location and check that we have at least one. If -there are no councils then return false. +Try to load areas for this location and check that we have at least one. If +there are no areas then return false. =cut -sub load_and_check_councils : Private { +sub load_and_check_areas : Private { my ( $self, $c ) = @_; my $latitude = $c->stash->{latitude}; my $longitude = $c->stash->{longitude}; - # Look up councils and do checks for the point we've got + # Look up areas and do checks for the point we've got my $area_types; if ( $c->stash->{area_types} and scalar @{ $c->stash->{area_types} } ) { $area_types = $c->stash->{area_types}; @@ -52,49 +52,49 @@ sub load_and_check_councils : Private { my $short_latitude = Utils::truncate_coordinate($latitude); my $short_longitude = Utils::truncate_coordinate($longitude); - my $all_councils; + my $all_areas; if ( $c->stash->{fetch_all_areas} ) { my %area_types = map { $_ => 1 } @$area_types; - my $all_areas = + $all_areas = mySociety::MaPit::call( 'point', "4326/$short_longitude,$short_latitude" ); - $c->stash->{all_areas} = $all_areas; - $all_councils = { + $c->stash->{all_areas_mapit} = $all_areas; + $all_areas = { map { $_ => $all_areas->{$_} } grep { $area_types{ $all_areas->{$_}->{type} } } keys %$all_areas }; } else { - $all_councils = + $all_areas = mySociety::MaPit::call( 'point', "4326/$short_longitude,$short_latitude", type => $area_types ); } - if ($all_councils->{error}) { - $c->stash->{location_error} = $all_councils->{error}; + if ($all_areas->{error}) { + $c->stash->{location_error} = $all_areas->{error}; return; } # Let cobrand do a check my ( $success, $error_msg ) = - $c->cobrand->council_check( { all_councils => $all_councils }, - $c->stash->{council_check_action} ); + $c->cobrand->area_check( { all_areas => $all_areas }, + $c->stash->{area_check_action} ); if ( !$success ) { $c->stash->{location_error} = $error_msg; return; } # edit hash in-place - $c->cobrand->remove_redundant_councils($all_councils) if $c->stash->{remove_redundant_councils}; + $c->cobrand->remove_redundant_areas($all_areas) if $c->stash->{remove_redundant_areas}; - # If we don't have any councils we can't accept the report - if ( !scalar keys %$all_councils ) { + # If we don't have any areas we can't accept the report + if ( !scalar keys %$all_areas ) { $c->stash->{location_error} = _('That location does not appear to be covered by a council; perhaps it is offshore or outside the country. Please try again.'); return; } - # all good if we have some councils left - $c->stash->{all_councils} = $all_councils; - $c->stash->{all_council_names} = - [ map { $_->{name} } values %$all_councils ]; + # all good if we have some areas left + $c->stash->{all_areas} = $all_areas; + $c->stash->{all_area_names} = + [ map { $_->{name} } values %$all_areas ]; return 1; } diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm index 17fd8b867..028b9aadd 100644 --- a/perllib/FixMyStreet/App/Controller/Dashboard.pm +++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm @@ -29,7 +29,7 @@ sub example : Local : Args(0) { } # TODO Set up manual version of what the below would do - #$c->forward( '/report/new/setup_categories_and_councils' ); + #$c->forward( '/report/new/setup_categories_and_bodies' ); # See if we've had anything from the dropdowns - perhaps vary results if so $c->stash->{ward} = $c->req->param('ward'); @@ -74,9 +74,9 @@ sub check_page_allowed : Private { $c->detach( '/auth/redirect' ) unless $c->user_exists; $c->detach( '/page_error_404_not_found' ) - unless $c->user_exists && $c->user->from_council; + unless $c->user_exists && $c->user->from_body; - return $c->user->from_council; + return $c->user->from_body; } =head2 index @@ -88,20 +88,23 @@ Show the dashboard table. sub index : Path : Args(0) { my ( $self, $c ) = @_; - my $council = $c->forward('check_page_allowed'); + my $body = $c->forward('check_page_allowed'); # Set up the data for the dropdowns - my $council_detail = mySociety::MaPit::call('area', $council ); + # Just take the first area ID we find + my $area_id = $body->body_areas->first->area_id; + + my $council_detail = mySociety::MaPit::call('area', $area_id ); $c->stash->{council} = $council_detail; - my $children = mySociety::MaPit::call('area/children', $council, + my $children = mySociety::MaPit::call('area/children', $area_id, type => $c->cobrand->area_types_children, ); $c->stash->{children} = $children; - $c->stash->{all_councils} = { $council => $council_detail }; - $c->forward( '/report/new/setup_categories_and_councils' ); + $c->stash->{all_areas} = { $area_id => $council_detail }; + $c->forward( '/report/new/setup_categories_and_bodies' ); # See if we've had anything from the dropdowns @@ -109,7 +112,7 @@ sub index : Path : Args(0) { $c->stash->{category} = $c->req->param('category'); my %where = ( - council => $council, # XXX This will break in a two tier council. Restriction needs looking at... + bodies_str => $body->id, # XXX Does this break in a two tier council? Restriction needs looking at... 'problem.state' => [ FixMyStreet::DB::Result::Problem->visible_states() ], ); $where{areas} = { 'like', '%,' . $c->stash->{ward} . ',%' } diff --git a/perllib/FixMyStreet/App/Controller/JS.pm b/perllib/FixMyStreet/App/Controller/JS.pm index d7847af75..483c3c2cc 100755 --- a/perllib/FixMyStreet/App/Controller/JS.pm +++ b/perllib/FixMyStreet/App/Controller/JS.pm @@ -11,13 +11,13 @@ FixMyStreet::App::Controller::JS - Catalyst Controller =head1 DESCRIPTION JS Catalyst Controller. To return a language-dependent list -of validation strings. +of translation strings. =head1 METHODS =cut -sub validation_strings : LocalRegex('^validation_strings\.(.*?)\.js$') : Args(0) { +sub translation_strings : LocalRegex('^translation_strings\.(.*?)\.js$') : Args(0) { my ( $self, $c ) = @_; my $lang = $c->req->captures->[0]; $c->cobrand->set_lang_and_domain( $lang, 1 ); diff --git a/perllib/FixMyStreet/App/Controller/JSON.pm b/perllib/FixMyStreet/App/Controller/JSON.pm index f3607341a..1a7c1915b 100644 --- a/perllib/FixMyStreet/App/Controller/JSON.pm +++ b/perllib/FixMyStreet/App/Controller/JSON.pm @@ -92,7 +92,7 @@ sub problems : Local { my @problems = $c->cobrand->problems->search( $query, { order_by => { -asc => 'confirmed' }, columns => [ - 'id', 'title', 'council', 'category', + 'id', 'title', 'bodies_str', 'category', 'detail', 'name', 'anonymous', 'confirmed', 'whensent', 'service', 'latitude', 'longitude', 'used_map', @@ -100,23 +100,13 @@ sub problems : Local { ] } ); - my @councils; foreach my $problem (@problems) { $problem->name( '' ) if $problem->anonymous == 1; $problem->service( 'Web interface' ) if $problem->service eq ''; - if ($problem->council) { - (my $council = $problem->council) =~ s/\|.*//g; - my @council_ids = split /,/, $council; - push(@councils, @council_ids); - $problem->council( \@council_ids ); - } - } - @councils = uniq @councils; - my $areas_info = mySociety::MaPit::call('areas', \@councils); - foreach my $problem (@problems) { - if ($problem->council) { - my @council_names = map { $areas_info->{$_}->{name} } @{$problem->council} ; - $problem->council( join(' and ', @council_names) ); + my $bodies = $problem->bodies; + if (keys %$bodies) { + my @body_names = map { $_->name } values %$bodies; + $problem->bodies_str( join(' and ', @body_names) ); } } diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm index 3382c0cea..7b8cb649f 100644 --- a/perllib/FixMyStreet/App/Controller/Open311.pm +++ b/perllib/FixMyStreet/App/Controller/Open311.pm @@ -164,12 +164,12 @@ sub get_services : Private { if ($lat || $lon) { my $area_types = $c->cobrand->area_types; - my $all_councils = mySociety::MaPit::call('point', + my $all_areas = mySociety::MaPit::call('point', "4326/$lon,$lat", type => $area_types); $categories = $categories->search( { - area_id => [ keys %$all_councils ], - } ); + 'body_areas.area_id' => [ keys %$all_areas ], + }, { join => { 'body' => 'body_areas' } } ); } my @categories = $categories->search( undef, { @@ -223,19 +223,11 @@ sub output_requests : Private { ); 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 = @@ -256,7 +248,7 @@ sub output_requests : Private { 'service_code' => [ $problem->category ], 'service_name' => [ $problem->category ], # 'service_notice' => [ {} ], - 'agency_responsible' => $problem->council , # FIXME Not according to Open311 v2 + 'agency_responsible' => $problem->bodies , # FIXME Not according to Open311 v2 # 'zipcode' => [ {} ], 'interface_used' => [ $problem->service ], # Not in Open311 v2 }; @@ -288,12 +280,12 @@ sub output_requests : Private { } 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}} ; + my @body_names = map { $_->name } values %{$request->{agency_responsible}} ; $request->{agency_responsible} = - [ {'recipient' => [ @council_names ] } ]; + [ {'recipient' => [ @body_names ] } ]; } } $c->forward( 'format_output', [ { @@ -319,7 +311,7 @@ sub get_requests : Private { service_request_id => [ '=', 'id' ], service_code => [ '=', 'category' ], status => [ 'IN', 'state' ], - agency_responsible => [ '~', 'council' ], + agency_responsible => [ '~', 'bodies_str' ], interface_used => [ '=', 'service' ], has_photo => [ '=', 'photo' ], ); diff --git a/perllib/FixMyStreet/App/Controller/Photo.pm b/perllib/FixMyStreet/App/Controller/Photo.pm index fa4baf045..279623922 100644 --- a/perllib/FixMyStreet/App/Controller/Photo.pm +++ b/perllib/FixMyStreet/App/Controller/Photo.pm @@ -102,10 +102,7 @@ sub output : Private { File::Path::make_path( FixMyStreet->path_to( 'web', 'photo', 'c' )->stringify ); File::Slurp::write_file( FixMyStreet->path_to( 'web', $c->req->path )->stringify, \$photo ); - my $dt = DateTime->now()->add( years => 1 ); - $c->res->content_type( 'image/jpeg' ); - $c->res->header( 'expires', DateTime::Format::HTTP->format_datetime( $dt ) ); $c->res->body( $photo ); } diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm index ef966a8a8..460ccaec5 100644 --- a/perllib/FixMyStreet/App/Controller/Report.pm +++ b/perllib/FixMyStreet/App/Controller/Report.pm @@ -51,6 +51,25 @@ sub display : Path('') : Args(1) { return $c->res->redirect( $c->uri_for($1), 301 ); } + $c->forward( '_display', [ $id ] ); +} + +=head2 ajax + +Return JSON formatted details of a report + +=cut + +sub ajax : Path('ajax') : Args(1) { + my ( $self, $c, $id ) = @_; + + $c->stash->{ajax} = 1; + $c->forward( '_display', [ $id ] ); +} + +sub _display : Private { + my ( $self, $c, $id ) = @_; + $c->forward( 'load_problem_or_display_error', [ $id ] ); $c->forward( 'load_updates' ); $c->forward( 'format_problem_for_display' ); @@ -66,7 +85,7 @@ sub support : Path('support') : Args(0) { ? $c->uri_for( '/report', $id ) : $c->uri_for('/'); - if ( $id && $c->cobrand->can_support_problems && $c->user && $c->user->from_council ) { + if ( $id && $c->cobrand->can_support_problems && $c->user && $c->user->from_body ) { $c->forward( 'load_problem_or_display_error', [ $id ] ); $c->stash->{problem}->update( { interest_count => \'interest_count +1' } ); } @@ -83,7 +102,7 @@ sub load_problem_or_display_error : Private { : $c->cobrand->problems->find( { id => $id } ); # check that the problem is suitable to show. - if ( !$problem || $problem->state eq 'unconfirmed' || $problem->state eq 'partial' ) { + if ( !$problem || ($problem->state eq 'unconfirmed' && !$c->cobrand->show_unconfirmed_reports) || $problem->state eq 'partial' ) { $c->detach( '/page_error_404_not_found', [ _('Unknown problem ID') ] ); } elsif ( $problem->state eq 'hidden' ) { @@ -147,10 +166,19 @@ sub format_problem_for_display : Private { $c->stash->{add_alert} = 1; } - $c->stash->{extra_name_info} = $problem->council && $problem->council eq '2482' ? 1 : 0; + $c->stash->{extra_name_info} = $problem->bodies_str && $problem->bodies_str eq '2482' ? 1 : 0; $c->forward('generate_map_tags'); + if ( $c->stash->{ajax} ) { + $c->res->content_type('application/json; charset=utf-8'); + my $content = JSON->new->utf8(1)->encode( + $problem->as_hashref( $c ) + ); + $c->res->body( $content ); + return 1; + } + return 1; } @@ -187,11 +215,10 @@ sub delete :Local :Args(1) { return $c->res->redirect($uri) unless $c->user_exists; - my $council = $c->user->obj->from_council; - return $c->res->redirect($uri) unless $council; + my $body = $c->user->obj->from_body; + return $c->res->redirect($uri) unless $body; - my %councils = map { $_ => 1 } @{$p->councils}; - return $c->res->redirect($uri) unless $councils{$council}; + return $c->res->redirect($uri) unless $p->bodies->{$body}; $p->state('hidden'); $p->lastupdate( \'ms_current_timestamp()' ); diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 9194f5318..687e54fbe 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -89,7 +89,7 @@ sub report_new : Path : Args(0) { # create a problem from the submitted details $c->stash->{template} = "report/new/fill_in_details.html"; - $c->forward('setup_categories_and_councils'); + $c->forward('setup_categories_and_bodies'); $c->forward('generate_map'); $c->forward('check_for_category'); @@ -119,7 +119,7 @@ sub report_new_ajax : Path('mobile') : Args(0) { return 1; } - $c->forward('setup_categories_and_councils'); + $c->forward('setup_categories_and_bodies'); $c->forward('process_user'); $c->forward('process_report'); $c->forward('/photo/process_photo'); @@ -147,7 +147,7 @@ sub report_new_ajax : Path('mobile') : Args(0) { } else { $c->stash->{token_url} = $c->uri_for_email( '/P', $token->token ); $c->send_email( 'problem-confirm.txt', { - to => [ [ $report->user->email, $report->name ] ], + to => [ $report->name ? [ $report->user->email, $report->name ] : $report->user->email ], } ); $c->stash->{ json_response } = { success => 1 }; } @@ -180,7 +180,7 @@ sub report_form_ajax : Path('ajax') : Args(0) { return; } - $c->forward('setup_categories_and_councils'); + $c->forward('setup_categories_and_bodies'); # render templates to get the html my $category = $c->render_fragment( 'report/new/category.html'); @@ -216,7 +216,7 @@ sub category_extras_ajax : Path('category_extras') : Args(0) { $c->res->body($body); return 1; } - $c->forward('setup_categories_and_councils'); + $c->forward('setup_categories_and_bodies'); my $category_extra = ''; if ( $c->stash->{category_extras}->{ $c->req->param('category') } && @{ $c->stash->{category_extras}->{ $c->req->param('category') } } >= 1 ) { @@ -569,27 +569,34 @@ sub determine_location_from_report : Private { return; } -=head2 setup_categories_and_councils +=head2 setup_categories_and_bodies -Look up categories for this council or councils +Look up categories for the relevant body or bodies. =cut -sub setup_categories_and_councils : Private { +sub setup_categories_and_bodies : Private { my ( $self, $c ) = @_; - my $all_councils = $c->stash->{all_councils}; - my $first_council = ( values %$all_councils )[0]; + my $all_areas = $c->stash->{all_areas}; + my $first_area = ( values %$all_areas )[0]; + + my @bodies = $c->model('DB::Body')->search( + { 'body_areas.area_id' => [ keys %$all_areas ] }, + { join => 'body_areas' } + )->all; + my %bodies = map { $_->id => $_ } @bodies; + my $first_body = ( values %bodies )[0]; my @contacts # = $c # ->model('DB::Contact') # ->not_deleted # - ->search( { area_id => [ keys %$all_councils ] } ) # + ->search( { body_id => [ keys %bodies ] } ) ->all; # variables to populate - my %area_ids_to_list = (); # Areas with categories assigned + my %bodies_to_list = (); # Bodies with categories assigned my @category_options = (); # categories to show my $category_label = undef; # what to call them my %category_extras = (); # extra fields to fill in for open311 @@ -599,9 +606,9 @@ sub setup_categories_and_councils : Private { # FIXME - implement in cobrand if ( $c->cobrand->moniker eq 'emptyhomes' ) { - # add all areas found to the list + # add all bodies found to the list foreach (@contacts) { - $area_ids_to_list{ $_->area_id } = 1; + $bodies_to_list{ $_->body_id } = 1; } # set our own categories @@ -616,11 +623,11 @@ sub setup_categories_and_councils : Private { ); $category_label = _('Property type:'); - } elsif ($first_council->{id} != COUNCIL_ID_BROMLEY && $first_council->{type} eq 'LBO') { + } elsif ($first_area->{id} != COUNCIL_ID_BROMLEY && $first_area->{type} eq 'LBO') { - $area_ids_to_list{ $first_council->{id} } = 1; + $bodies_to_list{ $first_body->id } = 1; my @local_categories; - if ($first_council->{id} == COUNCIL_ID_BARNET) { + if ($first_area->{id} == COUNCIL_ID_BARNET) { @local_categories = sort keys %{ Utils::barnet_categories() } } else { @local_categories = sort keys %{ Utils::london_categories() } @@ -639,7 +646,7 @@ sub setup_categories_and_councils : Private { my %seen; foreach my $contact (@contacts) { - $area_ids_to_list{ $contact->area_id } = 1; + $bodies_to_list{ $contact->body_id } = 1; unless ( $seen{$contact->category} ) { push @category_options, $contact->category; @@ -661,24 +668,21 @@ sub setup_categories_and_councils : Private { } # put results onto stash for display - $c->stash->{area_ids_to_list} = [ keys %area_ids_to_list ]; + $c->stash->{bodies} = \%bodies; + $c->stash->{all_body_names} = [ map { $_->name } values %bodies ]; + $c->stash->{bodies_to_list} = [ keys %bodies_to_list ]; $c->stash->{category_label} = $category_label; $c->stash->{category_options} = \@category_options; $c->stash->{category_extras} = \%category_extras; $c->stash->{non_public_categories} = \%non_public_categories; $c->stash->{category_extras_json} = encode_json \%category_extras; - $c->stash->{extra_name_info} = $first_council->{id} == COUNCIL_ID_BROMLEY ? 1 : 0; - - my @missing_details_councils = - grep { !$area_ids_to_list{$_} } # - keys %$all_councils; + $c->stash->{extra_name_info} = $first_area->{id} == COUNCIL_ID_BROMLEY ? 1 : 0; - my @missing_details_council_names = - map { $all_councils->{$_}->{name} } # - @missing_details_councils; + my @missing_details_bodies = grep { !$bodies_to_list{$_->id} } values %bodies; + my @missing_details_body_names = map { $_->name } @missing_details_bodies; - $c->stash->{missing_details_councils} = \@missing_details_councils; - $c->stash->{missing_details_council_names} = \@missing_details_council_names; + $c->stash->{missing_details_bodies} = \@missing_details_bodies; + $c->stash->{missing_details_body_names} = \@missing_details_body_names; } =head2 check_form_submitted @@ -820,60 +824,62 @@ sub process_report : Private { $report->subcategory( $params{subcategory} ); - my $areas = $c->stash->{all_areas}; + my $areas = $c->stash->{all_areas_mapit}; $report->areas( ',' . join( ',', sort keys %$areas ) . ',' ); # From earlier in the process. - my $councils = $c->stash->{all_councils}; - my $first_council = ( values %$councils )[0]; + $areas = $c->stash->{all_areas}; + my $bodies = $c->stash->{bodies}; + my $first_area = ( values %$areas )[0]; + my $first_body = ( values %$bodies )[0]; if ( $c->cobrand->moniker eq 'emptyhomes' ) { - $councils = join( ',', @{ $c->stash->{area_ids_to_list} } ) || -1; - $report->council( $councils ); + $bodies = join( ',', @{ $c->stash->{bodies_to_list} } ) || -1; + $report->bodies_str( $bodies ); - } elsif ( $first_council->{id} == COUNCIL_ID_BARNET ) { + } elsif ( $first_area->{id} == COUNCIL_ID_BARNET ) { unless ( exists Utils::barnet_categories()->{ $report->category } ) { $c->stash->{field_errors}->{category} = _('Please choose a category'); } - $report->council( $first_council->{id} ); + $report->bodies_str( $first_body->id ); - } elsif ( $first_council->{id} != COUNCIL_ID_BROMLEY && $first_council->{type} eq 'LBO') { + } elsif ( $first_area->{id} != COUNCIL_ID_BROMLEY && $first_area->{type} eq 'LBO') { unless ( Utils::london_categories()->{ $report->category } ) { $c->stash->{field_errors}->{category} = _('Please choose a category'); } - $report->council( $first_council->{id} ); + $report->bodies_str( $first_body->id ); } elsif ( $report->category ) { - # FIXME All contacts were fetched in setup_categories_and_councils, + # FIXME All contacts were fetched in setup_categories_and_bodies, # so can this DB call also be avoided? my @contacts = $c-> # model('DB::Contact') # ->not_deleted # ->search( { - area_id => [ keys %$councils ], + body_id => [ keys %$bodies ], category => $report->category } )->all; unless ( @contacts ) { $c->stash->{field_errors}->{category} = _('Please choose a category'); - $report->council( -1 ); + $report->bodies_str( -1 ); return 1; } - # construct the council string: - # 'x,x' - x are council IDs that have this category - # 'x,x|y,y' - x are council IDs that have this category, y council IDs with *no* contact - my $council_string = join( ',', map { $_->area_id } @contacts ); - $council_string .= - '|' . join( ',', @{ $c->stash->{missing_details_councils} } ) - if $council_string && @{ $c->stash->{missing_details_councils} }; - $report->council($council_string); + # construct the bodies string: + # 'x,x' - x are body IDs that have this category + # 'x,x|y' - x are body IDs that have this category, y body IDs with *no* contact + my $body_string = join( ',', map { $_->body_id } @contacts ); + $body_string .= + '|' . join( ',', map { $_->id } @{ $c->stash->{missing_details_bodies} } ) + if $body_string && @{ $c->stash->{missing_details_bodies} }; + $report->bodies_str($body_string); my @extra = (); my $metas = $contacts[0]->extra; @@ -895,13 +901,13 @@ sub process_report : Private { $report->non_public( 1 ); } - $c->cobrand->process_extras( $c, $contacts[0]->area_id, \@extra ); + $c->cobrand->process_extras( $c, $contacts[0]->body_id, \@extra ); if ( @extra ) { $c->stash->{report_meta} = { map { $_->{name} => $_ } @extra }; $report->extra( \@extra ); } - } elsif ( @{ $c->stash->{area_ids_to_list} } ) { + } elsif ( @{ $c->stash->{bodies_to_list} } ) { # There was an area with categories, but we've not been given one. Bail. $c->stash->{field_errors}->{category} = _('Please choose a category'); @@ -910,7 +916,7 @@ sub process_report : Private { # If we're here, we've been submitted somewhere # where we have no contact information at all. - $report->council( -1 ); + $report->bodies_str( -1 ); } @@ -944,6 +950,8 @@ sub check_for_errors : Private { if ( $c->cobrand->moniker eq 'zurich' ) { delete $field_errors{title}; delete $field_errors{name}; + my $report = $c->stash->{report}; + $report->title( Utils::cleanup_text( substr($report->detail, 0, 25) ) ); } # FIXME: need to check for required bromley fields here @@ -1006,8 +1014,10 @@ sub save_user_and_report : Private { $c->log->info($report->user->id . ' created for this report'); } elsif ( $c->user && $report->user->id == $c->user->id ) { + # Logged in and matches, so instantly confirm (except Zurich, with no confirmation) $report->user->update(); - $report->confirm; + $report->confirm + unless $c->cobrand->moniker eq 'zurich'; $c->log->info($report->user->id . ' is logged in for this report'); } else { @@ -1032,7 +1042,7 @@ sub save_user_and_report : Private { $report->category( _('Other') ) unless $report->category; # Set unknown to DB unknown - $report->council( undef ) if $report->council eq '-1'; + $report->bodies_str( undef ) if $report->bodies_str eq '-1'; # if there is a Message Manager message ID, pass it back to the client view if ($c->cobrand->moniker eq 'fixmybarangay' && $c->req->param('external_source_id')=~/^\d+$/) { @@ -1113,7 +1123,7 @@ sub redirect_or_confirm_creation : Private { $c->forward( 'create_reporter_alert' ); my $report_uri; - if ( $c->cobrand->moniker eq 'fixmybarangay' && $c->user->from_council && $c->stash->{external_source_id}) { + if ( $c->cobrand->moniker eq 'fixmybarangay' && $c->user->from_body && $c->stash->{external_source_id}) { $report_uri = $c->uri_for( '/report', $report->id, undef, { external_source_id => $c->stash->{external_source_id} } ); } elsif ( $c->cobrand->never_confirm_reports && $report->non_public ) { $c->log->info( 'cobrand was set to always confirm reports and report was non public, success page showed'); @@ -1138,7 +1148,7 @@ sub redirect_or_confirm_creation : Private { } ); $c->stash->{token_url} = $c->uri_for_email( '/P', $token->token ); $c->send_email( 'problem-confirm.txt', { - to => [ [ $report->user->email, $report->name ] ], + to => [ $report->name ? [ $report->user->email, $report->name ] : $report->user->email ], } ); # tell user that they've been sent an email diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index 5e0d9f388..dbfd57e78 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -76,7 +76,7 @@ sub update_problem : Private { $problem->state('confirmed'); } - if ( $c->cobrand->can_support_problems && $c->user && $c->user->from_council && $c->req->param('external_source_id') ) { + if ( $c->cobrand->can_support_problems && $c->user && $c->user->from_body && $c->req->param('external_source_id') ) { $problem->interest_count( \'interest_count + 1' ); } @@ -201,7 +201,7 @@ sub process_update : Private { if ( $params{state} ) { $params{state} = 'fixed - council' - if $params{state} eq 'fixed' && $c->user && $c->user->belongs_to_council( $update->problem->council ); + if $params{state} eq 'fixed' && $c->user && $c->user->belongs_to_body( $update->problem->bodies_str ); $update->problem_state( $params{state} ); } else { # we do this so we have a record of the state of the problem at this point @@ -223,7 +223,7 @@ sub process_update : Private { my @extra; # Next function fills this, but we don't need it here. # This is just so that the error checkign for these extra fields runs. # TODO Use extra here as it is used on reports. - $c->cobrand->process_extras( $c, $update->problem->council, \@extra ); + $c->cobrand->process_extras( $c, $update->problem->bodies_str, \@extra ); if ( $c->req->param('fms_extra_title') ) { my %extras = (); @@ -261,7 +261,7 @@ sub check_for_errors : Private { # they have to be an authority user to update the state if ( $c->req->param('state') ) { my $error = 0; - $error = 1 unless $c->user && $c->user->belongs_to_council( $c->stash->{update}->problem->council ); + $error = 1 unless $c->user && $c->user->belongs_to_body( $c->stash->{update}->problem->bodies_str ); my $state = $c->req->param('state'); $state = 'fixed - council' if $state eq 'fixed'; diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index 1fcbfbd72..781dee698 100644 --- a/perllib/FixMyStreet/App/Controller/Reports.pm +++ b/perllib/FixMyStreet/App/Controller/Reports.pm @@ -5,6 +5,7 @@ use namespace::autoclean; use File::Slurp; use List::MoreUtils qw(zip); use POSIX qw(strcoll); +use RABX; use mySociety::MaPit; BEGIN { extends 'Catalyst::Controller'; } @@ -30,34 +31,26 @@ Show the summary page of all reports. sub index : Path : Args(0) { my ( $self, $c ) = @_; - # Fetch all areas of the types we're interested in - my $areas_info; - eval { - my $area_types = $c->cobrand->area_types; - $areas_info = mySociety::MaPit::call('areas', $area_types, - min_generation => $c->cobrand->area_min_generation + # Zurich goes straight to map page, with all reports + if ( $c->cobrand->moniker eq 'zurich' ) { + $c->forward( 'load_and_group_problems' ); + my $pins = $c->stash->{pins}; + $c->stash->{page} = 'reports'; + FixMyStreet::Map::display_map( + $c, + latitude => @$pins ? $pins->[0]{latitude} : 0, + longitude => @$pins ? $pins->[0]{longitude} : 0, + area => 274456, + pins => $pins, + any_zoom => 1, ); - }; - if ($@) { - $c->stash->{message} = _("Unable to look up areas in MaPit. Please try again later.") . ' ' . - sprintf(_('The error was: %s'), $@); - $c->stash->{template} = 'errors/generic.html'; - return; + return 1; } - # For each area, add its link and perhaps alter its name if we need to for - # places with the same name. - foreach (values %$areas_info) { - $_->{url} = $c->uri_for( '/reports/' . $c->cobrand->short_name( $_, $areas_info ) ); - if ($_->{parent_area} && $_->{url} =~ /,|%2C/) { - $_->{name} .= ', ' . $areas_info->{$_->{parent_area}}{name}; - } - } - - $c->stash->{areas_info} = $areas_info; - my @keys = sort { strcoll($areas_info->{$a}{name}, $areas_info->{$b}{name}) } keys %$areas_info; - @keys = $c->cobrand->filter_all_council_ids_list( @keys ); - $c->stash->{areas_info_sorted} = [ map { $areas_info->{$_} } @keys ]; + # Fetch all areas of the types we're interested in + my @bodies = $c->model('DB::Body')->all; + @bodies = sort { strcoll($a->name, $b->name) } @bodies; + $c->stash->{bodies} = \@bodies; eval { my $data = File::Slurp::read_file( @@ -99,21 +92,20 @@ Show the summary page for a particular ward. =cut sub ward : Path : Args(2) { - my ( $self, $c, $council, $ward ) = @_; + my ( $self, $c, $body, $ward ) = @_; - $c->forward( 'body_check', [ $council ] ); + $c->forward( 'body_check', [ $body ] ); $c->forward( 'ward_check', [ $ward ] ) if $ward; - $c->forward( 'load_parent' ); - $c->forward( 'check_canonical_url', [ $council ] ); + $c->forward( 'check_canonical_url', [ $body ] ); $c->forward( 'load_and_group_problems' ); - my $council_short = $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} ); - $c->stash->{rss_url} = '/rss/reports/' . $council_short; + my $body_short = $c->cobrand->short_name( $c->stash->{body} ); + $c->stash->{rss_url} = '/rss/reports/' . $body_short; $c->stash->{rss_url} .= '/' . $c->cobrand->short_name( $c->stash->{ward} ) if $c->stash->{ward}; - $c->stash->{council_url} = '/reports/' . $council_short; + $c->stash->{body_url} = '/reports/' . $body_short; $c->stash->{stats} = $c->cobrand->get_report_stats(); @@ -123,7 +115,7 @@ sub ward : Path : Args(2) { my %map_params = ( latitude => @$pins ? $pins->[0]{latitude} : 0, longitude => @$pins ? $pins->[0]{longitude} : 0, - area => $c->stash->{ward} ? $c->stash->{ward}->{id} : $c->stash->{council}->{id}, + area => $c->stash->{ward} ? $c->stash->{ward}->{id} : [ keys %{$c->stash->{body}->areas} ], any_zoom => 1, ); if ( $c->cobrand->moniker eq 'emptyhomes' ) { @@ -139,64 +131,121 @@ sub ward : Path : Args(2) { $c->cobrand->tweak_all_reports_map( $c ); # List of wards - # Ignore external_body special council thing - unless ($c->stash->{ward} || !$c->stash->{council}->{id}) { - my $children = mySociety::MaPit::call('area/children', [ $c->stash->{council}->{id} ], + # Ignore external_body special body thing + unless ($c->stash->{ward} || !$c->stash->{body}->id) { + my $children = mySociety::MaPit::call('area/children', [ $c->stash->{body}->body_areas->first->area_id ], type => $c->cobrand->area_types_children, ); - foreach (values %$children) { - $_->{url} = $c->uri_for( $c->stash->{council_url} - . '/' . $c->cobrand->short_name( $_ ) - ); + unless ($children->{error}) { + foreach (values %$children) { + $_->{url} = $c->uri_for( $c->stash->{body_url} + . '/' . $c->cobrand->short_name( $_ ) + ); + } + $c->stash->{children} = $children; } - $c->stash->{children} = $children; } } -sub rss_body : Regex('^rss/(reports|area)$') : Args(1) { - my ( $self, $c, $body ) = @_; - $c->detach( 'rss_ward', [ $body ] ); +sub rss_area : Path('/rss/area') : Args(1) { + my ( $self, $c, $area ) = @_; + $c->detach( 'rss_area_ward', [ $area ] ); } -sub rss_ward : Regex('^rss/(reports|area)$') : Args(2) { - my ( $self, $c, $council, $ward ) = @_; - - my ( $rss ) = $c->req->captures->[0]; +sub rss_area_ward : Path('/rss/area') : Args(2) { + my ( $self, $c, $area, $ward ) = @_; $c->stash->{rss} = 1; - $c->forward( 'body_check', [ $council ] ); - $c->forward( 'ward_check', [ $ward ] ) if $ward; + # area_check + + $area =~ s/\+/ /g; + $area =~ s/\.html//; - if ($rss eq 'area' && $c->stash->{council}{type} ne 'DIS' && $c->stash->{council}{type} ne 'CTY') { - # Two possibilites are the same for one-tier councils, so redirect one to the other - $c->detach( 'redirect_area' ); + # If we're passed an ID number (don't think this is used anywhere, it + # certainly shouldn't be), just look that up on mapit and redirect + if ($area =~ /^\d+$/) { + my $council = mySociety::MaPit::call('area', $area); + $c->detach( 'redirect_index') if $council->{error}; + $c->stash->{body} = $council; + $c->detach( 'redirect_body' ); + } + + # We must now have a string to check on mapit + my $areas = mySociety::MaPit::call( 'areas', $area, + type => $c->cobrand->area_types, + ); + + if (keys %$areas == 1) { + ($c->stash->{area}) = values %$areas; + } else { + foreach (keys %$areas) { + if (lc($areas->{$_}->{name}) eq lc($area) || $areas->{$_}->{name} =~ /^\Q$area\E (Borough|City|District|County) Council$/i) { + $c->stash->{area} = $areas->{$_}; + } + } } - my $url = $c->cobrand->short_name( $c->stash->{council} ); - $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} ) if $c->stash->{ward}; + $c->forward( 'ward_check', [ $ward ] ) if $ward; + + my $url = $c->cobrand->short_name( $c->stash->{area} ); + $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} ) if $c->stash->{ward}; $c->stash->{qs} = "/$url"; - if ( $rss eq 'area' && $c->stash->{ward} ) { + if ($c->stash->{area}{type} ne 'DIS' && $c->stash->{area}{type} ne 'CTY') { + # UK-specific types - two possibilites are the same for one-tier councils, so redirect one to the other + # With bodies, this should presumably redirect if only one body covers + # the area, and then it will need that body's name (rather than + # assuming as now it is the same as the area) + $c->stash->{body} = $c->stash->{area}; + $c->detach( 'redirect_body' ); + } + + $c->stash->{type} = 'area_problems'; + if ( $c->stash->{ward} ) { # All problems within a particular ward - $c->stash->{type} = 'area_problems'; $c->stash->{title_params} = { NAME => $c->stash->{ward}{name} }; $c->stash->{db_params} = [ $c->stash->{ward}->{id} ]; - } elsif ( $rss eq 'area' ) { - # Problems within a particular council - $c->stash->{type} = 'area_problems'; - $c->stash->{title_params} = { NAME => $c->stash->{council}{name} }; - $c->stash->{db_params} = [ $c->stash->{council}->{id} ]; - } elsif ($c->stash->{ward}) { + } else { + # Problems within a particular area + $c->stash->{title_params} = { NAME => $c->stash->{area}->{name} }; + $c->stash->{db_params} = [ $c->stash->{area}->{id} ]; + } + + # Send on to the RSS generation + $c->forward( '/rss/output' ); + +} + +sub rss_body : Path('/rss/reports') : Args(1) { + my ( $self, $c, $body ) = @_; + $c->detach( 'rss_ward', [ $body ] ); +} + +sub rss_ward : Path('/rss/reports') : Args(2) { + my ( $self, $c, $body, $ward ) = @_; + + $c->stash->{rss} = 1; + + $c->forward( 'body_check', [ $body ] ); + $c->forward( 'ward_check', [ $ward ] ) if $ward; + + my $url = $c->cobrand->short_name( $c->stash->{body} ); + $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} ) if $c->stash->{ward}; + $c->stash->{qs} = "/$url"; + + if ($c->stash->{ward}) { # Problems sent to a council, restricted to a ward $c->stash->{type} = 'ward_problems'; - $c->stash->{title_params} = { COUNCIL => $c->stash->{council}{name}, WARD => $c->stash->{ward}{name} }; - $c->stash->{db_params} = [ $c->stash->{council}->{id}, $c->stash->{ward}->{id} ]; + $c->stash->{title_params} = { COUNCIL => $c->stash->{body}->name, WARD => $c->stash->{ward}{name} }; + $c->stash->{db_params} = [ $c->stash->{body}->id, $c->stash->{ward}->{id} ]; } else { # Problems sent to a council $c->stash->{type} = 'council_problems'; - $c->stash->{title_params} = { COUNCIL => $c->stash->{council}{name} }; - $c->stash->{db_params} = [ $c->stash->{council}->{id}, $c->stash->{council}->{id} ]; + $c->stash->{title_params} = { COUNCIL => $c->stash->{body}->name }; + # XXX This looks up in both bodies_str and areas, but is only using body ID. + # This will not work properly in any install where body IDs are not === area IDs. + $c->stash->{db_params} = [ $c->stash->{body}->id, $c->stash->{body}->id ]; } # Send on to the RSS generation @@ -205,60 +254,56 @@ sub rss_ward : Regex('^rss/(reports|area)$') : Args(2) { =head2 body_check -This action checks the council or external_body name (or code) given in a URI -exists, is valid and so on. If it is, it stores the area or body in the stash, -otherwise it redirects to the all reports page. +This action checks the body name (or code) given in a URI exists, is valid and +so on. If it is, it stores the body in the stash, otherwise it redirects to the +all reports page. =cut sub body_check : Private { - my ( $self, $c, $q_council ) = @_; + my ( $self, $c, $q_body ) = @_; - $q_council =~ s/\+/ /g; - $q_council =~ s/\.html//; + $q_body =~ s/\+/ /g; + $q_body =~ s/\.html//; # Check cobrand specific incantations - e.g. ONS codes for UK, # Oslo/ kommunes sharing a name in Norway - return if $c->cobrand->reports_body_check( $c, $q_council ); + return if $c->cobrand->reports_body_check( $c, $q_body ); # If we're passed an ID number (don't think this is used anywhere, it # certainly shouldn't be), just look that up on MaPit and redirect - if ($q_council =~ /^\d+$/) { - my $council = mySociety::MaPit::call('area', $q_council); - $c->detach( 'redirect_index') if $council->{error}; - $c->stash->{council} = $council; - $c->detach( 'redirect_area' ); + if ($q_body =~ /^\d+$/) { + my $area = mySociety::MaPit::call('area', $q_body); + $c->detach( 'redirect_index') if $area->{error}; + $c->stash->{body} = $area; + $c->detach( 'redirect_body' ); } if ( $c->cobrand->reports_by_body ) { - my $problem = $c->cobrand->problems->search({ 'lower(me.external_body)' => lc $q_council }, { columns => [ 'external_body' ], rows => 1 })->single; + my $problem = $c->cobrand->problems->search({ 'lower(me.external_body)' => lc $q_body }, { columns => [ 'external_body' ], rows => 1 })->single; if ( $problem ) { - # If external_body, put as a council with ID 0 for the moment. - $c->stash->{council} = { id => 0, name => $problem->external_body }; + # If external_body, put as a body with ID 0 for the moment. + $c->stash->{body} = $c->model('DB::Body')->new( { id => 0, name => $problem->external_body } ); return; } } # We must now have a string to check - my $area_types = $c->cobrand->area_types; - my $areas = mySociety::MaPit::call( 'areas', $q_council, - type => $area_types, - min_generation => $c->cobrand->area_min_generation - ); + my @bodies = $c->model('DB::Body')->search( { name => { -like => "$q_body%" } } )->all; - if (keys %$areas == 1) { - ($c->stash->{council}) = values %$areas; + if (@bodies == 1) { + $c->stash->{body} = $bodies[0]; return; } else { - foreach (keys %$areas) { - if (lc($areas->{$_}->{name}) eq lc($q_council) || $areas->{$_}->{name} =~ /^\Q$q_council\E (Borough|City|District|County) Council$/i) { - $c->stash->{council} = $areas->{$_}; + foreach (@bodies) { + if (lc($_->name) eq lc($q_body) || $_->name =~ /^\Q$q_body\E (Borough|City|District|County) Council$/i) { + $c->stash->{body} = $_; return; } } } - # No result, bad council name. + # No result, bad body name. $c->detach( 'redirect_index' ); } @@ -266,7 +311,7 @@ sub body_check : Private { This action checks the ward name from a URI exists and is part of the right parent, already found with body_check. It either stores the ward Area if -okay, or redirects to the council page if bad. +okay, or redirects to the body page if bad. =cut @@ -277,48 +322,41 @@ sub ward_check : Private { $ward =~ s/\.html//; $ward =~ s{_}{/}g; - my $council = $c->stash->{council}; + # Could be from RSS area, or body... + my $parent_id; + if ( $c->stash->{body} ) { + $parent_id = $c->stash->{body}->body_areas->first->area_id; + } else { + $parent_id = $c->stash->{area}->{id}; + } my $qw = mySociety::MaPit::call('areas', $ward, type => $c->cobrand->area_types_children, - min_generation => $c->cobrand->area_min_generation ); foreach my $area (sort { $a->{name} cmp $b->{name} } values %$qw) { - if ($area->{parent_area} == $council->{id}) { + if ($area->{parent_area} == $parent_id) { $c->stash->{ward} = $area; return; } } # Given a false ward name - $c->detach( 'redirect_area' ); -} - -sub load_parent : Private { - my ( $self, $c ) = @_; - - my $council = $c->stash->{council}; - my $areas_info; - if ($council->{parent_area}) { - $c->stash->{areas_info} = mySociety::MaPit::call('areas', [ $council->{id}, $council->{parent_area} ]) - } else { - $c->stash->{areas_info} = { $council->{id} => $council }; - } + $c->detach( 'redirect_body' ); } =head2 check_canonical_url -Given an already found (case-insensitively) council, check what URL +Given an already found (case-insensitively) body, check what URL we are at and redirect accordingly if different. =cut sub check_canonical_url : Private { - my ( $self, $c, $q_council ) = @_; + my ( $self, $c, $q_body ) = @_; - my $council_short = $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} ); - my $url_short = URI::Escape::uri_escape_utf8($q_council); + my $body_short = $c->cobrand->short_name( $c->stash->{body} ); + my $url_short = URI::Escape::uri_escape_utf8($q_body); $url_short =~ s/%2B/+/g; - $c->detach( 'redirect_area' ) unless $council_short eq $url_short; + $c->detach( 'redirect_body' ) unless $body_short eq $url_short; } sub load_and_group_problems : Private { @@ -332,69 +370,57 @@ sub load_and_group_problems : Private { }; if ($c->stash->{ward}) { $where->{areas} = { 'like', '%,' . $c->stash->{ward}->{id} . ',%' }; - $where->{council} = [ + $where->{bodies_str} = [ undef, - $c->stash->{council}->{id}, - { 'like', $c->stash->{council}->{id} . ',%' }, - { 'like', '%,' . $c->stash->{council}->{id} }, + $c->stash->{body}->id, + { 'like', $c->stash->{body}->id . ',%' }, + { 'like', '%,' . $c->stash->{body}->id }, ]; - } elsif ($c->stash->{council} && $c->stash->{council}->{id} == 0) { + } elsif ($c->stash->{body} && $c->stash->{body}->id == 0) { # A proxy for an external_body - $where->{'lower(external_body)'} = lc $c->stash->{council}->{name}; - } elsif ($c->stash->{council}) { - $where->{areas} = { 'like', '%,' . $c->stash->{council}->{id} . ',%' }; - $where->{council} = [ - undef, - $c->stash->{council}->{id}, - { 'like', $c->stash->{council}->{id} . ',%' }, - { 'like', '%,' . $c->stash->{council}->{id} }, + $where->{'lower(external_body)'} = lc $c->stash->{body}->name; + } elsif ($c->stash->{body}) { + # XXX FixMyStreet used to have the following line so that reports not + # currently sent anywhere could still be listed in the appropriate + # (body/area), as they were the same. Now they're not, not sure if + # there's a way to do this easily. + #$where->{areas} = { 'like', '%,' . $c->stash->{body}->id . ',%' }; + $where->{bodies_str} = [ + # undef, + $c->stash->{body}->id, + { 'like', $c->stash->{body}->id . ',%' }, + { 'like', '%,' . $c->stash->{body}->id }, ]; } my $problems = $c->cobrand->problems->search( $where, { - columns => [ - 'id', 'council', 'state', 'areas', 'latitude', 'longitude', 'title', 'cobrand', - #{ duration => { extract => "epoch from current_timestamp-lastupdate" } }, - #{ age => { extract => "epoch from current_timestamp-confirmed" } }, - { confirmed => { extract => 'epoch from confirmed' } }, - { whensent => { extract => 'epoch from whensent' } }, - { lastupdate => { extract => 'epoch from lastupdate' } }, - { photo => 'photo is not null' }, - ], order_by => { -desc => 'lastupdate' }, rows => $c->cobrand->reports_per_page, } )->page( $page ); $c->stash->{pager} = $problems->pager; - $problems = $problems->cursor; # Raw DB cursor for speed my ( %problems, @pins ); - my $re_councils = join('|', keys %{$c->stash->{areas_info}}); - my @cols = ( 'id', 'council', 'state', 'areas', 'latitude', 'longitude', 'title', 'cobrand', 'confirmed', 'whensent', 'lastupdate', 'photo' ); - while ( my @problem = $problems->next ) { - my %problem = zip @cols, @problem; - $problem{is_fixed} = FixMyStreet::DB::Result::Problem->fixed_states()->{$problem{state}}; - $c->log->debug( $problem{'cobrand'} . ', cobrand is ' . $c->cobrand->moniker ); - if ( !$c->stash->{council}->{id} ) { + while ( my $problem = $problems->next ) { + $c->log->debug( $problem->cobrand . ', cobrand is ' . $c->cobrand->moniker ); + if ( !$c->stash->{body} || !$c->stash->{body}->id ) { # An external_body entry - add_row( \%problem, 0, \%problems, \@pins ); + add_row( $c, $problem, 0, \%problems, \@pins ); next; } - if ( !$problem{council} ) { - # Problem was not sent to any council, add to possible councils - $problem{councils} = 0; - while ($problem{areas} =~ /,($re_councils)(?=,)/g) { - add_row( \%problem, $1, \%problems, \@pins ); + if ( !$problem->bodies_str ) { + # Problem was not sent to any body, add to all possible areas XXX + while ($problem->areas =~ /,(\d+)(?=,)/g) { + add_row( $c, $problem, $1, \%problems, \@pins ); } } else { - # Add to councils it was sent to - (my $council = $problem{council}) =~ s/\|.*$//; - my @council = split( /,/, $council ); - $problem{councils} = scalar @council; - foreach ( @council ) { - next if $_ != $c->stash->{council}->{id}; - add_row( \%problem, $_, \%problems, \@pins ); + # Add to bodies it was sent to + # XXX Assumes body ID matches "council ID" + my $bodies = $problem->bodies_str_ids; + foreach ( @$bodies ) { + next if $_ != $c->stash->{body}->id; + add_row( $c, $problem, $_, \%problems, \@pins ); } } } @@ -413,26 +439,26 @@ sub redirect_index : Private { $c->res->redirect( $c->uri_for($url) ); } -sub redirect_area : Private { +sub redirect_body : Private { my ( $self, $c ) = @_; my $url = ''; $url .= "/rss" if $c->stash->{rss}; $url .= '/reports'; - $url .= '/' . $c->cobrand->short_name( $c->stash->{council}, $c->stash->{areas_info} ); + $url .= '/' . $c->cobrand->short_name( $c->stash->{body} ); $url .= '/' . $c->cobrand->short_name( $c->stash->{ward} ) if $c->stash->{ward}; $c->res->redirect( $c->uri_for($url) ); } sub add_row { - my ( $problem, $council, $problems, $pins ) = @_; - push @{$problems->{$council}}, $problem; + my ( $c, $problem, $body, $problems, $pins ) = @_; + push @{$problems->{$body}}, $problem; push @$pins, { - latitude => $problem->{latitude}, - longitude => $problem->{longitude}, - colour => 'yellow', # FixMyStreet::DB::Result::Problem->fixed_states()->{$problem->{state}} ? 'green' : 'red', - id => $problem->{id}, - title => $problem->{title}, + latitude => $problem->latitude, + longitude => $problem->longitude, + colour => $c->cobrand->pin_colour( $problem, 'reports' ), + id => $problem->id, + title => $problem->title_safe, }; } diff --git a/perllib/FixMyStreet/App/Controller/Rss.pm b/perllib/FixMyStreet/App/Controller/Rss.pm index baaa3b927..90ab7ad41 100755 --- a/perllib/FixMyStreet/App/Controller/Rss.pm +++ b/perllib/FixMyStreet/App/Controller/Rss.pm @@ -241,6 +241,15 @@ sub add_row : Private { $row->{name} = 'anonymous' if $row->{anonymous} || !$row->{name}; my $pubDate; + if ($row->{created}) { + $row->{created} =~ /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/; + $pubDate = mySociety::Locale::in_gb_locale { + strftime("%a, %d %b %Y %H:%M:%S %z", $6, $5, $4, $3, $2-1, $1-1900, -1, -1, 0) + }; + $row->{created} = strftime("%e %B", $6, $5, $4, $3, $2-1, $1-1900, -1, -1, 0); + $row->{created} =~ s/^\s+//; + $row->{created} =~ s/^(\d+)/ordinal($1)/e if $c->stash->{lang_code} eq 'en-gb'; + } if ($row->{confirmed}) { $row->{confirmed} =~ /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/; $pubDate = mySociety::Locale::in_gb_locale { @@ -257,7 +266,7 @@ sub add_row : Private { my $hashref_restriction = $c->cobrand->site_restriction; my $base_url = $c->cobrand->base_url; - if ( $hashref_restriction && $hashref_restriction->{council} && $row->{council} && $row->{council} ne $hashref_restriction->{council} ) { + if ( $hashref_restriction && $hashref_restriction->{bodies_str} && $row->{bodies_str} && $row->{bodies_str} ne $hashref_restriction->{bodies_str} ) { $base_url = $c->config->{BASE_URL}; } my $url = $base_url . $link; diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm index 03dc69b00..740822876 100644 --- a/perllib/FixMyStreet/App/Controller/Tokens.pm +++ b/perllib/FixMyStreet/App/Controller/Tokens.pm @@ -53,6 +53,28 @@ sub confirm_problem : Path('/P') { return; } + # For Zurich, email confirmation simply sets a flag, it does not change the + # problem state, log in, or anything else + if ($c->cobrand->moniker eq 'zurich') { + my $extra = { %{ $problem->extra || {} } }; + $extra->{email_confirmed} = 1; + $problem->update( { + extra => $extra, + confirmed => \'ms_current_timestamp()', + } ); + + $c->stash->{report} = $c->stash->{problem}; + $c->forward( '/report/new/create_reporter_alert' ); + + if ( ref($data) && ( $data->{name} || $data->{password} ) ) { + $problem->user->name( $data->{name} ) if $data->{name}; + $problem->user->phone( $data->{phone} ) if $data->{phone}; + $problem->user->update; + } + + return 1; + } + # We have a problem - confirm it if needed! my $old_state = $problem->state; $problem->update( |