]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/FacebookSSO/FacebookSSOPlugin.php
- Some reorganizing
[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 $apikey   = null; // Facebook API key (for deprecated "Old REST API")
58     public $secret   = null; // Facebook application secret
59     public $facebook = null; // Facebook application instance
60     public $dir      = null; // Facebook SSO plugin dir
61
62     /**
63      * Initializer for this plugin
64      *
65      * Plugins overload this method to do any initialization they need,
66      * like connecting to remote servers or creating paths or so on.
67      *
68      * @return boolean hook value; true means continue processing, false means stop.
69      */
70     function initialize()
71     {
72         $this->facebook = Facebookclient::getFacebook(
73             $this->appId,
74             $this->apikey,
75             $this->secret
76         );
77
78         return true;
79     }
80
81     /**
82      * Cleanup for this plugin
83      *
84      * Plugins overload this method to do any cleanup they need,
85      * like disconnecting from remote servers or deleting temp files or so on.
86      *
87      * @return boolean hook value; true means continue processing, false means stop.
88      */
89     function cleanup()
90     {
91         return true;
92     }
93
94     /**
95      * Load related modules when needed
96      *
97      * Most non-trivial plugins will require extra modules to do their work. Typically
98      * these include data classes, action classes, widget classes, or external libraries.
99      *
100      * This method receives a class name and loads the PHP file related to that class. By
101      * tradition, action classes typically have files named for the action, all lower-case.
102      * Data classes are in files with the data class name, initial letter capitalized.
103      *
104      * Note that this method will be called for *all* overloaded classes, not just ones
105      * in this plugin! So, make sure to return true by default to let other plugins, and
106      * the core code, get a chance.
107      *
108      * @param string $cls Name of the class to be loaded
109      *
110      * @return boolean hook value; true means continue processing, false means stop.
111      */
112     function onAutoload($cls)
113     {
114
115         $dir = dirname(__FILE__);
116
117         //common_debug("class = " . $cls);
118
119         switch ($cls)
120         {
121         case 'Facebook': // New JavaScript SDK
122             include_once $dir . '/extlib/facebook.php';
123             return false;
124         case 'FacebookRestClient': // Old REST lib
125             include_once $dir . '/extlib/facebookapi_php5_restlib.php';
126             return false;
127         case 'FacebookloginAction':
128         case 'FacebookregisterAction':
129         case 'FacebookadminpanelAction':
130         case 'FacebooksettingsAction':
131             include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
132             return false;
133         case 'Facebookclient':
134         case 'Facebookqueuehandler':
135             include_once $dir . '/lib/' . strtolower($cls) . '.php';
136             return false;
137         default:
138             return true;
139         }
140
141     }
142
143     /*
144      * Does this $action need the Facebook JavaScripts?
145      */
146     function needsScripts($action)
147     {
148         static $needy = array(
149             'FacebookloginAction',
150             'FacebookregisterAction',
151             'FacebookadminpanelAction',
152             'FacebooksettingsAction'
153         );
154
155         if (in_array(get_class($action), $needy)) {
156             return true;
157         } else {
158             return false;
159         }
160     }
161
162     /**
163      * Map URLs to actions
164      *
165      * This event handler lets the plugin map URLs on the site to actions (and
166      * thus an action handler class). Note that the action handler class for an
167      * action will be named 'FoobarAction', where action = 'foobar'. The class
168      * must be loaded in the onAutoload() method.
169      *
170      * @param Net_URL_Mapper $m path-to-action mapper
171      *
172      * @return boolean hook value; true means continue processing, false means stop.
173      */
174     function onRouterInitialized($m)
175     {
176         // Always add the admin panel route
177         $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
178
179         // Only add these routes if an application has been setup on
180         // Facebook for the plugin to use.
181         if ($this->hasApplication()) {
182
183             $m->connect(
184                 'main/facebooklogin',
185                 array('action' => 'facebooklogin')
186             );
187             $m->connect(
188                 'main/facebookregister',
189                 array('action' => 'facebookregister')
190             );
191
192             $m->connect(
193                 'settings/facebook',
194                 array('action' => 'facebooksettings')
195             );
196
197         }
198
199         return true;
200     }
201
202     /*
203      * Add a login tab for Facebook, but only if there's a Facebook
204      * application defined for the plugin to use.
205      *
206      * @param Action &action the current action
207      *
208      * @return void
209      */
210     function onEndLoginGroupNav(&$action)
211     {
212         $action_name = $action->trimmed('action');
213
214         if ($this->hasApplication()) {
215
216             $action->menuItem(
217                 common_local_url('facebooklogin'),
218                 _m('MENU', 'Facebook'),
219                 // TRANS: Tooltip for menu item "Facebook".
220                 _m('Login or register using Facebook'),
221                'facebooklogin' === $action_name
222             );
223         }
224
225         return true;
226     }
227
228     /**
229      * Add a Facebook tab to the admin panels
230      *
231      * @param Widget $nav Admin panel nav
232      *
233      * @return boolean hook value
234      */
235     function onEndAdminPanelNav($nav)
236     {
237         if (AdminPanelAction::canAdmin('facebook')) {
238
239             $action_name = $nav->action->trimmed('action');
240
241             $nav->out->menuItem(
242                 common_local_url('facebookadminpanel'),
243                 // TRANS: Menu item.
244                 _m('MENU','Facebook'),
245                 // TRANS: Tooltip for menu item "Facebook".
246                 _m('Facebook integration configuration'),
247                 $action_name == 'facebookadminpanel',
248                 'nav_facebook_admin_panel'
249             );
250         }
251
252         return true;
253     }
254
255     /*
256      * Add a tab for user-level Facebook settings
257      *
258      * @param Action &action the current action
259      *
260      * @return void
261      */
262     function onEndConnectSettingsNav(&$action)
263     {
264         if ($this->hasApplication()) {
265             $action_name = $action->trimmed('action');
266
267             $action->menuItem(
268                 common_local_url('facebooksettings'),
269                 // TRANS: Menu item tab.
270                 _m('MENU','Facebook'),
271                 // TRANS: Tooltip for menu item "Facebook".
272                 _m('Facebook settings'),
273                 $action_name === 'facebooksettings'
274             );
275         }
276
277         return true;
278     }
279
280     /*
281      * Is there a Facebook application for the plugin to use?
282      */
283     function hasApplication()
284     {
285         if (!empty($this->facebook)) {
286
287             $appId  = $this->facebook->getAppId();
288             $secret = $this->facebook->getApiSecret();
289
290             if (!empty($appId) && !empty($secret)) {
291                 return true;
292             }
293
294         }
295
296         return false;
297     }
298
299     function onStartShowHeader($action)
300     {
301         if ($this->needsScripts($action)) {
302
303             // output <div id="fb-root"></div> as close to <body> as possible
304             $action->element('div', array('id' => 'fb-root'));
305
306             $dir = dirname(__FILE__);
307
308             $script = <<<ENDOFSCRIPT
309 window.fbAsyncInit = function() {
310
311     FB.init({
312       appId   : %s,
313       session : %s,   // don't refetch the session when PHP already has it
314       status  : true, // check login status
315       cookie  : true, // enable cookies to allow the server to access the session
316       xfbml   : true  // parse XFBML
317     });
318
319     // whenever the user logs in, refresh the page
320     FB.Event.subscribe(
321         'auth.login',
322         function() {
323             window.location.reload();
324         }
325     );
326 };
327
328 (function() {
329     var e = document.createElement('script');
330     e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
331     e.async = true;
332     document.getElementById('fb-root').appendChild(e);
333 }());
334 ENDOFSCRIPT;
335
336             $action->inlineScript(
337                 sprintf($script,
338                     json_encode($this->facebook->getAppId()),
339                     json_encode($this->facebook->getSession())
340                 )
341             );
342         }
343
344         return true;
345     }
346
347     /*
348      * Log the user out of Facebook, per the Facebook authentication guide
349      *
350      * @param Action action the action
351      */
352     function onEndLogout($action)
353     {
354         if ($this->hasApplication()) {
355             $session = $this->facebook->getSession();
356             $fbuser  = null;
357             $fbuid   = null;
358
359             if ($session) {
360                 try {
361                     $fbuid  = $this->facebook->getUser();
362                     $fbuser = $this->facebook->api('/me');
363                  } catch (FacebookApiException $e) {
364                      common_log(LOG_ERROR, $e, __FILE__);
365                  }
366             }
367
368             if (!empty($fbuser)) {
369
370                 $logoutUrl = $this->facebook->getLogoutUrl(
371                     array('next' => common_local_url('public'))
372                 );
373
374                 common_log(
375                     LOG_INFO,
376                     sprintf(
377                         "Logging user out of Facebook (fbuid = %s)",
378                         $fbuid
379                     ),
380                     __FILE__
381                 );
382                 common_debug("LOGOUT URL = $logoutUrl");
383                 common_redirect($logoutUrl, 303);
384             }
385
386         }
387     }
388
389     /*
390      * Add fbml namespace so Facebook's JavaScript SDK can parse and render
391      * XFBML tags (e.g: <fb:login-button>)
392      *
393      * @param Action    $action   current action
394      * @param array     $attrs    array of attributes for the HTML tag
395      *
396      * @return nothing
397      */
398     function onStartHtmlElement($action, $attrs) {
399
400         if ($this->needsScripts($action)) {
401             $attrs = array_merge(
402                 $attrs,
403                 array('xmlns:fb' => 'http://www.facebook.com/2008/fbml')
404             );
405         }
406
407         return true;
408     }
409
410     /*
411      * Add version info for this plugin
412      *
413      * @param array &$versions    plugin version descriptions
414      */
415     function onPluginVersion(&$versions)
416     {
417         $versions[] = array(
418             'name' => 'Facebook Single-Sign-On',
419             'version' => STATUSNET_VERSION,
420             'author' => 'Craig Andrews, Zach Copley',
421             'homepage' => 'http://status.net/wiki/Plugin:FacebookSSO',
422             'rawdescription' =>
423             _m('A plugin for integrating StatusNet with Facebook.')
424         );
425
426         return true;
427     }
428 }