Андрей (andy) wrote in changelog,
Андрей
andy
changelog

[ljcom] r9319: LJSUP-6137 (FaceBook authorization. Serv...

Committer: ailyin
LJSUP-6137 (FaceBook authorization. Server side): merge the branch to trunk, yay
U   trunk/bin/upgrading/en_LJ.dat
U   trunk/bin/upgrading/proplists-local.dat
U   trunk/bin/upgrading/update-db-local.pl
A   trunk/bin/worker/content-repost
A   trunk/cgi-bin/LJ/API/
D   trunk/cgi-bin/LJ/API/BitLy.pm
A   trunk/cgi-bin/LJ/API/BitLy.pm
D   trunk/cgi-bin/LJ/API/Twitter.pm
A   trunk/cgi-bin/LJ/API/Twitter.pm
A   trunk/cgi-bin/LJ/Hooks/Cleanup.pm
U   trunk/cgi-bin/LJ/Hooks/Userinfo.pm
A   trunk/cgi-bin/LJ/Identity/
D   trunk/cgi-bin/LJ/Identity/Facebook.pm
A   trunk/cgi-bin/LJ/Identity/Facebook.pm
U   trunk/cgi-bin/LJ/Setting/Display/AccountLevel.pm
A   trunk/cgi-bin/LJ/Setting/TwitterConnect.pm
A   trunk/cgi-bin/LJ/Talk/
D   trunk/cgi-bin/LJ/Talk/Author/
A   trunk/cgi-bin/LJ/Talk/Author/
D   trunk/cgi-bin/LJ/Talk/Author/Facebook.pm
A   trunk/cgi-bin/LJ/Talk/Author/Facebook.pm
A   trunk/cgi-bin/LJ/Widget/ConvertIdentityAccount.pm
A   trunk/cgi-bin/LJ/Worker/Repost/
D   trunk/cgi-bin/LJ/Worker/Repost/CommentToFacebook.pm
A   trunk/cgi-bin/LJ/Worker/Repost/CommentToFacebook.pm
D   trunk/cgi-bin/LJ/Worker/Repost/CommentToTwitter.pm
A   trunk/cgi-bin/LJ/Worker/Repost/CommentToTwitter.pm
D   trunk/cgi-bin/LJ/Worker/Repost/EntryToFacebook.pm
A   trunk/cgi-bin/LJ/Worker/Repost/EntryToFacebook.pm
D   trunk/cgi-bin/LJ/Worker/Repost/EntryToTwitter.pm
A   trunk/cgi-bin/LJ/Worker/Repost/EntryToTwitter.pm
A   trunk/cgi-bin/LJ/Worker/Repost.pm
U   trunk/cgi-bin/bml/scheme/lanzelot.look
U   trunk/cgi-bin/ljcom.pl
U   trunk/cvs/multicvs-local.conf
A   trunk/htdocs/identity/
D   trunk/htdocs/identity/callback-facebook.bml
A   trunk/htdocs/identity/callback-facebook.bml
D   trunk/htdocs/identity/convert.bml
A   trunk/htdocs/identity/convert.bml
D   trunk/htdocs/identity/convert.bml.text.local
A   trunk/htdocs/identity/convert.bml.text.local
D   trunk/htdocs/identity/facebook-interstitial.bml
A   trunk/htdocs/identity/facebook-interstitial.bml
D   trunk/htdocs/identity/facebook-interstitial.bml.text.local
A   trunk/htdocs/identity/facebook-interstitial.bml.text.local
D   trunk/htdocs/identity/login.bml.text
A   trunk/htdocs/identity/login.bml.text
D   trunk/htdocs/identity/login.bml.text.local
A   trunk/htdocs/identity/login.bml.text.local
U   trunk/htdocs/manage/profile/index.bml.text.local
A   trunk/htdocs/manage/settings/facebook.bml
A   trunk/htdocs/manage/settings/facebook.bml.text.local
A   trunk/htdocs/manage/settings/twitter.bml
A   trunk/htdocs/manage/settings/twitter.bml.text.local
A   trunk/htdocs/stc/img/twitter-profile.gif
U   trunk/htdocs/userinfo.bml.text.local
A   trunk/templates/CommentForm/
D   trunk/templates/CommentForm/Author-Facebook.tmpl
A   trunk/templates/CommentForm/Author-Facebook.tmpl
A   trunk/templates/Identity/
D   trunk/templates/Identity/Convert.tmpl
A   trunk/templates/Identity/Convert.tmpl
D   trunk/templates/Identity/FacebookInterstitial.tmpl
A   trunk/templates/Identity/FacebookInterstitial.tmpl
D   trunk/templates/Identity/Login-facebook.tmpl
A   trunk/templates/Identity/Login-facebook.tmpl
A   trunk/templates/Settings/
D   trunk/templates/Settings/FacebookConnect.tmpl
A   trunk/templates/Settings/FacebookConnect.tmpl
D   trunk/templates/Settings/TwitterConnect.tmpl
A   trunk/templates/Settings/TwitterConnect.tmpl
Modified: trunk/bin/upgrading/en_LJ.dat
===================================================================
--- trunk/bin/upgrading/en_LJ.dat	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/bin/upgrading/en_LJ.dat	2010-08-09 04:17:05 UTC (rev 9319)
@@ -1427,6 +1427,54 @@
 
 Email=E-mail
 
+email.converted_identity.body.A<<
+You're ready to embark on an exciting journey and discover friends, old and new, who share your hobbies and interests!
+
+Here are your vital stats:
+
+ Username: [[username]]
+
+ Journal URL: [[journal_base]]/
+
+ Forgot your password? To receive a reminder, click here:  [[lostinfourl]]
+
+You're all set!
+
+Once you validate your email, you'll be ready to customize and post to your Journal, find friends, and complete your user profile.  We look forward to getting to know you better! 
+
+Best,
+
+The [[sitename]] Team
+.
+
+email.converted_identity.body.N<<
+You're ready to embark on an exciting journey and discover friends, old and new, who share your hobbies and interests!
+
+Here are your vital stats:
+
+ Username: [[username]]
+
+ Journal URL: [[journal_base]]/
+
+ Forgot your password? To receive a reminder, click here:  [[lostinfourl]]
+
+Please click the following link to validate your email address*: 
+
+[[regurl]]
+
+*If you're unable to click on this link, copy and paste it into your browser window.
+
+You need to validate your email to gain access to all the cool features of your new account (posting comments, posting entries in communities, receiving comment notification emails, etc.). Validation is also very important for protecting the security of your new journal.
+
+You're all set!
+
+Once you validate your email, you'll be ready to customize and post to your Journal, find friends, and complete your user profile.  We look forward to getting to know you better! 
+
+Best,
+
+The [[sitename]] Team
+.
+
 email.newacct.body<<
 Congratulations, you have a new LiveJournal account!
 
@@ -2052,11 +2100,15 @@
 
 facebookconnect.notification.title2=posted a new entry to '[[journal]]' at LiveJournal. Read and comment: [[url]].
 
+facebookconnect.notification.title.comment=posted a new comment to '[[journal]]' at LiveJournal. Read and comment: [[url]].
+
 facebookconnect.question|staleness=1
 facebookconnect.question=What do you think?
 
 facebookconnect.untitled=(Untitled)
 
+facebookconnect.untitled.comment=(Untitled)
+
 fcklang.videoprompt=Please enter a video URL:
 
 find|staleness=1
@@ -2385,6 +2437,8 @@
 
 horizon.nav.home=Home
 
+horizon.nav.identity_convert=Upgrade to full account
+
 horizon.nav.journal=Journal
 
 horizon.nav.journal.calendar=Calendar
@@ -4810,6 +4864,16 @@
 
 reskining.july2010=sometext1 <lj user="test"> sometext2 <lj user="test"> sometext3 
 
+repost.label.facebook=Facebook
+
+repost.label.twitter=Twitter
+
+repost.link.edit=Edit
+
+repost.label.entryform=Repost
+
+repost.label.talkform=Repost to
+
 schemes.horizon.desc|staleness=1
 schemes.horizon.desc=site default
 
@@ -4887,6 +4951,8 @@
 setting.display.accountlevel.actionlink.basic|staleness=1
 setting.display.accountlevel.actionlink.basic=Upgrade
 
+setting.display.accountlevel.actionlink.identity_convert=Upgrade to full account
+
 setting.display.accountlevel.actionlink.paid|staleness=1
 setting.display.accountlevel.actionlink.paid=Manage
 
@@ -4935,14 +5001,26 @@
 
 setting.facebookbeacon.option.note=Note: If you are logged into Facebook, this allows you to update your Facebook Wall when you post an entry.
 
+setting.facebookconnect.button.facebook_connect=Connect
+
 setting.facebookconnect.label=Facebook Connect
 
+setting.facebookconnect.link.remove=remove
+
+setting.facebookconnect.note.connected=Note: your account on Facebook
+
+setting.facebookconnect.note.disconnected=Note: to allow publishing your entries and comments to Facebook, connect your accounts
+
 setting.facebookconnect.option.always|staleness=1
 setting.facebookconnect.option.always=Always
 
 setting.facebookconnect.option.ask|staleness=1
 setting.facebookconnect.option.ask=Ask each time
 
+setting.facebookconnect.option.comments_facebook=Publish my comments to Facebook by default
+
+setting.facebookconnect.option.entries_facebook=Publish my entries to Facebook by default
+
 setting.facebookconnect.option.never|staleness=1
 setting.facebookconnect.option.never=Never
 
@@ -5018,6 +5096,20 @@
 
 setting.sms.option=Receive updates and interact with [[sitename]] by text message to TXTLJ (89855). See the <a [[aopts]]>full list of commands</a>. 
 
+setting.twitterconnect.button.twitter_connect=Connect
+
+setting.twitterconnect.label=Twitter Connect
+
+setting.twitterconnect.link.remove=remove
+
+setting.twitterconnect.note.connected=Note: your account on Twitter
+
+setting.twitterconnect.note.disconnected=Note: to allow publishing your entries and comments to Twitter, connect your accounts
+
+setting.twitterconnect.option.comments_twitter=Publish my comments to Twitter by default
+
+setting.twitterconnect.option.entries_twitter=Publish my entries to Twitter by default
+
 setting.userapps.change=Change
 
 setting.userapps.install=Install
@@ -5984,6 +6076,10 @@
 <A href="[[typepad_rules_url]]">Read the official rules here</a>.
 .
 
+twitterconnect.untitled.comment=New comment at LiveJournal:
+
+twitterconnect.untitled.entry=New entry at LiveJournal:
+
 userpic.inactive=<a href="http://www.livejournal.com/manage/payments/">Inactive</a>
 
 userpic.link=default userpic

Modified: trunk/bin/upgrading/proplists-local.dat
===================================================================
--- trunk/bin/upgrading/proplists-local.dat	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/bin/upgrading/proplists-local.dat	2010-08-09 04:17:05 UTC (rev 9319)
@@ -431,3 +431,44 @@
   datatype: char
   des: URL of the copy of this entry in LJ Times
   prettyname: URL of copy of the entry
+
+userproplist.facebook_access_token:
+  datatype: char
+  des: an access token for accessing the user's data on facebook
+  cldversion: 8
+
+userproplist.facebook_name:
+  datatype: char
+  des: a name set by the user on facebook (for identities of type 'facebook' only)
+  cldversion: 8
+
+userproplist.facebook_link:
+  datatype: char
+  des: a link to the user's profile on facebook (for identities of type 'facebook' only)
+  cldversion: 8
+
+userproplist.im_openid:
+  datatype: char
+  des: a user-provided OpenID identity, to be displayed in the user's profile
+  cldversion: 8
+
+userproplist.twitter_access_token:
+  datatype: char
+  des: an access token for accessing the user's data on twitter
+  cldversion: 8
+
+userproplist.twitter_access_token_secret:
+  datatype: char
+  des: an access token secret for accessing the user's data on twitter
+  cldversion: 8
+
+userproplist.twitter_name:
+  datatype: char
+  des: a name set by the user on twitter
+  cldversion: 8
+
+userproplist.twitter_link:
+  datatype: char
+  des: a link to the user's profile on twitter
+  cldversion: 8
+

Modified: trunk/bin/upgrading/update-db-local.pl
===================================================================
--- trunk/bin/upgrading/update-db-local.pl	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/bin/upgrading/update-db-local.pl	2010-08-09 04:17:05 UTC (rev 9319)
@@ -1505,6 +1505,16 @@
 ) TYPE=InnoDB
 EOC
 
