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