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