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