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