| 1 |
<?php
|
| 2 |
// $Id: fb.module,v 1.55 2009/10/27 20:39:52 yogadex Exp $
|
| 3 |
|
| 4 |
// hook_fb
|
| 5 |
define('FB_HOOK', 'fb');
|
| 6 |
|
| 7 |
// Ops for hook_fb.
|
| 8 |
define('FB_OP_GET_APP', 'get_app'); // Load data from a known app
|
| 9 |
define('FB_OP_GET_ALL_APPS', 'get_all_apps'); // Load data about all apps
|
| 10 |
|
| 11 |
define('FB_OP_CURRENT_APP', 'current_app'); // determine active app in canvas page or facebook connect
|
| 12 |
define('FB_OP_INITIALIZE', 'init'); //
|
| 13 |
define('FB_OP_POST_INIT', 'post init'); //
|
| 14 |
|
| 15 |
define('FB_OP_EXIT', 'exit'); // End an FB callback
|
| 16 |
define('FB_OP_GET_FBU', 'get_fbu'); // Query the local user's FB account
|
| 17 |
define('FB_OP_GET_INFINITE_SESSION', 'get_inf_sess'); // Query infinite session data
|
| 18 |
define('FB_OP_GET_USER_SESSION', 'get_user_sess');
|
| 19 |
|
| 20 |
define('FB_OP_PRE_USER', 'pre_user'); // Before account creation, fb_user.module
|
| 21 |
define('FB_OP_POST_USER', 'post_user'); // After account creation, fb_user.module
|
| 22 |
define('FB_OP_APP_IS_AUTHORIZED', 'app_authorized'); // Invoked if user has authorized an app. Triggers creation of user accounts or authmap entries
|
| 23 |
|
| 24 |
define('FB_OP_SET_PROPERTIES', 'set_props'); // build props for admin.setAppProperties
|
| 25 |
define('FB_OP_LIST_PROPERTIES', 'list_props'); // list of known properties for admin.getAppProperties
|
| 26 |
|
| 27 |
define('FB_OP_CONNECT_JS_INIT', 'fb_connect_js_init'); // A chance to customize fbConnect javascript
|
| 28 |
define('FB_OP_CANVAS_FBJS_INIT', 'fb_canvas_js_init'); // A chance to customize FBJS.
|
| 29 |
|
| 30 |
// node_access realms (belongs here?)
|
| 31 |
define('FB_GRANT_REALM_FRIEND', 'fb_friend');
|
| 32 |
define('FB_GRANT_REALM_GROUP', 'fb_group');
|
| 33 |
|
| 34 |
// When initializing Facebook API, which user to log in as:
|
| 35 |
define('FB_FBU_INFINITE_SESSION', 'fbu_infinite'); // Some APIs still require a session, use no session where possible
|
| 36 |
define('FB_FBU_NO_SESSION', 'fbu_no_session'); // http://wiki.developers.facebook.com/index.php/Category:Sessionless_API
|
| 37 |
|
| 38 |
// NOTE: on Connect Pages, using anything other than FB_FBU_CURRENT will cause cookies to be set which cause problems on subsequent pages. So only use something other than FB_FBU_CURRENT if you absolutely must!
|
| 39 |
|
| 40 |
define('FB_FBU_CURRENT', 'fbu_current'); // Canvas pages and Connect pages
|
| 41 |
define('FB_FBU_ANY', 'fbu_any'); // Use current user on canvas page, fall back to infinite session otherwise.
|
| 42 |
|
| 43 |
//// Constants for internal use
|
| 44 |
define('FB_APP_CURRENT', '000_app_current'); // Canvas pages only. 000 makes it appear first in options list
|
| 45 |
|
| 46 |
/**
|
| 47 |
* Implementation of hook_init
|
| 48 |
*
|
| 49 |
* Determines whether we are servicing a Facebook App request.
|
| 50 |
*
|
| 51 |
* We invoke our hook, first to determine which application is being invoked.
|
| 52 |
* (Because we support more than one in the same Drupal instance.) Then, we
|
| 53 |
* notify interested modules in various events.
|
| 54 |
*
|
| 55 |
*/
|
| 56 |
function fb_init() {
|
| 57 |
global $fb, $fb_app; // Set by this function.
|
| 58 |
|
| 59 |
// http://drupal.org/node/329810
|
| 60 |
if (!function_exists('arg')) {
|
| 61 |
// Ensure arg function is defined.
|
| 62 |
drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
|
| 63 |
}
|
| 64 |
|
| 65 |
if (arg(0) != 'admin') {
|
| 66 |
// Session sanity check. This is relevant on iframe pages when user once
|
| 67 |
// had the app added, but has since removed it. This is a bit of a hack
|
| 68 |
// because we rely on the fb_... params passed by facebook.
|
| 69 |
if (isset($_SESSION['fb_frame_params']) &&
|
| 70 |
$_REQUEST['fb_sig']) {
|
| 71 |
if ($_REQUEST['fb_sig_session_key'] != $_SESSION['fb_frame_params']['session_key']) {
|
| 72 |
// The session key from facebook has changed. Treat this as a new session.
|
| 73 |
$req = array();
|
| 74 |
foreach ($_REQUEST as $key => $val) {
|
| 75 |
if (strpos($key, 'fb_') === 0)
|
| 76 |
$req[] = $key . '=' . $val;
|
| 77 |
}
|
| 78 |
$url = url($_REQUEST['q'], array('query' => $req, 'absolute' => TRUE));
|
| 79 |
if (fb_verbose())
|
| 80 |
watchdog('fb_debug', 'Facebook session key was %old, now %new. Destroying session and sending user to %url.',
|
| 81 |
array('%old' => $_SESSION['fb_frame_params']['session_key'],
|
| 82 |
'%new' => $_REQUEST['fb_sig_session_key'],
|
| 83 |
'%url' => $url));
|
| 84 |
|
| 85 |
session_destroy();
|
| 86 |
drupal_goto($url);
|
| 87 |
}
|
| 88 |
}
|
| 89 |
}
|
| 90 |
|
| 91 |
// Purge out-of-date session info
|
| 92 |
if (isset($_SESSION['fb_frame_params']) &&
|
| 93 |
$_SESSION['fb_frame_params']['expires'] &&
|
| 94 |
$_SESSION['fb_frame_params']['expires'] < time()) {
|
| 95 |
unset($_SESSION['fb_frame_params']);
|
| 96 |
}
|
| 97 |
|
| 98 |
// Perform sanity check, help users who skip the README.
|
| 99 |
if (!variable_get('fb_settings_check', FALSE) && user_access('access administration pages')) {
|
| 100 |
drupal_set_message(t('!drupal_for_facebook has been enabled, but not properly installed. Please read the !readme.',
|
| 101 |
array('!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'),
|
| 102 |
// This link should work with clean URLs
|
| 103 |
// disabled.
|
| 104 |
'!readme' => '<a href='. base_path() . drupal_get_path('module', 'fb') .'/README.txt>README.txt</a>')), 'error');
|
| 105 |
}
|
| 106 |
|
| 107 |
// Ask other modules for app detail
|
| 108 |
$fb_app = fb_invoke(FB_OP_CURRENT_APP);
|
| 109 |
|
| 110 |
if ($fb_app) {
|
| 111 |
// we are in a callback
|
| 112 |
|
| 113 |
// For canvas pages, use current user, never infinite session.
|
| 114 |
$fb = fb_api_init($fb_app, FB_FBU_CURRENT);
|
| 115 |
|
| 116 |
if ($fb) {
|
| 117 |
// Give other modules a chance to initialize, require login, etc...
|
| 118 |
fb_invoke(FB_OP_INITIALIZE, array('fb_app' => $fb_app,
|
| 119 |
'fb' => $fb));
|
| 120 |
|
| 121 |
// See if the facebook user id is known
|
| 122 |
if ($fbu = $fb->get_loggedin_user()) {
|
| 123 |
fb_invoke(FB_OP_APP_IS_AUTHORIZED, array('fb_app' => $fb_app,
|
| 124 |
'fb' => $fb,
|
| 125 |
'fbu' => $fbu));
|
| 126 |
}
|
| 127 |
}
|
| 128 |
else
|
| 129 |
watchdog('fb', "URL indicates a facebook app, but could not initialize Facebook", array(), WATCHDOG_ERROR);
|
| 130 |
|
| 131 |
}
|
| 132 |
|
| 133 |
fb_invoke(FB_OP_POST_INIT, array('fb_app' => $fb_app,
|
| 134 |
'fb' => $fb));
|
| 135 |
}
|
| 136 |
|
| 137 |
|
| 138 |
|
| 139 |
/**
|
| 140 |
* Include the necessary facebook-platform code and initialize the API object.
|
| 141 |
*
|
| 142 |
* @param fbu To log into facebook as a particular user, pass the facebook id.
|
| 143 |
* This is useful during cron jobs, for example, but rarely if ever needed on
|
| 144 |
* a canvas page. If no valid session key is known for the user, this call
|
| 145 |
* will still return a facebook reference.
|
| 146 |
*
|
| 147 |
* If FB_FBU_INFINITE_SESSION is passed, we attempt to log into an infinite
|
| 148 |
* session we've configured.
|
| 149 |
*
|
| 150 |
* If FB_FBU_ANY is passed in, we will log in as the canvas page user if
|
| 151 |
* already logged in. Otherwise we try the infinite session, if configured.
|
| 152 |
*
|
| 153 |
* Future calls into the the facebook api could fail for various reasons. For
|
| 154 |
* one, we may fail to log in as the specified user. This call does not
|
| 155 |
* actually contact facebook to test the connection, it just sets things up so
|
| 156 |
* that when the connection is needed, it might work. Even if the connection
|
| 157 |
* is established, the user may not have sufficient permission to whatever you
|
| 158 |
* are asking facebook to do.
|
| 159 |
*
|
| 160 |
*/
|
| 161 |
function fb_api_init($fb_app, $fbu = FB_FBU_CURRENT) {
|
| 162 |
//dpm(func_get_args(), "fb_api_init");
|
| 163 |
//$trace = debug_backtrace();
|
| 164 |
//dpm($trace, "fb_api_init call stack");
|
| 165 |
|
| 166 |
static $cache = array();
|
| 167 |
|
| 168 |
// This helps with uncaught exceptions. However, it should be configurable
|
| 169 |
// or at least not overwrite previously declared handler.
|
| 170 |
set_exception_handler('fb_handle_exception');
|
| 171 |
|
| 172 |
if (!count($cache)) {
|
| 173 |
$filename = variable_get('fb_api_file', 'facebook-platform/php/facebook.php');
|
| 174 |
if (!include($filename)) {
|
| 175 |
$message = t('Failed to find the Facebook client libraries at %filename. Read the !readme and follow the instructions carefully.',
|
| 176 |
array('!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'),
|
| 177 |
// This link should work with clean URLs disabled.
|
| 178 |
'!readme' => '<a href='.base_path() . '/' . drupal_get_path('module', 'fb') . '/README.txt>README.txt</a>',
|
| 179 |
'%filename' => $filename,
|
| 180 |
));
|
| 181 |
drupal_set_message($message, 'error');
|
| 182 |
watchdog('fb', $message);
|
| 183 |
return NULL;
|
| 184 |
}
|
| 185 |
}
|
| 186 |
|
| 187 |
global $facebook_config;
|
| 188 |
$facebook_config['debug'] = variable_get('fb_debug', FALSE); // set TRUE for debug output from FB API
|
| 189 |
|
| 190 |
if (!$cache[$fb_app->apikey]) {
|
| 191 |
$cache[$fb_app->apikey] = array();
|
| 192 |
}
|
| 193 |
$fbu_orig = $fbu;
|
| 194 |
|
| 195 |
// Determine the actual facebook user id to use.
|
| 196 |
if ($GLOBALS['fb'] && $GLOBALS['fb']->api_key == $fb_app->apikey &&
|
| 197 |
($fbu == FB_FBU_CURRENT || $fbu == FB_FBU_ANY ||
|
| 198 |
$fbu == $GLOBALS['fb']->get_loggedin_user())) {
|
| 199 |
return $GLOBALS['fb'];
|
| 200 |
}
|
| 201 |
else if ($GLOBALS['fb']->api_key == $fb_app->apikey &&
|
| 202 |
($fbu == FB_FBU_CURRENT || $fbu == FB_FBU_ANY)
|
| 203 |
&& isset($_SESSION['fb_frame_params']) && !$_REQUEST['fb_sig']) {
|
| 204 |
$params = $_SESSION['fb_frame_params'];
|
| 205 |
$fbu = $params['user'];
|
| 206 |
$session = $params['session_key'];
|
| 207 |
}
|
| 208 |
else if ($GLOBALS['fb'] && $GLOBALS['fb']->api_key == $fb_app->apikey &&
|
| 209 |
$fbu == FB_FBU_CURRENT) {
|
| 210 |
// No current user to use, probably anonymous canvas page.
|
| 211 |
return $GLOBALS['fb'];
|
| 212 |
}
|
| 213 |
else if ($fbu == FB_FBU_INFINITE_SESSION || $fbu == FB_FBU_ANY) {
|
| 214 |
// Learn the infinite session id and key
|
| 215 |
$data = fb_invoke(FB_OP_GET_INFINITE_SESSION, array('fb_app' => $fb_app,
|
| 216 |
'fb' => $GLOBALS['fb']),
|
| 217 |
array());
|
| 218 |
$fbu = $data[0];
|
| 219 |
$session = $data[1];
|
| 220 |
}
|
| 221 |
else if ($fbu != FB_FBU_NO_SESSION) {
|
| 222 |
// FB user id passed in. If we happen to have valid session info for
|
| 223 |
// them, we can log in as them.
|
| 224 |
$data = fb_invoke(FB_OP_GET_USER_SESSION,
|
| 225 |
array('fb_app' => $fb_app,
|
| 226 |
'fb' => $fb,
|
| 227 |
'fbu' => $fbu),
|
| 228 |
array());
|
| 229 |
|
| 230 |
if (count($data) && $data[0] && $data[1]) {
|
| 231 |
$fbu = $data[0];
|
| 232 |
$session = $data[1];
|
| 233 |
}
|
| 234 |
}
|
| 235 |
|
| 236 |
if (!$cache[$fb_app->apikey][$fbu]) {
|
| 237 |
// We don't have a cached resource for this app/user combo, so we're going to create one.
|
| 238 |
$fb = new Facebook($fb_app->apikey, $fb_app->secret);
|
| 239 |
|
| 240 |
// Iframes may link or redirect within the iframe, in which case facebook
|
| 241 |
// will not provide all its normal variables. To prepare for such a case,
|
| 242 |
// we cache the facebook parameters in the session.
|
| 243 |
// We update the cache whenever possible, to keep it current.
|
| 244 |
if ($fb->in_frame() && $fb->fb_params) {
|
| 245 |
$_SESSION['fb_frame_params'] = $fb->fb_params; // hacky to use fb's internal data structure
|
| 246 |
}
|
| 247 |
|
| 248 |
// If we learned the session, above, use it
|
| 249 |
if ($fbu && $session) {
|
| 250 |
$fb->set_user($fbu, $session);
|
| 251 |
}
|
| 252 |
else if ($fbu && $fbu == $fb->get_loggedin_user()) {
|
| 253 |
// Canvas page or Connect page, current user is logged in already.
|
| 254 |
// Nothing to do here.
|
| 255 |
}
|
| 256 |
else if ($fbu == FB_FBU_NO_SESSION) {
|
| 257 |
$fb->set_user(NULL, NULL);
|
| 258 |
}
|
| 259 |
|
| 260 |
// Cache the result, in case we're called again.
|
| 261 |
$cache[$fb_app->apikey][$fbu] = $fb;
|
| 262 |
|
| 263 |
// Note that facebook api has not actually logged into facebook yet.
|
| 264 |
// We won't really know if our session is valid until later.
|
| 265 |
// get_loggedin_user does not really test it.
|
| 266 |
if ($fbu_orig != FB_FBU_NO_SESSION &&
|
| 267 |
$fbu_orig != FB_FBU_CURRENT && !$fb->get_loggedin_user()) {
|
| 268 |
// An FBU other than CURRENT was specified, but we failed to log in.
|
| 269 |
watchdog('fb', 'Failed to log into facebook app %app as user %user',
|
| 270 |
array('%app' => $fb_app->title,
|
| 271 |
'%user' => $fbu_orig), WATCHDOG_ERROR);
|
| 272 |
}
|
| 273 |
}
|
| 274 |
|
| 275 |
$fb = $cache[$fb_app->apikey][$fbu];
|
| 276 |
return $fb;
|
| 277 |
}
|
| 278 |
|
| 279 |
/**
|
| 280 |
* Wrapper function for fb_api_init. This helps for functions that should
|
| 281 |
* work whether or not we are on a canvas page. For canvas pages, the active
|
| 282 |
* fb object is used. For non-canvas pages, it will initialize the API using
|
| 283 |
* an infinite session, if configured.
|
| 284 |
*
|
| 285 |
* @param $fb_app Note this is ignored on canvas pages.
|
| 286 |
*
|
| 287 |
* This is for internal use. Third party modules use fb_api_init().
|
| 288 |
*/
|
| 289 |
function _fb_api_init($fb_app = NULL) {
|
| 290 |
$fb = $GLOBALS['fb']; // Default to active app on canvas pages
|
| 291 |
if (!$fb && $fb_app)
|
| 292 |
// Otherwise, log into facebook api.
|
| 293 |
$fb = fb_api_init($fb_app, FB_FBU_ANY);
|
| 294 |
|
| 295 |
if (!$fb) {
|
| 296 |
watchdog('fb', '%function unable to initialize Facebook API.',
|
| 297 |
array('%function' => '_fb_api_init()'), WATCHDOG_ERROR);
|
| 298 |
return;
|
| 299 |
}
|
| 300 |
else
|
| 301 |
return $fb;
|
| 302 |
}
|
| 303 |
|
| 304 |
/**
|
| 305 |
* Sometimes calls to fb_api_init succeed, but calls to the client api
|
| 306 |
* will fail because cookies are obsolete or what have you. This
|
| 307 |
* function makes a call to facebook to test the session. Expensive,
|
| 308 |
* so use only when necessary.
|
| 309 |
*/
|
| 310 |
function fb_api_check_session($fb) {
|
| 311 |
// TODO: caching
|
| 312 |
$success = FALSE;
|
| 313 |
try {
|
| 314 |
$is_user = $fb->api_client->users_isAppUser();
|
| 315 |
// Does not matter what is returned, as long as exception is not thrown.
|
| 316 |
$success = TRUE;
|
| 317 |
}
|
| 318 |
catch (Exception $e) {
|
| 319 |
$success = FALSE;
|
| 320 |
}
|
| 321 |
return $success;
|
| 322 |
}
|
| 323 |
|
| 324 |
/**
|
| 325 |
* Returns the facebook user id currently visiting a canvas page, or if set_user has been called.
|
| 326 |
* Unlike fb_get_fbu(), works only on canvas pages or when infinite session has been initialized.
|
| 327 |
*/
|
| 328 |
function fb_facebook_user($fb = NULL) {
|
| 329 |
if (!isset($fb))
|
| 330 |
$fb = $GLOBALS['fb'];
|
| 331 |
|
| 332 |
if (!$fb)
|
| 333 |
return;
|
| 334 |
|
| 335 |
$fbu = $fb->get_loggedin_user();
|
| 336 |
if ($fb->api_client->error_code) {
|
| 337 |
if (fb_verbose()) {
|
| 338 |
watchdog('fb', 'Failed to get Facebook user id. detail: !detail',
|
| 339 |
array('!detail' => print_r($_REQUEST, 1)), WATCHDOG_ERROR);
|
| 340 |
}
|
| 341 |
}
|
| 342 |
return $fbu;
|
| 343 |
}
|
| 344 |
|
| 345 |
/**
|
| 346 |
* Facebook provides a method, users_isAppUser(), which is buggy and
|
| 347 |
* unreliable. So we need to implement our own.
|
| 348 |
*/
|
| 349 |
function fb_is_app_user($fb) {
|
| 350 |
if ($fb->api_client->added || $fb->api_client->is_user)
|
| 351 |
return TRUE;
|
| 352 |
else
|
| 353 |
return $fb->api_client->users_isAppUser;
|
| 354 |
}
|
| 355 |
|
| 356 |
|
| 357 |
/**
|
| 358 |
* Given a local user id, find the facebook id.
|
| 359 |
*/
|
| 360 |
function fb_get_fbu($uid, $fb_app = NULL) {
|
| 361 |
// default to current app (only set if we're in a FB callback)
|
| 362 |
if (!$fb_app)
|
| 363 |
$fb_app = $GLOBALS['fb_app'];
|
| 364 |
|
| 365 |
// Accept either a user object or uid passed in.
|
| 366 |
if (is_object($uid) && $uid->fbu)
|
| 367 |
return $uid->fbu;
|
| 368 |
else if (is_object($uid))
|
| 369 |
$uid = $uid->uid;
|
| 370 |
|
| 371 |
// User management is handled by another module. Use our hook to ask for mapping.
|
| 372 |
$fbu = fb_invoke(FB_OP_GET_FBU, array('fb_app' => $fb_app,
|
| 373 |
'uid' => $uid,
|
| 374 |
'fb' => $GLOBALS['fb']));
|
| 375 |
|
| 376 |
return $fbu;
|
| 377 |
}
|
| 378 |
|
| 379 |
/**
|
| 380 |
* Convenience method to get an apps callback URL.
|
| 381 |
*
|
| 382 |
* TODO: This code will probably not handle every case and may need some work.
|
| 383 |
*/
|
| 384 |
function fb_get_callback_url($fb_app) {
|
| 385 |
$url = url('', array('absolute' => TRUE)) . FB_SETTINGS_APP_NID . '/'. $fb_app->nid . '/';
|
| 386 |
return $url;
|
| 387 |
}
|
| 388 |
|
| 389 |
/**
|
| 390 |
* Convenience method to get app info based on apikey or nid.
|
| 391 |
*/
|
| 392 |
function fb_get_app($search_data) {
|
| 393 |
// $search_data can be an apikey, or an array of other search params.
|
| 394 |
if (!is_array($search_data))
|
| 395 |
$search_data = array('apikey' => $search_data);
|
| 396 |
|
| 397 |
$fb_app = fb_invoke(FB_OP_GET_APP, $search_data);
|
| 398 |
return $fb_app;
|
| 399 |
}
|
| 400 |
|
| 401 |
/**
|
| 402 |
* Convenience method to return a list of all known apps, suitable for form
|
| 403 |
* elements.
|
| 404 |
*/
|
| 405 |
function fb_get_app_options($include_current = FALSE) {
|
| 406 |
$apps = fb_get_all_apps();
|
| 407 |
$options = array();
|
| 408 |
if ($include_current)
|
| 409 |
$options[FB_APP_CURRENT] = t('<current>');
|
| 410 |
foreach ($apps as $app) {
|
| 411 |
$options[$app->nid] = $app->title;
|
| 412 |
}
|
| 413 |
return $options;
|
| 414 |
}
|
| 415 |
|
| 416 |
/**
|
| 417 |
* Convenience method to return array of all know fb_apps.
|
| 418 |
*/
|
| 419 |
function fb_get_all_apps() {
|
| 420 |
$apps = fb_invoke(FB_OP_GET_ALL_APPS, NULL, array());
|
| 421 |
return $apps;
|
| 422 |
}
|
| 423 |
|
| 424 |
/**
|
| 425 |
* A convenience method for returning a list of facebook friends.
|
| 426 |
*
|
| 427 |
* This should work efficiently in canvas pages for finding friends of
|
| 428 |
* the current user. In other cases it tries to work, but will be an
|
| 429 |
* expensive operation and only succeed when the user is logged in via
|
| 430 |
* Connect, or has created an infinite session.
|
| 431 |
*
|
| 432 |
* @return: an array of facebook ids
|
| 433 |
*/
|
| 434 |
function fb_get_friends($fbu, $fb_app = NULL) {
|
| 435 |
static $cache = array();
|
| 436 |
if (!$fb_app)
|
| 437 |
$fb_app = $GLOBALS['fb_app'];
|
| 438 |
|
| 439 |
// Facebook only allows us to query the current user's friends, so let's try
|
| 440 |
// to log in as that user. It will only actually work if they are the
|
| 441 |
// current user of a canvas page, or they've signed up for an infinite
|
| 442 |
// session.
|
| 443 |
$fb = fb_api_init($fb_app, $fbu);
|
| 444 |
if (!$fb || !$fbu)
|
| 445 |
return;
|
| 446 |
|
| 447 |
if (!isset($cache[$fbu])) {
|
| 448 |
if ($fb === $GLOBALS['fb'] &&
|
| 449 |
$fbu == fb_facebook_user($fb))
|
| 450 |
$items = $fb->api_client->friends_get();
|
| 451 |
// friends_get does not work in cron call, so we double check.
|
| 452 |
if (!$items || !count($items)) {
|
| 453 |
$logged_in = fb_facebook_user($fb);
|
| 454 |
$query = "SELECT uid2 FROM friend WHERE uid1=$fbu";
|
| 455 |
$result = $fb->api_client->fql_query($query);
|
| 456 |
fb_report_errors($fb);
|
| 457 |
|
| 458 |
$items = array();
|
| 459 |
if (is_array($result))
|
| 460 |
foreach ($result as $data) {
|
| 461 |
$items[] = $data['uid2'];
|
| 462 |
}
|
| 463 |
}
|
| 464 |
// Facebook's API has the annoying habit of returning an item even if user
|
| 465 |
// has no friends. We need to clean that up.
|
| 466 |
if (!$items[0])
|
| 467 |
unset($items[0]);
|
| 468 |
|
| 469 |
$cache[$fbu] = $items;
|
| 470 |
}
|
| 471 |
|
| 472 |
return $cache[$fbu];
|
| 473 |
}
|
| 474 |
|
| 475 |
// Return array of facebook gids
|
| 476 |
function fb_get_groups($fbu, $fb_app = NULL) {
|
| 477 |
$items = array();
|
| 478 |
$groups = fb_get_groups_data($fbu);
|
| 479 |
|
| 480 |
if ($groups && count($groups))
|
| 481 |
foreach ($groups as $data) {
|
| 482 |
$items[] = $data['gid'];
|
| 483 |
}
|
| 484 |
return $items;
|
| 485 |
}
|
| 486 |
|
| 487 |
function fb_get_groups_data($fbu, $fb_app = NULL) {
|
| 488 |
static $cache = array();
|
| 489 |
|
| 490 |
$fb = _fb_api_init($fb_app);
|
| 491 |
if (!$fb || !$fbu)
|
| 492 |
return;
|
| 493 |
|
| 494 |
if (!isset($cache[$fbu])) {
|
| 495 |
$cache[$fbu] = $fb->api_client->groups_get($fbu, NULL);
|
| 496 |
}
|
| 497 |
|
| 498 |
return $cache[$fbu];
|
| 499 |
}
|
| 500 |
|
| 501 |
|
| 502 |
// deprecated since creation of fb_user module, but cron hook still uses this.
|
| 503 |
function fb_user_load($fbu = NULL) {
|
| 504 |
global $user;
|
| 505 |
if (!$fbu)
|
| 506 |
// default to current logged in user
|
| 507 |
$fbu = fb_facebook_user();
|
| 508 |
if ($fbu && $user->fbu == $fbu) {
|
| 509 |
return $user;
|
| 510 |
}
|
| 511 |
if ($fbu) {
|
| 512 |
$account = user_external_load("$fbu-$fb_app->apikey@facebook.com");
|
| 513 |
if (!$account)
|
| 514 |
$account = user_external_load("$fbu@facebook.com");
|
| 515 |
if (!$account)
|
| 516 |
$account = user_load(array('uid' => variable_get('fb_facebook_user', 2)));
|
| 517 |
if (!$account)
|
| 518 |
watchdog('fb', 'Failed to load user from facebook fbu=%fbu',
|
| 519 |
array('%fbu' => $fbu), WATCHDOG_ERROR);
|
| 520 |
$account->fbu = $fbu;
|
| 521 |
return $account;
|
| 522 |
}
|
| 523 |
}
|
| 524 |
|
| 525 |
|
| 526 |
function fb_form_alter(&$form, &$form_state, $form_id) {
|
| 527 |
// Because facebook users don't have email, it can't be required on user form
|
| 528 |
if ($form_id == 'user_register') {
|
| 529 |
if (user_access('administer users')) {
|
| 530 |
$form['mail']['#required'] = FALSE;
|
| 531 |
}
|
| 532 |
}
|
| 533 |
if ($form_id == 'user_edit') {
|
| 534 |
if (user_access('administer users')) {
|
| 535 |
$form['account']['mail']['#required'] = FALSE;
|
| 536 |
}
|
| 537 |
}
|
| 538 |
}
|
| 539 |
|
| 540 |
|
| 541 |
function fb_menu() {
|
| 542 |
$items = array();
|
| 543 |
|
| 544 |
// When forms are submitted directly to us, we cache the results,
|
| 545 |
// and show them later via this callback
|
| 546 |
$items['fb/form_cache'] = array(
|
| 547 |
'page callback' => '_fb_form_cache_cb',
|
| 548 |
'type' => MENU_CALLBACK,
|
| 549 |
'access callback' => TRUE);
|
| 550 |
|
| 551 |
return $items;
|
| 552 |
}
|
| 553 |
|
| 554 |
/**
|
| 555 |
* When exiting we need to do some special stuff for forms
|
| 556 |
*/
|
| 557 |
function fb_exit($destination = NULL) {
|
| 558 |
global $fb_app, $fb;
|
| 559 |
if ($fb_app && $fb) {
|
| 560 |
fb_invoke(FB_OP_EXIT, array('fb_app' => $fb_app,
|
| 561 |
'fb' => $GLOBALS['fb']),
|
| 562 |
$destination);
|
| 563 |
}
|
| 564 |
}
|
| 565 |
|
| 566 |
function _fb_form_cache_cb($cid) {
|
| 567 |
// Facebook started appending a '?', we need to get rid of it.
|
| 568 |
if ($pos = strpos($cid, '?'))
|
| 569 |
$cid = substr($cid, 0, $pos);
|
| 570 |
|
| 571 |
watchdog('fb', "Returning cached form page $cid");
|
| 572 |
$cache = cache_get($cid, 'cache_page');
|
| 573 |
// Don't clear, as user may refresh browser. Cache will expire eventually.
|
| 574 |
// cache_clear_all($cid, 'cache_page');
|
| 575 |
print $cache->data;
|
| 576 |
exit();
|
| 577 |
}
|
| 578 |
|
| 579 |
function fb_session_key_form() {
|
| 580 |
global $fb_app;
|
| 581 |
$form = array('auth_token' => array('#type' => 'textfield',
|
| 582 |
'#title' => t('One-time code'),
|
| 583 |
'#description' => t('If you do not have a one-time code, you can get one !here.',
|
| 584 |
array('!here' => l(t('here'), 'http://www.facebook.com/code_gen.php?v=1.0&api_key='.$fb_app->apikey))),
|
| 585 |
),
|
| 586 |
'submit' => array('#type' => 'submit',
|
| 587 |
'#value' => t('Submit')),
|
| 588 |
|
| 589 |
'#redirect' => FALSE, /* necessary when submitting via facebook */
|
| 590 |
);
|
| 591 |
return $form;
|
| 592 |
}
|
| 593 |
|
| 594 |
/**
|
| 595 |
* Invoke hook_fb.
|
| 596 |
*/
|
| 597 |
function fb_invoke($op, $data = NULL, $return = NULL) {
|
| 598 |
foreach (module_implements(FB_HOOK) as $name) {
|
| 599 |
$function = $name . '_' . FB_HOOK;
|
| 600 |
try {
|
| 601 |
$function($op, $data, $return);
|
| 602 |
}
|
| 603 |
catch (Exception $e) {
|
| 604 |
fb_log_exception($e, t('Exception calling %function(%op)',
|
| 605 |
array('%function' => $function,
|
| 606 |
'%op' => $op)));
|
| 607 |
}
|
| 608 |
}
|
| 609 |
return $return;
|
| 610 |
}
|
| 611 |
|
| 612 |
/**
|
| 613 |
* This method will clean up URLs. When serving canvas pages, extra
|
| 614 |
* information is included in URLs (see fb_settings.inc). This will remove
|
| 615 |
* the extra information.
|
| 616 |
*/
|
| 617 |
function fb_scrub_urls($content) {
|
| 618 |
foreach (array(FB_SETTINGS_APP_NID, FB_SETTINGS_PAGE_TYPE) as $key) {
|
| 619 |
$patterns[] = "|$key/[^/]*/|";
|
| 620 |
$replacements[] = "";
|
| 621 |
}
|
| 622 |
$content = preg_replace($patterns, $replacements, $content);
|
| 623 |
return $content;
|
| 624 |
}
|
| 625 |
|
| 626 |
/**
|
| 627 |
* Implementation of hook_node_grants.
|
| 628 |
*
|
| 629 |
* We put this here so all facebook modules have a standard way to implement
|
| 630 |
* hook_node_access_records. They use that hook to insert records into the
|
| 631 |
* node access table. We use this hook to allow access when the grants are
|
| 632 |
* there.
|
| 633 |
*
|
| 634 |
* DEPRECATED. Not sure where, if anywhere, this belongs.
|
| 635 |
*/
|
| 636 |
function fb_node_grantsXXX($account, $op) {
|
| 637 |
$grants = array();
|
| 638 |
$fbu = fb_get_fbu($account);
|
| 639 |
if ($fbu) { // If not anonymous (facebook user not logged in to this app)
|
| 640 |
$friends = fb_get_friends($fbu);
|
| 641 |
// For access purposes, consider a users to be a friend of themself
|
| 642 |
$friends[] = $fbu;
|
| 643 |
if (count($friends))
|
| 644 |
$grants[FB_GRANT_REALM_FRIEND] = $friends;
|
| 645 |
|
| 646 |
$groups = fb_get_groups($fbu);
|
| 647 |
if (count($groups))
|
| 648 |
$grants[FB_GRANT_REALM_GROUP] = $groups;
|
| 649 |
}
|
| 650 |
|
| 651 |
return $grants;
|
| 652 |
}
|
| 653 |
|
| 654 |
/**
|
| 655 |
* Convenience method for displaying facebook api errors.
|
| 656 |
*/
|
| 657 |
function fb_report_errors($fb = FB_APP_CURRENT, $message = NULL) {
|
| 658 |
if ($fb == FB_APP_CURRENT) {
|
| 659 |
$fb = $GLOBALS['fb'];
|
| 660 |
}
|
| 661 |
if ($fb) {
|
| 662 |
if ($fb->api_client->error_code) {
|
| 663 |
$message = t('!message Facebook API error %code (see !link).',
|
| 664 |
array('%code' => $fb->api_client->error_code,
|
| 665 |
'!link' => l(t('error codes'), "http://wiki.developers.facebook.com/index.php/Error_codes"),
|
| 666 |
'!message' => $message,
|
| 667 |
));
|
| 668 |
watchdog('fb', $message, array(), WATCHDOG_ERROR);
|
| 669 |
drupal_set_message($message, 'error');
|
| 670 |
}
|
| 671 |
}
|
| 672 |
}
|
| 673 |
|
| 674 |
function fb_log_exception($e, $text = '', $fb = NULL) {
|
| 675 |
if ($text)
|
| 676 |
$message = $text .': '. $e->getMessage();
|
| 677 |
else
|
| 678 |
$message = $e->getMessage();
|
| 679 |
$message .= ' ' . $e->getCode();
|
| 680 |
|
| 681 |
if ($fb) {
|
| 682 |
$message .= '. (' . t('logged into facebook as %fbu', array('%fbu' => $fb->get_loggedin_user())) . ')';
|
| 683 |
}
|
| 684 |
if (fb_verbose()) {
|
| 685 |
$message .= '<pre>' . $e . '</pre>';
|
| 686 |
}
|
| 687 |
watchdog('fb', $message, array(), WATCHDOG_ERROR);
|
| 688 |
if (user_access('administer fb apps')) {
|
| 689 |
drupal_set_message($message, 'error');
|
| 690 |
}
|
| 691 |
}
|
| 692 |
|
| 693 |
/**
|
| 694 |
* Exception handler for PHP5 exceptions.
|
| 695 |
*/
|
| 696 |
function fb_handle_exception($exception) {
|
| 697 |
$message = t('Facebook API exception %message. !trace',
|
| 698 |
array('%message' => $exception->getMessage(),
|
| 699 |
'!trace' => '<pre>'.$exception->getTraceAsString().'</pre>',
|
| 700 |
));
|
| 701 |
watchdog('fb', $message, array(), WATCHDOG_ERROR);
|
| 702 |
//drupal_set_message($message, 'error');
|
| 703 |
print $message;
|
| 704 |
|
| 705 |
print "<pre>\$_REQUEST:\n";
|
| 706 |
print_r($_REQUEST);
|
| 707 |
print "\n\nREQUEST_URI:\n" . $_SERVER['REQUEST_URI'];
|
| 708 |
print "</pre>";
|
| 709 |
|
| 710 |
}
|
| 711 |
|
| 712 |
/**
|
| 713 |
* Helper function for facebook's users_getInfo API.
|
| 714 |
*
|
| 715 |
* This function makes calls to users_getInfo more efficient, by caching
|
| 716 |
* results in the session, so calls do not always require hitting Facebook's
|
| 717 |
* servers.
|
| 718 |
*
|
| 719 |
* @param $oids
|
| 720 |
* Array of facebook object IDs. In this case they should each be a user id.
|
| 721 |
*/
|
| 722 |
function fb_users_getInfo($oids, $fb = NULL, $refresh_cache = FALSE) {
|
| 723 |
if (!$fb) {
|
| 724 |
$fb = $GLOBALS['fb'];
|
| 725 |
}
|
| 726 |
$infos = array();
|
| 727 |
|
| 728 |
if (!is_array($oids))
|
| 729 |
$oids = array();
|
| 730 |
|
| 731 |
if ($fb) {
|
| 732 |
// First try cache
|
| 733 |
if (!$refresh_cache)
|
| 734 |
foreach ($oids as $oid) {
|
| 735 |
if ($info = $_SESSION['fb'][$fb->api_key]['userinfo'][$oid])
|
| 736 |
$infos[] = $info;
|
| 737 |
}
|
| 738 |
if (count($infos) != count($oids)) {
|
| 739 |
// Session cache did not include all users, update the cache.
|
| 740 |
$infos = $fb->api_client->users_getInfo($oids,
|
| 741 |
array('about_me',
|
| 742 |
'affiliations',
|
| 743 |
'name',
|
| 744 |
'is_app_user',
|
| 745 |
'pic',
|
| 746 |
'pic_big',
|
| 747 |
'pic_square',
|
| 748 |
'profile_update_time',
|
| 749 |
'proxied_email',
|
| 750 |
'status',
|
| 751 |
'email_hashes',
|
| 752 |
));
|
| 753 |
// Update cache with recent results.
|
| 754 |
if (is_array($infos)) {
|
| 755 |
foreach($infos as $info) {
|
| 756 |
$_SESSION['fb'][$fb->api_key]['userinfo'][$info['uid']] = $info;
|
| 757 |
}
|
| 758 |
}
|
| 759 |
}
|
| 760 |
|
| 761 |
return $infos;
|
| 762 |
}
|
| 763 |
}
|
| 764 |
|
| 765 |
/**
|
| 766 |
* Helper function for FBJS files.
|
| 767 |
*
|
| 768 |
* Useful for adding Facebook Javascript, which will be incorporated into
|
| 769 |
* canvas pages or profile boxes. When included this way, javascript must be
|
| 770 |
* embedded inline, rather than refer to an external URL. So this function
|
| 771 |
* will actually read a local file and include the contents inline.
|
| 772 |
*/
|
| 773 |
function fb_add_js($filename, $type) {
|
| 774 |
static $cache;
|
| 775 |
if (!$cache) {
|
| 776 |
$cache = array();
|
| 777 |
|
| 778 |
// Add the most basic file we need.
|
| 779 |
$base_file = drupal_get_path('module', 'fb') . '/fb_fbml.js';
|
| 780 |
$base_file .= "?v=".filemtime($base_file);
|
| 781 |
drupal_add_js($base_file, 'module', 'fbml');
|
| 782 |
|
| 783 |
// Add some settings that FBJS code will often need.
|
| 784 |
$baseUrl = url('', array('absolute' => TRUE));
|
| 785 |
drupal_add_js(array('fbjs' => array('baseUrlFb' => $baseUrl,
|
| 786 |
'baseUrl' => fb_scrub_urls($baseUrl),
|
| 787 |
),
|
| 788 |
),
|
| 789 |
'setting', 'fbml');
|
| 790 |
|
| 791 |
}
|
| 792 |
|
| 793 |
if (!$cache[$filename]) {
|
| 794 |
if (file_exists($filename)) {
|
| 795 |
// Refresh facebook's cache when file changes
|
| 796 |
$filename .= "?v=".filemtime($filename);
|
| 797 |
}
|
| 798 |
// 'post_settings' is a hack to make our code come after settings. This is
|
| 799 |
// ugly, but we're doing it because there is no "onready" in FBJS.
|
| 800 |
drupal_add_js($filename, 'post_settings', 'fbml');
|
| 801 |
$cache[$filename] = TRUE;
|
| 802 |
}
|
| 803 |
}
|
| 804 |
|
| 805 |
/**
|
| 806 |
* For debugging, add $conf['fb_verbose'] = TRUE; to settings.php.
|
| 807 |
*/
|
| 808 |
function fb_verbose() {
|
| 809 |
return variable_get('fb_verbose', NULL);
|
| 810 |
}
|
| 811 |
|
| 812 |
/**
|
| 813 |
* Define custom_url_rewrite_inbound() if not defined already.
|
| 814 |
*/
|
| 815 |
if (!function_exists('custom_url_rewrite_inbound')) {
|
| 816 |
function custom_url_rewrite_inbound(&$result, $path, $path_language) {
|
| 817 |
fb_url_inbound_alter($result, $path, $path_language);
|
| 818 |
}
|
| 819 |
}
|
| 820 |
|
| 821 |
/**
|
| 822 |
* Define custom_url_rewrite_outbound() if the url_alter.module is not enabled.
|
| 823 |
*/
|
| 824 |
if (!function_exists('custom_url_rewrite_outbound')) {
|
| 825 |
function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
|
| 826 |
fb_url_outbound_alter($path, $options, $original_path);
|
| 827 |
}
|
| 828 |
}
|
| 829 |
|
| 830 |
/**
|
| 831 |
* Implementation of hook_url_outbound_alter().
|
| 832 |
*/
|
| 833 |
function fb_url_outbound_alter(&$path, &$options, $original_path) {
|
| 834 |
//dpm(func_get_args(), 'fb_settings_url_rewrite_outbound');
|
| 835 |
$pre = '';
|
| 836 |
|
| 837 |
// Prefix each known value to the URL
|
| 838 |
foreach (array_reverse(_fb_settings_url_rewrite_prefixes()) as $prefix) {
|
| 839 |
if ($value = fb_settings($prefix))
|
| 840 |
$pre .= $prefix . '/'. $value . '/';
|
| 841 |
}
|
| 842 |
$path = $pre . $path;
|
| 843 |
|
| 844 |
return $path;
|
| 845 |
}
|
| 846 |
|
| 847 |
/**
|
| 848 |
* Implementation of hook_url_inbound_alter().
|
| 849 |
*
|
| 850 |
* Rewrite URLs for facebook canvas pages, and connect callbacks.
|
| 851 |
*
|
| 852 |
*/
|
| 853 |
function fb_url_inbound_alter(&$result, $path, $path_language){
|
| 854 |
//$origpath = $path;
|
| 855 |
//watchdog('fb_settings', "fb_settings_url_rewrite_inbound($result, $path, $path_language)", array(), WATCHDOG_DEBUG);
|
| 856 |
|
| 857 |
// See if this is a request for us.
|
| 858 |
if (strpos($path, FB_SETTINGS_APP_NID . '/') === 0) {
|
| 859 |
// Too soon for arg() function.
|
| 860 |
$args = explode('/', $path);
|
| 861 |
while (count($args) && in_array($args[0], _fb_settings_url_rewrite_prefixes())) {
|
| 862 |
$key = array_shift($args);
|
| 863 |
$value = array_shift($args);
|
| 864 |
$app_nid = fb_settings($key, $value); // Store for use later.
|
| 865 |
}
|
| 866 |
if ($app_nid = fb_settings(FB_SETTINGS_APP_NID)) {
|
| 867 |
if (count($args)) {
|
| 868 |
$path = implode('/', $args); // remaining args
|
| 869 |
$alias = drupal_lookup_path('source', $path, $path_language); //can't use drupal_get_normal_path, it calls custom_url_rewrite_inbound
|
| 870 |
if ($alias)
|
| 871 |
$path = $alias;
|
| 872 |
}
|
| 873 |
else {
|
| 874 |
// frontpage
|
| 875 |
$path = variable_get('site_frontpage', 'node');
|
| 876 |
$alias = drupal_lookup_path('source', $path, $path_language);
|
| 877 |
if ($alias)
|
| 878 |
$path = $alias;
|
| 879 |
$_REQUEST['destination'] = $path; //required workaround for compatibility with Global Redirect module, best practice?
|
| 880 |
}
|
| 881 |
}
|
| 882 |
}
|
| 883 |
else { //resolve aliases for non-fb-callbacks
|
| 884 |
$alias = drupal_lookup_path('source', $path, $path_language);
|
| 885 |
if ($alias)
|
| 886 |
$path = $alias;
|
| 887 |
}
|
| 888 |
|
| 889 |
$result = $path;
|
| 890 |
}
|
| 891 |
|
| 892 |
/**
|
| 893 |
* This function will be replaced, hopefully, by format_username in D7.
|
| 894 |
*
|
| 895 |
* See http://drupal.org/node/192056
|
| 896 |
*/
|
| 897 |
function fb_format_username($account) {
|
| 898 |
$name = !empty($account->name) ? $account->name : variable_get('anonymous', t('Anonymous'));
|
| 899 |
drupal_alter('username', $name, $account);
|
| 900 |
return $name;
|
| 901 |
}
|
| 902 |
|
| 903 |
/**
|
| 904 |
* hook_username_alter().
|
| 905 |
*
|
| 906 |
* Return a user's facebook name, instead of local username.
|
| 907 |
*/
|
| 908 |
function fb_username_alter(&$name, $account) {
|
| 909 |
//dpm(func_get_args(), "fb_username_alter($name)");
|
| 910 |
if ($account->fbu && ($name == $account->fbu . '@facebook')) {
|
| 911 |
$info = fb_users_getInfo(array($account->fbu));
|
| 912 |
if ($info[0]['name']) {
|
| 913 |
$name = $info[0]['name'];
|
| 914 |
}
|
| 915 |
}
|
| 916 |
}
|
| 917 |
|
| 918 |
?>
|