| Commit | Line | Data |
|---|---|---|
| 186f033e | 1 | <?php |
| 86e52b97 DC |
2 | /** |
| 3 | * @file | |
| 29be740a DC |
4 | * This module manages relations between local Drupal user accounts |
| 5 | * and their accounts on facebook.com. | |
| 19092d2f | 6 | * |
| 29be740a DC |
7 | * This module can create a new local user account, when a facebook |
| 8 | * user authorizes an application hosted on this server. | |
| 19092d2f | 9 | * |
| 29be740a | 10 | * Links existing local accounts to remote accounts on facebook via |
| d143d368 | 11 | * fb_user table. |
| 19092d2f | 12 | * |
| 3cf45120 DC |
13 | * Drupal refers to a local user id as 'uid'. Facebook's documentation |
| 14 | * and code also uses 'uid'. In these modules we use 'fbu' for facebook's | |
| 29be740a | 15 | * id and 'uid' for Drupal's id. |
| 86e52b97 DC |
16 | */ |
| 17 | ||
| 186f033e DC |
18 | define('FB_USER_OPTION_CREATE_NEVER', 1); |
| 19 | define('FB_USER_OPTION_CREATE_LOGIN', 2); | |
| 186f033e | 20 | |
| 86e52b97 | 21 | define('FB_USER_OPTION_MAP_NEVER', 1); |
| 040ff71e DC |
22 | define('FB_USER_OPTION_MAP_ALWAYS', 2); // Map when user is registered and authorized. |
| 23 | define('FB_USER_OPTION_MAP_EMAIL', 3); // Map when email is exact match. | |
| 86e52b97 | 24 | |
| fa9071e7 DC |
25 | define('FB_USER_VAR_USERNAME_STYLE', 'fb_user_username_style'); // Key used in variables table for this option. |
| 26 | define('FB_USER_OPTION_USERNAME_FULL', 1); // Get full name from FB | |
| 27 | define('FB_USER_OPTION_USERNAME_FBU', 2); // Use unique name | |
| f54b3a2b | 28 | |
| 8dd84187 DC |
29 | define('FB_USER_VAR_ALTER_REGISTER', 'fb_user_alter_register'); |
| 30 | define('FB_USER_VAR_ALTER_LOGIN', 'fb_user_alter_login'); | |
| 73a3b338 | 31 | define('FB_USER_VAR_ALTER_LOGIN_BLOCK', 'fb_user_alter_login_block'); |
| 17bebf6d | 32 | define('FB_USER_VAR_ALTER_CONTACT', 'fb_user_alter_contact'); |
| 8dd84187 | 33 | |
| b24ac6ad DC |
34 | define('FB_USER_VAR_TEXT_REGISTER', 'fb_button_text_register'); |
| 35 | define('FB_USER_VAR_TEXT_LOGIN', 'fb_button_text_login'); | |
| 36 | define('FB_USER_VAR_TEXT_LOGIN_BLOCK', 'fb_button_text_login_block'); | |
| 37 | ||
| faa4c5d6 DC |
38 | define('FB_USER_VAR_CHECK_SESSION', 'fb_user_check_session'); |
| 39 | ||
| 303d3e67 DC |
40 | // Controls - see fb_controls(). |
| 41 | define('FB_USER_CONTROL_NO_CREATE_ACCOUNT', 'fb_user_no_account'); | |
| 42 | define('FB_USER_CONTROL_NO_CREATE_MAP', 'fb_user_no_map_create'); | |
| 43 | define('FB_USER_CONTROL_NO_HONOR_MAP', 'fb_user_no_map'); | |
| 93136bfa | 44 | define('FB_USER_CONTROL_NO_REDIRECT', 'fb_user_no_redirect'); |
| 303d3e67 | 45 | |
| 78672778 DC |
46 | // hook_fb_user(). |
| 47 | define('FB_USER_OP_PRE_USER', 'pre_user'); // Before account creation, fb_user.module | |
| 48 | define('FB_USER_OP_POST_USER', 'post_user'); // After account creation, fb_user.module | |
| 49 | define('FB_USER_OP_POST_EXTERNAL_LOGIN', 'post_external_login'); // user map has changed global user. | |
| 5dd2cd82 DC |
50 | define('FB_USER_OP_POST_USER_CONNECT', 'post_user_connect'); // Connected local account to FB account, fb_user.module |
| 51 | define('FB_USER_OP_POST_USER_DISCONNECT', 'post_user_disconnect'); // Disconnected local account from FB account, fb_user.module | |
| 78672778 DC |
52 | |
| 53 | ||
| ed5232d8 | 54 | /** |
| 3cf45120 | 55 | * Implements hook_permission(). |
| ed5232d8 | 56 | */ |
| 3cf45120 DC |
57 | function fb_user_permission() { |
| 58 | return array( | |
| 59 | 'delete own fb_user authmap' => array( | |
| 60 | 'title' => t('Delete own fb_user authmap'), | |
| 61 | 'description' => t('User can remove their connection to Facebook.'), | |
| 62 | ), | |
| 63 | ); | |
| 72dcacc7 | 64 | } |
| 86e52b97 DC |
65 | |
| 66 | /** | |
| f6ec9cba | 67 | * Implements hook_menu(). |
| f54b3a2b DC |
68 | */ |
| 69 | function fb_user_menu() { | |
| 70 | $items = array(); | |
| 19092d2f | 71 | |
| f54b3a2b DC |
72 | // Admin pages |
| 73 | $items[FB_PATH_ADMIN . '/fb_user'] = array( | |
| 5d87b01a | 74 | 'title' => 'User Settings', |
| f54b3a2b DC |
75 | 'description' => 'Local account to facebook account mapping', |
| 76 | 'page callback' => 'drupal_get_form', | |
| 77 | 'page arguments' => array('fb_user_admin_settings'), | |
| 78 | 'access arguments' => array(FB_PERM_ADMINISTER), | |
| 79 | 'file' => 'fb_user.admin.inc', | |
| 80 | 'type' => MENU_LOCAL_TASK, | |
| 81 | ); | |
| 19092d2f | 82 | |
| f54b3a2b DC |
83 | return $items; |
| 84 | } | |
| 85 | ||
| f54b3a2b | 86 | /** |
| ed5232d8 DC |
87 | * Returns configuration for this module, on a per-app basis. |
| 88 | */ | |
| 89 | function _fb_user_get_config($fb_app) { | |
| 67523320 | 90 | $fb_app_data = fb_get_app_data($fb_app); |
| d8f85b06 | 91 | $fb_user_data = isset($fb_app_data['fb_user']) ? $fb_app_data['fb_user'] : array(); |
| 19092d2f | 92 | |
| ed5232d8 DC |
93 | // Merge in defaults |
| 94 | $fb_user_data += array( | |
| a7339f59 | 95 | 'create_account' => FB_USER_OPTION_CREATE_NEVER, |
| 040ff71e DC |
96 | 'map_account' => array( |
| 97 | FB_USER_OPTION_MAP_ALWAYS => FB_USER_OPTION_MAP_ALWAYS, | |
| 98 | FB_USER_OPTION_MAP_EMAIL => FB_USER_OPTION_MAP_EMAIL, | |
| 99 | ), | |
| d8f85b06 | 100 | 'new_user_rid' => NULL, |
| 93136bfa | 101 | 'connected_user_rid' => NULL, |
| ed5232d8 DC |
102 | ); |
| 103 | return $fb_user_data; | |
| 104 | } | |
| 105 | ||
| 106 | /** | |
| 7364859a DC |
107 | * There are several pages where we don't want to automatically create a new |
| 108 | * account or use an account configured for this app. | |
| 86e52b97 | 109 | */ |
| 86e52b97 | 110 | function _fb_user_special_page() { |
| 3cf45120 | 111 | // fb_app/event is called by facebook. Don't create accounts on that page. |
| 303d3e67 | 112 | return ((arg(0) == 'fb_app' && arg(1) == 'event')); |
| 86e52b97 DC |
113 | } |
| 114 | ||
| 7364859a | 115 | /** |
| 3cf45120 | 116 | * Implements hook_fb. |
| 3a512626 | 117 | */ |
| 8fbcf653 | 118 | function fb_user_fb($op, $data, &$return) { |
| 81e5e23a DC |
119 | $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL; |
| 120 | $fb = isset($data['fb']) ? $data['fb'] : NULL; | |
| 19092d2f | 121 | |
| 8fbcf653 | 122 | global $user; |
| 19092d2f | 123 | |
| ed5232d8 DC |
124 | if ($fb_app) { |
| 125 | $fb_user_data = _fb_user_get_config($fb_app); | |
| 126 | } | |
| 19092d2f | 127 | |
| 78672778 | 128 | if ($op == FB_OP_POST_INIT && $fb) { |
| 93136bfa | 129 | $fbu = fb_facebook_user(); |
| 040ff71e | 130 | if (isset($_SESSION['fb_user_fbu']) && |
| 626faa71 DC |
131 | $_SESSION['fb_user_fbu'] != $fbu && |
| 132 | !(fb_settings(FB_SETTINGS_CB_SESSION) && !$fbu)) { | |
| 133 | // User has logged out of facebook, and drupal is only now learning | |
| 3cf45120 | 134 | // about it. Check disabled when using FB_SETTINGS_CB_SESSION, because |
| 626faa71 DC |
135 | // we aren't always passed a signed_request in that case, which would |
| 136 | // otherwise trigger this. | |
| faa4c5d6 | 137 | _fb_logout(); |
| 93136bfa | 138 | if (!fb_controls(FB_USER_CONTROL_NO_REDIRECT)) { |
| a732fef6 | 139 | drupal_goto(current_path()); // @TODO - need request params here? |
| 93136bfa | 140 | } |
| 040ff71e | 141 | } |
| 303d3e67 DC |
142 | |
| 143 | if (_fb_user_special_page() || | |
| 144 | (variable_get('site_offline', FALSE) && !user_access('administer site configuration'))) { | |
| 145 | // Prevent some behavior. | |
| 146 | fb_controls(FB_USER_CONTROL_NO_HONOR_MAP, TRUE); | |
| 147 | fb_controls(FB_USER_CONTROL_NO_CREATE_MAP, TRUE); | |
| 148 | fb_controls(FB_USER_CONTROL_NO_CREATE_ACCOUNT, TRUE); | |
| 149 | } | |
| 187e24c9 | 150 | if (isset($_REQUEST['_fb_user_fbu'])) { |
| 3cf45120 | 151 | // We've triggered a reload. Don't redirect again, as that will |
| 626faa71 DC |
152 | // cause infinite loop if browser not accepting third-party cookies. |
| 153 | fb_controls(FB_USER_CONTROL_NO_REDIRECT, TRUE); | |
| 154 | } | |
| 155 | ||
| 93136bfa DC |
156 | if ($rid = $fb_user_data['connected_user_rid']) { |
| 157 | if ($fbu) { | |
| 158 | // User is connected to facebook. | |
| 159 | if (!isset($user->roles[$rid])) { | |
| 160 | $user->roles[$rid] = $rid; // Should be role name, but that requires db query. | |
| 161 | // Reload user permissions. | |
| ae674940 DC |
162 | drupal_static_reset('user_access'); |
| 163 | drupal_static_reset('menu_get_item'); | |
| 93136bfa DC |
164 | } |
| 165 | } | |
| 166 | else { | |
| 167 | // User is not connected to facebook. | |
| 0eefdc90 DC |
168 | if ($rid != DRUPAL_AUTHENTICATED_RID && isset($user->roles[$rid])) { |
| 169 | // Out of paranoia, unset role. This will be reached only if the | |
| 170 | //user was somehow saved while connected to facebook. | |
| 93136bfa DC |
171 | unset($user->roles[$rid]); |
| 172 | // Reload user permissions. | |
| ae674940 DC |
173 | drupal_static_reset('user_access'); |
| 174 | drupal_static_reset('menu_get_item'); | |
| 93136bfa DC |
175 | } |
| 176 | } | |
| 177 | } | |
| 040ff71e | 178 | } |
| 456be90c | 179 | elseif ($op == FB_OP_GET_FBU) { |
| 3a512626 | 180 | // This is a request to learn the user's FB id. |
| d143d368 | 181 | $return = _fb_user_get_fbu($data['uid']); |
| 186f033e | 182 | } |
| 346e5a9b DC |
183 | elseif ($op == FB_OP_GET_UID) { |
| 184 | // This is a request to learn the facebook user's local id. | |
| e1442496 | 185 | $return = _fb_user_get_uid($data['fbu'], $data['fb_app']); |
| 346e5a9b | 186 | } |
| a24a1868 | 187 | elseif ($op == FB_OP_AJAX_EVENT) { |
| 3cf45120 | 188 | // fb.js has notified us of an event via AJAX. Not the same as facebook event callback above. |
| a24a1868 DC |
189 | if ($data['event_type'] == 'session_change' && isset($data['event_data']['fbu'])) { |
| 190 | // A user has logged in. | |
| 7308ca43 | 191 | // Don't trust fbu from $data['event_data'], too easy to spoof. |
| d143d368 | 192 | // Don't set fb_user if SESSION[fb_user_fbu], could be an old session not properly cleaned up. |
| 040ff71e | 193 | if (($fbu = fb_facebook_user($data['fb'])) && |
| 303d3e67 DC |
194 | $fbu != fb_get_fbu($GLOBALS['user'])) { |
| 195 | ||
| 187e24c9 DC |
196 | // In ajax callback, there's no reason to redirect even if user |
| 197 | // changes. But we should honor session, as even ajax can set a new | |
| 198 | // cookie. | |
| 199 | fb_controls(FB_USER_CONTROL_NO_REDIRECT, TRUE); | |
| 200 | ||
| 5a247ec5 | 201 | _fb_user_process_authorized_user(); |
| 7308ca43 | 202 | } |
| a24a1868 DC |
203 | } |
| 204 | } | |
| 86e52b97 DC |
205 | } |
| 206 | ||
| 3cf45120 DC |
207 | /** |
| 208 | * Implements hook_page_alter(). | |
| 209 | * | |
| 210 | * Reload page if user has changed. This would not make sense during an ajax | |
| 211 | * callback (or anything else) where a redirect would not refresh the browsers | |
| 212 | * page. That's why we do it here in page_alter(). | |
| 213 | */ | |
| 214 | function fb_user_page_alter(&$page) { | |
| 215 | _fb_user_check_and_goto(); | |
| 216 | } | |
| 217 | ||
| 218 | /** | |
| 219 | * Detect whether facebook indicates the user has changed. If so, redirect. | |
| 220 | */ | |
| 221 | function _fb_user_check_and_goto() { | |
| 5a247ec5 | 222 | if (($fbu = fb_facebook_user()) && |
| 626faa71 | 223 | !fb_is_tab() && // $fbu is page id, not visitor id, on tabs. |
| 41ae3090 | 224 | $fbu != fb_get_fbu($GLOBALS['user'])) { |
| 303d3e67 | 225 | $uid = $GLOBALS['user']->uid; // Remember original uid. |
| 5a247ec5 | 226 | _fb_user_process_authorized_user(); |
| 303d3e67 DC |
227 | if ($uid != $GLOBALS['user']->uid) { |
| 228 | // during user processing, we started a new session. | |
| 93136bfa | 229 | if (!fb_controls(FB_USER_CONTROL_NO_REDIRECT)) { |
| b389f367 DC |
230 | // No longer passing _fb_user_fbu, because it causes problem when logging out of one frame then refreshing a frame with that parameter in the URL. |
| 231 | drupal_goto(request_path(), array( | |
| 232 | //'_fb_user_fbu' => $fbu, // Avoid refresh loop, disabled for now. Still needed? | |
| 233 | )); | |
| 93136bfa | 234 | } |
| 303d3e67 DC |
235 | } |
| 236 | } | |
| 237 | } | |
| 5a247ec5 | 238 | |
| 303d3e67 | 239 | /** |
| 3cf45120 DC |
240 | * Test facebook session by calling into facebook. This is expensive, so |
| 241 | * limit check to once per session. Use session variable to flag that we have | |
| 303d3e67 DC |
242 | * completed the test. |
| 243 | */ | |
| 244 | function _fb_user_check_session($fbu) { | |
| d143d368 | 245 | // Make sure facebook session is valid and fb_user table is correct. |
| 303d3e67 DC |
246 | // Relatively expensive operations, so we perform them only once per session. |
| 247 | if (!isset($_SESSION['fb_user_fbu']) || $_SESSION['fb_user_fbu'] != $fbu) { | |
| 248 | if ($valid_session = fb_api_check_session($GLOBALS['_fb'])) { // Expensive check. | |
| 249 | $_SESSION['fb_user_fbu'] = $fbu; | |
| 250 | } | |
| 251 | else { | |
| 252 | unset($_SESSION['fb_user_fbu']); | |
| 253 | } | |
| 254 | } | |
| 255 | return (isset($_SESSION['fb_user_fbu']) && $_SESSION['fb_user_fbu'] == $fbu); | |
| 256 | } | |
| 257 | ||
| 258 | /** | |
| 259 | * If facebook user has authorized app, and account map exists, login as the local user. | |
| 260 | * | |
| 261 | * @return - TRUE, if user_external_login succeeds. | |
| 262 | */ | |
| fa9071e7 | 263 | function _fb_user_external_login($account = NULL) { |
| 303d3e67 | 264 | $fbu = fb_facebook_user(); |
| fa9071e7 DC |
265 | if (!$account) { |
| 266 | $account = fb_user_get_local_user($fbu, $GLOBALS['_fb_app']); | |
| 267 | } | |
| 303d3e67 DC |
268 | if ($account && |
| 269 | $account->uid == $GLOBALS['user']->uid) { | |
| 270 | // Already logged in. | |
| 271 | return $account; | |
| 272 | } | |
| 273 | elseif ($fbu && | |
| 274 | $account && | |
| 275 | $account->uid != $GLOBALS['user']->uid && | |
| 276 | !fb_controls(FB_USER_CONTROL_NO_HONOR_MAP)) { | |
| 626faa71 | 277 | |
| 3cf45120 | 278 | // Map exists. Log in as local user. |
| 626faa71 | 279 | $session_id = session_id(); |
| 303d3e67 DC |
280 | if (fb_verbose() === 'extreme') { // debug |
| 281 | watchdog("fb_user", "fb_user_fb changing user to $account->uid"); | |
| 303d3e67 | 282 | } |
| 626faa71 | 283 | |
| 5a247ec5 DC |
284 | // user_external_login() fails if already logged in, so log out first. |
| 285 | if ($GLOBALS['user']->uid) { | |
| faa4c5d6 | 286 | _fb_logout(); |
| 5a247ec5 | 287 | } |
| 626faa71 | 288 | |
| 3cf45120 DC |
289 | // user_external_login() removed in D7, no replacement. Let's hope the following works. |
| 290 | $GLOBALS['user'] = $account; | |
| 291 | $drupal_sux = (array) $account; | |
| 292 | user_login_finalize($drupal_sux); | |
| 626faa71 DC |
293 | |
| 294 | // Special effort to support browsers without third-party cookies. | |
| 295 | if (function_exists('fb_sess_regenerate_hack')) { | |
| 296 | fb_sess_regenerate_hack(); | |
| 297 | } | |
| 298 | ||
| 303d3e67 DC |
299 | if (fb_verbose() === 'extreme') { // debug |
| 300 | watchdog("fb_user", "fb_user_fb changed session from $session_id to " . session_id()); | |
| 301 | } | |
| 626faa71 | 302 | |
| 3cf45120 DC |
303 | // Session changed after external login. Invoking hook here allows modules to drupal_set_message(). |
| 304 | fb_invoke(FB_USER_OP_POST_EXTERNAL_LOGIN, array('account' => $account), NULL, 'fb_user'); | |
| 305 | return $account; | |
| 303d3e67 DC |
306 | } |
| 307 | return FALSE; | |
| 308 | } | |
| 309 | ||
| 310 | /** | |
| 311 | * Create a map linking the facebook account to the currently logged in local user account. | |
| 312 | * | |
| 313 | * @return - TRUE, if map created. | |
| 314 | */ | |
| 315 | function _fb_user_create_map() { | |
| 316 | if ($GLOBALS['user']->uid) { | |
| 317 | $fbu = fb_facebook_user(); | |
| d143d368 | 318 | $account = fb_user_get_local_user($fbu); |
| 303d3e67 DC |
319 | if ($fbu && |
| 320 | !$account && | |
| 321 | !fb_controls(FB_USER_CONTROL_NO_CREATE_MAP)) { | |
| d143d368 | 322 | _fb_user_set_map($GLOBALS['user'], $fbu); |
| 5dd2cd82 DC |
323 | fb_invoke(FB_USER_OP_POST_USER_CONNECT, array( |
| 324 | 'account' => $GLOBALS['user'], | |
| 325 | 'fbu' => $fbu, | |
| 326 | ), NULL, 'fb_user'); | |
| 303d3e67 DC |
327 | return TRUE; |
| 328 | } | |
| 329 | } | |
| 330 | return FALSE; | |
| 331 | } | |
| 332 | ||
| 333 | function _fb_user_create_map_by_email() { | |
| 334 | $fbu = fb_facebook_user(); | |
| 335 | $account = fb_user_get_local_user($fbu, $GLOBALS['_fb_app']); | |
| 336 | if ($fbu && | |
| 337 | !$account && | |
| 338 | ($email_account = fb_user_get_local_user_by_email($fbu)) && | |
| 339 | !fb_controls(FB_USER_CONTROL_NO_CREATE_MAP)) { | |
| d143d368 | 340 | _fb_user_set_map($email_account, $fbu); |
| 5dd2cd82 DC |
341 | fb_invoke(FB_USER_OP_POST_USER_CONNECT, array( |
| 342 | 'account' => $GLOBALS['user'], | |
| 343 | 'fbu' => $fbu, | |
| 344 | ), NULL, 'fb_user'); | |
| 303d3e67 DC |
345 | return TRUE; |
| 346 | } | |
| 347 | return FALSE; | |
| 348 | } | |
| 349 | ||
| 350 | /** | |
| 351 | * Helper function to create local account for the currently authorized user. | |
| 352 | */ | |
| 353 | function _fb_user_create_local_account() { | |
| 354 | $fbu = fb_facebook_user(); | |
| d143d368 | 355 | $account = fb_user_get_local_user($fbu); |
| 303d3e67 DC |
356 | if ($fbu && |
| 357 | !$account && | |
| 358 | !fb_controls(FB_USER_CONTROL_NO_CREATE_ACCOUNT)) { | |
| 359 | $config = _fb_user_get_config($GLOBALS['_fb_app']); | |
| 19092d2f DC |
360 | |
| 361 | // Establish user name. | |
| fa9071e7 DC |
362 | // Case 1: use name from FB |
| 363 | // Case 2: create a unique user name ourselves | |
| 19092d2f | 364 | // Which we use is determined by the setting at |
| 3cf45120 | 365 | // admin/structure/fb/fb_user |
| 19092d2f | 366 | if (variable_get(FB_USER_VAR_USERNAME_STYLE, FB_USER_OPTION_USERNAME_FBU) == FB_USER_OPTION_USERNAME_FULL) { |
| fa9071e7 | 367 | try { |
| 3cf45120 | 368 | // Use fb->api() rather than fb_users_getInfo(). Later fails to learn name on test accounts. |
| fa9071e7 DC |
369 | $info = $GLOBALS['_fb']->api($fbu); |
| 370 | $username = $info['name']; | |
| 371 | } catch (Exception $e) { | |
| 372 | fb_log_exception($e, t('Failed to learn full name of new user'), $GLOBALS['_fb']); | |
| 373 | } | |
| 374 | } | |
| 375 | else { | |
| 376 | // Create a name that is likely to be unique. | |
| 377 | $username = "$fbu@facebook"; | |
| 378 | } | |
| 379 | ||
| 303d3e67 DC |
380 | if ($config['new_user_rid']) { |
| 381 | $roles = array($config['new_user_rid'] => TRUE); | |
| 382 | } | |
| 383 | else { | |
| 384 | $roles = array(); | |
| 385 | } | |
| 386 | ||
| d143d368 | 387 | $account = fb_user_create_local_user($GLOBALS['_fb'], $GLOBALS['_fb_app'], |
| 303d3e67 DC |
388 | $fbu, array( |
| 389 | 'name' => $username, | |
| 390 | 'roles' => $roles, | |
| 391 | )); | |
| 19092d2f | 392 | watchdog('fb_user', |
| 303d3e67 DC |
393 | t("Created new user !username for application %app", array( |
| 394 | '!username' => l($account->name, 'user/' . $account->uid), | |
| fa9071e7 | 395 | '%app' => $GLOBALS['_fb_app']->label))); |
| 19092d2f | 396 | |
| 303d3e67 DC |
397 | return $account; |
| 398 | } | |
| 399 | return FALSE; | |
| 400 | } | |
| 401 | ||
| 402 | /** | |
| 403 | * Create local account or account map for a facebook user who has authorized the application. | |
| 404 | */ | |
| 405 | function _fb_user_process_authorized_user() { | |
| 406 | $fbu = fb_facebook_user(); | |
| 407 | $mapped = FALSE; | |
| 626faa71 | 408 | |
| faa4c5d6 DC |
409 | if ($fbu && |
| 410 | (!variable_get(FB_USER_VAR_CHECK_SESSION, FALSE) || _fb_user_check_session($fbu))) { | |
| 5a5422ab | 411 | $fb_app = $GLOBALS['_fb_app']; |
| 303d3e67 | 412 | // First check if map already exists. |
| 5a5422ab DC |
413 | $account = fb_user_get_local_user($fbu, $fb_app); |
| 414 | $config = _fb_user_get_config($fb_app); | |
| 626faa71 | 415 | |
| 303d3e67 | 416 | if (!$account) { |
| 303d3e67 DC |
417 | if ($GLOBALS['user']->uid > 0 && |
| 418 | $config['map_account'][FB_USER_OPTION_MAP_ALWAYS]) { | |
| 419 | // Create map for logged in user. | |
| 420 | $mapped = _fb_user_create_map(); | |
| 421 | } | |
| 422 | if (!$mapped && | |
| 423 | $config['map_account'][FB_USER_OPTION_MAP_EMAIL]) { | |
| 424 | // Create map if email matches. | |
| 425 | $mapped = _fb_user_create_map_by_email(); | |
| 426 | } | |
| 626faa71 | 427 | |
| 303d3e67 DC |
428 | if (!$mapped && |
| 429 | $config['create_account'] == FB_USER_OPTION_CREATE_LOGIN) { | |
| 430 | // Create new local account with map. | |
| 431 | $mapped = _fb_user_create_local_account(); | |
| 432 | } | |
| 626faa71 | 433 | |
| 303d3e67 | 434 | if ($mapped) { |
| 5a5422ab | 435 | $account = fb_user_get_local_user($fbu, $fb_app); |
| 303d3e67 DC |
436 | } |
| 437 | } | |
| 626faa71 | 438 | |
| 303d3e67 DC |
439 | if ($account) { |
| 440 | // Ensure the user has any roles associated with this app. | |
| 441 | $rid = $config['new_user_rid']; | |
| 442 | if ($account && $rid && | |
| fa9071e7 | 443 | (!isset($account->roles[$rid]) || !$account->roles[$rid])) { |
| 303d3e67 | 444 | // there should be an API for this... |
| 3cf45120 DC |
445 | $query = db_insert('users_roles') |
| 446 | ->fields(array( | |
| 447 | 'uid' => $account->uid, | |
| 448 | 'rid' => $rid)) | |
| 449 | ->execute(); | |
| 303d3e67 | 450 | watchdog('fb_user', "Added role %role to existing user !username for application %app", array( |
| fa9071e7 | 451 | '!username' => theme('username', $account), |
| 303d3e67 DC |
452 | '%app' => $fb_app->label, |
| 453 | '%role' => $rid)); | |
| 454 | } | |
| fa9071e7 DC |
455 | |
| 456 | // Login as facebook user, if not already. | |
| 457 | _fb_user_external_login($account); | |
| 303d3e67 DC |
458 | } |
| 459 | } | |
| 460 | } | |
| 19092d2f | 461 | |
| 303d3e67 | 462 | |
| 17bebf6d | 463 | function _fb_user_facebook_data($fb) { |
| 329b36aa DC |
464 | if ($fbu = fb_facebook_user($fb)) { |
| 465 | try { | |
| 466 | $data = fb_api($fbu); | |
| 467 | return $data; | |
| 468 | } | |
| 469 | catch (FacebookApiException $e) { | |
| 470 | fb_log_exception($e, t('Failed lookup of %fbu.', array('%fbu' => $fbu))); | |
| 471 | } | |
| 17bebf6d DC |
472 | } |
| 473 | } | |
| 474 | ||
| 8dd84187 | 475 | /** |
| 73a3b338 DC |
476 | * Helper function to retrieve button text. |
| 477 | */ | |
| 478 | function _fb_user_button_text($form_id) { | |
| 3cf45120 | 479 | $button_text = &drupal_static(__FUNCTION__); |
| 73a3b338 DC |
480 | |
| 481 | if (!isset($button_text)) { | |
| 482 | $button_text = array( | |
| c592885c | 483 | 'user_register_form' => variable_get(FB_USER_VAR_TEXT_REGISTER, NULL), |
| b24ac6ad DC |
484 | 'user_login' => variable_get(FB_USER_VAR_TEXT_LOGIN, NULL), |
| 485 | 'user_login_block' => variable_get(FB_USER_VAR_TEXT_LOGIN_BLOCK, NULL), | |
| 73a3b338 DC |
486 | ); |
| 487 | } | |
| 488 | ||
| 489 | return isset($button_text[$form_id]) ? $button_text[$form_id] : ''; | |
| 490 | } | |
| 491 | ||
| 492 | /** | |
| 8dd84187 DC |
493 | * Implements hook_form_alter(). |
| 494 | */ | |
| a9167ce4 | 495 | function fb_user_form_alter(&$form, &$form_state, $form_id) { |
| 81e5e23a | 496 | if (isset($form['fb_app_data'])) { |
| 040ff71e DC |
497 | // Add our settings to the fb_app edit form. |
| 498 | module_load_include('inc', 'fb_user', 'fb_user.admin'); | |
| 499 | fb_user_admin_form_alter($form, $form_state, $form_id); | |
| 186f033e | 500 | } |
| 456be90c | 501 | elseif ($form_id == 'user_edit' && ($app = $form['#fb_app'])) { |
| 7364859a DC |
502 | // Disable buttons on user/edit/app pages, nothing to submit |
| 503 | unset($form['submit']); | |
| 504 | unset($form['delete']); | |
| 505 | } | |
| 17bebf6d DC |
506 | |
| 507 | // Add name and email to some forms. | |
| 93136bfa DC |
508 | if (isset($GLOBALS['_fb'])) { |
| 509 | $fb = $GLOBALS['_fb']; | |
| b26c58b4 | 510 | if (!$GLOBALS['user']->uid && // No alters to user add form. |
| c592885c | 511 | (($form_id == 'user_register_form' && variable_get(FB_USER_VAR_ALTER_REGISTER, TRUE)) || |
| b26c58b4 DC |
512 | ($form_id == 'user_login' && variable_get(FB_USER_VAR_ALTER_LOGIN, TRUE)) || |
| 513 | ($form_id == 'user_login_block' && variable_get(FB_USER_VAR_ALTER_LOGIN_BLOCK, TRUE)))) { | |
| 3cf45120 | 514 | |
| 8dd84187 DC |
515 | if ($fbu = fb_facebook_user()) { |
| 516 | // Facebook user has authorized app. | |
| 93136bfa | 517 | |
| 8dd84187 DC |
518 | // Show user name and picture. |
| 519 | $form['fb_user'] = array( | |
| 520 | 'name' => array( | |
| 3cf45120 | 521 | '#markup' => '<fb:name uid="' . $fbu . '" useyou="false" linked="false"></fb:name>', |
| 8dd84187 DC |
522 | '#prefix' => '<div class="fb_user_name">', |
| 523 | '#suffix' => '</div>', | |
| 524 | ), | |
| 525 | 'picture' => array( | |
| 3cf45120 | 526 | '#markup' => '<fb:profile-pic uid="' . $fbu . '" linked="false"></fb:profile-pic>', |
| 8dd84187 DC |
527 | '#prefix' => '<div class="fb_user_picture">', |
| 528 | '#suffix' => '</div>', | |
| 529 | ), | |
| 530 | '#weight' => -1, | |
| 531 | ); | |
| 19092d2f | 532 | |
| c592885c | 533 | if ($form_id == 'user_register_form') { |
| 8dd84187 | 534 | // Provide defaults for name and email. |
| 17bebf6d | 535 | if ($data = _fb_user_facebook_data($fb)) { |
| 303d3e67 | 536 | $form['fb_user']['#fb_user'] = $data; |
| 19092d2f | 537 | |
| 8dd84187 DC |
538 | if (isset($form['name']) && !$form['name']['#default_value']) { |
| 539 | // @TODO - ensure name is unique to Drupal. | |
| 540 | $form['name']['#default_value'] = $data['name']; | |
| 541 | } | |
| 303d3e67 DC |
542 | elseif (isset($form['account']) && isset($form['account']['name']) && |
| 543 | !$form['account']['name']['#default_value']) { | |
| 544 | // @TODO - ensure name is unique to Drupal. | |
| 545 | $form['account']['name']['#default_value'] = $data['name']; | |
| 546 | } | |
| 8dd84187 DC |
547 | if (isset($form['mail']) && !$form['mail']['#default_value']) { |
| 548 | $form['mail']['#default_value'] = $data['email']; | |
| 549 | } | |
| 303d3e67 DC |
550 | elseif (isset($form['account']['mail']) && isset($form['account']['mail']) && |
| 551 | !$form['account']['mail']['#default_value']) { | |
| 552 | $form['account']['mail']['#default_value'] = $data['email']; | |
| 553 | } | |
| 8dd84187 | 554 | } |
| 9f770b6e DC |
555 | } |
| 556 | } | |
| 19092d2f | 557 | |
| 8dd84187 DC |
558 | else { |
| 559 | // facebook user has not authorized app. | |
| 3cf45120 | 560 | $fb_button = theme('fb_login_button', array('text' => t(_fb_user_button_text($form_id)))); |
| 8dd84187 | 561 | $form['fb_user'] = array( |
| 8dd84187 | 562 | '#type' => 'markup', |
| 3cf45120 | 563 | '#markup' => $fb_button, |
| 73a3b338 | 564 | '#weight' => -1, // Ideally, we'd put ourself next to openid login, but doesn't look right when next to form buttons. |
| 475821b8 | 565 | '#prefix' => '<div class="fb_user-login-button-wrapper">', |
| 73a3b338 | 566 | '#suffix' => '</div>', |
| 8dd84187 | 567 | ); |
| 9f770b6e DC |
568 | } |
| 569 | } | |
| ae674940 | 570 | elseif ($form_id == 'contact_site_form' && variable_get(FB_USER_VAR_ALTER_CONTACT, TRUE)) { |
| 17bebf6d DC |
571 | if ($data = _fb_user_facebook_data($fb)) { |
| 572 | if (!$form['name']['#default_value'] || strpos($form['name']['#default_value'], '@facebook')) { | |
| 573 | $form['name']['#default_value'] = $data['name']; | |
| 574 | } | |
| 575 | if (!$form['mail']['#default_value']) { | |
| 576 | $form['mail']['#default_value'] = $data['email']; | |
| 577 | } | |
| 578 | } | |
| 579 | } | |
| 9f770b6e | 580 | } |
| 19092d2f | 581 | |
| 1038c8a6 DC |
582 | } |
| 583 | ||
| 584 | /** | |
| 2b70138f DC |
585 | * Helper function for menu item access check. |
| 586 | */ | |
| 587 | function fb_user_access_own($account, $perm, $allow_admin) { | |
| 588 | if ($GLOBALS['user']->uid == $account->uid && user_access($perm)) { | |
| 589 | return TRUE; | |
| 590 | } | |
| 456be90c | 591 | elseif ($allow_admin) { |
| 2b70138f DC |
592 | return user_access('administer users'); |
| 593 | } | |
| 594 | } | |
| 595 | ||
| 3cf45120 | 596 | |
| 86e52b97 | 597 | /** |
| 3cf45120 DC |
598 | * Implements hook_user_load. |
| 599 | * | |
| 600 | * Use no standard email, use proxy email if available | |
| 86e52b97 | 601 | */ |
| 3cf45120 DC |
602 | function fb_user_user_load($users) { |
| 603 | global $_fb_app; | |
| 604 | ||
| 605 | foreach ($users as $account) { | |
| 606 | if ($account->uid && $_fb_app) { | |
| 607 | if (!$account->mail && ($fbu = _fb_user_get_fbu($account->uid))) { | |
| 608 | // Use proxied email, if facebook app is active and user uses it. | |
| 609 | // TODO: confirm drupal never saves proxied address to users.mail. | |
| 610 | $account->mail = fb_user_get_proxied_email($fbu, $_fb_app); | |
| 611 | $account->fb_user_proxied_mail = $account->mail; // Remember where we got address. | |
| 612 | } | |
| 613 | } | |
| 614 | } | |
| 615 | } | |
| 616 | ||
| 617 | /** | |
| 618 | * Implements hook_user_login. | |
| 619 | * | |
| 620 | * Map local Drupal user to FB user under certain circumstances. | |
| 621 | */ | |
| 622 | function fb_user_user_login(&$edit, $account) { | |
| d143d368 | 623 | global $user, $_fb_app; |
| 19092d2f | 624 | |
| 3cf45120 DC |
625 | // A facebook user has logged in. We can map the two accounts together. |
| 626 | $fb_user_data = _fb_user_get_config($_fb_app); | |
| 627 | if (($fbu = fb_facebook_user()) && | |
| 475821b8 | 628 | $fb_user_data['map_account'][FB_USER_OPTION_MAP_ALWAYS] && |
| 3cf45120 DC |
629 | !fb_controls(FB_USER_CONTROL_NO_CREATE_MAP)) { |
| 630 | ||
| 631 | // Create fb_user record if it doesn't exist or update existing one | |
| 632 | _fb_user_set_map($account, $fbu); | |
| 633 | ||
| 634 | // @TODO - if the app has a role, make sure the user gets that role. (presently, | |
| 635 | // that will not happen until their next request) | |
| 1038c8a6 | 636 | } |
| 3cf45120 | 637 | } |
| 1038c8a6 | 638 | |
| 3cf45120 DC |
639 | /** |
| 640 | * Implements hook_user_insert. | |
| 641 | * | |
| 642 | * When user is created create record | |
| 643 | * in fb_user to map local Drupal user to FB user. | |
| 644 | */ | |
| 645 | function fb_user_user_insert(&$edit, $account, $category) { | |
| 646 | global $user, $_fb_app; | |
| 647 | ||
| 648 | // Map the two accounts together. | |
| d143d368 DC |
649 | $fb_user_data = _fb_user_get_config($_fb_app); |
| 650 | if (($fbu = fb_facebook_user()) && | |
| 475821b8 | 651 | $fb_user_data['map_account'][FB_USER_OPTION_MAP_ALWAYS] && |
| 303d3e67 | 652 | !fb_controls(FB_USER_CONTROL_NO_CREATE_MAP)) { |
| 19092d2f | 653 | |
| f6ec9cba DC |
654 | // Create fb_user record if it doesn't exist or update existing one. |
| 655 | if ($account->uid == $user->uid) { | |
| 656 | _fb_user_set_map($account, $fbu); | |
| 657 | } | |
| 19092d2f | 658 | |
| 303d3e67 | 659 | // @TODO - if the app has a role, make sure the user gets that role. (presently, that will not happen until their next request) |
| 86e52b97 | 660 | } |
| 3cf45120 DC |
661 | } |
| 662 | ||
| 663 | /** | |
| 664 | * Implements hook_user_view. | |
| 665 | * | |
| 666 | * Show extra info when user being viewed. | |
| 667 | */ | |
| 668 | function fb_user_user_view($account, $view_mode, $langcode) { | |
| 669 | ||
| 670 | } | |
| 671 | ||
| 672 | /** | |
| 41ae3090 | 673 | * Implements hook_user_update(). |
| 3cf45120 DC |
674 | * |
| 675 | * User is about to be updated. | |
| 676 | */ | |
| 677 | function fb_user_user_update(&$edit, $account, $category) { | |
| 678 | if ($edit['map']) { | |
| 679 | _fb_user_set_map($account, $edit['map']); | |
| 86e52b97 | 680 | } |
| 3cf45120 DC |
681 | else { |
| 682 | // Delete account mapping, because administrator has unchecked the connect option. | |
| 683 | $num_deleted = db_delete('fb_user') | |
| 684 | ->condition('uid', $account->uid) | |
| 685 | ->execute(); | |
| 19092d2f | 686 | |
| 5dd2cd82 | 687 | fb_invoke(FB_USER_OP_POST_USER_DISCONNECT, array('account' => $account), NULL, 'fb_user'); |
| 86e52b97 | 688 | } |
| 3cf45120 | 689 | } |
| 19092d2f | 690 | |
| 3cf45120 DC |
691 | /** |
| 692 | * Implements hook_user_delete. | |
| 693 | * | |
| 694 | * User is about to be deleted. | |
| 695 | */ | |
| 696 | function fb_user_user_delete($account) { | |
| 697 | $num_deleted = db_delete('fb_user') | |
| 698 | ->condition('uid', $account->uid) | |
| 699 | ->execute(); | |
| 700 | } | |
| 19092d2f | 701 | |
| 3cf45120 DC |
702 | /** |
| 703 | * Implements hook_user_logout. | |
| 704 | * | |
| 705 | * User has logged out. | |
| 706 | */ | |
| 707 | function fb_user_user_logout($account) { | |
| 708 | global $user, $_fb_app; | |
| 19092d2f | 709 | |
| 3cf45120 DC |
710 | if (fb_facebook_user() && |
| 711 | fb_api_check_session($GLOBALS['_fb'])) { | |
| 712 | // Log out of facebook, as well as Drupal. Note that code in | |
| 713 | // fb_connect.js and fb_canvas.js attempts to call FB.logout. However, | |
| 714 | // that code is not reached if the user types "/logout" directly into | |
| 715 | // the browser URL. Also, a sometimes-occuring bug in firefox prevents | |
| 716 | // FB.logout from always succeeding. | |
| 717 | ||
| 718 | // Figure out where to send the user. | |
| 719 | if (isset($_REQUEST['destination'])) { | |
| 720 | $next_url = url($_REQUEST['destination'], array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas())); | |
| 721 | // Unset desination so drupal_goto() below does what we need it to do. | |
| 722 | unset($_REQUEST['destination']); | |
| 723 | } | |
| 724 | else { | |
| 725 | $next_url = url('<front>', array('absolute' => TRUE, 'fb_canvas' => fb_is_canvas())); | |
| 726 | } | |
| 727 | $logout_url = $GLOBALS['_fb']->getLogoutUrl(array( | |
| 728 | 'next' => $next_url, | |
| 729 | 'cancel_url' => $next_url, | |
| 730 | )); | |
| 731 | drupal_goto($logout_url); | |
| 732 | } | |
| 733 | } | |
| 8f184418 | 734 | |
| 3cf45120 DC |
735 | /** |
| 736 | * Implements hook_form_user_profile_form_alter. | |
| 737 | */ | |
| 738 | function fb_user_form_user_profile_form_alter(&$form, &$form_state, $form_id) { | |
| 739 | global $user, $_fb_app; | |
| 8f184418 | 740 | |
| 3cf45120 DC |
741 | if (!user_access('administer users') && |
| 742 | !(user_access('delete own fb_user authmap') && | |
| 743 | $user->uid == $form['#user']->uid)) | |
| 744 | return; // hide from this user | |
| 19092d2f | 745 | |
| 3cf45120 | 746 | $fb_user_data = _fb_user_get_config($_fb_app); |
| fdc2c009 DC |
747 | $account = $form['#user']; |
| 748 | $fbu = _fb_user_get_fbu($account->uid); | |
| 3cf45120 DC |
749 | |
| 750 | if ($fbu) { | |
| fdc2c009 DC |
751 | // The drupal user is a facebook user. |
| 752 | $form['fb_user'] = array( | |
| 3cf45120 | 753 | '#type' => 'fieldset', |
| fdc2c009 | 754 | '#title' => t('Facebook Application ' . $_fb_app->title), |
| 3cf45120 DC |
755 | '#collapsed' => false, |
| 756 | '#collapsible' => false, | |
| 757 | ); | |
| fdc2c009 | 758 | $form['fb_user']['map'] = array( |
| 3cf45120 | 759 | '#type' => 'checkbox', |
| fdc2c009 | 760 | '#title' => t('Connect to facebook.com'), |
| 3cf45120 DC |
761 | '#default_value' => $fbu, |
| 762 | '#return_value' => $fbu, | |
| 763 | '#description' => '', | |
| 764 | ); | |
| fdc2c009 DC |
765 | // Now, learn more from facebook. |
| 766 | try { | |
| 767 | $data = fb_api($fbu, array('access_token' => fb_get_token())); | |
| 768 | if (count($data)) { | |
| 769 | $form['fb_user']['map']['#description'] .= | |
| 770 | t('Local account !username corresponds to !profile_page on Facebook.com.', | |
| 771 | array( | |
| 772 | '!username' => l($account->name, 'user/' . $account->uid), | |
| 773 | '!profile_page' => l($data['name'], $data['link']))); | |
| 86e52b97 | 774 | } |
| fdc2c009 DC |
775 | } |
| 776 | catch (Exception $e) { | |
| 777 | fb_log_exception($e, t('Failed to get user data from facebook.')); | |
| 778 | } | |
| 3cf45120 | 779 | |
| fdc2c009 DC |
780 | if (fb_facebook_user() == $fbu) { |
| 781 | // The user is currently connected to facebook. Depending on | |
| 782 | // config, they may not be able to break the connection. | |
| 783 | $form['fb_user']['map']['#disabled'] = TRUE; | |
| 784 | $form['fb_user']['map']['#description'] .= '<br/>' . t('(Checkbox disabled because you are currently connected to facebook.)'); | |
| 785 | } | |
| 786 | else { | |
| 787 | $form['fb_user']['map']['#description'] .= '<br/>' . t('Uncheck then click save to delete this connection.'); | |
| 3cf45120 DC |
788 | } |
| 789 | } | |
| 790 | ||
| 791 | if (!$fbu) { // this tells us that a mapping hasn't been created | |
| fdc2c009 | 792 | if ($user->uid == $account->uid) { |
| 3cf45120 | 793 | // Could not obtain the $fbu from an existing map. |
| 3cf45120 DC |
794 | $fbu = fb_facebook_user(); |
| 795 | ||
| 796 | if ($fbu) { // they are connected to facebook; give option to map | |
| 797 | ||
| fdc2c009 | 798 | $form['fb_user'] = array( |
| 3cf45120 | 799 | '#type' => 'fieldset', |
| fdc2c009 | 800 | '#title' => t('Facebook Application ' . $_fb_app->title), |
| 3cf45120 DC |
801 | '#collapsed' => false, |
| 802 | '#collapsible' => false, | |
| 803 | ); | |
| 804 | ||
| fdc2c009 | 805 | $form['fb_user']['map'] = array( |
| 3cf45120 DC |
806 | '#type' => 'checkbox', |
| 807 | '#title' => t('Connect account to facebook.com'), | |
| 808 | '#default_value' => 0, | |
| 809 | '#return_value' => $fbu, | |
| 810 | '#description' => '', | |
| 811 | ); | |
| fdc2c009 | 812 | $form['fb_user']['message'] = array( |
| ae674940 DC |
813 | '#markup' => t('If checked, link local account (!username) to facebook.com account (!fb_name).', array( |
| 814 | '!username' => theme('username', array('account' => $form['#user'])), | |
| 815 | '!fb_name' => "<fb:name uid=$fbu useyou=false></fb:name>", | |
| 816 | )), | |
| d143d368 DC |
817 | '#prefix' => "\n<p>", |
| 818 | '#suffix' => "</p>\n", | |
| 819 | ); | |
| 3cf45120 DC |
820 | |
| 821 | } | |
| fdc2c009 | 822 | elseif (!$fbu && $_fb_app) { |
| 3cf45120 | 823 | // they are not connected to facebook; give option to connect here |
| 3cf45120 | 824 | $form['fb_user'] = array( |
| fdc2c009 DC |
825 | '#type' => 'fieldset', |
| 826 | '#title' => t('Facebook Application ' . $_fb_app->title), | |
| 827 | '#collapsed' => false, | |
| 828 | '#collapsible' => false, | |
| 829 | ); | |
| 830 | ||
| 6982d7f1 | 831 | $fb_button = theme('fb_login_button', array('text' => t('Connect with Facebook'))); |
| fdc2c009 | 832 | $form['fb_user']['button'] = array( |
| 3cf45120 DC |
833 | '#markup' => $fb_button, |
| 834 | '#weight' => -1, | |
| 835 | '#prefix' => "\n<p>", | |
| 836 | '#suffix' => "</p>\n", | |
| 837 | ); | |
| 86e52b97 | 838 | } |
| 72dcacc7 DC |
839 | } |
| 840 | else { | |
| fdc2c009 | 841 | $form['fb_user'] = array( |
| 3cf45120 | 842 | '#type' => 'fieldset', |
| fdc2c009 | 843 | '#title' => t('Facebook Application ' . $_fb_app->title), |
| 3cf45120 DC |
844 | '#collapsed' => false, |
| 845 | '#collapsible' => false, | |
| 846 | ); | |
| 847 | ||
| fdc2c009 | 848 | $form['fb_user']['message'] = array( |
| 3cf45120 DC |
849 | '#markup' => t('Local account !username is not connected to facebook.com.', |
| 850 | array('!username' => theme('username', array('account' => $form['#user'])), | |
| 851 | )), | |
| 852 | '#prefix' => "\n<p>", | |
| 853 | '#suffix' => "</p>\n", | |
| 72dcacc7 DC |
854 | ); |
| 855 | } | |
| 86e52b97 | 856 | } |
| 3cf45120 DC |
857 | |
| 858 | if (isset($form)) { | |
| fdc2c009 | 859 | $form['fb_user']['map']['#tree'] = TRUE; |
| 4847eea8 | 860 | } |
| 3cf45120 DC |
861 | else { |
| 862 | // Could add a facebook connect button or canvas page authorization link. | |
| 863 | $form['description'] = array( | |
| 864 | '#markup' => t('This account is not associated with a Facebook Application.'), | |
| 865 | '#prefix' => '<p>', | |
| 866 | '#suffix' => '</p>', | |
| 867 | ); | |
| 72dcacc7 | 868 | } |
| 3cf45120 DC |
869 | |
| 870 | // On user/edit, hide proxied email | |
| 871 | if (isset($form['account']) && isset($form['account']['mail'])) { | |
| 872 | $account = $form['#user']; | |
| 873 | if (isset($account->fb_user_proxied_mail) && | |
| 874 | ($form['account']['mail']['#default_value'] == $account->fb_user_proxied_mail)) { | |
| 875 | unset($form['account']['mail']['#default_value']); | |
| 1026ebc4 DC |
876 | } |
| 877 | } | |
| 3cf45120 DC |
878 | |
| 879 | return $form; | |
| 86e52b97 DC |
880 | } |
| 881 | ||
| 86e52b97 DC |
882 | |
| 883 | /** | |
| d143d368 | 884 | * Helper function to add or update a row in the fb_user table, which maps local uid to facebook ids. |
| bad80793 | 885 | */ |
| d143d368 | 886 | function _fb_user_set_map($account, $fbu) { |
| 3cf45120 | 887 | if ($fbu && $account->uid != 0) { |
| 19092d2f | 888 | |
| fdc2c009 DC |
889 | // Delete any pre-existing mapping that might exist for this local uid or fbu. |
| 890 | db_query("DELETE FROM {fb_user} WHERE uid=:uid OR fbu=:fbu", array( | |
| 891 | ':uid' => $account->uid, | |
| 892 | ':fbu' => $fbu, | |
| 893 | )); | |
| 894 | ||
| 895 | // Create the new mapping. | |
| 896 | db_query("INSERT INTO {fb_user} (uid, fbu) VALUES (:uid, :fbu)", array( | |
| 897 | ':uid' => $account->uid, | |
| 898 | ':fbu' => $fbu, | |
| 899 | )); | |
| 19092d2f | 900 | |
| bad80793 | 901 | if (fb_verbose()) { |
| d143d368 | 902 | watchdog('fb_user', 'Using fb_user to associate user !user with facebook user id %fbu.', |
| bad80793 DC |
903 | array('!user' => l($account->name, 'user/' . $account->uid), |
| 904 | '%fbu' => $fbu, | |
| 905 | )); | |
| 906 | } | |
| 907 | } | |
| 908 | } | |
| 909 | ||
| 910 | ||
| 911 | /** | |
| 86e52b97 | 912 | * Creates a local Drupal account for the specified facebook user id. |
| 19092d2f | 913 | * |
| 86e52b97 DC |
914 | * @param fbu |
| 915 | * The facebook user id corresponding to this account. | |
| 19092d2f | 916 | * |
| fdd0a700 | 917 | * @param edit |
| 3cf45120 | 918 | * An associative array with user configuration. As would be passed to user_save(). |
| 86e52b97 | 919 | */ |
| c38a8224 | 920 | function fb_user_create_local_user($fb, $fb_app, $fbu, |
| fdd0a700 | 921 | $edit = array()) { |
| 19092d2f | 922 | |
| aa2f15ae DC |
923 | // Ensure $fbu is a real facebook user id. |
| 924 | if (!$fbu || !is_numeric($fbu)) { | |
| 925 | return; | |
| 926 | } | |
| 19092d2f | 927 | |
| d143d368 | 928 | $account = fb_user_get_local_user($fbu); |
| 19092d2f | 929 | |
| 186f033e DC |
930 | if (!$account) { |
| 931 | // Create a new user in our system | |
| 19092d2f | 932 | |
| fdd0a700 | 933 | // Learn some details from facebook. |
| 3907c6ee | 934 | $infos = fb_users_getInfo(array($fbu), $fb); |
| fdd0a700 | 935 | $info = $infos[0]; |
| 19092d2f | 936 | |
| fdd0a700 DC |
937 | // All Drupal users get authenticated user role. |
| 938 | $edit['roles'][DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; | |
| 19092d2f | 939 | |
| fdd0a700 DC |
940 | if (isset($edit['name']) && $edit['name']) { |
| 941 | $username = $edit['name']; | |
| 942 | } | |
| 943 | else { | |
| fa9071e7 | 944 | // Fallback, should never be reached. |
| fdd0a700 DC |
945 | $username = "$fbu@facebook"; |
| 946 | $edit['name'] = $username; | |
| 947 | } | |
| 3cf45120 DC |
948 | $i = 0; |
| 949 | ||
| 950 | // Keep looking until we find a username_n that isn't being used. | |
| 951 | while (db_query("SELECT 1 FROM {users} WHERE name = :name", array(':name' => $edit['name']))->fetchField(0)) { | |
| fdd0a700 DC |
952 | $i++; |
| 953 | $edit['name'] = $username . '_' . $i; | |
| 954 | } | |
| 303d3e67 DC |
955 | |
| 956 | // Give modules a way to suppress new account creation. | |
| 957 | $edit['fb_user_do_create'] = TRUE; | |
| 19092d2f | 958 | |
| fdd0a700 | 959 | // Allow third-party module to adjust any of our data before we create |
| 86e52b97 | 960 | // the user. |
| 78672778 DC |
961 | $edit = fb_invoke(FB_USER_OP_PRE_USER, array( |
| 962 | 'fbu' => $fbu, | |
| 963 | 'fb' => $GLOBALS['_fb'], | |
| 964 | 'fb_app' => $fb_app, | |
| 965 | 'info' => $info, | |
| 966 | ), $edit, 'fb_user'); | |
| 303d3e67 DC |
967 | |
| 968 | if ($edit['fb_user_do_create']) { | |
| 969 | unset($edit['fb_user_do_create']); // Don't confuse user_save. | |
| 19092d2f | 970 | |
| 303d3e67 DC |
971 | // Fill in any default that are missing. |
| 972 | $defaults = array( | |
| 973 | 'pass' => user_password(), | |
| 3cf45120 | 974 | 'init' => $fbu . '@facebook', // Supposed to be email, but we may not know it. |
| 303d3e67 | 975 | 'status' => 1, |
| 303d3e67 | 976 | ); |
| 475821b8 DC |
977 | |
| 978 | // Mail available only if user has granted email extended permission. | |
| 979 | if (isset($info['email'])) { | |
| 303d3e67 DC |
980 | $defaults['mail'] = $info['email']; |
| 981 | } | |
| 19092d2f | 982 | |
| 303d3e67 DC |
983 | // Merge defaults |
| 984 | $edit = array_merge($defaults, $edit); | |
| 19092d2f | 985 | |
| 3cf45120 DC |
986 | // Confirm username is not taken. FB_USER_OP_PRE_USER may have changed it. |
| 987 | if ($uid = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' => $edit['name']))->fetchField(0)) { | |
| 303d3e67 | 988 | // The desired name is taken. |
| 3cf45120 | 989 | watchdog('fb_user', 'Failed to create new user %name. That name is already in the users table.', |
| fa9071e7 | 990 | array('%name' => $edit['name']), |
| 303d3e67 DC |
991 | WATCHDOG_ERROR, l(t('view user'), 'user/' . $uid)); |
| 992 | } | |
| 993 | else { | |
| 994 | $account = user_save('', $edit); | |
| 19092d2f | 995 | |
| d143d368 | 996 | _fb_user_set_map($account, $fbu); |
| 19092d2f DC |
997 | |
| 998 | watchdog('fb_user', 'New user: %name %email.', | |
| 3cf45120 DC |
999 | array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), |
| 1000 | WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit')); | |
| 19092d2f | 1001 | |
| 303d3e67 | 1002 | // Allow third-party modules to act after account creation. |
| 78672778 DC |
1003 | fb_invoke(FB_USER_OP_POST_USER, array( |
| 1004 | 'account' => $account, | |
| 1005 | 'fb_app' => $fb_app, | |
| 1006 | 'fb' => $fb, | |
| 1007 | ), NULL, 'fb_user'); | |
| 303d3e67 | 1008 | } |
| aa2f15ae | 1009 | } |
| 186f033e | 1010 | } |
| 186f033e DC |
1011 | return $account; |
| 1012 | } | |
| 1013 | ||
| 86e52b97 DC |
1014 | /** |
| 1015 | * Given an app and facebook user id, return the corresponding local user. | |
| f54b3a2b | 1016 | * |
| d143d368 DC |
1017 | * @param $fbu |
| 1018 | * User's id on facebook.com | |
| 1019 | * | |
| 1020 | * @param $fb_app | |
| 3cf45120 | 1021 | * Historically, this method took the app details into account when mapping user ids. Presently, this parameter is not used. |
| 86e52b97 | 1022 | */ |
| d143d368 | 1023 | function fb_user_get_local_user($fbu, $fb_app = NULL) { |
| 346e5a9b DC |
1024 | if ($uid = _fb_user_get_uid($fbu, $fb_app)) { |
| 1025 | return user_load($uid); | |
| 1026 | } | |
| 1027 | } | |
| 1028 | ||
| 3cf45120 | 1029 | // TODO $fb_app is holdover and can be removed in the future |
| 346e5a9b | 1030 | function _fb_user_get_uid($fbu, $fb_app = NULL) { |
| 3cf45120 DC |
1031 | $result = db_query("SELECT uid FROM {fb_user} WHERE fbu = :fbu", array( |
| 1032 | ':fbu' => $fbu, | |
| 1033 | ))->fetchObject(); | |
| 1034 | ||
| 1035 | if (is_object($result)) { | |
| 1036 | return $result->uid; | |
| 1037 | } | |
| 1038 | else { | |
| 1039 | return 0; | |
| 1040 | } | |
| 186f033e DC |
1041 | } |
| 1042 | ||
| 7e1a7e15 | 1043 | /** |
| 040ff71e DC |
1044 | * Try to determine the local user account by the email address. |
| 1045 | */ | |
| 1046 | function fb_user_get_local_user_by_email($fbu) { | |
| 1047 | global $_fb; | |
| 1048 | if (isset($_fb) && $fbu) { | |
| 43256e0a DC |
1049 | try { |
| 1050 | $info = $_fb->api($fbu); | |
| 1051 | if (isset($info['email']) && | |
| 1052 | ($email = $info['email'])) { | |
| 6135c77d | 1053 | return user_load_by_mail($email); |
| 43256e0a DC |
1054 | } |
| 1055 | } | |
| 1056 | catch (Exception $e) { | |
| 1057 | // This can occur when user logs out of facebook in another window, then returns to our site. | |
| 1058 | if (fb_verbose()) { | |
| 1059 | fb_log_exception($e, t('Failed to get facebook user email.')); | |
| 1060 | } | |
| 040ff71e DC |
1061 | } |
| 1062 | } | |
| 1063 | } | |
| 1064 | ||
| 1065 | /** | |
| 7e1a7e15 | 1066 | * Returns local uids of friends of a given user. |
| 19092d2f | 1067 | * |
| 3cf45120 | 1068 | * Query is relatively efficient for the current user of a canvas page. For |
| 7e1a7e15 | 1069 | * all other users, and non-canvas pages it requires expensive call to |
| 3cf45120 | 1070 | * facebook. That said, our local database query may be inefficient for users |
| 7e1a7e15 | 1071 | * with large numbers of friends, so use with caution. |
| 19092d2f | 1072 | * |
| 7e1a7e15 | 1073 | * TODO: should this function cache results? |
| 19092d2f | 1074 | * |
| 7e1a7e15 DC |
1075 | * Note: the api takes fbu as a parameter, but this usually causes problems |
| 1076 | * because facebook restricts users to query only about their own friends. | |
| 1077 | * For the time being, expect this function to work only on canvas pages to | |
| 1078 | * find friends of the current user. | |
| f54b3a2b DC |
1079 | * |
| 1080 | * Only works if the "map accounts" feature is enabled. | |
| 7e1a7e15 DC |
1081 | */ |
| 1082 | function fb_user_get_local_friends($fbu = NULL, $fb_app = NULL) { | |
| 1083 | if (!isset($fbu)) { | |
| 1084 | $fbu = fb_facebook_user(); | |
| 1085 | } | |
| 1086 | $uids = array(); | |
| 1087 | if ($fbus = fb_get_friends($fbu, $fb_app)) { | |
| 3cf45120 DC |
1088 | $result = db_select('fb_user', 'fb') |
| 1089 | ->fields('fb', array('uid')) | |
| 1090 | ->condition('fb.fbu', $fbus, 'IN') | |
| 1091 | ->execute(); | |
| 19092d2f | 1092 | |
| 3cf45120 | 1093 | foreach ($result as $data) { |
| 6737bc61 DC |
1094 | if ($data->uid) { |
| 1095 | $uids[] = $data->uid; | |
| 1096 | } | |
| 7e1a7e15 DC |
1097 | } |
| 1098 | } | |
| 1099 | return $uids; | |
| 1100 | } | |
| 1101 | ||
| 3a512626 DC |
1102 | |
| 1103 | /** | |
| 3cf45120 | 1104 | * Given a local user id, find the facebook id. This is for internal use. |
| 3a512626 | 1105 | * Outside modules use fb_get_fbu(). |
| f54b3a2b | 1106 | * |
| d143d368 | 1107 | * Only works if the "map accounts" feature is enabled, or the account was created by this module. |
| 3a512626 | 1108 | */ |
| d143d368 | 1109 | function _fb_user_get_fbu($uid) { |
| 3cf45120 | 1110 | $cache = &drupal_static(__FUNCTION__); // cache to avoid excess queries. |
| 19092d2f | 1111 | |
| 81e5e23a | 1112 | if (!isset($cache[$uid])) { |
| 3a512626 | 1113 | // Look up this user in the authmap |
| 3cf45120 | 1114 | $result = db_query("SELECT fbu FROM {fb_user} WHERE uid = :uid", array( |
| b389f367 | 1115 | ':uid' => $uid, |
| 3cf45120 | 1116 | ))->fetchObject(); |
| f54b3a2b | 1117 | if ($result) { |
| 3cf45120 | 1118 | $cache[$uid] = $result->fbu; |
| 3a512626 | 1119 | } |
| b389f367 DC |
1120 | else { |
| 1121 | $cache[$uid] = NULL; | |
| 1122 | } | |
| 3a512626 | 1123 | } |
| f54b3a2b | 1124 | |
| b389f367 | 1125 | return $cache[$uid]; |
| 3a512626 DC |
1126 | } |
| 1127 | ||
| d143d368 | 1128 | //// token module hooks. |
| 3a512626 | 1129 | |
| 4c0bff53 DC |
1130 | function fb_user_token_list($type = 'all') { |
| 1131 | if ($type == 'all' || $type == 'fb' || $type == 'fb_app') { | |
| 1132 | $tokens['fb_app']['fb-app-user-fbu'] = t('Current user\'s Facebook ID'); | |
| 1133 | $tokens['fb_app']['fb-app-user-name'] = t('Current user\'s name on Facebook (TODO)'); | |
| 1134 | $tokens['fb_app']['fb-app-user-name-fbml'] = t('Current user\'s name for display on Facebook profile and canvas pages.'); | |
| 1135 | $tokens['fb_app']['fb-app-profile-url'] = t('Current user\'s Facebook profile URL'); | |
| cb150b03 | 1136 | return $tokens; |
| 4c0bff53 | 1137 | } |
| 4c0bff53 DC |
1138 | } |
| 1139 | ||
| 1140 | function fb_user_token_values($type = 'all', $object = NULL) { | |
| 8f2c7e1f | 1141 | $values = array(); |
| 4c0bff53 DC |
1142 | if ($type == 'fb_app' && $object) { |
| 1143 | $fb_app = $object; | |
| 1144 | global $user; | |
| d143d368 | 1145 | $fbu = _fb_user_get_fbu($user->uid); |
| 4c0bff53 DC |
1146 | if ($fbu) { |
| 1147 | $values['fb-app-user-fbu'] = $fbu; | |
| 5d87b01a | 1148 | //$values['fb-app-user-name'] = ''; // @TODO |
| 3cf45120 | 1149 | $values['fb-app-user-name-fbml'] = '<fb:name uid="' . $fbu . '" />'; |
| 19092d2f | 1150 | $values['fb-app-profile-url'] = |
| 3cf45120 | 1151 | 'http://www.facebook.com/profile.php?id=' . $fbu; |
| 4c0bff53 DC |
1152 | } |
| 1153 | } | |
| 1154 | return $values; | |
| 1155 | } | |
| 030e44e8 DC |
1156 | |
| 1157 | /** | |
| e70105e5 DC |
1158 | * Learn the user's proxied email address. If fb_user_app.module is enabled, |
| 1159 | * it will defer to that module, which queries a local database. If not, ask | |
| 1160 | * facebook for the data. | |
| 1161 | * | |
| 1162 | * @TODO: Facebook may no longer provide proxied_email. Does this work? | |
| 030e44e8 DC |
1163 | */ |
| 1164 | function fb_user_get_proxied_email($fbu, $fb_app) { | |
| 1165 | $mail = ""; | |
| 19092d2f | 1166 | |
| 030e44e8 DC |
1167 | if (function_exists("fb_user_app_get_proxied_email")) { |
| 1168 | // Function at fb_user_app module queries fb_use_app table first | |
| 1169 | $mail = fb_user_app_get_proxied_email($fbu, $fb_app); | |
| 1170 | } | |
| 19092d2f | 1171 | |
| 030e44e8 DC |
1172 | if (!$mail) { |
| 1173 | // Ask facebook for info. | |
| 1174 | $fb = fb_api_init($fb_app); | |
| dc41c986 | 1175 | $info = fb_users_getInfo(array($fbu), $fb); // TODO deprecated |
| f6ec9cba | 1176 | $data = $info[0]; |
| e70105e5 DC |
1177 | if (isset($data['email'])) { |
| 1178 | $mail = $data['email']; | |
| 1179 | } | |
| 1180 | elseif (isset($data['proxied_email'])) { | |
| 1181 | $mail = $data['proxied_email']; | |
| 1182 | } | |
| 030e44e8 | 1183 | } |
| 19092d2f | 1184 | |
| 030e44e8 DC |
1185 | return $mail; |
| 1186 | } |