3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2010, StatusNet, Inc.
6 * A plugin for single-sign-in (SSO) with Facebook
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * @author Zach Copley <zach@status.net>
26 * @copyright 2010 StatusNet, Inc.
27 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
28 * @link http://status.net/
31 if (!defined('STATUSNET')) {
35 define("FACEBOOK_SERVICE", 2);
38 * Main class for Facebook single-sign-on plugin
41 * Simple plugins can be implemented as a single module. Others are more complex
42 * and require additional modules; these should use their own directory, like
43 * 'local/plugins/{$name}/'. All files related to the plugin, including images,
44 * JavaScript, CSS, external libraries or PHP modules should go in the plugin
49 * @author Zach Copley <zach@status.net>
50 * @copyright 2010 StatusNet, Inc.
51 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
52 * @link http://status.net/
54 class FacebookSSOPlugin extends Plugin
56 public $appId = null; // Facebook application ID
57 public $secret = null; // Facebook application secret
58 public $facebook = null; // Facebook application instance
59 public $dir = null; // Facebook SSO plugin dir
62 * Initializer for this plugin
64 * Plugins overload this method to do any initialization they need,
65 * like connecting to remote servers or creating paths or so on.
67 * @return boolean hook value; true means continue processing, false means stop.
71 // Check defaults and configuration for application ID and secret
72 if (empty($this->appId)) {
73 $this->appId = common_config('facebook', 'appid');
76 if (empty($this->secret)) {
77 $this->secret = common_config('facebook', 'secret');
80 if (empty($this->facebook)) {
81 $this->facebook = new Facebook(
83 'appId' => $this->appId,
84 'secret' => $this->secret,
94 * Cleanup for this plugin
96 * Plugins overload this method to do any cleanup they need,
97 * like disconnecting from remote servers or deleting temp files or so on.
99 * @return boolean hook value; true means continue processing, false means stop.
107 * Load related modules when needed
109 * Most non-trivial plugins will require extra modules to do their work. Typically
110 * these include data classes, action classes, widget classes, or external libraries.
112 * This method receives a class name and loads the PHP file related to that class. By
113 * tradition, action classes typically have files named for the action, all lower-case.
114 * Data classes are in files with the data class name, initial letter capitalized.
116 * Note that this method will be called for *all* overloaded classes, not just ones
117 * in this plugin! So, make sure to return true by default to let other plugins, and
118 * the core code, get a chance.
120 * @param string $cls Name of the class to be loaded
122 * @return boolean hook value; true means continue processing, false means stop.
124 function onAutoload($cls)
127 $dir = dirname(__FILE__);
129 //common_debug("class = " . $cls);
134 include_once $dir . '/extlib/facebook.php';
136 case 'FacebookloginAction':
137 case 'FacebookregisterAction':
138 case 'FacebookadminpanelAction':
139 case 'FacebooksettingsAction':
140 include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
149 * Map URLs to actions
151 * This event handler lets the plugin map URLs on the site to actions (and
152 * thus an action handler class). Note that the action handler class for an
153 * action will be named 'FoobarAction', where action = 'foobar'. The class
154 * must be loaded in the onAutoload() method.
156 * @param Net_URL_Mapper $m path-to-action mapper
158 * @return boolean hook value; true means continue processing, false means stop.
160 function onRouterInitialized($m)
162 // Always add the admin panel route
163 $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
165 // Only add these routes if an application has been setup on
166 // Facebook for the plugin to use.
167 if ($this->hasApplication()) {
170 'main/facebooklogin',
171 array('action' => 'facebooklogin')
174 'main/facebookregister',
175 array('action' => 'facebookregister')
180 array('action' => 'facebooksettings')
189 * Add a login tab for Facebook, but only if there's a Facebook
190 * application defined for the plugin to use.
192 * @param Action &action the current action
196 function onEndLoginGroupNav(&$action)
198 $action_name = $action->trimmed('action');
200 if ($this->hasApplication()) {
203 common_local_url('facebooklogin'),
204 _m('MENU', 'Facebook'),
205 // TRANS: Tooltip for menu item "Facebook".
206 _m('Login or register using Facebook'),
207 'facebooklogin' === $action_name
215 * Add a Facebook tab to the admin panels
217 * @param Widget $nav Admin panel nav
219 * @return boolean hook value
221 function onEndAdminPanelNav($nav)
223 if (AdminPanelAction::canAdmin('facebook')) {
225 $action_name = $nav->action->trimmed('action');
228 common_local_url('facebookadminpanel'),
230 _m('MENU','Facebook'),
231 // TRANS: Tooltip for menu item "Facebook".
232 _m('Facebook integration configuration'),
233 $action_name == 'facebookadminpanel',
234 'nav_facebook_admin_panel'
242 * Add a tab for user-level Facebook settings
244 * @param Action &action the current action
248 function onEndConnectSettingsNav(&$action)
250 if ($this->hasApplication()) {
251 $action_name = $action->trimmed('action');
254 common_local_url('facebooksettings'),
255 // TRANS: Menu item tab.
256 _m('MENU','Facebook'),
257 // TRANS: Tooltip for menu item "Facebook".
258 _m('Facebook settings'),
259 $action_name === 'facebooksettings'
267 * Is there a Facebook application for the plugin to use?
269 function hasApplication()
271 if (!empty($this->appId) && !empty($this->secret)) {
278 function onStartShowHeader($action)
280 // output <div id="fb-root"></div> as close to <body> as possible
281 $action->element('div', array('id' => 'fb-root'));
283 $session = $this->facebook->getSession();
284 $dir = dirname(__FILE__);
287 $script = <<<ENDOFSCRIPT
288 window.fbAsyncInit = function() {
292 session : %s, // don't refetch the session when PHP already has it
293 status : true, // check login status
294 cookie : true, // enable cookies to allow the server to access the session
295 xfbml : true // parse XFBML
298 // whenever the user logs in, refresh the page
302 window.location.reload();
308 var e = document.createElement('script');
309 e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
311 document.getElementById('fb-root').appendChild(e);
315 $action->inlineScript(
317 json_encode($this->appId),
318 json_encode($this->session)
326 * Log the user out of Facebook, per the Facebook authentication guide
328 * @param Action action the action
330 function onEndLogout($action)
332 $session = $this->facebook->getSession();
338 $fbuid = $this->facebook->getUser();
339 $fbuser = $this->facebook->api('/me');
340 } catch (FacebookApiException $e) {
341 common_log(LOG_ERROR, $e, __FILE__);
345 if (!empty($fbuser)) {
347 $logoutUrl = $this->facebook->getLogoutUrl(
348 array('next' => common_local_url('public'))
354 "Logging user out of Facebook (fbuid = %s)",
360 common_redirect($logoutUrl, 303);
365 * Add fbml namespace so Facebook's JavaScript SDK can parse and render
366 * XFBML tags (e.g: <fb:login-button>)
368 * @param Action $action current action
369 * @param array $attrs array of attributes for the HTML tag
373 function onStartHtmlElement($action, $attrs) {
374 $attrs = array_merge($attrs, array('xmlns:fb' => 'http://www.facebook.com/2008/fbml'));
379 * Add version info for this plugin
381 * @param array &$versions plugin version descriptions
383 function onPluginVersion(&$versions)
386 'name' => 'Facebook Single-Sign-On',
387 'version' => STATUSNET_VERSION,
388 'author' => 'Zach Copley',
389 'homepage' => 'http://status.net/wiki/Plugin:FacebookSSO',
391 _m('A plugin for single-sign-on with Facebook.')