You are here

fb.module in Drupal for Facebook 6.2

Same filename and directory in other branches
  1. 5.2 fb.module
  2. 5 fb.module
  3. 6.3 fb.module
  4. 7.4 fb.module
  5. 7.3 fb.module

File

fb.module
View source
<?php

// hook_fb
define('FB_HOOK', 'fb');

// Paths.
define('FB_PATH_ADMIN', 'admin/build/fb');
define('FB_PATH_ADMIN_ARGS', 3);

// args in path.
define('FB_PATH_ADMIN_APPS', 'admin/build/fb/app');
define('FB_PATH_ADMIN_APPS_ARGS', 4);

// permissions
define('FB_PERM_ADMINISTER', 'administer fb apps');

// Ops for hook_fb.
define('FB_OP_GET_APP', 'get_app');

// Load data from a known app
define('FB_OP_GET_ALL_APPS', 'get_all_apps');

// Load data about all apps
define('FB_OP_CURRENT_APP', 'current_app');

// determine active app in canvas page or facebook connect
define('FB_OP_INITIALIZE', 'init');

//
define('FB_OP_POST_INIT', 'post init');

//
define('FB_OP_EXIT', 'exit');

// End an FB callback
define('FB_OP_GET_FBU', 'get_fbu');

// Query the local user's FB account
define('FB_OP_GET_USER_SESSION', 'get_user_sess');
define('FB_OP_PRE_USER', 'pre_user');

// Before account creation, fb_user.module
define('FB_OP_POST_USER', 'post_user');

// After account creation, fb_user.module
define('FB_OP_APP_IS_AUTHORIZED', 'app_authorized');

// Invoked if user has authorized an app.  Triggers creation of user accounts or authmap entries
define('FB_OP_CONNECT_JS_INIT', 'fb_connect_js_init');

// A chance to customize fbConnect javascript
define('FB_OP_CANVAS_FBJS_INIT', 'fb_canvas_js_init');

// A chance to customize FBJS.
define('FB_OP_CANVAS_EXIT', 'fb_op_canvas_exit');

// hook_exit for canvas pages.
// node_access realms (belongs here?)
define('FB_GRANT_REALM_FRIEND', 'fb_friend');
define('FB_GRANT_REALM_GROUP', 'fb_group');

// When initializing Facebook API, which user to log in as:
define('FB_FBU_NO_SESSION', 'fbu_no_session');

// http://wiki.developers.facebook.com/index.php/Category:Sessionless_API
// 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!
define('FB_FBU_CURRENT', 'fbu_current');

// Canvas pages and Connect pages
define('FB_FBU_ANY', 'fbu_any');

// Use current user on canvas page, fall back to infinite session otherwise.

//// Constants for internal use
define('FB_APP_CURRENT', '000_app_current');

// Canvas pages only.  000 makes it appear first in options list

/**
 * Implementation of hook_init
 * 
 * Determines whether we are servicing a Facebook App request.
 * 
 * We invoke our hook, first to determine which application is being invoked.
 * (Because we support more than one in the same Drupal instance.)  Then, we
 * notify interested modules in various events.
 * 
 */
function fb_init() {

  // Globals provided for internal use and convenience to third-parties.
  global $_fb;
  global $_fb_app;

  // http://drupal.org/node/329810
  if (!function_exists('arg')) {

    // Ensure arg function is defined.
    drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);
  }

  // Perform sanity check, help users who skip the README.
  if (!function_exists('fb_settings')) {

    // Avoid fatal errors caused by settings not included.
    module_load_include('inc', 'fb', 'fb_settings');
    if (user_access('access administration pages')) {
      drupal_set_message(t('!drupal_for_facebook has been enabled, but not properly installed.  Please read the !readme.', array(
        '!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'),
        // This link should work with clean URLs
        // disabled.
        '!readme' => '<a href=' . base_path() . drupal_get_path('module', 'fb') . '/README.txt>README.txt</a>',
      )), 'error');
    }
  }

  // Ask other modules for app details.
  $_fb_app = fb_invoke(FB_OP_CURRENT_APP);

  // DEPRECATED global $fb_app has been renamed to $_fb_app.
  $GLOBALS['fb_app'] = $_fb_app;

  // For compatibility with old third-party modules.
  if ($_fb_app) {

    // we are in a callback
    // For canvas and connect pages, use current user.
    $_fb = fb_api_init($_fb_app, FB_FBU_CURRENT);

    // DEPRECATED global $fb has been renamed to $_fb.
    $GLOBALS['fb'] = $_fb;

    // For compatibility with old third-party modul
    if ($_fb) {

      // Give other modules a chance to initialize, require login, etc...
      fb_invoke(FB_OP_INITIALIZE, array(
        'fb_app' => $_fb_app,
        'fb' => $_fb,
      ));

      // See if the facebook user id is known
      if ($fbu = $_fb
        ->get_loggedin_user()) {
        fb_invoke(FB_OP_APP_IS_AUTHORIZED, array(
          'fb_app' => $_fb_app,
          'fb' => $_fb,
          'fbu' => $fbu,
        ));
      }
    }
    else {
      watchdog('fb', "URL indicates a facebook app, but could not initialize Facebook", array(), WATCHDOG_ERROR);
    }
  }
  fb_invoke(FB_OP_POST_INIT, array(
    'fb_app' => $_fb_app,
    'fb' => $_fb,
  ));
}

