]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/FacebookBridge/FacebookBridgePlugin.php
Merge branch 'master' into 0.9.x
[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 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 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    = null; // Facebook application ID
51     public $secret   = null; // Facebook application secret
52     public $facebook = null; // Facebook application instance
53     public $dir      = null; // Facebook 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      * If the plugin's installed, this should be accessible to admins
213      */
214     function onAdminPanelCheck($name, &$isOK)
215     {
216         if ($name == 'facebook') {
217             $isOK = true;
218             return false;
219         }
220
221         return true;
222     }
223
224     /**
225      * Add a Facebook tab to the admin panels
226      *
227      * @param Widget $nav Admin panel nav
228      *
229      * @return boolean hook value
230      */
231     function onEndAdminPanelNav($nav)
232     {
233         if (AdminPanelAction::canAdmin('facebook')) {
234
235             $action_name = $nav->action->trimmed('action');
236
237             $nav->out->menuItem(
238                 common_local_url('facebookadminpanel'),
239                 // TRANS: Menu item.
240                 _m('MENU','Facebook'),
241                 // TRANS: Tooltip for menu item "Facebook".
242                 _m('Facebook integration configuration'),
243                 $action_name == 'facebookadminpanel',
244                 'nav_facebook_admin_panel'
245             );
246         }
247
248         return true;
249     }
250
251     /*
252      * Add a tab for user-level Facebook settings if the user
253      * has a link to Facebook
254      *
255      * @param Action &action the current action
256      *
257      * @return void
258      */
259     function onEndConnectSettingsNav(&$action)
260     {
261         if ($this->hasApplication()) {
262             $action_name = $action->trimmed('action');
263
264             // CurrentUserDesignAction stores the current user in $cur
265             $user = $action->getCurrentUser();
266
267             $flink = null;
268
269             if (!empty($user)) {
270                 $flink = Foreign_link::getByUserID(
271                     $user->id,
272                     FACEBOOK_SERVICE
273                 );
274             }
275
276             if (!empty($flink)) {
277
278                 $action->menuItem(
279                     common_local_url('facebooksettings'),
280                     // TRANS: Menu item tab.
281                     _m('MENU','Facebook'),
282                     // TRANS: Tooltip for menu item "Facebook".
283                     _m('Facebook settings'),
284                     $action_name === 'facebooksettings'
285                 );
286
287             }
288         }
289
290     }
291
292     /*
293      * Is there a Facebook application for the plugin to use?
294      *
295      * Checks to see if a Facebook application ID and secret
296      * have been configured and a valid Facebook API client
297      * object exists.
298      *
299      */
300     function hasApplication()
301     {
302         if (!empty($this->facebook)) {
303
304             $appId  = $this->facebook->getAppId();
305             $secret = $this->facebook->getApiSecret();
306
307             if (!empty($appId) && !empty($secret)) {
308                 return true;
309             }
310
311         }
312
313         return false;
314     }
315
316     /*
317      * Output a Facebook div for the Facebook JavaSsript SDK to use
318      *
319      * @param Action $action the current action
320      *
321      */
322     function onStartShowHeader($action)
323     {
324         // output <div id="fb-root"></div> as close to <body> as possible
325         $action->element('div', array('id' => 'fb-root'));
326         return true;
327     }
328
329     /*
330      * Load the Facebook JavaScript SDK on pages that need them.
331      *
332      * @param Action $action the current action
333      *
334      */
335     function onEndShowScripts($action)
336     {
337         if ($this->needsScripts($action)) {
338
339             $action->script('https://connect.facebook.net/en_US/all.js');
340
341             $script = <<<ENDOFSCRIPT
342 FB.init({appId: %1\$s, session: %2\$s, status: true, cookie: true, xfbml: true});
343
344 $('#facebook_button').bind('click', function(event) {
345
346     event.preventDefault();
347
348     FB.login(function(response) {
349         if (response.session && response.perms) {
350             window.location.href = '%3\$s';
351         } else {
352             // NOP (user cancelled login)
353         }
354     }, {perms:'read_stream,publish_stream,offline_access,user_status,user_location,user_website,email'});
355 });
356 ENDOFSCRIPT;
357
358             $action->inlineScript(
359                 sprintf($script,
360                     json_encode($this->facebook->getAppId()),
361                     json_encode($this->facebook->getSession()),
362                     common_local_url('facebookfinishlogin')
363                 )
364             );
365         }
366     }
367
368     /*
369      * Log the user out of Facebook, per the Facebook authentication guide
370      *
371      * @param Action action the current action
372      */
373     function onEndLogout($action)
374     {
375         if ($this->hasApplication()) {
376             $session = $this->facebook->getSession();
377             $fbuser  = null;
378             $fbuid   = null;
379
380             if ($session) {
381                 try {
382                     $fbuid  = $this->facebook->getUser();
383                     $fbuser = $this->facebook->api('/me');
384                  } catch (FacebookApiException $e) {
385                      common_log(LOG_ERROR, $e, __FILE__);
386                  }
387             }
388
389             if (!empty($fbuser)) {
390
391                 $logoutUrl = $this->facebook->getLogoutUrl(
392                     array('next' => common_local_url('public'))
393                 );
394
395                 common_log(
396                     LOG_INFO,
397                     sprintf(
398                         "Logging user out of Facebook (fbuid = %s)",
399                         $fbuid
400                     ),
401                     __FILE__
402                 );
403                 common_debug("LOGOUT URL = $logoutUrl");
404                 common_redirect($logoutUrl, 303);
405             }
406
407         }
408     }
409
410     /*
411      * Add fbml namespace to our HTML, so Facebook's JavaScript SDK can parse
412      * and render XFBML tags
413      *
414      * @param Action    $action   the current action
415      * @param array     $attrs    array of attributes for the HTML tag
416      *
417      * @return nothing
418      */
419     function onStartHtmlElement($action, $attrs) {
420
421         if ($this->needsScripts($action)) {
422             $attrs = array_merge(
423                 $attrs,
424                 array('xmlns:fb' => 'http://www.facebook.com/2008/fbml')
425             );
426         }
427
428         return true;
429     }
430
431     /**
432      * Add a Facebook queue item for each notice
433      *
434      * @param Notice $notice      the notice
435      * @param array  &$transports the list of transports (queues)
436      *
437      * @return boolean hook return
438      */
439     function onStartEnqueueNotice($notice, &$transports)
440     {
441         if (self::hasApplication() && $notice->isLocal()) {
442             array_push($transports, 'facebook');
443         }
444         return true;
445     }
446
447     /**
448      * Register Facebook notice queue handler
449      *
450      * @param QueueManager $manager
451      *
452      * @return boolean hook return
453      */
454     function onEndInitializeQueueManager($manager)
455     {
456         if (self::hasApplication()) {
457             $manager->connect('facebook', 'FacebookQueueHandler');
458         }
459         return true;
460     }
461
462     /*
463      * Use SSL for Facebook stuff
464      *
465      * @param string $action name
466      * @param boolean $ssl outval to force SSL
467      * @return mixed hook return value
468      */
469     function onSensitiveAction($action, &$ssl)
470     {
471         $sensitive = array(
472             'facebookadminpanel',
473             'facebooksettings',
474             'facebooklogin',
475             'facebookfinishlogin'
476         );
477
478         if (in_array($action, $sensitive)) {
479             $ssl = true;
480             return false;
481         } else {
482             return true;
483         }
484     }
485
486     /**
487      * If a notice gets deleted, remove the Notice_to_item mapping and
488      * delete the item on Facebook
489      *
490      * @param User   $user   The user doing the deleting
491      * @param Notice $notice The notice getting deleted
492      *
493      * @return boolean hook value
494      */
495     function onStartDeleteOwnNotice(User $user, Notice $notice)
496     {
497         $client = new Facebookclient($notice);
498         $client->streamRemove();
499
500         return true;
501     }
502
503     /**
504      * Notify remote users when their notices get favorited.
505      *
506      * @param Profile or User $profile of local user doing the faving
507      * @param Notice $notice being favored
508      * @return hook return value
509      */
510     function onEndFavorNotice(Profile $profile, Notice $notice)
511     {
512         $client = new Facebookclient($notice);
513         $client->like();
514
515         return true;
516     }
517
518     /**
519      * Notify remote users when their notices get de-favorited.
520      *
521      * @param Profile $profile Profile person doing the de-faving
522      * @param Notice  $notice  Notice being favored
523      *
524      * @return hook return value
525      */
526     function onEndDisfavorNotice(Profile $profile, Notice $notice)
527     {
528         $client = new Facebookclient($notice);
529         $client->unLike();
530
531         return true;
532     }
533
534     /*
535      * Add version info for this plugin
536      *
537      * @param array &$versions    plugin version descriptions
538      */
539     function onPluginVersion(&$versions)
540     {
541         $versions[] = array(
542             'name' => 'Facebook Bridge',
543             'version' => STATUSNET_VERSION,
544             'author' => 'Craig Andrews, Zach Copley',
545             'homepage' => 'http://status.net/wiki/Plugin:FacebookBridge',
546             'rawdescription' =>
547             _m('A plugin for integrating StatusNet with Facebook.')
548         );
549
550         return true;
551     }
552 }