aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/App')
-rwxr-xr-xperllib/FixMyStreet/App/Controller/About.pm67
-rw-r--r--perllib/FixMyStreet/App/Controller/Admin.pm99
-rw-r--r--perllib/FixMyStreet/App/Controller/Around.pm13
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm144
-rw-r--r--perllib/FixMyStreet/App/Controller/Dashboard.pm3
-rwxr-xr-xperllib/FixMyStreet/App/Controller/FakeMapit.pm3
-rw-r--r--perllib/FixMyStreet/App/Controller/JSON.pm5
-rw-r--r--perllib/FixMyStreet/App/Controller/Location.pm3
-rw-r--r--perllib/FixMyStreet/App/Controller/My.pm7
-rw-r--r--perllib/FixMyStreet/App/Controller/Open311.pm2
-rw-r--r--perllib/FixMyStreet/App/Controller/Photo.pm98
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm4
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm183
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/Update.pm170
-rw-r--r--perllib/FixMyStreet/App/Controller/Reports.pm3
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Rss.pm14
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Static.pm51
-rwxr-xr-xperllib/FixMyStreet/App/Controller/Status.pm5
-rw-r--r--perllib/FixMyStreet/App/Controller/Tokens.pm9
-rw-r--r--perllib/FixMyStreet/App/Model/DB.pm2
-rw-r--r--perllib/FixMyStreet/App/Model/EmailSend.pm55
-rw-r--r--perllib/FixMyStreet/App/Model/PhotoSet.pm111
22 files changed, 643 insertions, 408 deletions
diff --git a/perllib/FixMyStreet/App/Controller/About.pm b/perllib/FixMyStreet/App/Controller/About.pm
new file mode 100755
index 000000000..78e548c5f
--- /dev/null
+++ b/perllib/FixMyStreet/App/Controller/About.pm
@@ -0,0 +1,67 @@
+package FixMyStreet::App::Controller::About;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+FixMyStreet::App::Controller::About - Catalyst Controller
+
+=head1 DESCRIPTION
+
+About pages Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+my %found;
+
+sub page : Path("/about") : Args(1) {
+ my ( $self, $c, $page ) = @_;
+ my $template = $c->forward('find_template');
+ $c->detach('/page_error_404_not_found', []) unless $template;
+ $c->stash->{template} = $template;
+}
+
+sub index : Path("/about") : Args(0) {
+ my ( $self, $c ) = @_;
+ $c->forward('page', [ 'about' ]);
+}
+
+# We have multiple possibilities to try, and we want to cache where we find it
+sub find_template : Private {
+ my ( $self, $c, $page ) = @_;
+
+ return $found{$page} if !FixMyStreet->config('STAGING_SITE') && exists $found{$page};
+
+ my $lang_code = $c->stash->{lang_code};
+ foreach my $dir_templates (@{$c->stash->{additional_template_paths}}, @{$c->view('Web')->paths}) {
+ foreach my $dir_static (static_dirs($page, $dir_templates)) {
+ foreach my $file ("$page-$lang_code.html", "$page.html") {
+ if (-e "$dir_templates/$dir_static/$file") {
+ $found{$page} = "$dir_static/$file";
+ return $found{$page};
+ }
+ }
+ }
+ }
+ # Cache that the page does not exist, so we don't look next time
+ $found{$page} = undef;
+ return $found{$page};
+}
+
+sub static_dirs {
+ my ($page, $dir_templates) = @_;
+ my @v = ("about");
+ # If legacy directories exist, check for templates there too;
+ # The FAQ page used to be in its own directory
+ push @v, "static" if -d "$dir_templates/static";
+ push @v, "faq" if -d "$dir_templates/faq" && $page =~ /faq/;
+ return @v;
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/perllib/FixMyStreet/App/Controller/Admin.pm b/perllib/FixMyStreet/App/Controller/Admin.pm
index a61032988..2bf215c56 100644
--- a/perllib/FixMyStreet/App/Controller/Admin.pm
+++ b/perllib/FixMyStreet/App/Controller/Admin.pm
@@ -84,14 +84,14 @@ sub index : Path : Args(0) {
for ( FixMyStreet::DB::Result::Problem->visible_states() );
$c->stash->{total_problems_users} = $c->cobrand->problems->unique_users;
- my $comments = $c->model('DB::Comment')->summary_count( $c->cobrand->body_restriction );
+ my $comments = $c->cobrand->updates->summary_count;
my %comment_counts =
map { $_->state => $_->get_column('state_count') } $comments->all;
$c->stash->{comments} = \%comment_counts;
- my $alerts = $c->model('DB::Alert')->summary_count( $c->cobrand->restriction );
+ my $alerts = $c->model('DB::Alert')->summary_report_alerts( $c->cobrand->restriction );
my %alert_counts =
map { $_->confirmed => $_->get_column('confirmed_count') } $alerts->all;
@@ -171,7 +171,7 @@ sub timeline : Path( 'timeline' ) : Args(0) {
push @{$time{$_->whenanswered->epoch}}, { type => 'quesAnswered', date => $_->whenanswered, obj => $_ } if $_->whenanswered;
}
- my $updates = $c->model('DB::Comment')->timeline( $c->cobrand->body_restriction );
+ my $updates = $c->cobrand->updates->timeline;
foreach ($updates->all) {
push @{$time{$_->created->epoch}}, { type => 'update', date => $_->created, obj => $_} ;
@@ -622,9 +622,7 @@ sub reports : Path('reports') {
}
if (@$query) {
- my $updates = $c->model('DB::Comment')
- ->to_body($c->cobrand->body_restriction)
- ->search(
+ my $updates = $c->cobrand->updates->search(
{
-or => $query,
},
@@ -685,7 +683,7 @@ sub report_edit : Path('report_edit') : Args(1) {
}
if (my $rotate_photo_param = $self->_get_rotate_photo_param($c)) {
- $self->rotate_photo($c, @$rotate_photo_param);
+ $self->rotate_photo($c, $problem, @$rotate_photo_param);
if ( $c->cobrand->moniker eq 'zurich' ) {
# Clicking the photo rotation buttons should do nothing
# except for rotating the photo, so return the user
@@ -792,11 +790,12 @@ sub report_edit : Path('report_edit') : Args(1) {
}
# Deal with photos
- if ( $c->get_param('remove_photo') ) {
- $problem->photo(undef);
+ my $remove_photo_param = $self->_get_remove_photo_param($c);
+ if ($remove_photo_param) {
+ $self->remove_photo($c, $problem, $remove_photo_param);
}
- if ( $c->get_param('remove_photo') || $new_state eq 'hidden' ) {
+ if ( $remove_photo_param || $new_state eq 'hidden' ) {
unlink glob FixMyStreet->path_to( 'web', 'photo', $problem->id . '.*' );
}
@@ -967,9 +966,7 @@ sub users: Path('users') : Args(0) {
sub update_edit : Path('update_edit') : Args(1) {
my ( $self, $c, $id ) = @_;
- my $update = $c->model('DB::Comment')
- ->to_body($c->cobrand->body_restriction)
- ->search({ id => $id })->first;
+ my $update = $c->cobrand->updates->search({ id => $id })->first;
$c->detach( '/page_error_404_not_found' )
unless $update;
@@ -978,6 +975,11 @@ sub update_edit : Path('update_edit') : Args(1) {
$c->stash->{update} = $update;
+ if (my $rotate_photo_param = $self->_get_rotate_photo_param($c)) {
+ $self->rotate_photo($c, $update, @$rotate_photo_param);
+ return 1;
+ }
+
$c->forward('check_email_for_abuse', [ $update->user->email ] );
if ( $c->get_param('banuser') ) {
@@ -1007,13 +1009,14 @@ sub update_edit : Path('update_edit') : Args(1) {
|| $c->get_param('anonymous') ne $update->anonymous
|| $c->get_param('text') ne $update->text ) {
$edited = 1;
- }
+ }
- if ( $c->get_param('remove_photo') ) {
- $update->photo(undef);
+ my $remove_photo_param = $self->_get_remove_photo_param($c);
+ if ($remove_photo_param) {
+ $self->remove_photo($c, $update, $remove_photo_param);
}
- if ( $c->get_param('remove_photo') || $new_state eq 'hidden' ) {
+ if ( $remove_photo_param || $new_state eq 'hidden' ) {
unlink glob FixMyStreet->path_to( 'web', 'photo', 'c', $update->id . '.*' );
}
@@ -1076,16 +1079,18 @@ sub user_add : Path('user_edit') : Args(0) {
$c->forward('get_token');
$c->forward('fetch_all_bodies');
- return 1 unless $c->get_param('submit');
+ return unless $c->get_param('submit');
$c->forward('check_token');
- if ( $c->cobrand->moniker eq 'zurich' and $c->get_param('email') eq '' ) {
+ unless ($c->get_param('email')) {
$c->stash->{field_errors}->{email} = _('Please enter a valid email');
- return 1;
+ return;
+ }
+ unless ($c->get_param('name')) {
+ $c->stash->{field_errors}->{name} = _('Please enter a name');
+ return;
}
-
- return unless $c->get_param('name') && $c->get_param('email');
my $user = $c->model('DB::User')->find_or_create( {
name => $c->get_param('name'),
@@ -1133,12 +1138,16 @@ sub user_edit : Path('user_edit') : Args(1) {
$user->from_body( $c->get_param('body') || undef );
$user->flagged( $c->get_param('flagged') || 0 );
- if ( $c->cobrand->moniker eq 'zurich' and $user->email eq '' ) {
+ unless ($user->email) {
$c->stash->{field_errors}->{email} = _('Please enter a valid email');
- return 1;
+ return;
+ }
+ unless ($user->name) {
+ $c->stash->{field_errors}->{name} = _('Please enter a name');
+ return;
}
- $user->update;
+ $user->update;
if ($edited) {
$c->forward( 'log_edit', [ $id, 'user', 'edit' ] );
}
@@ -1316,9 +1325,9 @@ Generate a token based on user and secret
sub get_token : Private {
my ( $self, $c ) = @_;
- my $secret = $c->model('DB::Secret')->search()->first;
+ my $secret = $c->model('DB::Secret')->get;
my $user = $c->forward('get_user');
- my $token = sha1_hex($user . $secret->secret);
+ my $token = sha1_hex($user . $secret);
$c->stash->{token} = $token;
return 1;
@@ -1486,25 +1495,51 @@ sub _get_rotate_photo_param {
my $key = first { /^rotate_photo/ } keys %{ $c->req->params } or return;
my ($index) = $key =~ /(\d+)$/;
my $direction = $c->get_param($key);
- return [ $index || 0, $key, $direction ];
+ return [ $index || 0, $direction ];
}
sub rotate_photo : Private {
- my ( $self, $c, $index, $key, $direction ) = @_;
+ my ( $self, $c, $object, $index, $direction ) = @_;
return unless $direction eq _('Rotate Left') or $direction eq _('Rotate Right');
- my $problem = $c->stash->{problem};
- my $fileid = $problem->get_photoset($c)->rotate_image(
+ my $fileid = $object->get_photoset->rotate_image(
$index,
$direction eq _('Rotate Left') ? -90 : 90
) or return;
- $problem->update({ photo => $fileid });
+ $object->update({ photo => $fileid });
return 1;
}
+=head2 remove_photo
+
+Remove a photo from a report
+
+=cut
+
+# Returns index of photo(s) to remove, if any
+sub _get_remove_photo_param {
+ my ($self, $c) = @_;
+
+ return 'ALL' if $c->get_param('remove_photo');
+
+ my @keys = map { /(\d+)$/ } grep { /^remove_photo_/ } keys %{ $c->req->params } or return;
+ return \@keys;
+}
+
+sub remove_photo : Private {
+ my ($self, $c, $object, $keys) = @_;
+ if ($keys eq 'ALL') {
+ $object->photo(undef);
+ } else {
+ my $fileids = $object->get_photoset->remove_images($keys);
+ $object->photo($fileids);
+ }
+ return 1;
+}
+
=head2 check_page_allowed
Checks if the current catalyst action is in the list of allowed pages and
diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm
index 4aa695ae5..1e6d9ec9e 100644
--- a/perllib/FixMyStreet/App/Controller/Around.pm
+++ b/perllib/FixMyStreet/App/Controller/Around.pm
@@ -6,6 +6,7 @@ BEGIN { extends 'Catalyst::Controller'; }
use FixMyStreet::Map;
use Encode;
+use JSON::MaybeXS;
use Utils;
=head1 NAME
@@ -306,7 +307,7 @@ sub ajax : Path('/ajax') {
# JSON encode the response
my $json = { pins => $pins };
$json->{current} = $on_map_list_html if $on_map_list_html;
- my $body = JSON->new->utf8(1)->encode($json);
+ my $body = encode_json($json);
$c->res->body($body);
}
@@ -350,8 +351,10 @@ sub _geocode : Private {
} else {
if ( ref($suggestions) eq 'ARRAY' ) {
foreach (@$suggestions) {
- push @addresses, decode_utf8($_->{address});
- push @locations, { address => decode_utf8($_->{address}), lat => $_->{latitude}, long => $_->{longitude} };
+ my $address = $_->{address};
+ $address = decode_utf8($address) if !utf8::is_utf8($address);
+ push @addresses, $address;
+ push @locations, { address => $address, lat => $_->{latitude}, long => $_->{longitude} };
}
$response = { suggestions => \@addresses, locations => \@locations };
} else {
@@ -363,9 +366,7 @@ sub _geocode : Private {
$response = \@addresses;
}
- my $body = JSON->new->utf8(1)->encode(
- $response
- );
+ my $body = encode_json($response);
$c->res->body($body);
}
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm
index 6de416c53..9e8fb29aa 100644
--- a/perllib/FixMyStreet/App/Controller/Auth.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth.pm
@@ -7,7 +7,8 @@ BEGIN { extends 'Catalyst::Controller'; }
use Email::Valid;
use Net::Domain::TLD;
use mySociety::AuthToken;
-use JSON;
+use JSON::MaybeXS;
+use Net::Facebook::Oauth2;
=head1 NAME
@@ -36,6 +37,8 @@ sub general : Path : Args(0) {
return unless $c->req->method eq 'POST';
# decide which action to take
+ $c->detach('facebook_sign_in') if $c->get_param('facebook_sign_in');
+
my $clicked_password = $c->get_param('sign_in');
my $clicked_email = $c->get_param('email_sign_in');
my $data_password = $c->get_param('password_sign_in');
@@ -122,18 +125,19 @@ sub email_sign_in : Private {
if $c->get_param('password_register');
my $user = $c->model('DB::User')->new( $user_params );
- my $token_obj = $c->model('DB::Token') #
- ->create(
- {
- scope => 'email_sign_in',
- data => {
- email => $good_email,
- r => $c->get_param('r'),
- name => $c->get_param('name'),
- password => $user->password,
- }
- }
- );
+ my $token_data = {
+ email => $good_email,
+ r => $c->get_param('r'),
+ name => $c->get_param('name'),
+ password => $user->password,
+ };
+ $token_data->{facebook_id} = $c->session->{oauth}{facebook_id}
+ if $c->get_param('oauth_need_email') && $c->session->{oauth}{facebook_id};
+
+ my $token_obj = $c->model('DB::Token')->create({
+ scope => 'email_sign_in',
+ data => $token_data,
+ });
$c->stash->{token} = $token_obj->token;
$c->send_email( 'login.txt', { to => $good_email } );
@@ -175,6 +179,7 @@ sub token : Path('/M') : Args(1) {
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};
+ $user->facebook_id( $data->{facebook_id} ) if $data->{facebook_id};
$user->update;
$c->authenticate( { email => $user->email }, 'no_password' );
@@ -182,6 +187,113 @@ sub token : Path('/M') : Args(1) {
$c->detach( 'redirect_on_signin', [ $data->{r} ] );
}
+=head2 facebook_sign_in
+
+Starts the Facebook authentication sequence.
+
+=cut
+
+sub fb : Private {
+ my ($self, $c) = @_;
+ Net::Facebook::Oauth2->new(
+ application_id => $c->config->{FACEBOOK_APP_ID},
+ application_secret => $c->config->{FACEBOOK_APP_SECRET},
+ callback => $c->uri_for('/auth/Facebook'),
+ );
+}
+
+sub facebook_sign_in : Private {
+ my( $self, $c ) = @_;
+
+ my $fb = $c->forward('/auth/fb');
+ my $url = $fb->get_authorization_url(scope => ['email']);
+
+ my %oauth;
+ $oauth{return_url} = $c->get_param('r');
+ $oauth{detach_to} = $c->stash->{detach_to};
+ $oauth{detach_args} = $c->stash->{detach_args};
+ $c->session->{oauth} = \%oauth;
+ $c->res->redirect($url);
+}
+
+=head2 facebook_callback
+
+Handles the Facebook callback request and completes the authentication sequence.
+
+=cut
+
+sub facebook_callback: Path('/auth/Facebook') : Args(0) {
+ my( $self, $c ) = @_;
+
+ if ( $c->get_param('error_code') ) {
+ $c->stash->{oauth_failure} = 1;
+ if ($c->session->{oauth}{detach_to}) {
+ $c->detach($c->session->{oauth}{detach_to}, $c->session->{oauth}{detach_args});
+ } else {
+ $c->stash->{template} = 'auth/general.html';
+ $c->detach;
+ }
+ }
+
+ my $fb = $c->forward('/auth/fb');
+ my $access_token;
+ eval {
+ $access_token = $fb->get_access_token(code => $c->get_param('code'));
+ };
+ if ($@) {
+ ($c->stash->{message} = $@) =~ s/at [^ ]*Auth.pm.*//;
+ $c->stash->{template} = 'errors/generic.html';
+ $c->detach;
+ }
+
+ # save this token in session
+ $c->session->{oauth}{token} = $access_token;
+
+ my $info = $fb->get('https://graph.facebook.com/me?fields=name,email')->as_hash();
+ my $name = $info->{name};
+ my $email = lc ($info->{email} || "");
+ my $uid = $info->{id};
+
+ my $user;
+ if ($email) {
+ # We've got an ID and an email address
+ # Remove any existing mention of this ID
+ my $existing = $c->model('DB::User')->find( { facebook_id => $uid } );
+ $existing->update( { facebook_id => undef } ) if $existing;
+ # Get or create a user, give it this Facebook ID
+ $user = $c->model('DB::User')->find_or_new( { email => $email } );
+ $user->facebook_id($uid);
+ $user->name($name);
+ $user->in_storage() ? $user->update : $user->insert;
+ } else {
+ # We've got an ID, but no email
+ $user = $c->model('DB::User')->find( { facebook_id => $uid } );
+ if ($user) {
+ # Matching Facebook ID in our database
+ $user->name($name);
+ $user->update;
+ } else {
+ # No matching ID, store ID for use later
+ $c->session->{oauth}{facebook_id} = $uid;
+ $c->stash->{oauth_need_email} = 1;
+ }
+ }
+
+ # If we've got here with a full user, log in
+ if ($user) {
+ $c->authenticate( { email => $user->email }, 'no_password' );
+ $c->stash->{login_success} = 1;
+ }
+
+ if ($c->session->{oauth}{detach_to}) {
+ $c->detach($c->session->{oauth}{detach_to}, $c->session->{oauth}{detach_args});
+ } elsif ($c->stash->{oauth_need_email}) {
+ $c->stash->{template} = 'auth/general.html';
+ } else {
+ $c->detach( 'redirect_on_signin', [ $c->session->{oauth}{return_url} ] );
+ }
+}
+
=head2 redirect_on_signin
Used after signing in to take the person back to where they were.
@@ -275,7 +387,7 @@ sub ajax_sign_in : Path('ajax/sign_in') {
$return->{error} = 1;
}
- my $body = JSON->new->utf8(1)->encode( $return );
+ my $body = encode_json($return);
$c->res->content_type('application/json; charset=utf-8');
$c->res->body($body);
@@ -287,7 +399,7 @@ sub ajax_sign_out : Path('ajax/sign_out') {
$c->logout();
- my $body = JSON->new->utf8(1)->encode( { signed_out => 1 } );
+ my $body = encode_json( { signed_out => 1 } );
$c->res->content_type('application/json; charset=utf-8');
$c->res->body($body);
@@ -305,7 +417,7 @@ sub ajax_check_auth : Path('ajax/check_auth') {
$code = 200;
}
- my $body = JSON->new->utf8(1)->encode( $data );
+ my $body = encode_json($data);
$c->res->content_type('application/json; charset=utf-8');
$c->res->code($code);
$c->res->body($body);
diff --git a/perllib/FixMyStreet/App/Controller/Dashboard.pm b/perllib/FixMyStreet/App/Controller/Dashboard.pm
index faddaa89e..9189b28e5 100644
--- a/perllib/FixMyStreet/App/Controller/Dashboard.pm
+++ b/perllib/FixMyStreet/App/Controller/Dashboard.pm
@@ -4,6 +4,7 @@ use namespace::autoclean;
use DateTime;
use File::Slurp;
+use JSON::MaybeXS;
BEGIN { extends 'Catalyst::Controller'; }
@@ -40,7 +41,7 @@ sub example : Local : Args(0) {
my $data = File::Slurp::read_file(
FixMyStreet->path_to( 'data/dashboard.json' )->stringify
);
- my $j = JSON->new->utf8->decode($data);
+ my $j = decode_json($data);
if ( !$c->stash->{ward} && !$c->stash->{category} ) {
$c->stash->{problems} = $j->{counts_all};
} else {
diff --git a/perllib/FixMyStreet/App/Controller/FakeMapit.pm b/perllib/FixMyStreet/App/Controller/FakeMapit.pm
index 253c75ba4..a4adadd09 100755
--- a/perllib/FixMyStreet/App/Controller/FakeMapit.pm
+++ b/perllib/FixMyStreet/App/Controller/FakeMapit.pm
@@ -1,6 +1,7 @@
package FixMyStreet::App::Controller::FakeMapit;
use Moose;
use namespace::autoclean;
+use JSON::MaybeXS;
BEGIN { extends 'Catalyst::Controller'; }
@@ -22,7 +23,7 @@ my $area = { "name" => "Everywhere", "type" => "ZZZ", "id" => 161 };
sub output : Private {
my ( $self, $c, $data ) = @_;
- my $body = JSON->new->utf8(1)->encode( $data );
+ my $body = encode_json($data);
$c->res->content_type('application/json; charset=utf-8');
$c->res->body( $body );
}
diff --git a/perllib/FixMyStreet/App/Controller/JSON.pm b/perllib/FixMyStreet/App/Controller/JSON.pm
index 959ead245..d3cd33546 100644
--- a/perllib/FixMyStreet/App/Controller/JSON.pm
+++ b/perllib/FixMyStreet/App/Controller/JSON.pm
@@ -4,11 +4,10 @@ use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }
-use JSON;
+use JSON::MaybeXS;
use DateTime;
use DateTime::Format::ISO8601;
use List::MoreUtils 'uniq';
-use FixMyStreet::App;
=head1 NAME
@@ -81,7 +80,7 @@ sub problems : Local {
$date_col = 'lastupdate';
}
- my $dt_parser = FixMyStreet::App->model('DB')->schema->storage->datetime_parser;
+ my $dt_parser = $c->model('DB')->schema->storage->datetime_parser;
my $one_day = DateTime::Duration->new( days => 1 );
my $query = {
diff --git a/perllib/FixMyStreet/App/Controller/Location.pm b/perllib/FixMyStreet/App/Controller/Location.pm
index ff90d3d60..6def423ce 100644
--- a/perllib/FixMyStreet/App/Controller/Location.pm
+++ b/perllib/FixMyStreet/App/Controller/Location.pm
@@ -95,7 +95,8 @@ sub determine_location_from_pc : Private {
# $error doubles up to return multiple choices by being an array
if ( ref($error) eq 'ARRAY' ) {
foreach (@$error) {
- my $a = decode_utf8($_->{address});
+ my $a = $_->{address};
+ $a = decode_utf8($a) if !utf8::is_utf8($a);
$a =~ s/, United Kingdom//;
$a =~ s/, UK//;
$_->{address} = $a;
diff --git a/perllib/FixMyStreet/App/Controller/My.pm b/perllib/FixMyStreet/App/Controller/My.pm
index 3c4ce2cf7..8eb7f079e 100644
--- a/perllib/FixMyStreet/App/Controller/My.pm
+++ b/perllib/FixMyStreet/App/Controller/My.pm
@@ -36,6 +36,7 @@ sub my : Path : Args(0) {
my $states = $c->stash->{filter_problem_states};
my $params = {
state => [ keys %$states ],
+ user_id => $c->user->id,
};
my $category = $c->get_param('filter_category');
@@ -44,9 +45,7 @@ sub my : Path : Args(0) {
$c->stash->{filter_category} = $category;
}
- my $rs = $c->user->problems
- ->to_body($c->cobrand->body_restriction)
- ->search( $params, {
+ my $rs = $c->cobrand->problems->search( $params, {
order_by => { -desc => 'confirmed' },
rows => 50
} )->page( $p_page );
@@ -77,7 +76,7 @@ sub my : Path : Args(0) {
$c->stash->{updates} = \@updates;
$c->stash->{updates_pager} = $rs->pager;
- my @categories = $c->user->problems->search( undef, {
+ my @categories = $c->cobrand->problems->search( { user_id => $c->user->id }, {
columns => [ 'category' ],
distinct => 1,
order_by => [ 'category' ],
diff --git a/perllib/FixMyStreet/App/Controller/Open311.pm b/perllib/FixMyStreet/App/Controller/Open311.pm
index 96066ca93..f35dc64a5 100644
--- a/perllib/FixMyStreet/App/Controller/Open311.pm
+++ b/perllib/FixMyStreet/App/Controller/Open311.pm
@@ -4,7 +4,7 @@ use utf8;
use Moose;
use namespace::autoclean;
-use JSON;
+use JSON::MaybeXS;
use XML::Simple;
use DateTime::Format::W3CDTF;
diff --git a/perllib/FixMyStreet/App/Controller/Photo.pm b/perllib/FixMyStreet/App/Controller/Photo.pm
index bc72f4bfb..bfb1c5535 100644
--- a/perllib/FixMyStreet/App/Controller/Photo.pm
+++ b/perllib/FixMyStreet/App/Controller/Photo.pm
@@ -4,11 +4,9 @@ use namespace::autoclean;
BEGIN {extends 'Catalyst::Controller'; }
-use DateTime::Format::HTTP;
-use Digest::SHA qw(sha1_hex);
+use JSON::MaybeXS;
use File::Path;
use File::Slurp;
-use Path::Class;
use FixMyStreet::App::Model::PhotoSet;
use if !$ENV{TRAVIS}, 'Image::Magick';
@@ -35,16 +33,12 @@ sub during :LocalRegex('^([0-9a-f]{40})\.(temp|fulltemp)\.jpeg$') {
my ( $self, $c ) = @_;
my ( $hash, $size ) = @{ $c->req->captures };
- my $file = file( $c->config->{UPLOAD_DIR}, "$hash.jpeg" );
- my $photo = $file->slurp;
+ my $photoset = FixMyStreet::App::Model::PhotoSet->new({
+ data_items => [ $hash ]
+ });
- if ( $size eq 'temp' ) {
- if ( $c->cobrand->default_photo_resize ) {
- $photo = _shrink( $photo, $c->cobrand->default_photo_resize );
- } else {
- $photo = _shrink( $photo, '250x250' );
- }
- }
+ $size = $size eq 'temp' ? 'default' : 'full';
+ my $photo = $photoset->get_image_data(size => $size, default => $c->cobrand->default_photo_resize);
$c->forward( 'output', [ $photo ] );
}
@@ -61,15 +55,9 @@ sub index :LocalRegex('^(c/)?(\d+)(?:\.(\d+))?(?:\.(full|tn|fp))?\.jpeg$') {
photo => { '!=', undef },
} );
} else {
- # GoogleBot-Image is doing this for some reason?
- if ( $id =~ m{ ^(\d+) \D .* $ }x ) {
- return $c->res->redirect( $c->uri_with( { id => $1 } ), 301 );
- }
-
- $c->detach( 'no_photo' ) if $id =~ /\D/;
($item) = $c->cobrand->problems->search( {
id => $id,
- state => [ FixMyStreet::DB::Result::Problem->visible_states(), 'partial' ],
+ state => [ FixMyStreet::DB::Result::Problem->visible_states() ],
photo => { '!=', undef },
} );
}
@@ -79,29 +67,9 @@ sub index :LocalRegex('^(c/)?(\d+)(?:\.(\d+))?(?:\.(full|tn|fp))?\.jpeg$') {
$c->detach( 'no_photo' ) unless $c->cobrand->allow_photo_display($item); # Should only be for reports, not updates
my $photo;
- if ($item->can('get_photoset')) {
- $photo = $item->get_photoset( $c )
- ->get_image_data( num => $photo_number, size => $size )
+ $photo = $item->get_photoset
+ ->get_image_data( num => $photo_number, size => $size, default => $c->cobrand->default_photo_resize )
or $c->detach( 'no_photo' );
- } else {
- $photo = $item->photo;
- # If photo field contains a hash
- if (length($photo) == 40) {
- my $file = file( $c->config->{UPLOAD_DIR}, "$photo.jpeg" );
- $photo = $file->slurp;
- }
-
- if ( $size eq 'tn' ) {
- $photo = _shrink( $photo, 'x100' );
- } elsif ( $size eq 'fp' ) {
- $photo = _crop( $photo );
- } elsif ( $size eq 'full' ) {
- } elsif ( $c->cobrand->default_photo_resize ) {
- $photo = _shrink( $photo, $c->cobrand->default_photo_resize );
- } else {
- $photo = _shrink( $photo, '250x250' );
- }
- }
$c->forward( 'output', [ $photo ] );
}
@@ -122,32 +90,30 @@ sub no_photo : Private {
$c->detach( '/page_error_404_not_found', [ 'No photo' ] );
}
-# Shrinks a picture to the specified size, but keeping in proportion.
-sub _shrink {
- my ($photo, $size) = @_;
- my $image = Image::Magick->new;
- $image->BlobToImage($photo);
- my $err = $image->Scale(geometry => "$size>");
- throw Error::Simple("resize failed: $err") if "$err";
- $image->Strip();
- my @blobs = $image->ImageToBlob();
- undef $image;
- return $blobs[0];
-}
+sub upload : Local {
+ my ( $self, $c ) = @_;
+ my @items = (
+ ( map {
+ /^photo/ ? # photo, photo1, photo2 etc.
+ ($c->req->upload($_)) : ()
+ } sort $c->req->upload),
+ );
+ my $photoset = FixMyStreet::App::Model::PhotoSet->new({
+ c => $c,
+ data_items => \@items,
+ });
+
+ my $fileid = $photoset->data;
+ my $out;
+ if ($c->stash->{photo_error} || !$fileid) {
+ $c->res->status(500);
+ $out = { error => $c->stash->{photo_error} || _('Unknown error') };
+ } else {
+ $out = { id => $fileid };
+ }
-# Shrinks a picture to 90x60, cropping so that it is exactly that.
-sub _crop {
- my ($photo) = @_;
- my $image = Image::Magick->new;
- $image->BlobToImage($photo);
- my $err = $image->Resize( geometry => "90x60^" );
- throw Error::Simple("resize failed: $err") if "$err";
- $err = $image->Extent( geometry => '90x60', gravity => 'Center' );
- throw Error::Simple("resize failed: $err") if "$err";
- $image->Strip();
- my @blobs = $image->ImageToBlob();
- undef $image;
- return $blobs[0];
+ $c->res->content_type('application/json; charset=utf-8');
+ $c->res->body(encode_json($out));
}
=head2 process_photo
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index d7cae05d4..5cc44bb8f 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -2,6 +2,8 @@ package FixMyStreet::App::Controller::Report;
use Moose;
use namespace::autoclean;
+use JSON::MaybeXS;
+
BEGIN { extends 'Catalyst::Controller'; }
=head1 NAME
@@ -184,7 +186,7 @@ sub format_problem_for_display : Private {
if ( $c->stash->{ajax} ) {
$c->res->content_type('application/json; charset=utf-8');
- my $content = JSON->new->utf8(1)->encode(
+ my $content = encode_json(
{
report => $c->cobrand->problem_as_hashref( $problem, $c ),
updates => $c->cobrand->updates_as_hashref( $problem, $c ),
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 246facbee..66dc20a3a 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -12,7 +12,7 @@ use mySociety::MaPit;
use Path::Class;
use Utils;
use mySociety::EmailUtil;
-use JSON;
+use JSON::MaybeXS;
=head1 NAME
@@ -149,6 +149,7 @@ sub report_new_ajax : Path('mobile') : Args(0) {
}
} );
if ( $report->confirmed ) {
+ $c->forward( 'create_reporter_alert' );
$c->stash->{ json_response } = { success => 1, report => $report->id };
} else {
$c->stash->{token_url} = $c->uri_for_email( '/P', $token->token );
@@ -164,9 +165,7 @@ sub report_new_ajax : Path('mobile') : Args(0) {
sub send_json_response : Private {
my ( $self, $c ) = @_;
- my $body = JSON->new->utf8(1)->encode(
- $c->stash->{json_response},
- );
+ my $body = encode_json($c->stash->{json_response});
$c->res->content_type('application/json; charset=utf-8');
$c->res->body($body);
}
@@ -178,9 +177,7 @@ sub report_form_ajax : Path('ajax') : Args(0) {
# work out the location for this report and do some checks
if ( ! $c->forward('determine_location') ) {
- my $body = JSON->new->utf8(1)->encode( {
- error => $c->stash->{location_error},
- } );
+ my $body = encode_json({ error => $c->stash->{location_error} });
$c->res->content_type('application/json; charset=utf-8');
$c->res->body($body);
return;
@@ -197,7 +194,7 @@ sub report_form_ajax : Path('ajax') : Args(0) {
my $extra_titles_list = $c->cobrand->title_list($c->stash->{all_areas});
- my $body = JSON->new->utf8(1)->encode(
+ my $body = encode_json(
{
councils_text => $councils_text,
category => $category,
@@ -216,11 +213,7 @@ sub category_extras_ajax : Path('category_extras') : Args(0) {
$c->forward('initialize_report');
if ( ! $c->forward('determine_location') ) {
- my $body = JSON->new->utf8(1)->encode(
- {
- error => _("Sorry, we could not find that location."),
- }
- );
+ my $body = encode_json({ error => _("Sorry, we could not find that location.") });
$c->res->content_type('application/json; charset=utf-8');
$c->res->body($body);
return 1;
@@ -244,11 +237,7 @@ sub category_extras_ajax : Path('category_extras') : Args(0) {
$category_extra = $c->render_fragment( 'report/new/category_extras.html');
}
- my $body = JSON->new->utf8(1)->encode(
- {
- category_extra => $category_extra,
- }
- );
+ my $body = encode_json({ category_extra => $category_extra });
$c->res->content_type('application/json; charset=utf-8');
$c->res->body($body);
@@ -403,6 +392,12 @@ sub report_import : Path('/import') {
return 1;
}
+sub oauth_callback : Private {
+ my ( $self, $c, $token_code ) = @_;
+ $c->stash->{oauth_report} = $token_code;
+ $c->detach('report_new');
+}
+
=head2 initialize_report
Create the report and set up some basics in it. If there is a partial report
@@ -426,26 +421,21 @@ sub initialize_report : Private {
for (1) { # use as pseudo flow control
- # did we find a token
- last unless $partial;
-
# is it in the database
my $token =
$c->model("DB::Token")
- ->find( { scope => 'partial', token => $partial } ) #
+ ->find( { scope => 'partial', token => $partial } )
|| last;
# can we get an id from it?
- my $id = $token->data #
- || last;
+ my $id = $token->data || last;
# load the related problem
- $report = $c->cobrand->problems #
- ->search( { id => $id, state => 'partial' } ) #
+ $report = $c->cobrand->problems
+ ->search( { id => $id, state => 'partial' } )
->first;
if ($report) {
-
# log the problem creation user in to the site
$c->authenticate( { email => $report->user->email },
'no_password' );
@@ -453,27 +443,32 @@ sub initialize_report : Private {
# save the token to delete at the end
$c->stash->{partial_token} = $token if $report;
- }
- else {
-
+ } else {
# no point keeping it if it is done.
$token->delete;
}
}
}
- if ( !$report ) {
+ if (!$report && $c->stash->{oauth_report}) {
+ my $auth_token = $c->forward( '/tokens/load_auth_token',
+ [ $c->stash->{oauth_report}, 'problem/social' ] );
+ $report = $c->model("DB::Problem")->new($auth_token->data);
+ }
- # If we didn't find a partial then create a new one
+ if ($report) {
+ # Stash the photo IDs for "already got" display
+ $c->stash->{upload_fileid} = $report->get_photoset->data;
+ } else {
+ # If we didn't find one otherwise, start with a blank report
$report = $c->model('DB::Problem')->new( {} );
+ }
- # If we have a user logged in let's prefill some values for them.
- if ( $c->user ) {
- my $user = $c->user->obj;
- $report->user($user);
- $report->name( $user->name );
- }
-
+ # If we have a user logged in let's prefill some values for them.
+ if (!$report->user && $c->user) {
+ my $user = $c->user->obj;
+ $report->user($user);
+ $report->name( $user->name );
}
if ( $c->get_param('first_name') && $c->get_param('last_name') ) {
@@ -859,12 +854,6 @@ sub process_report : Private {
$bodies = join( ',', @{ $c->stash->{bodies_to_list} } ) || -1;
$report->bodies_str( $bodies );
- my %extra;
- $c->cobrand->process_extras( $c, undef, \%extra );
- if ( %extra ) {
- $report->extra( \%extra );
- }
-
} elsif ( $report->category ) {
# FIXME All contacts were fetched in setup_categories_and_bodies,
@@ -936,7 +925,7 @@ sub process_report : Private {
$report->non_public( 1 );
}
- $c->cobrand->process_extras( $c, $contacts[0]->body_id, \@extra );
+ $c->cobrand->process_open311_extras( $c, $contacts[0]->body_id, \@extra );
if ( @extra ) {
$c->stash->{report_meta} = { map { $_->{name} => $_ } @extra };
@@ -955,6 +944,15 @@ sub process_report : Private {
}
+ # Get a list of custom form fields we want and store them in extra metadata
+ foreach my $field ($c->cobrand->report_form_extras) {
+ my $form_name = $field->{name};
+ my $value = $c->get_param($form_name) || '';
+ $c->stash->{field_errors}->{$form_name} = _('This information is required')
+ if $field->{required} && !$value;
+ $report->set_extra_metadata( $form_name => $value );
+ }
+
# set defaults that make sense
$report->state('unconfirmed');
@@ -1005,6 +1003,13 @@ sub check_for_errors : Private {
delete $field_errors{name};
}
+ # if using social login then we don't care about name and email errors
+ $c->stash->{is_social_user} = $c->get_param('facebook_sign_in') || $c->get_param('twitter_sign_in');
+ if ( $c->stash->{is_social_user} ) {
+ delete $field_errors{name};
+ delete $field_errors{email};
+ }
+
# add the photo error if there is one.
if ( my $photo_error = delete $c->stash->{photo_error} ) {
$field_errors{photo} = $photo_error;
@@ -1018,6 +1023,19 @@ sub check_for_errors : Private {
return;
}
+# Store changes in token for when token is validated.
+sub tokenize_user : Private {
+ my ($self, $c, $report) = @_;
+ $c->stash->{token_data} = {
+ name => $report->user->name,
+ phone => $report->user->phone,
+ password => $report->user->password,
+ title => $report->user->title,
+ };
+ $c->stash->{token_data}{facebook_id} = $c->session->{oauth}{facebook_id}
+ if $c->get_param('oauth_need_email') && $c->session->{oauth}{facebook_id};
+}
+
=head2 save_user_and_report
Save the user and the report.
@@ -1029,7 +1047,41 @@ before or they are currently logged in. Otherwise discard any changes.
sub save_user_and_report : Private {
my ( $self, $c ) = @_;
- my $report = $c->stash->{report};
+ my $report = $c->stash->{report};
+
+ # If there was a photo add that
+ if ( my $fileid = $c->stash->{upload_fileid} ) {
+ $report->photo($fileid);
+ }
+
+ # Set a default if possible
+ $report->category( _('Other') ) unless $report->category;
+
+ # Set unknown to DB unknown
+ $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->get_param('external_source_id') =~ /^\d+$/) {
+ $c->stash->{external_source_id} = $c->get_param('external_source_id');
+ $report->external_source_id( $c->get_param('external_source_id') );
+ $report->external_source( $c->config->{MESSAGE_MANAGER_URL} ) ;
+ }
+
+ if ( $c->stash->{is_social_user} ) {
+ my $token = $c->model("DB::Token")->create( {
+ scope => 'problem/social',
+ data => { $report->get_inflated_columns },
+ } );
+
+ $c->stash->{detach_to} = '/report/new/oauth_callback';
+ $c->stash->{detach_args} = [$token->token];
+
+ if ( $c->get_param('facebook_sign_in') ) {
+ $c->detach('/auth/facebook_sign_in');
+ } elsif ( $c->get_param('twitter_sign_in') ) {
+ $c->detach('/auth/twitter_sign_in');
+ }
+ }
# Save or update the user if appropriate
if ( $c->cobrand->never_confirm_reports ) {
@@ -1039,15 +1091,10 @@ sub save_user_and_report : Private {
$report->user->insert();
}
$report->confirm();
+
} elsif ( !$report->user->in_storage ) {
# User does not exist.
- # Store changes in token for when token is validated.
- $c->stash->{token_data} = {
- name => $report->user->name,
- phone => $report->user->phone,
- password => $report->user->password,
- title => $report->user->title,
- };
+ $c->forward('tokenize_user', [ $report ]);
$report->user->name( undef );
$report->user->phone( undef );
$report->user->password( '', 1 );
@@ -1064,35 +1111,11 @@ sub save_user_and_report : Private {
}
else {
# User exists and we are not logged in as them.
- # Store changes in token for when token is validated.
- $c->stash->{token_data} = {
- name => $report->user->name,
- phone => $report->user->phone,
- password => $report->user->password,
- title => $report->user->title,
- };
+ $c->forward('tokenize_user', [ $report ]);
$report->user->discard_changes();
$c->log->info($report->user->id . ' exists, but is not logged in for this report');
}
- # If there was a photo add that too
- if ( my $fileid = $c->stash->{upload_fileid} ) {
- $report->photo($fileid);
- }
-
- # Set a default if possible
- $report->category( _('Other') ) unless $report->category;
-
- # Set unknown to DB unknown
- $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->get_param('external_source_id') =~ /^\d+$/) {
- $c->stash->{external_source_id} = $c->get_param('external_source_id');
- $report->external_source_id( $c->get_param('external_source_id') );
- $report->external_source( $c->config->{MESSAGE_MANAGER_URL} ) ;
- }
-
# save the report;
$report->in_storage ? $report->update : $report->insert();
@@ -1118,8 +1141,8 @@ sub generate_map : Private {
my $longitude = $c->stash->{longitude};
# Don't do anything if the user skipped the map
+ $c->stash->{page} = 'new';
if ( $c->stash->{report}->used_map ) {
- $c->stash->{page} = 'new';
FixMyStreet::Map::display_map(
$c,
latitude => $latitude,
diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm
index 445723fec..8d6bc2019 100644
--- a/perllib/FixMyStreet/App/Controller/Report/Update.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm
@@ -20,12 +20,16 @@ Creates an update to a report
sub report_update : Path : Args(0) {
my ( $self, $c ) = @_;
- $c->forward( '/report/load_problem_or_display_error', [ $c->get_param('id') ] );
+ $c->forward('initialize_update');
+ $c->forward('load_problem');
+ $c->forward('check_form_submitted')
+ or $c->go( '/report/display', [ $c->stash->{problem}->id ] );
+
$c->forward('process_update');
$c->forward('process_user');
$c->forward('/photo/process_photo');
$c->forward('check_for_errors')
- or $c->go( '/report/display', [ $c->get_param('id') ] );
+ or $c->go( '/report/display', [ $c->stash->{problem}->id ] );
$c->forward('save_update');
$c->forward('redirect_or_confirm_creation');
@@ -151,19 +155,46 @@ sub process_user : Private {
return 1;
}
-=head2 process_update
+=head2 oauth_callback
-Take the submitted params and create a new update item. Does not save
-anything to the database.
+Called when we successfully login via OAuth. Stores the token so we can look up
+what we have so far.
-NB: relies on their being a problem and update_user in the stash. May
-want to move adding these elsewhere
+=cut
+
+sub oauth_callback : Private {
+ my ( $self, $c, $token_code ) = @_;
+ $c->stash->{oauth_update} = $token_code;
+ $c->detach('report_update');
+}
+
+=head2 initialize_update
+
+Create an initial update object, either empty or from stored OAuth data.
=cut
-sub process_update : Private {
+sub initialize_update : Private {
my ( $self, $c ) = @_;
+ my $update;
+ if ($c->stash->{oauth_update}) {
+ my $auth_token = $c->forward( '/tokens/load_auth_token',
+ [ $c->stash->{oauth_update}, 'update/social' ] );
+ $update = $c->model("DB::Comment")->new($auth_token->data);
+ }
+
+ if ($update) {
+ $c->stash->{upload_fileid} = $update->get_photoset->data;
+ } else {
+ $update = $c->model('DB::Comment')->new({
+ state => 'unconfirmed',
+ cobrand => $c->cobrand->moniker,
+ cobrand_data => '',
+ lang => $c->stash->{lang_code},
+ });
+ }
+
if ( $c->get_param('first_name') && $c->get_param('last_name') ) {
my $first_name = $c->get_param('first_name');
my $last_name = $c->get_param('last_name');
@@ -173,6 +204,48 @@ sub process_update : Private {
$c->stash->{last_name} = $last_name;
}
+ $c->stash->{update} = $update;
+}
+
+=head2 load_problem
+
+Our update could be prefilled, or we could be submitting a form containing an
+ID. Look up the relevant report either way.
+
+=cut
+
+sub load_problem : Private {
+ my ( $self, $c ) = @_;
+
+ my $update = $c->stash->{update};
+ # Problem ID could come from existing update in token, or from query parameter
+ my $problem_id = $update->problem_id || $c->get_param('id');
+ $c->forward( '/report/load_problem_or_display_error', [ $problem_id ] );
+ $update->problem($c->stash->{problem});
+}
+
+=head2 check_form_submitted
+
+This makes sure we only proceed to processing if we've had the form submitted
+(we may have come here via an OAuth login, for example).
+
+=cut
+
+sub check_form_submitted : Private {
+ my ( $self, $c ) = @_;
+ return $c->get_param('submit_update') || '';
+}
+
+=head2 process_update
+
+Take the submitted params and updates our update item. Does not save
+anything to the database.
+
+=cut
+
+sub process_update : Private {
+ my ( $self, $c ) = @_;
+
my %params =
map { $_ => $c->get_param($_) } ( 'update', 'name', 'fixed', 'state', 'reopen' );
@@ -184,20 +257,12 @@ sub process_update : Private {
$params{reopen} = 0 unless $c->user && $c->user->id == $c->stash->{problem}->user->id;
- my $update = $c->model('DB::Comment')->new(
- {
- text => $params{update},
- name => $name,
- problem => $c->stash->{problem},
- state => 'unconfirmed',
- mark_fixed => $params{fixed} ? 1 : 0,
- mark_open => $params{reopen} ? 1 : 0,
- cobrand => $c->cobrand->moniker,
- cobrand_data => '',
- lang => $c->stash->{lang_code},
- anonymous => $anonymous,
- }
- );
+ my $update = $c->stash->{update};
+ $update->text($params{update});
+ $update->name($name);
+ $update->mark_fixed($params{fixed} ? 1 : 0);
+ $update->mark_open($params{reopen} ? 1 : 0);
+ $update->anonymous($anonymous);
if ( $params{state} ) {
$params{state} = 'fixed - council'
@@ -221,9 +286,9 @@ sub process_update : Private {
my @extra; # Next function fills this, but we don't need it here.
- # This is just so that the error checkign for these extra fields runs.
+ # This is just so that the error checking for these extra fields runs.
# TODO Use extra here as it is used on reports.
- $c->cobrand->process_extras( $c, $update->problem->bodies_str, \@extra );
+ $c->cobrand->process_open311_extras( $c, $update->problem->bodies_str, \@extra );
if ( $c->get_param('fms_extra_title') ) {
my %extras = ();
@@ -241,7 +306,6 @@ sub process_update : Private {
$c->log->debug( 'name is ' . $c->get_param('name') );
- $c->stash->{update} = $update;
$c->stash->{add_alert} = $c->get_param('add_alert');
return 1;
@@ -283,6 +347,13 @@ sub check_for_errors : Private {
%{ $c->stash->{update}->check_for_errors },
);
+ # if using social login then we don't care about name and email errors
+ $c->stash->{is_social_user} = $c->get_param('facebook_sign_in') || $c->get_param('twitter_sign_in');
+ if ( $c->stash->{is_social_user} ) {
+ delete $field_errors{name};
+ delete $field_errors{email};
+ }
+
if ( my $photo_error = delete $c->stash->{photo_error} ) {
$field_errors{photo} = $photo_error;
}
@@ -302,6 +373,17 @@ sub check_for_errors : Private {
return;
}
+# Store changes in token for when token is validated.
+sub tokenize_user : Private {
+ my ($self, $c, $update) = @_;
+ $c->stash->{token_data} = {
+ name => $update->user->name,
+ password => $update->user->password,
+ };
+ $c->stash->{token_data}{facebook_id} = $c->session->{oauth}{facebook_id}
+ if $c->get_param('oauth_need_email') && $c->session->{oauth}{facebook_id};
+}
+
=head2 save_update
Save the update and the user as appropriate.
@@ -313,6 +395,27 @@ sub save_update : Private {
my $update = $c->stash->{update};
+ # If there was a photo add that too
+ if ( my $fileid = $c->stash->{upload_fileid} ) {
+ $update->photo($fileid);
+ }
+
+ if ( $c->stash->{is_social_user} ) {
+ my $token = $c->model("DB::Token")->create( {
+ scope => 'update/social',
+ data => { $update->get_inflated_columns },
+ } );
+
+ $c->stash->{detach_to} = '/report/update/oauth_callback';
+ $c->stash->{detach_args} = [$token->token];
+
+ if ( $c->get_param('facebook_sign_in') ) {
+ $c->detach('/auth/facebook_sign_in');
+ } elsif ( $c->get_param('twitter_sign_in') ) {
+ $c->detach('/auth/twitter_sign_in');
+ }
+ }
+
if ( $c->cobrand->never_confirm_updates ) {
if ( $update->user->in_storage() ) {
$update->user->update();
@@ -322,11 +425,7 @@ sub save_update : Private {
$update->confirm();
} elsif ( !$update->user->in_storage ) {
# User does not exist.
- # Store changes in token for when token is validated.
- $c->stash->{token_data} = {
- name => $update->user->name,
- password => $update->user->password,
- };
+ $c->forward('tokenize_user', [ $update ]);
$update->user->name( undef );
$update->user->password( '', 1 );
$update->user->insert;
@@ -338,19 +437,10 @@ sub save_update : Private {
$update->confirm;
} else {
# User exists and we are not logged in as them.
- # Store changes in token for when token is validated.
- $c->stash->{token_data} = {
- name => $update->user->name,
- password => $update->user->password,
- };
+ $c->forward('tokenize_user', [ $update ]);
$update->user->discard_changes();
}
- # If there was a photo add that too
- if ( my $fileid = $c->stash->{upload_fileid} ) {
- $update->photo($fileid);
- }
-
if ( $update->in_storage ) {
$update->update;
}
diff --git a/perllib/FixMyStreet/App/Controller/Reports.pm b/perllib/FixMyStreet/App/Controller/Reports.pm
index 407fc625e..027b0d5a4 100644
--- a/perllib/FixMyStreet/App/Controller/Reports.pm
+++ b/perllib/FixMyStreet/App/Controller/Reports.pm
@@ -3,6 +3,7 @@ use Moose;
use namespace::autoclean;
use File::Slurp;
+use JSON::MaybeXS;
use List::MoreUtils qw(any);
use POSIX qw(strcoll);
use RABX;
@@ -68,7 +69,7 @@ sub index : Path : Args(0) {
my $data = File::Slurp::read_file(
FixMyStreet->path_to( '../data/all-reports.json' )->stringify
);
- my $j = JSON->new->utf8->decode($data);
+ my $j = decode_json($data);
$c->stash->{fixed} = $j->{fixed};
$c->stash->{open} = $j->{open};
};
diff --git a/perllib/FixMyStreet/App/Controller/Rss.pm b/perllib/FixMyStreet/App/Controller/Rss.pm
index b817fe326..6047f063b 100755
--- a/perllib/FixMyStreet/App/Controller/Rss.pm
+++ b/perllib/FixMyStreet/App/Controller/Rss.pm
@@ -218,7 +218,7 @@ sub query_main : Private {
. ($alert_type->head_table ? $alert_type->head_table . '_id=? and ' : '')
. $alert_type->item_where . ' order by '
. $alert_type->item_order;
- my $rss_limit = mySociety::Config::get('RSS_LIMIT');
+ my $rss_limit = FixMyStreet->config('RSS_LIMIT');
$query .= " limit $rss_limit" unless $c->stash->{type} =~ /^all/;
my $q = $c->model('DB::Alert')->result_source->storage->dbh->prepare($query);
@@ -260,9 +260,9 @@ sub add_row : Private {
$row->{confirmed} =~ s/^(\d+)/ordinal($1)/e if $c->stash->{lang_code} eq 'en-gb';
}
- (my $title = _($alert_type->item_title)) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $link = $alert_type->item_link) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $desc = _($alert_type->item_description)) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $title = _($alert_type->item_title)) =~ s/\{\{(.*?)}}/$row->{$1}/g;
+ (my $link = $alert_type->item_link) =~ s/\{\{(.*?)}}/$row->{$1}/g;
+ (my $desc = _($alert_type->item_description)) =~ s/\{\{(.*?)}}/$row->{$1}/g;
my $base_url = $c->cobrand->base_url_for_report($row);
my $url = $base_url . $link;
@@ -317,9 +317,9 @@ sub add_parameters : Private {
$row->{$_} = $c->stash->{title_params}->{$_};
}
- (my $title = _($alert_type->head_title)) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $link = $alert_type->head_link) =~ s/{{(.*?)}}/$row->{$1}/g;
- (my $desc = _($alert_type->head_description)) =~ s/{{(.*?)}}/$row->{$1}/g;
+ (my $title = _($alert_type->head_title)) =~ s/\{\{(.*?)}}/$row->{$1}/g;
+ (my $link = $alert_type->head_link) =~ s/\{\{(.*?)}}/$row->{$1}/g;
+ (my $desc = _($alert_type->head_description)) =~ s/\{\{(.*?)}}/$row->{$1}/g;
$c->stash->{rss}->channel(
title => ent($title),
diff --git a/perllib/FixMyStreet/App/Controller/Static.pm b/perllib/FixMyStreet/App/Controller/Static.pm
index d91a07fea..5054809c7 100755
--- a/perllib/FixMyStreet/App/Controller/Static.pm
+++ b/perllib/FixMyStreet/App/Controller/Static.pm
@@ -10,56 +10,23 @@ FixMyStreet::App::Controller::Static - Catalyst Controller
=head1 DESCRIPTION
-Static pages Catalyst Controller. FAQ does some smarts to choose the correct
-template depending on language, will need extending at some point.
+Old static pages Catalyst Controller.
=head1 METHODS
=cut
-sub about : Global : Args(0) {
- my ( $self, $c ) = @_;
-
- my $lang_code = $c->stash->{lang_code};
- my $template = "static/about-$lang_code.html";
- $c->stash->{template} = $template;
-}
-
-sub privacy : Global : Args(0) {
+sub about_redirect : Private {
my ( $self, $c ) = @_;
+ $c->res->redirect( $c->uri_for_action('/about/page', [ $c->action->name ] ));
}
-sub faq : Global : Args(0) {
- my ( $self, $c ) = @_;
-
- # There should be a faq template for each language in a cobrand or default.
- # This is because putting the FAQ translations into the PO files is
- # overkill.
-
- # We rely on the list of languages for the site being restricted so that there
- # will be a faq template for that language/cobrand combo.
-
- my $lang_code = $c->stash->{lang_code};
- my $template = "faq/faq-$lang_code.html";
- $c->stash->{template} = $template;
-}
-
-sub fun : Global : Args(0) {
- my ( $self, $c ) = @_;
- # don't need to do anything here - should just pass through.
-}
-
-sub posters : Global : Args(0) {
- my ( $self, $c ) = @_;
-}
-
-sub iphone : Global : Args(0) {
- my ( $self, $c ) = @_;
-}
-
-sub council : Global : Args(0) {
- my ( $self, $c ) = @_;
-}
+sub faq : Global : Args(0) { $_[1]->forward('/about/page', ['faq']) }
+sub privacy : Global : Args(0) { $_[1]->detach('about_redirect') }
+sub fun : Global : Args(0) { $_[1]->detach('about_redirect') }
+sub posters : Global : Args(0) { $_[1]->detach('about_redirect') }
+sub iphone : Global : Args(0) { $_[1]->detach('about_redirect') }
+sub council : Global : Args(0) { $_[1]->detach('about_redirect') }
sub unresponsive : Global : Args(0) {
my ( $self, $c ) = @_;
diff --git a/perllib/FixMyStreet/App/Controller/Status.pm b/perllib/FixMyStreet/App/Controller/Status.pm
index 907fe5456..931c7bd47 100755
--- a/perllib/FixMyStreet/App/Controller/Status.pm
+++ b/perllib/FixMyStreet/App/Controller/Status.pm
@@ -3,7 +3,7 @@ use Moose;
use namespace::autoclean;
use HTTP::Negotiate;
-use JSON;
+use JSON::MaybeXS;
BEGIN { extends 'Catalyst::Controller'; }
@@ -27,6 +27,9 @@ sub index_json : Path('/status.json') : Args(0) {
sub index : Path : Args(0) {
my ($self, $c, $format) = @_;
+ # Workaround that the admin summary page is only displayed to Zurich
+ # superusers. It doesn't have anything sensitive
+ $c->stash->{admin_type} = 'super';
# Fetch summary stats from admin front page
$c->forward('/admin/index');
diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm
index ba15162ce..eb35fd152 100644
--- a/perllib/FixMyStreet/App/Controller/Tokens.pm
+++ b/perllib/FixMyStreet/App/Controller/Tokens.pm
@@ -34,6 +34,7 @@ sub confirm_problem : Path('/P') {
title => 'Title of Report',
bodies_str => 'True',
url => '/report/123',
+ service => $c->get_param('service'),
};
return;
}
@@ -104,6 +105,7 @@ sub confirm_problem : Path('/P') {
$problem->user->phone( $data->{phone} ) if $data->{phone};
$problem->user->password( $data->{password}, 1 ) if $data->{password};
$problem->user->title( $data->{title} ) if $data->{title};
+ $problem->user->facebook_id( $data->{facebook_id} ) if $data->{facebook_id};
$problem->user->update;
}
$c->authenticate( { email => $problem->user->email }, 'no_password' );
@@ -229,6 +231,7 @@ sub confirm_update : Path('/C') {
if ( $data->{name} || $data->{password} ) {
$comment->user->name( $data->{name} ) if $data->{name};
$comment->user->password( $data->{password}, 1 ) if $data->{password};
+ $comment->user->facebook_id( $data->{facebook_id} ) if $data->{facebook_id};
$comment->user->update;
}
@@ -323,11 +326,7 @@ sub load_auth_token : Private {
}
);
- 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;
- }
+ $c->detach('token_too_old') unless $token;
return $token;
}
diff --git a/perllib/FixMyStreet/App/Model/DB.pm b/perllib/FixMyStreet/App/Model/DB.pm
index f9e43172f..ac1f98dc9 100644
--- a/perllib/FixMyStreet/App/Model/DB.pm
+++ b/perllib/FixMyStreet/App/Model/DB.pm
@@ -8,7 +8,7 @@ use FixMyStreet;
__PACKAGE__->config(
schema_class => 'FixMyStreet::DB',
- connect_info => FixMyStreet->dbic_connect_info,
+ connect_info => sub { FixMyStreet::DB->storage->dbh },
);
=head1 NAME
diff --git a/perllib/FixMyStreet/App/Model/EmailSend.pm b/perllib/FixMyStreet/App/Model/EmailSend.pm
index 475026267..93751d4a6 100644
--- a/perllib/FixMyStreet/App/Model/EmailSend.pm
+++ b/perllib/FixMyStreet/App/Model/EmailSend.pm
@@ -4,67 +4,16 @@ use base 'Catalyst::Model::Factory';
use strict;
use warnings;
-use FixMyStreet;
-use Email::Send;
-
=head1 NAME
FixMyStreet::App::Model::EmailSend
=head1 DESCRIPTION
-Thin wrapper around Email::Send - configuring it correctly acording to our config.
-
-If the config value 'SMTP_SMARTHOST' is set then email is routed via SMTP to
-that. Otherwise it is sent using a 'sendmail' like binary on the local system.
-
-And finally if if FixMyStreet->test_mode returns true then emails are not sent
-at all but are stored in memory for the test suite to inspect (using
-Email::Send::Test).
+Catalyst Model wrapper around FixMyStreet::EmailSend
=cut
-my $args = undef;
-
-if ( FixMyStreet->test_mode ) {
-
- # Email::Send::Test
- $args = { mailer => 'Test', };
-}
-elsif ( my $smtp_host = FixMyStreet->config('SMTP_SMARTHOST') ) {
-
- # Email::Send::SMTP
- my $type = FixMyStreet->config('SMTP_TYPE') || '';
- my $port = FixMyStreet->config('SMTP_PORT') || '';
- my $username = FixMyStreet->config('SMTP_USERNAME') || '';
- my $password = FixMyStreet->config('SMTP_PASSWORD') || '';
-
- unless ($port) {
- $port = 25;
- $port = 465 if $type eq 'ssl';
- $port = 587 if $type eq 'tls';
- }
-
- my $mailer_args = [
- Host => $smtp_host,
- Port => $port,
- ];
- push @$mailer_args, ssl => 1 if $type eq 'ssl';
- push @$mailer_args, tls => 1 if $type eq 'tls';
- push @$mailer_args, username => $username, password => $password
- if $username && $password;
- $args = {
- mailer => 'FixMyStreet::EmailSend::DoNotReply',
- mailer_args => $mailer_args,
- };
-}
-else {
-
- # Email::Send::Sendmail
- $args = { mailer => 'Sendmail' };
-}
-
__PACKAGE__->config(
- class => 'Email::Send',
- args => $args,
+ class => 'FixMyStreet::EmailSend',
);
diff --git a/perllib/FixMyStreet/App/Model/PhotoSet.pm b/perllib/FixMyStreet/App/Model/PhotoSet.pm
index b18460821..54457bae9 100644
--- a/perllib/FixMyStreet/App/Model/PhotoSet.pm
+++ b/perllib/FixMyStreet/App/Model/PhotoSet.pm
@@ -14,27 +14,32 @@ has c => (
is => 'ro',
);
+# The attached report, for using its ID
has object => (
is => 'ro',
);
-has data => ( # generic data from DB field
+# If a PhotoSet is generated from a database row, db_data is set, which then
+# fills data_items -> ids -> data. If it is generated during creation,
+# data_items is set, which then similarly fills ids -> data.
+
+has db_data => ( # generic data from DB field
+ is => 'ro',
+);
+
+has data => ( # String of photo hashes
is => 'ro',
lazy => 1,
default => sub {
- # yes, this is a little circular: data -> data_items -> items -> data
- # e.g. if not provided, then we're presumably uploading/etc., so calculate from
- # the stored cached fileids
- # (obviously if you provide none of these, then you'll get an infinite loop)
my $self = shift;
- my $data = join ',', map { $_->[0] } $self->all_images;
+ my $data = join ',', $self->all_ids;
return $data;
}
);
-has data_items => ( # either a) split from data or b) provided by photo upload
+has data_items => ( # either a) split from db_data or b) provided by photo upload
isa => 'ArrayRef',
- is => 'rw',
+ is => 'ro',
traits => ['Array'],
lazy => 1,
handles => {
@@ -42,8 +47,7 @@ has data_items => ( # either a) split from data or b) provided by photo upload
},
default => sub {
my $self = shift;
- my $data = $self->data
- or return [];
+ my $data = $self->db_data or return [];
return [$data] if (_jpeg_magic($data));
@@ -56,7 +60,7 @@ has upload_dir => (
lazy => 1,
default => sub {
my $self = shift;
- my $cache_dir = path( $self->c->config->{UPLOAD_DIR} );
+ my $cache_dir = path( FixMyStreet->config('UPLOAD_DIR') );
$cache_dir->mkpath;
unless ( -d $cache_dir && -w $cache_dir ) {
warn "Can't find/write to photo cache directory '$cache_dir'";
@@ -72,33 +76,29 @@ sub _jpeg_magic {
# and \x{49}\x{49} (Tiff, 3 results in live DB) ?
}
-=head2 C<images>, C<num_images>, C<get_raw_image_data>, C<all_images>
+=head2 C<ids>, C<num_images>, C<get_id>, C<all_ids>
-C<$photoset-E<GT>images> is an AoA containing the filed and the binary image data.
+C<$photoset-E<GT>ids> is an arrayref containing the fileid data.
- [
- [ $fileid1, $binary_data ],
- [ $fileid2, $binary_data ],
- ...
- ]
+ [ $fileid1, $fileid2, ... ]
Various accessors are provided onto it:
num_images: count
- get_raw_image_data ($index): return the [$fileid, $binary_data] tuple
- all_images: return AoA as an array (e.g. rather than arrayref)
+ get_id ($index): return the correct id
+ all_ids: array of elements, rather than arrayref
=cut
-has images => ( # AoA of [$fileid, $binary_data] tuples
+has ids => ( # Arrayref of $fileid tuples (always, so post upload/raw data processing)
isa => 'ArrayRef',
- is => 'rw',
+ is => 'ro',
traits => ['Array'],
lazy => 1,
handles => {
num_images => 'count',
- get_raw_image_data => 'get',
- all_images => 'elements',
+ get_id => 'get',
+ all_ids => 'elements',
},
default => sub {
my $self = shift;
@@ -159,7 +159,7 @@ has images => ( # AoA of [$fileid, $binary_data] tuples
my $fileid = $self->get_fileid($photo_blob);
my $file = $self->get_file($fileid);
$upload->copy_to( $file );
- return [$fileid, $photo_blob];
+ return $fileid;
}
if (_jpeg_magic($part)) {
@@ -167,21 +167,18 @@ has images => ( # AoA of [$fileid, $binary_data] tuples
my $fileid = $self->get_fileid($photo_blob);
my $file = $self->get_file($fileid);
$file->spew_raw($photo_blob);
- return [$fileid, $photo_blob];
+ return $fileid;
}
if (length($part) == 40) {
my $fileid = $part;
my $file = $self->get_file($fileid);
if ($file->exists) {
- my $photo = $file->slurp_raw;
- [$fileid, $photo];
- }
- else {
+ $fileid;
+ } else {
warn "File $fileid doesn't exist";
();
}
- }
- else {
+ } else {
warn sprintf "Received bad photo hash of length %d", length($part);
();
}
@@ -201,15 +198,23 @@ sub get_file {
return path( $cache_dir, "$fileid.jpeg" );
}
+sub get_raw_image_data {
+ my ($self, $index) = @_;
+ my $fileid = $self->get_id($index);
+ my $file = $self->get_file($fileid);
+ if ($file->exists) {
+ my $photo = $file->slurp_raw;
+ return $photo;
+ }
+}
+
sub get_image_data {
my ($self, %args) = @_;
my $num = $args{num} || 0;
- my $data = $self->get_raw_image_data( $num )
+ my $photo = $self->get_raw_image_data( $num )
or return;
- my ($fileid, $photo) = @$data;
-
my $size = $args{size};
if ( $size eq 'tn' ) {
$photo = _shrink( $photo, 'x100' );
@@ -218,7 +223,7 @@ sub get_image_data {
} elsif ( $size eq 'full' ) {
# do nothing
} else {
- $photo = _shrink( $photo, $self->c->cobrand->default_photo_resize || '250x250' );
+ $photo = _shrink( $photo, $args{default} || '250x250' );
}
return $photo;
@@ -235,18 +240,37 @@ sub delete_cached {
);
}
+sub remove_images {
+ my ($self, $ids) = @_;
+
+ my @images = $self->all_ids;
+ my $dec = 0;
+ for (sort { $a <=> $b } @$ids) {
+ splice(@images, $_ + $dec, 1);
+ --$dec;
+ }
+
+ my $new_set = (ref $self)->new({
+ data_items => \@images,
+ object => $self->object,
+ });
+
+ $self->delete_cached();
+
+ return $new_set->data; # e.g. new comma-separated fileid
+}
+
sub rotate_image {
my ($self, $index, $direction) = @_;
- my @images = $self->all_images;
+ my @images = $self->all_ids;
return if $index > $#images;
- my @items = map $_->[0], @images;
- $items[$index] = _rotate_image( $images[$index][1], $direction );
+ my $image_data = $self->get_raw_image_data($index);
+ $images[$index] = _rotate_image( $image_data, $direction );
my $new_set = (ref $self)->new({
- data_items => \@items,
- c => $self->c,
+ data_items => \@images,
object => $self->object,
});
@@ -268,11 +292,6 @@ sub _rotate_image {
}
-
-
-
-# NB: These 2 subs stolen from A::C::Photo, should be purged from there!
-#
# Shrinks a picture to the specified size, but keeping in proportion.
sub _shrink {
my ($photo, $size) = @_;