/**
 * Include the necessary facebook-platform code and initialize the API object.
 * 
 * @param fbu To log into facebook as a particular user, pass the facebook id.
 * This is useful during cron jobs, for example, but rarely if ever needed on
 * a canvas page.  If no valid session key is known for the user, this call
 * will still return a facebook reference.
 * 
 * If FB_FBU_ANY is passed in, we will log in as the canvas page user if
 * already logged in.  Otherwise we try the infinite session, if configured.
 * 
 * Future calls into the the facebook api could fail for various reasons.  For
 * one, we may fail to log in as the specified user.  This call does not
 * actually contact facebook to test the connection, it just sets things up so
 * that when the connection is needed, it might work.  Even if the connection
 * is established, the user may not have sufficient permission to whatever you
 * are asking facebook to do.
 * 
 */
function fb_api_init($fb_app, $fbu = FB_FBU_CURRENT) {

  //dpm(func_get_args(), "fb_api_init");

  //$trace = debug_backtrace();

  //dpm($trace, "fb_api_init call stack");
  static $cache = array();

  // This helps with uncaught exceptions.  However, it should be configurable
  // or at least not overwrite previously declared handler.
  set_exception_handler('fb_handle_exception');
  if (!count($cache) && !class_exists('Facebook')) {
    $filename = variable_get('fb_api_file', 'facebook-platform/php/facebook.php');
    if (!(include $filename)) {
      $message = t('Failed to find the Facebook client libraries at %filename.  Read the !readme and follow the instructions carefully.', array(
        '!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'),
        // This link should work with clean URLs disabled.
        '!readme' => '<a href=' . base_path() . '/' . drupal_get_path('module', 'fb') . '/README.txt>README.txt</a>',
        '%filename' => $filename,
      ));
      drupal_set_message($message, 'error');
      watchdog('fb', $message);
      return NULL;
    }
  }

  // This global comes from facebook's client API.
  $GLOBAL['facebook_config']['debug'] = variable_get('fb_debug', FALSE);

  // set TRUE for debug output from FB API
  if (!isset($cache[$fb_app->apikey])) {
    $cache[$fb_app->apikey] = array();
  }
  $fbu_orig = $fbu;

  // Avoid E_NOTICE errors and keep code below clean.
  if (isset($GLOBALS['_fb'])) {
    $global_fb = $GLOBALS['_fb'];
  }
  else {
    $global_fb = (object) array(
      'api_key' => NULL,
    );
  }

  // Determine the actual facebook user id to use.
  if ($global_fb->api_key == $fb_app->apikey && ($fbu == FB_FBU_CURRENT || $fbu == FB_FBU_ANY || $fbu == $GLOBALS['_fb']
    ->get_loggedin_user())) {
    return $GLOBALS['_fb'];
  }
  elseif ($global_fb->api_key == $fb_app->apikey && $fbu == FB_FBU_CURRENT) {

    // No current user to use, probably anonymous canvas page.
    return $GLOBALS['_fb'];
  }
  elseif (is_numeric($fbu)) {

    // FB user id passed in.  If we happen to have valid session info for
    // them, we can log in as them.
    $data = fb_invoke(FB_OP_GET_USER_SESSION, array(
      'fb_app' => $fb_app,
      'fbu' => $fbu,
    ), array());
    if (count($data) && $data[0] && $data[1]) {
      $fbu = $data[0];
      $session = $data[1];
    }
  }
  if (!isset($cache[$fb_app->apikey][$fbu])) {
    try {

      // We don't have a cached resource for this app/user combo, so we're going to create one.
      $fb = new Facebook($fb_app->apikey, $fb_app->secret);

      // If we learned the session, above, use it
      if ($fbu && isset($session)) {
        $fb
          ->set_user($fbu, $session);
      }
      elseif ($fbu && $fbu == $fb
        ->get_loggedin_user()) {

        // Canvas page or Connect page, current user is logged in already.
        // Nothing to do here.
      }
      elseif ($fbu == FB_FBU_NO_SESSION) {
        $fb
          ->set_user(NULL, NULL);
      }

      // Cache the result, in case we're called again.
      $cache[$fb_app->apikey][$fbu] = $fb;

      // Note that facebook api has not actually logged into facebook yet.
      // We won't really know if our session is valid until later.
      // get_loggedin_user does not really test it.
      if ($fbu_orig != FB_FBU_NO_SESSION && $fbu_orig != FB_FBU_CURRENT && !$fb
        ->get_loggedin_user()) {

        // An FBU other than CURRENT was specified, but we failed to log in.
        watchdog('fb', 'Failed to log into facebook app %app as user %user', array(
          '%app' => $fb_app->title,
          '%user' => $fbu_orig,
        ), WATCHDOG_ERROR);
      }
    } catch (Exception $e) {
      fb_log_exception($e, t('Failed to construct Facebook client API.'));
    }
  }
  $fb = $cache[$fb_app->apikey][$fbu];
  return $fb;
}

