]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/FacebookSSO/FacebookSSOPlugin.php
Facebook SSO - Log the user out of Facebook when s/he logs out of StatusNet
[quix0rs-gnu-social.git] / plugins / FacebookSSO / FacebookSSOPlugin.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * A plugin for single-sign-in (SSO) with Facebook
7  *
8  * PHP version 5
9  *
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.
14  *
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.
19  *
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/>.
22  *
23  * @category  Pugin
24  * @package   StatusNet
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/
29  */
30
31 if (!defined('STATUSNET')) {
32     exit(1);
33 }
34
35 define("FACEBOOK_SERVICE", 2);
36
37 /**
38  * Main class for Facebook single-sign-on plugin
39  *
40  *
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
45  * directory.
46  *
47  * @category  Plugin
48  * @package   StatusNet
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/
53  */
54 class FacebookSSOPlugin extends Plugin
55 {
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
60
61     /**
62      * Initializer for this plugin
63      *
64      * Plugins overload this method to do any initialization they need,
65      * like connecting to remote servers or creating paths or so on.
66      *
67      * @return boolean hook value; true means continue processing, false means stop.
68      */
69     function initialize()
70     {
71         // Check defaults and configuration for application ID and secret
72         if (empty($this->appId)) {
73             $this->appId = common_config('facebook', 'appid');
74         }
75
76         if (empty($this->secret)) {
77             $this->secret = common_config('facebook', 'secret');
78         }
79
80         if (empty($this->facebook)) {
81             $this->facebook = new Facebook(
82                 array(
83                     'appId'  => $this->appId,
84                     'secret' => $this->secret,
85                     'cookie' => true
86                 )
87             );
88         }
89         
90         return true;
91     }
92
93     /**
94      * Cleanup for this plugin
95      *
96      * Plugins overload this method to do any cleanup they need,
97      * like disconnecting from remote servers or deleting temp files or so on.
98      *
99      * @return boolean hook value; true means continue processing, false means stop.
100      */
101     function cleanup()
102     {
103         return true;
104     }
105
106     /**
107      * Load related modules when needed
108      *
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.
111      *
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.
115      *
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.
119      *
120      * @param string $cls Name of the class to be loaded
121      *
122      * @return boolean hook value; true means continue processing, false means stop.
123      */
124     function onAutoload($cls)
125     {
126
127         $dir = dirname(__FILE__);
128
129         //common_debug("class = " . $cls);
130
131         switch ($cls)
132         {
133         case 'Facebook':
134             include_once $dir . '/extlib/facebook.php';
135             return false;
136         case 'FacebookloginAction':
137         case 'FacebookregisterAction':
138         case 'FacebookadminpanelAction':
139         case 'FacebooksettingsAction':
140             include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
141             return false;
142         default:
143             return true;
144         }
145
146     }
147
148     /**
149      * Map URLs to actions
150      *
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.
155      *
156      * @param Net_URL_Mapper $m path-to-action mapper
157      *
158      * @return boolean hook value; true means continue processing, false means stop.
159      */
160     function onRouterInitialized($m)
161     {
162         // Always add the admin panel route
163         $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
164
165         // Only add these routes if an application has been setup on
166         // Facebook for the plugin to use.
167         if ($this->hasApplication()) {
168
169             $m->connect(
170                 'main/facebooklogin',
171                 array('action' => 'facebooklogin')
172             );
173             $m->connect(
174                 'main/facebookregister',
175                 array('action' => 'facebookregister')
176             );
177
178             $m->connect(
179                 'settings/facebook',
180                 array('action' => 'facebooksettings')
181             );
182             
183         }
184
185         return true;
186     }
187
188     /*
189      * Add a login tab for Facebook, but only if there's a Facebook
190      * application defined for the plugin to use.
191      *
192      * @param Action &action the current action
193      *
194      * @return void
195      */
196     function onEndLoginGroupNav(&$action)
197     {
198         $action_name = $action->trimmed('action');
199
200         if ($this->hasApplication()) {
201
202             $action->menuItem(
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
208             );
209         }
210
211         return true;
212     }
213
214     /**
215      * Add a Facebook tab to the admin panels
216      *
217      * @param Widget $nav Admin panel nav
218      *
219      * @return boolean hook value
220      */
221     function onEndAdminPanelNav($nav)
222     {
223         if (AdminPanelAction::canAdmin('facebook')) {
224
225             $action_name = $nav->action->trimmed('action');
226
227             $nav->out->menuItem(
228                 common_local_url('facebookadminpanel'),
229                 // TRANS: Menu item.
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'
235             );
236         }
237
238         return true;
239     }
240
241     /*
242      * Add a tab for user-level Facebook settings
243      *
244      * @param Action &action the current action
245      *
246      * @return void
247      */
248     function onEndConnectSettingsNav(&$action)
249     {
250         if ($this->hasApplication()) {
251             $action_name = $action->trimmed('action');
252
253             $action->menuItem(
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'
260             );
261         }
262
263         return true;
264     }
265
266     /*
267      * Is there a Facebook application for the plugin to use?
268      */
269     function hasApplication()
270     {
271         if (!empty($this->appId) && !empty($this->secret)) {
272             return true;
273         } else {
274             return false;
275         }
276     }
277
278     function onStartShowHeader($action)
279     {
280         // output <div id="fb-root"></div> as close to <body> as possible
281         $action->element('div', array('id' => 'fb-root'));
282
283         $session = $this->facebook->getSession();
284         $dir = dirname(__FILE__);
285
286         // XXX: minify this
287         $script = <<<ENDOFSCRIPT
288         window.fbAsyncInit = function() {
289
290     FB.init({
291       appId   : %s,
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
296     });
297
298     // whenever the user logs in, refresh the page
299     FB.Event.subscribe(
300         'auth.login',
301         function() {
302             window.location.reload();
303         }
304     );
305 };
306
307 (function() {
308     var e = document.createElement('script');
309     e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
310     e.async = true;
311     document.getElementById('fb-root').appendChild(e);
312 }());
313 ENDOFSCRIPT;
314
315         $action->inlineScript(
316             sprintf($script,
317                 json_encode($this->appId),
318                 json_encode($this->session)
319             )
320         );
321         
322         return true;
323     }
324
325     /*
326      * Log the user out of Facebook, per the Facebook authentication guide
327      *
328      * @param Action action the action
329      */
330     function onEndLogout($action)
331     {
332         $session = $this->facebook->getSession();
333         $fbuser  = null;
334         $fbuid   = null;
335
336         if ($session) {
337             try {
338                 $fbuid  = $this->facebook->getUser();
339                 $fbuser = $this->facebook->api('/me');
340              } catch (FacebookApiException $e) {
341                  common_log(LOG_ERROR, $e, __FILE__);
342              }
343         }
344
345         if (!empty($fbuser)) {
346
347             $logoutUrl = $this->facebook->getLogoutUrl(
348                 array('next' => common_local_url('public'))
349             );
350
351             common_log(
352                 LOG_INFO,
353                 sprintf(
354                     "Logging user out of Facebook (fbuid = %s)",
355                     $fbuid
356                 ),
357                 __FILE__
358             );
359
360             common_redirect($logoutUrl, 303);
361         }
362     }
363
364     /*
365      * Add fbml namespace so Facebook's JavaScript SDK can parse and render
366      * XFBML tags (e.g: <fb:login-button>)
367      *
368      * @param Action    $action   current action
369      * @param array     $attrs    array of attributes for the HTML tag
370      *
371      * @return nothing
372      */
373     function onStartHtmlElement($action, $attrs) {
374         $attrs = array_merge($attrs, array('xmlns:fb' => 'http://www.facebook.com/2008/fbml'));
375         return true;
376     }
377
378     /*
379      * Add version info for this plugin
380      *
381      * @param array &$versions    plugin version descriptions
382      */
383     function onPluginVersion(&$versions)
384     {
385         $versions[] = array(
386             'name' => 'Facebook Single-Sign-On',
387             'version' => STATUSNET_VERSION,
388             'author' => 'Zach Copley',
389             'homepage' => 'http://status.net/wiki/Plugin:FacebookSSO',
390             'rawdescription' =>
391             _m('A plugin for single-sign-on with Facebook.')
392         );
393
394         return true;
395     }
396 }