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