/**
 * Wrapper function for fb_api_init.  This helps for functions that should
 * work whether or not we are on a canvas page.  For canvas pages, the active
 * fb object is used.  For non-canvas pages, it will initialize the API using
 * an infinite session, if configured.
 * 
 * @param $fb_app Note this is ignored on canvas pages.
 * 
 * This is for internal use.  Third party modules use fb_api_init().
 */
function _fb_api_init($fb_app = NULL) {
  $fb = $GLOBALS['_fb'];

  // Default to active app on canvas pages
  if (!$fb && $fb_app) {

    // Otherwise, log into facebook api.
    $fb = fb_api_init($fb_app, FB_FBU_ANY);
  }
  if (!$fb) {
    watchdog('fb', '%function unable to initialize Facebook API.', array(
      '%function' => '_fb_api_init()',
    ), WATCHDOG_ERROR);
    return;
  }
  else {
    return $fb;
  }
}

/**
 * Sometimes calls to fb_api_init succeed, but calls to the client api
 * will fail because cookies are obsolete or what have you.  This
 * function makes a call to facebook to test the session.  Expensive,
 * so use only when necessary.
 */
function fb_api_check_session($fb) {

  // TODO: caching
  $success = FALSE;
  try {
    $is_user = $fb->api_client
      ->users_isAppUser();

    // Does not matter what is returned, as long as exception is not thrown.
    $success = TRUE;
  } catch (Exception $e) {
    $success = FALSE;
  }
  return $success;
}

/**
 * Returns the facebook user id currently visiting a canvas page, or if set_user has been called.
 * Unlike fb_get_fbu(), works only on canvas pages or when infinite session has been initialized.
 */
function fb_facebook_user($fb = NULL) {
  if (!isset($fb)) {
    $fb = $GLOBALS['_fb'];
  }
  if (!$fb) {
    return;
  }
  $fbu = $fb
    ->get_loggedin_user();
  if (isset($fb->api_client->error_code)) {
    if (fb_verbose()) {
      watchdog('fb', 'Failed to get Facebook user id.  detail: !detail', array(
        '!detail' => print_r($_REQUEST, 1),
      ), WATCHDOG_ERROR);
    }
  }
  return $fbu;
}

/**
 * Determine whether current page is FBML canvas page.
 */
function fb_is_fbml_canvas() {
  if (function_exists('fb_canvas_is_fbml')) {

    // More complex logic if fb_canvas.module enabled.
    return fb_canvas_is_fbml();
  }
  else {
    if (isset($GLOBALS['_fb'])) {
      return $GLOBALS['_fb']
        ->in_fb_canvas();
    }
  }
}

/**
 * Determine whether current page is iframe canvas page.
 */
function fb_is_iframe_canvas() {
  if (function_exists('fb_canvas_is_iframe')) {

    // More complex logic if fb_canvas.module enabled.
    return fb_canvas_is_iframe();
  }
  else {
    if (isset($GLOBALS['_fb'])) {
      return $GLOBALS['_fb']
        ->in_frame();
    }
  }
}
function fb_is_connect() {

  // fb_settings.inc sets this global.
  if (isset($GLOBALS['_fb_connect_apikey'])) {
    return $GLOBALS['_fb_connect_apikey'];
  }
  elseif (function_exists('fb_connect_get_app')) {
    $fb_app = fb_connect_get_app();
    if (isset($fb_app)) {
      return $fb_app->apikey;
    }
  }
}

