]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Facebook/FacebookPlugin.php
Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline 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
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 onRouterInitialized($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      * Alter the local nav menu to have a Facebook Connect login and
399      * settings pages
400      *
401      * @param Action $action the current action
402      *
403      * @return void
404      *
405      */
406
407     function onStartShowLocalNavBlock($action)
408     {
409         $action_name = get_class($action);
410
411         $login_actions = array('LoginAction', 'RegisterAction',
412             'OpenidloginAction', 'FBConnectLoginAction');
413
414         if (in_array($action_name, $login_actions)) {
415             $nav = new FBCLoginGroupNav($action);
416             $nav->show();
417             return false;
418         }
419
420         $connect_actions = array('SmssettingsAction', 'ImsettingsAction',
421             'TwittersettingsAction', 'FBConnectSettingsAction');
422
423         if (in_array($action_name, $connect_actions)) {
424             $nav = new FBCSettingsNav($action);
425             $nav->show();
426             return false;
427         }
428
429         return true;
430     }
431
432     /**
433      * Have the logout process do some Facebook Connect cookie cleanup
434      *
435      * @param Action $action the current action
436      *
437      * @return void
438      */
439
440     function onStartLogout($action)
441     {
442         $action->logout();
443         $fbuid = $this->loggedIn();
444
445         if (!empty($fbuid)) {
446             try {
447                 $facebook = getFacebook();
448                 $facebook->expire_session();
449             } catch (Exception $e) {
450                 common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
451                            'Could\'t logout of Facebook: ' .
452                            $e->getMessage());
453             }
454         }
455
456         return true;
457     }
458
459     /**
460      * Get the URL of the user's Facebook avatar
461      *
462      * @param int $fbuid the Facebook user ID
463      *
464      * @return string $url the url for the user's Facebook avatar
465      */
466
467     function getProfilePicURL($fbuid)
468     {
469         $facebook = getFacebook();
470         $url      = null;
471
472         try {
473
474             $fqry = 'SELECT pic_square FROM user WHERE uid = %s';
475
476             $result = $facebook->api_client->fql_query(sprintf($fqry, $fbuid));
477
478             if (!empty($result)) {
479                 $url = $result[0]['pic_square'];
480             }
481
482         } catch (Exception $e) {
483             common_log(LOG_WARNING, 'Facebook Connect Plugin - ' .
484                        "Facebook client failure requesting profile pic!");
485         }
486
487         return $url;
488     }
489
490     /**
491      * Add a Facebook queue item for each notice
492      *
493      * @param Notice $notice      the notice
494      * @param array  &$transports the list of transports (queues)
495      *
496      * @return boolean hook return
497      */
498
499     function onStartEnqueueNotice($notice, &$transports)
500     {
501         array_push($transports, 'facebook');
502         return true;
503     }
504
505     /**
506      * broadcast the message when not using queuehandler
507      *
508      * @param Notice &$notice the notice
509      * @param array  $queue   destination queue
510      *
511      * @return boolean hook return
512      */
513
514     function onUnqueueHandleNotice(&$notice, $queue)
515     {
516         if (($queue == 'facebook') && ($this->_isLocal($notice))) {
517             facebookBroadcastNotice($notice);
518             return false;
519         }
520         return true;
521     }
522
523     /**
524      * Determine whether the notice was locally created
525      *
526      * @param Notice $notice the notice
527      *
528      * @return boolean locality
529      */
530
531     function _isLocal($notice)
532     {
533         return ($notice->is_local == Notice::LOCAL_PUBLIC ||
534                 $notice->is_local == Notice::LOCAL_NONPUBLIC);
535     }
536
537     /**
538      * Add Facebook queuehandler to the list of daemons to start
539      *
540      * @param array $daemons the list fo daemons to run
541      *
542      * @return boolean hook return
543      *
544      */
545
546     function onGetValidDaemons($daemons)
547     {
548         array_push($daemons, INSTALLDIR .
549                    '/plugins/Facebook/facebookqueuehandler.php');
550         return true;
551     }
552
553 }