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