/**
 * Helper tells other modules when to load admin hooks.
 */
function fb_is_fb_admin_page() {
  if (arg(0) == 'admin' && (arg(1) == 'fb' || arg(2) == 'fb')) {

    // Keep consistant titles across tabs served by multiple modules.
    if ($label = arg(FB_PATH_ADMIN_APPS_ARGS)) {
      drupal_set_title($label);
    }
    else {
      drupal_set_title(t('Drupal for Facebook'));
    }
    return TRUE;
  }
}

/**
 * Determine whether we are rendering a profile tab.
 */
function fb_is_profile_tab() {
  if (isset($_REQUEST['fb_sig_in_profile_tab'])) {
    return $_REQUEST['fb_sig_in_profile_tab'];
  }
}
function fb_get_profile_id() {
  if (isset($_REQUEST['fb_sig_profile_id'])) {
    return $_REQUEST['fb_sig_profile_id'];
  }
}

/**
 * Facebook provides a method, users_isAppUser(), which is buggy and
 * unreliable.  So we need to implement our own.
 */
function fb_is_app_user($fb) {
  if ($fb->api_client->added || $fb->api_client->is_user) {
    return TRUE;
  }
  else {
    return $fb->api_client->users_isAppUser;
  }
}

/**
 * Given a local user id, find the facebook id.
 */
function fb_get_fbu($uid, $fb_app = NULL) {

  // default to current app (only set if we're in a FB callback)
  if (!$fb_app) {
    $fb_app = $GLOBALS['_fb_app'];
  }

  // Accept either a user object or uid passed in.
  if (is_object($uid) && $uid->uid && isset($uid->fbu) && $uid->fbu) {
    return $uid->fbu;
  }
  elseif (is_object($uid)) {
    $uid = $uid->uid;
  }
  if ($uid) {

    // User management is handled by another module.  Use our hook to ask for mapping.
    $fbu = fb_invoke(FB_OP_GET_FBU, array(
      'fb_app' => $fb_app,
      'uid' => $uid,
      'fb' => $GLOBALS['_fb'],
    ));
  }
  return $fbu;
}

/**
 * Convenience function to learn the fbu associated with a user, node or comment.
 * Used in theming (X)FBML tags.
 */
function fb_get_object_fbu($object) {
  static $cache;
  if (!isset($cache)) {
    $cache = array();
  }
  if (isset($object->uid) && isset($cache[$object->uid])) {
    $fbu = $cache[$object->uid];
    return $fbu;
  }
  elseif (isset($object->fbu)) {

    // Explicitly set.
    $fbu = $object->fbu;
  }
  elseif ($pos = strpos($object->name, '@facebook')) {

    // Naming convention.
    $fbu = substr($object->name, 0, $pos);
  }
  elseif ($object->uid > 0) {

    // Experimental.  This can be expensive on pages with many comments or nodes!

    //$fbu = fb_get_fbu($object->uid);
  }
  if (isset($fbu) && is_numeric($fbu)) {
    if (isset($object->uid) && $object->uid > 0) {
      $cache[$object->uid] = $fbu;
    }
    return $fbu;
  }
}

/**
 * Convenience method to get app info based on apikey or nid.
 */
function fb_get_app($search_data) {

  // $search_data can be an apikey, or an array of other search params.
  if (!is_array($search_data)) {
    $search_data = array(
      'apikey' => $search_data,
    );
  }
  $fb_app = fb_invoke(FB_OP_GET_APP, $search_data);
  return $fb_app;
}

/**
 * Convenience method for other modules to attach data to the fb_app
 * object.  
 * 
 * It is assumed the fb_app implementation will fill in the data
 * field.  We really should clean up the separation between modules,
 * or merge fb_app.module into this one.
 */
function fb_get_app_data(&$fb_app) {
  if (!isset($fb_app->fb_app_data)) {
    $fb_app->fb_app_data = isset($fb_app->data) ? unserialize($fb_app->data) : array();
  }
  return $fb_app->fb_app_data;
}

/**
 * Will return a human-readable name if the fb_app module supports it, or
 * fb_admin_get_app_properties($fb_app) has been called.  However we don't
 * take the relatively expensive step of calling that ourselves.
 */
