]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/FacebookBridge/FacebookBridgePlugin.php
Merge remote-tracking branch 'origin/testing' into testing
[quix0rs-gnu-social.git] / plugins / FacebookBridge / FacebookBridgePlugin.php
1 <?php
2 /**
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010-2011, 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  Plugin
25  * @package   StatusNet
26  * @author    Zach Copley <zach@status.net>
27  * @copyright 2011 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 Bridge plugin
40  *
41  * @category  Plugin
42  * @package   StatusNet
43  * @author    Zach Copley <zach@status.net>
44  * @copyright 2010-2011 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;  // Facebook application ID
51     public $secret; // Facebook application secret
52
53     public $facebook = null; // Facebook application instance
54     public $dir      = null; // Facebook plugin dir
55
56     /**
57      * Initializer for this plugin
58      *
59      * Gets an instance of the Facebook API client object
60      *
61      * @return boolean hook value; true means continue processing, false means stop.
62      */
63     function initialize()
64     {
65
66         // Allow the id and key to be passed in
67         // Control panel will override
68
69         if (isset($this->appId)) {
70             $appId = common_config('facebook', 'appid');
71             if (empty($appId)) {
72                 Config::save(
73                     'facebook',
74                     'appid',
75                     $this->appId
76                 );
77             }
78         }
79
80         if (isset($this->secret)) {
81             $secret = common_config('facebook', 'secret');
82             if (empty($secret)) {
83                 Config::save('facebook', 'secret', $this->secret);
84             }
85         }
86
87         $this->facebook = Facebookclient::getFacebook(
88             $this->appId,
89             $this->secret
90         );
91
92         return true;
93     }
94
95     /**
96      * Load related modules when needed
97      *
98      * @param string $cls Name of the class to be loaded
99      *
100      * @return boolean hook value; true means continue processing, false means stop.
101      */
102     function onAutoload($cls)
103     {
104         $dir = dirname(__FILE__);
105
106         switch ($cls)
107         {
108         case 'Facebook': // Facebook PHP SDK
109             include_once $dir . '/extlib/base_facebook.php';
110             include_once $dir . '/extlib/facebook.php';
111             return false;
112         case 'FacebookloginAction':
113         case 'FacebookfinishloginAction':
114         case 'FacebookadminpanelAction':
115         case 'FacebooksettingsAction':
116         case 'FacebookdeauthorizeAction':
117             include_once $dir . '/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
118             return false;
119         case 'Facebookclient':
120         case 'FacebookQueueHandler':
121             include_once $dir . '/lib/' . strtolower($cls) . '.php';
122             return false;
123         case 'Notice_to_item':
124             include_once $dir . '/classes/' . $cls . '.php';
125             return false;
126         default:
127             return true;
128         }
129     }
130
131     /**
132      * Database schema setup
133      *
134      * We maintain a table mapping StatusNet notices to Facebook items
135      *
136      * @see Schema
137      * @see ColumnDef
138      *
139      * @return boolean hook value; true means continue processing, false means stop.
140      */
141     function onCheckSchema()
142     {
143         $schema = Schema::get();
144         $schema->ensureTable('notice_to_item', Notice_to_item::schemaDef());
145         return true;
146     }
147
148     /*
149      * Does this $action need the Facebook JavaScripts?
150      */
151     function needsScripts($action)
152     {
153         static $needy = array(
154             'FacebookloginAction',
155             'FacebookfinishloginAction',
156             'FacebookadminpanelAction',
157             'FacebooksettingsAction'
158         );
159
160         if (in_array(get_class($action), $needy)) {
161             return true;
162         } else {
163             return false;
164         }
165     }
166
167     /**
168      * Map URLs to actions
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('panel/facebook', array('action' => 'facebookadminpanel'));
178
179         $m->connect(
180             'main/facebooklogin',
181             array('action' => 'facebooklogin')
182         );
183         $m->connect(
184             'main/facebookfinishlogin',
185             array('action' => 'facebookfinishlogin')
186         );
187         $m->connect(
188             'settings/facebook',
189             array('action' => 'facebooksettings')
190         );
191         $m->connect(
192             'facebook/deauthorize',
193             array('action' => 'facebookdeauthorize')
194         );
195
196         return true;
197     }
198
199     /*
200      * Add a login tab for Facebook, but only if there's a Facebook
201      * application defined for the plugin to use.
202      *
203      * @param Action $action the current action
204      *
205      * @return void
206      */
207     function onEndLoginGroupNav($action)
208     {
209         $action_name = $action->trimmed('action');
210
211         if ($this->hasApplication()) {
212
213             $action->menuItem(
214                 // TRANS: Menu item for "Facebook" login.
215                 common_local_url('facebooklogin'),
216                 _m('MENU', 'Facebook'),
217                 // TRANS: Menu title for "Facebook" login.
218                 _m('Login or register using Facebook.'),
219                'facebooklogin' === $action_name
220             );
221         }
222
223         return true;
224     }
225
226     /**
227      * If the plugin's installed, this should be accessible to admins
228      */
229     function onAdminPanelCheck($name, &$isOK)
230     {
231         if ($name == 'facebook') {
232             $isOK = true;
233             return false;
234         }
235
236         return true;
237     }
238
239     /**
240      * Add a Facebook tab to the admin panels
241      *
242      * @param Widget $nav Admin panel nav
243      *
244      * @return boolean hook value
245      */
246     function onEndAdminPanelNav($nav)
247     {
248         if (AdminPanelAction::canAdmin('facebook')) {
249
250             $action_name = $nav->action->trimmed('action');
251
252             $nav->out->menuItem(
253                 common_local_url('facebookadminpanel'),
254                 // TRANS: Menu item for "Facebook" in administration panel.
255                 _m('MENU','Facebook'),
256                 // TRANS: Menu title for "Facebook" in administration panel.
257                 _m('Facebook integration configuration.'),
258                 $action_name == 'facebookadminpanel',
259                 'nav_facebook_admin_panel'
260             );
261         }
262
263         return true;
264     }
265
266     /*
267      * Add a tab for user-level Facebook settings if the user
268      * has a link to Facebook
269      *
270      * @param Action $action the current action
271      *
272      * @return void
273      */
274     function onEndConnectSettingsNav($action)
275     {
276         if ($this->hasApplication()) {
277             $action_name = $action->trimmed('action');
278
279             $user = common_current_user();
280
281             $flink = null;
282
283             if (!empty($user)) {
284                 $flink = Foreign_link::getByUserID(
285                     $user->id,
286                     FACEBOOK_SERVICE
287                 );
288             }
289
290             if (!empty($flink)) {
291
292                 $action->menuItem(
293                     common_local_url('facebooksettings'),
294                     // TRANS: Menu item for "Facebook" in user settings.
295                     _m('MENU','Facebook'),
296                     // TRANS: Menu title for "Facebook" in user settings.
297                     _m('Facebook settings.'),
298                     $action_name === 'facebooksettings'
299                 );
300             }
301         }
302     }
303
304     /*
305      * Is there a Facebook application for the plugin to use?
306      *
307      * Checks to see if a Facebook application ID and secret
308      * have been configured and a valid Facebook API client
309      * object exists.
310      *
311      */
312     function hasApplication()
313     {
314         if (!empty($this->facebook)) {
315
316             $appId  = $this->facebook->getAppId();
317             $secret = $this->facebook->getApiSecret();
318
319             if (!empty($appId) && !empty($secret)) {
320                 return true;
321             }
322         }
323
324         return false;
325     }
326
327     /*
328      * Output a Facebook div for the Facebook JavaSsript SDK to use
329      *
330      * @param Action $action the current action
331      *
332      */
333     function onStartShowHeader($action)
334     {
335         // output <div id="fb-root"></div> as close to <body> as possible
336         $action->element('div', array('id' => 'fb-root'));
337         return true;
338     }
339
340     /*
341      * Load the Facebook JavaScript SDK on pages that need them.
342      *
343      * @param Action $action the current action
344      *
345      */
346     function onEndShowScripts($action)
347     {
348         if ($this->needsScripts($action)) {
349
350             $action->script('https://connect.facebook.net/en_US/all.js');
351
352             $script = <<<ENDOFSCRIPT
353 function setCookie(name, value) {
354     var date = new Date();
355     date.setTime(date.getTime() + (5 * 60 * 1000)); // 5 mins
356     var expires = "; expires=" + date.toGMTString();
357     document.cookie = name + "=" + value + expires + "; path=/";
358 }
359
360 FB.init({appId: %1\$s, status: true, cookie: true, xfbml: true, oauth: true});
361
362 $('#facebook_button').bind('click', function(event) {
363
364     event.preventDefault();
365
366     FB.login(function(response) {
367         if (response.authResponse) {
368             // put the access token in a cookie for the next step
369             setCookie('fb_access_token', response.authResponse.accessToken);
370             window.location.href = '%2\$s';
371         } else {
372             // NOP (user cancelled login)
373         }
374     }, {scope:'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email'});
375 });
376 ENDOFSCRIPT;
377
378             $action->inlineScript(
379                 sprintf(
380                     $script,
381                     json_encode($this->facebook->getAppId()),
382                     common_local_url('facebookfinishlogin')
383                 )
384             );
385         }
386     }
387
388     /*
389      * Log the user out of Facebook, per the Facebook authentication guide
390      *
391      * @param Action action the current action
392      */
393     function onStartLogout($action)
394     {
395         if ($this->hasApplication()) {
396
397             $cur = common_current_user();
398             $flink = Foreign_link::getByUserID($cur->id, FACEBOOK_SERVICE);
399
400             if (!empty($flink)) {
401
402                 $this->facebook->setAccessToken($flink->credentials);
403
404                 if (common_config('singleuser', 'enabled')) {
405                     $user = User::singleUser();
406
407                     $destination = common_local_url(
408                         'showstream',
409                         array('nickname' => $user->nickname)
410                     );
411                 } else {
412                     $destination = common_local_url('public');
413                 }
414
415                 $logoutUrl = $this->facebook->getLogoutUrl(
416                     array('next' => $destination)
417                 );
418
419                 common_log(
420                     LOG_INFO,
421                     sprintf(
422                         "Logging user out of Facebook (fbuid = %s)",
423                         $fbuid
424                     ),
425                     __FILE__
426                 );
427
428                 $action->logout();
429
430                 common_redirect($logoutUrl, 303);
431                 return false; // probably never get here, but hey
432             }
433
434             return true;
435         }
436     }
437
438     /*
439      * Add fbml namespace to our HTML, so Facebook's JavaScript SDK can parse
440      * and render XFBML tags
441      *
442      * @param Action    $action   the current action
443      * @param array     $attrs    array of attributes for the HTML tag
444      *
445      * @return nothing
446      */
447     function onStartHtmlElement($action, $attrs) {
448
449         if ($this->needsScripts($action)) {
450             $attrs = array_merge(
451                 $attrs,
452                 array('xmlns:fb' => 'http://www.facebook.com/2008/fbml')
453             );
454         }
455
456         return true;
457     }
458
459     /**
460      * Add a Facebook queue item for each notice
461      *
462      * @param Notice $notice      the notice
463      * @param array  &$transports the list of transports (queues)
464      *
465      * @return boolean hook return
466      */
467     function onStartEnqueueNotice($notice, &$transports)
468     {
469         if (self::hasApplication() && $notice->isLocal() && $notice->inScope(null)) {
470             array_push($transports, 'facebook');
471         }
472         return true;
473     }
474
475     /**
476      * Register Facebook notice queue handler
477      *
478      * @param QueueManager $manager
479      *
480      * @return boolean hook return
481      */
482     function onEndInitializeQueueManager($manager)
483     {
484         if (self::hasApplication()) {
485             $manager->connect('facebook', 'FacebookQueueHandler');
486         }
487         return true;
488     }
489
490     /*
491      * Use SSL for Facebook stuff
492      *
493      * @param string $action name
494      * @param boolean $ssl outval to force SSL
495      * @return mixed hook return value
496      */
497     function onSensitiveAction($action, &$ssl)
498     {
499         $sensitive = array(
500             'facebookadminpanel',
501             'facebooksettings',
502             'facebooklogin',
503             'facebookfinishlogin'
504         );
505
506         if (in_array($action, $sensitive)) {
507             $ssl = true;
508             return false;
509         } else {
510             return true;
511         }
512     }
513
514     /**
515      * If a notice gets deleted, remove the Notice_to_item mapping and
516      * delete the item on Facebook
517      *
518      * @param User   $user   The user doing the deleting
519      * @param Notice $notice The notice getting deleted
520      *
521      * @return boolean hook value
522      */
523     function onStartDeleteOwnNotice(User $user, Notice $notice)
524     {
525         $client = new Facebookclient($notice);
526         $client->streamRemove();
527
528         return true;
529     }
530
531     /**
532      * Notify remote users when their notices get favorited.
533      *
534      * @param Profile or User $profile of local user doing the faving
535      * @param Notice $notice being favored
536      * @return hook return value
537      */
538     function onEndFavorNotice(Profile $profile, Notice $notice)
539     {
540         $client = new Facebookclient($notice, $profile);
541         $client->like();
542
543         return true;
544     }
545
546     /**
547      * Notify remote users when their notices get de-favorited.
548      *
549      * @param Profile $profile Profile person doing the de-faving
550      * @param Notice  $notice  Notice being favored
551      *
552      * @return hook return value
553      */
554     function onEndDisfavorNotice(Profile $profile, Notice $notice)
555     {
556         $client = new Facebookclient($notice, $profile);
557         $client->unLike();
558
559         return true;
560     }
561
562     /*
563      * Add version info for this plugin
564      *
565      * @param array &$versions    plugin version descriptions
566      */
567     function onPluginVersion(&$versions)
568     {
569         $versions[] = array(
570             'name' => 'Facebook Bridge',
571             'version' => STATUSNET_VERSION,
572             'author' => 'Craig Andrews, Zach Copley',
573             'homepage' => 'http://status.net/wiki/Plugin:FacebookBridge',
574             'rawdescription' =>
575              // TRANS: Plugin description.
576             _m('A plugin for integrating StatusNet with Facebook.')
577         );
578
579         return true;
580     }
581 }