+register_tablecreate("twitter_request_tokens", <<'EOC');
+CREATE TABLE `twitter_request_tokens` (
+  public CHAR(100) NOT NULL DEFAULT "",
+  secret CHAR(100) NOT NULL DEFAULT "",
+  issued INT NOT NULL DEFAULT 0,
+  UNIQUE INDEX(public),
+  INDEX(issued)
+)
+EOC
+
 # *************************************************************
 register_alter(sub {
 

Copied: trunk/bin/worker/content-repost (from rev 9318, branches/facebook-integration2/bin/worker/content-repost)
===================================================================
--- trunk/bin/worker/content-repost	                        (rev 0)
+++ trunk/bin/worker/content-repost	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,18 @@
+#!/usr/bin/perl
+
+use strict;
+
+use lib "$ENV{LJHOME}/cgi-bin";
+require 'ljlib.pl';
+
+use LJ::Worker::Repost;
+
+package LJ::NewWorker::TheSchwartz::ContentRepost;
+use base 'LJ::NewWorker::TheSchwartz';
+sub capabilities {
+    return LJ::Worker::Repost->subclasses;
+}
+
+__PACKAGE__->start;
+
+1;

Deleted: trunk/cgi-bin/LJ/API/BitLy.pm
===================================================================
--- branches/facebook-integration2/cgi-bin/LJ/API/BitLy.pm	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/cgi-bin/LJ/API/BitLy.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -1,19 +0,0 @@
-package LJ::API::BitLy;
-use strict;
-
-use LWP::UserAgent;
-
-sub shorten {
-    my ($class, $url) = @_;
-
-    my $ua = LWP::UserAgent->new;
-    my $address = 'http://api.j.mp/v3/shorten?' .
-                  'login=' . $LJ::BIT_LY_LOGIN . '&' .
-                  'apiKey=' . $LJ::BIT_LY_API_KEY . '&' .
-                  'longUrl=' . LJ::eurl($url) . '&' .
-                  'format=txt';
-
-    return $ua->get($address)->content;
-}
-
-1;

Copied: trunk/cgi-bin/LJ/API/BitLy.pm (from rev 9318, branches/facebook-integration2/cgi-bin/LJ/API/BitLy.pm)
===================================================================
--- trunk/cgi-bin/LJ/API/BitLy.pm	                        (rev 0)
+++ trunk/cgi-bin/LJ/API/BitLy.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,19 @@
+package LJ::API::BitLy;
+use strict;
+
+use LWP::UserAgent;
+
+sub shorten {
+    my ($class, $url) = @_;
+
+    my $ua = LWP::UserAgent->new;
+    my $address = 'http://api.j.mp/v3/shorten?' .
+                  'login=' . $LJ::BIT_LY_LOGIN . '&' .
+                  'apiKey=' . $LJ::BIT_LY_API_KEY . '&' .
+                  'longUrl=' . LJ::eurl($url) . '&' .
+                  'format=txt';
+
+    return $ua->get($address)->content;
+}
+
+1;

Deleted: trunk/cgi-bin/LJ/API/Twitter.pm
===================================================================
--- branches/facebook-integration2/cgi-bin/LJ/API/Twitter.pm	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/cgi-bin/LJ/API/Twitter.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -1,272 +0,0 @@
-package LJ::API::Twitter;
-use strict;
-
-use Net::OAuth;
-$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
-use Net::OAuth::RequestTokenRequest;
-use Net::OAuth::AccessTokenRequest;
-use Net::OAuth::ProtectedResourceRequest;
-use HTTP::Request::Common;
-use LWP::UserAgent;
-
-=head1 NAME
-
-LJ::API::Twitter - the module that handles communication between LJ
-and Twitter.
-
-=head1 SYNOPSIS
-
- use LJ::API::Twitter;
- 
- # request an OAuth request token
- my $req_token = LJ::API::Twitter->request_request_token;
- 
- # redirect to Twitter where one can confirm that they trust us
- LJ::Request->redirect( 'https://api.twitter.com/oauth/authorize?' .
-                        'oauth_token=' . $req_token->{'public'} );
- 
- # once the Twitter redirects the user back, get back our token
- # in full, along with the verifier
- my $public = LJ::Request->get_param('oauth_token');
- my $req_token = LJ::API::Twitter->fetch_request_token($public);
- my $verifier = LJ::Request->get_param('oauth_verifier');
- 
- # get the OAuth access token from Twitter and save it
- my $access_token = LJ::API::Twitter->request_access_token(
-    $req_token, $verifier);
- $u->prop( 'twitter_access_token'        => $access_token->{'public'} );
- $u->prop( 'twitter_access_token_secret' => $access_token->{'secret'} );
- 
- # use the token to call the API
- LJ::API::Twitter->call(
-    'api_method'   => 'account/verify_credentials',
-    'access_token' => $access_token,
- );
- 
- # alternatively, access token can be extracted from userprops
- LJ::API::Twitter->call(
-    'api_method' => 'account/verify_credentials',
-    'user'       => $u,
- );
- 
- # we can haz POST requests as well
- LJ::API::Twitter->call(
-    'api_method' => 'statuses/update',
-    'user' => $u,
-    'http_method' => 'POST',
-    'params' => { 'status' => $status },
- );
- 
- # the module saves request_token public/secret mapping
- # in the DB, so every so often you may want to clear that
- LJ::API::Twitter->remove_request_token($req_token);
- LJ::API::Twitter->clean_stale_request_tokens;
-
-=head1 SEE ALSO
-
-Twitter API Documentation: http://dev.twitter.com/doc
-
-This module is primarily used for reposting, so
-LJ::Setting::TwitterConnect,
-LJ::Hooks::ThirdPartyNotify,
-LJ::Worker::Repost::EntryToTwitter.pm,
-LJ::Worker::Repost::CommentToTwitter.pm
-
-=cut
-
-sub generate_nonce {
-    return time . '/' . LJ::rand_chars(20);
-}
-
-sub default_request_params {
-    return (
-        consumer_key => $LJ::TWITTER_CONSUMER_KEY,
-        consumer_secret => $LJ::TWITTER_CONSUMER_SECRET,
-        request_method => 'POST',
-        signature_method => 'HMAC-SHA1',
-        timestamp => time,
-        nonce => generate_nonce,
-    );
-}
-
-sub request_request_token {
-    my ($class) = @_;
-
-    my $request = Net::OAuth::RequestTokenRequest->new(
-        $class->default_request_params,
-        request_url => 'https://api.twitter.com/oauth/request_token',
-        callback => "$LJ::SITEROOT/manage/settings/twitter.bml?act=connect",
-    );
-
-    $request->sign;
-
-    my $ua = LWP::UserAgent->new;
-    my $res = $ua->post($request->to_url);
-
-    unless ($res->is_success) {
-        die "twitter connectivity error";
-    }
-
-    my %params_returned;
-    LJ::decode_url_string($res->content, \%params_returned);
-
-    my $token = {
-        'public' => $params_returned{'oauth_token'},
-        'secret' => $params_returned{'oauth_token_secret'},
-    };
-
-    $class->store_request_token($token);
-
-    return $token;
-}
-
-sub request_access_token {
-    my ($class, $request_token, $verifier) = @_;
-
-    my $request = Net::OAuth::AccessTokenRequest->new(
-        $class->default_request_params,
-        token => $request_token->{'public'},
-        token_secret => $request_token->{'secret'},
-        verifier => $verifier,
-        request_url => 'https://api.twitter.com/oauth/access_token',
-        callback => "$LJ::SITEROOT/manage/settings/twitter.bml?act=connect",
-    );
-
-    $request->sign;
-
-    my $ua = LWP::UserAgent->new;
-    my $res = $ua->post($request->to_url);
-
-    unless ($res->is_success) {
-        die "twitter connectivity error";
-    }
-
-    my %params_returned;
-    LJ::decode_url_string($res->content, \%params_returned);
-
-    my $token = {
-        'public' => $params_returned{'oauth_token'},
-        'secret' => $params_returned{'oauth_token_secret'},
-    };
-
-    return $token;
-}
-
-sub call {
-    my ($class, %opts) = @_;
-
-    my $api_method  = $opts{'api_method'};
-    die 'API method not provided' unless $api_method;
-
-    if (my $u = $opts{'user'}) {
-        $opts{'access_token'} = {
-            'public' => $u->prop('twitter_access_token'),
-            'secret' => $u->prop('twitter_access_token_secret'),
-        };
-    }
-
-    my $access_token = $opts{'access_token'};
-    die 'access token not provided' unless $access_token;
-
-    my $http_method = $opts{'http_method'} || 'GET';
-    die 'invalid HTTP method' unless $http_method =~ /^(?:GET|POST)$/;
-
-    my $params  = $opts{'params'}  || {};
-
-    my %params = $class->default_request_params;
-    $params{'request_method'} = $http_method;
-
-    my $request_url = 'https://api.twitter.com/1/' . $api_method . '.json';
-
-    my $request = Net::OAuth::ProtectedResourceRequest->new(
-        %params,
-        token => $access_token->{'public'},
-        token_secret => $access_token->{'secret'},
-        request_url => $request_url,
-        extra_params => {
-            %$params,
-        },
-    );
-
-    $request->sign;
-
-    my $ua = LWP::UserAgent->new;
-    my $res;
-    if ($http_method eq 'GET') {
-        $res = $ua->get($request->to_url);
-    } else {
-        $res = $ua->post( $request_url, $request->to_hash );
-    }
-
-    unless ($res->is_success) {
-        die "twitter connectivity error";
-    }
-
-    return LJ::JSON->from_json($res->content);
-}
-
-sub store_request_token {
-    my ($class, $token) = @_;
-
-    my $dbh = LJ::get_db_writer();
-    $dbh->{'RaiseError'} = 1;
-    $dbh->do(qq{
-        REPLACE INTO twitter_request_tokens
-        SET public=?, secret=?, issued=UNIX_TIMESTAMP()
-    }, undef, $token->{'public'}, $token->{'secret'});
-
-    LJ::MemCache::set(
-        'twitter_secret:' . $token->{'public'} => $token->{'secret'}
-    );
-}
-
-sub fetch_request_token {
-    my ($class, $public) = @_;
-
-    my $secret = LJ::MemCache::get("twitter_secret:$public");
-
-    unless ($secret) {
-        my $dbh = LJ::get_db_writer();
-        $dbh->{'RaiseError'} = 1;
-        ($secret) = $dbh->selectrow_array(qq{
-            SELECT secret FROM twitter_request_tokens WHERE public=?
-        }, undef, $public);
-
-        return unless $secret;
-
-        LJ::MemCache::set("twitter_secret:$public" => $secret);
-    }
-
-    return {
-        'public' => $public,
-        'secret' => $secret,
-    };
-}
-
-sub remove_request_token {
-    my ($class, $public) = @_;
-
-    my $dbh = LJ::get_db_writer();
-    $dbh->{'RaiseError'} = 1;
-    $dbh->do(qq{
-        DELETE FROM twitter_request_tokens WHERE public=?
-    }, undef, $public);
-
-    LJ::MemCache::delete("twitter_secret:$public");
-}
-
-sub clean_stale_request_tokens {
-    my ($class) = @_;
-
-    # we store them for a day
-    my $time_stale = time - 86400;
-
-    my $dbh = LJ::get_db_writer();
-    $dbh->{'RaiseError'} = 1;
-    $dbh->do(
-        'DELETE FROM twitter_request_tokens WHERE issued < ?',
-        undef, $time_stale
-    );
-}
-
-1;

Copied: trunk/cgi-bin/LJ/API/Twitter.pm (from rev 9318, branches/facebook-integration2/cgi-bin/LJ/API/Twitter.pm)
===================================================================
--- trunk/cgi-bin/LJ/API/Twitter.pm	                        (rev 0)
+++ trunk/cgi-bin/LJ/API/Twitter.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,272 @@
+package LJ::API::Twitter;
+use strict;
+
+use Net::OAuth;
+$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
+use Net::OAuth::RequestTokenRequest;
+use Net::OAuth::AccessTokenRequest;
+use Net::OAuth::ProtectedResourceRequest;
+use HTTP::Request::Common;
+use LWP::UserAgent;
+
+=head1 NAME
+
+LJ::API::Twitter - the module that handles communication between LJ
+and Twitter.
+
+=head1 SYNOPSIS
+
+ use LJ::API::Twitter;
+ 
+ # request an OAuth request token
+ my $req_token = LJ::API::Twitter->request_request_token;
+ 
+ # redirect to Twitter where one can confirm that they trust us
+ LJ::Request->redirect( 'https://api.twitter.com/oauth/authorize?' .
+                        'oauth_token=' . $req_token->{'public'} );
+ 
+ # once the Twitter redirects the user back, get back our token
+ # in full, along with the verifier
+ my $public = LJ::Request->get_param('oauth_token');
+ my $req_token = LJ::API::Twitter->fetch_request_token($public);
+ my $verifier = LJ::Request->get_param('oauth_verifier');
+ 
+ # get the OAuth access token from Twitter and save it
+ my $access_token = LJ::API::Twitter->request_access_token(
+    $req_token, $verifier);
+ $u->prop( 'twitter_access_token'        => $access_token->{'public'} );
+ $u->prop( 'twitter_access_token_secret' => $access_token->{'secret'} );
+ 
+ # use the token to call the API
+ LJ::API::Twitter->call(
+    'api_method'   => 'account/verify_credentials',
+    'access_token' => $access_token,
+ );
+ 
+ # alternatively, access token can be extracted from userprops
+ LJ::API::Twitter->call(
+    'api_method' => 'account/verify_credentials',
+    'user'       => $u,
+ );
+ 
+ # we can haz POST requests as well
+ LJ::API::Twitter->call(
+    'api_method' => 'statuses/update',
+    'user' => $u,
+    'http_method' => 'POST',
+    'params' => { 'status' => $status },
+ );
+ 
+ # the module saves request_token public/secret mapping
+ # in the DB, so every so often you may want to clear that
+ LJ::API::Twitter->remove_request_token($req_token);
+ LJ::API::Twitter->clean_stale_request_tokens;
+
+=head1 SEE ALSO
+
+Twitter API Documentation: http://dev.twitter.com/doc
+
+This module is primarily used for reposting, so
+LJ::Setting::TwitterConnect,
+LJ::Hooks::ThirdPartyNotify,
+LJ::Worker::Repost::EntryToTwitter.pm,
+LJ::Worker::Repost::CommentToTwitter.pm
+
+=cut
+
+sub generate_nonce {
+    return time . '/' . LJ::rand_chars(20);
+}
+
+sub default_request_params {
+    return (
+        consumer_key => $LJ::TWITTER_CONSUMER_KEY,
+        consumer_secret => $LJ::TWITTER_CONSUMER_SECRET,
+        request_method => 'POST',
+        signature_method => 'HMAC-SHA1',
+        timestamp => time,
+        nonce => generate_nonce,
+    );
+}
+
+sub request_request_token {
+    my ($class) = @_;
+
+    my $request = Net::OAuth::RequestTokenRequest->new(
+        $class->default_request_params,
+        request_url => 'https://api.twitter.com/oauth/request_token',
+        callback => "$LJ::SITEROOT/manage/settings/twitter.bml?act=connect",
+    );
+
+    $request->sign;
+
+    my $ua = LWP::UserAgent->new;
+    my $res = $ua->post($request->to_url);
+
+    unless ($res->is_success) {
+        die "twitter connectivity error";
+    }
+
+    my %params_returned;
+    LJ::decode_url_string($res->content, \%params_returned);
+
+    my $token = {
+        'public' => $params_returned{'oauth_token'},
+        'secret' => $params_returned{'oauth_token_secret'},
+    };
+
+    $class->store_request_token($token);
+
+    return $token;
+}
+
+sub request_access_token {
+    my ($class, $request_token, $verifier) = @_;
+
+    my $request = Net::OAuth::AccessTokenRequest->new(
+        $class->default_request_params,
+        token => $request_token->{'public'},
+        token_secret => $request_token->{'secret'},
+        verifier => $verifier,
+        request_url => 'https://api.twitter.com/oauth/access_token',
+        callback => "$LJ::SITEROOT/manage/settings/twitter.bml?act=connect",
+    );
+
+    $request->sign;
+
+    my $ua = LWP::UserAgent->new;
+    my $res = $ua->post($request->to_url);
+
+    unless ($res->is_success) {
+        die "twitter connectivity error";
+    }
+
+    my %params_returned;
+    LJ::decode_url_string($res->content, \%params_returned);
+
+    my $token = {
+        'public' => $params_returned{'oauth_token'},
+        'secret' => $params_returned{'oauth_token_secret'},
+    };
+
+    return $token;
+}
+
+sub call {
+    my ($class, %opts) = @_;
+
+    my $api_method  = $opts{'api_method'};
+    die 'API method not provided' unless $api_method;
+
+    if (my $u = $opts{'user'}) {
+        $opts{'access_token'} = {
+            'public' => $u->prop('twitter_access_token'),
+            'secret' => $u->prop('twitter_access_token_secret'),
+        };
+    }
+
+    my $access_token = $opts{'access_token'};
+    die 'access token not provided' unless $access_token;
+
+    my $http_method = $opts{'http_method'} || 'GET';
+    die 'invalid HTTP method' unless $http_method =~ /^(?:GET|POST)$/;
+
+    my $params  = $opts{'params'}  || {};
+
+    my %params = $class->default_request_params;
+    $params{'request_method'} = $http_method;
+
+    my $request_url = 'https://api.twitter.com/1/' . $api_method . '.json';
+
+    my $request = Net::OAuth::ProtectedResourceRequest->new(
+        %params,
+        token => $access_token->{'public'},
+        token_secret => $access_token->{'secret'},
+        request_url => $request_url,
+        extra_params => {
+            %$params,
+        },
+    );
+
+    $request->sign;
+
+    my $ua = LWP::UserAgent->new;
+    my $res;
+    if ($http_method eq 'GET') {
+        $res = $ua->get($request->to_url);
+    } else {
+        $res = $ua->post( $request_url, $request->to_hash );
+    }
+
+    unless ($res->is_success) {
+        die "twitter connectivity error";
+    }
+
+    return LJ::JSON->from_json($res->content);
+}
+
+sub store_request_token {
+    my ($class, $token) = @_;
+
+    my $dbh = LJ::get_db_writer();
+    $dbh->{'RaiseError'} = 1;
+    $dbh->do(qq{
+        REPLACE INTO twitter_request_tokens
+        SET public=?, secret=?, issued=UNIX_TIMESTAMP()
+    }, undef, $token->{'public'}, $token->{'secret'});
+
+    LJ::MemCache::set(
+        'twitter_secret:' . $token->{'public'} => $token->{'secret'}
+    );
+}
+
+sub fetch_request_token {
+    my ($class, $public) = @_;
+
+    my $secret = LJ::MemCache::get("twitter_secret:$public");
+
+    unless ($secret) {
+        my $dbh = LJ::get_db_writer();
+        $dbh->{'RaiseError'} = 1;
+        ($secret) = $dbh->selectrow_array(qq{
+            SELECT secret FROM twitter_request_tokens WHERE public=?
+        }, undef, $public);
+
+        return unless $secret;
+
+        LJ::MemCache::set("twitter_secret:$public" => $secret);
+    }
+
+    return {
+        'public' => $public,
+        'secret' => $secret,
+    };
+}
+
+sub remove_request_token {
+    my ($class, $public) = @_;
+
+    my $dbh = LJ::get_db_writer();
+    $dbh->{'RaiseError'} = 1;
+    $dbh->do(qq{
+        DELETE FROM twitter_request_tokens WHERE public=?
+    }, undef, $public);
+
+    LJ::MemCache::delete("twitter_secret:$public");
+}
+
+sub clean_stale_request_tokens {
+    my ($class) = @_;
+
+    # we store them for a day
+    my $time_stale = time - 86400;
+
+    my $dbh = LJ::get_db_writer();
+    $dbh->{'RaiseError'} = 1;
+    $dbh->do(
+        'DELETE FROM twitter_request_tokens WHERE issued < ?',
+        undef, $time_stale
+    );
+}
+
+1;

Copied: trunk/cgi-bin/LJ/Hooks/Cleanup.pm (from rev 9318, branches/facebook-integration2/cgi-bin/LJ/Hooks/Cleanup.pm)
===================================================================
--- trunk/cgi-bin/LJ/Hooks/Cleanup.pm	                        (rev 0)
+++ trunk/cgi-bin/LJ/Hooks/Cleanup.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,10 @@
+package LJ::Hooks::Cleanup;
+use strict;
+
+use LJ::API::Twitter;
+
+LJ::register_hook('extra_cache_clean', sub {
+    LJ::API::Twitter->clean_stale_request_tokens;
+});
+
+1;

Modified: trunk/cgi-bin/LJ/Hooks/Userinfo.pm
===================================================================
--- trunk/cgi-bin/LJ/Hooks/Userinfo.pm	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/cgi-bin/LJ/Hooks/Userinfo.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -411,26 +411,57 @@
     my $remote = $opts{remote};
     my $mangleaddress = $opts{mangleaddress} || sub { return $_[0]; };
 
-    return "" if $u->hide_ljtalk;
+    my $ret = '';
 
-    my $jab_title = LJ::run_hook('jabber_title');
-    my $jab_link = LJ::run_hook('jabber_link');
+    if ($u->is_person && $u->prop('facebook_access_token')) {
+        my $fb_name = LJ::ehtml($u->prop('facebook_name'));
+        my $fb_link = LJ::ehtml($u->prop('facebook_link'));
 
-    my $online = "";
-    if ($u->can_show_onlinestatus($remote)) {
-        if ($u->jabber_is_online) {
-            my $jabon = LJ::Lang::ml('ljcom.userinfo.im.jabber.status.on', { jabbertitle => $jab_title });
-            $online = "<img src='$LJ::IMGPREFIX/profile_icons/jabber.gif' alt=\"$jabon\" title=\"$jabon\" align='absmiddle' />";
-        } else {
-            my $jaboff = LJ::Lang::ml('ljcom.userinfo.im.jabber.status.off', { jabbertitle => $jab_title });
-            $online = "<img src='$LJ::IMGPREFIX/profile_icons/jabber-off.gif' alt=\"$jaboff\" title=\"$jaboff\" align='absmiddle' />";
+        $ret .= qq{
+            <tr class='im_lj'>
+                <td class='im_icon'>
+                    <img src='$LJ::IMGPREFIX/facebook-profile.gif' alt='' />
+                </td>
+                <td colspan="2"><a href="$fb_link">$fb_name</a></td>
+            </tr>
+        };
+    }
+
+    if ($u->prop('im_openid')) {
+        my $openid = LJ::ehtml($u->prop('im_openid'));
+
+        $ret .= qq{
+            <tr class='im_lj'>
+                <td class='im_icon'>
+                    <img src='$LJ::IMGPREFIX/openid-profile.gif' alt='' />
+                </td>
+                <td colspan="2">$openid</td>
+            </tr>
+        };
+    }
+
+    unless ($u->hide_ljtalk) {
+        my $jab_title = LJ::run_hook('jabber_title');
+        my $jab_link = LJ::run_hook('jabber_link');
+
+        my $online = "";
+        if ($u->can_show_onlinestatus($remote)) {
+            if ($u->jabber_is_online) {
+                my $jabon = LJ::Lang::ml('ljcom.userinfo.im.jabber.status.on', { jabbertitle => $jab_title });
+                $online = "<img src='$LJ::IMGPREFIX/profile_icons/jabber.gif' alt=\"$jabon\" title=\"$jabon\" align='absmiddle' />";
+            } else {
+                my $jaboff = LJ::Lang::ml('ljcom.userinfo.im.jabber.status.off', { jabbertitle => $jab_title });
+                $online = "<img src='$LJ::IMGPREFIX/profile_icons/jabber-off.gif' alt=\"$jaboff\" title=\"$jaboff\" align='absmiddle' />";
+            }
         }
+
+        my $ljtalk = $mangleaddress ? $mangleaddress->($u->ljtalk_id) : $u->ljtalk_id;
+        $ljtalk = "<tr class='im_lj'><td class='im_icon'><a href='$jab_link'><img src='$LJ::IMGPREFIX/profile_icons/ljtalk.gif' alt='$jab_title' title='$jab_title' /></a></td><td>$ljtalk</td><td class='im_status'>$online</td></tr>\n";
+
+        $ret .= $ljtalk;
     }
 
-    my $ljtalk = $mangleaddress ? $mangleaddress->($u->ljtalk_id) : $u->ljtalk_id;
-    $ljtalk = "<tr class='im_lj'><td class='im_icon'><a href='$jab_link'><img src='$LJ::IMGPREFIX/profile_icons/ljtalk.gif' alt='$jab_title' title='$jab_title' /></a></td><td>$ljtalk</td><td class='im_status'>$online</td></tr>\n";
-
-    return $ljtalk;
+    return $ret;
 });
 
 LJ::register_hook("userinfo_modify_view_links", sub {
@@ -448,4 +479,55 @@
     return;
 });
 
+LJ::register_hook('extra_im_services', sub {
+    my ($u) = @_;
+
+    my $ret = '';
+
+    my %ml = (
+        'facebook_field_name' =>
+            LJ::Lang::ml('/manage/profile/index.bml.chat.facebook'),
+
+        'facebook_none' =>
+            LJ::Lang::ml('/manage/profile/index.bml.chat.facebook.none'),
+
+        'facebook_change' =>
+            LJ::Lang::ml('/manage/profile/index.bml.chat.facebook.change'),
+
+        'openid_field_name' =>
+            LJ::Lang::ml('/manage/profile/index.bml.chat.openid'),
+    );
+
+    $ret .= "<tr><td class='field_name'>$ml{facebook_field_name}</td><td>";
+    if ($u->prop('facebook_access_token')) {
+        my $facebook_name = $u->prop('facebook_name');
+        $ret .= qq{
+            <img src="$LJ::IMGPREFIX/facebook-profile.gif">
+            <strong>$facebook_name</strong>
+        }
+    } else {
+        $ret .= "<em>($ml{facebook_none})</em> ";
+    }
+    $ret .= qq{<a href="$LJ::SITEROOT/manage/settings/?cat=privacy">$ml{facebook_change}</a>};
+    $ret .= "</td></tr>\n";
+
+    $ret .= "<tr><td class='field_name'>$ml{openid_field_name}</td><td>";
+    $ret .= LJ::html_text({
+        'name' => 'contact_openid',
+        'value' => $u->prop('im_openid'),
+        'size' => '20',
+        'maxlength' => 150,
+    });
+
+    $ret .= "</td></tr>\n";
+
+    return $ret;
+});
+
+LJ::register_hook('save_extra_profile_fields', sub {
+    my ($u) = @_;
+
+    $u->set_prop('im_openid' => LJ::Request->post_param('contact_openid'));
+});
+
 1;

Deleted: trunk/cgi-bin/LJ/Identity/Facebook.pm
===================================================================
--- branches/facebook-integration2/cgi-bin/LJ/Identity/Facebook.pm	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/cgi-bin/LJ/Identity/Facebook.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -1,185 +0,0 @@
-package LJ::Identity::Facebook;
-use strict;
-
-use base qw(LJ::Identity);
-
-use LJ::JSON;
-
-sub typeid { 'F' }
-sub pretty_type { 'Facebook' }
-sub short_code { 'facebook' }
-
-my @request_permissions = qw( publish_stream offline_access email
-                              user_about_me user_birthday user_hometown
-                              user_interests user_website );
-
-sub attempt_login {
-    my ($class, $errs, %opts) = @_;
-
-    my $returl = $opts{'returl'} || $LJ::SITEROOT;
-    my $returl_fail = $opts{'returl_fail'} || $returl || $LJ::SITEROOT;
-
-    my $callback_url = "$LJ::SITEROOT/identity/callback-facebook.bml?" .
-                       'ret=' . LJ::Text->eurl($returl) . '&' .
-                       'ret_fail=' . LJ::Text->eurl($returl_fail);
-
-    my $addr = 'https://graph.facebook.com/oauth/authorize?' .
-               "client_id=$LJ::FACEBOOK_CONNECT_CLIENT_ID&" .
-               'redirect_uri=' . LJ::Text->eurl($callback_url) . '&' .
-               'scope=' . join(',', @request_permissions);
-
-    return LJ::Request->redirect($addr);
-}
-
-sub parse_city {
-    my ($self, $city) = @_;
-
-    my $ret = {
-        'city' => $city,
-        'state' => '',
-        'country' => '',
-    };
-
-    my @city_split = split /,\s+/, $city;
-
-    return $ret if scalar(@city_split) == 1;
-
-    my $last_part = pop @city_split;
-    my $head      = join(', ', @city_split);
-
-    if (my $country = $LJ::FACEBOOK_COUNTRIES_MAP->{$last_part}) {
-        $ret->{'city'} = $head;
-        $ret->{'country'} = $country;
-    } elsif (my $state = $LJ::FACEBOOK_US_STATES_MAP->{$last_part}) {
-        $ret->{'city'} = $head;
-        $ret->{'state'} = $state;
-        $ret->{'country'} = 'US';
-    }
-
-    return $ret;
-}
-
-sub initialize_user {
-    my ($self, $u, $extra) = @_;
-
-    my $ua = LWP::UserAgent->new;
-
-    my $token = $extra->{'token'};
-    die "no access token passed" unless $token;
-
-    my $userdata = $extra->{'userdata'};
-    unless ($userdata) {
-        my $res = $ua->get('https://graph.facebook.com/me/?' .
-                           'locale=en_US&' .
-                           'access_token=' . $token);
-
-        $userdata = LJ::JSON->from_json($res->content);
-    }
-
-    ## INITIALIZE USER PROFILE
-
-    # these are pretty much essential and cannot be changed by the
-    # user: the access token we're using to communicate on the user's
-    # behalf, their name, and the link to their profile
-    $u->set_prop('facebook_access_token' => $token);
-    $u->set_prop('facebook_name'         => $userdata->{'name'});
-    $u->set_prop('facebook_link'         => $userdata->{'link'});
-
-    # gender
-    my %gender_map = (
-        'male'   => 'M',
-        'female' => 'F',
-    );
-    # if they didn't specify a gender on fb, default to unspecified
-    $u->set_prop('gender' => $gender_map{$userdata->{'gender'}} || 'U');
-
-    # hometown
-    my $city = $userdata->{'hometown'} ? $userdata->{'hometown'}{'name'} : '';
-    if ($city) {
-        my $city_parsed = $self->parse_city($city);
-
-        $u->set_prop( 'city'    => $city_parsed->{'city'} );
-        $u->set_prop( 'state'   => $city_parsed->{'state'} );
-        $u->set_prop( 'country' => $city_parsed->{'country'} );
-    }
-
-    # birthday
-    if (my $birthday = $userdata->{'birthday'}) {
-        my ($month, $day, $year) = ($birthday =~ /(\d+)\/(\d+)\/(\d+)/);
-        LJ::update_user($u, { 'bdate' => "$year-$month-$day" });
-
-        # if we've got their birthday, let's display it in full
-        $u->set_prop( 'opt_showbday' => 'F' );
-    }
-
-    # the name specified in profile; this can actually be changed
-    # by the user later in the profile settings
-    LJ::update_user($u, { 'name' => $userdata->{'name'}});
-
-    # bio
-    $u->set_bio($userdata->{'bio'});
-
-    # userpic
-    my $upidata = $ua->get('https://graph.facebook.com/me/picture?' .
-                           'type=square&' .
-                           'access_token=' . $token)->content;
-
-    my $userpic = eval { LJ::Userpic->create($u, data => \$upidata) };
-    $userpic->make_default if $userpic;
-
-    # interests
-    my $interests = $ua->get('https://graph.facebook.com/me/interests?' .
-                             'locale=en_US&' .
-                             'access_token=' . $token)->content;
-
-    $interests = eval { LJ::JSON->from_json($interests) };
-    if ($interests && $interests->{'data'}) {
-        my @interests = map { $_->{'name'} } @{$interests->{'data'}};
-        $u->set_interests($u->interests, \@interests);
-    }
-
-    # email
-    $u->set_email($userdata->{'email'});
-    # we don't care, set it as validated as well:
-    LJ::update_user($u, { 'status' => 'A' });
-
-    # website
-    $u->set_prop('url' => $userdata->{'website'});
-
-    # facebook identities are plus accounts by default
-    $u->add_to_class('plus');
-
-    # until they get notifications set, they don't get anything
-    LJ::update_user($u, { 'opt_gettalkemail' => 'N' });
-}
-
-sub url {
-    my ($self, $u) = @_;
-
-    # the 'url' prop should have been set by the initialization code;
-    # failing that, let's not re-fetch the website from there, and let's
-    # link to their facebook profile instead.
-    return $u->prop('facebook_link');
-}
-
-sub display_name {
-    my ($self, $u) = @_;
-    return $u->prop('facebook_name') || $u->username;
-}
-
-sub ljuser_display_params {
-    my ($self, $u, $opts) = @_;
-
-    return {
-        'journal_url'  => $u->prop('facebook_link') || 'about:blank',
-        'journal_name' => $u->display_name,
-        'userhead'     => 'facebook-profile.gif',
-        'userhead_w'   => 16,
-    };
-}
-
-sub profile_window_title {
-    return LJ::Lang::ml('/userinfo.bml.title.facebookprofile');
-}
-
-1;

Copied: trunk/cgi-bin/LJ/Identity/Facebook.pm (from rev 9318, branches/facebook-integration2/cgi-bin/LJ/Identity/Facebook.pm)
===================================================================
--- trunk/cgi-bin/LJ/Identity/Facebook.pm	                        (rev 0)
+++ trunk/cgi-bin/LJ/Identity/Facebook.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,185 @@
+package LJ::Identity::Facebook;
+use strict;
+
+use base qw(LJ::Identity);
+
+use LJ::JSON;
+
+sub typeid { 'F' }
+sub pretty_type { 'Facebook' }
+sub short_code { 'facebook' }
+
+my @request_permissions = qw( publish_stream offline_access email
+                              user_about_me user_birthday user_hometown
+                              user_interests user_website );
+
+sub attempt_login {
+    my ($class, $errs, %opts) = @_;
+
+    my $returl = $opts{'returl'} || $LJ::SITEROOT;
+    my $returl_fail = $opts{'returl_fail'} || $returl || $LJ::SITEROOT;
+
+    my $callback_url = "$LJ::SITEROOT/identity/callback-facebook.bml?" .
+                       'ret=' . LJ::Text->eurl($returl) . '&' .
+                       'ret_fail=' . LJ::Text->eurl($returl_fail);
+
+    my $addr = 'https://graph.facebook.com/oauth/authorize?' .
+               "client_id=$LJ::FACEBOOK_CONNECT_CLIENT_ID&" .
+               'redirect_uri=' . LJ::Text->eurl($callback_url) . '&' .
+               'scope=' . join(',', @request_permissions);
+
+    return LJ::Request->redirect($addr);
+}
+
+sub parse_city {
+    my ($self, $city) = @_;
+
+    my $ret = {
+        'city' => $city,
+        'state' => '',
+        'country' => '',
+    };
+
+    my @city_split = split /,\s+/, $city;
+
+    return $ret if scalar(@city_split) == 1;
+
+    my $last_part = pop @city_split;
+    my $head      = join(', ', @city_split);
+
+    if (my $country = $LJ::FACEBOOK_COUNTRIES_MAP->{$last_part}) {
+        $ret->{'city'} = $head;
+        $ret->{'country'} = $country;
+    } elsif (my $state = $LJ::FACEBOOK_US_STATES_MAP->{$last_part}) {
+        $ret->{'city'} = $head;
+        $ret->{'state'} = $state;
+        $ret->{'country'} = 'US';
+    }
+
+    return $ret;
+}
+
+sub initialize_user {
+    my ($self, $u, $extra) = @_;
+
+    my $ua = LWP::UserAgent->new;
+
+    my $token = $extra->{'token'};
+    die "no access token passed" unless $token;
+
+    my $userdata = $extra->{'userdata'};
+    unless ($userdata) {
+        my $res = $ua->get('https://graph.facebook.com/me/?' .
+                           'locale=en_US&' .
+                           'access_token=' . $token);
+
+        $userdata = LJ::JSON->from_json($res->content);
+    }
+
+    ## INITIALIZE USER PROFILE
+
+    # these are pretty much essential and cannot be changed by the
+    # user: the access token we're using to communicate on the user's
+    # behalf, their name, and the link to their profile
+    $u->set_prop('facebook_access_token' => $token);
+    $u->set_prop('facebook_name'         => $userdata->{'name'});
+    $u->set_prop('facebook_link'         => $userdata->{'link'});
+
+    # gender
+    my %gender_map = (
+        'male'   => 'M',
+        'female' => 'F',
+    );
+    # if they didn't specify a gender on fb, default to unspecified
+    $u->set_prop('gender' => $gender_map{$userdata->{'gender'}} || 'U');
+
+    # hometown
+    my $city = $userdata->{'hometown'} ? $userdata->{'hometown'}{'name'} : '';
+    if ($city) {
+        my $city_parsed = $self->parse_city($city);
+
+        $u->set_prop( 'city'    => $city_parsed->{'city'} );
+        $u->set_prop( 'state'   => $city_parsed->{'state'} );
+        $u->set_prop( 'country' => $city_parsed->{'country'} );
+    }
+
+    # birthday
+    if (my $birthday = $userdata->{'birthday'}) {
+        my ($month, $day, $year) = ($birthday =~ /(\d+)\/(\d+)\/(\d+)/);
+        LJ::update_user($u, { 'bdate' => "$year-$month-$day" });
+
+        # if we've got their birthday, let's display it in full
+        $u->set_prop( 'opt_showbday' => 'F' );
+    }
+
+    # the name specified in profile; this can actually be changed
+    # by the user later in the profile settings
+    LJ::update_user($u, { 'name' => $userdata->{'name'}});
+
+    # bio
+    $u->set_bio($userdata->{'bio'});
+
+    # userpic
+    my $upidata = $ua->get('https://graph.facebook.com/me/picture?' .
+                           'type=square&' .
+                           'access_token=' . $token)->content;
+
+    my $userpic = eval { LJ::Userpic->create($u, data => \$upidata) };
+    $userpic->make_default if $userpic;
+
+    # interests
+    my $interests = $ua->get('https://graph.facebook.com/me/interests?' .
+                             'locale=en_US&' .
+                             'access_token=' . $token)->content;
+
+    $interests = eval { LJ::JSON->from_json($interests) };
+    if ($interests && $interests->{'data'}) {
+        my @interests = map { $_->{'name'} } @{$interests->{'data'}};
+        $u->set_interests($u->interests, \@interests);
+    }
+
+    # email
+    $u->set_email($userdata->{'email'});
+    # we don't care, set it as validated as well:
+    LJ::update_user($u, { 'status' => 'A' });
+
+    # website
+    $u->set_prop('url' => $userdata->{'website'});
+
+    # facebook identities are plus accounts by default
+    $u->add_to_class('plus');
+
+    # until they get notifications set, they don't get anything
+    LJ::update_user($u, { 'opt_gettalkemail' => 'N' });
+}
+
+sub url {
+    my ($self, $u) = @_;
+
+    # the 'url' prop should have been set by the initialization code;
+    # failing that, let's not re-fetch the website from there, and let's
+    # link to their facebook profile instead.
+    return $u->prop('facebook_link');
+}
+
+sub display_name {
+    my ($self, $u) = @_;
+    return $u->prop('facebook_name') || $u->username;
+}
+
+sub ljuser_display_params {
+    my ($self, $u, $opts) = @_;
+
+    return {
+        'journal_url'  => $u->prop('facebook_link') || 'about:blank',
+        'journal_name' => $u->display_name,
+        'userhead'     => 'facebook-profile.gif',
+        'userhead_w'   => 16,
+    };
+}
+
+sub profile_window_title {
+    return LJ::Lang::ml('/userinfo.bml.title.facebookprofile');
+}
+
+1;

Modified: trunk/cgi-bin/LJ/Setting/Display/AccountLevel.pm
===================================================================
--- trunk/cgi-bin/LJ/Setting/Display/AccountLevel.pm	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/cgi-bin/LJ/Setting/Display/AccountLevel.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -12,13 +12,18 @@
 sub helpurl {
     my ($class, $u) = @_;
 
-    return "account_levels";
+    return 'identity_convert' if $u->is_identity;
+    return 'account_levels';
 }
 
 sub actionlink {
     my ($class, $u) = @_;
 
-    return "" if $u->is_identity;
+    if ($u->is_identity) {
+        my $text =
+            LJ::Lang::ml('setting.display.accountlevel.actionlink.identity_convert');
+        return "<a href='$LJ::SSLROOT/identity/convert.bml'>$text</a>";
+    }
 
     my $paid_url = $u->is_community ? "$LJ::SITEROOT/community/account.bml?authas=" . $u->user : "$LJ::SITEROOT/manage/account/";
     my $plus_url = $u->is_community ? "$LJ::SITEROOT/community/account.bml?authas=" . $u->user : "$LJ::SITEROOT/manage/account/?changelevel=1";

Copied: trunk/cgi-bin/LJ/Setting/TwitterConnect.pm (from rev 9318, branches/facebook-integration2/cgi-bin/LJ/Setting/TwitterConnect.pm)
===================================================================
--- trunk/cgi-bin/LJ/Setting/TwitterConnect.pm	                        (rev 0)
+++ trunk/cgi-bin/LJ/Setting/TwitterConnect.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,100 @@
+package LJ::Setting::TwitterConnect;
+use base 'LJ::Setting';
+use strict;
+use warnings;
+
+use LJ::API::Twitter;
+
+sub should_render {
+    my ( $class, $u ) = @_;
+
+    return $u
+        && LJ::is_enabled( "twitter_connect", $u )
+        && ( $u->is_personal || $u->is_identity ) ? 1 : 0;
+}
+
+sub helpurl {
+    my ( $class, $u ) = @_;
+
+    return "twitter_connect";
+}
+
+sub label {
+    my ( $class, $u ) = @_;
+
+    return $class->ml('setting.twitterconnect.label');
+}
+
+sub option {
+    my ( $class, $u, $errs, $args ) = @_;
+    my $key = $class->pkgkey;
+
+    my $connected = $u->prop('twitter_access_token') ? 1 : 0;
+
+    my $repost_entries = $class->get_arg( $args, 'repost_entries' )
+        || $u->third_party_notify_list_contains('twitter');
+    my $repost_comments = $class->get_arg( $args, 'repost_comments' )
+        || $u->third_party_notify_list_contains('twitter-comments');
+
+    my $disconnect_link = "$LJ::SITEROOT/manage/settings/twitter.bml?"
+        . 'act=disconnect&'
+        . 'auth_token='
+        . LJ::eurl(
+            LJ::Auth->ajax_auth_token(
+                $u,
+                "/manage/settings/twitter.bml",
+                'act' => 'disconnect',
+            )
+        );
+
+    my $template = LJ::HTML::Template->new(
+        { use_expr => 1 },    # force HTML::Template::Pro with Expr support
+        filename => "$ENV{'LJHOME'}/templates/Settings/TwitterConnect.tmpl",
+        )
+        or die "Can't open template: $!";
+
+    $template->param(
+        'form_field_prefix' => $key,
+        'connected'         => $connected,
+        'disconnect_link'   => $disconnect_link,
+        'twitter_name'      => $u->prop('twitter_name'),
+        'twitter_link'      => $u->prop('twitter_link'),
+        'repost_entries'    => $repost_entries,
+        'repost_comments'   => $repost_comments,
+        'is_identity'       => $u->is_identity,
+    );
+
+    return $template->output;
+}
+
+sub save {
+    my ( $class, $u, $args ) = @_;
+
+    if ( $class->get_arg( $args, "connect" ) ) {
+        my $token = LJ::API::Twitter->request_request_token;
+        my $addr  = 'https://api.twitter.com/oauth/authorize?' .
+                    'oauth_token=' . $token->{'public'};
+
+        return LJ::Request->redirect($addr);
+    }
+
+    if ( $u->is_personal ) {
+        if ( $class->get_arg( $args, 'repost_entries' ) ) {
+            $u->third_party_notify_list_add('twitter');
+        }
+        else {
+            $u->third_party_notify_list_remove('twitter');
+        }
+    }
+
+    if ( $class->get_arg( $args, 'repost_comments' ) ) {
+        $u->third_party_notify_list_add('twitter-comments');
+    }
+    else {
+        $u->third_party_notify_list_remove('twitter-comments');
+    }
+
+    return 1;
+}
+
+1;

Deleted: trunk/cgi-bin/LJ/Talk/Author/Facebook.pm
===================================================================
--- branches/facebook-integration2/cgi-bin/LJ/Talk/Author/Facebook.pm	2010-08-09 03:41:05 UTC (rev 9318)
+++ trunk/cgi-bin/LJ/Talk/Author/Facebook.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -1,93 +0,0 @@
-package LJ::Talk::Author::Facebook;
-use strict;
-
-use base qw(LJ::Talk::Author);
-
-sub display_params {
-    my ($class, $opts) = @_;
-
-    my $remote = LJ::get_remote();
-    my $form = $opts->{'form'};
-    my $is_identity =    $remote
-                      && $remote->is_identity
-                      && $remote->identity->short_code eq 'facebook';
-
-    my $is_trusted_identity = $is_identity && $remote->is_trusted_identity;
-
-    my %whocheck = (
-        'facebook'        => $form->{'usertype'} eq 'facebook',
-        'facebook_cookie' => $form->{'usertype'} eq 'facebook_cookie'
-                                    || $is_identity,
-    );
-
-    return {
-        'is_identity'              => $is_identity,
-        'is_trusted_identity'      => $is_trusted_identity,
-
-        'whocheck_facebook'        => $whocheck{'facebook'},
-        'whocheck_facebook_cookie' => $whocheck{'facebook_cookie'},
-
-        'helpicon_facebook'        => LJ::help_icon_html( "facebook", " " ),
-    };
-}
-
-sub want_user_input {
-    my ($class, $usertype) = @_;
-    return $usertype =~ /^(?:facebook|facebook_cookie)$/;
-}
-
-sub handle_user_input {
-    my ($class, $form, $remote, $need_captcha, $errret, $init) = @_;
-
-    return if @$errret;
-
-    my $journalu = $init->{'journalu'};
-    my $up; # user posting
-
-    my $remote_is_facebook = $remote &&
-                             $remote->is_identity &&
-                             $remote->identity->short_code eq 'facebook';
-
-    if ($remote_is_facebook) {
-        return LJ::get_remote();
-    }
-
-    # Store the entry
-    my $pendcid = LJ::alloc_user_counter($journalu, "C");
-
-    unless ($pendcid) {
-        push @$errret, "Unable to allocate pending id";
-        return;
-    }
-
-    # Since these were gotten from the openid:url and won't
-    # persist in the form data
-    my $penddata = Storable::nfreeze($form);
-
-    unless ($journalu->writer) {
-        push @$errret,
-            "Unable to get database handle to store pending comment";
-        return;
-    }
-
-    $journalu->do(qq{
-        INSERT INTO pendcomments
-        SET jid = ?, pendcid = ?, data = ?, datesubmit = UNIX_TIMESTAMP()
-    }, undef, $journalu->id, $pendcid, $penddata);
-
-    if ($journalu->err) {
-        push @$errret, $journalu->errstr;
-        return;
-    }
-
-    my $returl = "$LJ::SITEROOT/talkpost_do.bml?" .
-                 'jid=' . $journalu->id . '&' .
-                 "pendcid=$pendcid";
-
-    LJ::Identity::Facebook->attempt_login($errret,
-        'returl'      => $returl,
-        'returl_fail' => $returl . '&failed=1',
-    );
-}
-
-1;

Copied: trunk/cgi-bin/LJ/Talk/Author/Facebook.pm (from rev 9318, branches/facebook-integration2/cgi-bin/LJ/Talk/Author/Facebook.pm)
===================================================================
--- trunk/cgi-bin/LJ/Talk/Author/Facebook.pm	                        (rev 0)
+++ trunk/cgi-bin/LJ/Talk/Author/Facebook.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,93 @@
+package LJ::Talk::Author::Facebook;
+use strict;
+
+use base qw(LJ::Talk::Author);
+
+sub display_params {
+    my ($class, $opts) = @_;
+
+    my $remote = LJ::get_remote();
+    my $form = $opts->{'form'};
+    my $is_identity =    $remote
+                      && $remote->is_identity
+                      && $remote->identity->short_code eq 'facebook';
+
+    my $is_trusted_identity = $is_identity && $remote->is_trusted_identity;
+
+    my %whocheck = (
+        'facebook'        => $form->{'usertype'} eq 'facebook',
+        'facebook_cookie' => $form->{'usertype'} eq 'facebook_cookie'
+                                    || $is_identity,
+    );
+
+    return {
+        'is_identity'              => $is_identity,
+        'is_trusted_identity'      => $is_trusted_identity,
+
+        'whocheck_facebook'        => $whocheck{'facebook'},
+        'whocheck_facebook_cookie' => $whocheck{'facebook_cookie'},
+
+        'helpicon_facebook'        => LJ::help_icon_html( "facebook", " " ),
+    };
+}
+
+sub want_user_input {
+    my ($class, $usertype) = @_;
+    return $usertype =~ /^(?:facebook|facebook_cookie)$/;
+}
+
+sub handle_user_input {
+    my ($class, $form, $remote, $need_captcha, $errret, $init) = @_;
+
+    return if @$errret;
+
+    my $journalu = $init->{'journalu'};
+    my $up; # user posting
+
+    my $remote_is_facebook = $remote &&
+                             $remote->is_identity &&
+                             $remote->identity->short_code eq 'facebook';
+
+    if ($remote_is_facebook) {
+        return LJ::get_remote();
+    }
+
+    # Store the entry
+    my $pendcid = LJ::alloc_user_counter($journalu, "C");
+
+    unless ($pendcid) {
+        push @$errret, "Unable to allocate pending id";
+        return;
+    }
+
+    # Since these were gotten from the openid:url and won't
+    # persist in the form data
+    my $penddata = Storable::nfreeze($form);
+
+    unless ($journalu->writer) {
+        push @$errret,
+            "Unable to get database handle to store pending comment";
+        return;
+    }
+
+    $journalu->do(qq{
+        INSERT INTO pendcomments
+        SET jid = ?, pendcid = ?, data = ?, datesubmit = UNIX_TIMESTAMP()
+    }, undef, $journalu->id, $pendcid, $penddata);
+
+    if ($journalu->err) {
+        push @$errret, $journalu->errstr;
+        return;
+    }
+
+    my $returl = "$LJ::SITEROOT/talkpost_do.bml?" .
+                 'jid=' . $journalu->id . '&' .
+                 "pendcid=$pendcid";
+
+    LJ::Identity::Facebook->attempt_login($errret,
+        'returl'      => $returl,
+        'returl_fail' => $returl . '&failed=1',
+    );
+}
+
+1;

Copied: trunk/cgi-bin/LJ/Widget/ConvertIdentityAccount.pm (from rev 9318, branches/facebook-integration2/cgi-bin/LJ/Widget/ConvertIdentityAccount.pm)
===================================================================
--- trunk/cgi-bin/LJ/Widget/ConvertIdentityAccount.pm	                        (rev 0)
+++ trunk/cgi-bin/LJ/Widget/ConvertIdentityAccount.pm	2010-08-09 04:17:05 UTC (rev 9319)
@@ -0,0 +1,225 @@
+package LJ::Widget::ConvertIdentityAccount;
+use strict;
+
+use base qw(LJ::Widget::CreateAccount);
+
+sub need_res { return qw(); }
+
+sub render_body {
+    my ($class, %opts) = @_;
+
+    my $post = $opts{post};
+    my $get = $opts{get};
+    my $from_post = $opts{from_post};
+    my $errors = $from_post->{errors};
+    my $remote = LJ::get_remote();
+
+    my $template = LJ::HTML::Template->new(
+        { use_expr => 1 }, # force HTML::Template::Pro with Expr support
+        filename => "$ENV{'LJHOME'}/templates/Identity/Convert.tmpl",
+    ) or die "Can't open template: $!";
+
+    $template->param(
+        'form_intro' => LJ::form_auth(),
+        'input_prefix' => $class->input_prefix,
+    );
+
+    $template->param( "form_$_" => LJ::ehtml($post->{$_} || $get->{$_}) )
+        foreach qw( user password1 password2 );
+
+    my %bdpart;
+    if ($remote->{'bdate'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/) {
+        ($bdpart{'year'}, $bdpart{'month'}, $bdpart{'day'}) = ($1, $2, $3);
+        if ($bdpart{'year'} eq "0000") { $bdpart{'year'} = ""; }
+        if ($bdpart{'day'} eq "00") { $bdpart{'day'} = ""; }
+    }
+
+    $template->param(
+        'form_email'     => LJ::ehtml( $post->{'email'}
+                                    || $get->{'email'}
+                                    || $remote->email_raw ),
+        'form_bday_dd'   => LJ::ehtml( $post->{'bday_dd'}
+                                    || $get->{'bday_dd'}
+                                    || $bdpart{'day'} ),
+        'form_bday_yyyy' => LJ::ehtml( $post->{'bday_yyy'}
+                                    || $get->{'bday_yyy'}
+                                    || $bdpart{'year'} ),
+    );
+
+    my $selected_gender    = $post->{'gender'}  || $get->{'gender'}
+                          || $remote->prop('gender') || 'U';
+
+    my $selected_bday_mm   = $post->{'bday_mm'} || $get->{'bday_mm'}
+                          || $bdpart{'month'};
+
+    my $selected_sharebday = $post->{'sharebday'} || $get->{'sharebday'}
+                          || $remote->prop('opt_sharebday') || 'A';
+
+    my @bday_month_options;
+    foreach my $mon (1..12) {
+        push @bday_month_options, {
+            'value' => $mon,
+            'name' => LJ::Lang::ml(LJ::Lang::mon...
 (truncated)
Tags: andy, bml, conf, dat, gif, ljcom, local, look, pl, pm, text, tmpl
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 0 comments