function fb_get_app_title($fb_app) {
  if (isset($fb_app->title)) {
    return $fb_app->title;
  }
  elseif (isset($fb_app->application_name)) {
    return $fb_app->application_name;
  }
  else {
    return $fb_app->label;
  }
}

/**
 * Convenience method to return array of all know fb_apps.
 */
function fb_get_all_apps() {
  $apps = fb_invoke(FB_OP_GET_ALL_APPS, NULL, array());
  return $apps;
}

/**
 * A convenience method for returning a list of facebook friends.  
 *
 * This should work efficiently in canvas pages for finding friends of
 * the current user.  In other cases it tries to work, but will be an
 * expensive operation and only succeed when the user is logged in via
 * Connect, or has created an infinite session.
 * 
 * @return: an array of facebook ids
 */
function fb_get_friends($fbu, $fb_app = NULL) {
  static $cache = array();
  if (!$fb_app) {
    $fb_app = $GLOBALS['_fb_app'];
  }

  // Facebook only allows us to query the current user's friends, so let's try
  // to log in as that user.  It will only actually work if they are the
  // current user of a canvas page, or they've signed up for an infinite
  // session.
  $fb = fb_api_init($fb_app, $fbu);
  if (!$fb || !$fbu) {
    return;
  }
  if (!isset($cache[$fbu])) {
    if ($fb === $GLOBALS['_fb'] && $fbu == fb_facebook_user($fb)) {
      $items = $fb->api_client
        ->friends_get();
    }

    // friends_get does not work in cron call, so we double check.
    if (!$items || !count($items)) {
      $logged_in = fb_facebook_user($fb);
      $query = "SELECT uid2 FROM friend WHERE uid1={$fbu}";

      // FQL, no {curly_brackets}!
      $result = $fb->api_client
        ->fql_query($query);
      fb_report_errors($fb);
      $items = array();
      if (is_array($result)) {
        foreach ($result as $data) {
          $items[] = $data['uid2'];
        }
      }
    }

    // Facebook's API has the annoying habit of returning an item even if user
    // has no friends.  We need to clean that up.
    if (!$items[0]) {
      unset($items[0]);
    }
    $cache[$fbu] = $items;
  }
  return $cache[$fbu];
}

// Return array of facebook gids
function fb_get_groups($fbu, $fb_app = NULL) {
  $items = array();
  $groups = fb_get_groups_data($fbu);
  if ($groups && count($groups)) {
    foreach ($groups as $data) {
      $items[] = $data['gid'];
    }
  }
  return $items;
}
function fb_get_groups_data($fbu, $fb_app = NULL) {
  static $cache = array();
  $fb = _fb_api_init($fb_app);
  if (!$fb || !$fbu) {
    return;
  }
  if (!isset($cache[$fbu])) {
    $cache[$fbu] = $fb->api_client
      ->groups_get($fbu, NULL);
  }
  return $cache[$fbu];
}

// deprecated since creation of fb_user module, but cron hook still uses this.
function fb_user_load($fbu = NULL) {
  global $user;
  if (!$fbu) {

    // default to current logged in user
    $fbu = fb_facebook_user();
  }
  if ($fbu && $user->fbu == $fbu) {
    return $user;
  }
  if ($fbu) {
    $account = user_external_load("{$fbu}-{$fb_app->apikey}@facebook.com");
    if (!$account) {
      $account = user_external_load("{$fbu}@facebook.com");
    }
    if (!$account) {
      $account = user_load(array(
        'uid' => variable_get('fb_facebook_user', 2),
      ));
    }
    if (!$account) {
      watchdog('fb', 'Failed to load user from facebook fbu=%fbu', array(
        '%fbu' => $fbu,
      ), WATCHDOG_ERROR);
    }
    $account->fbu = $fbu;
    return $account;
  }
}
function fb_form_alter(&$form, &$form_state, $form_id) {

  // Because facebook users don't have email, it can't be required on user form
  if ($form_id == 'user_register') {
    if (user_access('administer users')) {
      $form['mail']['#required'] = FALSE;
    }
  }
  if ($form_id == 'user_edit') {
    if (user_access('administer users')) {
      $form['account']['mail']['#required'] = FALSE;
    }
  }
}

//// Menu structure.

/**
 * Implementation of hook_menu().
 */
