]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Facebook/FacebookPlugin.php
Facebook plugin no longer takes over Login and Connect settings nav menus
[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 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 application
40  *
41  * @category Plugin
42  * @package  StatusNet
43  * @author   Zach Copley <zach@status.net>
44  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
45  * @link     http://status.net/
46  */
47
48 class FacebookPlugin extends Plugin
49 {
50
51     /**
52      * Add Facebook app actions to the router table
53      *
54      * Hook for RouterInitialized event.
55      *
56      * @param Net_URL_Mapper &$m path-to-action mapper
57      *
58      * @return boolean hook return
59      */
60
61     function onStartInitializeRouter($m)
62     {
63
64         // Facebook App stuff
65
66         $m->connect('facebook/app', array('action' => 'facebookhome'));
67         $m->connect('facebook/app/index.php', array('action' => 'facebookhome'));
68         $m->connect('facebook/app/settings.php',
69                     array('action' => 'facebooksettings'));
70         $m->connect('facebook/app/invite.php', array('action' => 'facebookinvite'));
71         $m->connect('facebook/app/remove', array('action' => 'facebookremove'));
72
73         // Facebook Connect stuff
74
75         $m->connect('main/facebookconnect', array('action' => 'FBConnectAuth'));
76         $m->connect('main/facebooklogin', array('action' => 'FBConnectLogin'));
77         $m->connect('settings/facebook', array('action' => 'FBConnectSettings'));
78         $m->connect('xd_receiver.html', array('action' => 'FBC_XDReceiver'));
79
80         return true;
81     }
82
83     /**
84      * Automatically load the actions and libraries used by the Facebook app
85      *
86      * @param Class $cls the class
87      *
88      * @return boolean hook return
89      *
90      */
91
92     function onAutoload($cls)
93     {
94         switch ($cls) {
95         case 'FacebookAction':
96         case 'FacebookhomeAction':
97         case 'FacebookinviteAction':
98         case 'FacebookremoveAction':
99         case 'FacebooksettingsAction':
100             include_once INSTALLDIR . '/plugins/Facebook/' .
101               strtolower(mb_substr($cls, 0, -6)) . '.php';
102             return false;
103         case 'FBConnectAuthAction':
104         case 'FBConnectLoginAction':
105         case 'FBConnectSettingsAction':
106         case 'FBC_XDReceiverAction':
107             include_once INSTALLDIR . '/plugins/Facebook/' .
108               mb_substr($cls, 0, -6) . '.php';
109             return false;
110         case 'FBCLoginGroupNav':
111             include_once INSTALLDIR . '/plugins/Facebook/FBCLoginGroupNav.php';
112             return false;
113         case 'FBCSettingsNav':
114             include_once INSTALLDIR . '/plugins/Facebook/FBCSettingsNav.php';
115             return false;
116         default:
117             return true;
118         }
119     }
120
121     /**
122      * Override normal HTML output to force the content type to
123      * text/html and add in xmlns:fb
124      *
125      * @param Action $action the current action
126      *
127      * @return void
128      */
129
130     function onStartShowHTML($action)
131     {
132
133         if ($this->reqFbScripts($action)) {
134
135             // XXX: Horrible hack to make Safari, FF2, and Chrome work with
136             // Facebook Connect. These browser cannot use Facebook's
137             // DOM parsing routines unless the mime type of the page is
138             // text/html even though Facebook Connect uses XHTML.  This is
139             // A bug in Facebook Connect, and this is a temporary solution
140             // until they fix their JavaScript libs.
141
142             header('Content-Type: text/html');
143
144             $action->extraHeaders();
145
146             $action->startXML('html');
147
148             $language = $action->getLanguage();
149
150             $action->elementStart('html',
151                 array('xmlns'  => 'http://www.w3.org/1999/xhtml',
152                       'xmlns:fb' => 'http://www.facebook.com/2008/fbml',
153                       'xml:lang' => $language,
154                       'lang'     => $language));
155
156             return false;
157
158         } else {
159
160             return true;
161         }
162     }
163
164     /**
165      * Add in the Facebook Connect JavaScript stuff
166      *
167      * Note: this script needs to appear in the <body>
168      *
169      * @param Action $action the current action
170      *
171      * @return void
172      *
173      */
174
175     function onEndShowScripts($action)
176     {
177         if ($this->reqFbScripts($action)) {
178
179             $apikey      = common_config('facebook', 'apikey');
180             $plugin_path = common_path('plugins/Facebook');
181
182             $login_url  = common_local_url('FBConnectAuth');
183             $logout_url = common_local_url('logout');
184
185             // XXX: Facebook says we don't need this FB_RequireFeatures(),
186             // but we actually do, for IE and Safari. Gar.
187
188             $js  = '<script type="text/javascript">';
189             $js .= '    $(document).ready(function () {';
190             $js .= '         FB_RequireFeatures(';
191             $js .= '             ["XFBML"], function() {';
192             $js .= '                 FB.init("%1$s", "../xd_receiver.html");';
193             $js .= '             }';
194             $js .= '         );';
195             $js .= '    });';
196
197             $js .= '    function goto_login() {';
198             $js .= '        window.location = "%2$s";';
199             $js .= '    }';
200
201             // The below function alters the logout link so that it logs the user out
202             // of Facebook Connect as well as the site.  However, for some pages
203             // (FB Connect Settings) we need to output the FB Connect scripts (to
204             // show an existing FB connection even if the user isn't authenticated
205             // with Facebook connect) but NOT alter the logout link. And the only
206             // way to reliably do that is with the FB Connect .js libs.  Crazy.
207
208             $js .= '    FB.ensureInit(function() {';
209             $js .= '        FB.Connect.ifUserConnected(';
210             $js .= '            function() { ';
211             $js .= '                $(\'#nav_logout a\').attr(\'href\', \'#\');';
212             $js .= '                $(\'#nav_logout a\').click(function() {';
213             $js .= '                   FB.Connect.logoutAndRedirect(\'%3$s\');';
214             $js .= '                   return false;';
215             $js .= '                })';
216             $js .= '            },';
217             $js .= '            function() {';
218             $js .= '                return false;';
219             $js .= '            }';
220             $js .= '        );';
221             $js .= '     });';
222             $js .= '</script>';
223
224             $js = sprintf($js, $apikey, $login_url, $logout_url);
225
226             // Compress the bugger down a bit
227
228             $js = str_replace('  ', '', $js);
229
230             $action->raw("  $js");  // leading two spaces to make it line up
231         }
232
233     }
234
235     /**
236      * Add in an additional Facebook Connect script that's supposed to
237      * appear as close as possible to </body>
238      *
239      * @param Action $action the current action
240      *
241      * @return void
242      *
243      */
244
245     function onEndShowFooter($action)
246     {
247         if ($this->reqFbScripts($action)) {
248             $action->script('http://static.ak.connect.facebook.com' .
249                             '/js/api_lib/v0.4/FeatureLoader.js.php');
250         }
251     }
252
253     /**
254      * Output Facebook Connect specific CSS link
255      *
256      * @param Action $action the current action
257      *
258      * @return void
259      *
260      */
261
262     function onEndShowStatusNetStyles($action)
263     {
264         if ($this->reqFbScripts($action)) {
265             $action->cssLink('plugins/Facebook/FBConnect.css');
266         }
267     }
268
269     /**
270      * Does the Action we're plugged into require the FB Scripts?  We only
271      * want to output FB namespace, scripts, CSS, etc. on the pages that
272      * really need them.
273      *
274      * @param Action $action the current action
275      *
276      * @return boolean true
277      */
278
279     function reqFbScripts($action)
280     {
281
282         // If you're logged in w/FB Connect, you always need the FB stuff
283
284         $fbuid = $this->loggedIn();
285
286         if (!empty($fbuid)) {
287             return true;
288         }
289
290         // List of actions that require FB stuff
291
292         $needy = array('FBConnectLoginAction',
293                        'FBConnectauthAction',
294                        'FBConnectSettingsAction');
295
296         if (in_array(get_class($action), $needy)) {
297             return true;
298         }
299
300         return false;
301
302     }
303
304     /**
305      * Is the user currently logged in with FB Connect?
306      *
307      * @return mixed $fbuid the Facebook ID of the logged in user, or null
308      */
309
310     function loggedIn()
311     {
312         $user = common_current_user();
313
314         if (!empty($user)) {
315
316             $flink = Foreign_link::getByUserId($user->id,
317                 FACEBOOK_CONNECT_SERVICE);
318             $fbuid = 0;
319
320             if (!empty($flink)) {
321
322                 try {
323
324                     $facebook = getFacebook();
325                     $fbuid    = $facebook->get_loggedin_user();
326
327                 } catch (Exception $e) {
328                     common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
329                         'Problem getting Facebook user: ' .
330                             $e->getMessage());
331                 }
332
333                 if ($fbuid > 0) {
334                     return $fbuid;
335                 }
336             }
337         }
338
339         return null;
340     }
341
342     /**
343      * Add in a Facebook Connect avatar to the primary nav menu
344      *
345      * @param Action $action the current action
346      *
347      * @return void
348      *
349      */
350
351     function onStartPrimaryNav($action)
352     {
353         $user = common_current_user();
354
355         $connect = 'FBConnectSettings';
356         if (common_config('xmpp', 'enabled')) {
357             $connect = 'imsettings';
358         } else if (common_config('sms', 'enabled')) {
359             $connect = 'smssettings';
360         } else if (common_config('twitter', 'enabled')) {
361             $connect = 'twittersettings';
362         }
363
364         if (!empty($user)) {
365
366             $fbuid = $this->loggedIn();
367
368             if (!empty($fbuid)) {
369
370                 /* Default FB silhouette pic for FB users who haven't
371                    uploaded a profile pic yet. */
372
373                 $silhouetteUrl =
374                     'http://static.ak.fbcdn.net/pics/q_silhouette.gif';
375
376                 $url = $this->getProfilePicURL($fbuid);
377
378                 $action->elementStart('li', array('id' => 'nav_fb'));
379
380                 $action->element('img', array('id' => 'fbc_profile-pic',
381                     'src' => (!empty($url)) ? $url : $silhouetteUrl,
382                     'alt' => 'Facebook Connect User',
383                     'width' => '16'), '');
384
385                 $iconurl =  common_path('plugins/Facebook/fbfavicon.ico');
386                 $action->element('img', array('id' => 'fb_favicon',
387                     'src' => $iconurl));
388
389                 $action->elementEnd('li');
390
391             }
392         }
393
394         return true;
395     }
396
397     /*
398      * Add a login tab for Facebook Connect
399      *
400      * @param Action &action the current action
401      *
402      * @return void
403      */
404
405     function onEndLoginGroupNav(&$action)
406     {
407
408         $action_name = $action->trimmed('action');
409
410         $action->menuItem(common_local_url('FBConnectLogin'),
411                                            _('Facebook'),
412                                            _('Login or register using Facebook'),
413                                              'FBConnectLogin' === $action_name);
414
415         return true;
416     }
417
418     /*
419      * Add a tab for managing Facebook Connect settings
420      *
421      * @param Action &action the current action
422      *
423      * @return void
424      */
425
426     function onEndConnectSettingsNav(&$action)
427     {
428         $action_name = $action->trimmed('action');
429
430         $action->menuItem(common_local_url('FBConnectSettings'),
431                           _('Facebook'),
432                           _('Facebook Connect Settings'),
433                           $action_name === 'FBConnectSettings');
434
435         return true;
436     }
437
438     /**
439      * Have the logout process do some Facebook Connect cookie cleanup
440      *
441      * @param Action $action the current action
442      *
443      * @return void
444      */
445
446     function onStartLogout($action)
447     {
448         $action->logout();
449         $fbuid = $this->loggedIn();
450
451         if (!empty($fbuid)) {
452             try {
453                 $facebook = getFacebook();
454                 $facebook->expire_session();
455             } catch (Exception $e) {
456                 common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
457                            'Could\'t logout of Facebook: ' .
458                            $e->getMessage());
459             }
460         }
461
462         return true;
463     }
464
465     /**
466      * Get the URL of the user's Facebook avatar
467      *
468      * @param int $fbuid the Facebook user ID
469      *
470      * @return string $url the url for the user's Facebook avatar
471      */
472
473     function getProfilePicURL($fbuid)
474     {
475         $facebook = getFacebook();
476         $url      = null;
477
478         try {
479
480             $fqry = 'SELECT pic_square FROM user WHERE uid = %s';
481
482             $result = $facebook->api_client->fql_query(sprintf($fqry, $fbuid));
483
484             if (!empty($result)) {
485                 $url = $result[0]['pic_square'];
486             }
487
488         } catch (Exception $e) {
489             common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
490                        "Facebook client failure requesting profile pic!");
491         }
492
493         return $url;
494     }
495
496     /**
497      * Add a Facebook queue item for each notice
498      *
499      * @param Notice $notice      the notice
500      * @param array  &$transports the list of transports (queues)
501      *
502      * @return boolean hook return
503      */
504
505     function onStartEnqueueNotice($notice, &$transports)
506     {
507         array_push($transports, 'facebook');
508         return true;
509     }
510
511     /**
512      * broadcast the message when not using queuehandler
513      *
514      * @param Notice &$notice the notice
515      * @param array  $queue   destination queue
516      *
517      * @return boolean hook return
518      */
519
520     function onUnqueueHandleNotice(&$notice, $queue)
521     {
522         if (($queue == 'facebook') && ($this->_isLocal($notice))) {
523             facebookBroadcastNotice($notice);
524             return false;
525         }
526         return true;
527     }
528
529     /**
530      * Determine whether the notice was locally created
531      *
532      * @param Notice $notice the notice
533      *
534      * @return boolean locality
535      */
536
537     function _isLocal($notice)
538     {
539         return ($notice->is_local == Notice::LOCAL_PUBLIC ||
540                 $notice->is_local == Notice::LOCAL_NONPUBLIC);
541     }
542
543     /**
544      * Add Facebook queuehandler to the list of daemons to start
545      *
546      * @param array $daemons the list fo daemons to run
547      *
548      * @return boolean hook return
549      *
550      */
551
552     function onGetValidDaemons($daemons)
553     {
554         array_push($daemons, INSTALLDIR .
555                    '/plugins/Facebook/facebookqueuehandler.php');
556         return true;
557     }
558
559 }