Newer
Older
* This module manages relations between local Drupal user accounts
* and their accounts on facebook.com.
* This module can create a new local user account, when a facebook
* user authorizes an application hosted on this server.
*
* Links existing local accounts to remote accounts on facebook via
* fb_user table.
*
* Drupal refers to a local user id as 'uid'. Facebook's documentation
* and code also uses 'uid'. In these modules we use 'fbu' for facebook's
* id and 'uid' for Drupal's id.
define('FB_USER_OPTION_CREATE_NEVER', 1);
define('FB_USER_OPTION_CREATE_LOGIN', 2);
define('FB_USER_OPTION_MAP_NEVER', 1);
define('FB_USER_OPTION_MAP_ALWAYS', 2); // Map when user is registered and authorized.
define('FB_USER_OPTION_MAP_EMAIL', 3); // Map when email is exact match.
define('FB_USER_VAR_USERNAME_STYLE', 'fb_user_username_style'); // Key used in variables table for this option.
define('FB_USER_OPTION_USERNAME_FULL', 1); // Get full name from FB
define('FB_USER_OPTION_USERNAME_FBU', 2); // Use unique name
define('FB_USER_VAR_STATS', 'fb_user_stats'); // Whether to use fb_user_app table.
Dave Cohen
committed
define('FB_USER_VAR_ALTER_REGISTER', 'fb_user_alter_register');
define('FB_USER_VAR_ALTER_LOGIN', 'fb_user_alter_login');
// Controls - see fb_controls().
define('FB_USER_CONTROL_NO_CREATE_ACCOUNT', 'fb_user_no_account');
define('FB_USER_CONTROL_NO_CREATE_MAP', 'fb_user_no_map_create');
define('FB_USER_CONTROL_NO_HONOR_MAP', 'fb_user_no_map');
* Implements hook_perm().
Dave Cohen
committed
function fb_user_perm() {
return array('delete own fb_user authmap');
Dave Cohen
committed
}
/**
* Implements of hook_menu().
*/
function fb_user_menu() {
$items = array();
// Admin pages
$items[FB_PATH_ADMIN . '/fb_user'] = array(
'title' => 'User Management',
'description' => 'Local account to facebook account mapping',
'page callback' => 'drupal_get_form',
'page arguments' => array('fb_user_admin_settings'),
'access arguments' => array(FB_PERM_ADMINISTER),
'file' => 'fb_user.admin.inc',
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/**
* Returns configuration for this module, on a per-app basis.
*/
function _fb_user_get_config($fb_app) {
Dave Cohen
committed
$fb_app_data = fb_get_app_data($fb_app);
$fb_user_data = isset($fb_app_data['fb_user']) ? $fb_app_data['fb_user'] : array();
// Merge in defaults
$fb_user_data += array(
'create_account' => FB_USER_OPTION_CREATE_NEVER,
'map_account' => array(
FB_USER_OPTION_MAP_ALWAYS => FB_USER_OPTION_MAP_ALWAYS,
FB_USER_OPTION_MAP_EMAIL => FB_USER_OPTION_MAP_EMAIL,
),
);
return $fb_user_data;
}
* There are several pages where we don't want to automatically create a new
* account or use an account configured for this app.
*/
function _fb_user_special_page() {
// fb_app/event is called by facebook. Don't create accounts on that page.
return ((arg(0) == 'fb_app' && arg(1) == 'event'));
/**
* Keep track of when the user has visited the app, and whether they've
* authorized the app or not.
*
* Historically this supported infinite sessions. I believe if this data is
* no longer necessary for the offline access extended permission.
*/
function _fb_user_track($fb, $fb_app, $account) {
if (variable_get(FB_USER_VAR_STATS, TRUE)) {
// In special cases, do not modify the uid column.
$fb_user_data = _fb_user_get_config($fb_app);
if (!$account->uid) {
// Keep data for fbu, even if we do not know uid.
$uid = 0;
}
else {
// Uid is accurate.
$uid = $account->uid;
}
$session = $fb->getSession();
$session_key = isset($session['session_key']) ? $session['session_key'] : '';
$result = db_query("UPDATE {fb_user_app} SET time_access=%d, session_key='%s', session_key_expires=%d, uid=%d WHERE apikey='%s' AND fbu=%d",
time(),
$session_key, $session['expires'],
$uid,
$fb_app->apikey, fb_facebook_user($fb));
if ($result && !db_affected_rows()) {
// The row for this user was never inserted, or deleted. Insert now.
$fbu = fb_facebook_user($fb);
if ($fbu) {
$info = fb_users_getInfo(array($fbu), $fb);
$data = $info[0];
$result = db_query("INSERT INTO {fb_user_app} (apikey, fbu, added, session_key, session_key_expires, time_access, uid, proxied_email, time_cron) VALUES ('%s', %d, %d, '%s', %d, %d, %d, '%s', %d)",
$fb_app->apikey, $fbu,
$data['is_app_user'],
time(),
$uid,
$data['proxied_email'],
0 // time_cron
);
}
}
if ($result === FALSE) {
watchdog('fb_user', "Failed to update fb_user_app table.", array(), WATCHDOG_ERROR);
Dave Cohen
committed
}
}
/**
* Helper to ensure local user is logged out.
*/
function _fb_user_logout() {
session_destroy();
$user = drupal_anonymous_user();
}
/**
* Implementation of hook_fb.
*/
$fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL;
$fb = isset($data['fb']) ? $data['fb'] : NULL;
Dave Cohen
committed
Dave Cohen
committed
if ($fb_app) {
$fb_user_data = _fb_user_get_config($fb_app);
}
if ($op == FB_OP_POST_INIT) {
if (isset($_SESSION['fb_user_fbu']) &&
$_SESSION['fb_user_fbu'] != fb_facebook_user()) {
// User has logged out of facebook, and drupal is only now learning about it.
_fb_user_logout();
drupal_goto($_GET['q']); // @TODO - need request params here?
if (_fb_user_special_page() ||
(variable_get('site_offline', FALSE) && !user_access('administer site configuration'))) {
// Prevent some behavior.
fb_controls(FB_USER_CONTROL_NO_HONOR_MAP, TRUE);
fb_controls(FB_USER_CONTROL_NO_CREATE_MAP, TRUE);
fb_controls(FB_USER_CONTROL_NO_CREATE_ACCOUNT, TRUE);
}
}
elseif ($op == FB_OP_APP_IS_AUTHORIZED) {
// This hook is called on every page request, if the user has authorized
// the app. We used to create accounts and maps here. That code is now
// in FB_OP_AJAX_EVENT, because it turns out this hook is invoked even on
// page not found and access denied pages.
Dave Cohen
committed
// Keep a record of user visiting this app.
// This is a request to learn the user's FB id.
$return = _fb_user_get_fbu($data['uid']);
elseif ($op == FB_OP_GET_USER_SESSION) { // Still necessary???
Dave Cohen
committed
// The fb module is asking for session login information. For example, to
// log in as the user when not on a canvas page. This module may be able
// to provide it, depending on whether the user has logged in, and whether
// the session has expired.
$fbu = $data['fbu'];
$result = db_query("SELECT * FROM {fb_user_app} WHERE apikey = '%s' and fbu = %d AND session_key_expires > %d", $fb_app->apikey, $fbu, time());
Dave Cohen
committed
$data = db_fetch_object($result);
if ($data && $data->session_key)
// Return array with FB id and apikey.
$return = array($data->fbu, $data->session_key);
}
// Facebook has notified us of some event.
// We handle some of the events here.
$event_type = $data['event_type'];
Dave Cohen
committed
Dave Cohen
committed
// Ensure fb_user_app table accurately reflects whether user has authorized.
if ($event_type == FB_APP_EVENT_POST_AUTHORIZE) {
Dave Cohen
committed
// User has authorized us to know some details about her.
$fbu = fb_facebook_user($fb);
$proxied_email = fb_user_get_proxied_email($fbu, $fb_app);
// In special cases, do not store the uid column.
$fb_user_data = _fb_user_get_config($fb_app);
if (variable_get(FB_USER_VAR_STATS, TRUE)) {
// If user has authorized then later removed, there will be a row we can replace
db_query("DELETE FROM {fb_user_app} WHERE apikey = '%s' AND fbu = %d", $fb_app->apikey, $fbu);
if ($user->uid) {
db_query("INSERT INTO {fb_user_app} (apikey, fbu, uid, added, session_key, session_key_expires, time_cron, time_access, proxied_email) VALUES ('%s', %d, %d, 1, '%s', %d, %d, %d, '%s')",
$fb_app->apikey, $fbu,
$user->uid,
0, // time_cron
time(), // time_access
$proxied_email
);
}
else {
db_query("INSERT INTO {fb_user_app} (apikey, fbu, uid, added, session_key, session_key_expires, time_cron, time_access, proxied_email) VALUES ('%s', %d, %d, 1, '%s', %d, %d, %d, '%s')",
$fb_app->apikey, $fbu,
0, // uid not known
0, // time_cron
time(), // time_access
$proxied_email
);
}
Dave Cohen
committed
}
elseif ($event_type == FB_APP_EVENT_POST_REMOVE) {
// User has removed the app from their account.
Dave Cohen
committed
// Should we delete the row here???
db_query("UPDATE {fb_user_app} SET added=0, session_key=NULL, session_key_expires=NULL WHERE apikey='%s' AND fbu=%d",
$fb_app->apikey, fb_facebook_user($fb));
elseif ($op == FB_OP_AJAX_EVENT) {
// fb.js has notified us of an event via AJAX. Not the same as facebook event callback above.
if ($data['event_type'] == 'session_change' && isset($data['event_data']['fbu'])) {
// A user has logged in.
// Don't trust fbu from $data['event_data'], too easy to spoof.
// Don't set fb_user if SESSION[fb_user_fbu], could be an old session not properly cleaned up.
if (($fbu = fb_facebook_user($data['fb'])) &&
$fbu != fb_get_fbu($GLOBALS['user'])) {
// There's no need to honor maps in ajax callback. If honored,
// could lead to new session start which would not actually have any
// effect in ajax callback.
fb_controls(FB_USER_CONTROL_NO_HONOR_MAP, TRUE);
_fb_user_process_authorized_user();
function fb_user_footer() {
$fbu != fb_get_fbu($GLOBALS['user']) &&
$_GET['q'] !== variable_get('site_403', FALSE) &&
$_GET['q'] !== variable_get('site_404', FALSE)) {
$uid = $GLOBALS['user']->uid; // Remember original uid.
_fb_user_process_authorized_user();
if ($uid != $GLOBALS['user']->uid) {
// during user processing, we started a new session.
drupal_goto($_GET['q']); // @TODO - need request params here?
}
}
}
/**
* Test facebook session by calling into facebook. This is expensive, so
* limit check to once per session. Use session variable to flag that we have
* completed the test.
*/
function _fb_user_check_session($fbu) {
// Make sure facebook session is valid and fb_user table is correct.
// Relatively expensive operations, so we perform them only once per session.
if (!isset($_SESSION['fb_user_fbu']) || $_SESSION['fb_user_fbu'] != $fbu) {
if ($valid_session = fb_api_check_session($GLOBALS['_fb'])) { // Expensive check.
$_SESSION['fb_user_fbu'] = $fbu;
}
else {
unset($_SESSION['fb_user_fbu']);
}
}
return (isset($_SESSION['fb_user_fbu']) && $_SESSION['fb_user_fbu'] == $fbu);
}
/**
* If facebook user has authorized app, and account map exists, login as the local user.
*
* @return - TRUE, if user_external_login succeeds.
*/
function _fb_user_external_login($account = NULL) {
$fbu = fb_facebook_user();
if (!$account) {
$account = fb_user_get_local_user($fbu, $GLOBALS['_fb_app']);
}
if ($account &&
$account->uid == $GLOBALS['user']->uid) {
// Already logged in.
return $account;
}
elseif ($fbu &&
$account &&
$account->uid != $GLOBALS['user']->uid &&
!fb_controls(FB_USER_CONTROL_NO_HONOR_MAP)) {
// Map exists. Log in as local user.
if (fb_verbose() === 'extreme') { // debug
watchdog("fb_user", "fb_user_fb changing user to $account->uid");
$session_id = session_id();
}
// user_external_login() fails if already logged in, so log out first.
if ($GLOBALS['user']->uid) {
_fb_user_logout();
}
$valid_user = user_external_login($account);
if (fb_verbose() === 'extreme') { // debug
watchdog("fb_user", "fb_user_fb changed session from $session_id to " . session_id());
}
if ($valid_user) {
// Session changed after external login. Invoking hook here allows modules to drupal_set_message().
fb_invoke(FB_OP_POST_EXTERNAL_LOGIN, array('account' => $account));
return $account;
}
}
return FALSE;
}
/**
* Create a map linking the facebook account to the currently logged in local user account.
*
* @return - TRUE, if map created.
*/
function _fb_user_create_map() {
if ($GLOBALS['user']->uid) {
$fbu = fb_facebook_user();
$account = fb_user_get_local_user($fbu);
if ($fbu &&
!$account &&
!fb_controls(FB_USER_CONTROL_NO_CREATE_MAP)) {
_fb_user_set_map($GLOBALS['user'], $fbu);
return TRUE;
}
}
return FALSE;
}
function _fb_user_create_map_by_email() {
$fbu = fb_facebook_user();
$account = fb_user_get_local_user($fbu, $GLOBALS['_fb_app']);
if ($fbu &&
!$account &&
($email_account = fb_user_get_local_user_by_email($fbu)) &&
!fb_controls(FB_USER_CONTROL_NO_CREATE_MAP)) {
_fb_user_set_map($email_account, $fbu);
return TRUE;
}
return FALSE;
}
/**
* Helper function to create local account for the currently authorized user.
*/
function _fb_user_create_local_account() {
$fbu = fb_facebook_user();
$account = fb_user_get_local_user($fbu);
if ($fbu &&
!$account &&
!fb_controls(FB_USER_CONTROL_NO_CREATE_ACCOUNT)) {
$config = _fb_user_get_config($GLOBALS['_fb_app']);
// Establish user name.
// Case 1: use name from FB
// Case 2: create a unique user name ourselves
// Which we use is determined by the setting at
// admin/build/fb/fb_user
if (variable_get(FB_USER_VAR_USERNAME_STYLE, FB_USER_OPTION_USERNAME_FBU) == FB_USER_OPTION_USERNAME_FULL) {
try {
// Use fb->api() rather than fb_users_getInfo(). Later fails to learn name on test accounts.
$info = $GLOBALS['_fb']->api($fbu);
$username = $info['name'];
} catch (Exception $e) {
fb_log_exception($e, t('Failed to learn full name of new user'), $GLOBALS['_fb']);
}
}
else {
// Create a name that is likely to be unique.
$username = "$fbu@facebook";
}
if ($config['new_user_rid']) {
$roles = array($config['new_user_rid'] => TRUE);
}
else {
$roles = array();
}
$account = fb_user_create_local_user($GLOBALS['_fb'], $GLOBALS['_fb_app'],
$fbu, array(
'name' => $username,
'roles' => $roles,
));
watchdog('fb_user',
t("Created new user !username for application %app", array(
'!username' => l($account->name, 'user/' . $account->uid),
'%app' => $GLOBALS['_fb_app']->label)));
return $account;
}
return FALSE;
}
/**
* Create local account or account map for a facebook user who has authorized the application.
*/
function _fb_user_process_authorized_user() {
$fbu = fb_facebook_user();
$mapped = FALSE;
if ($fbu && _fb_user_check_session($fbu)) {
// First check if map already exists.
$account = fb_user_get_local_user($fbu, $GLOBALS['_fb_app']);
$config = _fb_user_get_config($GLOBALS['_fb_app']);
if (!$account) {
if ($GLOBALS['user']->uid > 0 &&
$config['map_account'][FB_USER_OPTION_MAP_ALWAYS]) {
// Create map for logged in user.
$mapped = _fb_user_create_map();
}
if (!$mapped &&
$config['map_account'][FB_USER_OPTION_MAP_EMAIL]) {
// Create map if email matches.
$mapped = _fb_user_create_map_by_email();
}
if (!$mapped &&
$config['create_account'] == FB_USER_OPTION_CREATE_LOGIN) {
// Create new local account with map.
$mapped = _fb_user_create_local_account();
}
if ($mapped) {
$account = fb_user_get_local_user($fbu, $GLOBALS['_fb_app']);
}
}
if ($account) {
// Ensure the user has any roles associated with this app.
$rid = $config['new_user_rid'];
if ($account && $rid &&
(!isset($account->roles[$rid]) || !$account->roles[$rid])) {
// there should be an API for this...
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)',
$account->uid, $rid);
watchdog('fb_user', "Added role %role to existing user !username for application %app", array(
'!username' => theme('username', $account),
'%app' => $fb_app->label,
'%role' => $rid));
}
// Login as facebook user, if not already.
_fb_user_external_login($account);
}
}
}
Dave Cohen
committed
/**
* Implements hook_form_alter().
*/
function fb_user_form_alter(&$form, &$form_state, $form_id) {
// Add our settings to the fb_app edit form.
module_load_include('inc', 'fb_user', 'fb_user.admin');
fb_user_admin_form_alter($form, $form_state, $form_id);
elseif ($form_id == 'user_edit' && ($app = $form['#fb_app'])) {
// Disable buttons on user/edit/app pages, nothing to submit
unset($form['submit']);
unset($form['delete']);
}
elseif ($form_id == 'user_profile_form') {
Dave Cohen
committed
// On user/edit, hide proxied email
if (isset($form['account']) && isset($form['account']['mail'])) {
$account = $form['_account']['#value'];
if (isset($account->fb_user_proxied_mail) &&
($form['account']['mail']['#default_value'] == $account->fb_user_proxied_mail)) {
Dave Cohen
committed
unset($form['account']['mail']['#default_value']);
}
}
}
Dave Cohen
committed
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
elseif (($form_id == 'user_register' && variable_get(FB_USER_VAR_ALTER_REGISTER, TRUE)) ||
($form_id == 'user_login' && variable_get(FB_USER_VAR_ALTER_LOGIN, TRUE))) {
if ($fb = $GLOBALS['_fb']) {
if ($fbu = fb_facebook_user()) {
// Facebook user has authorized app.
// Show user name and picture.
$form['fb_user'] = array(
'name' => array(
'#value' => '<fb:name uid="' . $fbu . '" useyou="false" linked="false"></fb:name>',
'#type' => 'markup',
'#prefix' => '<div class="fb_user_name">',
'#suffix' => '</div>',
),
'picture' => array(
'#value' => '<fb:profile-pic uid="' . $fbu .'" linked="false"></fb:profile-pic>',
'#type' => 'markup',
'#prefix' => '<div class="fb_user_picture">',
'#suffix' => '</div>',
),
'#weight' => -1,
);
if ($form_id == 'user_register') {
// Provide defaults for name and email.
try {
$data = $fb->api('/' . $fbu); // Facebook Graph lookup.
$form['fb_user']['#fb_user'] = $data;
Dave Cohen
committed
if (isset($form['name']) && !$form['name']['#default_value']) {
// @TODO - ensure name is unique to Drupal.
$form['name']['#default_value'] = $data['name'];
}
elseif (isset($form['account']) && isset($form['account']['name']) &&
!$form['account']['name']['#default_value']) {
// @TODO - ensure name is unique to Drupal.
$form['account']['name']['#default_value'] = $data['name'];
}
Dave Cohen
committed
if (isset($form['mail']) && !$form['mail']['#default_value']) {
$form['mail']['#default_value'] = $data['email'];
}
elseif (isset($form['account']['mail']) && isset($form['account']['mail']) &&
!$form['account']['mail']['#default_value']) {
$form['account']['mail']['#default_value'] = $data['email'];
}
Dave Cohen
committed
}
catch (FacebookApiException $e) {
fb_log_exception($e, t('Failed lookup of %fbu.', array('%fbu' => $fbu)));
}
}
Dave Cohen
committed
}
Dave Cohen
committed
else {
// facebook user has not authorized app.
$fb_button = theme('fb_login_button', t('Connect with Facebook'), array('form_id' => $form_id));
$form['fb_user'] = array(
'#value' => $fb_button,
'#type' => 'markup',
'#weight' => -1,
);
}
}
}
Dave Cohen
committed
}
/**
* Learn the user's proxied email address.
* http://wiki.developers.facebook.com/index.php/Proxied_Email
*/
function fb_user_get_proxied_email($fbu, $fb_app) {
// Try to learn from local database
$result = db_query("SELECT * FROM {fb_user_app} WHERE apikey='%s' AND fbu=%d",
$fb_app->apikey,
$fbu);
if ($data = db_fetch_object($result)) {
$mail = $data->proxied_email;
}
if (!$mail) {
// Ask facebook for info.
$fb = fb_api_init($fb_app);
$info = fb_users_getInfo(array($fbu), $fb);
$data = $info[0];
$mail = $data['proxied_email'];
if ($mail && variable_get(FB_USER_VAR_STATS, TRUE)) {
Dave Cohen
committed
// Store locally.
$result = db_query("UPDATE {fb_user_app} SET proxied_email='%s' WHERE apikey='%s' AND fbu=%d",
$mail, $fb_app->apikey, $fbu);
}
}
return $mail;
/**
* Helper function for menu item access check.
*/
function fb_user_access_own($account, $perm, $allow_admin) {
if ($GLOBALS['user']->uid == $account->uid && user_access($perm)) {
return TRUE;
}
return user_access('administer users');
}
}
/**
* Implementation of hook_user.
*/
function fb_user_user($op, &$edit, &$account, $category = NULL) {
global $user, $_fb_app;
if ($op == 'load' && $account->uid && $_fb_app) {
if (!$account->mail && ($fbu = _fb_user_get_fbu($account->uid))) {
Dave Cohen
committed
// Use proxied email, if facebook app is active and user uses it.
// TODO: confirm drupal never saves proxied address to users.mail.
$account->mail = fb_user_get_proxied_email($fbu, $fb_app);
$account->fb_user_proxied_mail = $account->mail; // Remember where we got address.
}
}
if (!$_fb_app && $op == 'insert' || $op == 'login') {
// A facebook user has logged in. We can map the two accounts together.
$fb_user_data = _fb_user_get_config($_fb_app);
if (($fbu = fb_facebook_user()) &&
$fb_user_data['map_account'] == FB_USER_OPTION_MAP_ALWAYS &&
!fb_controls(FB_USER_CONTROL_NO_CREATE_MAP)) {
// Create fb_user record if it doesn't exist or update existing one
_fb_user_set_map($account, $fbu);
// @TODO - if the app has a role, make sure the user gets that role. (presently, that will not happen until their next request)
// Add tabs on user edit pages to manage maps between local accounts and facebook accounts.
if ($op == 'categories') {
// A tab allowing authmaps to be changed.
$items[] = array(
'name' => 'fb_user',
'title' => t('Facebook'),
'access callback' => 'fb_user_access_own',
'access arguments' => array(1, 'delete own fb_user authmap', TRUE),
'weight' => 1,
elseif ($op == 'form' && $category == 'fb_user') {
Dave Cohen
committed
if (!user_access('administer users') &&
!(user_access('delete own fb_user authmap') &&
$user->uid == $account->uid))
Dave Cohen
committed
return; // hide from this user
$fb_user_data = _fb_user_get_config($_fb_app);
$fbu = _fb_user_get_fbu($account->uid);
if ($fbu) {
// The drupal user is a facebook user. Now, learn more from facebook.
$info = fb_users_getInfo(array($fbu));
$form['map'] = array(
'#type' => 'checkbox',
'#title' => t('Connect account to facebook.com'),
'#default_value' => $fbu,
'#return_value' => $fbu,
'#description' => '',
);
if (is_array($info[0])) {
$data = $info[0];
$fb_link = l($data['name'] ? $data['name'] : $fbu,
'http://www.facebook.com/profile.php', array(
'query' => array('id' => $data['uid'])));
$form['map']['#description'] .=
t('Local account !username corresponds to !profile_page on Facebook.com. <br/>Uncheck then click save to delete this connection.',
array(
'!username' => l($account->name, 'user/' . $account->uid),
'!profile_page' => $fb_link));
}
if (!$fbu) {
if ($user->uid == $account->uid) {
// @TODO - give a user a way to map their facebook account to their local account.
else {
$form[$_fb_app->label] = array(
'#type' => 'markup',
'#value' => t('Local account !username is not connected to facebook.com.',
array('!username' => theme('username', $account),
)),
'#prefix' => "\n<p>",
'#suffix' => "</p>\n",
);
Dave Cohen
committed
if (isset($form)) {
$form['map']['#tree'] = TRUE;
}
else {
// Could add a facebook connect button or canvas page authorization link.
$form['description'] = array(
'#type' => 'markup',
'#value' => t('This account is not associated with a Facebook Application.'),
'#prefix' => '<p>',
'#suffix' => '</p>',
);
}
elseif ($op == 'update' && $category == 'fb_user') {
if ($edit['map']) {
_fb_user_set_map($account, $edit['map']);
}
else {
// Delete account mapping, because administrator has unchecked the connect option.
db_query('DELETE FROM {fb_user} WHERE uid=%d', $account->uid);
db_query('DELETE FROM {fb_user_app} WHERE uid=%d', $account->uid);
db_query('DELETE FROM {fb_user} WHERE uid=%d', $account->uid);
Dave Cohen
committed
}
* Helper function to add or update a row in the fb_user table, which maps local uid to facebook ids.
function _fb_user_set_map($account, $fbu) {
if ($fbu &&
$account->uid != 0) {
$an_fb_user = array(
'uid' => $account->uid,
'fbu' => $fbu,
);
// test if record exists
// @TODO - should this be DELETE FROM {fb_user} WHERE uid=%d OR fbu=%d?
$sql = "SELECT count(*) AS c FROM {fb_user} WHERE uid = %d";
$result = db_query($sql, $account->uid);
$rec = db_fetch_object($result);
if ($rec->c > 0) {
// exists so send along keys for drupal_write_record to update record
$result = drupal_write_record('fb_user', $an_fb_user, "uid");
}
else {
$result = drupal_write_record('fb_user', $an_fb_user);
}
watchdog('fb_user', 'Using fb_user to associate user !user with facebook user id %fbu.',
array('!user' => l($account->name, 'user/' . $account->uid),
'%fbu' => $fbu,
));
}
}
}
/**
* Creates a local Drupal account for the specified facebook user id.
*
* @param fbu
* The facebook user id corresponding to this account.
*
Dave Cohen
committed
* @param edit
* An associative array with user configuration. As would be passed to user_save().
function fb_user_create_local_user($fb, $fb_app, $fbu,
Dave Cohen
committed
$edit = array()) {
Dave Cohen
committed
// Ensure $fbu is a real facebook user id.
if (!$fbu || !is_numeric($fbu)) {
return;
}
$account = fb_user_get_local_user($fbu);
if (!$account) {
// Create a new user in our system
Dave Cohen
committed
Dave Cohen
committed
// Learn some details from facebook.
Dave Cohen
committed
$infos = fb_users_getInfo(array($fbu), $fb);
Dave Cohen
committed
$info = $infos[0];
// All Drupal users get authenticated user role.
$edit['roles'][DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
Dave Cohen
committed
Dave Cohen
committed
if (isset($edit['name']) && $edit['name']) {
$username = $edit['name'];
}
else {
// Fallback, should never be reached.
Dave Cohen
committed
$username = "$fbu@facebook";
$edit['name'] = $username;
}
$i = 1;
while (db_result(db_query("SELECT name FROM {users} WHERE name='%s'", $edit['name']))) {
$i++;
$edit['name'] = $username . '_' . $i;
}
// Give modules a way to suppress new account creation.
$edit['fb_user_do_create'] = TRUE;
Dave Cohen
committed
// Allow third-party module to adjust any of our data before we create
Dave Cohen
committed
$edit = fb_invoke(FB_OP_PRE_USER, array('fbu' => $fbu,
Dave Cohen
committed
'fb' => $GLOBALS['_fb'],
Dave Cohen
committed
'fb_app' => $fb_app,
'info' => $info),
$edit);
if ($edit['fb_user_do_create']) {
unset($edit['fb_user_do_create']); // Don't confuse user_save.
// Fill in any default that are missing.
$defaults = array(
'pass' => user_password(),
'init' => db_escape_string($fbu . '@facebook'), // Supposed to be email, but we may not know it.
'status' => 1,
);
// Mail available only if user has granted extended permission.
if (isset($info['email']) && ($info['email'] != $info['proxied_email'])) {
$defaults['mail'] = $info['email'];
}
Dave Cohen
committed
// Merge defaults
$edit = array_merge($defaults, $edit);
Dave Cohen
committed
// Confirm username is not taken. FB_OP_PRE_USER may have changed it.
if ($uid = db_result(db_query("SELECT uid FROM {users} WHERE name='%s'", $edit['name']))) {
// The desired name is taken.
watchdog('fb_user', 'Failed to create new user %name. That name is already in the users table.',
array('%name' => $edit['name']),
WATCHDOG_ERROR, l(t('view user'), 'user/' . $uid));
}
else {
$account = user_save('', $edit);
_fb_user_set_map($account, $fbu);
watchdog('fb_user', 'New user: %name %email.',
array('%name' => $account->name, '%email' => '<'. $account->mail .'>'),
WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
// Allow third-party modules to act after account creation.
fb_invoke(FB_OP_POST_USER, array('account' => $account,
'fb_app' => $fb_app,
'fb' => $fb));
}
Dave Cohen
committed
}
}
return $account;
}
/**
* Given an app and facebook user id, return the corresponding local user.
* @param $fbu
* User's id on facebook.com
*
* @param $fb_app
* Historically, this method took the app details into account when mapping user ids. Presently, this parameter is not used.
function fb_user_get_local_user($fbu, $fb_app = NULL) {
$result = db_result(db_query("SELECT uid FROM {fb_user} WHERE fbu=%d", array(
$fbu,
)));
if ($result) {
return user_load($result);
/**
* Try to determine the local user account by the email address.
*/
function fb_user_get_local_user_by_email($fbu) {
global $_fb;
if (isset($_fb) && $fbu) {
$info = $_fb->api($fbu);
if (isset($info['email']) &&
($email = $info['email'])) {
return user_load(array('mail' => $email));
}
}
}
/**
* Returns local uids of friends of a given user.
*
* Query is relatively efficient for the current user of a canvas page. For
* all other users, and non-canvas pages it requires expensive call to
* facebook. That said, our local database query may be inefficient for users
* with large numbers of friends, so use with caution.
*
* TODO: should this function cache results?
*
* Note: the api takes fbu as a parameter, but this usually causes problems
* because facebook restricts users to query only about their own friends.
* For the time being, expect this function to work only on canvas pages to
* find friends of the current user.
*
* Only works if the "map accounts" feature is enabled.
*/
function fb_user_get_local_friends($fbu = NULL, $fb_app = NULL) {
if (!isset($fbu)) {
$fbu = fb_facebook_user();
}
$uids = array();
if ($fbus = fb_get_friends($fbu, $fb_app)) {
$query = "SELECT uid FROM {fb_user} WHERE fbu IN ('%s')";
$args[] = implode(',', $fbus);
$result = db_query($query, $args);
while ($data = db_fetch_object($result)) {
if ($data->uid) {
$uids[] = $data->uid;
}
}
}
return $uids;
}
/**
* Given a local user id, find the facebook id. This is for internal use.
* Outside modules use fb_get_fbu().
* Only works if the "map accounts" feature is enabled, or the account was created by this module.
*/
function _fb_user_get_fbu($uid) {
static $cache = array(); // cache to avoid excess queries.
// Look up this user in the authmap
$result = db_result(db_query("SELECT fbu FROM {fb_user} WHERE uid=%d", array(
$uid,
)));
if ($result) {
$cache[$uid] = $result;
}
}
if (isset($cache[$uid])) {
return $cache[$uid];
}
}
//// token module hooks.
Dave Cohen
committed
function fb_user_token_list($type = 'all') {
if ($type == 'all' || $type == 'fb' || $type == 'fb_app') {
$tokens['fb_app']['fb-app-user-fbu'] = t('Current user\'s Facebook ID');
$tokens['fb_app']['fb-app-user-name'] = t('Current user\'s name on Facebook (TODO)');
$tokens['fb_app']['fb-app-user-name-fbml'] = t('Current user\'s name for display on Facebook profile and canvas pages.');
$tokens['fb_app']['fb-app-profile-url'] = t('Current user\'s Facebook profile URL');
}
return $tokens;
}
function fb_user_token_values($type = 'all', $object = NULL) {
Dave Cohen
committed
if ($type == 'fb_app' && $object) {
$fb_app = $object;
global $user;
$fbu = _fb_user_get_fbu($user->uid);
Dave Cohen
committed
if ($fbu) {
$values['fb-app-user-fbu'] = $fbu;
$values['fb-app-user-name'] = 'TODO XXX'; // XXX
$values['fb-app-user-name-fbml'] = '<fb:name uid="'. $fbu .'" />';
$values['fb-app-profile-url'] =
'http://www.facebook.com/profile.php?id='. $fbu;
Dave Cohen
committed
}
}
return $values;
}