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