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