]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Facebook/FacebookPlugin.php
Add IdentiCurse to notice sources
[quix0rs-gnu-social.git] / plugins / Facebook / FacebookPlugin.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Plugin to add a StatusNet Facebook application
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Plugin
23  * @package   StatusNet
24  * @author    Zach Copley <zach@status.net>
25  * @copyright 2009-2010 StatusNet, Inc.
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://status.net/
28  */
29
30 if (!defined('STATUSNET')) {
31     exit(1);
32 }
33
34 define("FACEBOOK_CONNECT_SERVICE", 3);
35
36 require_once INSTALLDIR . '/plugins/Facebook/facebookutil.php';
37
38 /**
39  * Facebook plugin to add a StatusNet Facebook canvas application
40  * and allow registration and authentication via Facebook Connect
41  *
42  * @category Plugin
43  * @package  StatusNet
44  * @author   Zach Copley <zach@status.net>
45  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
46  * @link     http://status.net/
47  */
48 class FacebookPlugin extends Plugin
49 {
50     const VERSION = STATUSNET_VERSION;
51
52     /**
53      * Initializer for the plugin.
54      */
55
56     function initialize()
57     {
58         // Allow the key and secret to be passed in
59         // Control panel will override
60         if (isset($this->apikey)) {
61             $key = common_config('facebook', 'apikey');
62             if (empty($key)) {
63                 Config::save('facebook', 'apikey', $this->apikey);
64             }
65         }
66
67         if (isset($this->secret)) {
68             $secret = common_config('facebook', 'secret');
69             if (empty($secret)) {
70                 Config::save(
71                     'facebook',
72                     'secret',
73                     $this->secret
74                 );
75             }
76         }
77     }
78
79     /**
80      * Check to see if there is an API key and secret defined
81      * for Facebook integration.
82      *
83      * @return boolean result
84      */
85     static function hasKeys()
86     {
87         $apiKey    = common_config('facebook', 'apikey');
88         $apiSecret = common_config('facebook', 'secret');
89
90         if (!empty($apiKey) && !empty($apiSecret)) {
91             return true;
92         }
93
94         return false;
95     }
96
97     /**
98      * Add Facebook app actions to the router table
99      *
100      * Hook for RouterInitialized event.
101      *
102      * @param Net_URL_Mapper &$m path-to-action mapper
103      *
104      * @return boolean hook return
105      */
106     function onStartInitializeRouter($m)
107     {
108         $m->connect('admin/facebook', array('action' => 'facebookadminpanel'));
109
110         if (self::hasKeys()) {
111             // Facebook App stuff
112
113             $m->connect('facebook/app', array('action' => 'facebookhome'));
114             $m->connect('facebook/app/index.php', array('action' => 'facebookhome'));
115             $m->connect('facebook/app/settings.php',
116                         array('action' => 'facebooksettings'));
117             $m->connect('facebook/app/invite.php', array('action' => 'facebookinvite'));
118             $m->connect('facebook/app/remove', array('action' => 'facebookremove'));
119
120             // Facebook Connect stuff
121
122             $m->connect('main/facebookconnect', array('action' => 'FBConnectAuth'));
123             $m->connect('main/facebooklogin', array('action' => 'FBConnectLogin'));
124             $m->connect('settings/facebook', array('action' => 'FBConnectSettings'));
125             $m->connect('xd_receiver.html', array('action' => 'FBC_XDReceiver'));
126         }
127
128         return true;
129     }
130
131     /**
132      * Automatically load the actions and libraries used by the Facebook app
133      *
134      * @param Class $cls the class
135      *
136      * @return boolean hook return
137      *
138      */
139     function onAutoload($cls)
140     {
141         switch ($cls) {
142         case 'FacebookAction':
143         case 'FacebookhomeAction':
144         case 'FacebookinviteAction':
145         case 'FacebookremoveAction':
146         case 'FacebooksettingsAction':
147         case 'FacebookadminpanelAction':
148             include_once INSTALLDIR . '/plugins/Facebook/' .
149               strtolower(mb_substr($cls, 0, -6)) . '.php';
150             return false;
151         case 'FBConnectAuthAction':
152         case 'FBConnectLoginAction':
153         case 'FBConnectSettingsAction':
154         case 'FBC_XDReceiverAction':
155             include_once INSTALLDIR . '/plugins/Facebook/' .
156               mb_substr($cls, 0, -6) . '.php';
157             return false;
158         case 'FBCLoginGroupNav':
159             include_once INSTALLDIR . '/plugins/Facebook/FBCLoginGroupNav.php';
160             return false;
161         case 'FBCSettingsNav':
162             include_once INSTALLDIR . '/plugins/Facebook/FBCSettingsNav.php';
163             return false;
164         case 'FacebookQueueHandler':
165             include_once INSTALLDIR . '/plugins/Facebook/facebookqueuehandler.php';
166             return false;
167         default:
168             return true;
169         }
170     }
171
172     /**
173      * Add a Facebook tab to the admin panels
174      *
175      * @param Widget $nav Admin panel nav
176      *
177      * @return boolean hook value
178      */
179     function onEndAdminPanelNav($nav)
180     {
181         if (AdminPanelAction::canAdmin('facebook')) {
182
183             $action_name = $nav->action->trimmed('action');
184
185             $nav->out->menuItem(
186                 common_local_url('facebookadminpanel'),
187                 // TRANS: Menu item.
188                 _m('MENU','Facebook'),
189                 // TRANS: Tooltip for menu item "Facebook".
190                 _m('Facebook integration configuration'),
191                 $action_name == 'facebookadminpanel',
192                 'nav_facebook_admin_panel'
193             );
194         }
195
196         return true;
197     }
198
199     /**
200      * Override normal HTML output to force the content type to
201      * text/html and add in xmlns:fb
202      *
203      * @param Action $action the current action
204      *
205      * @return void
206      */
207     function onStartShowHTML($action)
208     {
209         if ($this->reqFbScripts($action)) {
210             // XXX: Horrible hack to make Safari, FF2, and Chrome work with
211             // Facebook Connect. These browser cannot use Facebook's
212             // DOM parsing routines unless the mime type of the page is
213             // text/html even though Facebook Connect uses XHTML.  This is
214             // A bug in Facebook Connect, and this is a temporary solution
215             // until they fix their JavaScript libs.
216
217             header('Content-Type: text/html');
218
219             $action->extraHeaders();
220
221             $action->startXML('html');
222
223             $language = $action->getLanguage();
224
225             $action->elementStart('html',
226                 array('xmlns'  => 'http://www.w3.org/1999/xhtml',
227                       'xmlns:fb' => 'http://www.facebook.com/2008/fbml',
228                       'xml:lang' => $language,
229                       'lang'     => $language));
230
231             return false;
232         } else {
233             return true;
234         }
235     }
236
237     /**
238      * Add in the Facebook Connect JavaScript stuff
239      *
240      * Note: this script needs to appear in the <body>
241      *
242      * @param Action $action the current action
243      *
244      * @return void
245      *
246      */
247     function onEndShowScripts($action)
248     {
249         if ($this->reqFbScripts($action)) {
250
251             $apikey      = common_config('facebook', 'apikey');
252             $plugin_path = 'plugins/Facebook';
253
254             $login_url  = common_local_url('FBConnectAuth');
255             $logout_url = common_local_url('logout');
256
257             // XXX: Facebook says we don't need this FB_RequireFeatures(),
258             // but we actually do, for IE and Safari. Gar.
259
260             $js .= '    $(document).ready(function () {';
261             $js .= '         FB_RequireFeatures(';
262             $js .= '             ["XFBML"], function() {';
263             $js .= '                 FB.init("%1$s", "../xd_receiver.html");';
264             $js .= '             }';
265             $js .= '         );';
266             $js .= '    });';
267
268             $js .= '    function goto_login() {';
269             $js .= '        window.location = "%2$s";';
270             $js .= '    }';
271
272             // The below function alters the logout link so that it logs the user out
273             // of Facebook Connect as well as the site.  However, for some pages
274             // (FB Connect Settings) we need to output the FB Connect scripts (to
275             // show an existing FB connection even if the user isn't authenticated
276             // with Facebook connect) but NOT alter the logout link. And the only
277             // way to reliably do that is with the FB Connect .js libs.  Crazy.
278
279             $js .= '    FB.ensureInit(function() {';
280             $js .= '        FB.Connect.ifUserConnected(';
281             $js .= '            function() { ';
282             $js .= '                $(\'#nav_logout a\').attr(\'href\', \'#\');';
283             $js .= '                $(\'#nav_logout a\').click(function() {';
284             $js .= '                   FB.Connect.logoutAndRedirect(\'%3$s\');';
285             $js .= '                   return false;';
286             $js .= '                })';
287             $js .= '            },';
288             $js .= '            function() {';
289             $js .= '                return false;';
290             $js .= '            }';
291             $js .= '        );';
292             $js .= '     });';
293
294             $js = sprintf($js, $apikey, $login_url, $logout_url);
295
296             // Compress the bugger down a bit
297             $js = str_replace('  ', '', $js);
298
299             $action->inlineScript($js);
300         }
301     }
302
303     /**
304      * Add in an additional Facebook Connect script that's supposed to
305      * appear as close as possible to </body>
306      *
307      * @param Action $action the current action
308      *
309      * @return void
310      *
311      */
312     function onEndShowFooter($action)
313     {
314         if ($this->reqFbScripts($action)) {
315             $action->script('http://static.ak.connect.facebook.com' .
316                             '/js/api_lib/v0.4/FeatureLoader.js.php');
317         }
318     }
319
320     /**
321      * Output Facebook Connect specific CSS link
322      *
323      * @param Action $action the current action
324      *
325      * @return void
326      *
327      */
328     function onEndShowStatusNetStyles($action)
329     {
330         if ($this->reqFbScripts($action)) {
331             $action->cssLink('plugins/Facebook/FBConnect.css');
332         }
333     }
334
335     /**
336      * Does the Action we're plugged into require the FB Scripts?  We only
337      * want to output FB namespace, scripts, CSS, etc. on the pages that
338      * really need them.
339      *
340      * @param Action $action the current action
341      *
342      * @return boolean true
343      */
344     function reqFbScripts($action)
345     {
346         if (!self::hasKeys()) {
347             return false;
348         }
349
350         // If you're logged in w/FB Connect, you always need the FB stuff
351         $fbuid = $this->loggedIn();
352
353         if (!empty($fbuid)) {
354             return true;
355         }
356
357         // List of actions that require FB stuff
358         $needy = array('FBConnectLoginAction',
359                        'FBConnectauthAction',
360                        'FBConnectSettingsAction');
361
362         if (in_array(get_class($action), $needy)) {
363             return true;
364         }
365
366         return false;
367     }
368
369     /**
370      * Is the user currently logged in with FB Connect?
371      *
372      * @return mixed $fbuid the Facebook ID of the logged in user, or null
373      */
374     function loggedIn()
375     {
376         $user = common_current_user();
377
378         if (!empty($user)) {
379
380             $flink = Foreign_link::getByUserId($user->id,
381                 FACEBOOK_CONNECT_SERVICE);
382             $fbuid = 0;
383
384             if (!empty($flink)) {
385                 try {
386                     $facebook = getFacebook();
387                     $fbuid    = $facebook->get_loggedin_user();
388                 } catch (Exception $e) {
389                     common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
390                         'Problem getting Facebook user: ' .
391                             $e->getMessage());
392                 }
393
394                 if ($fbuid > 0) {
395                     return $fbuid;
396                 }
397             }
398         }
399
400         return null;
401     }
402
403     /**
404      * Add in a Facebook Connect avatar to the primary nav menu
405      *
406      * @param Action $action the current action
407      *
408      * @return void
409      *
410      */
411     function onStartPrimaryNav($action)
412     {
413         if (self::hasKeys()) {
414             $user = common_current_user();
415             if (!empty($user)) {
416                 $fbuid = $this->loggedIn();
417
418                 if (!empty($fbuid)) {
419                     /* Default FB silhouette pic for FB users who haven't
420                        uploaded a profile pic yet. */
421
422                     $silhouetteUrl =
423                         'http://static.ak.fbcdn.net/pics/q_silhouette.gif';
424
425                     $url = $this->getProfilePicURL($fbuid);
426
427                     $action->elementStart('li', array('id' => 'nav_fb'));
428
429                     $action->element('img', array('id' => 'fbc_profile-pic',
430                         'src' => (!empty($url)) ? $url : $silhouetteUrl,
431                         'alt' => _m('Facebook Connect User'),
432                         'width' => '16'), '');
433
434                     $iconurl =  common_path('plugins/Facebook/fbfavicon.ico');
435                     $action->element('img', array('id' => 'fb_favicon',
436                         'src' => $iconurl));
437
438                     $action->elementEnd('li');
439                 }
440             }
441         }
442
443         return true;
444     }
445
446     /*
447      * Add a login tab for Facebook Connect
448      *
449      * @param Action &action the current action
450      *
451      * @return void
452      */
453     function onEndLoginGroupNav(&$action)
454     {
455         if (self::hasKeys()) {
456             $action_name = $action->trimmed('action');
457
458             $action->menuItem(common_local_url('FBConnectLogin'),
459                                                // @todo CHECKME: Should be 'Facebook Login'?
460                                                // TRANS: Menu item.
461                                                _m('MENU','Facebook'),
462                                                // TRANS: Tooltip for menu item "Facebook".
463                                                _m('Login or register using Facebook'),
464                                                'FBConnectLogin' === $action_name);
465         }
466
467         return true;
468     }
469
470     /*
471      * Add a tab for managing Facebook Connect settings
472      *
473      * @param Action &action the current action
474      *
475      * @return void
476      */
477     function onEndConnectSettingsNav(&$action)
478     {
479         if (self::hasKeys()) {
480             $action_name = $action->trimmed('action');
481
482             $action->menuItem(common_local_url('FBConnectSettings'),
483                               // @todo CHECKME: Should be 'Facebook Connect'? 
484                               // TRANS: Menu item tab.
485                               _m('MENU','Facebook'),
486                               // TRANS: Tooltip for menu item "Facebook".
487                               _m('Facebook Connect Settings'),
488                               $action_name === 'FBConnectSettings');
489         }
490
491         return true;
492     }
493
494     /**
495      * Have the logout process do some Facebook Connect cookie cleanup
496      *
497      * @param Action $action the current action
498      *
499      * @return void
500      */
501     function onStartLogout($action)
502     {
503         if (self::hasKeys()) {
504
505             $action->logout();
506             $fbuid = $this->loggedIn();
507
508             if (!empty($fbuid)) {
509                 try {
510                     $facebook = getFacebook();
511                     $facebook->expire_session();
512                 } catch (Exception $e) {
513                     common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
514                                'Could\'t logout of Facebook: ' .
515                                $e->getMessage());
516                 }
517             }
518         }
519         return true;
520     }
521
522     /**
523      * Get the URL of the user's Facebook avatar
524      *
525      * @param int $fbuid the Facebook user ID
526      *
527      * @return string $url the url for the user's Facebook avatar
528      */
529     function getProfilePicURL($fbuid)
530     {
531         $facebook = getFacebook();
532         $url      = null;
533
534         try {
535             $fqry = 'SELECT pic_square FROM user WHERE uid = %s';
536
537             $result = $facebook->api_client->fql_query(sprintf($fqry, $fbuid));
538
539             if (!empty($result)) {
540                 $url = $result[0]['pic_square'];
541             }
542
543         } catch (Exception $e) {
544             common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
545                        "Facebook client failure requesting profile pic!");
546         }
547
548         return $url;
549     }
550
551     /**
552      * Add a Facebook queue item for each notice
553      *
554      * @param Notice $notice      the notice
555      * @param array  &$transports the list of transports (queues)
556      *
557      * @return boolean hook return
558      */
559     function onStartEnqueueNotice($notice, &$transports)
560     {
561         if (self::hasKeys() && $notice->isLocal()) {
562             array_push($transports, 'facebook');
563         }
564         return true;
565     }
566
567     /**
568      * Register Facebook notice queue handler
569      *
570      * @param QueueManager $manager
571      *
572      * @return boolean hook return
573      */
574     function onEndInitializeQueueManager($manager)
575     {
576         if (self::hasKeys()) {
577             $manager->connect('facebook', 'FacebookQueueHandler');
578         }
579         return true;
580     }
581
582     function onPluginVersion(&$versions)
583     {
584         $versions[] = array(
585             'name' => 'Facebook',
586             'version' => self::VERSION,
587             'author' => 'Zach Copley',
588             'homepage' => 'http://status.net/wiki/Plugin:Facebook',
589             // TRANS: Plugin description.
590             'rawdescription' => _m(
591                 'The Facebook plugin allows integrating ' .
592                 'StatusNet instances with ' .
593                 '<a href="http://facebook.com/">Facebook</a> ' .
594                 'and Facebook Connect.'
595             )
596         );
597         return true;
598     }
599 }