function fb_menu() {
  $items = array();

  // Admin pages overview.
  $items[FB_PATH_ADMIN] = array(
    'title' => 'Facebook Applications',
    'description' => 'Facebook Applications',
    'page callback' => 'fb_admin_page',
    'access arguments' => array(
      FB_PERM_ADMINISTER,
    ),
    'file' => 'fb.admin.inc',
    'type' => MENU_NORMAL_ITEM,
  );
  $items[FB_PATH_ADMIN . '/list'] = array(
    'title' => 'List',
    'weight' => -2,
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );

  // Admin pages for each app.
  $items[FB_PATH_ADMIN_APPS . '/%fb'] = array(
    'title' => 'Application Detail',
    'description' => 'Facebook Applications',
    'page callback' => 'fb_admin_app_page',
    'page arguments' => array(
      FB_PATH_ADMIN_APPS_ARGS,
    ),
    'access arguments' => array(
      FB_PERM_ADMINISTER,
    ),
    'file' => 'fb.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items[FB_PATH_ADMIN_APPS . '/%fb/fb'] = array(
    'title' => 'View',
    'weight' => -2,
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );

  // When forms are submitted directly to us, we cache the results,
  // and show them later via this callback
  $items['fb/form_cache'] = array(
    'page callback' => '_fb_form_cache_cb',
    'type' => MENU_CALLBACK,
    'access callback' => TRUE,
  );
  return $items;
}

/**
 * Implementation of a %wildcard_load(). http://drupal.org/node/224170
 */
function fb_load($id) {
  $query = array(
    'label' => $id,
  );
  if (fb_is_fb_admin_page()) {

    // Show disabled apps to admins.
    $query['status'] = 0;

    // status >= 0
  }
  $fb_app = fb_get_app($query);
  return $fb_app;
}

/**
 * Implementation of hook_perm().
 */
function fb_perm() {
  return array(
    FB_PERM_ADMINISTER,
  );
}

/**
 * Implements hook_exit().
 *
 * When completing a canvas page we need special processing for the session.  See fb_session.inc.
 *
 * Also invoke hook_fb(FB_OP_EXIT), so that other modules can handle special
 * cases (in particular form support in b_canvas.module.
 */
function fb_exit($destination = NULL) {
  global $_fb_app, $_fb;
  if ($_fb_app && $_fb) {

    // Session management
    if (function_exists('fb_session_exit')) {
      fb_session_exit();
    }

    // Invoke other modules.
    fb_invoke(FB_OP_EXIT, array(
      'fb_app' => $_fb_app,
      'fb' => $GLOBALS['_fb'],
    ), $destination);
  }
}
function _fb_form_cache_cb($cid) {

  // Facebook started appending a '?', we need to get rid of it.
  if ($pos = strpos($cid, '?')) {
    $cid = substr($cid, 0, $pos);
  }
  if (fb_verbose() == 'extreme') {
    watchdog('fb', "Returning cached form page {$cid}");

    // debug
  }
  $cache = cache_get($cid, 'cache_page');

  // Don't clear, as user may refresh browser.  Cache will expire eventually.
  // cache_clear_all($cid, 'cache_page');
  print $cache->data;
  exit;
}
function fb_session_key_form() {
  global $_fb_app;
  $form = array(
    'auth_token' => array(
      '#type' => 'textfield',
      '#title' => t('One-time code'),
      '#description' => t('If you do not have a one-time code, you can get one !here.', array(
        '!here' => l(t('here'), 'http://www.facebook.com/code_gen.php?v=1.0&api_key=' . $_fb_app->apikey),
      )),
    ),
    'submit' => array(
      '#type' => 'submit',
      '#value' => t('Submit'),
    ),
    '#redirect' => FALSE,
  );
  return $form;
}

/**
 * Invoke hook_fb.
 */
function fb_invoke($op, $data = NULL, $return = NULL, $hook = FB_HOOK) {
  foreach (module_implements($hook) as $name) {
    $function = $name . '_' . $hook;
    try {
      $function($op, $data, $return);
    } catch (Exception $e) {
      fb_log_exception($e, t('Exception calling %function(%op)', array(
        '%function' => $function,
        '%op' => $op,
      )));
    }
  }
  return $return;
}

/**
 * This method will clean up URLs.  When serving canvas pages, extra
 * information is included in URLs (see fb_url_rewrite.inc).  This will remove
 * the extra information.
 */
function fb_scrub_urls($content) {
  if (function_exists('_fb_settings_url_rewrite_prefixes')) {
    foreach (_fb_settings_url_rewrite_prefixes() as $key) {
      $patterns[] = "|{$key}/[^/]*/|";
      $replacements[] = "";
    }
    $content = preg_replace($patterns, $replacements, $content);
  }
  return $content;
}

