Newer
Older
/**
* @file
* This is the core required module of Drupal for Facebook.
*
* @see http://drupal.org/project/fb
*
*/
define('FB_HOOK', 'fb');
Dave Cohen
committed
// Paths.
define('FB_PATH_ADMIN', 'admin/build/fb');
define('FB_PATH_ADMIN_ARGS', 3); // how many args in path.
Dave Cohen
committed
define('FB_PATH_ADMIN_APPS', 'admin/build/fb/app');
define('FB_PATH_ADMIN_APPS_ARGS', 4);
define('FB_PATH_AJAX_EVENT', 'fb/ajax');
define('FB_PATH_AJAX_EVENT_ARGS', 2);
Dave Cohen
committed
// permissions
define('FB_PERM_ADMINISTER', 'administer fb apps');
// Ops for hook_fb.
Dave Cohen
committed
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
Dave Cohen
committed
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
Dave Cohen
committed
define('FB_OP_GET_UID', 'get_uid'); // Query the facebook user's local 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
Dave Cohen
committed
define('FB_OP_POST_EXTERNAL_LOGIN', 'post_external_login'); // user map has changed global user.
define('FB_OP_APP_IS_AUTHORIZED', 'app_authorized'); // Invoked if user has authorized an app. Triggers creation of user accounts or fb_user entries
define('FB_OP_JS', 'fb_op_js'); // A chance to inject javascript onto the page.
define('FB_OP_AJAX_EVENT', 'fb_op_ajax'); // Notification of an event via ajax.
// node_access realms (belongs here?)
define('FB_GRANT_REALM_FRIEND', 'fb_friend');
define('FB_GRANT_REALM_GROUP', 'fb_group');
Dave Cohen
committed
// 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!
Dave Cohen
committed
// @TODO - new libs, are these FBU values still needed???
Dave Cohen
committed
define('FB_FBU_CURRENT', 'fbu_current'); // Canvas pages and Connect pages
Dave Cohen
committed
define('FB_FBU_ANY', 'fbu_any'); // Use current user on canvas page, fall back to infinite session otherwise.
Dave Cohen
committed
//// Constants for internal use
Dave Cohen
committed
define('FB_APP_CURRENT', '000_app_current'); // Canvas pages only. 000 makes it appear first in options list
Dave Cohen
committed
Dave Cohen
committed
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
* Controls are one way to customize the behavior of Drupal for Facebook modules.
*
* Controls are stored as an array of flags. Each flag overrides a
* configurable or built-in behavior. Third-party modules can use this to
* provide exceptions to otherwise useful behavior. For example see
* fb_user.module, where this is used to suppress some behavior in off-line
* mode.
*
* Controls take effect not just for the current page request, but also for
* ajax callbacks generated by the subsequent page.
*
* Because ajax controls could be spoofed by a malicious client, flags should
* not enable any "risky" features. For example, fb_user.module provides a
* control to suppress the creation of account, but not a control to enable
* new accounts, as that would be a security risk.
*
*/
function fb_controls($control = NULL, $value = NULL) {
static $controls;
if (!isset($controls)) {
// Initialize.
if (isset($_REQUEST['fb_controls'])) {
// Comma separated list passed to ajax calls.
foreach(explode(',', $_REQUEST['fb_controls']) as $key) {
$controls[$key] = TRUE;
}
}
else {
$controls = array();
}
// @TODO - would a drupal_alter() be useful here?
}
if (isset($control)) {
if ($value === FALSE) {
unset($controls[$control]);
return;
}
elseif ($value === TRUE)
$controls[$control] = TRUE;
Dave Cohen
committed
return isset($controls[$control]) ? $controls[$control] : FALSE; // Return requested control.
Dave Cohen
committed
}
return array_keys($controls); // Return all controls.
}
/**
* Implements hook_init().
*
* Initializes facebook's javascript.
* 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). We invoke
* the hook again to let interested modules know the sdk is initialized.
*
*/
function fb_init() {
Dave Cohen
committed
// Globals provided for internal use and convenience to third-parties.
global $_fb;
global $_fb_app;
Dave Cohen
committed
// fb_settings.inc may have been included in settings.php. If not, included it now.
if (!function_exists('fb_settings')) {
module_load_include('inc', 'fb', 'fb_settings');
// trigger test in fb_devel.module
$GLOBALS['fb_init_no_settings'] = TRUE;
// Values needed by fb.js.
$js_settings = array(
'ajax_event_url' => url(FB_PATH_AJAX_EVENT, array('absolute' => TRUE)),
// @TODO - replace en_US with dynamically determined locale.
'js_sdk_url' => variable_get('fb_js_sdk', 'http://connect.facebook.net/en_US/all.js'),
);
Dave Cohen
committed
$_fb_app = fb_invoke(FB_OP_CURRENT_APP);
Dave Cohen
committed
Dave Cohen
committed
if ($_fb_app) {
// An App is configured.
// Initialize javascript.
$js_settings['apikey'] = $_fb_app->apikey;
$js_settings['label'] = $_fb_app->label;
$js_settings['page_type'] = fb_settings(FB_SETTINGS_TYPE); // canvas or connect.
// Initialize the PHP API.
$_fb = fb_api_init($_fb_app);
Dave Cohen
committed
if ($_fb) {
// Give other modules a chance to initialize.
'fb_app' => $_fb_app,
'fb' => $_fb,
));
if ($fbs = $_fb->getSession()) {
'fb_app' => $_fb_app,
'fb' => $_fb,
'fbu' => $_fb->getUser(),
));
$js_settings['fbu'] = $_fb->getUser();
else {
// Add perms to settings, for calling FB.login().
$perms = array();
drupal_alter('fb_required_perms', $perms);
$js_settings['perms'] = implode(',', $perms);
}
}
else
watchdog('fb', "URL indicates a facebook app, but could not initialize Facebook", array(), WATCHDOG_ERROR);
if (fb_is_canvas()) {
$js_settings['reload_url'] = 'http://apps.facebook.com/' . $_fb_app->canvas . '/' . fb_scrub_urls($_REQUEST['q']);
}
else {
$js_settings['reload_url'] = url(isset($_GET['q']) ? $_GET['q'] : '<front>',
array('absolute' => TRUE));
}
Dave Cohen
committed
// Allow third-parties to act, even if we did not initialize $_fb.
fb_invoke(FB_OP_POST_INIT, array('fb_app' => $_fb_app,
'fb' => $_fb));
Dave Cohen
committed
$js_settings['controls'] = implode(',',fb_controls());
// Add javascript to all pages.
drupal_add_js(drupal_get_path('module', 'fb') . '/fb.js');
// @TODO - move this to fb_preprocess_page(). See og_preprocess_page for example.
drupal_add_js(array('fb' => $js_settings), 'setting');
}
* Include and initialize Facebook's PHP SDK.
function fb_api_init($fb_app) {
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 (isset($cache[$fb_app->apikey])) {
return $cache[$fb_app->apikey];
}
$filename = variable_get('fb_api_file', 'sites/all/libraries/facebook-php-sdk/src/facebook.php');
if (!class_exists('Facebook') && !include($filename)) {
$message = t('Failed to find the Facebook client libraries at %filename. Read the !readme and follow the instructions carefully.', array(
Dave Cohen
committed
'!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;
Dave Cohen
committed
try {
// We don't have a cached resource for this app, so we're going to create one.
$fb = new Facebook(array(
'appId' => $fb_app->apikey,
'secret' => $fb_app->secret,
'cookie' => TRUE,
));
// Facebook certs are not valid.
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = FALSE;
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = FALSE;
// Cache the result, in case we're called again.
$cache[$fb_app->apikey] = $fb;
return $fb;
catch (Exception $e) {
fb_log_exception($e, t('Failed to construct Facebook client API.'));
/**
* 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) {
Dave Cohen
committed
$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;
}
/**
* Since facebook's php sdk is a joke, we have to implement the most basic
* crap, like this.
*
* @TODO - is this still needed? It's hard to know when their bugs are fixed.
*/
function fb_access_token($fb = NULL) {
static $cache;
if (!isset($fb))
$fb = $GLOBALS['_fb'];
$apikey = $fb->getAppId();
if (!isset($cache))
$cache = array();
if (!isset($cache[$apikey])) {
$path = "https://graph.facebook.com/oauth/access_token?client_id=" . $fb->getAppId() . "&client_secret=" . $fb->getApiSecret() . "&type=client_cred";
$http = drupal_http_request($path);
$data = explode('=', $http->data);
$token = $data[1];
if ($token)
$cache[$apikey] = $token;
}
return $cache[$apikey];
}
/**
* Facebook's own php-sdk is so friggin' buggy. If you try $_fb->api(...) and
* get invalid parameters exceptions, this may work instead.
*/
function fb_call_method($fb, $method, $params = array()) {
$params['access_token'] = fb_access_token($fb);
$params['api_key'] = $fb->getAppId();
$params['format'] = 'json-strings';
// Here's how to create a url that conforms to standards:
$url = url("https://api.facebook.com/method/{$method}", array(
'query' => $params,
));
// 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.
// http://forum.developers.facebook.net/viewtopic.php?id=76228
// $url = rawurldecode($url);
$http = drupal_http_request($url);
if ($http->data) {
$data = json_decode($http->data, TRUE);
// Yes, it's double encoded. At least sometimes.
if (is_string($data)) {
$data = json_decode($data, TRUE);
}
if (is_array($data)) {
if (isset($data['error_code'])) {
throw new FacebookApiException($data);
}
}
else {
// Never reach this???
}
return $data;
/**
* Helper function for fql queries.
*
* Use $params to pass a session_key, when needed.
*/
function fb_fql_query($fb, $query, $params = array()) {
//$result = fb_call_method($fb, 'fql.query', $params);
$params['method'] = 'fql.query';
$result = $fb->api($params);
/**
* Implements hook_footer().
*/
function fb_footer($is_front) {
global $_fb, $_fb_app;
// Necessary? All facebook examples seem to have it.
$output = "<div id=\"fb-root\"></div>\n";
$js_array = fb_invoke(FB_OP_JS, array('fb' => $GLOBALS['_fb'], 'fb_app' => $GLOBALS['_fb_app']), array());
if (count($js_array)) {
$output .= "<script type=\"text/javascript\">\n";
// The function we define in the footer will be called after FB is initialized.
$output .= "FB_JS.initHandler = function() {\n";
$output .= implode("\n", $js_array);
$output .= "};\n";
$output .= "jQuery(document).bind('fb_init', FB_JS.initHandler);\n";
$output .= "\n</script>\n";
}
return $output;
}
/**
* Is the current request a canvas page?
*/
function fb_is_canvas() {
if (fb_is_tab()) {
return FALSE;
}
elseif (fb_settings(FB_SETTINGS_CB)) {
// Using fb_url_rewrite.
return TRUE;
}
elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) {
// No rewrite, but fb_settings.inc has detected type.
return TRUE;
}
return FALSE;
/**
* Is the current page a profile tab.
*
* Only works when "Canvas Session Parameter" is disabled.
*/
function fb_is_tab() {
global $_fb;
// Old way, no migrations enabled.
if (isset($_REQUEST['fb_sig_in_profile_tab']) &&
$_REQUEST['fb_sig_in_profile_tab']) {
return TRUE;
}
// signed request migration enabled.
elseif ($_fb && ($sr = $_fb->getSignedRequest()) &&
isset($sr['profile_id'])) {
return TRUE;
}
return FALSE;
}
/**
* 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) {
$success = FALSE;
try {
$session = $fb->getSession();
// Newer API uses access_token
if (isset($session['access_token'])) {
$is_user = $fb->api(array(
'method' => 'users.isAppUser',
));
// Does not matter what is returned, as long as exception is not thrown.
$success = TRUE;
}
// Older API used session_key. Still needed?
elseif (isset($session['session_key'])) {
$is_user = $fb->api(array(
'method' => 'users.isAppUser',
));
$success = TRUE;
}
catch (Exception $e) {
if (fb_verbose()) {
watchdog('fb', 'fb_api_check_session failed. Possible attempt to spoof a facebook session!');
}
if (fb_verbose()) {
fb_log_exception($e, t("fb_api_check_session failed."));
}
}
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 and
* connect pages, or when infinite session has been initialized.
function fb_facebook_user($fb = NULL) {
if (!isset($fb))
Dave Cohen
committed
$fb = $GLOBALS['_fb'];
if (!$fb)
return;
try {
$fbu = $fb->getUser();
return $fbu;
catch (FacebookApiException $e) {
fb_log_exception($e,
t('Failed to get Facebook user id. detail: !detail',
array('!detail' => print_r($e, 1))));
Dave Cohen
committed
/**
* 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;
}
Dave Cohen
committed
}
Dave Cohen
committed
/**
* Given a facebook user id, learn the local uid, if any.
*
*/
function fb_get_uid($fbu, $fb_app = NULL) {
$uid = NULL;
if ($fbu) {
$uid = fb_invoke(FB_OP_GET_UID, array('fbu' => $fbu, 'fb_app' => $fb_app));
}
return $uid;
}
/**
* Given a local user id, find the facebook id.
*
* Invokes hook_fb(FB_OP_GET_FBU) in order to ask other modules what the fbu
* is. Typically, fb_user.module will answer the question.
function fb_get_fbu($uid, $fb_app = NULL) {
// Accept either a user object or uid passed in.
Dave Cohen
committed
if (is_object($uid) && ($uid->uid) &&
isset($uid->fbu) && $uid->fbu)
Dave Cohen
committed
elseif (is_object($uid))
Dave Cohen
committed
Dave Cohen
committed
if ($uid) {
// User management is handled by another module. Use our hook to ask for mapping.
$fbu = fb_invoke(FB_OP_GET_FBU, array('uid' => $uid,
Dave Cohen
committed
'fb' => $GLOBALS['_fb']));
Dave Cohen
committed
}
Dave Cohen
committed
else {
$fbu = NULL;
}
/**
* 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;
}
Dave Cohen
committed
elseif (isset($object->fbu)) {
// Explicitly set.
$fbu = $object->fbu;
}
elseif (isset($object->init) &&
($pos = strpos($object->init, '@facebook'))) {
// Naming convention used by fb_user when creating accounts.
// $object->init may be present when object is a user.
$fbu = substr($object->init, 0, $pos);
}
Dave Cohen
committed
elseif ($pos = strpos($object->name, '@facebook')) {
$fbu = substr($object->name, 0, $pos);
}
Dave Cohen
committed
elseif ($object->uid > 0) {
// 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;
}
}
Dave Cohen
committed
/**
* Convenience method to get app info based on apikey or nid.
Dave Cohen
committed
*/
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);
Dave Cohen
committed
$fb_app = fb_invoke(FB_OP_GET_APP, $search_data);
Dave Cohen
committed
return $fb_app;
}
/**
Dave Cohen
committed
* 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.
Dave Cohen
committed
*/
Dave Cohen
committed
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();
Dave Cohen
committed
}
Dave Cohen
committed
return $fb_app->fb_app_data;
Dave Cohen
committed
}
/**
* 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;
}
}
Dave Cohen
committed
/**
* 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.
*
* @TODO - also support users who have permitted offline access.
*
* @return: an array of facebook ids
*/
function fb_get_friends($fbu, $fb_app = NULL) {
if (!$fb_app)
Dave Cohen
committed
$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])) {
Dave Cohen
committed
if ($fb === $GLOBALS['_fb'] &&
$fbu == fb_facebook_user($fb)) {
try {
$items = fb_call_method($fb, 'friends.get', array(
'uid' => $fbu,
));
}
catch (Exception $e) {
fb_log_exception($e, t('Failed call to friends.get'), $fb);
}
// friends_get does not work in cron call, so we double check. @TODO - still needed?
if (!$items || !count($items)) {
$logged_in = fb_facebook_user($fb);
$query = "SELECT uid2 FROM friend WHERE uid1=$fbu"; // FQL, no {curly_brackets}!
try {
$result = fb_call_method($fb, 'fql.query', array(
'query' => $query,
));
//dpm($result, "FQL " . $query); // debug
}
catch (Exception $e) {
fb_log_exception($e, t('Failed call to fql.query: !query', array('!query' => $query)), $fb);
}
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) {
$fb = _fb_api_init($fb_app);
if (!$fb || !$fbu)
return;
if (!isset($cache[$fbu])) {
$cache[$fbu] = fb_call_method($fb, 'groups.get', array(
'uid' => $fbu,
));
}
return $cache[$fbu];
}
Dave Cohen
committed
//// Menu structure.
Dave Cohen
committed
/**
* Implementation of hook_menu().
*/
Dave Cohen
committed
// Admin pages overview.
Dave Cohen
committed
$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',
Dave Cohen
committed
$items[FB_PATH_ADMIN . '/list'] = array(
Dave Cohen
committed
'title' => 'List',
'weight' => -2,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
Dave Cohen
committed
Dave Cohen
committed
$items[FB_PATH_ADMIN_APPS . '/%fb'] = array(
'title' => 'Application Detail',
'description' => 'Facebook Applications',
Dave Cohen
committed
'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,
);
$items[FB_PATH_ADMIN_APPS . '/%fb/fb/set_props'] = array(
'title' => 'Set Properties',
'description' => 'Set Facebook Application Properties',
'page callback' => 'drupal_get_form',
'page arguments' => array('fb_admin_set_properties_form', FB_PATH_ADMIN_APPS_ARGS),
'access arguments' => array(FB_PERM_ADMINISTER),
'type' => MENU_CALLBACK,
);
// Javascript helper
$items['fb/js'] = array(
'page callback' => 'fb_js_cb',
'type' => MENU_CALLBACK,
'access callback' => TRUE,
);
// Ajax event handler.
$items[FB_PATH_AJAX_EVENT . '/%'] = array(
'page callback' => 'fb_ajax_event',
'type' => MENU_CALLBACK,
'access callback' => TRUE,
'page arguments' => array(FB_PATH_AJAX_EVENT_ARGS),
);
Dave Cohen
committed
/**
* Implementation of a %wildcard_load(). http://drupal.org/node/224170
*
* Seems to get called a lot(!) so we cache.
Dave Cohen
committed
*/
function fb_load($id) {
static $cache;
if (!isset($cache))
$cache = array();
if (!isset($cache[$id])) {
$query = array('label' => $id);
if (fb_is_fb_admin_page()) {
// Show disabled apps to admins.
$query['status'] = 0; // status >= 0
}
$cache[$id] = fb_get_app($query);
Dave Cohen
committed
}
Dave Cohen
committed
}
/**
* Implementation of hook_perm().
*/
function fb_perm() {
* 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) {
Dave Cohen
committed
global $_fb_app, $_fb;
Dave Cohen
committed
if ($_fb_app && $_fb) {
// Invoke other modules.
Dave Cohen
committed
fb_invoke(FB_OP_EXIT, array('fb_app' => $_fb_app,
'fb' => $GLOBALS['_fb']),
Dave Cohen
committed
$destination);
}
/**
* Invoke hook_fb.
*/
Dave Cohen
committed
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) {
if (isset($data['fb_app'])) {
fb_log_exception($e, t('Exception calling %function(%op) (!app)', array(
'%function' => $function,
'%op' => $op,
'%label' => $data['fb_app']->label,
'%apikey' => $data['fb_app']->apikey,
'!app' => l($data['fb_app']->label, FB_PATH_ADMIN_APPS . '/' . $data['fb_app']->label),
)));
}
else {
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. This will remove the extra
* information. Useful when linking back to the website from a canvas page or
* wall post.
*
* For example in the following code, $url2 will link out to the server's domain:
*
* $url = url('node/42', array('absolute' => TRUE)); // i.e. http://apps.facebook.com/example/node/42
* $url2 = fb_scrub_urls($url); // i.e. http://example.com/node/42
*
*
* @see fb_url_rewrite.inc
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);
/**
* Convenience function to log and report exceptions.
*/
Dave Cohen
committed
function fb_log_exception($e, $text = '', $fb = NULL) {
if ($text)
$message = $text .': '. $e->getMessage();
else
$message = $e->getMessage();
$message .= ' ' . $e->getCode();
Dave Cohen
committed
if ($fb) {
$message .= '. (' . t('logged into facebook as %fbu', array('%fbu' => $fb->getUser())) . ')';
Dave Cohen
committed
}
if (fb_verbose()) {
$message .= '<pre>' . $e . '</pre>';
}
watchdog('fb', $message, array(), WATCHDOG_ERROR);
if (user_access(FB_PERM_ADMINISTER)) {
drupal_set_message($message, 'error');
}
}
Dave Cohen
committed
/**
* 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);
Dave Cohen
committed
print $message;
print "<pre>\$_REQUEST:\n";
print_r($_REQUEST);
print "\n\nREQUEST_URI:\n" . request_uri();
Dave Cohen
committed
}
/**
* 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.
*
* Update, users.getInfo appears to be growing less reliable. For example,
* does not return names of test accounts. @TODO - migrate away from this and
* towards use of fb->api().
*
* @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) {
Dave Cohen
committed
$fb = $GLOBALS['_fb'];
if (!is_array($oids))
$oids = array();
if (!$refresh_cache && isset($_SESSION['fb'])) {
if (isset($_SESSION['fb'][$apikey]['userinfo'][$oid])) {
$info = $_SESSION['fb'][$apikey]['userinfo'][$oid];
if (count($infos) != count($oids)) {
// Session cache did not include all users, update the cache.
// For historical reasons, use users.getInfo. New code should migrate to graph api.
$infos = fb_call_method($fb, 'users.getInfo', array(
'uids' => $oids,
'fields' => 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'][$apikey]['userinfo'][$info['uid']] = $info;
} catch(FacebookApiException $e) {
fb_log_exception($e, t('Failed call to users.getInfo'), $fb);
return $infos;
}
}
/**
* For debugging, add $conf['fb_verbose'] = TRUE; to settings.php.
*/
function fb_verbose() {
return variable_get('fb_verbose', NULL);
}
Dave Cohen
committed
/**
* This function will be replaced, hopefully, by format_username in D7.