| Commit | Line | Data |
|---|---|---|
| 186f033e DC |
1 | <?php |
| 2 | ||
| 225abd5d DC |
3 | /** |
| 4 | * @file | |
| 5 | * This is the core required module of Drupal for Facebook. | |
| 6 | * | |
| 7 | * @see http://drupal.org/project/fb | |
| 19092d2f | 8 | * |
| 225abd5d DC |
9 | */ |
| 10 | ||
| 8fbcf653 | 11 | // hook_fb |
| 549aa675 DC |
12 | define('FB_HOOK', 'fb'); |
| 13 | ||
| 14 | // Ops for hook_fb. | |
| 19a42c4b | 15 | define('FB_OP_GET_APP', 'get_app'); // Load data from a known app |
| b1c7ed93 | 16 | define('FB_OP_GET_ALL_APPS', 'get_all_apps'); // Load data about all apps |
| 19a42c4b DC |
17 | |
| 18 | define('FB_OP_CURRENT_APP', 'current_app'); // determine active app in canvas page or facebook connect | |
| 19092d2f DC |
19 | define('FB_OP_INITIALIZE', 'init'); // |
| 20 | define('FB_OP_POST_INIT', 'post init'); // | |
| 19a42c4b DC |
21 | |
| 22 | define('FB_OP_EXIT', 'exit'); // End an FB callback | |
| 3a512626 | 23 | define('FB_OP_GET_FBU', 'get_fbu'); // Query the local user's FB account |
| 346e5a9b | 24 | define('FB_OP_GET_UID', 'get_uid'); // Query the facebook user's local account |
| 2b08ca9d | 25 | define('FB_OP_GET_USER_SESSION', 'get_user_sess'); |
| 8fbcf653 | 26 | |
| cd11871d | 27 | define('FB_OP_APP_IS_AUTHORIZED', 'app_authorized'); // Invoked if user has authorized an app. Triggers creation of user accounts or fb_user entries |
| 186f033e | 28 | |
| ebb5649d | 29 | define('FB_OP_JS', 'fb_op_js'); // A chance to inject javascript onto the page. |
| a24a1868 | 30 | define('FB_OP_AJAX_EVENT', 'fb_op_ajax'); // Notification of an event via ajax. |
| ebb5649d | 31 | |
| b24ac6ad DC |
32 | |
| 33 | // Paths. | |
| 34 | define('FB_PATH_ADMIN', 'admin/build/fb'); | |
| 35 | define('FB_PATH_ADMIN_ARGS', 3); // how many args in path. | |
| 36 | define('FB_PATH_ADMIN_APPS', 'admin/build/fb/app'); | |
| 37 | define('FB_PATH_ADMIN_APPS_ARGS', 4); | |
| 38 | define('FB_PATH_AJAX_EVENT', 'fb/ajax'); | |
| 39 | define('FB_PATH_AJAX_EVENT_ARGS', 2); | |
| 40 | ||
| 41 | // permissions | |
| 42 | define('FB_PERM_ADMINISTER', 'administer fb apps'); | |
| 43 | ||
| 44 | // Variables and $conf[] keys. | |
| 45 | define('FB_VAR_LANGUAGE_OVERRIDE', 'fb_language_override'); | |
| 46 | define('FB_VAR_JS_SDK', 'fb_js_sdk'); | |
| 47 | define('FB_VAR_API_FILE', 'fb_api_file'); | |
| 48 | define('FB_VAR_JS_CHANNEL', 'fb_js_channel'); | |
| 49 | define('FB_VAR_VERBOSE', 'fb_verbose'); | |
| 995936db DC |
50 | define('FB_VAR_APIKEY', 'fb_apikey'); // Deprecated. Use FB_VAR_ID |
| 51 | define('FB_VAR_ID', 'fb_id'); | |
| 875843a4 DC |
52 | define('FB_VAR_USE_COOKIE', 'fb_use_cookie'); |
| 53 | define('FB_VAR_USE_SESSION', 'fb_use_session'); | |
| 733004f5 | 54 | define('FB_VAR_RELOAD_APPEND_HASH', 'fb_reload_append_hash'); |
| 49c878eb | 55 | define('FB_VAR_CURL_NOVERIFY', 'fb_curl_noverify'); |
| b24ac6ad | 56 | |
| 549aa675 | 57 | // node_access realms (belongs here?) |
| 186f033e DC |
58 | define('FB_GRANT_REALM_FRIEND', 'fb_friend'); |
| 59 | define('FB_GRANT_REALM_GROUP', 'fb_group'); | |
| 60 | ||
| 245f3cf6 DC |
61 | // 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! |
| 62 | ||
| 92a6e858 | 63 | // @TODO - new libs, are these FBU values still needed??? |
| 245f3cf6 | 64 | define('FB_FBU_CURRENT', 'fbu_current'); // Canvas pages and Connect pages |
| 0f4300e5 | 65 | define('FB_FBU_ANY', 'fbu_any'); // Use current user on canvas page, fall back to infinite session otherwise. |
| 4c0bff53 | 66 | |
| 549aa675 | 67 | //// Constants for internal use |
| 4c0bff53 DC |
68 | define('FB_APP_CURRENT', '000_app_current'); // Canvas pages only. 000 makes it appear first in options list |
| 69 | ||
| 92a6e858 | 70 | |
| 549aa675 | 71 | /** |
| 92a6e858 DC |
72 | * Controls are one way to customize the behavior of Drupal for Facebook modules. |
| 73 | * | |
| 74 | * Controls are stored as an array of flags. Each flag overrides a | |
| 75 | * configurable or built-in behavior. Third-party modules can use this to | |
| 76 | * provide exceptions to otherwise useful behavior. For example see | |
| 77 | * fb_user.module, where this is used to suppress some behavior in off-line | |
| 78 | * mode. | |
| 79 | * | |
| 80 | * Controls take effect not just for the current page request, but also for | |
| 81 | * ajax callbacks generated by the subsequent page. | |
| 82 | * | |
| 83 | * Because ajax controls could be spoofed by a malicious client, flags should | |
| 84 | * not enable any "risky" features. For example, fb_user.module provides a | |
| 85 | * control to suppress the creation of account, but not a control to enable | |
| 86 | * new accounts, as that would be a security risk. | |
| 87 | * | |
| 88 | */ | |
| 89 | function fb_controls($control = NULL, $value = NULL) { | |
| 90 | static $controls; | |
| 91 | if (!isset($controls)) { | |
| 92 | // Initialize. | |
| 93 | if (isset($_REQUEST['fb_controls'])) { | |
| 94 | // Comma separated list passed to ajax calls. | |
| cd64a628 | 95 | foreach (explode(',', $_REQUEST['fb_controls']) as $key) { |
| 92a6e858 DC |
96 | $controls[$key] = TRUE; |
| 97 | } | |
| 98 | } | |
| 99 | else { | |
| 100 | $controls = array(); | |
| 101 | } | |
| 102 | // @TODO - would a drupal_alter() be useful here? | |
| 103 | } | |
| 104 | if (isset($control)) { | |
| 105 | if ($value === FALSE) { | |
| 106 | unset($controls[$control]); | |
| 107 | return; | |
| 108 | } | |
| 109 | elseif ($value === TRUE) | |
| 110 | $controls[$control] = TRUE; | |
| 19092d2f | 111 | |
| e44a9ee0 | 112 | return isset($controls[$control]) ? $controls[$control] : FALSE; // Return requested control. |
| 92a6e858 DC |
113 | } |
| 114 | return array_keys($controls); // Return all controls. | |
| 115 | } | |
| 116 | ||
| 117 | /** | |
| 118 | * Implements hook_init(). | |
| 225abd5d DC |
119 | * |
| 120 | * Initializes facebook's javascript. | |
| 549aa675 | 121 | * Determines whether we are servicing a Facebook App request. |
| 19092d2f | 122 | * |
| 225abd5d DC |
123 | * We invoke our hook, first to determine which application is being invoked |
| 124 | * (because we support more than one in the same Drupal instance). We invoke | |
| 125 | * the hook again to let interested modules know the sdk is initialized. | |
| 19092d2f | 126 | * |
| 549aa675 DC |
127 | */ |
| 128 | function fb_init() { | |
| a46642fe DC |
129 | // Globals provided for internal use and convenience to third-parties. |
| 130 | global $_fb; | |
| 131 | global $_fb_app; | |
| 19092d2f | 132 | |
| 875843a4 | 133 | // fb_settings.inc may have been included in settings.php. If not, include it now. |
| f54b3a2b | 134 | if (!function_exists('fb_settings')) { |
| af716bdd | 135 | module_load_include('inc', 'fb', 'fb_settings'); |
| 225abd5d DC |
136 | // trigger test in fb_devel.module |
| 137 | $GLOBALS['fb_init_no_settings'] = TRUE; | |
| 549aa675 | 138 | } |
| 5d492838 | 139 | |
| f671097b | 140 | // Javascript settings needed by fb.js. |
| 8c514893 DC |
141 | fb_js_settings('ajax_event_url', url(FB_PATH_AJAX_EVENT, array( |
| 142 | 'absolute' => TRUE, | |
| 143 | ))); | |
| 97ed5deb | 144 | |
| 875843a4 DC |
145 | // Data structure to pass to FB.init(); |
| 146 | $fb_init_settings = array( | |
| 147 | 'xfbml' => FALSE, | |
| 148 | 'status' => FALSE, | |
| 149 | 'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE), | |
| 150 | ); | |
| 19092d2f | 151 | |
| 214f9ccf | 152 | // Figure out which app the current request is for. |
| a46642fe | 153 | $_fb_app = fb_invoke(FB_OP_CURRENT_APP); |
| 67523320 | 154 | |
| a46642fe | 155 | if ($_fb_app) { |
| 5d492838 | 156 | // An App is configured. |
| 214f9ccf | 157 | |
| f671097b | 158 | // Javascript settings needed by fb.js. |
| 75be9c97 | 159 | //fb_js_settings('apikey', $_fb_app->apikey); // deprecated XXX |
| f671097b DC |
160 | fb_js_settings('label', $_fb_app->label); |
| 161 | fb_js_settings('page_type', fb_settings(FB_SETTINGS_TYPE)); // canvas or connect. | |
| 75be9c97 | 162 | //$fb_init_settings['apiKey'] = $_fb_app->apikey; |
| ef4e7efd | 163 | $fb_init_settings['appId'] = $_fb_app->id; |
| 19092d2f | 164 | |
| 5d492838 DC |
165 | // Initialize the PHP API. |
| 166 | $_fb = fb_api_init($_fb_app); | |
| 19092d2f | 167 | |
| a46642fe | 168 | if ($_fb) { |
| 875843a4 | 169 | |
| e357a4a5 | 170 | // Look for session info from several sources. |
| 875843a4 | 171 | if ($session = $_fb->getSession()) { |
| e357a4a5 | 172 | // Learned session from cookie or signed request. |
| 875843a4 DC |
173 | // Below, we store in our $_SESSION, just in case third-party cookies are not enabled. |
| 174 | } | |
| 175 | elseif (isset($_REQUEST['fb_js_session'])) { | |
| 176 | // Ajax callback via fb.js. | |
| 177 | $_fb->setSession(json_decode($_REQUEST['fb_js_session'], TRUE)); | |
| 178 | $session = $_fb->getSession(); | |
| 179 | } | |
| 75be9c97 | 180 | elseif (isset($_SESSION['fb'][$_fb_app->id]['session']) && arg(0) != 'logout') { |
| 875843a4 | 181 | // Use the session previously stored. |
| 75be9c97 | 182 | $_fb->setSession($_SESSION['fb'][$_fb_app->id]['session']); |
| 875843a4 DC |
183 | } |
| 184 | ||
| 185 | // Store session for future use. We'll need it if third-party cookies | |
| 186 | // disabled, or we are not using facebook's cookie. | |
| 187 | if (isset($session) && variable_get(FB_VAR_USE_SESSION, TRUE)) { | |
| 75be9c97 | 188 | $_SESSION['fb'][$_fb_app->id]['session'] = $session; |
| 875843a4 DC |
189 | } |
| 190 | ||
| e357a4a5 | 191 | // Make javascript work even when third-party cookies disabled. |
| 875843a4 DC |
192 | $fb_init_settings['session'] = $_fb->getSession(); |
| 193 | ||
| e357a4a5 DC |
194 | // Sometimes when canvas page is open in one tab, and user logs out of |
| 195 | // facebook in another, the canvas page has bogus session info when | |
| 196 | // refreshed. Here we attempt to detect and cleanup. | |
| 197 | if (isset($session) && ($request = $_fb->getSignedRequest())) { | |
| 198 | if (!isset($request['user_id']) || | |
| 199 | $request['user_id'] != $session['uid']) { | |
| 200 | _fb_logout(); | |
| 201 | $_fb->setSession(NULL); | |
| 202 | unset($session); | |
| 75be9c97 | 203 | unset($_SESSION['fb'][$_fb_app->id]); |
| e357a4a5 DC |
204 | unset($fb_init_settings['session']); |
| 205 | } | |
| 206 | } | |
| 207 | ||
| 225abd5d | 208 | // Give other modules a chance to initialize. |
| 123ed3d0 | 209 | fb_invoke(FB_OP_INITIALIZE, array( |
| 5d492838 | 210 | 'fb_app' => $_fb_app, |
| 024cd4af DC |
211 | 'fb' => $_fb, |
| 212 | )); | |
| 19092d2f | 213 | |
| 8fbcf653 | 214 | // See if the facebook user id is known |
| 3946309e | 215 | if ($fbs = $_fb->getSession()) { |
| 123ed3d0 | 216 | fb_invoke(FB_OP_APP_IS_AUTHORIZED, array( |
| 3946309e DC |
217 | 'fb_app' => $_fb_app, |
| 218 | 'fb' => $_fb, | |
| 219 | 'fbu' => $_fb->getUser(), | |
| 220 | )); | |
| f671097b | 221 | fb_js_settings('fbu', $_fb->getUser()); |
| 8fbcf653 | 222 | } |
| 853c3f1b DC |
223 | else { |
| 224 | // Add perms to settings, for calling FB.login(). | |
| 225 | $perms = array(); | |
| 226 | drupal_alter('fb_required_perms', $perms); | |
| f671097b | 227 | fb_js_settings('perms', implode(',', $perms)); |
| 853c3f1b | 228 | } |
| 549aa675 DC |
229 | } |
| 230 | else | |
| a9167ce4 | 231 | watchdog('fb', "URL indicates a facebook app, but could not initialize Facebook", array(), WATCHDOG_ERROR); |
| a24a1868 | 232 | |
| 6927b5ad DC |
233 | if (isset($_REQUEST['destination'])) { |
| 234 | $destination = $_REQUEST['destination']; | |
| 235 | } | |
| 236 | elseif (isset($_REQUEST['q'])) { | |
| 237 | $destination = $_REQUEST['q']; | |
| 8f0e1ce9 DC |
238 | } |
| 239 | else { | |
| 6927b5ad | 240 | $destination = '<front>'; |
| 8f0e1ce9 | 241 | } |
| 6927b5ad DC |
242 | if (fb_is_canvas()) { |
| 243 | $destination = fb_scrub_urls($destination); // Needed? | |
| 244 | } | |
| 245 | ||
| 5500234f DC |
246 | //Stripping the fragment out to be tacked on during the javascript redirect |
| 247 | if (strpos($destination, '#') !== FALSE) { | |
| 248 | list($destination, $fragment) = explode('#', $destination, 2); | |
| 249 | fb_js_settings('reload_url_fragment', $fragment); | |
| 250 | } | |
| 251 | ||
| ef4e7efd DC |
252 | fb_js_settings('reload_url', url($destination, array( |
| 253 | 'absolute' => TRUE, | |
| 254 | 'fb_canvas' => fb_is_canvas(), | |
| 255 | 'language' => (object) array('prefix' => NULL, 'language' => NULL), // http://drupal.org/node/1000452 | |
| 256 | ))); | |
| 733004f5 | 257 | fb_js_settings('reload_url_append_hash', variable_get(FB_VAR_RELOAD_APPEND_HASH, FALSE)); |
| 024cd4af | 258 | } |
| af9b07be | 259 | |
| b24ac6ad | 260 | if ($channel = variable_get(FB_VAR_JS_CHANNEL, TRUE)) { |
| 0de240eb DC |
261 | if (!is_string($channel)) { |
| 262 | $channel = url('fb/channel', array('absolute' => TRUE, 'fb_url_alter' => FALSE)); | |
| 263 | } | |
| 875843a4 | 264 | $fb_init_settings['channelUrl'] = $channel; |
| 0de240eb DC |
265 | } |
| 266 | ||
| 267 | fb_js_settings('fb_init_settings', $fb_init_settings); | |
| 268 | ||
| 269 | ||
| 0c0eb937 DC |
270 | // Allow third-parties to act, even if we did not initialize $_fb. |
| 271 | fb_invoke(FB_OP_POST_INIT, array('fb_app' => $_fb_app, | |
| 272 | 'fb' => $_fb)); | |
| 19092d2f | 273 | |
| f671097b DC |
274 | fb_js_settings('controls', implode(',', fb_controls())); |
| 275 | ||
| 276 | if (!fb_js_settings('js_sdk_url')) { | |
| af9b07be | 277 | if (isset($_SESSION['fb_locale']) && |
| b24ac6ad | 278 | variable_get(FB_VAR_LANGUAGE_OVERRIDE, 'override')) { |
| 875843a4 | 279 | // @TODO - get locale from signed request. It appears to contain it now. |
| f671097b DC |
280 | $fb_lang = $_SESSION['fb_locale']; |
| 281 | } | |
| 282 | else { | |
| 283 | $user_language = user_preferred_language($GLOBALS['user']); | |
| 284 | $fb_lang = variable_get('fb_language_' . $user_language->language, 'en_US'); | |
| 285 | } | |
| af9b07be | 286 | |
| 562c4b37 DC |
287 | $js_sdk = fb_protocol() . "://connect.facebook.net/$fb_lang/all.js"; |
| 288 | fb_js_settings('js_sdk_url', variable_get(FB_VAR_JS_SDK, $js_sdk)); | |
| f671097b | 289 | } |
| 19092d2f | 290 | |
| 97ed5deb | 291 | // Add our module's javascript. |
| a24a1868 | 292 | drupal_add_js(drupal_get_path('module', 'fb') . '/fb.js'); |
| 97ed5deb DC |
293 | |
| 294 | // See also fb_footer(), where we initialize facebook's SDK. | |
| 549aa675 DC |
295 | } |
| 296 | ||
| f671097b DC |
297 | /** |
| 298 | * | |
| 97ed5deb DC |
299 | * Adds the javascript setting with the supplied key/value. This function |
| 300 | * merely keeps track of the settings and writes them as late as possible. | |
| 301 | * Currently, in the fb_footer() function. There has been a lot of | |
| 302 | * experimentation as to the best place to initialize the facebook javascript | |
| 303 | * SDK. The footer appears to be the best place because we may not know all | |
| 304 | * settings until well after hook_init(). | |
| f671097b DC |
305 | * |
| 306 | * @param $key | |
| 307 | * The javascript setting name. If the key is null then nothing is modified and the settings are returned. | |
| 308 | * @param $value | |
| 309 | * The value of the javascript setting. If the key is not null by the value is the setting is removed | |
| 310 | * @return | |
| 311 | * The associative array containing the current fb javascript settings | |
| 312 | */ | |
| f404514e | 313 | function fb_js_settings($key = NULL, $value = NULL) { |
| f671097b DC |
314 | static $fb_js_settings = array(); |
| 315 | ||
| 316 | if (isset($key) && isset($value)) { | |
| 317 | $fb_js_settings[$key] = $value; | |
| af9b07be | 318 | return $value; |
| f671097b DC |
319 | } |
| 320 | elseif (isset($key)) { | |
| 321 | return isset($fb_js_settings[$key]) ? $fb_js_settings[$key] : NULL; | |
| 322 | } | |
| 323 | else { | |
| 324 | return $fb_js_settings; | |
| 325 | } | |
| 326 | } | |
| 549aa675 DC |
327 | |
| 328 | ||
| 186f033e | 329 | /** |
| ebb5649d | 330 | * Include and initialize Facebook's PHP SDK. |
| 186f033e | 331 | */ |
| 3946309e | 332 | function fb_api_init($fb_app) { |
| beed4c38 | 333 | static $cache = array(); |
| b1b8915b DC |
334 | // This helps with uncaught exceptions. However, it should be configurable |
| 335 | // or at least not overwrite previously declared handler. | |
| 28922d16 | 336 | set_exception_handler('fb_handle_exception'); |
| 19092d2f | 337 | |
| 75be9c97 DC |
338 | if (isset($cache[$fb_app->id])) { |
| 339 | return $cache[$fb_app->id]; | |
| 3946309e | 340 | } |
| 19092d2f | 341 | |
| c93f57b9 DC |
342 | // Find Facebook's PHP SDK. Use libraries API if enabled. |
| 343 | $fb_lib_path = function_exists('libraries_get_path') ? libraries_get_path('facebook-php-sdk') : 'sites/all/libraries/facebook-php-sdk'; | |
| 344 | $fb_platform = variable_get(FB_VAR_API_FILE, $fb_lib_path . '/src/facebook.php'); | |
| 345 | ||
| 3946309e | 346 | try { |
| c93f57b9 | 347 | if (!class_exists('Facebook') && !include($fb_platform)) { |
| d3c72c12 DC |
348 | $message = t('Failed to find the Facebook client libraries at %filename. Read the !readme and follow the instructions carefully.', array( |
| 349 | '!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'), | |
| 350 | // This link should work with clean URLs disabled. | |
| 351 | '!readme' => '<a href='. base_path() . '/' . drupal_get_path('module', 'fb') . '/README.txt>README.txt</a>', | |
| 352 | '%filename' => $filename, | |
| 353 | )); | |
| 354 | drupal_set_message($message, 'error'); | |
| 355 | watchdog('fb', $message); | |
| 356 | return NULL; | |
| 357 | } | |
| 358 | ||
| 3946309e DC |
359 | // We don't have a cached resource for this app, so we're going to create one. |
| 360 | $fb = new Facebook(array( | |
| 91e9397d | 361 | 'appId' => $fb_app->id, |
| ff2501ec | 362 | 'secret' => isset($fb_app->secret) ? $fb_app->secret : NULL, |
| f404514e | 363 | 'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE), |
| 3946309e | 364 | )); |
| dca2f231 | 365 | |
| 49c878eb DC |
366 | // Some servers need these settings. |
| 367 | if (variable_get(FB_VAR_CURL_NOVERIFY, TRUE)) { | |
| 368 | Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = FALSE; | |
| 369 | Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = FALSE; | |
| 370 | //Facebook::$CURL_OPTS[CURLOPT_VERBOSE] = 1; // debug | |
| 371 | } | |
| 19092d2f | 372 | |
| 3946309e | 373 | // Cache the result, in case we're called again. |
| 75be9c97 | 374 | $cache[$fb_app->id] = $fb; |
| 19092d2f | 375 | |
| 3946309e | 376 | return $fb; |
| beed4c38 | 377 | } |
| 3946309e DC |
378 | catch (Exception $e) { |
| 379 | fb_log_exception($e, t('Failed to construct Facebook client API.')); | |
| 186f033e | 380 | } |
| 186f033e DC |
381 | } |
| 382 | ||
| beed4c38 DC |
383 | /** |
| 384 | * Wrapper function for fb_api_init. This helps for functions that should | |
| 385 | * work whether or not we are on a canvas page. For canvas pages, the active | |
| 386 | * fb object is used. For non-canvas pages, it will initialize the API using | |
| 387 | * an infinite session, if configured. | |
| 19092d2f | 388 | * |
| beed4c38 | 389 | * @param $fb_app Note this is ignored on canvas pages. |
| 19092d2f | 390 | * |
| beed4c38 DC |
391 | * This is for internal use. Third party modules use fb_api_init(). |
| 392 | */ | |
| 393 | function _fb_api_init($fb_app = NULL) { | |
| a46642fe | 394 | $fb = $GLOBALS['_fb']; // Default to active app on canvas pages |
| beed4c38 DC |
395 | if (!$fb && $fb_app) |
| 396 | // Otherwise, log into facebook api. | |
| 397 | $fb = fb_api_init($fb_app, FB_FBU_ANY); | |
| 19092d2f | 398 | |
| beed4c38 | 399 | if (!$fb) { |
| a9167ce4 DC |
400 | watchdog('fb', '%function unable to initialize Facebook API.', |
| 401 | array('%function' => '_fb_api_init()'), WATCHDOG_ERROR); | |
| beed4c38 DC |
402 | return; |
| 403 | } | |
| 404 | else | |
| 405 | return $fb; | |
| 406 | } | |
| 407 | ||
| 3946309e | 408 | |
| d3ef187d | 409 | /** |
| dca2f231 DC |
410 | * Helper function to get the most commonly used values. In your custom |
| 411 | * module, call extract(fb_vars()); to set $fb_app, $fb, and $fbu. | |
| 412 | */ | |
| 413 | function fb_vars() { | |
| 414 | return array( | |
| 415 | 'fb' => $GLOBALS['_fb'], | |
| 416 | 'fb_app' => $GLOBALS['_fb_app'], | |
| 417 | 'fbu' => fb_facebook_user(), | |
| 418 | ); | |
| 419 | } | |
| 420 | ||
| 421 | ||
| 422 | /** | |
| 449e690e DC |
423 | * Helper to get the tokens needed to accss facebook's API. |
| 424 | * | |
| 81618599 | 425 | * You would think that facebook's SDK would provide basic functions like this. |
| 19092d2f | 426 | * |
| 449e690e DC |
427 | * @param $fb |
| 428 | * Get the token for this API instance. If NULL, use the global $_fb. | |
| 429 | * | |
| 430 | * @param $fbu | |
| 431 | * Get the user-specific token. If NULL, get the application token. | |
| d3ef187d | 432 | */ |
| 449e690e DC |
433 | function fb_get_token($fb = NULL, $fbu = NULL) { |
| 434 | static $cache; | |
| 435 | if (!isset($cache)) | |
| 436 | $cache = array(); | |
| 437 | ||
| 438 | if (!$fb) | |
| 439 | $fb = $GLOBALS['_fb']; | |
| 81618599 DC |
440 | if (!$fb) |
| 441 | return; | |
| 449e690e | 442 | |
| 920d803c DC |
443 | $app_id = $fb->getAppId(); |
| 444 | $cache_key = $app_id; | |
| 449e690e DC |
445 | |
| 446 | if (!$fbu) { | |
| 447 | // Get the application token. | |
| 448 | if (!isset($cache[$cache_key])) { | |
| 920d803c | 449 | $path = "https://graph.facebook.com/oauth/access_token?client_id=" . $app_id . "&client_secret=" . $fb->getApiSecret() . "&type=client_cred"; |
| 449e690e | 450 | $http = drupal_http_request($path); |
| dca2f231 | 451 | if ($http->code == 200 && isset($http->data)) { |
| 449e690e DC |
452 | $data = explode('=', $http->data); |
| 453 | $token = $data[1]; | |
| 454 | if ($token) | |
| 455 | $cache[$cache_key] = $token; | |
| 456 | } | |
| 457 | } | |
| 458 | } | |
| 459 | else { | |
| 460 | $cache_key .= '_' . $fbu; | |
| 461 | // Get the user access token. | |
| 462 | if ($fbu == 'me' || $fbu == fb_facebook_user($fb)) { | |
| 463 | $session = $fb->getSession(); | |
| 464 | $cache[$cache_key] = $session['access_token']; | |
| 465 | } | |
| 466 | else { | |
| 024ebf40 DC |
467 | $session_data = fb_invoke(FB_OP_GET_USER_SESSION, array( |
| 468 | 'fb' => $fb, | |
| 920d803c | 469 | 'fb_app' => fb_get_app(array('id' => $app_id)), |
| 024ebf40 DC |
470 | 'fbu' => $fbu, |
| 471 | ), array()); | |
| 472 | if (count($session_data)) { | |
| 473 | $cache[$cache_key] = $session_data['access_token']; | |
| 474 | } | |
| 449e690e DC |
475 | } |
| 476 | } | |
| dca2f231 | 477 | return isset($cache[$cache_key]) ? $cache[$cache_key] : NULL; |
| 449e690e DC |
478 | } |
| 479 | ||
| 0ab1fd2c | 480 | /** |
| 8533770d DC |
481 | * This helper original written because facebook's $fb->api() function was |
| 482 | * very buggy. I'm not sure this is still needed. On the other hand, a | |
| 483 | * future version of modules/fb might use this instead of faceobok's PHP SDK, | |
| 484 | * eliminating the need for it entirely. | |
| 0ab1fd2c | 485 | */ |
| d3ef187d | 486 | function fb_call_method($fb, $method, $params = array()) { |
| e9df1cf1 DC |
487 | if (!isset($params['access_token'])) { |
| 488 | $params['access_token'] = fb_get_token($fb); | |
| 489 | } | |
| d3ef187d | 490 | $params['format'] = 'json-strings'; |
| 11665fdd DC |
491 | |
| 492 | // Here's how to create a url that conforms to standards: | |
| 3946309e DC |
493 | $url = url("https://api.facebook.com/method/{$method}", array( |
| 494 | 'query' => $params, | |
| 495 | )); | |
| 11665fdd DC |
496 | // If facebook gives errors like "Invalid OAuth 2.0 Access Token 190/Unable to get application prop" it might be necessary to uncomment the urldecode below. |
| 497 | // http://forum.developers.facebook.net/viewtopic.php?id=76228 | |
| 498 | // $url = rawurldecode($url); | |
| 19092d2f | 499 | |
| 3946309e | 500 | $http = drupal_http_request($url); |
| d3ef187d | 501 | |
| dca2f231 | 502 | if (!isset($http->error) && isset($http->data)) { |
| d3ef187d DC |
503 | $data = json_decode($http->data, TRUE); |
| 504 | // Yes, it's double encoded. At least sometimes. | |
| 505 | if (is_string($data)) { | |
| 506 | $data = json_decode($data, TRUE); | |
| 507 | } | |
| 508 | if (is_array($data)) { | |
| 509 | if (isset($data['error_code'])) { | |
| dcb2712f | 510 | throw new FacebookApiException($data); |
| d3ef187d DC |
511 | } |
| 512 | } | |
| 02376a09 DC |
513 | elseif ($http->data == 'true' || $http->code == 200) { |
| 514 | // No problems. | |
| 515 | } | |
| d3ef187d DC |
516 | else { |
| 517 | // Never reach this??? | |
| 5d87b01a | 518 | if (function_exists('dpm')) dpm($http, __FUNCTION__ . " unexpected result from $url"); // XXX |
| d3ef187d DC |
519 | } |
| 520 | return $data; | |
| 3946309e | 521 | } |
| 51ecb79f DC |
522 | else { |
| 523 | // Should we throw FacebookApiException, or plain old exception? | |
| 524 | throw new FacebookApiException( | |
| 525 | array( | |
| 526 | 'error_msg' => t('fb_call_method failed calling !method. !detail', array( | |
| 527 | '!method' => $method, | |
| 528 | '!detail' => $http->error, | |
| 529 | )), | |
| 530 | 'error_code' => $http->code, | |
| 531 | )); | |
| 532 | } | |
| 3946309e DC |
533 | } |
| 534 | ||
| ebb5649d DC |
535 | /** |
| 536 | * Helper function for fql queries. | |
| 537 | * | |
| 538 | * Use $params to pass a session_key, when needed. | |
| 539 | */ | |
| d3ef187d | 540 | function fb_fql_query($fb, $query, $params = array()) { |
| ebb5649d | 541 | $params['query'] = $query; |
| d3ef187d DC |
542 | //$result = fb_call_method($fb, 'fql.query', $params); |
| 543 | $params['method'] = 'fql.query'; | |
| 544 | $result = $fb->api($params); | |
| dcb2712f | 545 | |
| ebb5649d DC |
546 | return $result; |
| 547 | } | |
| 3946309e | 548 | |
| de5d0e5a DC |
549 | /** |
| 550 | * Helper function for facebook's batch graph api. | |
| 551 | * | |
| 6b8f8dcc DC |
552 | * This function accepts a simpler interface than facebook's. The queries are |
| 553 | * passed in as a simple array, and the data is parsed into a PHP data | |
| 554 | * structure. | |
| de5d0e5a | 555 | * |
| 6b8f8dcc | 556 | * @TODO: when $method=='GET', share caching with fb_api(). |
| de5d0e5a | 557 | */ |
| 6b8f8dcc | 558 | function fb_api_batch($fb, $queries, $params, $method = 'GET') { |
| de5d0e5a DC |
559 | $data = array(); |
| 560 | ||
| 6b8f8dcc DC |
561 | // Build facebook's data structure. Our's supports only GET or POST at a time. |
| 562 | $fb_queries = array(); | |
| 563 | foreach ($queries as $query) { | |
| 564 | $fb_queries[] = array('method' => $method, 'relative_url' => $query); | |
| 565 | } | |
| 566 | ||
| 567 | $wrapped_data = $fb->api('/?batch=' . json_encode($fb_queries), 'POST', $params); // Use POST, not $method. | |
| de5d0e5a DC |
568 | |
| 569 | foreach ($wrapped_data as $w_d) { | |
| 570 | if ($w_d['code'] == 200 && isset($w_d['body'])) { | |
| 571 | $data[] = json_decode($w_d['body'], TRUE); | |
| 572 | } | |
| 573 | else { | |
| 574 | // Unexpected code | |
| 575 | $data[] = $w_d; | |
| 576 | } | |
| 577 | } | |
| 578 | ||
| 579 | return $data; | |
| 580 | } | |
| 581 | ||
| 3946309e | 582 | |
| 5d492838 DC |
583 | /** |
| 584 | * Implements hook_footer(). | |
| 585 | */ | |
| 3946309e DC |
586 | function fb_footer($is_front) { |
| 587 | global $_fb, $_fb_app; | |
| dca2f231 DC |
588 | |
| 589 | // This element recommended by facebook. http://developers.facebook.com/docs/reference/javascript/ | |
| 5d492838 | 590 | $output = "<div id=\"fb-root\"></div>\n"; |
| ebb5649d | 591 | |
| 97ed5deb DC |
592 | $settings = fb_js_settings(); |
| 593 | ||
| 594 | $output .= "<script type=\"text/javascript\">\n"; | |
| 595 | $output .= "<!--//--><![CDATA[//><!--\n"; | |
| 596 | $output .= "jQuery.extend(Drupal.settings, " . json_encode(array('fb' => fb_js_settings())) . ");\n"; | |
| ebb5649d DC |
597 | $js_array = fb_invoke(FB_OP_JS, array('fb' => $GLOBALS['_fb'], 'fb_app' => $GLOBALS['_fb_app']), array()); |
| 598 | if (count($js_array)) { | |
| ebb5649d DC |
599 | // The function we define in the footer will be called after FB is initialized. |
| 600 | $output .= "FB_JS.initHandler = function() {\n"; | |
| ff2501ec | 601 | //$output .= "debugger;\n"; |
| ebb5649d DC |
602 | $output .= implode("\n", $js_array); |
| 603 | $output .= "};\n"; | |
| 604 | $output .= "jQuery(document).bind('fb_init', FB_JS.initHandler);\n"; | |
| ebb5649d | 605 | } |
| dca2f231 DC |
606 | |
| 607 | // Load the JS SDK asynchronously. | |
| 608 | // http://developers.facebook.com/docs/reference/javascript/ | |
| 609 | $output .= "var e = document.createElement('script');\n"; | |
| 610 | $output .= "e.async = true;\n"; | |
| 611 | $output .= "e.src = Drupal.settings.fb.js_sdk_url;\n"; | |
| 612 | $output .= "document.getElementById('fb-root').appendChild(e);\n"; | |
| 613 | ||
| 97ed5deb DC |
614 | $output .= "\n//--><!]]>\n"; |
| 615 | $output .= "\n</script>\n"; | |
| 3946309e DC |
616 | return $output; |
| 617 | } | |
| 618 | ||
| 5d492838 DC |
619 | /** |
| 620 | * Is the current request a canvas page? | |
| 621 | */ | |
| 3946309e | 622 | function fb_is_canvas() { |
| 5441e7bd DC |
623 | if (fb_is_tab()) { |
| 624 | return FALSE; | |
| 625 | } | |
| 626 | elseif (fb_settings(FB_SETTINGS_CB)) { | |
| c43fb19a DC |
627 | // Using fb_url_rewrite. |
| 628 | return TRUE; | |
| 629 | } | |
| 630 | elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) { | |
| 631 | // No rewrite, but fb_settings.inc has detected type. | |
| 632 | return TRUE; | |
| 633 | } | |
| 634 | return FALSE; | |
| 3946309e DC |
635 | } |
| 636 | ||
| 360cf127 | 637 | /** |
| 5441e7bd DC |
638 | * Is the current page a profile tab. |
| 639 | * | |
| 640 | * Only works when "Canvas Session Parameter" is disabled. | |
| 641 | */ | |
| 642 | function fb_is_tab() { | |
| 643 | global $_fb; | |
| 727e0e00 DC |
644 | |
| 645 | if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) { | |
| 646 | return TRUE; | |
| 647 | } | |
| 648 | elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) { // deprecated FBML tab | |
| 5441e7bd DC |
649 | return TRUE; |
| 650 | } | |
| c12b0663 | 651 | elseif (isset($_REQUEST['fb_sig_in_profile_tab']) && |
| 727e0e00 | 652 | $_REQUEST['fb_sig_in_profile_tab']) { // deprecated ancient history |
| c12b0663 | 653 | // Old way, no migrations enabled. |
| 5441e7bd DC |
654 | return TRUE; |
| 655 | } | |
| 656 | return FALSE; | |
| 657 | } | |
| 658 | ||
| 659 | ||
| 660 | /** | |
| 360cf127 DC |
661 | * Sometimes calls to fb_api_init succeed, but calls to the client api |
| 662 | * will fail because cookies are obsolete or what have you. This | |
| 663 | * function makes a call to facebook to test the session. Expensive, | |
| 664 | * so use only when necessary. | |
| 3946309e | 665 | * |
| 360cf127 DC |
666 | */ |
| 667 | function fb_api_check_session($fb) { | |
| 360cf127 DC |
668 | $success = FALSE; |
| 669 | try { | |
| f671097b DC |
670 | $me = $fb->api('me'); |
| 671 | ||
| 672 | // Store the locale if set. | |
| 673 | if (isset($me['locale'])) { | |
| 674 | $_SESSION['fb_locale'] = $me['locale']; | |
| 853c3f1b | 675 | } |
| f671097b DC |
676 | |
| 677 | // Does not matter what is returned, as long as exception is not thrown. | |
| 678 | $success = TRUE; | |
| 360cf127 DC |
679 | } |
| 680 | catch (Exception $e) { | |
| 7308ca43 DC |
681 | if (fb_verbose()) { |
| 682 | watchdog('fb', 'fb_api_check_session failed. Possible attempt to spoof a facebook session!'); | |
| 91e9397d | 683 | //watchdog('fb', print_r($fb->getSession(), 1)); |
| 7308ca43 | 684 | } |
| 360cf127 | 685 | $success = FALSE; |
| f5ac8b5b DC |
686 | if (fb_verbose()) { |
| 687 | fb_log_exception($e, t("fb_api_check_session failed.")); | |
| 688 | } | |
| af9b07be | 689 | |
| 875843a4 | 690 | unset($_SESSION['fb'][$fb->getAppId()]); |
| af9b07be DC |
691 | // Unsetting the javasript fbu can be helpful when third-party cookies disabled. |
| 692 | fb_js_settings('fbu', 0); | |
| 693 | ||
| 694 | // Might as well try to clean up the mess. | |
| 695 | if (isset($_COOKIE['fbs_' . $fb->getAppId()])) { | |
| 696 | setcookie('fbs_' . $fb->getAppId(), '', time() - 42000, '/'); | |
| 697 | } | |
| 698 | ||
| 360cf127 DC |
699 | } |
| 700 | return $success; | |
| 701 | } | |
| beed4c38 | 702 | |
| f671097b | 703 | |
| 186f033e | 704 | /** |
| 875843a4 DC |
705 | * Helper to ensure local user is logged out, or an anonymous session is refreshed. |
| 706 | */ | |
| 707 | function _fb_logout() { | |
| 708 | session_destroy(); | |
| 709 | // Fix for http://bugs.php.net/bug.php?id=32330 | |
| 710 | session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc'); | |
| 711 | $GLOBALS['user'] = drupal_anonymous_user(); | |
| 712 | ||
| 713 | // Unsetting the javasript fbu can be helpful when third-party cookies disabled. | |
| 714 | fb_js_settings('fbu', 0); | |
| 715 | ||
| 91e9397d DC |
716 | // Clean up facebook cookies. |
| 717 | if (isset($GLOBALS['_fb_app'])) { | |
| 75be9c97 | 718 | if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->apikey])) { // still needed? |
| 91e9397d DC |
719 | setcookie('fbs_' . $GLOBALS['_fb_app']->apikey, '', time() - 42000, '/'); |
| 720 | } | |
| 721 | if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->id])) { | |
| 722 | setcookie('fbs_' . $GLOBALS['_fb_app']->id, '', time() - 42000, '/'); | |
| 723 | } | |
| 875843a4 DC |
724 | } |
| 725 | } | |
| 726 | ||
| 727 | ||
| 728 | /** | |
| 225abd5d DC |
729 | * Returns the facebook user id currently visiting a canvas page, or if |
| 730 | * set_user has been called. Unlike fb_get_fbu(), works only on canvas and | |
| 731 | * connect pages, or when infinite session has been initialized. | |
| 186f033e | 732 | */ |
| 549aa675 DC |
733 | function fb_facebook_user($fb = NULL) { |
| 734 | if (!isset($fb)) | |
| a46642fe | 735 | $fb = $GLOBALS['_fb']; |
| 19092d2f | 736 | |
| 186f033e DC |
737 | if (!$fb) |
| 738 | return; | |
| 19092d2f | 739 | |
| 3946309e DC |
740 | try { |
| 741 | $fbu = $fb->getUser(); | |
| 742 | return $fbu; | |
| 5e212726 | 743 | } |
| 3946309e DC |
744 | catch (FacebookApiException $e) { |
| 745 | fb_log_exception($e, | |
| 35163e17 DC |
746 | t('Failed to get Facebook user id. detail: !detail', |
| 747 | array('!detail' => print_r($e, 1)))); | |
| 5e212726 DC |
748 | } |
| 749 | } | |
| 67523320 DC |
750 | |
| 751 | /** | |
| 449e690e DC |
752 | * Helper function to ensure user has authorized an application. |
| 753 | * | |
| 754 | * Similar to the old require_login() provided by the old facebook API. | |
| 755 | * Works by redirecting the user as described in http://developers.facebook.com/docs/authentication/. | |
| 756 | * | |
| cf551949 | 757 | * @TODO handle users who skip. |
| 449e690e DC |
758 | */ |
| 759 | function fb_require_authorization($fb = NULL, $destination = NULL) { | |
| 760 | if (!$fb) | |
| 761 | $fb = $GLOBALS['_fb']; | |
| 762 | ||
| 727e0e00 DC |
763 | if (!$fb) { |
| 764 | throw new Exception(t('Failed to authorize facebook application. Could not determine application id.')); | |
| 765 | } | |
| 766 | ||
| 449e690e DC |
767 | $fbu = fb_facebook_user($fb); |
| 768 | if (!$fbu) { | |
| 769 | $client_id = $fb->getAppId(); | |
| 770 | $redirect_uri = $destination ? $destination : url(fb_scrub_urls($_REQUEST['q']), array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas())); | |
| 771 | ||
| cf551949 DC |
772 | // Which permissions to prompt for? |
| 773 | $perms = array(); | |
| 774 | drupal_alter('fb_required_perms', $perms); | |
| 775 | $scope = implode(',', $perms); | |
| 776 | ||
| 777 | $url = "https://graph.facebook.com/oauth/authorize?client_id=$client_id&scope=$scope&redirect_uri=$redirect_uri"; | |
| 449e690e DC |
778 | drupal_goto($url); |
| 779 | } | |
| 780 | else { | |
| 781 | return $fbu; | |
| 782 | } | |
| 783 | } | |
| 784 | ||
| 785 | /** | |
| 67523320 DC |
786 | * Helper tells other modules when to load admin hooks. |
| 787 | */ | |
| 788 | function fb_is_fb_admin_page() { | |
| 7217ba45 DC |
789 | if (arg(0) == 'admin' && (arg(1) == 'fb' || arg(2) == 'fb')) { |
| 790 | // Keep consistant titles across tabs served by multiple modules. | |
| 791 | if ($label = arg(FB_PATH_ADMIN_APPS_ARGS)) | |
| 792 | drupal_set_title($label); | |
| 793 | else | |
| 794 | drupal_set_title(t('Drupal for Facebook')); | |
| 795 | ||
| 796 | return TRUE; | |
| 797 | } | |
| 67523320 DC |
798 | } |
| 799 | ||
| 8e801805 | 800 | /** |
| 346e5a9b DC |
801 | * Given a facebook user id, learn the local uid, if any. |
| 802 | * | |
| 19092d2f | 803 | */ |
| 346e5a9b DC |
804 | function fb_get_uid($fbu, $fb_app = NULL) { |
| 805 | $uid = NULL; | |
| 806 | if ($fbu) { | |
| 807 | $uid = fb_invoke(FB_OP_GET_UID, array('fbu' => $fbu, 'fb_app' => $fb_app)); | |
| 808 | } | |
| 809 | return $uid; | |
| 810 | } | |
| 811 | ||
| 812 | ||
| 813 | /** | |
| 186f033e | 814 | * Given a local user id, find the facebook id. |
| 225abd5d DC |
815 | * |
| 816 | * Invokes hook_fb(FB_OP_GET_FBU) in order to ask other modules what the fbu | |
| 817 | * is. Typically, fb_user.module will answer the question. | |
| 186f033e | 818 | */ |
| 3a512626 | 819 | function fb_get_fbu($uid, $fb_app = NULL) { |
| 186f033e | 820 | // Accept either a user object or uid passed in. |
| 19092d2f | 821 | if (is_object($uid) && ($uid->uid) && |
| 2cdd1dcf | 822 | isset($uid->fbu) && $uid->fbu) |
| 186f033e | 823 | return $uid->fbu; |
| a46642fe | 824 | elseif (is_object($uid)) |
| 186f033e | 825 | $uid = $uid->uid; |
| 19092d2f | 826 | |
| 2cdd1dcf DC |
827 | if ($uid) { |
| 828 | // User management is handled by another module. Use our hook to ask for mapping. | |
| cd11871d | 829 | $fbu = fb_invoke(FB_OP_GET_FBU, array('uid' => $uid, |
| a46642fe | 830 | 'fb' => $GLOBALS['_fb'])); |
| 2cdd1dcf | 831 | } |
| e44a9ee0 DC |
832 | else { |
| 833 | $fbu = NULL; | |
| 834 | } | |
| 186f033e DC |
835 | return $fbu; |
| 836 | } | |
| 837 | ||
| 4c0bff53 | 838 | /** |
| 9559f7ff DC |
839 | * Convenience function to learn the fbu associated with a user, node or comment. |
| 840 | * Used in theming (X)FBML tags. | |
| 841 | */ | |
| 842 | function fb_get_object_fbu($object) { | |
| 843 | static $cache; | |
| 844 | if (!isset($cache)) | |
| 845 | $cache = array(); | |
| 846 | ||
| 847 | if (isset($object->uid) && isset($cache[$object->uid])) { | |
| 848 | $fbu = $cache[$object->uid]; | |
| 849 | return $fbu; | |
| 850 | } | |
| a46642fe | 851 | elseif (isset($object->fbu)) { |
| 9559f7ff DC |
852 | // Explicitly set. |
| 853 | $fbu = $object->fbu; | |
| 854 | } | |
| cd11871d DC |
855 | elseif (isset($object->init) && |
| 856 | ($pos = strpos($object->init, '@facebook'))) { | |
| 857 | // Naming convention used by fb_user when creating accounts. | |
| 858 | // $object->init may be present when object is a user. | |
| 859 | $fbu = substr($object->init, 0, $pos); | |
| 860 | } | |
| a46642fe | 861 | elseif ($pos = strpos($object->name, '@facebook')) { |
| 9559f7ff DC |
862 | $fbu = substr($object->name, 0, $pos); |
| 863 | } | |
| a46642fe | 864 | elseif ($object->uid > 0) { |
| cd11871d DC |
865 | // This can be expensive on pages with many comments or nodes! |
| 866 | $fbu = fb_get_fbu($object->uid); | |
| 9559f7ff | 867 | } |
| 19092d2f | 868 | |
| 9559f7ff DC |
869 | if (isset($fbu) && is_numeric($fbu)) { |
| 870 | if (isset($object->uid) && ($object->uid > 0)) { | |
| 871 | $cache[$object->uid] = $fbu; | |
| 872 | } | |
| 873 | return $fbu; | |
| 874 | } | |
| 875 | } | |
| 876 | ||
| 877 | ||
| 878 | /** | |
| 75be9c97 | 879 | * Convenience method to get app info based on id or nid. |
| 4c0bff53 | 880 | */ |
| 2c870dd0 DC |
881 | function fb_get_app($search_data) { |
| 882 | // $search_data can be an apikey, or an array of other search params. | |
| 883 | if (!is_array($search_data)) | |
| 75be9c97 | 884 | $search_data = array('id' => $search_data); |
| 19092d2f | 885 | |
| 19a42c4b | 886 | $fb_app = fb_invoke(FB_OP_GET_APP, $search_data); |
| 4c0bff53 DC |
887 | return $fb_app; |
| 888 | } | |
| 889 | ||
| 890 | /** | |
| 67523320 | 891 | * Convenience method for other modules to attach data to the fb_app |
| 19092d2f DC |
892 | * object. |
| 893 | * | |
| 67523320 DC |
894 | * It is assumed the fb_app implementation will fill in the data |
| 895 | * field. We really should clean up the separation between modules, | |
| 896 | * or merge fb_app.module into this one. | |
| 4c0bff53 | 897 | */ |
| 67523320 DC |
898 | function fb_get_app_data(&$fb_app) { |
| 899 | if (!isset($fb_app->fb_app_data)) { | |
| 900 | $fb_app->fb_app_data = isset($fb_app->data) ? unserialize($fb_app->data) : array(); | |
| 4c0bff53 | 901 | } |
| 67523320 | 902 | return $fb_app->fb_app_data; |
| 4c0bff53 DC |
903 | } |
| 904 | ||
| 29be740a DC |
905 | /** |
| 906 | * Will return a human-readable name if the fb_app module supports it, or | |
| 907 | * fb_admin_get_app_properties($fb_app) has been called. However we don't | |
| 908 | * take the relatively expensive step of calling that ourselves. | |
| 909 | */ | |
| 910 | function fb_get_app_title($fb_app) { | |
| 911 | if (isset($fb_app->title)) | |
| 912 | return $fb_app->title; | |
| 913 | elseif (isset($fb_app->application_name)) { | |
| 914 | return $fb_app->application_name; | |
| 915 | } | |
| 916 | else { | |
| 917 | return $fb_app->label; | |
| 918 | } | |
| 919 | } | |
| 67523320 | 920 | |
| 2c870dd0 DC |
921 | /** |
| 922 | * Convenience method to return array of all know fb_apps. | |
| 923 | */ | |
| 924 | function fb_get_all_apps() { | |
| 8fbcf653 | 925 | $apps = fb_invoke(FB_OP_GET_ALL_APPS, NULL, array()); |
| 2c870dd0 DC |
926 | return $apps; |
| 927 | } | |
| 928 | ||
| beed4c38 | 929 | /** |
| 19092d2f | 930 | * A convenience method for returning a list of facebook friends. |
| c0af5627 DC |
931 | * |
| 932 | * This should work efficiently in canvas pages for finding friends of | |
| 3946309e DC |
933 | * the current user. |
| 934 | * | |
| 19092d2f DC |
935 | * @TODO - also support users who have permitted offline access. |
| 936 | * | |
| beed4c38 DC |
937 | * @return: an array of facebook ids |
| 938 | */ | |
| 939 | function fb_get_friends($fbu, $fb_app = NULL) { | |
| 186f033e | 940 | static $cache = array(); |
| c0af5627 | 941 | if (!$fb_app) |
| a46642fe | 942 | $fb_app = $GLOBALS['_fb_app']; |
| 19092d2f | 943 | |
| beed4c38 DC |
944 | // Facebook only allows us to query the current user's friends, so let's try |
| 945 | // to log in as that user. It will only actually work if they are the | |
| 946 | // current user of a canvas page, or they've signed up for an infinite | |
| 947 | // session. | |
| 948 | $fb = fb_api_init($fb_app, $fbu); | |
| 186f033e DC |
949 | if (!$fb || !$fbu) |
| 950 | return; | |
| e8503dbe DC |
951 | |
| 952 | $items = array(); | |
| 186f033e | 953 | if (!isset($cache[$fbu])) { |
| 19092d2f | 954 | if ($fb === $GLOBALS['_fb'] && |
| 3946309e | 955 | $fbu == fb_facebook_user($fb)) { |
| e8503dbe DC |
956 | try { |
| 957 | $items = fb_call_method($fb, 'friends.get', array( | |
| 958 | 'uid' => $fbu, | |
| 959 | )); | |
| 960 | } | |
| 961 | catch (Exception $e) { | |
| 962 | fb_log_exception($e, t('Failed call to friends.get'), $fb); | |
| 963 | } | |
| 964 | ||
| 3946309e | 965 | } |
| 853c3f1b | 966 | // friends_get does not work in cron call, so we double check. @TODO - still needed? |
| 186f033e | 967 | if (!$items || !count($items)) { |
| beed4c38 | 968 | $logged_in = fb_facebook_user($fb); |
| 7a2b37de | 969 | $query = "SELECT uid2 FROM friend WHERE uid1=$fbu"; // FQL, no {curly_brackets}! |
| e8503dbe DC |
970 | try { |
| 971 | $result = fb_call_method($fb, 'fql.query', array( | |
| 972 | 'query' => $query, | |
| 973 | )); | |
| 974 | //dpm($result, "FQL " . $query); // debug | |
| 975 | } | |
| 976 | catch (Exception $e) { | |
| 977 | fb_log_exception($e, t('Failed call to fql.query: !query', array('!query' => $query)), $fb); | |
| 978 | } | |
| 19092d2f DC |
979 | |
| 980 | if (is_array($result)) | |
| 186f033e DC |
981 | foreach ($result as $data) { |
| 982 | $items[] = $data['uid2']; | |
| 983 | } | |
| 984 | } | |
| 4566b785 DC |
985 | // Facebook's API has the annoying habit of returning an item even if user |
| 986 | // has no friends. We need to clean that up. | |
| 987 | if (!$items[0]) | |
| 988 | unset($items[0]); | |
| 19092d2f | 989 | |
| 186f033e DC |
990 | $cache[$fbu] = $items; |
| 991 | } | |
| 19092d2f | 992 | |
| 186f033e DC |
993 | return $cache[$fbu]; |
| 994 | } | |
| 995 | ||
| 996 | // Return array of facebook gids | |
| beed4c38 | 997 | function fb_get_groups($fbu, $fb_app = NULL) { |
| 186f033e DC |
998 | $items = array(); |
| 999 | $groups = fb_get_groups_data($fbu); | |
| 1000 | ||
| 1001 | if ($groups && count($groups)) | |
| 1002 | foreach ($groups as $data) { | |
| 1003 | $items[] = $data['gid']; | |
| 1004 | } | |
| 1005 | return $items; | |
| 1006 | } | |
| 1007 | ||
| beed4c38 | 1008 | function fb_get_groups_data($fbu, $fb_app = NULL) { |
| 186f033e | 1009 | static $cache = array(); |
| beed4c38 DC |
1010 | |
| 1011 | $fb = _fb_api_init($fb_app); | |
| 186f033e DC |
1012 | if (!$fb || !$fbu) |
| 1013 | return; | |
| 19092d2f | 1014 | |
| 186f033e | 1015 | if (!isset($cache[$fbu])) { |
| 853c3f1b | 1016 | $cache[$fbu] = fb_call_method($fb, 'groups.get', array( |
| 3946309e | 1017 | 'uid' => $fbu, |
| 3946309e | 1018 | )); |
| 186f033e | 1019 | } |
| 19092d2f | 1020 | |
| 186f033e DC |
1021 | return $cache[$fbu]; |
| 1022 | } | |
| 1023 | ||
| 1024 | ||
| f77127b5 | 1025 | //// Menu structure. |
| 67523320 | 1026 | /** |
| 0de240eb | 1027 | * Implements hook_menu(). |
| 67523320 | 1028 | */ |
| a9167ce4 | 1029 | function fb_menu() { |
| 186f033e | 1030 | $items = array(); |
| 19092d2f | 1031 | |
| f77127b5 | 1032 | // Admin pages overview. |
| 67523320 DC |
1033 | $items[FB_PATH_ADMIN] = array( |
| 1034 | 'title' => 'Facebook Applications', | |
| 7a2b37de | 1035 | 'description' => 'Facebook Applications', |
| 9559f7ff DC |
1036 | 'page callback' => 'fb_admin_page', |
| 1037 | 'access arguments' => array(FB_PERM_ADMINISTER), | |
| 1038 | 'file' => 'fb.admin.inc', | |
| 0c4a9f92 | 1039 | 'type' => MENU_NORMAL_ITEM, |
| 9559f7ff | 1040 | ); |
| f77127b5 | 1041 | $items[FB_PATH_ADMIN . '/list'] = array( |
| 875843a4 | 1042 | 'title' => 'List Apps', |
| 67523320 | 1043 | 'weight' => -2, |
| 9559f7ff DC |
1044 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 1045 | ); | |
| f77127b5 | 1046 | |
| 875843a4 DC |
1047 | $items[FB_PATH_ADMIN . '/settings'] = array( |
| 1048 | 'title' => 'Settings', | |
| f671097b | 1049 | 'access arguments' => array(FB_PERM_ADMINISTER), |
| 875843a4 | 1050 | 'weight' => -1, |
| f671097b DC |
1051 | 'type' => MENU_LOCAL_TASK, |
| 1052 | 'page callback' => 'drupal_get_form', | |
| 875843a4 | 1053 | 'page arguments' => array('fb_admin_settings'), |
| f671097b DC |
1054 | 'file' => 'fb.admin.inc', |
| 1055 | ); | |
| d3c72c12 | 1056 | |
| 875843a4 | 1057 | |
| 7217ba45 | 1058 | // Admin pages for each app. |
| f77127b5 | 1059 | $items[FB_PATH_ADMIN_APPS . '/%fb'] = array( |
| 7a2b37de DC |
1060 | 'title' => 'Application Detail', |
| 1061 | 'description' => 'Facebook Applications', | |
| f77127b5 DC |
1062 | 'page callback' => 'fb_admin_app_page', |
| 1063 | 'page arguments' => array(FB_PATH_ADMIN_APPS_ARGS), | |
| 1064 | 'access arguments' => array(FB_PERM_ADMINISTER), | |
| 1065 | 'file' => 'fb.admin.inc', | |
| 1066 | 'type' => MENU_CALLBACK, | |
| 1067 | ); | |
| 1068 | ||
| 1069 | $items[FB_PATH_ADMIN_APPS .'/%fb/fb'] = array( | |
| 1070 | 'title' => 'View', | |
| 1071 | 'weight' => -2, | |
| 1072 | 'type' => MENU_DEFAULT_LOCAL_TASK, | |
| 1073 | ); | |
| 853c3f1b DC |
1074 | $items[FB_PATH_ADMIN_APPS . '/%fb/fb/set_props'] = array( |
| 1075 | 'title' => 'Set Properties', | |
| 1076 | 'description' => 'Set Facebook Application Properties', | |
| 1077 | 'page callback' => 'drupal_get_form', | |
| 1078 | 'page arguments' => array('fb_admin_set_properties_form', FB_PATH_ADMIN_APPS_ARGS), | |
| 1079 | 'access arguments' => array(FB_PERM_ADMINISTER), | |
| cf551949 | 1080 | 'file' => 'fb.admin.inc', |
| 853c3f1b DC |
1081 | 'type' => MENU_CALLBACK, |
| 1082 | ); | |
| 1083 | ||
| ebb5649d DC |
1084 | // Javascript helper |
| 1085 | $items['fb/js'] = array( | |
| 1086 | 'page callback' => 'fb_js_cb', | |
| 1087 | 'type' => MENU_CALLBACK, | |
| 1088 | 'access callback' => TRUE, | |
| 1089 | ); | |
| a24a1868 DC |
1090 | |
| 1091 | // Ajax event handler. | |
| 1092 | $items[FB_PATH_AJAX_EVENT . '/%'] = array( | |
| 1093 | 'page callback' => 'fb_ajax_event', | |
| 1094 | 'type' => MENU_CALLBACK, | |
| 1095 | 'access callback' => TRUE, | |
| 1096 | 'page arguments' => array(FB_PATH_AJAX_EVENT_ARGS), | |
| 1097 | ); | |
| 19092d2f | 1098 | |
| 0de240eb DC |
1099 | // "Channel" http://developers.facebook.com/docs/reference/javascript/FB.init |
| 1100 | $items['fb/channel'] = array( | |
| 1101 | 'page callback' => 'fb_channel_page', | |
| 1102 | 'type' => MENU_CALLBACK, | |
| 1103 | 'access callback' => TRUE, | |
| 1104 | ); | |
| 1105 | ||
| 186f033e DC |
1106 | return $items; |
| 1107 | } | |
| 1108 | ||
| 1109 | /** | |
| f77127b5 | 1110 | * Implementation of a %wildcard_load(). http://drupal.org/node/224170 |
| 853c3f1b DC |
1111 | * |
| 1112 | * Seems to get called a lot(!) so we cache. | |
| f77127b5 DC |
1113 | */ |
| 1114 | function fb_load($id) { | |
| 853c3f1b DC |
1115 | static $cache; |
| 1116 | if (!isset($cache)) | |
| 1117 | $cache = array(); | |
| 1118 | if (!isset($cache[$id])) { | |
| 1119 | $query = array('label' => $id); | |
| 1120 | if (fb_is_fb_admin_page()) { | |
| 1121 | // Show disabled apps to admins. | |
| 1122 | $query['status'] = 0; // status >= 0 | |
| 1123 | } | |
| 1124 | $cache[$id] = fb_get_app($query); | |
| f77127b5 | 1125 | } |
| 853c3f1b | 1126 | return $cache[$id]; |
| f77127b5 DC |
1127 | } |
| 1128 | ||
| 1129 | /** | |
| 9559f7ff DC |
1130 | * Implementation of hook_perm(). |
| 1131 | */ | |
| 1132 | function fb_perm() { | |
| 7217ba45 | 1133 | return array(FB_PERM_ADMINISTER); |
| 9559f7ff DC |
1134 | } |
| 1135 | ||
| 1136 | ||
| 1137 | /** | |
| f54b3a2b DC |
1138 | * Implements hook_exit(). |
| 1139 | * | |
| 1140 | * When completing a canvas page we need special processing for the session. See fb_session.inc. | |
| 1141 | * | |
| 1142 | * Also invoke hook_fb(FB_OP_EXIT), so that other modules can handle special | |
| 1143 | * cases (in particular form support in b_canvas.module. | |
| 186f033e DC |
1144 | */ |
| 1145 | function fb_exit($destination = NULL) { | |
| a46642fe | 1146 | global $_fb_app, $_fb; |
| 19092d2f | 1147 | |
| a46642fe | 1148 | if ($_fb_app && $_fb) { |
| 19092d2f | 1149 | |
| f54b3a2b | 1150 | // Invoke other modules. |
| a46642fe DC |
1151 | fb_invoke(FB_OP_EXIT, array('fb_app' => $_fb_app, |
| 1152 | 'fb' => $GLOBALS['_fb']), | |
| 19a42c4b DC |
1153 | $destination); |
| 1154 | } | |
| 186f033e DC |
1155 | } |
| 1156 | ||
| 186f033e | 1157 | |
| b1b8915b DC |
1158 | /** |
| 1159 | * Invoke hook_fb. | |
| 1160 | */ | |
| 67523320 DC |
1161 | function fb_invoke($op, $data = NULL, $return = NULL, $hook = FB_HOOK) { |
| 1162 | foreach (module_implements($hook) as $name) { | |
| 1163 | $function = $name . '_' . $hook; | |
| 8fbcf653 DC |
1164 | try { |
| 1165 | $function($op, $data, $return); | |
| 1166 | } | |
| 1167 | catch (Exception $e) { | |
| ab102d9a DC |
1168 | if (isset($data['fb_app'])) { |
| 1169 | fb_log_exception($e, t('Exception calling %function(%op) (!app)', array( | |
| 1170 | '%function' => $function, | |
| 1171 | '%op' => $op, | |
| 1172 | '%label' => $data['fb_app']->label, | |
| 75be9c97 | 1173 | '%id' => $data['fb_app']->id, |
| ab102d9a DC |
1174 | '!app' => l($data['fb_app']->label, FB_PATH_ADMIN_APPS . '/' . $data['fb_app']->label), |
| 1175 | ))); | |
| 1176 | } | |
| 1177 | else { | |
| 1178 | fb_log_exception($e, t('Exception calling %function(%op)', array( | |
| 1179 | '%function' => $function, | |
| 1180 | '%op' => $op))); | |
| 1181 | } | |
| 8fbcf653 | 1182 | } |
| 186f033e | 1183 | } |
| 3a512626 | 1184 | return $return; |
| 186f033e DC |
1185 | } |
| 1186 | ||
| 1187 | /** | |
| bed8b838 | 1188 | * This method will clean up URLs. When serving canvas pages, extra |
| 38bb647b DC |
1189 | * information is included in URLs. This will remove the extra |
| 1190 | * information. Useful when linking back to the website from a canvas page or | |
| 1191 | * wall post. | |
| 1192 | * | |
| 1193 | * For example in the following code, $url2 will link out to the server's domain: | |
| 1194 | * | |
| 1195 | * $url = url('node/42', array('absolute' => TRUE)); // i.e. http://apps.facebook.com/example/node/42 | |
| 1196 | * $url2 = fb_scrub_urls($url); // i.e. http://example.com/node/42 | |
| 1197 | * | |
| 19092d2f | 1198 | * |
| 38bb647b | 1199 | * @see fb_url_rewrite.inc |
| bed8b838 DC |
1200 | */ |
| 1201 | function fb_scrub_urls($content) { | |
| f54b3a2b DC |
1202 | if (function_exists('_fb_settings_url_rewrite_prefixes')) { |
| 1203 | foreach (_fb_settings_url_rewrite_prefixes() as $key) { | |
| 1204 | $patterns[] = "|$key/[^/]*/|"; | |
| 1205 | $replacements[] = ""; | |
| 1206 | } | |
| 1207 | $content = preg_replace($patterns, $replacements, $content); | |
| bed8b838 | 1208 | } |
| bed8b838 DC |
1209 | return $content; |
| 1210 | } | |
| 1211 | ||
| 225abd5d DC |
1212 | /** |
| 1213 | * Convenience function to log and report exceptions. | |
| 1214 | */ | |
| 48389ede | 1215 | function fb_log_exception($e, $text = '', $fb = NULL) { |
| 28922d16 DC |
1216 | if ($text) |
| 1217 | $message = $text .': '. $e->getMessage(); | |
| 1218 | else | |
| 1219 | $message = $e->getMessage(); | |
| 1220 | $message .= ' ' . $e->getCode(); | |
| 19092d2f | 1221 | |
| 48389ede | 1222 | if ($fb) { |
| 5d492838 | 1223 | $message .= '. (' . t('logged into facebook as %fbu', array('%fbu' => $fb->getUser())) . ')'; |
| 48389ede | 1224 | } |
| 360cf127 DC |
1225 | if (fb_verbose()) { |
| 1226 | $message .= '<pre>' . $e . '</pre>'; | |
| 1227 | } | |
| a9167ce4 | 1228 | watchdog('fb', $message, array(), WATCHDOG_ERROR); |
| 9559f7ff | 1229 | if (user_access(FB_PERM_ADMINISTER)) { |
| 19092d2f | 1230 | drupal_set_message($message, 'error'); |
| 28922d16 DC |
1231 | } |
| 1232 | } | |
| 1233 | ||
| 0f4300e5 DC |
1234 | /** |
| 1235 | * Exception handler for PHP5 exceptions. | |
| 1236 | */ | |
| 28922d16 | 1237 | function fb_handle_exception($exception) { |
| 7a2b37de DC |
1238 | $message = t('Facebook API exception %message. !trace', array( |
| 1239 | '%message' => $exception->getMessage(), | |
| 1240 | '!trace' => '<pre>'. $exception->getTraceAsString() .'</pre>', | |
| 1241 | )); | |
| a9167ce4 | 1242 | watchdog('fb', $message, array(), WATCHDOG_ERROR); |
| bed8b838 | 1243 | //drupal_set_message($message, 'error'); |
| 0f4300e5 | 1244 | print $message; |
| bed8b838 DC |
1245 | |
| 1246 | print "<pre>\$_REQUEST:\n"; | |
| 1247 | print_r($_REQUEST); | |
| 7a2b37de | 1248 | print "\n\nREQUEST_URI:\n" . request_uri(); |
| bed8b838 DC |
1249 | print "</pre>"; |
| 1250 | ||
| 0f4300e5 DC |
1251 | } |
| 1252 | ||
| 5d2a0a39 | 1253 | /** |
| 49c878eb DC |
1254 | * Simple wrapper around $fb->api() which caches data. Does not support the |
| 1255 | * polymorphic arguments of $fb->api(). This is not a replacement for that | |
| 1256 | * function. | |
| 02376a09 DC |
1257 | * |
| 1258 | * This is intended to avoid performace problems when, for example, | |
| 1259 | * $fb->api('me') is called several times in a single request. | |
| 49c878eb DC |
1260 | * |
| 1261 | * @param $graph_path | |
| 1262 | * Something facebook's graph API will understand. Could be an ID or a path like 'me/accounts', for example. | |
| 1263 | * | |
| 1264 | * @param $params | |
| 1265 | * Extras to pass to the graph API. When making a request that requires a | |
| 1266 | * token, try array('access_token' => fb_get_token()). | |
| 02376a09 | 1267 | */ |
| 49c878eb | 1268 | function fb_api($graph_path, $params = array()) { |
| 02376a09 DC |
1269 | static $cache; |
| 1270 | $fb = $GLOBALS['_fb']; | |
| 1271 | if (!$fb) { | |
| 1272 | return; | |
| 1273 | } | |
| 1274 | if (!isset($cache)) { | |
| 1275 | $cache = array(); | |
| 1276 | } | |
| 1277 | if (!isset($cache[$graph_path])) { | |
| 49c878eb | 1278 | $cache[$graph_path] = $fb->api($graph_path, $params); |
| 02376a09 DC |
1279 | } |
| 1280 | return $cache[$graph_path]; | |
| 1281 | } | |
| 1282 | ||
| 1283 | /** | |
| 1284 | * DEPRECATED. Use fb_api() instead. | |
| dcb2712f | 1285 | * Returns information about one or more facebook users. |
| 5d2a0a39 | 1286 | * |
| dcb2712f DC |
1287 | * Historically, this helper function used facebook's users_getInfo API, hence |
| 1288 | * the name. Now it uses fql.query, but accomplishes the same thing. | |
| 5d2a0a39 | 1289 | * |
| 5d2a0a39 DC |
1290 | * @param $oids |
| 1291 | * Array of facebook object IDs. In this case they should each be a user id. | |
| dcb2712f DC |
1292 | * |
| 1293 | * @param $fb | |
| 1294 | * Rarely needed. For cases when global $_fb is not set, or more than one | |
| 1295 | * facebook api has been initialized. | |
| 1296 | * | |
| 1297 | * @param $refresh_cache | |
| 1298 | * If true, force a call to facebook instead of relying on temporarily stored | |
| 1299 | * data. | |
| 5d2a0a39 | 1300 | */ |
| 7e665271 | 1301 | function fb_users_getInfo($oids, $fb = NULL, $refresh_cache = FALSE) { |
| 5d2a0a39 | 1302 | if (!$fb) { |
| a46642fe | 1303 | $fb = $GLOBALS['_fb']; |
| 5d2a0a39 DC |
1304 | } |
| 1305 | $infos = array(); | |
| dcb2712f | 1306 | |
| 74c6f7b6 DC |
1307 | if (!is_array($oids)) |
| 1308 | $oids = array(); | |
| dcb2712f | 1309 | |
| 5d2a0a39 | 1310 | if ($fb) { |
| 920d803c | 1311 | $app_id = $fb->getAppId(); |
| 5d2a0a39 | 1312 | // First try cache |
| 8f0e1ce9 | 1313 | if (!$refresh_cache && isset($_SESSION['fb'])) { |
| 7e665271 | 1314 | foreach ($oids as $oid) { |
| 920d803c DC |
1315 | if (isset($_SESSION['fb'][$app_id]['userinfo'][$oid])) { |
| 1316 | $info = $_SESSION['fb'][$app_id]['userinfo'][$oid]; | |
| 7e665271 | 1317 | $infos[] = $info; |
| cd11871d | 1318 | } |
| 7e665271 | 1319 | } |
| 8f0e1ce9 | 1320 | } |
| 5d2a0a39 DC |
1321 | if (count($infos) != count($oids)) { |
| 1322 | // Session cache did not include all users, update the cache. | |
| dcb2712f DC |
1323 | $fields = array( |
| 1324 | 'about_me', | |
| 1325 | 'affiliations', | |
| 1326 | 'name', | |
| 1327 | 'is_app_user', | |
| 1328 | 'pic', | |
| 1329 | 'pic_big', | |
| 1330 | 'pic_square', | |
| 1331 | 'profile_update_time', | |
| 1332 | 'proxied_email', | |
| 1333 | 'status', | |
| 1334 | 'email_hashes', | |
| 1335 | 'email', | |
| b4f75c02 | 1336 | 'uid', |
| dcb2712f | 1337 | ); |
| 5d492838 | 1338 | try { |
| 81618599 | 1339 | $infos = fb_fql_query($fb, 'SELECT ' . implode(', ', $fields) . ' FROM user WHERE uid in(' . implode(', ', $oids) . ')', array(fb_get_token($fb))); |
| 5d492838 DC |
1340 | // Update cache with recent results. |
| 1341 | if (is_array($infos)) { | |
| 1342 | foreach ($infos as $info) { | |
| 920d803c | 1343 | $_SESSION['fb'][$app_id]['userinfo'][$info['uid']] = $info; |
| 5d492838 | 1344 | } |
| a0534e56 | 1345 | } |
| cd64a628 | 1346 | } catch (FacebookApiException $e) { |
| dcb2712f | 1347 | fb_log_exception($e, t('Failed to query facebook user info'), $fb); |
| 5d2a0a39 DC |
1348 | } |
| 1349 | } | |
| dcb2712f | 1350 | |
| 3946309e | 1351 | return $infos; |
| 01c37060 | 1352 | } |
| 01c37060 DC |
1353 | } |
| 1354 | ||
| 1355 | /** | |
| 1356 | * For debugging, add $conf['fb_verbose'] = TRUE; to settings.php. | |
| 1357 | */ | |
| 1d3d574b | 1358 | function fb_verbose() { |
| b24ac6ad | 1359 | return variable_get(FB_VAR_VERBOSE, NULL); |
| 1d3d574b | 1360 | } |
| ebb5649d | 1361 | |
| 56c9f8cc | 1362 | /** |
| 613fc5c5 DC |
1363 | * This function will be replaced, hopefully, by format_username in D7. |
| 1364 | * | |
| 225abd5d | 1365 | * @see http://drupal.org/node/192056 |
| 613fc5c5 DC |
1366 | */ |
| 1367 | function fb_format_username($account) { | |
| 1368 | $name = !empty($account->name) ? $account->name : variable_get('anonymous', t('Anonymous')); | |
| 1369 | drupal_alter('username', $name, $account); | |
| 1370 | return $name; | |
| 1371 | } | |
| 1372 | ||
| 1373 | /** | |
| 1374 | * hook_username_alter(). | |
| 1375 | * | |
| 1376 | * Return a user's facebook name, instead of local username. | |
| 1377 | */ | |
| 1378 | function fb_username_alter(&$name, $account) { | |
| 374c5a75 DC |
1379 | if ($fbu = fb_get_fbu($account)) { // @TODO - is fb_get_fbu() a performance hit here? |
| 1380 | $info = fb_users_getInfo(array($fbu)); | |
| 11665fdd | 1381 | if (is_array($info) && isset($info[0])) { |
| 7217ba45 DC |
1382 | if ($info[0]['name']) { |
| 1383 | $name = $info[0]['name']; | |
| 1384 | } | |
| 613fc5c5 DC |
1385 | } |
| 1386 | } | |
| 1387 | } | |
| 3946309e | 1388 | |
| 8dd84187 DC |
1389 | /** |
| 1390 | * Implements hook_theme(). | |
| 225abd5d DC |
1391 | * |
| 1392 | * Returns description of theme functions. | |
| 1393 | * | |
| 1394 | * @see fb.theme.inc | |
| 8dd84187 DC |
1395 | */ |
| 1396 | function fb_theme() { | |
| 1397 | return array( | |
| 1398 | 'fb_username' => array( | |
| 19092d2f | 1399 | 'arguments' => array( |
| 8dd84187 DC |
1400 | 'fbu' => NULL, |
| 1401 | 'object' => NULL, | |
| 1402 | 'orig' => NULL, | |
| 1403 | ), | |
| 1404 | 'file' => 'fb.theme.inc', | |
| 1405 | ), | |
| 1406 | 'fb_user_picture' => array( | |
| 19092d2f | 1407 | 'arguments' => array( |
| 8dd84187 DC |
1408 | 'fbu' => NULL, |
| 1409 | 'account' => NULL, | |
| 1410 | 'orig' => NULL, | |
| 1411 | ), | |
| 1412 | 'file' => 'fb.theme.inc', | |
| 1413 | ), | |
| 7d7e57b4 | 1414 | 'fb_fbml_popup' => array( |
| 8dd84187 DC |
1415 | 'arguments' => array('elements' => NULL), |
| 1416 | 'file' => 'fb.theme.inc', | |
| 1417 | ), | |
| 1418 | 'fb_login_button' => array( | |
| 1419 | 'arguments' => array( | |
| 1420 | 'text' => 'Connect with Facebook', | |
| 1421 | 'options' => NULL), | |
| 1422 | 'file' => 'fb.theme.inc', | |
| 1423 | ), | |
| 1424 | ); | |
| 1425 | } | |
| 1426 | ||
| ebb5649d DC |
1427 | //// Javascript and Ajax helpers |
| 1428 | ||
| 1429 | /** | |
| 1430 | * Ajax javascript callback. | |
| 1431 | * | |
| 1432 | * For sites which use ajax, various events may create javascript which is | |
| 1433 | * normally embedded in a page. For example, posting to a user's wall. When | |
| 1434 | * ajax is used instead of a page reload, this callback will provide any | |
| 1435 | * javascript which should be run. | |
| 1436 | */ | |
| 1437 | function fb_js_cb() { | |
| 1438 | $js_array = fb_invoke(FB_OP_JS, array('fb' => $GLOBALS['_fb'], 'fb_app' => $GLOBALS['_fb_app']), array()); | |
| 1439 | $extra_js = implode("\n", $extra); | |
| 1440 | print $extra_js; | |
| 1441 | exit(); | |
| 1442 | } | |
| 3946309e | 1443 | |
| a24a1868 DC |
1444 | /** |
| 1445 | * Ajax callback handles an event from facebook's javascript sdk. | |
| 1446 | * | |
| d32d32f8 DC |
1447 | * @see |
| 1448 | * fb.js and | |
| 1449 | * http://developers.facebook.com/docs/reference/javascript/FB.Event.subscribe | |
| a24a1868 | 1450 | * |
| d32d32f8 DC |
1451 | * @return |
| 1452 | * Array of javascript to be evaluated by the page which called this | |
| 1453 | * callback. | |
| a24a1868 DC |
1454 | */ |
| 1455 | function fb_ajax_event($event_type) { | |
| 92a6e858 | 1456 | global $_fb, $_fb_app; |
| ab102d9a | 1457 | $js_array = array(); |
| 19092d2f | 1458 | |
| 75be9c97 DC |
1459 | if (isset($_REQUEST['appId'])) { |
| 1460 | $_fb_app = fb_get_app(array('id' => $_REQUEST['appId'])); | |
| ab102d9a DC |
1461 | if ($_fb_app) { |
| 1462 | $_fb = fb_api_init($_fb_app); | |
| 1463 | // Data to pass to hook_fb. | |
| 1464 | $data = array( | |
| 1465 | 'fb_app' => $_fb_app, | |
| 1466 | 'fb' => $_fb, | |
| 1467 | 'event_type' => $event_type, | |
| 1468 | 'event_data' => $_POST, // POSTed via ajax. | |
| 1469 | ); | |
| 19092d2f | 1470 | |
| ab102d9a | 1471 | $js_array = fb_invoke(FB_OP_AJAX_EVENT, $data, array()); |
| 19092d2f | 1472 | |
| 91e9397d DC |
1473 | } |
| 1474 | else { | |
| 75be9c97 | 1475 | watchdog('fb', 'fb_ajax_event did not find application %id', array('%id' => $_REQUEST['appId']), WATCHDOG_ERROR); |
| 91e9397d | 1476 | } |
| 875843a4 | 1477 | |
| 91e9397d DC |
1478 | if ($event_type == 'session_change') { |
| 1479 | // Session change is a special case. If user has logged out of | |
| 1480 | // facebook, we want a new drupal session. We do this here, even if | |
| 1481 | // fb_user.module is not enabled. | |
| 1482 | if (!isset($_POST['fbu']) || !$_POST['fbu']) { // Logout, not login. | |
| 1483 | _fb_logout(); | |
| 1484 | } | |
| ab102d9a | 1485 | } |
| 91e9397d | 1486 | |
| ab102d9a DC |
1487 | } |
| 1488 | else { | |
| 6b8f8dcc | 1489 | watchdog('fb', 'fb_ajax_event called badly. Not passed appId.', array(), WATCHDOG_ERROR); |
| 875843a4 | 1490 | // Trying to track down what makes this happen. |
| 8c514893 | 1491 | if (fb_verbose() == 'extreme') { |
| 6b8f8dcc | 1492 | watchdog('fb', 'fb_ajax_event called badly. Not passed appId. trace: !trace', array( |
| 91e9397d DC |
1493 | '!trace' => '<pre>' . print_r(debug_backtrace(), 1) . '</pre>', |
| 1494 | ), WATCHDOG_ERROR); | |
| ab102d9a | 1495 | } |
| 7308ca43 | 1496 | } |
| 7308ca43 | 1497 | drupal_json($js_array); |
| a24a1868 | 1498 | exit(); |
| 7308ca43 | 1499 | } |
| 1461dd7b DC |
1500 | |
| 1501 | /** | |
| 0de240eb DC |
1502 | * Menu callback for custom channel. |
| 1503 | * | |
| 1504 | * @see http://developers.facebook.com/docs/reference/javascript/FB.init | |
| 1505 | */ | |
| 1506 | function fb_channel_page() { | |
| 1507 | //headers instructing browser to cache this page. | |
| 1508 | // Do these work? | |
| 1509 | drupal_set_header("Cache-Control: public"); | |
| 1510 | drupal_set_header("Expires: Sun, 17-Jan-2038 19:14:07 GMT"); | |
| 1511 | ||
| 1512 | $date = format_date(time()); | |
| 1513 | $output = "<!-- modules/fb fb_channel_page() $date -->\n"; | |
| 1514 | $url = fb_js_settings('js_sdk_url'); | |
| 1515 | $output .= "<script src=\"$url\"></script>\n"; | |
| 1516 | print $output; | |
| 1517 | exit(); | |
| 562c4b37 DC |
1518 | } |
| 1519 | ||
| 1520 | //// Miscellaneous helpers and convenience functions. | |
| 1521 | ||
| 1522 | /** | |
| 1523 | * Protocol (http or https) of the current request. | |
| 1524 | */ | |
| 1525 | function fb_protocol() { | |
| 1526 | return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http'; | |
| 1527 | } | |
| 1528 | ||
| 1529 | ||
| 1530 | /** | |
| 1531 | * Convenience wrapper around drupal_access_denied(). Call on pages where the | |
| 1532 | * access is denied because the user is not logged into facebook. | |
| 1533 | */ | |
| 1534 | function fb_access_denied() { | |
| 1535 | if (!fb_facebook_user()) { | |
| 1536 | drupal_set_message(t('You must <a href="#" onclick="FB.login(function(response) {}, {perms:Drupal.settings.fb.perms}); return false;">log into facebook to view this page</a>.')); | |
| 1537 | } | |
| 1538 | drupal_access_denied(); | |
| 1539 | exit(); | |
| 1540 | } | |
| 1541 |