/**
 * Convenience method for displaying facebook api errors.
 */
function fb_report_errors($fb = FB_APP_CURRENT, $message = NULL) {
  if ($fb == FB_APP_CURRENT) {
    $fb = $GLOBALS['_fb'];
  }
  if ($fb) {
    if (isset($fb->api_client->error_code)) {
      $message = t('!message Facebook API error %code (see !link).', array(
        '%code' => $fb->api_client->error_code,
        '!link' => l(t('error codes'), "http://wiki.developers.facebook.com/index.php/Error_codes"),
        '!message' => $message,
      ));
      watchdog('fb', $message, array(), WATCHDOG_ERROR);
      if (user_access(FB_PERM_ADMINISTER)) {
        drupal_set_message($message, 'error');
      }
    }
  }
}
function fb_log_exception($e, $text = '', $fb = NULL) {
  if ($text) {
    $message = $text . ': ' . $e
      ->getMessage();
  }
  else {
    $message = $e
      ->getMessage();
  }
  $message .= ' ' . $e
    ->getCode();
  if ($fb) {
    $message .= '. (' . t('logged into facebook as %fbu', array(
      '%fbu' => $fb
        ->get_loggedin_user(),
    )) . ')';
  }
  if (fb_verbose()) {
    $message .= '<pre>' . $e . '</pre>';
  }
  watchdog('fb', $message, array(), WATCHDOG_ERROR);
  if (user_access(FB_PERM_ADMINISTER)) {
    drupal_set_message($message, 'error');
  }
}

/**
 * Exception handler for PHP5 exceptions.
 */
function fb_handle_exception($exception) {
  $message = t('Facebook API exception %message.  !trace', array(
    '%message' => $exception
      ->getMessage(),
    '!trace' => '<pre>' . $exception
      ->getTraceAsString() . '</pre>',
  ));
  watchdog('fb', $message, array(), WATCHDOG_ERROR);

  //drupal_set_message($message, 'error');
  print $message;
  print "<pre>\$_REQUEST:\n";
  print_r($_REQUEST);
  print "\n\nREQUEST_URI:\n" . request_uri();
  print "</pre>";
}

/**
 * Helper function for facebook's users_getInfo API.
 *
 * This function makes calls to users_getInfo more efficient, by caching
 * results in the session, so calls do not always require hitting Facebook's
 * servers.
 *
 * @param $oids
 * Array of facebook object IDs.  In this case they should each be a user id.
 */
function fb_users_getInfo($oids, $fb = NULL, $refresh_cache = FALSE) {
  if (!$fb) {
    $fb = $GLOBALS['_fb'];
  }
  $infos = array();
  if (!is_array($oids)) {
    $oids = array();
  }
  if ($fb) {

    // First try cache
    if (!$refresh_cache) {
      foreach ($oids as $oid) {
        if ($info = $_SESSION['fb'][$fb->api_key]['userinfo'][$oid]) {
          $infos[] = $info;
        }
      }
    }
    if (count($infos) != count($oids)) {

      // Session cache did not include all users, update the cache.
      $infos = $fb->api_client
        ->users_getInfo($oids, array(
        'about_me',
        'affiliations',
        'name',
        'is_app_user',
        'pic',
        'pic_big',
        'pic_square',
        'profile_update_time',
        'proxied_email',
        'status',
        'email_hashes',
        'email',
      ));

      // Update cache with recent results.
      if (is_array($infos)) {
        foreach ($infos as $info) {
          $_SESSION['fb'][$fb->api_key]['userinfo'][$info['uid']] = $info;
        }
      }
    }
    return $infos;
  }
}

/**
 * Helper function for FBJS files.
 * 
 * Useful for adding Facebook Javascript, which will be incorporated into
 * canvas pages or profile boxes.  When included this way, javascript must be
 * embedded inline, rather than refer to an external URL.  So this function
 * will actually read a local file and include the contents inline.
 */
