aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm108
-rw-r--r--perllib/FixMyStreet/App/Controller/Report/New.pm125
-rw-r--r--perllib/FixMyStreet/App/Controller/Tokens.pm1
-rw-r--r--templates/web/base/js/validation_rules.html1
-rw-r--r--templates/web/base/report/new/fill_in_details.html10
-rw-r--r--templates/web/base/report/new/fill_in_details_form.html3
-rw-r--r--templates/web/base/report/new/form_user_loggedout.html21
-rw-r--r--templates/web/base/report/new/form_user_loggedout_email.html4
-rw-r--r--templates/web/base/report/new/login_success_form.html18
-rw-r--r--templates/web/base/report/new/oauth_email_form.html26
-rw-r--r--web/cobrands/fixmystreet/fixmystreet.js14
-rw-r--r--web/cobrands/sass/_base.scss30
-rw-r--r--web/i/facebook-icon-32.pngbin0 -> 211 bytes
-rw-r--r--web/js/fixmystreet.js4
14 files changed, 312 insertions, 53 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm
index b6cd84cf0..cf728806c 100644
--- a/perllib/FixMyStreet/App/Controller/Auth.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth.pm
@@ -8,6 +8,7 @@ use Email::Valid;
use Net::Domain::TLD;
use mySociety::AuthToken;
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');
@@ -182,6 +185,111 @@ 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});
+ } 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.
diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm
index 1b4c1b295..bab0f0fd0 100644
--- a/perllib/FixMyStreet/App/Controller/Report/New.pm
+++ b/perllib/FixMyStreet/App/Controller/Report/New.pm
@@ -391,6 +391,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
@@ -436,9 +442,6 @@ sub initialize_report : Private {
# save the token to delete at the end
$c->stash->{partial_token} = $token if $report;
- # Stash the photo IDs for "already got" display
- $c->stash->{upload_fileid} = $report->get_photoset->data;
-
} else {
# no point keeping it if it is done.
$token->delete;
@@ -446,18 +449,25 @@ sub initialize_report : Private {
}
}
- 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') ) {
@@ -989,6 +999,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;
@@ -1002,6 +1019,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.
@@ -1013,7 +1043,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 ) {
@@ -1023,15 +1087,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 );
@@ -1048,35 +1107,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();
diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm
index c10904f8f..9057f217e 100644
--- a/perllib/FixMyStreet/App/Controller/Tokens.pm
+++ b/perllib/FixMyStreet/App/Controller/Tokens.pm
@@ -105,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' );
diff --git a/templates/web/base/js/validation_rules.html b/templates/web/base/js/validation_rules.html
index 409d0971f..5d55baff1 100644
--- a/templates/web/base/js/validation_rules.html
+++ b/templates/web/base/js/validation_rules.html
@@ -1,7 +1,6 @@
validation_rules = {
title: { required: true },
detail: { required: true },
- email: { required: true },
update: { required: true },
rznvy: { required: true }
};
diff --git a/templates/web/base/report/new/fill_in_details.html b/templates/web/base/report/new/fill_in_details.html
index 9859fa4fd..55b3a5207 100644
--- a/templates/web/base/report/new/fill_in_details.html
+++ b/templates/web/base/report/new/fill_in_details.html
@@ -33,12 +33,14 @@
<div id="skipped-map">
[% END %]
- [% IF login_success %]
- <p class='form-success'>[% loc('You have successfully signed in; please check and confirm your details are accurate:') %]</p>
- [% END %]
-
<div id="report-a-problem-main">
+ [% IF login_success %]
+ [% PROCESS 'report/new/login_success_form.html' %]
+ [% ELSIF oauth_need_email %]
+ [% PROCESS 'report/new/oauth_email_form.html' %]
+ [% ELSE %]
[% PROCESS 'report/new/fill_in_details_form.html' %]
+ [% END %]
</div>
</div>
diff --git a/templates/web/base/report/new/fill_in_details_form.html b/templates/web/base/report/new/fill_in_details_form.html
index cd8a0d814..f9da3753f 100644
--- a/templates/web/base/report/new/fill_in_details_form.html
+++ b/templates/web/base/report/new/fill_in_details_form.html
@@ -19,6 +19,9 @@
[% IF report.used_map && partial_token %]
<p id="unknown">[% loc('Please note your report has <strong>not yet been sent</strong>. Choose a category and add further information below, then submit.') %]</p>
[% END %]
+[% IF oauth_failure %]
+ <p class="form-error">[% loc('Sorry, we could not log you in. Please fill in the form below.') %]</p>
+[% END %]
[% TRY %][% PROCESS 'report/new/sidebar.html' %][% CATCH file %][% END %]
diff --git a/templates/web/base/report/new/form_user_loggedout.html b/templates/web/base/report/new/form_user_loggedout.html
index 5f2f473df..6657c87a1 100644
--- a/templates/web/base/report/new/form_user_loggedout.html
+++ b/templates/web/base/report/new/form_user_loggedout.html
@@ -1,8 +1,25 @@
-[% PROCESS 'report/new/form_user_loggedout_email.html' %]
+[% IF c.config.FACEBOOK_APP_ID %]
+ <h3>[% loc("Now to submit your report&hellip;") %]</h3>
-<div id="form_sign_in">
+ <div class="form-box">
+ <button name="facebook_sign_in" id="facebook_sign_in" value="facebook_sign_in" class="btn btn--block btn--social btn--facebook">
+ <img alt="" src="/i/facebook-icon-32.png" width="17" height="32">
+ Log in with Facebook
+ </button>
+ </div>
+ <div id="js-social-email-hide">
+ [% PROCESS 'report/new/form_user_loggedout_email.html' required = 0 %]
+[% ELSE %]
+ [% PROCESS 'report/new/form_user_loggedout_email.html' required = 1 %]
<h3>[% loc("Now to submit your report&hellip;") %]</h3>
+[% END %]
+
+<div id="form_sign_in">
<h2>[% tprintf(loc("Do you have a %s password?", "%s is the site name"), site_name) %]</h2>
[% PROCESS 'report/new/form_user_loggedout_password.html' %]
[% PROCESS 'report/new/form_user_loggedout_by_email.html' %]
</div>
+
+[% IF c.config.FACEBOOK_APP_ID %]
+ </div>
+[% END %]
diff --git a/templates/web/base/report/new/form_user_loggedout_email.html b/templates/web/base/report/new/form_user_loggedout_email.html
index d7a1790e2..4f816f8cc 100644
--- a/templates/web/base/report/new/form_user_loggedout_email.html
+++ b/templates/web/base/report/new/form_user_loggedout_email.html
@@ -4,4 +4,6 @@
[% IF field_errors.email %]
<p class='form-error'>[% field_errors.email %]</p>
[% END %]
-<input type="email" value="[% report.user.email | html %]" name="email" id="form_email" placeholder="[% loc('Please enter your email address') %]" required>
+<input type="email" value="[% report.user.email | html %]" name="email" id="form_email" placeholder="[% loc('Please enter your email address') %]"
+ [% IF required %]required[% END %]
+ class="required">
diff --git a/templates/web/base/report/new/login_success_form.html b/templates/web/base/report/new/login_success_form.html
new file mode 100644
index 000000000..45d0221a7
--- /dev/null
+++ b/templates/web/base/report/new/login_success_form.html
@@ -0,0 +1,18 @@
+<h1>[% loc('Report your problem') %]</h1>
+
+<p class='form-success'>[% loc('You have successfully signed in; please check and confirm your details are accurate:') %]</p>
+
+[% TRY %][% PROCESS 'report/new/sidebar.html' %][% CATCH file %][% END %]
+
+[% INCLUDE 'errors.html' %]
+
+<fieldset>
+ <div id="problem_form">
+ [% IF c.user_exists %]
+ [% PROCESS "report/new/form_user_loggedin.html" %]
+ [% ELSE %]
+ [% PROCESS "report/new/form_user_loggedout.html" %]
+ [% END %]
+ [% PROCESS 'report/new/form_report.html' %]
+ </div>
+</fieldset>
diff --git a/templates/web/base/report/new/oauth_email_form.html b/templates/web/base/report/new/oauth_email_form.html
new file mode 100644
index 000000000..c897aaeea
--- /dev/null
+++ b/templates/web/base/report/new/oauth_email_form.html
@@ -0,0 +1,26 @@
+<h1>[% loc('Report your problem') %]</h1>
+
+<p class="form-error">
+ [% loc('Please note your report has <strong>not yet been sent</strong>.') %]
+ [% loc('We need your email address, please give it below.') %]
+</p>
+
+[% TRY %][% PROCESS 'report/new/sidebar.html' %][% CATCH file %][% END %]
+
+[% INCLUDE 'errors.html' %]
+
+<fieldset>
+ <div id="problem_form">
+ [% PROCESS 'report/new/form_user_loggedout_email.html' required=1 %]
+
+ <div id="form_sign_in">
+ <h3>[% loc("Now to submit your report&hellip;") %]</h3>
+ <h2>[% tprintf(loc("Do you have a %s password?", "%s is the site name"), site_name) %]</h2>
+ [% PROCESS 'report/new/form_user_loggedout_by_email.html' %]
+ [% PROCESS 'report/new/form_user_loggedout_password.html' %]
+ </div>
+
+ <input type="hidden" name="oauth_need_email" value="1">
+ [% PROCESS 'report/new/form_report.html' %]
+ </div>
+</fieldset>
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js
index 3b81ad144..129cc73bc 100644
--- a/web/cobrands/fixmystreet/fixmystreet.js
+++ b/web/cobrands/fixmystreet/fixmystreet.js
@@ -299,6 +299,20 @@ $(function(){
});
}
+ /* Log in with email button */
+ var email_form = $('#js-social-email-hide'),
+ button = $('<button class="btn btn--social btn--social-email">Log in with email</button>'),
+ form_box = $('<div class="form-box"></div>');
+ button.click(function(e){
+ e.preventDefault();
+ email_form.fadeIn(500);
+ form_box.hide();
+ });
+ form_box.append(button).insertBefore(email_form);
+ if ($('.form-error').length) {
+ button.click();
+ }
+
/*
* Show on click - pretty generic
*/
diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss
index 79ed91d97..8992893bb 100644
--- a/web/cobrands/sass/_base.scss
+++ b/web/cobrands/sass/_base.scss
@@ -316,6 +316,9 @@ label{
font-size:1.25em;
margin:0.5em 0;
}
+ h2 {
+ margin: 0 0 0.5em;
+ }
h5 {
margin:0 0 1em;
font: {
@@ -738,6 +741,33 @@ input.final-submit {
float: $right;
}
+.btn--facebook {
+ @include button-reset(#3b5998, darken(#3b5998, 10%), #3b5998, #fff, darken(#3b5998, 5%), darken(#3b5998, 10%), #3b5998, #fff);
+
+ &:visited {
+ color: #fff;
+ }
+
+ img {
+ margin-right: 0.5em;
+ vertical-align: -0.2em;
+ height: 1.3em;
+ width: auto;
+ }
+}
+
+// Under the button to override its text transform and width
+.btn--social {
+ display: block;
+ width: 100%;
+ text-transform: none;
+ text-align: center;
+}
+
+.js #js-social-email-hide {
+ display: none;
+}
+
.button-fwd {
padding: flip(1em 3em 1em 1em, 1em 1em 1em 3em);
background: inline-image("../fixmystreet/images/chevron-grey-#{$right}.svg") $right 50% no-repeat;
diff --git a/web/i/facebook-icon-32.png b/web/i/facebook-icon-32.png
new file mode 100644
index 000000000..460ddc8a6
--- /dev/null
+++ b/web/i/facebook-icon-32.png
Binary files differ
diff --git a/web/js/fixmystreet.js b/web/js/fixmystreet.js
index 51fa01559..ad3a8c570 100644
--- a/web/js/fixmystreet.js
+++ b/web/js/fixmystreet.js
@@ -128,6 +128,10 @@ $(function(){
$('#form_name').addClass('required').removeClass('valid');
} );
+ $('#facebook_sign_in').click(function(e){
+ $('#form_email').removeClass();
+ });
+
// Geolocation
if (geo_position_js.init()) {
var link = '<a href="#LINK" id="geolocate_link">&hellip; ' + translation_strings.geolocate + '</a>';