diff options
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Auth.pm | 109 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Photo.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/New.pm | 15 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Report/Update.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/Tokens.pm | 2 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Model/PhotoSet.pm | 10 | ||||
-rw-r--r-- | t/Mock/Twitter.pm | 43 | ||||
-rw-r--r-- | t/app/controller/auth_social.t | 114 | ||||
-rw-r--r-- | templates/web/base/auth/general.html | 14 | ||||
-rw-r--r-- | templates/web/base/report/new/form_user_loggedout.html | 14 | ||||
-rw-r--r-- | templates/web/base/report/update/form_user_loggedout.html | 14 | ||||
-rw-r--r-- | templates/web/fixmystreet.com/report/new/unresponsive_body.html | 2 | ||||
-rw-r--r-- | web/cobrands/sass/_base.scss | 21 | ||||
-rw-r--r-- | web/i/twitter-icon-32.png | bin | 0 -> 566 bytes | |||
-rw-r--r-- | web/js/fixmystreet.js | 2 |
15 files changed, 330 insertions, 34 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm index 9e8fb29aa..c5a6cf9bf 100644 --- a/perllib/FixMyStreet/App/Controller/Auth.pm +++ b/perllib/FixMyStreet/App/Controller/Auth.pm @@ -9,6 +9,7 @@ use Net::Domain::TLD; use mySociety::AuthToken; use JSON::MaybeXS; use Net::Facebook::Oauth2; +use Net::Twitter::Lite::WithAPIv1_1; =head1 NAME @@ -38,6 +39,7 @@ sub general : Path : Args(0) { # decide which action to take $c->detach('facebook_sign_in') if $c->get_param('facebook_sign_in'); + $c->detach('twitter_sign_in') if $c->get_param('twitter_sign_in'); my $clicked_password = $c->get_param('sign_in'); my $clicked_email = $c->get_param('email_sign_in'); @@ -133,6 +135,8 @@ sub email_sign_in : Private { }; $token_data->{facebook_id} = $c->session->{oauth}{facebook_id} if $c->get_param('oauth_need_email') && $c->session->{oauth}{facebook_id}; + $token_data->{twitter_id} = $c->session->{oauth}{twitter_id} + if $c->get_param('oauth_need_email') && $c->session->{oauth}{twitter_id}; my $token_obj = $c->model('DB::Token')->create({ scope => 'email_sign_in', @@ -180,6 +184,7 @@ sub token : Path('/M') : Args(1) { $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->twitter_id( $data->{twitter_id} ) if $data->{twitter_id}; $user->update; $c->authenticate( { email => $user->email }, 'no_password' ); @@ -203,7 +208,7 @@ sub fb : Private { } sub facebook_sign_in : Private { - my( $self, $c ) = @_; + my ( $self, $c ) = @_; my $fb = $c->forward('/auth/fb'); my $url = $fb->get_authorization_url(scope => ['email']); @@ -223,17 +228,9 @@ Handles the Facebook callback request and completes the authentication sequence. =cut sub facebook_callback: Path('/auth/Facebook') : Args(0) { - my( $self, $c ) = @_; + 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; - } - } + $c->detach('oauth_failure') if $c->get_param('error_code'); my $fb = $c->forward('/auth/fb'); my $access_token; @@ -250,12 +247,92 @@ sub facebook_callback: Path('/auth/Facebook') : Args(0) { $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}; + $c->forward('oauth_success', [ 'facebook', $info->{id}, $info->{name}, $email ]); +} + +=head2 twitter_sign_in + +Starts the Twitter authentication sequence. + +=cut + +sub tw : Private { + my ($self, $c) = @_; + Net::Twitter::Lite::WithAPIv1_1->new( + ssl => 1, + consumer_key => $c->config->{TWITTER_KEY}, + consumer_secret => $c->config->{TWITTER_SECRET}, + ); +} + +sub twitter_sign_in : Private { + my ( $self, $c ) = @_; + + my $twitter = $c->forward('/auth/tw'); + my $url = $twitter->get_authentication_url(callback => $c->uri_for('/auth/Twitter')); + + my %oauth; + $oauth{return_url} = $c->get_param('r'); + $oauth{detach_to} = $c->stash->{detach_to}; + $oauth{detach_args} = $c->stash->{detach_args}; + $oauth{token} = $twitter->request_token; + $oauth{token_secret} = $twitter->request_token_secret; + $c->session->{oauth} = \%oauth; + $c->res->redirect($url); +} + +=head2 twitter_callback + +Handles the Twitter callback request and completes the authentication sequence. + +=cut + +sub twitter_callback: Path('/auth/Twitter') : Args(0) { + my ( $self, $c ) = @_; + + my $request_token = $c->req->param('oauth_token'); + my $verifier = $c->req->param('oauth_verifier'); + my $oauth = $c->session->{oauth}; + + $c->detach('oauth_failure') if $c->get_param('denied') || $request_token ne $oauth->{token}; + + my $twitter = $c->forward('/auth/tw'); + $twitter->request_token($oauth->{token}); + $twitter->request_token_secret($oauth->{token_secret}); + + eval { + # request_access_token no longer returns UID or name + $twitter->request_access_token(verifier => $verifier); + }; + if ($@) { + ($c->stash->{message} = $@) =~ s/at [^ ]*Auth.pm.*//; + $c->stash->{template} = 'errors/generic.html'; + $c->detach; + } + + my $info = $twitter->verify_credentials(); + $c->forward('oauth_success', [ 'twitter', $info->{id}, $info->{name} ]); +} + +sub oauth_failure : Private { + my ( $self, $c ) = @_; + + $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; + } +} + +sub oauth_success : Private { + my ($self, $c, $type, $uid, $name, $email) = @_; my $user; if ($email) { + # Only Facebook gets here # 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 } ); @@ -267,14 +344,14 @@ sub facebook_callback: Path('/auth/Facebook') : Args(0) { $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 } ); + $user = $c->model('DB::User')->find( { $type . '_id' => $uid } ); if ($user) { - # Matching Facebook ID in our database + # Matching 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->session->{oauth}{$type . '_id'} = $uid; $c->stash->{oauth_need_email} = 1; } } diff --git a/perllib/FixMyStreet/App/Controller/Photo.pm b/perllib/FixMyStreet/App/Controller/Photo.pm index 59f54bad7..d24d3ff71 100644 --- a/perllib/FixMyStreet/App/Controller/Photo.pm +++ b/perllib/FixMyStreet/App/Controller/Photo.pm @@ -140,7 +140,7 @@ sub process_photo_upload_or_cache : Private { /^photo/ ? # photo, photo1, photo2 etc. ($c->req->upload($_)) : () } sort $c->req->upload), - split /,/, ($c->get_param('upload_fileid') || '') + grep { $_ } split /,/, ($c->get_param('upload_fileid') || '') ); my $photoset = FixMyStreet::App::Model::PhotoSet->new({ diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 7126e962e..5df182506 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -221,7 +221,7 @@ sub category_extras_ajax : Path('category_extras') : Args(0) { $c->forward('setup_categories_and_bodies'); $c->forward('check_for_category'); - my $category = $c->stash->{category}; + my $category = $c->stash->{category} || ""; my $category_extra = ''; my $generate; if ( $c->stash->{category_extras}->{$category} && @{ $c->stash->{category_extras}->{$category} } >= 1 ) { @@ -616,7 +616,16 @@ sub setup_categories_and_bodies : Private { $c->stash->{unresponsive} = {}; if (keys %bodies == 1 && $first_body->send_method && $first_body->send_method eq 'Refused') { - $c->stash->{unresponsive}{ALL} = $first_body->id; + # If there's only one body, and it's set to refused, we can show the + # message immediately, before they select a category. + if ($c->action->name eq 'category_extras_ajax' && $c->req->method eq 'POST') { + # The mobile app doesn't currently use this, in which case make + # sure the message is output, either below with a category, or when + # a blank category call is made. + $c->stash->{unresponsive}{""} = $first_body->id; + } else { + $c->stash->{unresponsive}{ALL} = $first_body->id; + } } # keysort does not appear to obey locale so use strcoll (see i18n.t) @@ -1009,6 +1018,8 @@ sub tokenize_user : Private { }; $c->stash->{token_data}{facebook_id} = $c->session->{oauth}{facebook_id} if $c->get_param('oauth_need_email') && $c->session->{oauth}{facebook_id}; + $c->stash->{token_data}{twitter_id} = $c->session->{oauth}{twitter_id} + if $c->get_param('oauth_need_email') && $c->session->{oauth}{twitter_id}; } =head2 save_user_and_report diff --git a/perllib/FixMyStreet/App/Controller/Report/Update.pm b/perllib/FixMyStreet/App/Controller/Report/Update.pm index 8d6bc2019..af4ccff03 100644 --- a/perllib/FixMyStreet/App/Controller/Report/Update.pm +++ b/perllib/FixMyStreet/App/Controller/Report/Update.pm @@ -382,6 +382,8 @@ sub tokenize_user : Private { }; $c->stash->{token_data}{facebook_id} = $c->session->{oauth}{facebook_id} if $c->get_param('oauth_need_email') && $c->session->{oauth}{facebook_id}; + $c->stash->{token_data}{twitter_id} = $c->session->{oauth}{twitter_id} + if $c->get_param('oauth_need_email') && $c->session->{oauth}{twitter_id}; } =head2 save_update diff --git a/perllib/FixMyStreet/App/Controller/Tokens.pm b/perllib/FixMyStreet/App/Controller/Tokens.pm index eb35fd152..38f344250 100644 --- a/perllib/FixMyStreet/App/Controller/Tokens.pm +++ b/perllib/FixMyStreet/App/Controller/Tokens.pm @@ -106,6 +106,7 @@ sub confirm_problem : Path('/P') { $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->twitter_id( $data->{twitter_id} ) if $data->{twitter_id}; $problem->user->update; } $c->authenticate( { email => $problem->user->email }, 'no_password' ); @@ -232,6 +233,7 @@ sub confirm_update : Path('/C') { $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->twitter_id( $data->{twitter_id} ) if $data->{twitter_id}; $comment->user->update; } diff --git a/perllib/FixMyStreet/App/Model/PhotoSet.pm b/perllib/FixMyStreet/App/Model/PhotoSet.pm index 41d02d2a7..243675f2c 100644 --- a/perllib/FixMyStreet/App/Model/PhotoSet.pm +++ b/perllib/FixMyStreet/App/Model/PhotoSet.pm @@ -176,7 +176,7 @@ has ids => ( # Arrayref of $fileid tuples (always, so post upload/raw data proc } my ($fileid, $type) = split /\./, $part; $type ||= 'jpeg'; - if (length($fileid) == 40) { + if ($fileid && length($fileid) == 40) { my $file = $self->get_file($fileid, $type); if ($file->exists) { $file->basename; @@ -185,7 +185,7 @@ has ids => ( # Arrayref of $fileid tuples (always, so post upload/raw data proc (); } } else { - warn sprintf "Received bad photo hash of length %d", length($fileid); + # A bad hash, probably a bot spamming with bad data. (); } }); @@ -265,13 +265,15 @@ sub remove_images { --$dec; } + $self->delete_cached(); + + return undef if !@images; + 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 } diff --git a/t/Mock/Twitter.pm b/t/Mock/Twitter.pm new file mode 100644 index 000000000..930895e28 --- /dev/null +++ b/t/Mock/Twitter.pm @@ -0,0 +1,43 @@ +package t::Mock::Twitter; + +use JSON::MaybeXS; +use Web::Simple; +use MooX::Types::MooseLike::Base qw(:all); + +has json => ( + is => 'lazy', + default => sub { + JSON->new->pretty->allow_blessed->convert_blessed; + }, +); + +sub dispatch_request { + my $self = shift; + + sub (GET + /oauth/authenticate + ?*) { + my ($self) = @_; + return [ 200, [ 'Content-Type' => 'text/html' ], [ 'TwitteB login page' ] ]; + }, + + sub (GET + /oauth/access_token + ?*) { + my ($self) = @_; + return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'oauth_token=access_token&oauth_token_secret=secret' ] ]; + }, + + sub (GET + /oauth/request_token + ?*) { + my ($self) = @_; + return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'oauth_token=request-token&oauth_token_secret=secret&oauth_callback_confirmed=true' ] ]; + }, + + sub (GET + /1.1/account/verify_credentials.json + ?*) { + my ($self) = @_; + my $data = { + id => '987654321', + name => 'Fiona Tester', + }; + my $json = $self->json->encode($data); + return [ 200, [ 'Content-Type' => 'text/html' ], [ $json ] ]; + }, +} + +__PACKAGE__->run_if_script; diff --git a/t/app/controller/auth_social.t b/t/app/controller/auth_social.t index b8675a8fd..6929c0ddc 100644 --- a/t/app/controller/auth_social.t +++ b/t/app/controller/auth_social.t @@ -6,6 +6,7 @@ use LWP::Simple; use JSON::MaybeXS; use t::Mock::Facebook; +use t::Mock::Twitter; use t::Mock::MapIt; use FixMyStreet::TestMech; @@ -21,6 +22,7 @@ LWP::Protocol::PSGI->register(t::Mock::MapIt->to_psgi_app, host => 'mapit.uk'); FixMyStreet::override_config { FACEBOOK_APP_ID => 'facebook-app-id', + TWITTER_KEY => 'twitter-key', ALLOWED_COBRANDS => [ { fixmystreet => '.' } ], MAPIT_URL => 'http://mapit.uk/', }, sub { @@ -138,6 +140,118 @@ for my $fb_state ( 'refused', 'no email', 'existing UID', 'okay' ) { } } +my $tw_email = 'twitter@example.org'; +my $tw_uid = 987654321; + +# Twitter has no way of getting the email, so no "okay" state here +for my $tw_state ( 'refused', 'existing UID', 'no email' ) { + for my $page ( 'my', 'report', 'update' ) { + subtest "test Twitter '$tw_state' login for page '$page'" => sub { + # Lots of user changes happening here, make sure we don't confuse + # Catalyst with a cookie session user that no longer exists + $mech->log_out_ok; + $mech->cookie_jar({}); + if ($tw_state eq 'existing UID') { + my $user = $mech->create_user_ok($tw_email); + $user->update({ twitter_id => $tw_uid }); + } else { + $mech->delete_user($tw_email); + } + + # Set up a mock to catch (most, see below) requests to Twitter + my $tw = t::Mock::Twitter->new; + LWP::Protocol::PSGI->register($tw->to_psgi_app, host => 'api.twitter.com'); + + # Due to https://metacpan.org/pod/Test::WWW::Mechanize::Catalyst#External-Redirects-and-allow_external + # the redirect to Twitter's OAuth page can mess up the session + # cookie. So let's pretend we always on api.twitter.com, which + # sorts that out. + $mech->host('api.twitter.com'); + + # Fetch the page with the form via which we wish to log in + my $fields; + if ($page eq 'my') { + $mech->get_ok('/my'); + } elsif ($page eq 'report') { + $mech->get_ok('/'); + $mech->submit_form_ok( { with_fields => { pc => 'SW1A1AA' } }, "submit location" ); + $mech->follow_link_ok( { text_regex => qr/skip this step/i, }, "follow 'skip this step' link" ); + $fields = { + title => 'Test title', + detail => 'Test detail', + }; + } else { + $mech->get_ok('/report/' . $report->id); + $fields = { + update => 'Test update', + }; + } + $mech->submit_form(with_fields => $fields, button => 'twitter_sign_in'); + + # As well as the cookie issue above, caused by this external + # redirect rewriting the host, the redirect gets handled directly + # by Catalyst, not our mocked handler, so will be a 404. Check + # the redirect happened instead. + is $mech->res->previous->code, 302, 'Twitter button redirected'; + like $mech->res->previous->header('Location'), qr{api\.twitter\.com/oauth/authenticate\?oauth_token=request-token}, 'Twitter redirect to oauth URL'; + + # Okay, now call the callback Facebook would send us to + if ($tw_state eq 'refused') { + $mech->get_ok('/auth/Twitter?denied=token'); + } else { + $mech->get_ok('/auth/Twitter?oauth_token=request-token&oauth_verifier=verifier'); + } + + # Check we're showing the right form, regardless of what came back + if ($page eq 'report') { + $mech->content_contains('/report/new'); + } elsif ($page eq 'update') { + $mech->content_contains('/report/update'); + } + + if ($tw_state eq 'refused') { + $mech->content_contains('Sorry, we could not log you in. Please fill in the form below.'); + $mech->not_logged_in_ok; + } elsif ($tw_state eq 'no email') { + $mech->content_contains('We need your email address, please give it below.'); + # We don't have an email, so check that we can still submit it, + # and the ID carries through the confirmation + if ($page eq 'update') { + $fields->{rznvy} = $tw_email; + } else { + $fields->{email} = $tw_email; + } + $fields->{name} = 'Ffion Tester'; + $mech->submit_form(with_fields => $fields); + $mech->content_contains('Nearly done! Now check your email'); + + my $email = $mech->get_email; + ok $email, "got an email"; + $mech->clear_emails_ok; + my ( $url, $url_token ) = $email->body =~ m{(https?://\S+/[CMP]/)(\S+)}; + ok $url, "extracted confirm url '$url'"; + + my $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $tw_email } ); + if ($page eq 'my') { + is $user, undef, 'No user yet exists'; + } else { + is $user->twitter_id, undef, 'User has no twitter ID'; + } + $mech->get_ok( $url . $url_token ); + $user = FixMyStreet::App->model( 'DB::User' )->find( { email => $tw_email } ); + is $user->twitter_id, $tw_uid, 'User now has correct twitter ID'; + + } elsif ($page ne 'my') { + # /my auth login goes directly there, no message like this + $mech->content_contains('You have successfully signed in; please check and confirm your details are accurate'); + $mech->logged_in_ok; + } else { + is $mech->uri->path, '/my', 'Successfully on /my page'; + } + } + } +} + }; END { diff --git a/templates/web/base/auth/general.html b/templates/web/base/auth/general.html index d856dc19a..253dc26a1 100644 --- a/templates/web/base/auth/general.html +++ b/templates/web/base/auth/general.html @@ -14,13 +14,23 @@ <input type="hidden" name="r" value="[% c.req.params.r | html %]"> -[% IF NOT oauth_need_email AND c.config.FACEBOOK_APP_ID %] +[% IF NOT oauth_need_email AND (c.config.FACEBOOK_APP_ID OR c.config.TWITTER_KEY) %] + [% IF c.config.FACEBOOK_APP_ID %] <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> + [% END %] + [% IF c.config.TWITTER_KEY %] + <div class="form-box"> + <button name="twitter_sign_in" id="twitter_sign_in" value="twitter_sign_in" class="btn btn--block btn--social btn--twitter"> + <img alt="" src="/i/twitter-icon-32.png" width="17" height="32"> + Log in with Twitter + </button> + </div> + [% END %] <div id="js-social-email-hide"> [% END %] @@ -56,7 +66,7 @@ [% END %] </div> -[% IF NOT oauth_need_email AND c.config.FACEBOOK_APP_ID %] +[% IF NOT oauth_need_email AND (c.config.FACEBOOK_APP_ID OR c.config.TWITTER_KEY) %] </div> [% END %] diff --git a/templates/web/base/report/new/form_user_loggedout.html b/templates/web/base/report/new/form_user_loggedout.html index 6657c87a1..889834580 100644 --- a/templates/web/base/report/new/form_user_loggedout.html +++ b/templates/web/base/report/new/form_user_loggedout.html @@ -1,12 +1,22 @@ -[% IF c.config.FACEBOOK_APP_ID %] +[% IF c.config.FACEBOOK_APP_ID OR c.config.TWITTER_KEY %] <h3>[% loc("Now to submit your report…") %]</h3> + [% IF c.config.FACEBOOK_APP_ID %] <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> + [% END %] + [% IF c.config.TWITTER_KEY %] + <div class="form-box"> + <button name="twitter_sign_in" id="twitter_sign_in" value="twitter_sign_in" class="btn btn--block btn--social btn--twitter"> + <img alt="" src="/i/twitter-icon-32.png" width="17" height="32"> + Log in with Twitter + </button> + </div> + [% END %] <div id="js-social-email-hide"> [% PROCESS 'report/new/form_user_loggedout_email.html' required = 0 %] [% ELSE %] @@ -20,6 +30,6 @@ [% PROCESS 'report/new/form_user_loggedout_by_email.html' %] </div> -[% IF c.config.FACEBOOK_APP_ID %] +[% IF c.config.FACEBOOK_APP_ID OR c.config.TWITTER_KEY %] </div> [% END %] diff --git a/templates/web/base/report/update/form_user_loggedout.html b/templates/web/base/report/update/form_user_loggedout.html index 4176633f1..fa26eb8f4 100644 --- a/templates/web/base/report/update/form_user_loggedout.html +++ b/templates/web/base/report/update/form_user_loggedout.html @@ -1,11 +1,21 @@ -[% IF c.config.FACEBOOK_APP_ID %] +[% IF c.config.FACEBOOK_APP_ID OR c.config.TWITTER_KEY %] <h3>[% loc("Now to submit your update…") %]</h3> + [% IF c.config.FACEBOOK_APP_ID %] <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> + [% END %] + [% IF c.config.TWITTER_KEY %] + <div class="form-box"> + <button name="twitter_sign_in" id="twitter_sign_in" value="twitter_sign_in" class="btn btn--block btn--social btn--twitter"> + <img alt="" src="/i/twitter-icon-32.png" width="17" height="32"> + Log in with Twitter + </button> + </div> + [% END %] <div id="js-social-email-hide"> [% INCLUDE 'report/update/form_user_loggedout_email.html' required=0 %] [% ELSE %] @@ -19,6 +29,6 @@ [% INCLUDE 'report/update/form_user_loggedout_by_email.html' %] </div> -[% IF c.config.FACEBOOK_APP_ID %] +[% IF c.config.FACEBOOK_APP_ID OR c.config.TWITTER_KEY %] </div> [% END %] diff --git a/templates/web/fixmystreet.com/report/new/unresponsive_body.html b/templates/web/fixmystreet.com/report/new/unresponsive_body.html index 2ad3b7992..d0f16d188 100644 --- a/templates/web/fixmystreet.com/report/new/unresponsive_body.html +++ b/templates/web/fixmystreet.com/report/new/unresponsive_body.html @@ -8,5 +8,5 @@ reports from third party reporting sites such as FixMyStreet. </p> <p>We can make your report public, but we can’t send it to the council.</p> - <a href="/unresponsive?body=[% body_id %][% IF category %];category=[% category | uri %][% END %]">What can I do instead?</a> + <a href="[% c.cobrand.base_url %]/unresponsive?body=[% body_id %][% IF category %];category=[% category | uri %][% END %]">What can I do instead?</a> </div> diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss index 59af6a71f..eaa0113a5 100644 --- a/web/cobrands/sass/_base.scss +++ b/web/cobrands/sass/_base.scss @@ -354,7 +354,7 @@ label{ color: #666; padding-#{$left}: 20px; background: transparent url(/cobrands/fixmystreet/images/wrong-location.png) $left 50% no-repeat; - border-#{$left}: solid 4px #fff; + border-#{$left}: solid 4px transparent; margin-top: -0.5em; } @@ -760,6 +760,21 @@ input.final-submit { } } +.btn--twitter { + @include button-reset(#55acee, darken(#55acee, 10%), #55acee, #fff, darken(#55acee, 5%), darken(#55acee, 10%), #55acee, #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; @@ -1376,7 +1391,7 @@ label .muted { padding-#{$left}: 16px; list-style: none; background: transparent url(/cobrands/fixmystreet/images/tick-10px-8px.png) $left 5px no-repeat; - border-#{$left}: solid 4px #fff; + border-#{$left}: solid 4px transparent; } .do { @@ -1391,7 +1406,7 @@ label .muted { background-image: url(/cobrands/fixmystreet/images/cross-7px-7px.png); background-position: $left 6px; padding-#{$left}: 14px; - border-#{$left}: solid 6px #fff; + border-#{$left}: solid 6px transparent; } } } diff --git a/web/i/twitter-icon-32.png b/web/i/twitter-icon-32.png Binary files differnew file mode 100644 index 000000000..9f6cefd9f --- /dev/null +++ b/web/i/twitter-icon-32.png diff --git a/web/js/fixmystreet.js b/web/js/fixmystreet.js index 07c1a1a6b..0a15982f5 100644 --- a/web/js/fixmystreet.js +++ b/web/js/fixmystreet.js @@ -128,7 +128,7 @@ $(function(){ $('#form_name').addClass('required').removeClass('valid'); } ); - $('#facebook_sign_in').click(function(e){ + $('#facebook_sign_in, #twitter_sign_in').click(function(e){ $('#form_email').removeClass(); $('#form_rznvy').removeClass(); $('#email').removeClass(); |