function fb_add_js($filename, $type) {
  static $cache;
  if (!$cache) {
    $cache = array();

    // Add the most basic file we need.
    $base_file = drupal_get_path('module', 'fb') . '/fb_fbml.js';
    $base_file .= "?v=" . filemtime($base_file);
    drupal_add_js($base_file, 'module', 'fbml');

    // Add some settings that FBJS code will often need.
    $baseUrl = url('', array(
      'absolute' => TRUE,
    ));
    drupal_add_js(array(
      'fbjs' => array(
        'baseUrlFb' => $baseUrl,
        'baseUrl' => fb_scrub_urls($baseUrl),
      ),
    ), 'setting', 'fbml');
  }
  if (!$cache[$filename]) {
    if (file_exists($filename)) {

      // Refresh facebook's cache when file changes
      $filename .= "?v=" . filemtime($filename);
    }

    // 'post_settings' is a hack to make our code come after settings. This is
    // ugly, but we're doing it because there is no "onready" in FBJS.
    drupal_add_js($filename, 'post_settings', 'fbml');
    $cache[$filename] = TRUE;
  }
}

/**
 * For debugging, add $conf['fb_verbose'] = TRUE; to settings.php.
 */
function fb_verbose() {
  return variable_get('fb_verbose', NULL);
}

/**
 * This function will be replaced, hopefully, by format_username in D7.
 *
 * See http://drupal.org/node/192056
 */
function fb_format_username($account) {
  $name = !empty($account->name) ? $account->name : variable_get('anonymous', t('Anonymous'));
  drupal_alter('username', $name, $account);
  return $name;
}

/**
 * hook_username_alter().
 *
 * Return a user's facebook name, instead of local username.
 */
function fb_username_alter(&$name, $account) {

  //dpm(func_get_args(), "fb_username_alter($name)");
  if (isset($account->fbu) && $name == $account->fbu . '@facebook') {
    $info = fb_users_getInfo(array(
      $account->fbu,
    ));
    if (is_array($info) && is_array($info[0])) {
      if ($info[0]['name']) {
        $name = $info[0]['name'];
      }
    }
  }
}

Functions

Namesort descending Description
fb_add_js Helper function for FBJS files.
fb_api_check_session Sometimes calls to fb_api_init succeed, but calls to the client api will fail because cookies are obsolete or what have you. This function makes a call to facebook to test the session. Expensive, so use only when necessary.
fb_api_init Include the necessary facebook-platform code and initialize the API object.
fb_exit Implements hook_exit().
fb_facebook_user Returns the facebook user id currently visiting a canvas page, or if set_user has been called. Unlike fb_get_fbu(), works only on canvas pages or when infinite session has been initialized.
fb_format_username This function will be replaced, hopefully, by format_username in D7.
fb_form_alter
fb_get_all_apps Convenience method to return array of all know fb_apps.
fb_get_app Convenience method to get app info based on apikey or nid.
fb_get_app_data Convenience method for other modules to attach data to the fb_app object.
fb_get_app_title Will return a human-readable name if the fb_app module supports it, or fb_admin_get_app_properties($fb_app) has been called. However we don't take the relatively expensive step of calling that ourselves.
fb_get_fbu Given a local user id, find the facebook id.
fb_get_friends A convenience method for returning a list of facebook friends.
fb_get_groups
fb_get_groups_data
fb_get_object_fbu Convenience function to learn the fbu associated with a user, node or comment. Used in theming (X)FBML tags.
fb_get_profile_id
fb_handle_exception Exception handler for PHP5 exceptions.
fb_init Implementation of hook_init
fb_invoke Invoke hook_fb.
fb_is_app_user Facebook provides a method, users_isAppUser(), which is buggy and unreliable. So we need to implement our own.
fb_is_connect
fb_is_fbml_canvas Determine whether current page is FBML canvas page.
fb_is_fb_admin_page Helper tells other modules when to load admin hooks.
fb_is_iframe_canvas Determine whether current page is iframe canvas page.
fb_is_profile_tab Determine whether we are rendering a profile tab.
fb_load Implementation of a %wildcard_load(). http://drupal.org/node/224170
fb_log_exception
fb_menu Implementation of hook_menu().
fb_perm Implementation of hook_perm().
fb_report_errors Convenience method for displaying facebook api errors.
fb_scrub_urls This method will clean up URLs. When serving canvas pages, extra information is included in URLs (see fb_url_rewrite.inc). This will remove the extra information.
fb_session_key_form
fb_username_alter hook_username_alter().
fb_users_getInfo Helper function for facebook's users_getInfo API.
fb_user_load
fb_verbose For debugging, add $conf['fb_verbose'] = TRUE; to settings.php.
_fb_api_init Wrapper function for fb_api_init. This helps for functions that should work whether or not we are on a canvas page. For canvas pages, the active fb object is used. For non-canvas pages, it will initialize the API using an infinite session, if…
_fb_form_cache_cb

Constants