diff options
Diffstat (limited to 'perllib/FixMyStreet/App/Controller')
21 files changed, 1154 insertions, 631 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm index 7a2790b31..cfe165f43 100644 --- a/perllib/FixMyStreet/App/Controller/Admin.pm +++ b/perllib/FixMyStreet/App/Controller/Admin.pm @@ -4,10 +4,13 @@ use namespace::autoclean; BEGIN { extends 'Catalyst::Controller'; } +use Path::Class; use POSIX qw(strftime strcoll); -use Digest::MD5 qw(md5_hex); +use Digest::SHA qw(sha1_hex); use mySociety::EmailUtil qw(is_valid_email); use if !$ENV{TRAVIS}, 'Image::Magick'; +use DateTime::Format::Strptime; + use FixMyStreet::SendReport; @@ -23,23 +26,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 +66,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(); @@ -65,11 +79,9 @@ sub index : Path : Args(0) { %prob_counts = map { $_ => $prob_counts{$_} || 0 } - ('confirmed', 'investigating', 'in progress', 'closed', 'fixed - council', - 'fixed - user', 'fixed', 'unconfirmed', 'hidden', - 'partial', 'planned'); + ( FixMyStreet::DB::Result::Problem->all_states() ); $c->stash->{problems} = \%prob_counts; - $c->stash->{total_problems_live} += $prob_counts{$_} + $c->stash->{total_problems_live} += $prob_counts{$_} ? $prob_counts{$_} : 0 for ( FixMyStreet::DB::Result::Problem->visible_states() ); $c->stash->{total_problems_users} = $c->cobrand->problems->unique_users; @@ -120,13 +132,23 @@ sub index : Path : Args(0) { $c->stash->{categories} = $c->cobrand->problems->categories_summary(); + $c->stash->{total_bodies} = $c->model('DB::Body')->count(); + return 1; } -sub timeline : Path( 'timeline' ) : Args(0) { +sub config_page : Path( 'config' ) : Args(0) { my ($self, $c) = @_; + my $dir = $c->stash->{additional_template_paths}->[0]; + my $git_version = `cd $dir && git describe --tags`; + chomp $git_version; + $c->stash( + git_version => $git_version, + ); +} - $c->forward('check_page_allowed'); +sub timeline : Path( 'timeline' ) : Args(0) { + my ($self, $c) = @_; my $site_restriction = $c->cobrand->site_restriction(); my %time; @@ -137,34 +159,34 @@ sub timeline : Path( 'timeline' ) : Args(0) { my $probs = $c->cobrand->problems->timeline; foreach ($probs->all) { - push @{$time{$_->created->epoch}}, { type => 'problemCreated', date => $_->created_local, obj => $_ }; - push @{$time{$_->confirmed->epoch}}, { type => 'problemConfirmed', date => $_->confirmed_local, obj => $_ } if $_->confirmed; - push @{$time{$_->whensent->epoch}}, { type => 'problemSent', date => $_->whensent_local, obj => $_ } if $_->whensent; + push @{$time{$_->created->epoch}}, { type => 'problemCreated', date => $_->created, obj => $_ }; + push @{$time{$_->confirmed->epoch}}, { type => 'problemConfirmed', date => $_->confirmed, obj => $_ } if $_->confirmed; + push @{$time{$_->whensent->epoch}}, { type => 'problemSent', date => $_->whensent, obj => $_ } if $_->whensent; } my $questionnaires = $c->model('DB::Questionnaire')->timeline( $c->cobrand->restriction ); foreach ($questionnaires->all) { - push @{$time{$_->whensent->epoch}}, { type => 'quesSent', date => $_->whensent_local, obj => $_ }; - push @{$time{$_->whenanswered->epoch}}, { type => 'quesAnswered', date => $_->whenanswered_local, obj => $_ } if $_->whenanswered; + push @{$time{$_->whensent->epoch}}, { type => 'quesSent', date => $_->whensent, obj => $_ }; + push @{$time{$_->whenanswered->epoch}}, { type => 'quesAnswered', date => $_->whenanswered, obj => $_ } if $_->whenanswered; } my $updates = $c->model('DB::Comment')->timeline( $site_restriction ); foreach ($updates->all) { - push @{$time{$_->created->epoch}}, { type => 'update', date => $_->created_local, obj => $_} ; + push @{$time{$_->created->epoch}}, { type => 'update', date => $_->created, obj => $_} ; } my $alerts = $c->model('DB::Alert')->timeline_created( $c->cobrand->restriction ); foreach ($alerts->all) { - push @{$time{$_->whensubscribed->epoch}}, { type => 'alertSub', date => $_->whensubscribed_local, obj => $_ }; + push @{$time{$_->whensubscribed->epoch}}, { type => 'alertSub', date => $_->whensubscribed, obj => $_ }; } $alerts = $c->model('DB::Alert')->timeline_disabled( $c->cobrand->restriction ); foreach ($alerts->all) { - push @{$time{$_->whendisabled->epoch}}, { type => 'alertDel', date => $_->whendisabled_local, obj => $_ }; + push @{$time{$_->whendisabled->epoch}}, { type => 'alertDel', date => $_->whendisabled, obj => $_ }; } $c->model('DB')->schema->storage->sql_maker->quote_char( '' ); @@ -177,8 +199,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' ], @@ -209,10 +229,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, @@ -226,54 +246,78 @@ 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_for_super_user'); + $c->forward('check_token'); + + 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'); + } - 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->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 @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; + my %council_info = map { $_->{body_id} => $_ } $contacts->all; - $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; + my $whitelist = $c->config->{MAPIT_ID_WHITELIST}; - my $posted = $c->req->param('posted') || ''; - $c->stash->{area_id} = $area_id; + if ( $whitelist && ref $whitelist eq 'ARRAY' && @$whitelist ) { + $areas = mySociety::MaPit::call('areas', $whitelist); + } else { + $areas = mySociety::MaPit::call('areas', $c->cobrand->area_types); + } + $c->stash->{areas} = [ sort { strcoll($a->{name}, $b->{name}) } values %$areas ]; + + 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( 'check_for_super_user' ); $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'); } @@ -283,11 +327,18 @@ sub council_contacts : Path('council_contacts') : Args(1) { return 1; } +sub check_for_super_user : Private { + my ( $self, $c ) = @_; + if ( $c->cobrand->moniker eq 'zurich' && $c->stash->{admin_type} ne 'super' ) { + $c->detach('/page_error_404_not_found', []); + } +} + 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'); @@ -299,7 +350,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, } ); @@ -333,7 +384,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 }, } ); @@ -348,67 +399,53 @@ sub update_contacts : Private { ); $c->stash->{updated} = _('Values updated'); - } elsif ( $posted eq 'open311' ) { + } elsif ( $posted eq 'body' ) { + $c->forward('check_for_super_user'); $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 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->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->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 deleted/; + 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, + deleted => 0, ); + 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; + $c->stash->{live_contacts} = $contacts->search({ deleted => 0 }); if ( $c->req->param('text') && $c->req->param('text') == 1 ) { $c->stash->{template} = 'admin/council_contacts.txt'; @@ -419,59 +456,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 +510,30 @@ 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'); + my $query = {}; + if ( $c->cobrand->moniker eq 'zurich' ) { + my $type = $c->stash->{admin_type}; + my $body = $c->stash->{body}; + if ( $type eq 'dm' ) { + my @children = map { $_->id } $body->bodies->all; + my @all = (@children, $body->id); + $query = { bodies_str => \@all }; + } elsif ( $type eq 'sdm' ) { + $query = { bodies_str => $body->id }; + } + } + + my $order = $c->req->params->{o} || 'created'; + my $dir = defined $c->req->params->{d} ? $c->req->params->{d} : 1; + $c->stash->{order} = $order; + $c->stash->{dir} = $dir; + $order .= ' desc' if $dir; if (my $search = $c->req->param('search')) { - $c->stash->{searched} = 1; + $c->stash->{searched} = $search; my $site_restriction = $c->cobrand->site_restriction; @@ -503,37 +550,40 @@ sub search_reports : Path('search_reports') { $c->model('DB')->schema->storage->sql_maker->quote_char( '"' ); $c->model('DB')->schema->storage->sql_maker->name_sep( '.' ); - my $query; if (is_valid_email($search)) { - $query = [ + $query->{'-or'} = [ 'user.email' => { ilike => $like_search }, ]; } elsif ($search =~ /^id:(\d+)$/) { - $query = [ + $query->{'-or'} = [ 'me.id' => int($1), ]; } elsif ($search =~ /^area:(\d+)$/) { - $query = [ + $query->{'-or'} = [ 'me.areas' => { like => "%,$1,%" } ]; + } elsif ($search =~ /^ref:(\d+)$/) { + $query->{'-or'} = [ + 'me.external_id' => { like => "%$1%" } + ]; } else { - $query = [ + $query->{'-or'} = [ 'me.id' => $search_n, 'user.email' => { ilike => $like_search }, + 'me.external_id' => { ilike => $like_search }, '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 }, ]; } + my $problems = $c->cobrand->problems->search( - { - -or => $query, - }, + $query, { prefetch => 'user', - order_by => [\"(state='hidden')",'created'] + order_by => [ \"(state='hidden')", \$order ] } ); @@ -542,9 +592,6 @@ 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}}); - if (is_valid_email($search)) { $query = [ 'user.email' => { ilike => $like_search }, @@ -574,9 +621,9 @@ 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'] + order_by => [ \"(me.state='hidden')", \"(problem.state='hidden')", 'me.created' ] } ); $c->stash->{updates} = [ $updates->all ]; @@ -584,7 +631,20 @@ sub search_reports : Path('search_reports') { # Switch quoting back off. See above for explanation of this. $c->model('DB')->schema->storage->sql_maker->quote_char( '' ); + } else { + + my $page = $c->req->params->{p} || 1; + my $problems = $c->cobrand->problems->search( + $query, + { order_by => $order } + )->page( $page ); + $c->stash->{problems} = [ $problems->all ]; + $c->stash->{pager} = $problems->pager; } + + $c->stash->{edit_body_contacts} = 1 + if ( grep {$_ eq 'body'} keys %{$c->stash->{allowed_pages}}); + } sub report_edit : Path('report_edit') : Args(1) { @@ -592,11 +652,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 +660,34 @@ sub report_edit : Path('report_edit') : Args(1) { $c->stash->{problem} = $problem; $c->forward('get_token'); - $c->forward('check_page_allowed'); + + if ( $c->cobrand->moniker eq 'zurich' ) { + $c->stash->{page} = 'admin'; + FixMyStreet::Map::display_map( + $c, + latitude => $problem->latitude, + longitude => $problem->longitude, + pins => $problem->used_map + ? [ { + latitude => $problem->latitude, + longitude => $problem->longitude, + colour => $c->cobrand->pin_colour($problem), + type => 'big', + } ] + : [], + ); + } + + if ( $c->req->param('rotate_photo') ) { + $c->forward('rotate_photo'); + return 1; + } + + if ( $c->cobrand->moniker eq 'zurich' ) { + my $done = $c->cobrand->admin_report_edit(); + return if $done; + } + $c->forward('check_email_for_abuse', [ $problem->user->email ] ); $c->stash->{updates} = @@ -633,9 +716,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 +744,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 +754,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 ); @@ -687,11 +770,16 @@ sub report_edit : Path('report_edit') : Args(1) { $problem->user( $user ); } + # Deal with photos if ( $c->req->param('remove_photo') ) { $problem->photo(undef); } - if ( $new_state eq 'confirmed' and $old_state eq 'unconfirmed' ) { + if ( $c->req->param('remove_photo') || $new_state eq 'hidden' ) { + unlink glob FixMyStreet->path_to( 'web', 'photo', $problem->id . '.*' ); + } + + if ( $problem->is_visible() and $old_state eq 'unconfirmed' ) { $problem->confirmed( \'ms_current_timestamp()' ); } @@ -721,26 +809,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 +846,19 @@ sub search_users: Path('search_users') : Args(0) { } } + } else { + $c->forward('get_token'); + $c->forward('fetch_all_bodies'); + + # Admin users by default + my $users = $c->model('DB::User')->search( + { from_body => { '!=', undef } }, + { order_by => 'name' } + ); + my @users = $users->all; + my %email2user = map { $_->email => $_ } @users; + $c->stash->{users} = \@users; + } return 1; @@ -782,7 +879,6 @@ sub update_edit : Path('update_edit') : Args(1) { unless $update; $c->forward('get_token'); - $c->forward('check_page_allowed'); $c->stash->{update} = $update; @@ -821,10 +917,14 @@ sub update_edit : Path('update_edit') : Args(1) { $update->photo(undef); } + if ( $c->req->param('remove_photo') || $new_state eq 'hidden' ) { + unlink glob FixMyStreet->path_to( 'web', 'photo', 'c', $update->id . '.*' ); + } + $update->name( $c->req->param('name') || '' ); $update->text( $c->req->param('text') ); $update->anonymous( $c->req->param('anonymous') ); - $update->state( $c->req->param('state') ); + $update->state( $new_state ); if ( $c->req->param('email') ne $update->user->email ) { my $user = @@ -837,6 +937,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 +973,51 @@ 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'); + + if ( $c->cobrand->moniker eq 'zurich' and $c->req->param('email') eq '' ) { + $c->stash->{field_errors}->{email} = _('Please enter a valid email'); + return 1; + } + + return unless $c->req->param('name') && $c->req->param('email'); + + 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,14 +1026,21 @@ 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 ); + + if ( $c->cobrand->moniker eq 'zurich' and $user->email eq '' ) { + $c->stash->{field_errors}->{email} = _('Please enter a valid email'); + return 1; + } $user->update; if ($edited) { @@ -907,11 +1054,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 @@ -919,8 +1064,20 @@ sub list_flagged : Path('list_flagged') : Args(0) { $c->stash->{problems} = [ $problems->all ]; my $users = $c->model('DB::User')->search( { flagged => 1 } ); + my @users = $users->all; + my %email2user = map { $_->email => $_ } @users; + $c->stash->{users} = [ @users ]; - $c->stash->{users} = $users; + my @abuser_emails = $c->model('DB::Abuse')->all(); + + foreach my $email (@abuser_emails) { + # Slight abuse of the boolean flagged value + if ($email2user{$email->email}) { + $email2user{$email->email}->flagged( 2 ); + } else { + push @{$c->stash->{users}}, { email => $email->email, flagged => 2 }; + } + } return 1; } @@ -928,37 +1085,24 @@ 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' ) { + if ( $c->cobrand->moniker eq 'seesomething' || $c->cobrand->moniker eq 'zurich' ) { return $c->cobrand->admin_stats(); } if ( $c->req->param('getcounts') ) { my ( $start_date, $end_date, @errors ); + my $parser = DateTime::Format::Strptime->new( pattern => '%d/%m/%Y' ); - 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'), - ); - }; + $start_date = $parser-> parse_datetime ( $c->req->param('start_date') ); - push @errors, _('Invalid start date') if $@; + push @errors, _('Invalid start date') unless defined $start_date; - 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'), - ); - }; + $end_date = $parser-> parse_datetime ( $c->req->param('end_date') ) ; - push @errors, _('Invalid end date') if $@; + push @errors, _('Invalid end date') unless defined $end_date; $c->stash->{errors} = \@errors; $c->stash->{start_date} = $start_date; @@ -970,11 +1114,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 +1153,7 @@ sub stats : Path('stats') : Args(0) { $field => { '>=', $start_date}, $field => { '<=', $end_date + $one_day }, ], - %council, + %body, %dates, }, \%select, @@ -1038,16 +1182,17 @@ 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], + 'config' => [ undef, undef ], '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 +1207,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 +1227,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 +1255,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 +1263,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 +1383,36 @@ sub rotate_photo : Private { my ( $self, $c ) =@_; my $direction = $c->req->param('rotate_photo'); + return unless $direction eq _('Rotate Left') or $direction eq _('Rotate Right'); - 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 ) { - $c->stash->{rotated} = 1; - $c->stash->{problem}->photo( $photo ); - $c->stash->{problem}->update(); + #Â If photo field contains a hash + if ( length($photo) == 40 ) { + $file = file( $c->config->{UPLOAD_DIR}, "$photo.jpeg" ); + $photo = $file->slurp; } + $photo = _rotate_image( $photo, $direction eq _('Rotate Left') ? -90 : 90 ); + return unless $photo; + + # Write out to new location + my $fileid = sha1_hex($photo); + $file = file( $c->config->{UPLOAD_DIR}, "$fileid.jpeg" ); + + my $fh = $file->open('w'); + print $fh $photo; + close $fh; + + unlink glob FixMyStreet->path_to( 'web', 'photo', $c->stash->{problem}->id . '.*' ); + + $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 +1436,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..41e0ad947 100644 --- a/perllib/FixMyStreet/App/Controller/Around.pm +++ b/perllib/FixMyStreet/App/Controller/Around.pm @@ -40,11 +40,12 @@ sub around_index : Path : Args(0) { my $partial_report = $c->forward('load_partial'); # Try to create a location for whatever we have - return - unless $c->forward('/location/determine_location_from_coords') - || $c->forward('/location/determine_location_from_pc'); + my $ret = $c->forward('/location/determine_location_from_coords') + || $c->forward('/location/determine_location_from_pc'); + return unless $ret; + return $c->res->redirect('/') if $ret == -1 && !$partial_report; - # 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 +193,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 +213,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 +221,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 +282,66 @@ 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, @locations); + + if ( $lat && $long ) { + $response = { latitude => $lat, longitude => $long }; + } else { + if ( ref($suggestions) eq 'ARRAY' ) { + foreach (@$suggestions) { + push @addresses, decode_utf8($_->{address}); + push @locations, { address => decode_utf8($_->{address}), lat => $_->{latitude}, long => $_->{longitude} }; + } + $response = { suggestions => \@addresses, locations => \@locations }; + } 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..5a4243fbf 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -157,11 +157,8 @@ sub token : Path('/M') : Args(1) { # Sign out in case we are another user $c->logout(); - # get the email and scrap the token - my $data = $token_obj->data; - $token_obj->delete; - # find or create the user related to the token. + my $data = $token_obj->data; my $user = $c->model('DB::User')->find_or_create( { email => $data->{email} } ); $user->name( $data->{name} ) if $data->{name}; $user->password( $data->{password}, 1 ) if $data->{password}; @@ -182,6 +179,10 @@ Used after signing in to take the person back to where they were. sub redirect_on_signin : Private { my ( $self, $c, $redirect ) = @_; $redirect = 'my' unless $redirect; + if ( $c->cobrand->moniker eq 'zurich' ) { + $redirect = 'my' if $redirect eq 'admin'; + $redirect = 'admin' if $c->user->from_body; + } $c->res->redirect( $c->uri_for( "/$redirect" ) ); } diff --git a/perllib/FixMyStreet/App/Controller/Contact.pm b/perllib/FixMyStreet/App/Controller/Contact.pm index 926a3f2a5..6bc6e90ef 100644 --- a/perllib/FixMyStreet/App/Controller/Contact.pm +++ b/perllib/FixMyStreet/App/Controller/Contact.pm @@ -108,11 +108,19 @@ sub validate : Private { if !mySociety::EmailUtil::is_valid_email( $c->req->param('em') ); } + %field_errors = ( + %field_errors, + $c->cobrand->extra_contact_validation($c) + ); + push @errors, _('Illegal ID') if $c->req->param('id') && $c->req->param('id') !~ /^[1-9]\d*$/ or $c->req->param('update_id') && $c->req->param('update_id') !~ /^[1-9]\d*$/; + push @errors, _('There was a problem showing this page. Please try again later.') + if $c->req->params->{message} && $c->req->params->{message} =~ /\[url=|<a/; + unshift @errors, _('There were problems with your report. Please see below.') if scalar keys %field_errors; @@ -146,7 +154,7 @@ sub prepare_params_for_email : Private { my $problem_url = $base_url . '/report/' . $c->stash->{update}->problem_id . '#update_' . $c->stash->{update}->id; - my $admin_url = " - $admin_url" . 'update_edit/' . $c->stash->{update}->id + my $admin_url = " - $admin_url" . '/update_edit/' . $c->stash->{update}->id if $admin_url; $c->stash->{message} .= sprintf( " \n\n[ Complaint about update %d on report %d - %s%s ]", @@ -158,7 +166,7 @@ sub prepare_params_for_email : Private { elsif ( $c->stash->{problem} ) { my $problem_url = $base_url . '/report/' . $c->stash->{problem}->id; - $admin_url = " - $admin_url" . 'report_edit/' . $c->stash->{problem}->id + $admin_url = " - $admin_url" . '/report_edit/' . $c->stash->{problem}->id if $admin_url; $c->stash->{message} .= sprintf( " \n\n[ Complaint about report %d - %s%s ]", @@ -184,7 +192,7 @@ generally required to stash sub setup_request : Private { my ( $self, $c ) = @_; - $c->stash->{contact_email} = $c->cobrand->contact_email( 'contact' ); + $c->stash->{contact_email} = $c->cobrand->contact_email; $c->stash->{contact_email} =~ s/\@/@/; for my $param (qw/em subject message/) { @@ -206,7 +214,7 @@ Sends the email sub send_email : Private { my ( $self, $c ) = @_; - my $recipient = $c->cobrand->contact_email( 'contact' ); + my $recipient = $c->cobrand->contact_email; my $recipient_name = $c->cobrand->contact_name(); $c->stash->{host} = $c->req->header('HOST'); diff --git a/perllib/FixMyStreet/App/Controller/Council.pm b/perllib/FixMyStreet/App/Controller/Council.pm index cb9e78421..ceec04027 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,52 @@ 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_mapit_error} = 1; + $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_cobrand_check} = 1; $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_no_areas} = 1; $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 a5ba8ff07..25c6e1923 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} . ',%' } @@ -118,16 +121,23 @@ sub index : Path : Args(0) { if $c->stash->{category}; $c->stash->{where} = \%where; my $prob_where = { %where }; - $prob_where->{state} = $prob_where->{'problem.state'}; + $prob_where->{'me.state'} = $prob_where->{'problem.state'}; delete $prob_where->{'problem.state'}; $c->stash->{prob_where} = $prob_where; + my $dtf = $c->model('DB')->storage->datetime_parser; + my %counts; - my $t = DateTime->today; - $counts{wtd} = $c->forward( 'updates_search', [ $t->subtract( days => $t->dow - 1 ) ] ); - $counts{week} = $c->forward( 'updates_search', [ DateTime->now->subtract( weeks => 1 ) ] ); - $counts{weeks} = $c->forward( 'updates_search', [ DateTime->now->subtract( weeks => 4 ) ] ); - $counts{ytd} = $c->forward( 'updates_search', [ DateTime->today->set( day => 1, month => 1 ) ] ); + my $now = DateTime->now( time_zone => 'local' ); + my $t = $now->clone->truncate( to => 'day' ); + $counts{wtd} = $c->forward( 'updates_search', + [ $dtf->format_datetime( $t->clone->subtract( days => $t->dow - 1 ) ) ] ); + $counts{week} = $c->forward( 'updates_search', + [ $dtf->format_datetime( $now->clone->subtract( weeks => 1 ) ) ] ); + $counts{weeks} = $c->forward( 'updates_search', + [ $dtf->format_datetime( $now->clone->subtract( weeks => 4 ) ) ] ); + $counts{ytd} = $c->forward( 'updates_search', + [ $dtf->format_datetime( $t->clone->set( day => 1, month => 1 ) ) ] ); $c->stash->{problems} = \%counts; @@ -135,26 +145,126 @@ sub index : Path : Args(0) { $c->stash->{q_state} = $c->req->param('state') || ''; if ( $c->stash->{q_state} eq 'fixed' ) { - $prob_where->{state} = [ FixMyStreet::DB::Result::Problem->fixed_states() ]; + $prob_where->{'me.state'} = [ FixMyStreet::DB::Result::Problem->fixed_states() ]; } elsif ( $c->stash->{q_state} ) { - $prob_where->{state} = $c->stash->{q_state}; + $prob_where->{'me.state'} = $c->stash->{q_state}; + $prob_where->{'me.state'} = { IN => [ 'planned', 'action scheduled' ] } + if $prob_where->{'me.state'} eq 'action scheduled'; } my $params = { %$prob_where, - 'me.confirmed' => { '>=', DateTime->now->subtract( days => 30 ) }, + 'me.confirmed' => { '>=', $dtf->format_datetime( $now->clone->subtract( days => 30 ) ) }, }; - my @problems = $c->cobrand->problems->search( $params )->all; + my $problems_rs = $c->cobrand->problems->search( $params ); + my @problems = $problems_rs->all; + my %problems; foreach (@problems) { - if ($_->confirmed >= DateTime->now->subtract(days => 7)) { + if ($_->confirmed >= $now->clone->subtract(days => 7)) { push @{$problems{1}}, $_; - } elsif ($_->confirmed >= DateTime->now->subtract(days => 14)) { + } elsif ($_->confirmed >= $now->clone->subtract(days => 14)) { push @{$problems{2}}, $_; } else { push @{$problems{3}}, $_; } } $c->stash->{lists} = \%problems; + + if ( $c->req->params->{export} ) { + $self->export_as_csv($c, $problems_rs, $body); + } +} + +sub export_as_csv { + my ($self, $c, $problems_rs, $body) = @_; + require Text::CSV; + my $problems = $problems_rs->search( + {}, { prefetch => 'comments' }); + + my $filename = do { + my %where = ( + body => $body->id, + category => $c->stash->{category}, + state => $c->stash->{q_state}, + ward => $c->stash->{ward}, + ); + join '-', + $c->req->uri->host, + map { + my $value = $where{$_}; + (defined $value and length $value) ? ($_, $value) : () + } sort keys %where }; + + my $csv = Text::CSV->new({ binary => 1, eol => "\n" }); + $csv->combine( + 'Report ID', + 'Title', + 'Detail', + 'User Name', + 'Category', + 'Created', + 'Confirmed', + 'Acknowledged', + 'Fixed', + 'Closed', + 'Status', + 'Latitude', 'Longitude', + 'Nearest Postcode', + 'Report URL', + ); + my @body = ($csv->string); + + my $fixed_states = FixMyStreet::DB::Result::Problem->fixed_states; + my $closed_states = FixMyStreet::DB::Result::Problem->closed_states; + + while ( my $report = $problems->next ) { + my $external_body; + my $body_name = ""; + if ( $external_body = $report->body($c) ) { + # seems to be a zurich specific thing + $body_name = $external_body->name if ref $external_body; + } + my $hashref = $report->as_hashref($c); + + $hashref->{user_name_display} = $report->anonymous? + '(anonymous)' : $report->user->name; + + for my $comment ($report->comments) { + my $problem_state = $comment->problem_state or next; + next if $problem_state eq 'confirmed'; + $hashref->{acknowledged_pp} //= $c->cobrand->prettify_dt( $comment->created ); + $hashref->{fixed_pp} //= $fixed_states->{ $problem_state } ? + $c->cobrand->prettify_dt( $comment->created ): undef; + if ($closed_states->{ $problem_state }) { + $hashref->{closed_pp} = $c->cobrand->prettify_dt( $comment->created ); + last; + } + } + + $csv->combine( + @{$hashref}{ + 'id', + 'title', + 'detail', + 'user_name_display', + 'category', + 'created_pp', + 'confirmed_pp', + 'acknowledged_pp', + 'fixed_pp', + 'closed_pp', + 'state', + 'latitude', 'longitude', + 'postcode', + }, + (join '', $c->cobrand->base_url_for_report($report), $report->url), + ); + + push @body, $csv->string; + } + $c->res->content_type('text/csv; charset=utf-8'); + $c->res->header('content-disposition' => "attachment; filename=${filename}.csv"); + $c->res->body( join "", @body ); } sub updates_search : Private { @@ -181,11 +291,13 @@ sub updates_search : Private { map { $_ => $counts{$_} || 0 } ('confirmed', 'investigating', 'in progress', 'closed', 'fixed - council', 'fixed - user', 'fixed', 'unconfirmed', 'hidden', - 'partial', 'planned'); + 'partial', 'action scheduled', 'planned'); + + $counts{'action scheduled'} += $counts{planned} || 0; for my $vars ( [ 'time_to_fix', 'fixed - council' ], - [ 'time_to_mark', 'in progress', 'planned', 'investigating', 'closed' ], + [ 'time_to_mark', 'in progress', 'action scheduled', 'investigating', 'closed' ], ) { my $col = shift @$vars; my $substmt = "select min(id) from comment where me.problem_id=comment.problem_id and problem_state in ('" diff --git a/perllib/FixMyStreet/App/Controller/FakeMapit.pm b/perllib/FixMyStreet/App/Controller/FakeMapit.pm index bc46df712..253c75ba4 100755 --- a/perllib/FixMyStreet/App/Controller/FakeMapit.pm +++ b/perllib/FixMyStreet/App/Controller/FakeMapit.pm @@ -12,13 +12,13 @@ FixMyStreet::App::Controller::FakeMapit - Catalyst Controller A controller to fake mapit when we don't have it. If you set MAPIT_URL to .../fakemapit/ it should all just work, with a mapit that assumes the whole -world is one area, with ID 161 and name "Default Area". +world is one area, with ID 161 and name "Everywhere". =head1 METHODS =cut -my $area = { "name" => "Default Area", "type" => "ZZZ", "id" => 161 }; +my $area = { "name" => "Everywhere", "type" => "ZZZ", "id" => 161 }; sub output : Private { my ( $self, $c, $data ) = @_; 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..17507a84b 100644 --- a/perllib/FixMyStreet/App/Controller/JSON.pm +++ b/perllib/FixMyStreet/App/Controller/JSON.pm @@ -8,6 +8,7 @@ use JSON; use DateTime; use DateTime::Format::ISO8601; use List::MoreUtils 'uniq'; +use FixMyStreet::App; =head1 NAME @@ -80,11 +81,13 @@ sub problems : Local { $date_col = 'lastupdate'; } + my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser; + my $one_day = DateTime::Duration->new( days => 1 ); my $query = { $date_col => { - '>=' => $start_dt, - '<=' => $end_dt + $one_day, + '>=' => $dt_parser->format_datetime($start_dt), + '<=' => $dt_parser->format_datetime($end_dt + $one_day), }, state => [ @state ], }; @@ -92,7 +95,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 +103,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/Location.pm b/perllib/FixMyStreet/App/Controller/Location.pm index e8bf2cd1c..4312b6911 100644 --- a/perllib/FixMyStreet/App/Controller/Location.pm +++ b/perllib/FixMyStreet/App/Controller/Location.pm @@ -5,6 +5,7 @@ use namespace::autoclean; BEGIN {extends 'Catalyst::Controller'; } use Encode; +use FixMyStreet::Geocode; =head1 NAME @@ -49,6 +50,8 @@ sub determine_location_from_coords : Private { User has searched for a location - try to find it for them. +Return -1 if nothing provided. + If one match is found returns true and lat/lng is set. If several possible matches are found puts an array onto stash so that user can be prompted to pick one and returns false. @@ -61,7 +64,7 @@ sub determine_location_from_pc : Private { my ( $self, $c, $pc ) = @_; # check for something to search - $pc ||= $c->req->param('pc') || return; + $pc ||= $c->req->param('pc') || return -1; $c->stash->{pc} = $pc; # for template if ( $pc =~ /^(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)$/ ) { @@ -100,6 +103,7 @@ sub determine_location_from_pc : Private { } # pass errors back to the template + $c->stash->{location_error_pc_lookup} = 1; $c->stash->{location_error} = $error; return; } diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm index c00264315..bbef1f8d8 100644 --- a/perllib/FixMyStreet/App/Controller/My.pm +++ b/perllib/FixMyStreet/App/Controller/My.pm @@ -45,6 +45,7 @@ sub my : Path : Args(0) { } )->page( $p_page ); while ( my $problem = $rs->next ) { + $c->stash->{has_content}++; push @$pins, { latitude => $problem->latitude, longitude => $problem->longitude, @@ -64,7 +65,9 @@ sub my : Path : Args(0) { order_by => { -desc => 'confirmed' }, rows => 50 } )->page( $u_page ); + my @updates = $rs->all; + $c->stash->{has_content} += scalar @updates; $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 index 040b0d3e6..f3841acef 100644 --- a/perllib/FixMyStreet/App/Controller/Open311.pm +++ b/perllib/FixMyStreet/App/Controller/Open311.pm @@ -100,7 +100,7 @@ sub error : Private { sub get_discovery : Private { my ( $self, $c ) = @_; - my $contact_email = $c->config->{CONTACT_EMAIL}; + my $contact_email = $c->cobrand->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'; @@ -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 = @@ -248,19 +240,23 @@ sub output_requests : Private { 'long' => [ $problem->longitude ], 'status' => [ $problem->state ], # 'status_notes' => [ {} ], - 'requested_datetime' => [ w3date($problem->confirmed_local) ], - 'updated_datetime' => [ w3date($problem->lastupdate_local) ], + 'requested_datetime' => [ w3date($problem->confirmed) ], + 'updated_datetime' => [ w3date($problem->lastupdate) ], # '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 ( $c->cobrand->moniker ne 'zurich' ) { # XXX + # FIXME Not according to Open311 v2 + $request->{agency_responsible} = $problem->bodies; + } + if ( !$problem->anonymous ) { # Not in Open311 v2 $request->{'requestor_name'} = [ $problem->name ]; @@ -268,7 +264,7 @@ sub output_requests : Private { if ( $problem->whensent ) { # Not in Open311 v2 $request->{'agency_sent_datetime'} = - [ w3date($problem->whensent_local) ]; + [ w3date($problem->whensent) ]; } # Extract number of updates @@ -280,7 +276,7 @@ sub output_requests : Private { $request->{'comment_count'} = [ $updates ]; } - my $display_photos = $c->cobrand->allow_photo_display; + my $display_photos = $c->cobrand->allow_photo_display($problem); if ($display_photos && $problem->photo) { my $url = $c->cobrand->base_url(); my $imgurl = $url . "/photo/$id.full.jpeg"; @@ -288,12 +284,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', [ { @@ -311,15 +307,17 @@ sub get_requests : Private { my $max_requests = $c->req->param('max_requests') || 0; # Only provide access to the published reports + my $states = FixMyStreet::DB::Result::Problem->visible_states(); + delete $states->{unconfirmed}; my $criteria = { - state => [ FixMyStreet::DB::Result::Problem->visible_states() ] + state => [ keys %$states ] }; my %rules = ( service_request_id => [ '=', 'id' ], service_code => [ '=', 'category' ], status => [ 'IN', 'state' ], - agency_responsible => [ '~', 'council' ], + agency_responsible => [ '~', 'bodies_str' ], interface_used => [ '=', 'service' ], has_photo => [ '=', 'photo' ], ); @@ -411,8 +409,10 @@ sub get_request : Private { return; } + my $states = FixMyStreet::DB::Result::Problem->visible_states(); + delete $states->{unconfirmed}; my $criteria = { - state => [ FixMyStreet::DB::Result::Problem->visible_states() ], + state => [ keys %$states ], id => $id, }; $c->forward( 'output_requests', [ $criteria ] ); diff --git a/perllib/FixMyStreet/App/Controller/Photo.pm b/perllib/FixMyStreet/App/Controller/Photo.pm index fa4baf045..09afabecf 100644 --- a/perllib/FixMyStreet/App/Controller/Photo.pm +++ b/perllib/FixMyStreet/App/Controller/Photo.pm @@ -5,7 +5,7 @@ use namespace::autoclean; BEGIN {extends 'Catalyst::Controller'; } use DateTime::Format::HTTP; -use Digest::SHA1 qw(sha1_hex); +use Digest::SHA qw(sha1_hex); use File::Path; use File::Slurp; use Path::Class; @@ -30,17 +30,19 @@ Display a photo =cut -sub during :LocalRegex('^([0-9a-f]{40})\.temp\.jpeg$') { +sub during :LocalRegex('^([0-9a-f]{40})\.(temp|fulltemp)\.jpeg$') { my ( $self, $c ) = @_; - my ( $hash ) = @{ $c->req->captures }; + my ( $hash, $size ) = @{ $c->req->captures }; my $file = file( $c->config->{UPLOAD_DIR}, "$hash.jpeg" ); my $photo = $file->slurp; - if ( $c->cobrand->default_photo_resize ) { - $photo = _shrink( $photo, $c->cobrand->default_photo_resize ); - } else { - $photo = _shrink( $photo, '250x250' ); + if ( $size eq 'temp' ) { + if ( $c->cobrand->default_photo_resize ) { + $photo = _shrink( $photo, $c->cobrand->default_photo_resize ); + } else { + $photo = _shrink( $photo, '250x250' ); + } } $c->forward( 'output', [ $photo ] ); @@ -73,7 +75,9 @@ sub index :LocalRegex('^(c/)?(\d+)(?:\.(full|tn|fp))?\.jpeg$') { $c->detach( 'no_photo' ) unless @photo; - my $photo = $photo[0]->photo; + my $item = $photo[0]; + $c->detach( 'no_photo' ) unless $c->cobrand->allow_photo_display($item); # Should only be for reports, not updates + my $photo = $item->photo; #Â If photo field contains a hash if (length($photo) == 40) { @@ -102,10 +106,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..13a347a90 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,25 @@ 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; + if ( $c->sessionid && $c->flash->{created_report} ) { + $c->stash->{created_report} = $c->flash->{created_report}; + } $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( + { + report => $c->cobrand->problem_as_hashref( $problem, $c ), + updates => $c->cobrand->updates_as_hashref( $problem, $c ), + } + ); + $c->res->body( $content ); + return 1; + } + return 1; } @@ -168,7 +202,7 @@ sub generate_map_tags : Private { ? [ { latitude => $problem->latitude, longitude => $problem->longitude, - colour => 'yellow', + colour => $c->cobrand->pin_colour($problem, 'report'), type => 'big', } ] : [], @@ -187,11 +221,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->id}; $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 169c3d152..1e9f83aec 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -4,7 +4,6 @@ use Moose; use namespace::autoclean; BEGIN { extends 'Catalyst::Controller'; } -use FixMyStreet::Geocode; use Encode; use List::MoreUtils qw(uniq); use POSIX 'strcoll'; @@ -90,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'); @@ -120,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'); @@ -148,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 }; } @@ -181,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'); @@ -190,11 +189,14 @@ sub report_form_ajax : Path('ajax') : Args(0) { ? $c->render_fragment('report/new/extra_name.html') : ''; + my $extra_titles_list = $c->cobrand->title_list($c->stash->{all_areas}); + my $body = JSON->new->utf8(1)->encode( { councils_text => $councils_text, category => $category, extra_name_info => $extra_name_info, + titles_list => $extra_titles_list, categories => $c->stash->{category_options}, } ); @@ -217,7 +219,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 ) { @@ -570,29 +572,35 @@ 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 ], deleted => 0 }, + { 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 my %non_public_categories = (); # categories for which the reports are not public @@ -600,9 +608,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 @@ -615,22 +623,18 @@ sub setup_categories_and_councils : Private { _('Empty pub or bar'), _('Empty public building - school, hospital, etc.') ); - $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->{id} != COUNCIL_ID_BARNET + && $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) { - @local_categories = sort keys %{ Utils::barnet_categories() } - } else { - @local_categories = sort keys %{ Utils::london_categories() } - } + @local_categories = sort keys %{ Utils::london_categories() }; @category_options = ( _('-- Pick a category --'), @local_categories ); - $category_label = _('Category'); } else { @@ -640,7 +644,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; @@ -657,29 +661,34 @@ sub setup_categories_and_councils : Private { # If there's an Other category present, put it at the bottom @category_options = ( _('-- Pick a category --'), grep { $_ ne _('Other') } @category_options ); push @category_options, _('Other') if $seen{_('Other')}; - $category_label = _('Category'); } } + if ($c->cobrand->can('hidden_categories')) { + my %hidden_categories = map { $_ => 1 } + $c->cobrand->hidden_categories; + + @category_options = grep { + !$hidden_categories{$_} + } @category_options; + } + # put results onto stash for display - $c->stash->{area_ids_to_list} = [ keys %area_ids_to_list ]; - $c->stash->{category_label} = $category_label; + $c->stash->{bodies} = \%bodies; + $c->stash->{all_body_names} = [ map { $_->name } values %bodies ]; + $c->stash->{all_body_urls} = [ map { $_->external_url } values %bodies ]; + $c->stash->{bodies_to_list} = [ keys %bodies_to_list ]; $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; + $c->stash->{extra_name_info} = $first_area->{id} == COUNCIL_ID_BROMLEY ? 1 : 0; - my @missing_details_councils = - grep { !$area_ids_to_list{$_} } # - keys %$all_councils; + my @missing_details_bodies = grep { !$bodies_to_list{$_->id} } values %bodies; + my @missing_details_body_names = map { $_->name } @missing_details_bodies; - my @missing_details_council_names = - map { $all_councils->{$_}->{name} } # - @missing_details_councils; - - $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 @@ -788,6 +797,7 @@ sub process_report : Private { 'category', # 'subcategory', # 'partial', # + 'service', # ); # load the report @@ -797,6 +807,7 @@ sub process_report : Private { $report->postcode( $params{pc} ); $report->latitude( $c->stash->{latitude} ); $report->longitude( $c->stash->{longitude} ); + $report->send_questionnaire( $c->cobrand->send_questionnaires() ); # set some simple bool values (note they get inverted) $report->anonymous( $params{may_show_name} ? 0 : 1 ); @@ -812,65 +823,71 @@ sub process_report : Private { } $report->detail( $detail ); + # mobile device type + $report->service( $params{service} ) if $params{service}; + # set these straight from the params $report->category( _ $params{category} ) if $params{category}; $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 ) { - - unless ( exists Utils::barnet_categories()->{ $report->category } ) { - $c->stash->{field_errors}->{category} = _('Please choose a category'); + my %extra; + $c->cobrand->process_extras( $c, undef, \%extra ); + if ( %extra ) { + $report->extra( \%extra ); } - $report->council( $first_council->{id} ); - - } elsif ( $first_council->{id} != COUNCIL_ID_BROMLEY && $first_council->{type} eq 'LBO') { - + + } elsif ($first_area->{id} != COUNCIL_ID_BROMLEY + && $first_area->{id} != COUNCIL_ID_BARNET + && $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; @@ -892,13 +909,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'); @@ -907,7 +924,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 ); } @@ -936,6 +953,22 @@ sub check_for_errors : Private { $c->stash->{field_errors} ||= {}; my %field_errors = $c->cobrand->report_check_for_errors( $c ); + # Zurich, we don't care about title or name + # There is no title, and name is optional + 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) ) ); + + # We only want to validate the phone number web requests (where the + # service parameter is blank) because previous versions of the mobile + # apps don't validate the presence of a phone number. + if ( ! $c->req->param('phone') and ! $c->req->param('service') ) { + $field_errors{phone} = _("This information is required"); + } + } + # FIXME: need to check for required bromley fields here # if they're got the login details wrong when signing in then @@ -996,8 +1029,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 { @@ -1022,7 +1057,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+$/) { @@ -1103,7 +1138,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'); @@ -1113,6 +1148,9 @@ sub redirect_or_confirm_creation : Private { $report_uri = $c->cobrand->base_url_for_report( $report ) . $report->url; } $c->log->info($report->user->id . ' was logged in, redirecting to /report/' . $report->id); + if ( $c->sessionid ) { + $c->flash->{created_report} = 'loggedin'; + } $c->res->redirect($report_uri); $c->detach; } @@ -1128,7 +1166,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 da4cc33ca..bc79cafd3 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,14 +201,29 @@ 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 + # for use when sending updates to external parties + if ( $update->mark_fixed ) { + $update->problem_state( 'fixed - user' ); + } elsif ( $update->mark_open ) { + $update->problem_state( 'confirmed' ); + # if there is not state param and neither of the above conditions apply + # then we are not changing the state of the problem so can use the current + # problem state + } else { + my $problem = $c->stash->{problem} || $update->problem; + $update->problem_state( $problem->state ); + } } + 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 = (); @@ -246,10 +261,11 @@ 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'); - $error = 1 unless ( grep { $state eq $_ } ( qw/confirmed closed fixed investigating planned/, 'in progress', 'fixed', 'fixed - user', 'fixed - council' ) ); + $state = 'fixed - council' if $state eq 'fixed'; + $error = 1 unless ( grep { $state eq $_ } ( FixMyStreet::DB::Result::Problem->council_states() ) ); if ( $error ) { $c->stash->{errors} ||= []; @@ -296,7 +312,14 @@ sub save_update : Private { my $update = $c->stash->{update}; - if ( !$update->user->in_storage ) { + if ( $c->cobrand->never_confirm_updates ) { + if ( $update->user->in_storage() ) { + $update->user->update(); + } else { + $update->user->insert(); + } + $update->confirm(); + } elsif ( !$update->user->in_storage ) { # User does not exist. # Store changes in token for when token is validated. $c->stash->{token_data} = { @@ -354,6 +377,7 @@ sub redirect_or_confirm_creation : Private { $c->forward( 'signup_for_alerts' ); my $report_uri = $c->cobrand->base_url_for_report( $update->problem ) . $update->problem->url; + $c->flash->{comment_created} = 1; $c->res->redirect($report_uri); $c->detach; } diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm index ec41dc17f..7e0cccc7b 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,31 @@ 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}; - } + if ( my $body = $c->cobrand->all_reports_single_body ) { + $c->stash->{body} = $body; + $c->detach( 'redirect_body' ); } - $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,97 +97,165 @@ 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(); my $pins = $c->stash->{pins}; $c->stash->{page} = 'reports'; # So the map knows to make clickable pins - FixMyStreet::Map::display_map( - $c, + 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}, - pins => $pins, + area => $c->stash->{ward} ? $c->stash->{ward}->{id} : [ keys %{$c->stash->{body}->areas} ], any_zoom => 1, ); + if ( $c->cobrand->moniker eq 'emptyhomes' ) { + FixMyStreet::Map::display_map( + $c, %map_params, latitude => 0, longitude => 0, + ); + } else { + FixMyStreet::Map::display_map( + $c, %map_params, pins => $pins, + ); + } $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} ], + if ( !$c->stash->{ward} && $c->stash->{body}->id && $c->stash->{body}->body_areas->first ) { + 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' ); + # XXX Currently body/area overlaps here are a bit muddy. + # We're checking an area here, but this function is currently doing that. + return if $c->cobrand->reports_body_check( $c, $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->detach( 'redirect_index' ) unless $c->stash->{area}; + + $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 @@ -198,60 +264,47 @@ 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 ( $c->cobrand->reports_by_body ) { - my $problem = $c->cobrand->problems->search({ 'lower(me.external_body)' => lc $q_council }, { 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 }; - return; - } + 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' ); } # 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' ); } @@ -259,7 +312,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 @@ -270,48 +323,45 @@ 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; + $c->detach( 'redirect_body' ) unless $parent_id; + $parent_id = $parent_id->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->stash->{body} = $c->stash->{area} + unless $c->stash->{body}; + $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 { @@ -325,69 +375,54 @@ 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) { - # 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} }, + } 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} ) { - # An external_body entry - add_row( \%problem, 0, \%problems, \@pins ); + while ( my $problem = $problems->next ) { + $c->log->debug( $problem->cobrand . ', cobrand is ' . $c->cobrand->moniker ); + if ( !$c->stash->{body} ) { + 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 + my $a = $problem->areas; # Store, as otherwise is looked up every iteration. + while ($a =~ /,(\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 ); } } } @@ -406,26 +441,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..ed47f6f87 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; @@ -271,7 +280,7 @@ sub add_row : Private { $item{pubDate} = $pubDate if $pubDate; $item{category} = $row->{category} if $row->{category}; - if ($c->cobrand->allow_photo_display && $row->{photo}) { + if ($c->cobrand->allow_photo_display($row) && $row->{photo}) { my $key = $alert_type->item_table eq 'comment' ? 'c/' : ''; $item{description} .= ent("\n<br><img src=\"". $base_url . "/photo/$key$row->{id}.jpeg\">"); } diff --git a/perllib/FixMyStreet/App/Controller/Static.pm b/perllib/FixMyStreet/App/Controller/Static.pm index 723f0f2e1..40e2431ea 100755 --- a/perllib/FixMyStreet/App/Controller/Static.pm +++ b/perllib/FixMyStreet/App/Controller/Static.pm @@ -19,7 +19,10 @@ template depending on language, will need extending at some point. sub about : Global : Args(0) { my ( $self, $c ) = @_; - # don't need to do anything here - should just pass through. + + my $lang_code = $c->stash->{lang_code}; + my $template = "static/about-$lang_code.html"; + $c->stash->{template} = $template; } sub privacy : Global : Args(0) { @@ -54,6 +57,10 @@ sub iphone : Global : Args(0) { my ( $self, $c ) = @_; } +sub council : Global : Args(0) { + my ( $self, $c ) = @_; +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm index 03dc69b00..44cb2429d 100644 --- a/perllib/FixMyStreet/App/Controller/Tokens.pm +++ b/perllib/FixMyStreet/App/Controller/Tokens.pm @@ -53,6 +53,25 @@ 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()', + } ); + + 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( @@ -83,6 +102,7 @@ sub confirm_problem : Path('/P') { $c->res->redirect($report_uri); } + $c->stash->{created_report} = 'fromemail'; return 1; } @@ -176,7 +196,12 @@ sub confirm_update : Path('/C') { $c->authenticate( { email => $comment->user->email }, 'no_password' ); $c->set_session_cookie_expire(0); - $c->forward('/report/update/confirm'); + if ( $comment->confirmed ) { + my $report_uri = $c->cobrand->base_url_for_report( $comment->problem ) . $comment->problem->url; + $c->res->redirect($report_uri); + } else { + $c->forward('/report/update/confirm'); + } return 1; } |