From 17ae690d5937b9a9ac3c7cd8a37d461960ce4964 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 9 Nov 2010 23:14:50 +0000 Subject: [PATCH] Make a richer StatusNet profile from a user's Facebook profile --- plugins/FacebookSSO/FacebookSSOPlugin.php | 102 +++++++++--------- .../actions/facebookfinishlogin.php | 80 ++++++++++++-- plugins/FacebookSSO/lib/facebookclient.php | 4 +- 3 files changed, 124 insertions(+), 62 deletions(-) diff --git a/plugins/FacebookSSO/FacebookSSOPlugin.php b/plugins/FacebookSSO/FacebookSSOPlugin.php index a094f2957f..32b6a29106 100644 --- a/plugins/FacebookSSO/FacebookSSOPlugin.php +++ b/plugins/FacebookSSO/FacebookSSOPlugin.php @@ -3,7 +3,8 @@ * StatusNet - the distributed open-source microblogging tool * Copyright (C) 2010, StatusNet, Inc. * - * A plugin for single-sign-in (SSO) with Facebook + * A plugin for integrating Facebook with StatusNet. Includes single-sign-on + * and publishing notices to Facebook using Facebook's Graph API. * * PHP version 5 * @@ -35,14 +36,7 @@ if (!defined('STATUSNET')) { define("FACEBOOK_SERVICE", 2); /** - * Main class for Facebook single-sign-on plugin - * - * - * Simple plugins can be implemented as a single module. Others are more complex - * and require additional modules; these should use their own directory, like - * 'local/plugins/{$name}/'. All files related to the plugin, including images, - * JavaScript, CSS, external libraries or PHP modules should go in the plugin - * directory. + * Main class for Facebook plugin * * @category Plugin * @package StatusNet @@ -62,8 +56,7 @@ class FacebookSSOPlugin extends Plugin /** * Initializer for this plugin * - * Plugins overload this method to do any initialization they need, - * like connecting to remote servers or creating paths or so on. + * Gets an instance of the Facebook API client object * * @return boolean hook value; true means continue processing, false means stop. */ @@ -78,33 +71,9 @@ class FacebookSSOPlugin extends Plugin return true; } - /** - * Cleanup for this plugin - * - * Plugins overload this method to do any cleanup they need, - * like disconnecting from remote servers or deleting temp files or so on. - * - * @return boolean hook value; true means continue processing, false means stop. - */ - function cleanup() - { - return true; - } - /** * Load related modules when needed * - * Most non-trivial plugins will require extra modules to do their work. Typically - * these include data classes, action classes, widget classes, or external libraries. - * - * This method receives a class name and loads the PHP file related to that class. By - * tradition, action classes typically have files named for the action, all lower-case. - * Data classes are in files with the data class name, initial letter capitalized. - * - * Note that this method will be called for *all* overloaded classes, not just ones - * in this plugin! So, make sure to return true by default to let other plugins, and - * the core code, get a chance. - * * @param string $cls Name of the class to be loaded * * @return boolean hook value; true means continue processing, false means stop. @@ -118,12 +87,9 @@ class FacebookSSOPlugin extends Plugin switch ($cls) { - case 'Facebook': // New JavaScript SDK + case 'Facebook': // Facebook PHP SDK include_once $dir . '/extlib/facebook.php'; return false; - case 'FacebookRestClient': // Old REST lib - include_once $dir . '/extlib/facebookapi_php5_restlib.php'; - return false; case 'FacebookloginAction': case 'FacebookfinishloginAction': case 'FacebookadminpanelAction': @@ -153,10 +119,8 @@ class FacebookSSOPlugin extends Plugin ); if (in_array(get_class($action), $needy)) { - common_debug("needs scripts!"); return true; } else { - common_debug("doesn't need scripts!"); return false; } } @@ -164,11 +128,6 @@ class FacebookSSOPlugin extends Plugin /** * Map URLs to actions * - * This event handler lets the plugin map URLs on the site to actions (and - * thus an action handler class). Note that the action handler class for an - * action will be named 'FoobarAction', where action = 'foobar'. The class - * must be loaded in the onAutoload() method. - * * @param Net_URL_Mapper $m path-to-action mapper * * @return boolean hook value; true means continue processing, false means stop. @@ -281,6 +240,11 @@ class FacebookSSOPlugin extends Plugin /* * Is there a Facebook application for the plugin to use? + * + * Checks to see if a Facebook application ID and secret + * have been configured and a valid Facebook API client + * object exists. + * */ function hasApplication() { @@ -298,6 +262,12 @@ class FacebookSSOPlugin extends Plugin return false; } + /* + * Output a Facebook div for the Facebook JavaSsript SDK to use + * + * @param Action $action the current action + * + */ function onStartShowHeader($action) { // output
as close to as possible @@ -305,6 +275,12 @@ class FacebookSSOPlugin extends Plugin return true; } + /* + * Load the Facebook JavaScript SDK on pages that need them. + * + * @param Action $action the current action + * + */ function onEndShowScripts($action) { if ($this->needsScripts($action)) { @@ -324,7 +300,7 @@ $('#facebook_button').bind('click', function(event) { } else { // NOP (user cancelled login) } - }, {perms:'read_stream,publish_stream,offline_access,user_status,user_location,user_website'}); + }, {perms:'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email'}); }); ENDOFSCRIPT; @@ -341,7 +317,7 @@ ENDOFSCRIPT; /* * Log the user out of Facebook, per the Facebook authentication guide * - * @param Action action the action + * @param Action action the current action */ function onEndLogout($action) { @@ -381,10 +357,10 @@ ENDOFSCRIPT; } /* - * Add fbml namespace so Facebook's JavaScript SDK can parse and render - * XFBML tags (e.g: ) + * Add fbml namespace to our HTML, so Facebook's JavaScript SDK can parse + * and render XFBML tags * - * @param Action $action current action + * @param Action $action the current action * @param array $attrs array of attributes for the HTML tag * * @return nothing @@ -432,6 +408,30 @@ ENDOFSCRIPT; return true; } + /* + * Use SSL for Facebook stuff + * + * @param string $action name + * @param boolean $ssl outval to force SSL + * @return mixed hook return value + */ + function onSensitiveAction($action, &$ssl) + { + $sensitive = array( + 'facebookadminpanel', + 'facebooksettings', + 'facebooklogin', + 'facebookfinishlogin' + ); + + if (in_array($action, $sensitive)) { + $ssl = true; + return false; + } else { + return true; + } + } + /* * Add version info for this plugin * diff --git a/plugins/FacebookSSO/actions/facebookfinishlogin.php b/plugins/FacebookSSO/actions/facebookfinishlogin.php index 16f7cff500..e61f351547 100644 --- a/plugins/FacebookSSO/actions/facebookfinishlogin.php +++ b/plugins/FacebookSSO/actions/facebookfinishlogin.php @@ -33,7 +33,6 @@ if (!defined('STATUSNET')) { class FacebookfinishloginAction extends Action { - private $facebook = null; // Facebook client private $fbuid = null; // Facebook user ID private $fbuser = null; // Facebook user object (JSON) @@ -341,12 +340,14 @@ class FacebookfinishloginAction extends Action } $args = array( - 'nickname' => $nickname, - 'fullname' => $this->fbuser['firstname'] . ' ' . $this->fbuser['lastname'], - // XXX: Figure out how to get email - 'homepage' => $this->fbuser['link'], - 'bio' => $this->fbuser['about'], - 'location' => $this->fbuser['location']['name'] + 'nickname' => $nickname, + 'fullname' => $this->fbuser['first_name'] + . ' ' . $this->fbuser['last_name'], + 'email' => $this->fbuser['email'], + 'email_confirmed' => true, + 'homepage' => $this->fbuser['website'], + 'bio' => $this->fbuser['about'], + 'location' => $this->fbuser['location']['name'] ); if (!empty($invite)) { @@ -362,6 +363,8 @@ class FacebookfinishloginAction extends Action return; } + $this->setAvatar($user); + common_set_user($user); common_real_login(true); @@ -384,6 +387,68 @@ class FacebookfinishloginAction extends Action ); } + /* + * Attempt to download the user's Facebook picture and create a + * StatusNet avatar for the new user. + */ + function setAvatar($user) + { + $picUrl = sprintf( + 'http://graph.facebook.com/%s/picture?type=large', + $this->fbuid + ); + + // fetch the picture from Facebook + $client = new HTTPClient(); + + common_debug("status = $status - " . $finalUrl , __FILE__); + + // fetch the actual picture + $response = $client->get($picUrl); + + if ($response->isOk()) { + + $finalUrl = $client->getUrl(); + $filename = 'facebook-' . substr(strrchr($finalUrl, '/'), 1 ); + + common_debug("Filename = " . $filename, __FILE__); + + $ok = file_put_contents( + Avatar::path($filename), + $response->getBody() + ); + + if (!$ok) { + common_log( + LOG_WARNING, + sprintf( + 'Couldn\'t save Facebook avatar %s', + $tmp + ), + __FILE__ + ); + + } else { + + $profile = $user->getProfile(); + + if ($profile->setOriginal($filename)) { + common_log( + LOG_INFO, + sprintf( + 'Saved avatar for %s (%d) from Facebook profile %s, filename = %s', + $user->nickname, + $user->id, + $this->fbuid, + $picture + ), + __FILE__ + ); + } + } + } + } + function connectNewUser() { $nickname = $this->trimmed('nickname'); @@ -437,7 +502,6 @@ class FacebookfinishloginAction extends Action __FILE__ ); - // Return to Facebook connection settings tab common_redirect(common_local_url('facebookfinishlogin'), 303); } diff --git a/plugins/FacebookSSO/lib/facebookclient.php b/plugins/FacebookSSO/lib/facebookclient.php index cf45c9ed95..6753243ed0 100644 --- a/plugins/FacebookSSO/lib/facebookclient.php +++ b/plugins/FacebookSSO/lib/facebookclient.php @@ -47,7 +47,6 @@ class Facebookclient protected $flink = null; // Foreign_link StatusNet -> Facebook protected $notice = null; // The user's notice protected $user = null; // Sender of the notice - //protected $oldRestClient = null; // Old REST API client function __construct($notice) { @@ -382,7 +381,7 @@ class Facebookclient $code = $e->getCode(); // The Facebook PHP SDK seems to always set the code attribute - // of the Exception to 0; they put the real error code it in + // of the Exception to 0; they put the real error code in // the message. Gar! if ($code == 0) { preg_match('/^\(#(?\d+)\)/', $errmsg, $matches); @@ -554,7 +553,6 @@ class Facebookclient */ function formatAttachments() { - $attachments = $this->notice->attachments(); $fbattachment = array();