]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/FacebookSSO/FacebookBridgePlugin.php
- Map notices to Facebook stream items
[quix0rs-gnu-social.git] / plugins / FacebookSSO / FacebookBridgePlugin.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * A plugin for integrating Facebook with StatusNet. Includes single-sign-on
7  * and publishing notices to Facebook using Facebook's Graph API.
8  *
9  * PHP version 5
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Affero General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Affero General Public License for more details.
20  *
21  * You should have received a copy of the GNU Affero General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  *
24  * @category  Pugin
25  * @package   StatusNet
26  * @author    Zach Copley <zach@status.net>
27  * @copyright 2010 StatusNet, Inc.
28  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
29  * @link      http://status.net/
30  */
31
32 if (!defined('STATUSNET')) {
33     exit(1);
34 }
35
36 define("FACEBOOK_SERVICE", 2);
37
38 /**
39  * Main class for Facebook plugin
40  *
41  * @category  Plugin
42  * @package   StatusNet
43  * @author    Zach Copley <zach@status.net>
44  * @copyright 2010 StatusNet, Inc.
45  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
46  * @link      http://status.net/
47  */
48 class FacebookBridgePlugin extends Plugin
49 {
50     public $appId    = null; // Facebook application ID
51     public $secret   = null; // Facebook application secret
52     public $facebook = null; // Facebook application instance
53     public $dir      = null; // Facebook SSO plugin dir
54
55     /**
56      * Initializer for this plugin
57      *
58      * Gets an instance of the Facebook API client object
59      *
60      * @return boolean hook value; true means continue processing, false means stop.
61      */
62     function initialize()
63     {
64         $this->facebook = Facebookclient::getFacebook(
65             $this->appId,
66             $this->secret
67         );
68
69         return true;
70     }
71
72     /**
73      * Load related modules when needed
74      *
75      * @param string $cls Name of the class to be loaded
76      *
77      * @return boolean hook value; true means continue processing, false means stop.
78      */
79     function onAutoload($cls)
80     {
81
82         $dir = dirname(__FILE__);
83
84         //common_debug("class = " . $cls);
85
86         switch ($cls)
87         {
88         case 'Facebook': // Facebook PHP SDK
89             include_once $dir . '/extlib/facebook.php';
90             return false;
91         case 'FacebookloginAction':
92         case 'FacebookfinishloginAction':
93         case 'FacebookadminpanelAction':
94         case 'FacebooksettingsAction':
95         case 'FacebookdeauthorizeAction':
96             include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
97             return false;
98         case 'Facebookclient':
99         case 'FacebookQueueHandler':
100             include_once $dir . '/lib/' . strtolower($cls) . '.php';
101             return false;
102         case 'Notice_to_item':
103             include_once $dir . '/classes/' . $cls . '.php';
104             return false;
105         default:
106             return true;
107         }
108
109     }
110
111     /**
112      * Database schema setup
113      *
114      * We maintain a table mapping StatusNet notices to Facebook items
115      *
116      * @see Schema
117      * @see ColumnDef
118      *
119      * @return boolean hook value; true means continue processing, false means stop.
120      */
121     function onCheckSchema()
122     {
123         $schema = Schema::get();
124         $schema->ensureTable('notice_to_item', Notice_to_item::schemaDef());
125         return true;
126     }
127
128     /*
129      * Does this $action need the Facebook JavaScripts?
130      */
131     function needsScripts($action)
132     {
133         static $needy = array(
134             'FacebookloginAction',
135             'FacebookfinishloginAction',
136             'FacebookadminpanelAction',
137             'FacebooksettingsAction'
138         );
139
140         if (in_array(get_class($action), $needy)) {
141             return true;
142         } else {
143             return false;
144         }
145     }
146
147     /**
148      * Map URLs to actions
149      *
150      * @param Net_URL_Mapper $m path-to-action mapper
151      *
152      * @return boolean hook value; true means continue processing, false means stop.
153      */
154     function onRouterInitialized($m)
155     {
156         // Always add the admin panel route
157         $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
158
159         // Only add these routes if an application has been setup on
160         // Facebook for the plugin to use.
161         if ($this->hasApplication()) {
162
163             $m->connect(
164                 'main/facebooklogin',
165                 array('action' => 'facebooklogin')
166             );
167             $m->connect(
168                 'main/facebookfinishlogin',
169                 array('action' => 'facebookfinishlogin')
170             );
171             $m->connect(
172                 'settings/facebook',
173                 array('action' => 'facebooksettings')
174             );
175             $m->connect(
176                 'facebook/deauthorize',
177                 array('action' => 'facebookdeauthorize')
178             );
179
180         }
181
182         return true;
183     }
184
185     /*
186      * Add a login tab for Facebook, but only if there's a Facebook
187      * application defined for the plugin to use.
188      *
189      * @param Action &action the current action
190      *
191      * @return void
192      */
193     function onEndLoginGroupNav(&$action)
194     {
195         $action_name = $action->trimmed('action');
196
197         if ($this->hasApplication()) {
198
199             $action->menuItem(
200                 common_local_url('facebooklogin'),
201                 _m('MENU', 'Facebook'),
202                 // TRANS: Tooltip for menu item "Facebook".
203                 _m('Login or register using Facebook'),
204                'facebooklogin' === $action_name
205             );
206         }
207
208         return true;
209     }
210
211     /**
212      * Add a Facebook tab to the admin panels
213      *
214      * @param Widget $nav Admin panel nav
215      *
216      * @return boolean hook value
217      */
218     function onEndAdminPanelNav($nav)
219     {
220         if (AdminPanelAction::canAdmin('facebook')) {
221
222             $action_name = $nav->action->trimmed('action');
223
224             $nav->out->menuItem(
225                 common_local_url('facebookadminpanel'),
226                 // TRANS: Menu item.
227                 _m('MENU','Facebook'),
228                 // TRANS: Tooltip for menu item "Facebook".
229                 _m('Facebook integration configuration'),
230                 $action_name == 'facebookadminpanel',
231                 'nav_facebook_admin_panel'
232             );
233         }
234
235         return true;
236     }
237
238     /*
239      * Add a tab for user-level Facebook settings
240      *
241      * @param Action &action the current action
242      *
243      * @return void
244      */
245     function onEndConnectSettingsNav(&$action)
246     {
247         if ($this->hasApplication()) {
248             $action_name = $action->trimmed('action');
249
250             $action->menuItem(
251                 common_local_url('facebooksettings'),
252                 // TRANS: Menu item tab.
253                 _m('MENU','Facebook'),
254                 // TRANS: Tooltip for menu item "Facebook".
255                 _m('Facebook settings'),
256                 $action_name === 'facebooksettings'
257             );
258         }
259
260         return true;
261     }
262
263     /*
264      * Is there a Facebook application for the plugin to use?
265      *
266      * Checks to see if a Facebook application ID and secret
267      * have been configured and a valid Facebook API client
268      * object exists.
269      *
270      */
271     function hasApplication()
272     {
273         if (!empty($this->facebook)) {
274
275             $appId  = $this->facebook->getAppId();
276             $secret = $this->facebook->getApiSecret();
277
278             if (!empty($appId) && !empty($secret)) {
279                 return true;
280             }
281
282         }
283
284         return false;
285     }
286
287     /*
288      * Output a Facebook div for the Facebook JavaSsript SDK to use
289      *
290      * @param Action $action the current action
291      *
292      */
293     function onStartShowHeader($action)
294     {
295         // output <div id="fb-root"></div> as close to <body> as possible
296         $action->element('div', array('id' => 'fb-root'));
297         return true;
298     }
299
300     /*
301      * Load the Facebook JavaScript SDK on pages that need them.
302      *
303      * @param Action $action the current action
304      *
305      */
306     function onEndShowScripts($action)
307     {
308         if ($this->needsScripts($action)) {
309
310             $action->script('https://connect.facebook.net/en_US/all.js');
311
312             $script = <<<ENDOFSCRIPT
313 FB.init({appId: %1\$s, session: %2\$s, status: true, cookie: true, xfbml: true});
314
315 $('#facebook_button').bind('click', function(event) {
316
317     event.preventDefault();
318
319     FB.login(function(response) {
320         if (response.session && response.perms) {
321             window.location.href = '%3\$s';
322         } else {
323             // NOP (user cancelled login)
324         }
325     }, {perms:'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email'});
326 });
327 ENDOFSCRIPT;
328
329             $action->inlineScript(
330                 sprintf($script,
331                     json_encode($this->facebook->getAppId()),
332                     json_encode($this->facebook->getSession()),
333                     common_local_url('facebookfinishlogin')
334                 )
335             );
336         }
337     }
338
339     /*
340      * Log the user out of Facebook, per the Facebook authentication guide
341      *
342      * @param Action action the current action
343      */
344     function onEndLogout($action)
345     {
346         if ($this->hasApplication()) {
347             $session = $this->facebook->getSession();
348             $fbuser  = null;
349             $fbuid   = null;
350
351             if ($session) {
352                 try {
353                     $fbuid  = $this->facebook->getUser();
354                     $fbuser = $this->facebook->api('/me');
355                  } catch (FacebookApiException $e) {
356                      common_log(LOG_ERROR, $e, __FILE__);
357                  }
358             }
359
360             if (!empty($fbuser)) {
361
362                 $logoutUrl = $this->facebook->getLogoutUrl(
363                     array('next' => common_local_url('public'))
364                 );
365
366                 common_log(
367                     LOG_INFO,
368                     sprintf(
369                         "Logging user out of Facebook (fbuid = %s)",
370                         $fbuid
371                     ),
372                     __FILE__
373                 );
374                 common_debug("LOGOUT URL = $logoutUrl");
375                 common_redirect($logoutUrl, 303);
376             }
377
378         }
379     }
380
381     /*
382      * Add fbml namespace to our HTML, so Facebook's JavaScript SDK can parse
383      * and render XFBML tags
384      *
385      * @param Action    $action   the current action
386      * @param array     $attrs    array of attributes for the HTML tag
387      *
388      * @return nothing
389      */
390     function onStartHtmlElement($action, $attrs) {
391
392         if ($this->needsScripts($action)) {
393             $attrs = array_merge(
394                 $attrs,
395                 array('xmlns:fb' => 'http://www.facebook.com/2008/fbml')
396             );
397         }
398
399         return true;
400     }
401
402     /**
403      * Add a Facebook queue item for each notice
404      *
405      * @param Notice $notice      the notice
406      * @param array  &$transports the list of transports (queues)
407      *
408      * @return boolean hook return
409      */
410     function onStartEnqueueNotice($notice, &$transports)
411     {
412         if (self::hasApplication() && $notice->isLocal()) {
413             array_push($transports, 'facebook');
414         }
415         return true;
416     }
417
418     /**
419      * Register Facebook notice queue handler
420      *
421      * @param QueueManager $manager
422      *
423      * @return boolean hook return
424      */
425     function onEndInitializeQueueManager($manager)
426     {
427         if (self::hasApplication()) {
428             $manager->connect('facebook', 'FacebookQueueHandler');
429         }
430         return true;
431     }
432
433     /*
434      * Use SSL for Facebook stuff
435      *
436      * @param string $action name
437      * @param boolean $ssl outval to force SSL
438      * @return mixed hook return value
439      */
440     function onSensitiveAction($action, &$ssl)
441     {
442         $sensitive = array(
443             'facebookadminpanel',
444             'facebooksettings',
445             'facebooklogin',
446             'facebookfinishlogin'
447         );
448
449         if (in_array($action, $sensitive)) {
450             $ssl = true;
451             return false;
452         } else {
453             return true;
454         }
455     }
456
457     /**
458      * If a notice gets deleted, remove the Notice_to_item mapping and
459      * delete the item on Facebook
460      *
461      * @param User   $user   The user doing the deleting
462      * @param Notice $notice The notice getting deleted
463      *
464      * @return boolean hook value
465      */
466     function onStartDeleteOwnNotice(User $user, Notice $notice)
467     {
468         $client = new Facebookclient($notice);
469         $client->streamRemove();
470         
471         return true;
472     }
473
474     /**
475      * Notify remote users when their notices get favorited.
476      *
477      * @param Profile or User $profile of local user doing the faving
478      * @param Notice $notice being favored
479      * @return hook return value
480      */
481     function onEndFavorNotice(Profile $profile, Notice $notice)
482     {
483         $client = new Facebookclient($notice);
484         $client->like();
485
486         return true;
487     }
488
489     /**
490      * Notify remote users when their notices get de-favorited.
491      *
492      * @param Profile $profile Profile person doing the de-faving
493      * @param Notice  $notice  Notice being favored
494      *
495      * @return hook return value
496      */
497     function onEndDisfavorNotice(Profile $profile, Notice $notice)
498     {
499         $client = new Facebookclient($notice);
500         $client->unLike();
501
502         return true;
503     }
504
505     /*
506      * Add version info for this plugin
507      *
508      * @param array &$versions    plugin version descriptions
509      */
510     function onPluginVersion(&$versions)
511     {
512         $versions[] = array(
513             'name' => 'Facebook Single-Sign-On',
514             'version' => STATUSNET_VERSION,
515             'author' => 'Craig Andrews, Zach Copley',
516             'homepage' => 'http://status.net/wiki/Plugin:FacebookBridge',
517             'rawdescription' =>
518             _m('A plugin for integrating StatusNet with Facebook.')
519         );
520
521         return true;
522     }
523 }