Newer
Older
/**
* @file
* This is the core required module of Drupal for Facebook.
*
* @see http://drupal.org/project/fb
define('FB_HOOK', 'fb');
// 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'); //
Dave Cohen
committed
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');
// These have moved to fb_user.module!
//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_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_POST_USER_CONNECT', 'post_user_connect'); // Connected local account to FB account, fb_user.module
define('FB_OP_POST_USER_DISCONNECT', 'post_user_disconnect'); // Disconnected local account from FB account, fb_user.module
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.
// Paths.
define('FB_PATH_ADMIN', 'admin/build/fb');
define('FB_PATH_ADMIN_ARGS', 3); // how many args in path.
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);
// permissions
define('FB_PERM_ADMINISTER', 'administer fb apps');
// Variables and $conf[] keys.
define('FB_VAR_LANGUAGE_OVERRIDE', 'fb_language_override');
define('FB_VAR_JS_SDK', 'fb_js_sdk');
define('FB_VAR_API_FILE', 'fb_api_file');
define('FB_VAR_JS_CHANNEL', 'fb_js_channel');
define('FB_VAR_VERBOSE', 'fb_verbose');
define('FB_VAR_APIKEY', 'fb_apikey'); // Deprecated. Use FB_VAR_ID
define('FB_VAR_ID', 'fb_id');
Dave Cohen
committed
define('FB_VAR_USE_COOKIE', 'fb_use_cookie');
define('FB_VAR_USE_SESSION', 'fb_use_session');
Dave Cohen
committed
define('FB_VAR_RELOAD_APPEND_HASH', 'fb_reload_append_hash');
define('FB_VAR_CURL_NOVERIFY', 'fb_curl_noverify');
// 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
* 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) {
Dave Cohen
committed
$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, include 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;
// Javascript settings needed by fb.js.
fb_js_settings('ajax_event_url', url(FB_PATH_AJAX_EVENT, array(
'absolute' => TRUE,
)));
Dave Cohen
committed
// Data structure to pass to FB.init();
$fb_init_settings = array(
'xfbml' => FALSE,
'status' => FALSE,
'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE),
);
// Figure out which app the current request is for.
Dave Cohen
committed
$_fb_app = fb_invoke(FB_OP_CURRENT_APP);
Dave Cohen
committed
Dave Cohen
committed
if ($_fb_app) {
// An App is configured.
// Javascript settings needed by fb.js.
Dave Cohen
committed
//fb_js_settings('apikey', $_fb_app->apikey); // deprecated XXX
fb_js_settings('label', $_fb_app->label);
fb_js_settings('page_type', fb_settings(FB_SETTINGS_TYPE)); // canvas or connect.
Dave Cohen
committed
//$fb_init_settings['apiKey'] = $_fb_app->apikey;
$fb_init_settings['appId'] = $_fb_app->id;
// Initialize the PHP API.
$_fb = fb_api_init($_fb_app);
Dave Cohen
committed
if ($_fb) {
Dave Cohen
committed
// Look for session info from several sources.
Dave Cohen
committed
if ($session = $_fb->getSession()) {
// Learned session from cookie or signed request.
Dave Cohen
committed
// Below, we store in our $_SESSION, just in case third-party cookies are not enabled.
}
elseif (isset($_REQUEST['fb_js_session'])) {
// Ajax callback via fb.js.
$_fb->setSession(json_decode($_REQUEST['fb_js_session'], TRUE));
$session = $_fb->getSession();
}
Dave Cohen
committed
elseif (isset($_SESSION['fb'][$_fb_app->id]['session']) && arg(0) != 'logout') {
Dave Cohen
committed
// Use the session previously stored.
Dave Cohen
committed
$_fb->setSession($_SESSION['fb'][$_fb_app->id]['session']);
Dave Cohen
committed
}
// Store session for future use. We'll need it if third-party cookies
// disabled, or we are not using facebook's cookie.
if (isset($session) && variable_get(FB_VAR_USE_SESSION, TRUE)) {
Dave Cohen
committed
$_SESSION['fb'][$_fb_app->id]['session'] = $session;
Dave Cohen
committed
}
// Make javascript work even when third-party cookies disabled.
Dave Cohen
committed
$fb_init_settings['session'] = $_fb->getSession();
// Sometimes when canvas page is open in one tab, and user logs out of
// facebook in another, the canvas page has bogus session info when
// refreshed. Here we attempt to detect and cleanup.
if (isset($session) && ($request = $_fb->getSignedRequest())) {
if (!isset($request['user_id']) ||
$request['user_id'] != $session['uid']) {
_fb_logout();
$_fb->setSession(NULL);
unset($session);
Dave Cohen
committed
unset($_SESSION['fb'][$_fb_app->id]);
unset($fb_init_settings['session']);
}
}
// 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(),
));
fb_js_settings('fbu', $_fb->getUser());
else {
// Add perms to settings, for calling FB.login().
$perms = array();
drupal_alter('fb_required_perms', $perms);
fb_js_settings('perms', implode(',', $perms));
}
else
watchdog('fb', "URL indicates a facebook app, but could not initialize Facebook", array(), WATCHDOG_ERROR);
if (isset($_REQUEST['destination'])) {
$destination = $_REQUEST['destination'];
}
elseif (isset($_REQUEST['q'])) {
$destination = $_REQUEST['q'];
if (fb_is_canvas()) {
$destination = fb_scrub_urls($destination); // Needed?
}
//Stripping the fragment out to be tacked on during the javascript redirect
if (strpos($destination, '#') !== FALSE) {
list($destination, $fragment) = explode('#', $destination, 2);
fb_js_settings('reload_url_fragment', $fragment);
}
fb_js_settings('reload_url', url($destination, array(
'absolute' => TRUE,
'fb_canvas' => fb_is_canvas(),
'language' => (object) array('prefix' => NULL, 'language' => NULL), // http://drupal.org/node/1000452
)));
Dave Cohen
committed
fb_js_settings('reload_url_append_hash', variable_get(FB_VAR_RELOAD_APPEND_HASH, FALSE));
if ($channel = variable_get(FB_VAR_JS_CHANNEL, TRUE)) {
if (!is_string($channel)) {
$channel = url('fb/channel', array('absolute' => TRUE, 'fb_url_alter' => FALSE));
}
Dave Cohen
committed
$fb_init_settings['channelUrl'] = $channel;
}
fb_js_settings('fb_init_settings', $fb_init_settings);
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));
fb_js_settings('controls', implode(',', fb_controls()));
if (!fb_js_settings('js_sdk_url')) {
if (isset($_SESSION['fb_locale']) &&
variable_get(FB_VAR_LANGUAGE_OVERRIDE, 'override')) {
Dave Cohen
committed
// @TODO - get locale from signed request. It appears to contain it now.
$fb_lang = $_SESSION['fb_locale'];
}
else {
$user_language = user_preferred_language($GLOBALS['user']);
$fb_lang = variable_get('fb_language_' . $user_language->language, 'en_US');
}
$js_sdk = fb_protocol() . "://connect.facebook.net/$fb_lang/all.js";
fb_js_settings('js_sdk_url', variable_get(FB_VAR_JS_SDK, $js_sdk));
}
// Add our module's javascript.
drupal_add_js(drupal_get_path('module', 'fb') . '/fb.js');
// See also fb_footer(), where we initialize facebook's SDK.
}
/**
*
* Adds the javascript setting with the supplied key/value. This function
* merely keeps track of the settings and writes them as late as possible.
* Currently, in the fb_footer() function. There has been a lot of
* experimentation as to the best place to initialize the facebook javascript
* SDK. The footer appears to be the best place because we may not know all
* settings until well after hook_init().
*
* @param $key
* The javascript setting name. If the key is null then nothing is modified and the settings are returned.
* @param $value
* The value of the javascript setting. If the key is not null by the value is the setting is removed
* @return
* The associative array containing the current fb javascript settings
*/
function fb_js_settings($key = NULL, $value = NULL) {
static $fb_js_settings = array();
if (isset($key) && isset($value)) {
$fb_js_settings[$key] = $value;
}
elseif (isset($key)) {
return isset($fb_js_settings[$key]) ? $fb_js_settings[$key] : NULL;
}
else {
return $fb_js_settings;
}
}
* 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');
Dave Cohen
committed
if (isset($cache[$fb_app->id])) {
return $cache[$fb_app->id];
Dave Cohen
committed
// Find Facebook's PHP SDK. Use libraries API if enabled.
$fb_lib_path = function_exists('libraries_get_path') ? libraries_get_path('facebook-php-sdk') : 'sites/all/libraries/facebook-php-sdk';
$fb_platform = variable_get(FB_VAR_API_FILE, $fb_lib_path . '/src/facebook.php');
Dave Cohen
committed
if (!class_exists('Facebook') && !include($fb_platform)) {
$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;
}
// We don't have a cached resource for this app, so we're going to create one.
$fb = new Facebook(array(
'secret' => isset($fb_app->secret) ? $fb_app->secret : NULL,
'cookie' => variable_get(FB_VAR_USE_COOKIE, TRUE),
// Some servers need these settings.
if (variable_get(FB_VAR_CURL_NOVERIFY, TRUE)) {
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYPEER] = FALSE;
Facebook::$CURL_OPTS[CURLOPT_SSL_VERIFYHOST] = FALSE;
//Facebook::$CURL_OPTS[CURLOPT_VERBOSE] = 1; // debug
}
// Cache the result, in case we're called again.
Dave Cohen
committed
$cache[$fb_app->id] = $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);
watchdog('fb', '%function unable to initialize Facebook API.',
array('%function' => '_fb_api_init()'), WATCHDOG_ERROR);
return;
}
else
return $fb;
}
/**
* Helper function to get the most commonly used values. In your custom
* module, call extract(fb_vars()); to set $fb_app, $fb, and $fbu.
*/
function fb_vars() {
return array(
'fb' => $GLOBALS['_fb'],
'fb_app' => $GLOBALS['_fb_app'],
'fbu' => fb_facebook_user(),
);
}
/**
* Helper to get the tokens needed to accss facebook's API.
*
* You would think that facebook's SDK would provide basic functions like this.
* @param $fb
* Get the token for this API instance. If NULL, use the global $_fb.
*
* @param $fbu
* Get the user-specific token. If NULL, get the application token.
*/
function fb_get_token($fb = NULL, $fbu = NULL) {
static $cache;
if (!isset($cache))
$cache = array();
if (!$fb)
$fb = $GLOBALS['_fb'];
if (!$fb)
return;
$app_id = $fb->getAppId();
$cache_key = $app_id;
if (!$fbu) {
// Get the application token.
if (!isset($cache[$cache_key])) {
$path = "https://graph.facebook.com/oauth/access_token?client_id=" . $app_id . "&client_secret=" . $fb->getApiSecret() . "&type=client_cred";
$http = drupal_http_request($path);
$data = explode('=', $http->data);
$token = $data[1];
if ($token)
$cache[$cache_key] = $token;
}
}
}
else {
$cache_key .= '_' . $fbu;
// Get the user access token.
if ($fbu == 'me' || $fbu == fb_facebook_user($fb)) {
$session = $fb->getSession();
$cache[$cache_key] = $session['access_token'];
}
else {
$session_data = fb_invoke(FB_OP_GET_USER_SESSION, array(
'fb' => $fb,
'fb_app' => fb_get_app(array('id' => $app_id)),
'fbu' => $fbu,
), array());
if (count($session_data)) {
$cache[$cache_key] = $session_data['access_token'];
}
return isset($cache[$cache_key]) ? $cache[$cache_key] : NULL;
* This helper original written because facebook's $fb->api() function was
* very buggy. I'm not sure this is still needed. On the other hand, a
* future version of modules/fb might use this instead of faceobok's PHP SDK,
* eliminating the need for it entirely.
function fb_call_method($fb, $method, $params = array()) {
$params['access_token'] = fb_get_token($fb);
$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);
$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'])) {
Dave Cohen
committed
throw new FacebookApiException($data);
}
}
elseif ($http->data == 'true' || $http->code == 200) {
// No problems.
}
else {
// Never reach this???
if (function_exists('dpm')) dpm($http, __FUNCTION__ . " unexpected result from $url"); // XXX
}
return $data;
else {
// Should we throw FacebookApiException, or plain old exception?
throw new FacebookApiException(
array(
'error_msg' => t('fb_call_method failed calling !method. !detail', array(
'!method' => $method,
'!detail' => $http->error,
)),
'error_code' => $http->code,
));
}
/**
* 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);
Dave Cohen
committed
/**
* Implements hook_footer().
*/
function fb_footer($is_front) {
global $_fb, $_fb_app;
// This element recommended by facebook. http://developers.facebook.com/docs/reference/javascript/
$output = "<div id=\"fb-root\"></div>\n";
$settings = fb_js_settings();
$output .= "<script type=\"text/javascript\">\n";
$output .= "<!--//--><![CDATA[//><!--\n";
$output .= "jQuery.extend(Drupal.settings, " . json_encode(array('fb' => fb_js_settings())) . ");\n";
$js_array = fb_invoke(FB_OP_JS, array('fb' => $GLOBALS['_fb'], 'fb_app' => $GLOBALS['_fb_app']), array());
if (count($js_array)) {
// The function we define in the footer will be called after FB is initialized.
$output .= "FB_JS.initHandler = function() {\n";
//$output .= "debugger;\n";
$output .= implode("\n", $js_array);
$output .= "};\n";
$output .= "jQuery(document).bind('fb_init', FB_JS.initHandler);\n";
}
// Load the JS SDK asynchronously.
// http://developers.facebook.com/docs/reference/javascript/
$output .= "var e = document.createElement('script');\n";
$output .= "e.async = true;\n";
$output .= "e.src = Drupal.settings.fb.js_sdk_url;\n";
$output .= "document.getElementById('fb-root').appendChild(e);\n";
$output .= "\n//--><!]]>\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;
if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) {
return TRUE;
}
elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) { // deprecated FBML tab
elseif (isset($_REQUEST['fb_sig_in_profile_tab']) &&
$_REQUEST['fb_sig_in_profile_tab']) { // deprecated ancient history
// Old way, no migrations enabled.
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 {
$me = $fb->api('me');
// Store the locale if set.
if (isset($me['locale'])) {
$_SESSION['fb_locale'] = $me['locale'];
// Does not matter what is returned, as long as exception is not thrown.
$success = TRUE;
}
catch (Exception $e) {
if (fb_verbose()) {
watchdog('fb', 'fb_api_check_session failed. Possible attempt to spoof a facebook session!');
//watchdog('fb', print_r($fb->getSession(), 1));
if (fb_verbose()) {
fb_log_exception($e, t("fb_api_check_session failed."));
}
Dave Cohen
committed
unset($_SESSION['fb'][$fb->getAppId()]);
// Unsetting the javasript fbu can be helpful when third-party cookies disabled.
fb_js_settings('fbu', 0);
// Might as well try to clean up the mess.
if (isset($_COOKIE['fbs_' . $fb->getAppId()])) {
setcookie('fbs_' . $fb->getAppId(), '', time() - 42000, '/');
}
}
return $success;
}
Dave Cohen
committed
/**
* Helper to ensure local user is logged out, or an anonymous session is refreshed.
*/
function _fb_logout() {
session_destroy();
// Fix for http://bugs.php.net/bug.php?id=32330
session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc');
$GLOBALS['user'] = drupal_anonymous_user();
// Unsetting the javasript fbu can be helpful when third-party cookies disabled.
fb_js_settings('fbu', 0);
// Clean up facebook cookies.
if (isset($GLOBALS['_fb_app'])) {
Dave Cohen
committed
if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->apikey])) { // still needed?
setcookie('fbs_' . $GLOBALS['_fb_app']->apikey, '', time() - 42000, '/');
}
if (isset($_COOKIE['fbs_' . $GLOBALS['_fb_app']->id])) {
setcookie('fbs_' . $GLOBALS['_fb_app']->id, '', time() - 42000, '/');
}
Dave Cohen
committed
}
}
* 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'];
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 function to ensure user has authorized an application.
*
* Similar to the old require_login() provided by the old facebook API.
* Works by redirecting the user as described in http://developers.facebook.com/docs/authentication/.
*
* @TODO perms if possible, handle users who skip.
*/
function fb_require_authorization($fb = NULL, $destination = NULL) {
if (!$fb)
$fb = $GLOBALS['_fb'];
if (!$fb) {
throw new Exception(t('Failed to authorize facebook application. Could not determine application id.'));
}
$fbu = fb_facebook_user($fb);
if (!$fbu) {
$client_id = $fb->getAppId();
$redirect_uri = $destination ? $destination : url(fb_scrub_urls($_REQUEST['q']), array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas()));
$url = "https://graph.facebook.com/oauth/authorize?client_id=$client_id&redirect_uri=$redirect_uri";
drupal_goto($url);
}
else {
return $fbu;
}
}
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.
*
Dave Cohen
committed
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
isset($uid->fbu) && $uid->fbu)
Dave Cohen
committed
elseif (is_object($uid))
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
/**
Dave Cohen
committed
* Convenience method to get app info based on id 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))
Dave Cohen
committed
$search_data = array('id' => $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
Dave Cohen
committed
* 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])) {
$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
/**
Dave Cohen
committed
*/
Dave Cohen
committed
// Admin pages overview.
Dave Cohen
committed
$items[FB_PATH_ADMIN] = array(