| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Enables the user registration and login system. 6 */ 7 8 /** 9 * Maximum length of username text field. 10 */ 11 define('USERNAME_MAX_LENGTH', 60); 12 13 /** 14 * Maximum length of user e-mail text field. 15 */ 16 define('EMAIL_MAX_LENGTH', 254); 17 18 /** 19 * Only administrators can create user accounts. 20 */ 21 define('USER_REGISTER_ADMINISTRATORS_ONLY', 0); 22 23 /** 24 * Visitors can create their own accounts. 25 */ 26 define('USER_REGISTER_VISITORS', 1); 27 28 /** 29 * Visitors can create accounts, but they don't become active without 30 * administrative approval. 31 */ 32 define('USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL', 2); 33 34 /** 35 * Implement hook_help(). 36 */ 37 function user_help($path, $arg) { 38 global $user; 39 40 switch ($path) { 41 case 'admin/help#user': 42 $output = ''; 43 $output .= '<h3>' . t('About') . '</h3>'; 44 $output .= '<p>' . t('The User module allows users to register, log in, and log out. It also allows users with proper permissions to manage user roles (used to classify users) and permissions associated with those roles. For more information, see the online handbook entry for <a href="@user">User module</a>.', array('@user' => 'http://drupal.org/documentation/modules/user')) . '</p>'; 45 $output .= '<h3>' . t('Uses') . '</h3>'; 46 $output .= '<dl>'; 47 $output .= '<dt>' . t('Creating and managing users') . '</dt>'; 48 $output .= '<dd>' . t('The User module allows users with the appropriate <a href="@permissions">permissions</a> to create user accounts through the <a href="@people">People administration page</a>, where they can also assign users to one or more roles, and block or delete user accounts. If allowed, users without accounts (anonymous users) can create their own accounts on the <a href="@register">Create new account</a> page.', array('@permissions' => url('admin/people/permissions', array('fragment' => 'module-user')), '@people' => url('admin/people'), '@register' => url('user/register'))) . '</dd>'; 49 $output .= '<dt>' . t('User roles and permissions') . '</dt>'; 50 $output .= '<dd>' . t('<em>Roles</em> are used to group and classify users; each user can be assigned one or more roles. By default there are two roles: <em>anonymous user</em> (users that are not logged in) and <em>authenticated user</em> (users that are registered and logged in). Depending on choices you made when you installed Drupal, the installation process may have defined more roles, and you can create additional custom roles on the <a href="@roles">Roles page</a>. After creating roles, you can set permissions for each role on the <a href="@permissions_user">Permissions page</a>. Granting a permission allows users who have been assigned a particular role to perform an action on the site, such as viewing a particular type of content, editing or creating content, administering settings for a particular module, or using a particular function of the site (such as search).', array('@permissions_user' => url('admin/people/permissions'), '@roles' => url('admin/people/permissions/roles'))) . '</dd>'; 51 $output .= '<dt>' . t('Account settings') . '</dt>'; 52 $output .= '<dd>' . t('The <a href="@accounts">Account settings page</a> allows you to manage settings for the displayed name of the anonymous user role, personal contact forms, user registration, and account cancellation. On this page you can also manage settings for account personalization (including signatures and user pictures), and adapt the text for the e-mail messages that are sent automatically during the user registration process.', array('@accounts' => url('admin/config/people/accounts'))) . '</dd>'; 53 $output .= '</dl>'; 54 return $output; 55 case 'admin/people/create': 56 return '<p>' . t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") . '</p>'; 57 case 'admin/people/permissions': 58 return '<p>' . t('Permissions let you control what users can do and see on your site. You can define a specific set of permissions for each role. (See the <a href="@role">Roles</a> page to create a role). Two important roles to consider are Authenticated Users and Administrators. Any permissions granted to the Authenticated Users role will be given to any user who can log into your site. You can make any role the Administrator role for the site, meaning this will be granted all new permissions automatically. You can do this on the <a href="@settings">User Settings</a> page. You should be careful to ensure that only trusted users are given this access and level of control of your site.', array('@role' => url('admin/people/permissions/roles'), '@settings' => url('admin/config/people/accounts'))) . '</p>'; 59 case 'admin/people/permissions/roles': 60 $output = '<p>' . t('Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that have certain privileges as defined on the <a href="@permissions">permissions page</a>. Examples of roles include: anonymous user, authenticated user, moderator, administrator and so on. In this area you will define the names and order of the roles on your site. It is recommended to order your roles from least permissive (anonymous user) to most permissive (administrator). To delete a role choose "edit role".', array('@permissions' => url('admin/people/permissions'))) . '</p>'; 61 $output .= '<p>' . t('By default, Drupal comes with two user roles:') . '</p>'; 62 $output .= '<ul>'; 63 $output .= '<li>' . t("Anonymous user: this role is used for users that don't have a user account or that are not authenticated.") . '</li>'; 64 $output .= '<li>' . t('Authenticated user: this role is automatically granted to all logged in users.') . '</li>'; 65 $output .= '</ul>'; 66 return $output; 67 case 'admin/config/people/accounts/fields': 68 return '<p>' . t('This form lets administrators add, edit, and arrange fields for storing user data.') . '</p>'; 69 case 'admin/config/people/accounts/display': 70 return '<p>' . t('This form lets administrators configure how fields should be displayed when rendering a user profile page.') . '</p>'; 71 case 'admin/people/search': 72 return '<p>' . t('Enter a simple pattern ("*" may be used as a wildcard match) to search for a username or e-mail address. For example, one may search for "br" and Drupal might return "brian", "brad", and "brenda@example.com".') . '</p>'; 73 } 74 } 75 76 /** 77 * Invokes a user hook in every module. 78 * 79 * We cannot use module_invoke() for this, because the arguments need to 80 * be passed by reference. 81 * 82 * @param $type 83 * A text string that controls which user hook to invoke. Valid choices are: 84 * - cancel: Invokes hook_user_cancel(). 85 * - insert: Invokes hook_user_insert(). 86 * - login: Invokes hook_user_login(). 87 * - presave: Invokes hook_user_presave(). 88 * - update: Invokes hook_user_update(). 89 * @param $edit 90 * An associative array variable containing form values to be passed 91 * as the first parameter of the hook function. 92 * @param $account 93 * The user account object to be passed as the second parameter of the hook 94 * function. 95 * @param $category 96 * The category of user information being acted upon. 97 */ 98 function user_module_invoke($type, &$edit, $account, $category = NULL) { 99 foreach (module_implements('user_' . $type) as $module) { 100 $function = $module . '_user_' . $type; 101 $function($edit, $account, $category); 102 } 103 } 104 105 /** 106 * Implements hook_theme(). 107 */ 108 function user_theme() { 109 return array( 110 'user_picture' => array( 111 'variables' => array('account' => NULL), 112 'template' => 'user-picture', 113 ), 114 'user_profile' => array( 115 'render element' => 'elements', 116 'template' => 'user-profile', 117 'file' => 'user.pages.inc', 118 ), 119 'user_profile_category' => array( 120 'render element' => 'element', 121 'template' => 'user-profile-category', 122 'file' => 'user.pages.inc', 123 ), 124 'user_profile_item' => array( 125 'render element' => 'element', 126 'template' => 'user-profile-item', 127 'file' => 'user.pages.inc', 128 ), 129 'user_list' => array( 130 'variables' => array('users' => NULL, 'title' => NULL), 131 ), 132 'user_admin_permissions' => array( 133 'render element' => 'form', 134 'file' => 'user.admin.inc', 135 ), 136 'user_admin_roles' => array( 137 'render element' => 'form', 138 'file' => 'user.admin.inc', 139 ), 140 'user_permission_description' => array( 141 'variables' => array('permission_item' => NULL, 'hide' => NULL), 142 'file' => 'user.admin.inc', 143 ), 144 'user_signature' => array( 145 'variables' => array('signature' => NULL), 146 ), 147 ); 148 } 149 150 /** 151 * Implements hook_entity_info(). 152 */ 153 function user_entity_info() { 154 $return = array( 155 'user' => array( 156 'label' => t('User'), 157 'controller class' => 'UserController', 158 'base table' => 'users', 159 'uri callback' => 'user_uri', 160 'label callback' => 'format_username', 161 'fieldable' => TRUE, 162 // $user->language is only the preferred user language for the user 163 // interface textual elements. As it is not necessarily related to the 164 // language assigned to fields, we do not define it as the entity language 165 // key. 166 'entity keys' => array( 167 'id' => 'uid', 168 ), 169 'bundles' => array( 170 'user' => array( 171 'label' => t('User'), 172 'admin' => array( 173 'path' => 'admin/config/people/accounts', 174 'access arguments' => array('administer users'), 175 ), 176 ), 177 ), 178 'view modes' => array( 179 'full' => array( 180 'label' => t('User account'), 181 'custom settings' => FALSE, 182 ), 183 ), 184 ), 185 ); 186 return $return; 187 } 188 189 /** 190 * Entity URI callback. 191 */ 192 function user_uri($user) { 193 return array( 194 'path' => 'user/' . $user->uid, 195 ); 196 } 197 198 /** 199 * Implements hook_field_info_alter(). 200 */ 201 function user_field_info_alter(&$info) { 202 // Add the 'user_register_form' instance setting to all field types. 203 foreach ($info as $field_type => &$field_type_info) { 204 $field_type_info += array('instance_settings' => array()); 205 $field_type_info['instance_settings'] += array( 206 'user_register_form' => FALSE, 207 ); 208 } 209 } 210 211 /** 212 * Implements hook_field_extra_fields(). 213 */ 214 function user_field_extra_fields() { 215 $return['user']['user'] = array( 216 'form' => array( 217 'account' => array( 218 'label' => t('User name and password'), 219 'description' => t('User module account form elements.'), 220 'weight' => -10, 221 ), 222 'timezone' => array( 223 'label' => t('Timezone'), 224 'description' => t('User module timezone form element.'), 225 'weight' => 6, 226 ), 227 ), 228 'display' => array( 229 'summary' => array( 230 'label' => t('History'), 231 'description' => t('User module history view element.'), 232 'weight' => 5, 233 ), 234 ), 235 ); 236 237 return $return; 238 } 239 240 /** 241 * Fetches a user object based on an external authentication source. 242 * 243 * @param string $authname 244 * The external authentication username. 245 * 246 * @return 247 * A fully-loaded user object if the user is found or FALSE if not found. 248 */ 249 function user_external_load($authname) { 250 $uid = db_query("SELECT uid FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchField(); 251 252 if ($uid) { 253 return user_load($uid); 254 } 255 else { 256 return FALSE; 257 } 258 } 259 260 /** 261 * Load multiple users based on certain conditions. 262 * 263 * This function should be used whenever you need to load more than one user 264 * from the database. Users are loaded into memory and will not require 265 * database access if loaded again during the same page request. 266 * 267 * @param $uids 268 * An array of user IDs. 269 * @param $conditions 270 * (deprecated) An associative array of conditions on the {users} 271 * table, where the keys are the database fields and the values are the 272 * values those fields must have. Instead, it is preferable to use 273 * EntityFieldQuery to retrieve a list of entity IDs loadable by 274 * this function. 275 * @param $reset 276 * A boolean indicating that the internal cache should be reset. Use this if 277 * loading a user object which has been altered during the page request. 278 * 279 * @return 280 * An array of user objects, indexed by uid. 281 * 282 * @see entity_load() 283 * @see user_load() 284 * @see user_load_by_mail() 285 * @see user_load_by_name() 286 * @see EntityFieldQuery 287 * 288 * @todo Remove $conditions in Drupal 8. 289 */ 290 function user_load_multiple($uids = array(), $conditions = array(), $reset = FALSE) { 291 return entity_load('user', $uids, $conditions, $reset); 292 } 293 294 /** 295 * Controller class for users. 296 * 297 * This extends the DrupalDefaultEntityController class, adding required 298 * special handling for user objects. 299 */ 300 class UserController extends DrupalDefaultEntityController { 301 302 function attachLoad(&$queried_users, $revision_id = FALSE) { 303 // Build an array of user picture IDs so that these can be fetched later. 304 $picture_fids = array(); 305 foreach ($queried_users as $key => $record) { 306 $picture_fids[] = $record->picture; 307 $queried_users[$key]->data = unserialize($record->data); 308 $queried_users[$key]->roles = array(); 309 if ($record->uid) { 310 $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; 311 } 312 else { 313 $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; 314 } 315 } 316 317 // Add any additional roles from the database. 318 $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users))); 319 foreach ($result as $record) { 320 $queried_users[$record->uid]->roles[$record->rid] = $record->name; 321 } 322 323 // Add the full file objects for user pictures if enabled. 324 if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) { 325 $pictures = file_load_multiple($picture_fids); 326 foreach ($queried_users as $account) { 327 if (!empty($account->picture) && isset($pictures[$account->picture])) { 328 $account->picture = $pictures[$account->picture]; 329 } 330 else { 331 $account->picture = NULL; 332 } 333 } 334 } 335 // Call the default attachLoad() method. This will add fields and call 336 // hook_user_load(). 337 parent::attachLoad($queried_users, $revision_id); 338 } 339 } 340 341 /** 342 * Loads a user object. 343 * 344 * Drupal has a global $user object, which represents the currently-logged-in 345 * user. So to avoid confusion and to avoid clobbering the global $user object, 346 * it is a good idea to assign the result of this function to a different local 347 * variable, generally $account. If you actually do want to act as the user you 348 * are loading, it is essential to call drupal_save_session(FALSE); first. 349 * See 350 * @link http://drupal.org/node/218104 Safely impersonating another user @endlink 351 * for more information. 352 * 353 * @param $uid 354 * Integer specifying the user ID to load. 355 * @param $reset 356 * TRUE to reset the internal cache and load from the database; FALSE 357 * (default) to load from the internal cache, if set. 358 * 359 * @return 360 * A fully-loaded user object upon successful user load, or FALSE if the user 361 * cannot be loaded. 362 * 363 * @see user_load_multiple() 364 */ 365 function user_load($uid, $reset = FALSE) { 366 $users = user_load_multiple(array($uid), array(), $reset); 367 return reset($users); 368 } 369 370 /** 371 * Fetch a user object by email address. 372 * 373 * @param $mail 374 * String with the account's e-mail address. 375 * @return 376 * A fully-loaded $user object upon successful user load or FALSE if user 377 * cannot be loaded. 378 * 379 * @see user_load_multiple() 380 */ 381 function user_load_by_mail($mail) { 382 $users = user_load_multiple(array(), array('mail' => $mail)); 383 return reset($users); 384 } 385 386 /** 387 * Fetch a user object by account name. 388 * 389 * @param $name 390 * String with the account's user name. 391 * @return 392 * A fully-loaded $user object upon successful user load or FALSE if user 393 * cannot be loaded. 394 * 395 * @see user_load_multiple() 396 */ 397 function user_load_by_name($name) { 398 $users = user_load_multiple(array(), array('name' => $name)); 399 return reset($users); 400 } 401 402 /** 403 * Save changes to a user account or add a new user. 404 * 405 * @param $account 406 * (optional) The user object to modify or add. If you want to modify 407 * an existing user account, you will need to ensure that (a) $account 408 * is an object, and (b) you have set $account->uid to the numeric 409 * user ID of the user account you wish to modify. If you 410 * want to create a new user account, you can set $account->is_new to 411 * TRUE or omit the $account->uid field. 412 * @param $edit 413 * An array of fields and values to save. For example array('name' 414 * => 'My name'). Key / value pairs added to the $edit['data'] will be 415 * serialized and saved in the {users.data} column. 416 * @param $category 417 * (optional) The category for storing profile information in. 418 * 419 * @return 420 * A fully-loaded $user object upon successful save or FALSE if the save failed. 421 * 422 * @todo D8: Drop $edit and fix user_save() to be consistent with others. 423 */ 424 function user_save($account, $edit = array(), $category = 'account') { 425 $transaction = db_transaction(); 426 try { 427 if (!empty($edit['pass'])) { 428 // Allow alternate password hashing schemes. 429 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); 430 $edit['pass'] = user_hash_password(trim($edit['pass'])); 431 // Abort if the hashing failed and returned FALSE. 432 if (!$edit['pass']) { 433 return FALSE; 434 } 435 } 436 else { 437 // Avoid overwriting an existing password with a blank password. 438 unset($edit['pass']); 439 } 440 if (isset($edit['mail'])) { 441 $edit['mail'] = trim($edit['mail']); 442 } 443 444 // Load the stored entity, if any. 445 if (!empty($account->uid) && !isset($account->original)) { 446 $account->original = entity_load_unchanged('user', $account->uid); 447 } 448 449 if (empty($account)) { 450 $account = new stdClass(); 451 } 452 if (!isset($account->is_new)) { 453 $account->is_new = empty($account->uid); 454 } 455 // Prepopulate $edit['data'] with the current value of $account->data. 456 // Modules can add to or remove from this array in hook_user_presave(). 457 if (!empty($account->data)) { 458 $edit['data'] = !empty($edit['data']) ? array_merge($account->data, $edit['data']) : $account->data; 459 } 460 461 // Invoke hook_user_presave() for all modules. 462 user_module_invoke('presave', $edit, $account, $category); 463 464 // Invoke presave operations of Field Attach API and Entity API. Those APIs 465 // require a fully-fledged and updated entity object. Therefore, we need to 466 // copy any new property values of $edit into it. 467 foreach ($edit as $key => $value) { 468 $account->$key = $value; 469 } 470 field_attach_presave('user', $account); 471 module_invoke_all('entity_presave', $account, 'user'); 472 473 if (is_object($account) && !$account->is_new) { 474 // Process picture uploads. 475 if (!empty($account->picture->fid) && (!isset($account->original->picture->fid) || $account->picture->fid != $account->original->picture->fid)) { 476 $picture = $account->picture; 477 // If the picture is a temporary file move it to its final location and 478 // make it permanent. 479 if (!$picture->status) { 480 $info = image_get_info($picture->uri); 481 $picture_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures'); 482 483 // Prepare the pictures directory. 484 file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY); 485 $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '-' . REQUEST_TIME . '.' . $info['extension']); 486 487 // Move the temporary file into the final location. 488 if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) { 489 $picture->status = FILE_STATUS_PERMANENT; 490 $account->picture = file_save($picture); 491 file_usage_add($picture, 'user', 'user', $account->uid); 492 } 493 } 494 // Delete the previous picture if it was deleted or replaced. 495 if (!empty($account->original->picture->fid)) { 496 file_usage_delete($account->original->picture, 'user', 'user', $account->uid); 497 file_delete($account->original->picture); 498 } 499 } 500 elseif (isset($edit['picture_delete']) && $edit['picture_delete']) { 501 file_usage_delete($account->original->picture, 'user', 'user', $account->uid); 502 file_delete($account->original->picture); 503 } 504 $account->picture = empty($account->picture->fid) ? 0 : $account->picture->fid; 505 506 // Do not allow 'uid' to be changed. 507 $account->uid = $account->original->uid; 508 // Save changes to the user table. 509 $success = drupal_write_record('users', $account, 'uid'); 510 if ($success === FALSE) { 511 // The query failed - better to abort the save than risk further 512 // data loss. 513 return FALSE; 514 } 515 516 // Reload user roles if provided. 517 if ($account->roles != $account->original->roles) { 518 db_delete('users_roles') 519 ->condition('uid', $account->uid) 520 ->execute(); 521 522 $query = db_insert('users_roles')->fields(array('uid', 'rid')); 523 foreach (array_keys($account->roles) as $rid) { 524 if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) { 525 $query->values(array( 526 'uid' => $account->uid, 527 'rid' => $rid, 528 )); 529 } 530 } 531 $query->execute(); 532 } 533 534 // Delete a blocked user's sessions to kick them if they are online. 535 if ($account->original->status != $account->status && $account->status == 0) { 536 drupal_session_destroy_uid($account->uid); 537 } 538 539 // If the password changed, delete all open sessions and recreate 540 // the current one. 541 if ($account->pass != $account->original->pass) { 542 drupal_session_destroy_uid($account->uid); 543 if ($account->uid == $GLOBALS['user']->uid) { 544 drupal_session_regenerate(); 545 } 546 } 547 548 // Save Field data. 549 field_attach_update('user', $account); 550 551 // Send emails after we have the new user object. 552 if ($account->status != $account->original->status) { 553 // The user's status is changing; conditionally send notification email. 554 $op = $account->status == 1 ? 'status_activated' : 'status_blocked'; 555 _user_mail_notify($op, $account); 556 } 557 558 // Update $edit with any interim changes to $account. 559 foreach ($account as $key => $value) { 560 if (!property_exists($account->original, $key) || $value !== $account->original->$key) { 561 $edit[$key] = $value; 562 } 563 } 564 user_module_invoke('update', $edit, $account, $category); 565 module_invoke_all('entity_update', $account, 'user'); 566 } 567 else { 568 // Allow 'uid' to be set by the caller. There is no danger of writing an 569 // existing user as drupal_write_record will do an INSERT. 570 if (empty($account->uid)) { 571 $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField()); 572 } 573 // Allow 'created' to be set by the caller. 574 if (!isset($account->created)) { 575 $account->created = REQUEST_TIME; 576 } 577 $success = drupal_write_record('users', $account); 578 if ($success === FALSE) { 579 // On a failed INSERT some other existing user's uid may be returned. 580 // We must abort to avoid overwriting their account. 581 return FALSE; 582 } 583 584 // Make sure $account is properly initialized. 585 $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; 586 587 field_attach_insert('user', $account); 588 $edit = (array) $account; 589 user_module_invoke('insert', $edit, $account, $category); 590 module_invoke_all('entity_insert', $account, 'user'); 591 592 // Save user roles. 593 if (count($account->roles) > 1) { 594 $query = db_insert('users_roles')->fields(array('uid', 'rid')); 595 foreach (array_keys($account->roles) as $rid) { 596 if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) { 597 $query->values(array( 598 'uid' => $account->uid, 599 'rid' => $rid, 600 )); 601 } 602 } 603 $query->execute(); 604 } 605 } 606 // Clear internal properties. 607 unset($account->is_new); 608 unset($account->original); 609 // Clear the static loading cache. 610 entity_get_controller('user')->resetCache(array($account->uid)); 611 612 return $account; 613 } 614 catch (Exception $e) { 615 $transaction->rollback(); 616 watchdog_exception('user', $e); 617 throw $e; 618 } 619 } 620 621 /** 622 * Verify the syntax of the given name. 623 */ 624 function user_validate_name($name) { 625 if (!$name) { 626 return t('You must enter a username.'); 627 } 628 if (substr($name, 0, 1) == ' ') { 629 return t('The username cannot begin with a space.'); 630 } 631 if (substr($name, -1) == ' ') { 632 return t('The username cannot end with a space.'); 633 } 634 if (strpos($name, ' ') !== FALSE) { 635 return t('The username cannot contain multiple spaces in a row.'); 636 } 637 if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) { 638 return t('The username contains an illegal character.'); 639 } 640 if (preg_match('/[\x{80}-\x{A0}' . // Non-printable ISO-8859-1 + NBSP 641 '\x{AD}' . // Soft-hyphen 642 '\x{2000}-\x{200F}' . // Various space characters 643 '\x{2028}-\x{202F}' . // Bidirectional text overrides 644 '\x{205F}-\x{206F}' . // Various text hinting characters 645 '\x{FEFF}' . // Byte order mark 646 '\x{FF01}-\x{FF60}' . // Full-width latin 647 '\x{FFF9}-\x{FFFD}' . // Replacement characters 648 '\x{0}-\x{1F}]/u', // NULL byte and control characters 649 $name)) { 650 return t('The username contains an illegal character.'); 651 } 652 if (drupal_strlen($name) > USERNAME_MAX_LENGTH) { 653 return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH)); 654 } 655 } 656 657 /** 658 * Validates a user's email address. 659 * 660 * Checks that a user's email address exists and follows all standard 661 * validation rules. Returns error messages when the address is invalid. 662 * 663 * @param $mail 664 * A user's email address. 665 * 666 * @return 667 * If the address is invalid, a human-readable error message is returned. 668 * If the address is valid, nothing is returned. 669 */ 670 function user_validate_mail($mail) { 671 if (!$mail) { 672 return t('You must enter an e-mail address.'); 673 } 674 if (!valid_email_address($mail)) { 675 return t('The e-mail address %mail is not valid.', array('%mail' => $mail)); 676 } 677 } 678 679 /** 680 * Validates an image uploaded by a user. 681 * 682 * @see user_account_form() 683 */ 684 function user_validate_picture(&$form, &$form_state) { 685 // If required, validate the uploaded picture. 686 $validators = array( 687 'file_validate_is_image' => array(), 688 'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')), 689 'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024), 690 ); 691 692 // Save the file as a temporary file. 693 $file = file_save_upload('picture_upload', $validators); 694 if ($file === FALSE) { 695 form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures')))); 696 } 697 elseif ($file !== NULL) { 698 $form_state['values']['picture_upload'] = $file; 699 } 700 } 701 702 /** 703 * Generate a random alphanumeric password. 704 */ 705 function user_password($length = 10) { 706 // This variable contains the list of allowable characters for the 707 // password. Note that the number 0 and the letter 'O' have been 708 // removed to avoid confusion between the two. The same is true 709 // of 'I', 1, and 'l'. 710 $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'; 711 712 // Zero-based count of characters in the allowable list: 713 $len = strlen($allowable_characters) - 1; 714 715 // Declare the password as a blank string. 716 $pass = ''; 717 718 // Loop the number of times specified by $length. 719 for ($i = 0; $i < $length; $i++) { 720 721 // Each iteration, pick a random character from the 722 // allowable string and append it to the password: 723 $pass .= $allowable_characters[mt_rand(0, $len)]; 724 } 725 726 return $pass; 727 } 728 729 /** 730 * Determine the permissions for one or more roles. 731 * 732 * @param $roles 733 * An array whose keys are the role IDs of interest, such as $user->roles. 734 * 735 * @return 736 * An array indexed by role ID. Each value is an array whose keys are the 737 * permission strings for the given role ID. 738 */ 739 function user_role_permissions($roles = array()) { 740 $cache = &drupal_static(__FUNCTION__, array()); 741 742 $role_permissions = $fetch = array(); 743 744 if ($roles) { 745 foreach ($roles as $rid => $name) { 746 if (isset($cache[$rid])) { 747 $role_permissions[$rid] = $cache[$rid]; 748 } 749 else { 750 // Add this rid to the list of those needing to be fetched. 751 $fetch[] = $rid; 752 // Prepare in case no permissions are returned. 753 $cache[$rid] = array(); 754 } 755 } 756 757 if ($fetch) { 758 // Get from the database permissions that were not in the static variable. 759 // Only role IDs with at least one permission assigned will return rows. 760 $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch)); 761 762 foreach ($result as $row) { 763 $cache[$row->rid][$row->permission] = TRUE; 764 } 765 foreach ($fetch as $rid) { 766 // For every rid, we know we at least assigned an empty array. 767 $role_permissions[$rid] = $cache[$rid]; 768 } 769 } 770 } 771 772 return $role_permissions; 773 } 774 775 /** 776 * Determine whether the user has a given privilege. 777 * 778 * @param $string 779 * The permission, such as "administer nodes", being checked for. 780 * @param $account 781 * (optional) The account to check, if not given use currently logged in user. 782 * 783 * @return 784 * Boolean TRUE if the current user has the requested permission. 785 * 786 * All permission checks in Drupal should go through this function. This 787 * way, we guarantee consistent behavior, and ensure that the superuser 788 * can perform all actions. 789 */ 790 function user_access($string, $account = NULL) { 791 global $user; 792 793 if (!isset($account)) { 794 $account = $user; 795 } 796 797 // User #1 has all privileges: 798 if ($account->uid == 1) { 799 return TRUE; 800 } 801 802 // To reduce the number of SQL queries, we cache the user's permissions 803 // in a static variable. 804 // Use the advanced drupal_static() pattern, since this is called very often. 805 static $drupal_static_fast; 806 if (!isset($drupal_static_fast)) { 807 $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__); 808 } 809 $perm = &$drupal_static_fast['perm']; 810 if (!isset($perm[$account->uid])) { 811 $role_permissions = user_role_permissions($account->roles); 812 813 $perms = array(); 814 foreach ($role_permissions as $one_role) { 815 $perms += $one_role; 816 } 817 $perm[$account->uid] = $perms; 818 } 819 820 return isset($perm[$account->uid][$string]); 821 } 822 823 /** 824 * Checks for usernames blocked by user administration. 825 * 826 * @param $name 827 * A string containing a name of the user. 828 * 829 * @return 830 * Object with property 'name' (the user name), if the user is blocked; 831 * FALSE if the user is not blocked. 832 */ 833 function user_is_blocked($name) { 834 return db_select('users') 835 ->fields('users', array('name')) 836 ->condition('name', db_like($name), 'LIKE') 837 ->condition('status', 0) 838 ->execute()->fetchObject(); 839 } 840 841 /** 842 * Implements hook_permission(). 843 */ 844 function user_permission() { 845 return array( 846 'administer permissions' => array( 847 'title' => t('Administer permissions'), 848 'restrict access' => TRUE, 849 ), 850 'administer users' => array( 851 'title' => t('Administer users'), 852 'restrict access' => TRUE, 853 ), 854 'access user profiles' => array( 855 'title' => t('View user profiles'), 856 ), 857 'change own username' => array( 858 'title' => t('Change own username'), 859 ), 860 'cancel account' => array( 861 'title' => t('Cancel own user account'), 862 'description' => t('Note: content may be kept, unpublished, deleted or transferred to the %anonymous-name user depending on the configured <a href="@user-settings-url">user settings</a>.', array('%anonymous-name' => variable_get('anonymous', t('Anonymous')), '@user-settings-url' => url('admin/config/people/accounts'))), 863 ), 864 'select account cancellation method' => array( 865 'title' => t('Select method for cancelling own account'), 866 'restrict access' => TRUE, 867 ), 868 ); 869 } 870 871 /** 872 * Implements hook_file_download(). 873 * 874 * Ensure that user pictures (avatars) are always downloadable. 875 */ 876 function user_file_download($uri) { 877 if (strpos(file_uri_target($uri), variable_get('user_picture_path', 'pictures') . '/picture-') === 0) { 878 $info = image_get_info($uri); 879 return array('Content-Type' => $info['mime_type']); 880 } 881 } 882 883 /** 884 * Implements hook_file_move(). 885 */ 886 function user_file_move($file, $source) { 887 // If a user's picture is replaced with a new one, update the record in 888 // the users table. 889 if (isset($file->fid) && isset($source->fid) && $file->fid != $source->fid) { 890 db_update('users') 891 ->fields(array( 892 'picture' => $file->fid, 893 )) 894 ->condition('picture', $source->fid) 895 ->execute(); 896 } 897 } 898 899 /** 900 * Implements hook_file_delete(). 901 */ 902 function user_file_delete($file) { 903 // Remove any references to the file. 904 db_update('users') 905 ->fields(array('picture' => 0)) 906 ->condition('picture', $file->fid) 907 ->execute(); 908 } 909 910 /** 911 * Implements hook_search_info(). 912 */ 913 function user_search_info() { 914 return array( 915 'title' => 'Users', 916 ); 917 } 918 919 /** 920 * Implements hook_search_access(). 921 */ 922 function user_search_access() { 923 return user_access('access user profiles'); 924 } 925 926 /** 927 * Implements hook_search_execute(). 928 */ 929 function user_search_execute($keys = NULL, $conditions = NULL) { 930 $find = array(); 931 // Replace wildcards with MySQL/PostgreSQL wildcards. 932 $keys = preg_replace('!\*+!', '%', $keys); 933 $query = db_select('users')->extend('PagerDefault'); 934 $query->fields('users', array('uid')); 935 if (user_access('administer users')) { 936 // Administrators can also search in the otherwise private email field. 937 $query->fields('users', array('mail')); 938 $query->condition(db_or()-> 939 condition('name', '%' . db_like($keys) . '%', 'LIKE')-> 940 condition('mail', '%' . db_like($keys) . '%', 'LIKE')); 941 } 942 else { 943 $query->condition('name', '%' . db_like($keys) . '%', 'LIKE'); 944 } 945 $uids = $query 946 ->limit(15) 947 ->execute() 948 ->fetchCol(); 949 $accounts = user_load_multiple($uids); 950 951 $results = array(); 952 foreach ($accounts as $account) { 953 $result = array( 954 'title' => format_username($account), 955 'link' => url('user/' . $account->uid, array('absolute' => TRUE)), 956 ); 957 if (user_access('administer users')) { 958 $result['title'] .= ' (' . $account->mail . ')'; 959 } 960 $results[] = $result; 961 } 962 963 return $results; 964 } 965 966 /** 967 * Implements hook_element_info(). 968 */ 969 function user_element_info() { 970 $types['user_profile_category'] = array( 971 '#theme_wrappers' => array('user_profile_category'), 972 ); 973 $types['user_profile_item'] = array( 974 '#theme' => 'user_profile_item', 975 ); 976 return $types; 977 } 978 979 /** 980 * Implements hook_user_view(). 981 */ 982 function user_user_view($account) { 983 $account->content['user_picture'] = array( 984 '#markup' => theme('user_picture', array('account' => $account)), 985 '#weight' => -10, 986 ); 987 if (!isset($account->content['summary'])) { 988 $account->content['summary'] = array(); 989 } 990 $account->content['summary'] += array( 991 '#type' => 'user_profile_category', 992 '#attributes' => array('class' => array('user-member')), 993 '#weight' => 5, 994 '#title' => t('History'), 995 ); 996 $account->content['summary']['member_for'] = array( 997 '#type' => 'user_profile_item', 998 '#title' => t('Member for'), 999 '#markup' => format_interval(REQUEST_TIME - $account->created), 1000 ); 1001 } 1002 1003 /** 1004 * Helper function to add default user account fields to user registration and edit form. 1005 * 1006 * @see user_account_form_validate() 1007 * @see user_validate_current_pass() 1008 * @see user_validate_picture() 1009 * @see user_validate_mail() 1010 */ 1011 function user_account_form(&$form, &$form_state) { 1012 global $user; 1013 1014 $account = $form['#user']; 1015 $register = ($form['#user']->uid > 0 ? FALSE : TRUE); 1016 1017 $admin = user_access('administer users'); 1018 1019 $form['#validate'][] = 'user_account_form_validate'; 1020 1021 // Account information. 1022 $form['account'] = array( 1023 '#type' => 'container', 1024 '#weight' => -10, 1025 ); 1026 // Only show name field on registration form or user can change own username. 1027 $form['account']['name'] = array( 1028 '#type' => 'textfield', 1029 '#title' => t('Username'), 1030 '#maxlength' => USERNAME_MAX_LENGTH, 1031 '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'), 1032 '#required' => TRUE, 1033 '#attributes' => array('class' => array('username')), 1034 '#default_value' => (!$register ? $account->name : ''), 1035 '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin), 1036 '#weight' => -10, 1037 ); 1038 1039 $form['account']['mail'] = array( 1040 '#type' => 'textfield', 1041 '#title' => t('E-mail address'), 1042 '#maxlength' => EMAIL_MAX_LENGTH, 1043 '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'), 1044 '#required' => TRUE, 1045 '#default_value' => (!$register ? $account->mail : ''), 1046 ); 1047 1048 // Display password field only for existing users or when user is allowed to 1049 // assign a password during registration. 1050 if (!$register) { 1051 $form['account']['pass'] = array( 1052 '#type' => 'password_confirm', 1053 '#size' => 25, 1054 '#description' => t('To change the current user password, enter the new password in both fields.'), 1055 ); 1056 // To skip the current password field, the user must have logged in via a 1057 // one-time link and have the token in the URL. 1058 $pass_reset = isset($_SESSION['pass_reset_' . $account->uid]) && isset($_GET['pass-reset-token']) && ($_GET['pass-reset-token'] == $_SESSION['pass_reset_' . $account->uid]); 1059 $protected_values = array(); 1060 $current_pass_description = ''; 1061 // The user may only change their own password without their current 1062 // password if they logged in via a one-time login link. 1063 if (!$pass_reset) { 1064 $protected_values['mail'] = $form['account']['mail']['#title']; 1065 $protected_values['pass'] = t('Password'); 1066 $request_new = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.')))); 1067 $current_pass_description = t('Enter your current password to change the %mail or %pass. !request_new.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'], '!request_new' => $request_new)); 1068 } 1069 // The user must enter their current password to change to a new one. 1070 if ($user->uid == $account->uid) { 1071 $form['account']['current_pass_required_values'] = array( 1072 '#type' => 'value', 1073 '#value' => $protected_values, 1074 ); 1075 $form['account']['current_pass'] = array( 1076 '#type' => 'password', 1077 '#title' => t('Current password'), 1078 '#size' => 25, 1079 '#access' => !empty($protected_values), 1080 '#description' => $current_pass_description, 1081 '#weight' => -5, 1082 '#attributes' => array('autocomplete' => 'off'), 1083 ); 1084 $form['#validate'][] = 'user_validate_current_pass'; 1085 } 1086 } 1087 elseif (!variable_get('user_email_verification', TRUE) || $admin) { 1088 $form['account']['pass'] = array( 1089 '#type' => 'password_confirm', 1090 '#size' => 25, 1091 '#description' => t('Provide a password for the new account in both fields.'), 1092 '#required' => TRUE, 1093 ); 1094 } 1095 1096 if ($admin) { 1097 $status = isset($account->status) ? $account->status : 1; 1098 } 1099 else { 1100 $status = $register ? variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS : $account->status; 1101 } 1102 $form['account']['status'] = array( 1103 '#type' => 'radios', 1104 '#title' => t('Status'), 1105 '#default_value' => $status, 1106 '#options' => array(t('Blocked'), t('Active')), 1107 '#access' => $admin, 1108 ); 1109 1110 $roles = array_map('check_plain', user_roles(TRUE)); 1111 // The disabled checkbox subelement for the 'authenticated user' role 1112 // must be generated separately and added to the checkboxes element, 1113 // because of a limitation in Form API not supporting a single disabled 1114 // checkbox within a set of checkboxes. 1115 // @todo This should be solved more elegantly. See issue #119038. 1116 $checkbox_authenticated = array( 1117 '#type' => 'checkbox', 1118 '#title' => $roles[DRUPAL_AUTHENTICATED_RID], 1119 '#default_value' => TRUE, 1120 '#disabled' => TRUE, 1121 ); 1122 unset($roles[DRUPAL_AUTHENTICATED_RID]); 1123 $form['account']['roles'] = array( 1124 '#type' => 'checkboxes', 1125 '#title' => t('Roles'), 1126 '#default_value' => (!$register && isset($account->roles) ? array_keys($account->roles) : array()), 1127 '#options' => $roles, 1128 '#access' => $roles && user_access('administer permissions'), 1129 DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated, 1130 ); 1131 1132 $form['account']['notify'] = array( 1133 '#type' => 'checkbox', 1134 '#title' => t('Notify user of new account'), 1135 '#access' => $register && $admin, 1136 ); 1137 1138 // Signature. 1139 $form['signature_settings'] = array( 1140 '#type' => 'fieldset', 1141 '#title' => t('Signature settings'), 1142 '#weight' => 1, 1143 '#access' => (!$register && variable_get('user_signatures', 0)), 1144 ); 1145 1146 $form['signature_settings']['signature'] = array( 1147 '#type' => 'text_format', 1148 '#title' => t('Signature'), 1149 '#default_value' => isset($account->signature) ? $account->signature : '', 1150 '#description' => t('Your signature will be publicly displayed at the end of your comments.'), 1151 '#format' => isset($account->signature_format) ? $account->signature_format : NULL, 1152 ); 1153 1154 // Picture/avatar. 1155 $form['picture'] = array( 1156 '#type' => 'fieldset', 1157 '#title' => t('Picture'), 1158 '#weight' => 1, 1159 '#access' => (!$register && variable_get('user_pictures', 0)), 1160 ); 1161 $form['picture']['picture'] = array( 1162 '#type' => 'value', 1163 '#value' => isset($account->picture) ? $account->picture : NULL, 1164 ); 1165 $form['picture']['picture_current'] = array( 1166 '#markup' => theme('user_picture', array('account' => $account)), 1167 ); 1168 $form['picture']['picture_delete'] = array( 1169 '#type' => 'checkbox', 1170 '#title' => t('Delete picture'), 1171 '#access' => !empty($account->picture->fid), 1172 '#description' => t('Check this box to delete your current picture.'), 1173 ); 1174 $form['picture']['picture_upload'] = array( 1175 '#type' => 'file', 1176 '#title' => t('Upload picture'), 1177 '#size' => 48, 1178 '#description' => t('Your virtual face or picture. Pictures larger than @dimensions pixels will be scaled down.', array('@dimensions' => variable_get('user_picture_dimensions', '85x85'))) . ' ' . filter_xss_admin(variable_get('user_picture_guidelines', '')), 1179 ); 1180 $form['#validate'][] = 'user_validate_picture'; 1181 } 1182 1183 /** 1184 * Form validation handler for the current password on the user_account_form(). 1185 * 1186 * @see user_account_form() 1187 */ 1188 function user_validate_current_pass(&$form, &$form_state) { 1189 $account = $form['#user']; 1190 foreach ($form_state['values']['current_pass_required_values'] as $key => $name) { 1191 // This validation only works for required textfields (like mail) or 1192 // form values like password_confirm that have their own validation 1193 // that prevent them from being empty if they are changed. 1194 if ((strlen(trim($form_state['values'][$key])) > 0) && ($form_state['values'][$key] != $account->$key)) { 1195 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); 1196 $current_pass_failed = empty($form_state['values']['current_pass']) || !user_check_password($form_state['values']['current_pass'], $account); 1197 if ($current_pass_failed) { 1198 form_set_error('current_pass', t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => $name))); 1199 form_set_error($key); 1200 } 1201 // We only need to check the password once. 1202 break; 1203 } 1204 } 1205 } 1206 1207 /** 1208 * Form validation handler for user_account_form(). 1209 * 1210 * @see user_account_form() 1211 */ 1212 function user_account_form_validate($form, &$form_state) { 1213 if ($form['#user_category'] == 'account' || $form['#user_category'] == 'register') { 1214 $account = $form['#user']; 1215 // Validate new or changing username. 1216 if (isset($form_state['values']['name'])) { 1217 if ($error = user_validate_name($form_state['values']['name'])) { 1218 form_set_error('name', $error); 1219 } 1220 elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('name', db_like($form_state['values']['name']), 'LIKE')->range(0, 1)->execute()->fetchField()) { 1221 form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name']))); 1222 } 1223 } 1224 1225 // Trim whitespace from mail, to prevent confusing 'e-mail not valid' 1226 // warnings often caused by cutting and pasting. 1227 $mail = trim($form_state['values']['mail']); 1228 form_set_value($form['account']['mail'], $mail, $form_state); 1229 1230 // Validate the e-mail address, and check if it is taken by an existing user. 1231 if ($error = user_validate_mail($form_state['values']['mail'])) { 1232 form_set_error('mail', $error); 1233 } 1234 elseif ((bool) db_select('users')->fields('users', array('uid'))->condition('uid', $account->uid, '<>')->condition('mail', db_like($form_state['values']['mail']), 'LIKE')->range(0, 1)->execute()->fetchField()) { 1235 // Format error message dependent on whether the user is logged in or not. 1236 if ($GLOBALS['user']->uid) { 1237 form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $form_state['values']['mail']))); 1238 } 1239 else { 1240 form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $form_state['values']['mail'], '@password' => url('user/password')))); 1241 } 1242 } 1243 1244 // Make sure the signature isn't longer than the size of the database field. 1245 // Signatures are disabled by default, so make sure it exists first. 1246 if (isset($form_state['values']['signature'])) { 1247 // Move text format for user signature into 'signature_format'. 1248 $form_state['values']['signature_format'] = $form_state['values']['signature']['format']; 1249 // Move text value for user signature into 'signature'. 1250 $form_state['values']['signature'] = $form_state['values']['signature']['value']; 1251 1252 $user_schema = drupal_get_schema('users'); 1253 if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) { 1254 form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length']))); 1255 } 1256 } 1257 } 1258 } 1259 1260 /** 1261 * Implements hook_user_presave(). 1262 */ 1263 function user_user_presave(&$edit, $account, $category) { 1264 if ($category == 'account' || $category == 'register') { 1265 if (!empty($edit['picture_upload'])) { 1266 $edit['picture'] = $edit['picture_upload']; 1267 } 1268 // Delete picture if requested, and if no replacement picture was given. 1269 elseif (!empty($edit['picture_delete'])) { 1270 $edit['picture'] = NULL; 1271 } 1272 // Prepare user roles. 1273 if (isset($edit['roles'])) { 1274 $edit['roles'] = array_filter($edit['roles']); 1275 } 1276 } 1277 1278 // Move account cancellation information into $user->data. 1279 foreach (array('user_cancel_method', 'user_cancel_notify') as $key) { 1280 if (isset($edit[$key])) { 1281 $edit['data'][$key] = $edit[$key]; 1282 } 1283 } 1284 } 1285 1286 /** 1287 * Implements hook_user_categories(). 1288 */ 1289 function user_user_categories() { 1290 return array(array( 1291 'name' => 'account', 1292 'title' => t('Account settings'), 1293 'weight' => 1, 1294 )); 1295 } 1296 1297 function user_login_block($form) { 1298 $form['#action'] = url(current_path(), array('query' => drupal_get_destination(), 'external' => FALSE)); 1299 $form['#id'] = 'user-login-form'; 1300 $form['#validate'] = user_login_default_validators(); 1301 $form['#submit'][] = 'user_login_submit'; 1302 $form['name'] = array('#type' => 'textfield', 1303 '#title' => t('Username'), 1304 '#maxlength' => USERNAME_MAX_LENGTH, 1305 '#size' => 15, 1306 '#required' => TRUE, 1307 ); 1308 $form['pass'] = array('#type' => 'password', 1309 '#title' => t('Password'), 1310 '#size' => 15, 1311 '#required' => TRUE, 1312 ); 1313 $form['actions'] = array('#type' => 'actions'); 1314 $form['actions']['submit'] = array('#type' => 'submit', 1315 '#value' => t('Log in'), 1316 ); 1317 $items = array(); 1318 if (variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)) { 1319 $items[] = l(t('Create new account'), 'user/register', array('attributes' => array('title' => t('Create a new user account.')))); 1320 } 1321 $items[] = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.')))); 1322 $form['links'] = array('#markup' => theme('item_list', array('items' => $items))); 1323 return $form; 1324 } 1325 1326 /** 1327 * Implements hook_block_info(). 1328 */ 1329 function user_block_info() { 1330 global $user; 1331 1332 $blocks['login']['info'] = t('User login'); 1333 // Not worth caching. 1334 $blocks['login']['cache'] = DRUPAL_NO_CACHE; 1335 1336 $blocks['new']['info'] = t('Who\'s new'); 1337 $blocks['new']['properties']['administrative'] = TRUE; 1338 1339 // Too dynamic to cache. 1340 $blocks['online']['info'] = t('Who\'s online'); 1341 $blocks['online']['cache'] = DRUPAL_NO_CACHE; 1342 $blocks['online']['properties']['administrative'] = TRUE; 1343 1344 return $blocks; 1345 } 1346 1347 /** 1348 * Implements hook_block_configure(). 1349 */ 1350 function user_block_configure($delta = '') { 1351 global $user; 1352 1353 switch ($delta) { 1354 case 'new': 1355 $form['user_block_whois_new_count'] = array( 1356 '#type' => 'select', 1357 '#title' => t('Number of users to display'), 1358 '#default_value' => variable_get('user_block_whois_new_count', 5), 1359 '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), 1360 ); 1361 return $form; 1362 1363 case 'online': 1364 $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval'); 1365 $form['user_block_seconds_online'] = array('#type' => 'select', '#title' => t('User activity'), '#default_value' => variable_get('user_block_seconds_online', 900), '#options' => $period, '#description' => t('A user is considered online for this long after they have last viewed a page.')); 1366 $form['user_block_max_list_count'] = array('#type' => 'select', '#title' => t('User list length'), '#default_value' => variable_get('user_block_max_list_count', 10), '#options' => drupal_map_assoc(array(0, 5, 10, 15, 20, 25, 30, 40, 50, 75, 100)), '#description' => t('Maximum number of currently online users to display.')); 1367 return $form; 1368 } 1369 } 1370 1371 /** 1372 * Implements hook_block_save(). 1373 */ 1374 function user_block_save($delta = '', $edit = array()) { 1375 global $user; 1376 1377 switch ($delta) { 1378 case 'new': 1379 variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']); 1380 break; 1381 1382 case 'online': 1383 variable_set('user_block_seconds_online', $edit['user_block_seconds_online']); 1384 variable_set('user_block_max_list_count', $edit['user_block_max_list_count']); 1385 break; 1386 } 1387 } 1388 1389 /** 1390 * Implements hook_block_view(). 1391 */ 1392 function user_block_view($delta = '') { 1393 global $user; 1394 1395 $block = array(); 1396 1397 switch ($delta) { 1398 case 'login': 1399 // For usability's sake, avoid showing two login forms on one page. 1400 if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) { 1401 1402 $block['subject'] = t('User login'); 1403 $block['content'] = drupal_get_form('user_login_block'); 1404 } 1405 return $block; 1406 1407 case 'new': 1408 if (user_access('access content')) { 1409 // Retrieve a list of new users who have subsequently accessed the site successfully. 1410 $items = db_query_range('SELECT uid, name FROM {users} WHERE status <> 0 AND access <> 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5))->fetchAll(); 1411 $output = theme('user_list', array('users' => $items)); 1412 1413 $block['subject'] = t('Who\'s new'); 1414 $block['content'] = $output; 1415 } 1416 return $block; 1417 1418 case 'online': 1419 if (user_access('access content')) { 1420 // Count users active within the defined period. 1421 $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900); 1422 1423 // Perform database queries to gather online user lists. We use s.timestamp 1424 // rather than u.access because it is much faster. 1425 $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField(); 1426 1427 $output = '<p>' . format_plural($authenticated_count, 'There is currently 1 user online.', 'There are currently @count users online.') . '</p>'; 1428 1429 // Display a list of currently online users. 1430 $max_users = variable_get('user_block_max_list_count', 10); 1431 if ($authenticated_count && $max_users) { 1432 $items = db_query_range('SELECT u.uid, u.name, MAX(s.timestamp) AS max_timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= :interval AND s.uid > 0 GROUP BY u.uid, u.name ORDER BY max_timestamp DESC', 0, $max_users, array(':interval' => $interval))->fetchAll(); 1433 $output .= theme('user_list', array('users' => $items)); 1434 } 1435 1436 $block['subject'] = t('Who\'s online'); 1437 $block['content'] = $output; 1438 } 1439 return $block; 1440 } 1441 } 1442 1443 /** 1444 * Process variables for user-picture.tpl.php. 1445 * 1446 * The $variables array contains the following arguments: 1447 * - $account: A user, node or comment object with 'name', 'uid' and 'picture' 1448 * fields. 1449 * 1450 * @see user-picture.tpl.php 1451 */ 1452 function template_preprocess_user_picture(&$variables) { 1453 $variables['user_picture'] = ''; 1454 if (variable_get('user_pictures', 0)) { 1455 $account = $variables['account']; 1456 if (!empty($account->picture)) { 1457 // @TODO: Ideally this function would only be passed file objects, but 1458 // since there's a lot of legacy code that JOINs the {users} table to 1459 // {node} or {comments} and passes the results into this function if we 1460 // a numeric value in the picture field we'll assume it's a file id 1461 // and load it for them. Once we've got user_load_multiple() and 1462 // comment_load_multiple() functions the user module will be able to load 1463 // the picture files in mass during the object's load process. 1464 if (is_numeric($account->picture)) { 1465 $account->picture = file_load($account->picture); 1466 } 1467 if (!empty($account->picture->uri)) { 1468 $filepath = $account->picture->uri; 1469 } 1470 } 1471 elseif (variable_get('user_picture_default', '')) { 1472 $filepath = variable_get('user_picture_default', ''); 1473 } 1474 if (isset($filepath)) { 1475 $alt = t("@user's picture", array('@user' => format_username($account))); 1476 // If the image does not have a valid Drupal scheme (for eg. HTTP), 1477 // don't load image styles. 1478 if (module_exists('image') && file_valid_uri($filepath) && $style = variable_get('user_picture_style', '')) { 1479 $variables['user_picture'] = theme('image_style', array('style_name' => $style, 'path' => $filepath, 'alt' => $alt, 'title' => $alt)); 1480 } 1481 else { 1482 $variables['user_picture'] = theme('image', array('path' => $filepath, 'alt' => $alt, 'title' => $alt)); 1483 } 1484 if (!empty($account->uid) && user_access('access user profiles')) { 1485 $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE); 1486 $variables['user_picture'] = l($variables['user_picture'], "user/$account->uid", $attributes); 1487 } 1488 } 1489 } 1490 } 1491 1492 /** 1493 * Returns HTML for a list of users. 1494 * 1495 * @param $variables 1496 * An associative array containing: 1497 * - users: An array with user objects. Should contain at least the name and 1498 * uid. 1499 * - title: (optional) Title to pass on to theme_item_list(). 1500 * 1501 * @ingroup themeable 1502 */ 1503 function theme_user_list($variables) { 1504 $users = $variables['users']; 1505 $title = $variables['title']; 1506 $items = array(); 1507 1508 if (!empty($users)) { 1509 foreach ($users as $user) { 1510 $items[] = theme('username', array('account' => $user)); 1511 } 1512 } 1513 return theme('item_list', array('items' => $items, 'title' => $title)); 1514 } 1515 1516 function user_is_anonymous() { 1517 // Menu administrators can see items for anonymous when administering. 1518 return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']); 1519 } 1520 1521 function user_is_logged_in() { 1522 return (bool) $GLOBALS['user']->uid; 1523 } 1524 1525 function user_register_access() { 1526 return user_is_anonymous() && variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL); 1527 } 1528 1529 /** 1530 * User view access callback. 1531 * 1532 * @param $account 1533 * Can either be a full user object or a $uid. 1534 */ 1535 function user_view_access($account) { 1536 $uid = is_object($account) ? $account->uid : (int) $account; 1537 1538 // Never allow access to view the anonymous user account. 1539 if ($uid) { 1540 // Admins can view all, users can view own profiles at all times. 1541 if ($GLOBALS['user']->uid == $uid || user_access('administer users')) { 1542 return TRUE; 1543 } 1544 elseif (user_access('access user profiles')) { 1545 // At this point, load the complete account object. 1546 if (!is_object($account)) { 1547 $account = user_load($uid); 1548 } 1549 return (is_object($account) && $account->status); 1550 } 1551 } 1552 return FALSE; 1553 } 1554 1555 /** 1556 * Access callback for user account editing. 1557 */ 1558 function user_edit_access($account) { 1559 return (($GLOBALS['user']->uid == $account->uid) || user_access('administer users')) && $account->uid > 0; 1560 } 1561 1562 /** 1563 * Menu access callback; limit access to account cancellation pages. 1564 * 1565 * Limit access to users with the 'cancel account' permission or administrative 1566 * users, and prevent the anonymous user from cancelling the account. 1567 */ 1568 function user_cancel_access($account) { 1569 return ((($GLOBALS['user']->uid == $account->uid) && user_access('cancel account')) || user_access('administer users')) && $account->uid > 0; 1570 } 1571 1572 /** 1573 * Implements hook_menu(). 1574 */ 1575 function user_menu() { 1576 $items['user/autocomplete'] = array( 1577 'title' => 'User autocomplete', 1578 'page callback' => 'user_autocomplete', 1579 'access callback' => 'user_access', 1580 'access arguments' => array('access user profiles'), 1581 'type' => MENU_CALLBACK, 1582 'file' => 'user.pages.inc', 1583 ); 1584 1585 // Registration and login pages. 1586 $items['user'] = array( 1587 'title' => 'User account', 1588 'title callback' => 'user_menu_title', 1589 'page callback' => 'user_page', 1590 'access callback' => TRUE, 1591 'file' => 'user.pages.inc', 1592 'weight' => -10, 1593 'menu_name' => 'user-menu', 1594 ); 1595 1596 $items['user/login'] = array( 1597 'title' => 'Log in', 1598 'access callback' => 'user_is_anonymous', 1599 'type' => MENU_DEFAULT_LOCAL_TASK, 1600 ); 1601 1602 $items['user/register'] = array( 1603 'title' => 'Create new account', 1604 'page callback' => 'drupal_get_form', 1605 'page arguments' => array('user_register_form'), 1606 'access callback' => 'user_register_access', 1607 'type' => MENU_LOCAL_TASK, 1608 ); 1609 1610 $items['user/password'] = array( 1611 'title' => 'Request new password', 1612 'page callback' => 'drupal_get_form', 1613 'page arguments' => array('user_pass'), 1614 'access callback' => TRUE, 1615 'type' => MENU_LOCAL_TASK, 1616 'file' => 'user.pages.inc', 1617 ); 1618 $items['user/reset/%/%/%'] = array( 1619 'title' => 'Reset password', 1620 'page callback' => 'drupal_get_form', 1621 'page arguments' => array('user_pass_reset', 2, 3, 4), 1622 'access callback' => TRUE, 1623 'type' => MENU_CALLBACK, 1624 'file' => 'user.pages.inc', 1625 ); 1626 1627 $items['user/logout'] = array( 1628 'title' => 'Log out', 1629 'access callback' => 'user_is_logged_in', 1630 'page callback' => 'user_logout', 1631 'weight' => 10, 1632 'menu_name' => 'user-menu', 1633 'file' => 'user.pages.inc', 1634 ); 1635 1636 // User listing pages. 1637 $items['admin/people'] = array( 1638 'title' => 'People', 1639 'description' => 'Manage user accounts, roles, and permissions.', 1640 'page callback' => 'user_admin', 1641 'page arguments' => array('list'), 1642 'access arguments' => array('administer users'), 1643 'position' => 'left', 1644 'weight' => -4, 1645 'file' => 'user.admin.inc', 1646 ); 1647 $items['admin/people/people'] = array( 1648 'title' => 'List', 1649 'description' => 'Find and manage people interacting with your site.', 1650 'access arguments' => array('administer users'), 1651 'type' => MENU_DEFAULT_LOCAL_TASK, 1652 'weight' => -10, 1653 'file' => 'user.admin.inc', 1654 ); 1655 1656 // Permissions and role forms. 1657 $items['admin/people/permissions'] = array( 1658 'title' => 'Permissions', 1659 'description' => 'Determine access to features by selecting permissions for roles.', 1660 'page callback' => 'drupal_get_form', 1661 'page arguments' => array('user_admin_permissions'), 1662 'access arguments' => array('administer permissions'), 1663 'file' => 'user.admin.inc', 1664 'type' => MENU_LOCAL_TASK, 1665 ); 1666 $items['admin/people/permissions/list'] = array( 1667 'title' => 'Permissions', 1668 'description' => 'Determine access to features by selecting permissions for roles.', 1669 'type' => MENU_DEFAULT_LOCAL_TASK, 1670 'weight' => -8, 1671 ); 1672 $items['admin/people/permissions/roles'] = array( 1673 'title' => 'Roles', 1674 'description' => 'List, edit, or add user roles.', 1675 'page callback' => 'drupal_get_form', 1676 'page arguments' => array('user_admin_roles'), 1677 'access arguments' => array('administer permissions'), 1678 'file' => 'user.admin.inc', 1679 'type' => MENU_LOCAL_TASK, 1680 'weight' => -5, 1681 ); 1682 $items['admin/people/permissions/roles/edit/%user_role'] = array( 1683 'title' => 'Edit role', 1684 'page arguments' => array('user_admin_role', 5), 1685 'access callback' => 'user_role_edit_access', 1686 'access arguments' => array(5), 1687 ); 1688 $items['admin/people/permissions/roles/delete/%user_role'] = array( 1689 'title' => 'Delete role', 1690 'page callback' => 'drupal_get_form', 1691 'page arguments' => array('user_admin_role_delete_confirm', 5), 1692 'access callback' => 'user_role_edit_access', 1693 'access arguments' => array(5), 1694 'file' => 'user.admin.inc', 1695 ); 1696 1697 $items['admin/people/create'] = array( 1698 'title' => 'Add user', 1699 'page arguments' => array('create'), 1700 'access arguments' => array('administer users'), 1701 'type' => MENU_LOCAL_ACTION, 1702 ); 1703 1704 // Administration pages. 1705 $items['admin/config/people'] = array( 1706 'title' => 'People', 1707 'description' => 'Configure user accounts.', 1708 'position' => 'left', 1709 'weight' => -20, 1710 'page callback' => 'system_admin_menu_block_page', 1711 'access arguments' => array('access administration pages'), 1712 'file' => 'system.admin.inc', 1713 'file path' => drupal_get_path('module', 'system'), 1714 ); 1715 $items['admin/config/people/accounts'] = array( 1716 'title' => 'Account settings', 1717 'description' => 'Configure default behavior of users, including registration requirements, e-mails, fields, and user pictures.', 1718 'page callback' => 'drupal_get_form', 1719 'page arguments' => array('user_admin_settings'), 1720 'access arguments' => array('administer users'), 1721 'file' => 'user.admin.inc', 1722 'weight' => -10, 1723 ); 1724 $items['admin/config/people/accounts/settings'] = array( 1725 'title' => 'Settings', 1726 'type' => MENU_DEFAULT_LOCAL_TASK, 1727 'weight' => -10, 1728 ); 1729 1730 $items['user/%user'] = array( 1731 'title' => 'My account', 1732 'title callback' => 'user_page_title', 1733 'title arguments' => array(1), 1734 'page callback' => 'user_view_page', 1735 'page arguments' => array(1), 1736 'access callback' => 'user_view_access', 1737 'access arguments' => array(1), 1738 // By assigning a different menu name, this item (and all registered child 1739 // paths) are no longer considered as children of 'user'. When accessing the 1740 // user account pages, the preferred menu link that is used to build the 1741 // active trail (breadcrumb) will be found in this menu (unless there is 1742 // more specific link), so the link to 'user' will not be in the breadcrumb. 1743 'menu_name' => 'navigation', 1744 ); 1745 1746 $items['user/%user/view'] = array( 1747 'title' => 'View', 1748 'type' => MENU_DEFAULT_LOCAL_TASK, 1749 'weight' => -10, 1750 ); 1751 1752 $items['user/%user/cancel'] = array( 1753 'title' => 'Cancel account', 1754 'page callback' => 'drupal_get_form', 1755 'page arguments' => array('user_cancel_confirm_form', 1), 1756 'access callback' => 'user_cancel_access', 1757 'access arguments' => array(1), 1758 'file' => 'user.pages.inc', 1759 ); 1760 1761 $items['user/%user/cancel/confirm/%/%'] = array( 1762 'title' => 'Confirm account cancellation', 1763 'page callback' => 'user_cancel_confirm', 1764 'page arguments' => array(1, 4, 5), 1765 'access callback' => 'user_cancel_access', 1766 'access arguments' => array(1), 1767 'file' => 'user.pages.inc', 1768 ); 1769 1770 $items['user/%user/edit'] = array( 1771 'title' => 'Edit', 1772 'page callback' => 'drupal_get_form', 1773 'page arguments' => array('user_profile_form', 1), 1774 'access callback' => 'user_edit_access', 1775 'access arguments' => array(1), 1776 'type' => MENU_LOCAL_TASK, 1777 'file' => 'user.pages.inc', 1778 ); 1779 1780 $items['user/%user_category/edit/account'] = array( 1781 'title' => 'Account', 1782 'type' => MENU_DEFAULT_LOCAL_TASK, 1783 'load arguments' => array('%map', '%index'), 1784 ); 1785 1786 if (($categories = _user_categories()) && (count($categories) > 1)) { 1787 foreach ($categories as $key => $category) { 1788 // 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK. 1789 if ($category['name'] != 'account') { 1790 $items['user/%user_category/edit/' . $category['name']] = array( 1791 'title callback' => 'check_plain', 1792 'title arguments' => array($category['title']), 1793 'page callback' => 'drupal_get_form', 1794 'page arguments' => array('user_profile_form', 1, 3), 1795 'access callback' => isset($category['access callback']) ? $category['access callback'] : 'user_edit_access', 1796 'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(1), 1797 'type' => MENU_LOCAL_TASK, 1798 'weight' => $category['weight'], 1799 'load arguments' => array('%map', '%index'), 1800 'tab_parent' => 'user/%/edit', 1801 'file' => 'user.pages.inc', 1802 ); 1803 } 1804 } 1805 } 1806 return $items; 1807 } 1808 1809 /** 1810 * Implements hook_menu_site_status_alter(). 1811 */ 1812 function user_menu_site_status_alter(&$menu_site_status, $path) { 1813 if ($menu_site_status == MENU_SITE_OFFLINE) { 1814 // If the site is offline, log out unprivileged users. 1815 if (user_is_logged_in() && !user_access('access site in maintenance mode')) { 1816 module_load_include('pages.inc', 'user', 'user'); 1817 user_logout(); 1818 } 1819 1820 if (user_is_anonymous()) { 1821 switch ($path) { 1822 case 'user': 1823 // Forward anonymous user to login page. 1824 drupal_goto('user/login'); 1825 case 'user/login': 1826 case 'user/password': 1827 // Disable offline mode. 1828 $menu_site_status = MENU_SITE_ONLINE; 1829 break; 1830 default: 1831 if (strpos($path, 'user/reset/') === 0) { 1832 // Disable offline mode. 1833 $menu_site_status = MENU_SITE_ONLINE; 1834 } 1835 break; 1836 } 1837 } 1838 } 1839 if (user_is_logged_in()) { 1840 if ($path == 'user/login') { 1841 // If user is logged in, redirect to 'user' instead of giving 403. 1842 drupal_goto('user'); 1843 } 1844 if ($path == 'user/register') { 1845 // Authenticated user should be redirected to user edit page. 1846 drupal_goto('user/' . $GLOBALS['user']->uid . '/edit'); 1847 } 1848 } 1849 } 1850 1851 /** 1852 * Implements hook_menu_link_alter(). 1853 */ 1854 function user_menu_link_alter(&$link) { 1855 // The path 'user' must be accessible for anonymous users, but only visible 1856 // for authenticated users. Authenticated users should see "My account", but 1857 // anonymous users should not see it at all. Therefore, invoke 1858 // user_translated_menu_link_alter() to conditionally hide the link. 1859 if ($link['link_path'] == 'user' && $link['module'] == 'system') { 1860 $link['options']['alter'] = TRUE; 1861 } 1862 1863 // Force the Logout link to appear on the top-level of 'user-menu' menu by 1864 // default (i.e., unless it has been customized). 1865 if ($link['link_path'] == 'user/logout' && $link['module'] == 'system' && empty($link['customized'])) { 1866 $link['plid'] = 0; 1867 } 1868 } 1869 1870 /** 1871 * Implements hook_translated_menu_link_alter(). 1872 */ 1873 function user_translated_menu_link_alter(&$link) { 1874 // Hide the "User account" link for anonymous users. 1875 if ($link['link_path'] == 'user' && $link['module'] == 'system' && !$GLOBALS['user']->uid) { 1876 $link['hidden'] = 1; 1877 } 1878 } 1879 1880 /** 1881 * Implements hook_admin_paths(). 1882 */ 1883 function user_admin_paths() { 1884 $paths = array( 1885 'user/*/cancel' => TRUE, 1886 'user/*/edit' => TRUE, 1887 'user/*/edit/*' => TRUE, 1888 ); 1889 return $paths; 1890 } 1891 1892 /** 1893 * Returns $arg or the user ID of the current user if $arg is '%' or empty. 1894 * 1895 * Deprecated. Use %user_uid_optional instead. 1896 * 1897 * @todo D8: Remove. 1898 */ 1899 function user_uid_only_optional_to_arg($arg) { 1900 return user_uid_optional_to_arg($arg); 1901 } 1902 1903 /** 1904 * Load either a specified or the current user account. 1905 * 1906 * @param $uid 1907 * An optional user ID of the user to load. If not provided, the current 1908 * user's ID will be used. 1909 * @return 1910 * A fully-loaded $user object upon successful user load, FALSE if user 1911 * cannot be loaded. 1912 * 1913 * @see user_load() 1914 * @todo rethink the naming of this in Drupal 8. 1915 */ 1916 function user_uid_optional_load($uid = NULL) { 1917 if (!isset($uid)) { 1918 $uid = $GLOBALS['user']->uid; 1919 } 1920 return user_load($uid); 1921 } 1922 1923 /** 1924 * Return a user object after checking if any profile category in the path exists. 1925 */ 1926 function user_category_load($uid, &$map, $index) { 1927 static $user_categories, $accounts; 1928 1929 // Cache $account - this load function will get called for each profile tab. 1930 if (!isset($accounts[$uid])) { 1931 $accounts[$uid] = user_load($uid); 1932 } 1933 $valid = TRUE; 1934 if ($account = $accounts[$uid]) { 1935 // Since the path is like user/%/edit/category_name, the category name will 1936 // be at a position 2 beyond the index corresponding to the % wildcard. 1937 $category_index = $index + 2; 1938 // Valid categories may contain slashes, and hence need to be imploded. 1939 $category_path = implode('/', array_slice($map, $category_index)); 1940 if ($category_path) { 1941 // Check that the requested category exists. 1942 $valid = FALSE; 1943 if (!isset($user_categories)) { 1944 $user_categories = _user_categories(); 1945 } 1946 foreach ($user_categories as $category) { 1947 if ($category['name'] == $category_path) { 1948 $valid = TRUE; 1949 // Truncate the map array in case the category name had slashes. 1950 $map = array_slice($map, 0, $category_index); 1951 // Assign the imploded category name to the last map element. 1952 $map[$category_index] = $category_path; 1953 break; 1954 } 1955 } 1956 } 1957 } 1958 return $valid ? $account : FALSE; 1959 } 1960 1961 /** 1962 * Returns $arg or the user ID of the current user if $arg is '%' or empty. 1963 * 1964 * @todo rethink the naming of this in Drupal 8. 1965 */ 1966 function user_uid_optional_to_arg($arg) { 1967 // Give back the current user uid when called from eg. tracker, aka. 1968 // with an empty arg. Also use the current user uid when called from 1969 // the menu with a % for the current account link. 1970 return empty($arg) || $arg == '%' ? $GLOBALS['user']->uid : $arg; 1971 } 1972 1973 /** 1974 * Menu item title callback for the 'user' path. 1975 * 1976 * Anonymous users should see "User account", but authenticated users are 1977 * expected to see "My account". 1978 */ 1979 function user_menu_title() { 1980 return user_is_logged_in() ? t('My account') : t('User account'); 1981 } 1982 1983 /** 1984 * Menu item title callback - use the user name. 1985 */ 1986 function user_page_title($account) { 1987 return is_object($account) ? format_username($account) : ''; 1988 } 1989 1990 /** 1991 * Discover which external authentication module(s) authenticated a username. 1992 * 1993 * @param $authname 1994 * A username used by an external authentication module. 1995 * @return 1996 * An associative array with module as key and username as value. 1997 */ 1998 function user_get_authmaps($authname = NULL) { 1999 $authmaps = db_query("SELECT module, authname FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchAllKeyed(); 2000 return count($authmaps) ? $authmaps : 0; 2001 } 2002 2003 /** 2004 * Save mappings of which external authentication module(s) authenticated 2005 * a user. Maps external usernames to user ids in the users table. 2006 * 2007 * @param $account 2008 * A user object. 2009 * @param $authmaps 2010 * An associative array with a compound key and the username as the value. 2011 * The key is made up of 'authname_' plus the name of the external authentication 2012 * module. 2013 * @see user_external_login_register() 2014 */ 2015 function user_set_authmaps($account, $authmaps) { 2016 foreach ($authmaps as $key => $value) { 2017 $module = explode('_', $key, 2); 2018 if ($value) { 2019 db_merge('authmap') 2020 ->key(array( 2021 'uid' => $account->uid, 2022 'module' => $module[1], 2023 )) 2024 ->fields(array('authname' => $value)) 2025 ->execute(); 2026 } 2027 else { 2028 db_delete('authmap') 2029 ->condition('uid', $account->uid) 2030 ->condition('module', $module[1]) 2031 ->execute(); 2032 } 2033 } 2034 } 2035 2036 /** 2037 * Form builder; the main user login form. 2038 * 2039 * @ingroup forms 2040 */ 2041 function user_login($form, &$form_state) { 2042 global $user; 2043 2044 // If we are already logged on, go to the user page instead. 2045 if ($user->uid) { 2046 drupal_goto('user/' . $user->uid); 2047 } 2048 2049 // Display login form: 2050 $form['name'] = array('#type' => 'textfield', 2051 '#title' => t('Username'), 2052 '#size' => 60, 2053 '#maxlength' => USERNAME_MAX_LENGTH, 2054 '#required' => TRUE, 2055 ); 2056 2057 $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal'))); 2058 $form['pass'] = array('#type' => 'password', 2059 '#title' => t('Password'), 2060 '#description' => t('Enter the password that accompanies your username.'), 2061 '#required' => TRUE, 2062 ); 2063 $form['#validate'] = user_login_default_validators(); 2064 $form['actions'] = array('#type' => 'actions'); 2065 $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in')); 2066 2067 return $form; 2068 } 2069 2070 /** 2071 * Set up a series for validators which check for blocked users, 2072 * then authenticate against local database, then return an error if 2073 * authentication fails. Distributed authentication modules are welcome 2074 * to use hook_form_alter() to change this series in order to 2075 * authenticate against their user database instead of the local users 2076 * table. If a distributed authentication module is successful, it 2077 * should set $form_state['uid'] to a user ID. 2078 * 2079 * We use three validators instead of one since external authentication 2080 * modules usually only need to alter the second validator. 2081 * 2082 * @see user_login_name_validate() 2083 * @see user_login_authenticate_validate() 2084 * @see user_login_final_validate() 2085 * @return array 2086 * A simple list of validate functions. 2087 */ 2088 function user_login_default_validators() { 2089 return array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate'); 2090 } 2091 2092 /** 2093 * A FAPI validate handler. Sets an error if supplied username has been blocked. 2094 */ 2095 function user_login_name_validate($form, &$form_state) { 2096 if (isset($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) { 2097 // Blocked in user administration. 2098 form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name']))); 2099 } 2100 } 2101 2102 /** 2103 * A validate handler on the login form. Check supplied username/password 2104 * against local users table. If successful, $form_state['uid'] 2105 * is set to the matching user ID. 2106 */ 2107 function user_login_authenticate_validate($form, &$form_state) { 2108 $password = trim($form_state['values']['pass']); 2109 if (!empty($form_state['values']['name']) && !empty($password)) { 2110 // Do not allow any login from the current user's IP if the limit has been 2111 // reached. Default is 50 failed attempts allowed in one hour. This is 2112 // independent of the per-user limit to catch attempts from one IP to log 2113 // in to many different user accounts. We have a reasonably high limit 2114 // since there may be only one apparent IP for all users at an institution. 2115 if (!flood_is_allowed('failed_login_attempt_ip', variable_get('user_failed_login_ip_limit', 50), variable_get('user_failed_login_ip_window', 3600))) { 2116 $form_state['flood_control_triggered'] = 'ip'; 2117 return; 2118 } 2119 $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject(); 2120 if ($account) { 2121 if (variable_get('user_failed_login_identifier_uid_only', FALSE)) { 2122 // Register flood events based on the uid only, so they apply for any 2123 // IP address. This is the most secure option. 2124 $identifier = $account->uid; 2125 } 2126 else { 2127 // The default identifier is a combination of uid and IP address. This 2128 // is less secure but more resistant to denial-of-service attacks that 2129 // could lock out all users with public user names. 2130 $identifier = $account->uid . '-' . ip_address(); 2131 } 2132 $form_state['flood_control_user_identifier'] = $identifier; 2133 2134 // Don't allow login if the limit for this user has been reached. 2135 // Default is to allow 5 failed attempts every 6 hours. 2136 if (!flood_is_allowed('failed_login_attempt_user', variable_get('user_failed_login_user_limit', 5), variable_get('user_failed_login_user_window', 21600), $identifier)) { 2137 $form_state['flood_control_triggered'] = 'user'; 2138 return; 2139 } 2140 } 2141 // We are not limited by flood control, so try to authenticate. 2142 // Set $form_state['uid'] as a flag for user_login_final_validate(). 2143 $form_state['uid'] = user_authenticate($form_state['values']['name'], $password); 2144 } 2145 } 2146 2147 /** 2148 * The final validation handler on the login form. 2149 * 2150 * Sets a form error if user has not been authenticated, or if too many 2151 * logins have been attempted. This validation function should always 2152 * be the last one. 2153 */ 2154 function user_login_final_validate($form, &$form_state) { 2155 if (empty($form_state['uid'])) { 2156 // Always register an IP-based failed login event. 2157 flood_register_event('failed_login_attempt_ip', variable_get('user_failed_login_ip_window', 3600)); 2158 // Register a per-user failed login event. 2159 if (isset($form_state['flood_control_user_identifier'])) { 2160 flood_register_event('failed_login_attempt_user', variable_get('user_failed_login_user_window', 21600), $form_state['flood_control_user_identifier']); 2161 } 2162 2163 if (isset($form_state['flood_control_triggered'])) { 2164 if ($form_state['flood_control_triggered'] == 'user') { 2165 form_set_error('name', format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password')))); 2166 } 2167 else { 2168 // We did not find a uid, so the limit is IP-based. 2169 form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password')))); 2170 } 2171 } 2172 else { 2173 form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password')))); 2174 watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name'])); 2175 } 2176 } 2177 elseif (isset($form_state['flood_control_user_identifier'])) { 2178 // Clear past failures for this user so as not to block a user who might 2179 // log in and out more than once in an hour. 2180 flood_clear_event('failed_login_attempt_user', $form_state['flood_control_user_identifier']); 2181 } 2182 } 2183 2184 /** 2185 * Try to validate the user's login credentials locally. 2186 * 2187 * @param $name 2188 * User name to authenticate. 2189 * @param $password 2190 * A plain-text password, such as trimmed text from form values. 2191 * @return 2192 * The user's uid on success, or FALSE on failure to authenticate. 2193 */ 2194 function user_authenticate($name, $password) { 2195 $uid = FALSE; 2196 if (!empty($name) && !empty($password)) { 2197 $account = user_load_by_name($name); 2198 if ($account) { 2199 // Allow alternate password hashing schemes. 2200 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); 2201 if (user_check_password($password, $account)) { 2202 // Successful authentication. 2203 $uid = $account->uid; 2204 2205 // Update user to new password scheme if needed. 2206 if (user_needs_new_hash($account)) { 2207 user_save($account, array('pass' => $password)); 2208 } 2209 } 2210 } 2211 } 2212 return $uid; 2213 } 2214 2215 /** 2216 * Finalize the login process. Must be called when logging in a user. 2217 * 2218 * The function records a watchdog message about the new session, saves the 2219 * login timestamp, calls hook_user op 'login' and generates a new session. * 2220 */ 2221 function user_login_finalize(&$edit = array()) { 2222 global $user; 2223 watchdog('user', 'Session opened for %name.', array('%name' => $user->name)); 2224 // Update the user table timestamp noting user has logged in. 2225 // This is also used to invalidate one-time login links. 2226 $user->login = REQUEST_TIME; 2227 db_update('users') 2228 ->fields(array('login' => $user->login)) 2229 ->condition('uid', $user->uid) 2230 ->execute(); 2231 2232 // Regenerate the session ID to prevent against session fixation attacks. 2233 // This is called before hook_user in case one of those functions fails 2234 // or incorrectly does a redirect which would leave the old session in place. 2235 drupal_session_regenerate(); 2236 2237 user_module_invoke('login', $edit, $user); 2238 } 2239 2240 /** 2241 * Submit handler for the login form. Load $user object and perform standard login 2242 * tasks. The user is then redirected to the My Account page. Setting the 2243 * destination in the query string overrides the redirect. 2244 */ 2245 function user_login_submit($form, &$form_state) { 2246 global $user; 2247 $user = user_load($form_state['uid']); 2248 $form_state['redirect'] = 'user/' . $user->uid; 2249 2250 user_login_finalize($form_state); 2251 } 2252 2253 /** 2254 * Helper function for authentication modules. Either logs in or registers 2255 * the current user, based on username. Either way, the global $user object is 2256 * populated and login tasks are performed. 2257 */ 2258 function user_external_login_register($name, $module) { 2259 $account = user_external_load($name); 2260 if (!$account) { 2261 // Register this new user. 2262 $userinfo = array( 2263 'name' => $name, 2264 'pass' => user_password(), 2265 'init' => $name, 2266 'status' => 1, 2267 'access' => REQUEST_TIME 2268 ); 2269 $account = user_save(drupal_anonymous_user(), $userinfo); 2270 // Terminate if an error occurred during user_save(). 2271 if (!$account) { 2272 drupal_set_message(t("Error saving user account."), 'error'); 2273 return; 2274 } 2275 user_set_authmaps($account, array("authname_$module" => $name)); 2276 } 2277 2278 // Log user in. 2279 $form_state['uid'] = $account->uid; 2280 user_login_submit(array(), $form_state); 2281 } 2282 2283 /** 2284 * Generates a unique URL for a user to login and reset their password. 2285 * 2286 * @param object $account 2287 * An object containing the user account. 2288 * 2289 * @return 2290 * A unique URL that provides a one-time log in for the user, from which 2291 * they can change their password. 2292 */ 2293 function user_pass_reset_url($account) { 2294 $timestamp = REQUEST_TIME; 2295 return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE)); 2296 } 2297 2298 /** 2299 * Generates a URL to confirm an account cancellation request. 2300 * 2301 * @param object $account 2302 * The user account object, which must contain at least the following 2303 * properties: 2304 * - uid: The user uid number. 2305 * - pass: The hashed user password string. 2306 * - login: The user login name. 2307 * 2308 * @return 2309 * A unique URL that may be used to confirm the cancellation of the user 2310 * account. 2311 * 2312 * @see user_mail_tokens() 2313 * @see user_cancel_confirm() 2314 */ 2315 function user_cancel_url($account) { 2316 $timestamp = REQUEST_TIME; 2317 return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE)); 2318 } 2319 2320 /** 2321 * Creates a unique hash value for use in time-dependent per-user URLs. 2322 * 2323 * This hash is normally used to build a unique and secure URL that is sent to 2324 * the user by email for purposes such as resetting the user's password. In 2325 * order to validate the URL, the same hash can be generated again, from the 2326 * same information, and compared to the hash value from the URL. The URL 2327 * normally contains both the time stamp and the numeric user ID. The login 2328 * name and hashed password are retrieved from the database as necessary. For a 2329 * usage example, see user_cancel_url() and user_cancel_confirm(). 2330 * 2331 * @param $password 2332 * The hashed user account password value. 2333 * @param $timestamp 2334 * A unix timestamp. 2335 * @param $login 2336 * The user account login name. 2337 * 2338 * @return 2339 * A string that is safe for use in URLs and SQL statements. 2340 */ 2341 function user_pass_rehash($password, $timestamp, $login) { 2342 return drupal_hmac_base64($timestamp . $login, drupal_get_hash_salt() . $password); 2343 } 2344 2345 /** 2346 * Cancel a user account. 2347 * 2348 * Since the user cancellation process needs to be run in a batch, either 2349 * Form API will invoke it, or batch_process() needs to be invoked after calling 2350 * this function and should define the path to redirect to. 2351 * 2352 * @param $edit 2353 * An array of submitted form values. 2354 * @param $uid 2355 * The user ID of the user account to cancel. 2356 * @param $method 2357 * The account cancellation method to use. 2358 * 2359 * @see _user_cancel() 2360 */ 2361 function user_cancel($edit, $uid, $method) { 2362 global $user; 2363 2364 $account = user_load($uid); 2365 2366 if (!$account) { 2367 drupal_set_message(t('The user account %id does not exist.', array('%id' => $uid)), 'error'); 2368 watchdog('user', 'Attempted to cancel non-existing user account: %id.', array('%id' => $uid), WATCHDOG_ERROR); 2369 return; 2370 } 2371 2372 // Initialize batch (to set title). 2373 $batch = array( 2374 'title' => t('Cancelling account'), 2375 'operations' => array(), 2376 ); 2377 batch_set($batch); 2378 2379 // Modules use hook_user_delete() to respond to deletion. 2380 if ($method != 'user_cancel_delete') { 2381 // Allow modules to add further sets to this batch. 2382 module_invoke_all('user_cancel', $edit, $account, $method); 2383 } 2384 2385 // Finish the batch and actually cancel the account. 2386 $batch = array( 2387 'title' => t('Cancelling user account'), 2388 'operations' => array( 2389 array('_user_cancel', array($edit, $account, $method)), 2390 ), 2391 ); 2392 batch_set($batch); 2393 2394 // Batch processing is either handled via Form API or has to be invoked 2395 // manually. 2396 } 2397 2398 /** 2399 * Last batch processing step for cancelling a user account. 2400 * 2401 * Since batch and session API require a valid user account, the actual 2402 * cancellation of a user account needs to happen last. 2403 * 2404 * @see user_cancel() 2405 */ 2406 function _user_cancel($edit, $account, $method) { 2407 global $user; 2408 2409 switch ($method) { 2410 case 'user_cancel_block': 2411 case 'user_cancel_block_unpublish': 2412 default: 2413 // Send account blocked notification if option was checked. 2414 if (!empty($edit['user_cancel_notify'])) { 2415 _user_mail_notify('status_blocked', $account); 2416 } 2417 user_save($account, array('status' => 0)); 2418 drupal_set_message(t('%name has been disabled.', array('%name' => $account->name))); 2419 watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE); 2420 break; 2421 2422 case 'user_cancel_reassign': 2423 case 'user_cancel_delete': 2424 // Send account canceled notification if option was checked. 2425 if (!empty($edit['user_cancel_notify'])) { 2426 _user_mail_notify('status_canceled', $account); 2427 } 2428 user_delete($account->uid); 2429 drupal_set_message(t('%name has been deleted.', array('%name' => $account->name))); 2430 watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE); 2431 break; 2432 } 2433 2434 // After cancelling account, ensure that user is logged out. 2435 if ($account->uid == $user->uid) { 2436 // Destroy the current session, and reset $user to the anonymous user. 2437 session_destroy(); 2438 } 2439 2440 // Clear the cache for anonymous users. 2441 cache_clear_all(); 2442 } 2443 2444 /** 2445 * Delete a user. 2446 * 2447 * @param $uid 2448 * A user ID. 2449 */ 2450 function user_delete($uid) { 2451 user_delete_multiple(array($uid)); 2452 } 2453 2454 /** 2455 * Delete multiple user accounts. 2456 * 2457 * @param $uids 2458 * An array of user IDs. 2459 */ 2460 function user_delete_multiple(array $uids) { 2461 if (!empty($uids)) { 2462 $accounts = user_load_multiple($uids, array()); 2463 2464 $transaction = db_transaction(); 2465 try { 2466 foreach ($accounts as $uid => $account) { 2467 module_invoke_all('user_delete', $account); 2468 module_invoke_all('entity_delete', $account, 'user'); 2469 field_attach_delete('user', $account); 2470 drupal_session_destroy_uid($account->uid); 2471 } 2472 2473 db_delete('users') 2474 ->condition('uid', $uids, 'IN') 2475 ->execute(); 2476 db_delete('users_roles') 2477 ->condition('uid', $uids, 'IN') 2478 ->execute(); 2479 db_delete('authmap') 2480 ->condition('uid', $uids, 'IN') 2481 ->execute(); 2482 } 2483 catch (Exception $e) { 2484 $transaction->rollback(); 2485 watchdog_exception('user', $e); 2486 throw $e; 2487 } 2488 entity_get_controller('user')->resetCache(); 2489 } 2490 } 2491 2492 /** 2493 * Page callback wrapper for user_view(). 2494 */ 2495 function user_view_page($account) { 2496 // An administrator may try to view a non-existent account, 2497 // so we give them a 404 (versus a 403 for non-admins). 2498 return is_object($account) ? user_view($account) : MENU_NOT_FOUND; 2499 } 2500 2501 /** 2502 * Generate an array for rendering the given user. 2503 * 2504 * When viewing a user profile, the $page array contains: 2505 * 2506 * - $page['content']['Profile Category']: 2507 * Profile categories keyed by their human-readable names. 2508 * - $page['content']['Profile Category']['profile_machine_name']: 2509 * Profile fields keyed by their machine-readable names. 2510 * - $page['content']['user_picture']: 2511 * User's rendered picture. 2512 * - $page['content']['summary']: 2513 * Contains the default "History" profile data for a user. 2514 * - $page['content']['#account']: 2515 * The user account of the profile being viewed. 2516 * 2517 * To theme user profiles, copy modules/user/user-profile.tpl.php 2518 * to your theme directory, and edit it as instructed in that file's comments. 2519 * 2520 * @param $account 2521 * A user object. 2522 * @param $view_mode 2523 * View mode, e.g. 'full'. 2524 * @param $langcode 2525 * (optional) A language code to use for rendering. Defaults to the global 2526 * content language of the current request. 2527 * 2528 * @return 2529 * An array as expected by drupal_render(). 2530 */ 2531 function user_view($account, $view_mode = 'full', $langcode = NULL) { 2532 if (!isset($langcode)) { 2533 $langcode = $GLOBALS['language_content']->language; 2534 } 2535 2536 // Retrieve all profile fields and attach to $account->content. 2537 user_build_content($account, $view_mode, $langcode); 2538 2539 $build = $account->content; 2540 // We don't need duplicate rendering info in account->content. 2541 unset($account->content); 2542 2543 $build += array( 2544 '#theme' => 'user_profile', 2545 '#account' => $account, 2546 '#view_mode' => $view_mode, 2547 '#language' => $langcode, 2548 ); 2549 2550 // Allow modules to modify the structured user. 2551 $type = 'user'; 2552 drupal_alter(array('user_view', 'entity_view'), $build, $type); 2553 2554 return $build; 2555 } 2556 2557 /** 2558 * Builds a structured array representing the profile content. 2559 * 2560 * @param $account 2561 * A user object. 2562 * @param $view_mode 2563 * View mode, e.g. 'full'. 2564 * @param $langcode 2565 * (optional) A language code to use for rendering. Defaults to the global 2566 * content language of the current request. 2567 */ 2568 function user_build_content($account, $view_mode = 'full', $langcode = NULL) { 2569 if (!isset($langcode)) { 2570 $langcode = $GLOBALS['language_content']->language; 2571 } 2572 2573 // Remove previously built content, if exists. 2574 $account->content = array(); 2575 2576 // Allow modules to change the view mode. 2577 $context = array( 2578 'entity_type' => 'user', 2579 'entity' => $account, 2580 'langcode' => $langcode, 2581 ); 2582 drupal_alter('entity_view_mode', $view_mode, $context); 2583 2584 // Build fields content. 2585 field_attach_prepare_view('user', array($account->uid => $account), $view_mode, $langcode); 2586 entity_prepare_view('user', array($account->uid => $account), $langcode); 2587 $account->content += field_attach_view('user', $account, $view_mode, $langcode); 2588 2589 // Populate $account->content with a render() array. 2590 module_invoke_all('user_view', $account, $view_mode, $langcode); 2591 module_invoke_all('entity_view', $account, 'user', $view_mode, $langcode); 2592 2593 // Make sure the current view mode is stored if no module has already 2594 // populated the related key. 2595 $account->content += array('#view_mode' => $view_mode); 2596 } 2597 2598 /** 2599 * Implements hook_mail(). 2600 */ 2601 function user_mail($key, &$message, $params) { 2602 $language = $message['language']; 2603 $variables = array('user' => $params['account']); 2604 $message['subject'] .= _user_mail_text($key . '_subject', $language, $variables); 2605 $message['body'][] = _user_mail_text($key . '_body', $language, $variables); 2606 } 2607 2608 /** 2609 * Returns a mail string for a variable name. 2610 * 2611 * Used by user_mail() and the settings forms to retrieve strings. 2612 */ 2613 function _user_mail_text($key, $language = NULL, $variables = array(), $replace = TRUE) { 2614 $langcode = isset($language) ? $language->language : NULL; 2615 2616 if ($admin_setting = variable_get('user_mail_' . $key, FALSE)) { 2617 // An admin setting overrides the default string. 2618 $text = $admin_setting; 2619 } 2620 else { 2621 // No override, return default string. 2622 switch ($key) { 2623 case 'register_no_approval_required_subject': 2624 $text = t('Account details for [user:name] at [site:name]', array(), array('langcode' => $langcode)); 2625 break; 2626 case 'register_no_approval_required_body': 2627 $text = t("[user:name], 2628 2629 Thank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser: 2630 2631 [user:one-time-login-url] 2632 2633 This link can only be used once to log in and will lead you to a page where you can set your password. 2634 2635 After setting your password, you will be able to log in at [site:login-url] in the future using: 2636 2637 username: [user:name] 2638 password: Your password 2639 2640 -- [site:name] team", array(), array('langcode' => $langcode)); 2641 break; 2642 2643 case 'register_admin_created_subject': 2644 $text = t('An administrator created an account for you at [site:name]', array(), array('langcode' => $langcode)); 2645 break; 2646 case 'register_admin_created_body': 2647 $text = t("[user:name], 2648 2649 A site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser: 2650 2651 [user:one-time-login-url] 2652 2653 This link can only be used once to log in and will lead you to a page where you can set your password. 2654 2655 After setting your password, you will be able to log in at [site:login-url] in the future using: 2656 2657 username: [user:name] 2658 password: Your password 2659 2660 -- [site:name] team", array(), array('langcode' => $langcode)); 2661 break; 2662 2663 case 'register_pending_approval_subject': 2664 case 'register_pending_approval_admin_subject': 2665 $text = t('Account details for [user:name] at [site:name] (pending admin approval)', array(), array('langcode' => $langcode)); 2666 break; 2667 case 'register_pending_approval_body': 2668 $text = t("[user:name], 2669 2670 Thank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details. 2671 2672 2673 -- [site:name] team", array(), array('langcode' => $langcode)); 2674 break; 2675 case 'register_pending_approval_admin_body': 2676 $text = t("[user:name] has applied for an account. 2677 2678 [user:edit-url]", array(), array('langcode' => $langcode)); 2679 break; 2680 2681 case 'password_reset_subject': 2682 $text = t('Replacement login information for [user:name] at [site:name]', array(), array('langcode' => $langcode)); 2683 break; 2684 case 'password_reset_body': 2685 $text = t("[user:name], 2686 2687 A request to reset the password for your account has been made at [site:name]. 2688 2689 You may now log in by clicking this link or copying and pasting it to your browser: 2690 2691 [user:one-time-login-url] 2692 2693 This link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used. 2694 2695 -- [site:name] team", array(), array('langcode' => $langcode)); 2696 break; 2697 2698 case 'status_activated_subject': 2699 $text = t('Account details for [user:name] at [site:name] (approved)', array(), array('langcode' => $langcode)); 2700 break; 2701 case 'status_activated_body': 2702 $text = t("[user:name], 2703 2704 Your account at [site:name] has been activated. 2705 2706 You may now log in by clicking this link or copying and pasting it into your browser: 2707 2708 [user:one-time-login-url] 2709 2710 This link can only be used once to log in and will lead you to a page where you can set your password. 2711 2712 After setting your password, you will be able to log in at [site:login-url] in the future using: 2713 2714 username: [user:name] 2715 password: Your password 2716 2717 -- [site:name] team", array(), array('langcode' => $langcode)); 2718 break; 2719 2720 case 'status_blocked_subject': 2721 $text = t('Account details for [user:name] at [site:name] (blocked)', array(), array('langcode' => $langcode)); 2722 break; 2723 case 'status_blocked_body': 2724 $text = t("[user:name], 2725 2726 Your account on [site:name] has been blocked. 2727 2728 -- [site:name] team", array(), array('langcode' => $langcode)); 2729 break; 2730 2731 case 'cancel_confirm_subject': 2732 $text = t('Account cancellation request for [user:name] at [site:name]', array(), array('langcode' => $langcode)); 2733 break; 2734 case 'cancel_confirm_body': 2735 $text = t("[user:name], 2736 2737 A request to cancel your account has been made at [site:name]. 2738 2739 You may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser: 2740 2741 [user:cancel-url] 2742 2743 NOTE: The cancellation of your account is not reversible. 2744 2745 This link expires in one day and nothing will happen if it is not used. 2746 2747 -- [site:name] team", array(), array('langcode' => $langcode)); 2748 break; 2749 2750 case 'status_canceled_subject': 2751 $text = t('Account details for [user:name] at [site:name] (canceled)', array(), array('langcode' => $langcode)); 2752 break; 2753 case 'status_canceled_body': 2754 $text = t("[user:name], 2755 2756 Your account on [site:name] has been canceled. 2757 2758 -- [site:name] team", array(), array('langcode' => $langcode)); 2759 break; 2760 } 2761 } 2762 2763 if ($replace) { 2764 // We do not sanitize the token replacement, since the output of this 2765 // replacement is intended for an e-mail message, not a web browser. 2766 return token_replace($text, $variables, array('language' => $language, 'callback' => 'user_mail_tokens', 'sanitize' => FALSE, 'clear' => TRUE)); 2767 } 2768 2769 return $text; 2770 } 2771 2772 /** 2773 * Token callback to add unsafe tokens for user mails. 2774 * 2775 * This function is used by the token_replace() call at the end of 2776 * _user_mail_text() to set up some additional tokens that can be 2777 * used in email messages generated by user_mail(). 2778 * 2779 * @param $replacements 2780 * An associative array variable containing mappings from token names to 2781 * values (for use with strtr()). 2782 * @param $data 2783 * An associative array of token replacement values. If the 'user' element 2784 * exists, it must contain a user account object with the following 2785 * properties: 2786 * - login: The account login name. 2787 * - pass: The hashed account login password. 2788 * @param $options 2789 * Unused parameter required by the token_replace() function. 2790 */ 2791 function user_mail_tokens(&$replacements, $data, $options) { 2792 if (isset($data['user'])) { 2793 $replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user']); 2794 $replacements['[user:cancel-url]'] = user_cancel_url($data['user']); 2795 } 2796 } 2797 2798 /*** Administrative features ***********************************************/ 2799 2800 /** 2801 * Retrieve an array of roles matching specified conditions. 2802 * 2803 * @param $membersonly 2804 * Set this to TRUE to exclude the 'anonymous' role. 2805 * @param $permission 2806 * A string containing a permission. If set, only roles containing that 2807 * permission are returned. 2808 * 2809 * @return 2810 * An associative array with the role id as the key and the role name as 2811 * value. 2812 */ 2813 function user_roles($membersonly = FALSE, $permission = NULL) { 2814 $query = db_select('role', 'r'); 2815 $query->addTag('translatable'); 2816 $query->fields('r', array('rid', 'name')); 2817 $query->orderBy('weight'); 2818 $query->orderBy('name'); 2819 if (!empty($permission)) { 2820 $query->innerJoin('role_permission', 'p', 'r.rid = p.rid'); 2821 $query->condition('p.permission', $permission); 2822 } 2823 $result = $query->execute(); 2824 2825 $roles = array(); 2826 foreach ($result as $role) { 2827 switch ($role->rid) { 2828 // We only translate the built in role names 2829 case DRUPAL_ANONYMOUS_RID: 2830 if (!$membersonly) { 2831 $roles[$role->rid] = t($role->name); 2832 } 2833 break; 2834 case DRUPAL_AUTHENTICATED_RID: 2835 $roles[$role->rid] = t($role->name); 2836 break; 2837 default: 2838 $roles[$role->rid] = $role->name; 2839 } 2840 } 2841 2842 return $roles; 2843 } 2844 2845 /** 2846 * Fetches a user role by role ID. 2847 * 2848 * @param $rid 2849 * An integer representing the role ID. 2850 * 2851 * @return 2852 * A fully-loaded role object if a role with the given ID exists, or FALSE 2853 * otherwise. 2854 * 2855 * @see user_role_load_by_name() 2856 */ 2857 function user_role_load($rid) { 2858 return db_select('role', 'r') 2859 ->fields('r') 2860 ->condition('rid', $rid) 2861 ->execute() 2862 ->fetchObject(); 2863 } 2864 2865 /** 2866 * Fetches a user role by role name. 2867 * 2868 * @param $role_name 2869 * A string representing the role name. 2870 * 2871 * @return 2872 * A fully-loaded role object if a role with the given name exists, or FALSE 2873 * otherwise. 2874 * 2875 * @see user_role_load() 2876 */ 2877 function user_role_load_by_name($role_name) { 2878 return db_select('role', 'r') 2879 ->fields('r') 2880 ->condition('name', $role_name) 2881 ->execute() 2882 ->fetchObject(); 2883 } 2884 2885 /** 2886 * Save a user role to the database. 2887 * 2888 * @param $role 2889 * A role object to modify or add. If $role->rid is not specified, a new 2890 * role will be created. 2891 * @return 2892 * Status constant indicating if role was created or updated. 2893 * Failure to write the user role record will return FALSE. Otherwise. 2894 * SAVED_NEW or SAVED_UPDATED is returned depending on the operation 2895 * performed. 2896 */ 2897 function user_role_save($role) { 2898 if ($role->name) { 2899 // Prevent leading and trailing spaces in role names. 2900 $role->name = trim($role->name); 2901 } 2902 if (!isset($role->weight)) { 2903 // Set a role weight to make this new role last. 2904 $query = db_select('role'); 2905 $query->addExpression('MAX(weight)'); 2906 $role->weight = $query->execute()->fetchField() + 1; 2907 } 2908 2909 // Let modules modify the user role before it is saved to the database. 2910 module_invoke_all('user_role_presave', $role); 2911 2912 if (!empty($role->rid) && $role->name) { 2913 $status = drupal_write_record('role', $role, 'rid'); 2914 module_invoke_all('user_role_update', $role); 2915 } 2916 else { 2917 $status = drupal_write_record('role', $role); 2918 module_invoke_all('user_role_insert', $role); 2919 } 2920 2921 // Clear the user access cache. 2922 drupal_static_reset('user_access'); 2923 drupal_static_reset('user_role_permissions'); 2924 2925 return $status; 2926 } 2927 2928 /** 2929 * Delete a user role from database. 2930 * 2931 * @param $role 2932 * A string with the role name, or an integer with the role ID. 2933 */ 2934 function user_role_delete($role) { 2935 if (is_int($role)) { 2936 $role = user_role_load($role); 2937 } 2938 else { 2939 $role = user_role_load_by_name($role); 2940 } 2941 2942 db_delete('role') 2943 ->condition('rid', $role->rid) 2944 ->execute(); 2945 db_delete('role_permission') 2946 ->condition('rid', $role->rid) 2947 ->execute(); 2948 // Update the users who have this role set: 2949 db_delete('users_roles') 2950 ->condition('rid', $role->rid) 2951 ->execute(); 2952 2953 module_invoke_all('user_role_delete', $role); 2954 2955 // Clear the user access cache. 2956 drupal_static_reset('user_access'); 2957 drupal_static_reset('user_role_permissions'); 2958 } 2959 2960 /** 2961 * Menu access callback for user role editing. 2962 */ 2963 function user_role_edit_access($role) { 2964 // Prevent the system-defined roles from being altered or removed. 2965 if ($role->rid == DRUPAL_ANONYMOUS_RID || $role->rid == DRUPAL_AUTHENTICATED_RID) { 2966 return FALSE; 2967 } 2968 2969 return user_access('administer permissions'); 2970 } 2971 2972 /** 2973 * Determine the modules that permissions belong to. 2974 * 2975 * @return 2976 * An associative array in the format $permission => $module. 2977 */ 2978 function user_permission_get_modules() { 2979 $permissions = array(); 2980 foreach (module_implements('permission') as $module) { 2981 $perms = module_invoke($module, 'permission'); 2982 foreach ($perms as $key => $value) { 2983 $permissions[$key] = $module; 2984 } 2985 } 2986 return $permissions; 2987 } 2988 2989 /** 2990 * Change permissions for a user role. 2991 * 2992 * This function may be used to grant and revoke multiple permissions at once. 2993 * For example, when a form exposes checkboxes to configure permissions for a 2994 * role, the form submit handler may directly pass the submitted values for the 2995 * checkboxes form element to this function. 2996 * 2997 * @param $rid 2998 * The ID of a user role to alter. 2999 * @param $permissions 3000 * An associative array, where the key holds the permission name and the value 3001 * determines whether to grant or revoke that permission. Any value that 3002 * evaluates to TRUE will cause the permission to be granted. Any value that 3003 * evaluates to FALSE will cause the permission to be revoked. 3004 * @code 3005 * array( 3006 * 'administer nodes' => 0, // Revoke 'administer nodes' 3007 * 'administer blocks' => FALSE, // Revoke 'administer blocks' 3008 * 'access user profiles' => 1, // Grant 'access user profiles' 3009 * 'access content' => TRUE, // Grant 'access content' 3010 * 'access comments' => 'access comments', // Grant 'access comments' 3011 * ) 3012 * @endcode 3013 * Existing permissions are not changed, unless specified in $permissions. 3014 * 3015 * @see user_role_grant_permissions() 3016 * @see user_role_revoke_permissions() 3017 */ 3018 function user_role_change_permissions($rid, array $permissions = array()) { 3019 // Grant new permissions for the role. 3020 $grant = array_filter($permissions); 3021 if (!empty($grant)) { 3022 user_role_grant_permissions($rid, array_keys($grant)); 3023 } 3024 // Revoke permissions for the role. 3025 $revoke = array_diff_assoc($permissions, $grant); 3026 if (!empty($revoke)) { 3027 user_role_revoke_permissions($rid, array_keys($revoke)); 3028 } 3029 } 3030 3031 /** 3032 * Grant permissions to a user role. 3033 * 3034 * @param $rid 3035 * The ID of a user role to alter. 3036 * @param $permissions 3037 * A list of permission names to grant. 3038 * 3039 * @see user_role_change_permissions() 3040 * @see user_role_revoke_permissions() 3041 */ 3042 function user_role_grant_permissions($rid, array $permissions = array()) { 3043 $modules = user_permission_get_modules(); 3044 // Grant new permissions for the role. 3045 foreach ($permissions as $name) { 3046 db_merge('role_permission') 3047 ->key(array( 3048 'rid' => $rid, 3049 'permission' => $name, 3050 )) 3051 ->fields(array( 3052 'module' => $modules[$name], 3053 )) 3054 ->execute(); 3055 } 3056 3057 // Clear the user access cache. 3058 drupal_static_reset('user_access'); 3059 drupal_static_reset('user_role_permissions'); 3060 } 3061 3062 /** 3063 * Revoke permissions from a user role. 3064 * 3065 * @param $rid 3066 * The ID of a user role to alter. 3067 * @param $permissions 3068 * A list of permission names to revoke. 3069 * 3070 * @see user_role_change_permissions() 3071 * @see user_role_grant_permissions() 3072 */ 3073 function user_role_revoke_permissions($rid, array $permissions = array()) { 3074 // Revoke permissions for the role. 3075 db_delete('role_permission') 3076 ->condition('rid', $rid) 3077 ->condition('permission', $permissions, 'IN') 3078 ->execute(); 3079 3080 // Clear the user access cache. 3081 drupal_static_reset('user_access'); 3082 drupal_static_reset('user_role_permissions'); 3083 } 3084 3085 /** 3086 * Implements hook_user_operations(). 3087 */ 3088 function user_user_operations($form = array(), $form_state = array()) { 3089 $operations = array( 3090 'unblock' => array( 3091 'label' => t('Unblock the selected users'), 3092 'callback' => 'user_user_operations_unblock', 3093 ), 3094 'block' => array( 3095 'label' => t('Block the selected users'), 3096 'callback' => 'user_user_operations_block', 3097 ), 3098 'cancel' => array( 3099 'label' => t('Cancel the selected user accounts'), 3100 ), 3101 ); 3102 3103 if (user_access('administer permissions')) { 3104 $roles = user_roles(TRUE); 3105 unset($roles[DRUPAL_AUTHENTICATED_RID]); // Can't edit authenticated role. 3106 3107 $add_roles = array(); 3108 foreach ($roles as $key => $value) { 3109 $add_roles['add_role-' . $key] = $value; 3110 } 3111 3112 $remove_roles = array(); 3113 foreach ($roles as $key => $value) { 3114 $remove_roles['remove_role-' . $key] = $value; 3115 } 3116 3117 if (count($roles)) { 3118 $role_operations = array( 3119 t('Add a role to the selected users') => array( 3120 'label' => $add_roles, 3121 ), 3122 t('Remove a role from the selected users') => array( 3123 'label' => $remove_roles, 3124 ), 3125 ); 3126 3127 $operations += $role_operations; 3128 } 3129 } 3130 3131 // If the form has been posted, we need to insert the proper data for 3132 // role editing if necessary. 3133 if (!empty($form_state['submitted'])) { 3134 $operation_rid = explode('-', $form_state['values']['operation']); 3135 $operation = $operation_rid[0]; 3136 if ($operation == 'add_role' || $operation == 'remove_role') { 3137 $rid = $operation_rid[1]; 3138 if (user_access('administer permissions')) { 3139 $operations[$form_state['values']['operation']] = array( 3140 'callback' => 'user_multiple_role_edit', 3141 'callback arguments' => array($operation, $rid), 3142 ); 3143 } 3144 else { 3145 watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING); 3146 return; 3147 } 3148 } 3149 } 3150 3151 return $operations; 3152 } 3153 3154 /** 3155 * Callback function for admin mass unblocking users. 3156 */ 3157 function user_user_operations_unblock($accounts) { 3158 $accounts = user_load_multiple($accounts); 3159 foreach ($accounts as $account) { 3160 // Skip unblocking user if they are already unblocked. 3161 if ($account !== FALSE && $account->status == 0) { 3162 user_save($account, array('status' => 1)); 3163 } 3164 } 3165 } 3166 3167 /** 3168 * Callback function for admin mass blocking users. 3169 */ 3170 function user_user_operations_block($accounts) { 3171 $accounts = user_load_multiple($accounts); 3172 foreach ($accounts as $account) { 3173 // Skip blocking user if they are already blocked. 3174 if ($account !== FALSE && $account->status == 1) { 3175 // For efficiency manually save the original account before applying any 3176 // changes. 3177 $account->original = clone $account; 3178 user_save($account, array('status' => 0)); 3179 } 3180 } 3181 } 3182 3183 /** 3184 * Callback function for admin mass adding/deleting a user role. 3185 */ 3186 function user_multiple_role_edit($accounts, $operation, $rid) { 3187 // The role name is not necessary as user_save() will reload the user 3188 // object, but some modules' hook_user() may look at this first. 3189 $role_name = db_query('SELECT name FROM {role} WHERE rid = :rid', array(':rid' => $rid))->fetchField(); 3190 3191 switch ($operation) { 3192 case 'add_role': 3193 $accounts = user_load_multiple($accounts); 3194 foreach ($accounts as $account) { 3195 // Skip adding the role to the user if they already have it. 3196 if ($account !== FALSE && !isset($account->roles[$rid])) { 3197 $roles = $account->roles + array($rid => $role_name); 3198 // For efficiency manually save the original account before applying 3199 // any changes. 3200 $account->original = clone $account; 3201 user_save($account, array('roles' => $roles)); 3202 } 3203 } 3204 break; 3205 case 'remove_role': 3206 $accounts = user_load_multiple($accounts); 3207 foreach ($accounts as $account) { 3208 // Skip removing the role from the user if they already don't have it. 3209 if ($account !== FALSE && isset($account->roles[$rid])) { 3210 $roles = array_diff($account->roles, array($rid => $role_name)); 3211 // For efficiency manually save the original account before applying 3212 // any changes. 3213 $account->original = clone $account; 3214 user_save($account, array('roles' => $roles)); 3215 } 3216 } 3217 break; 3218 } 3219 } 3220 3221 function user_multiple_cancel_confirm($form, &$form_state) { 3222 $edit = $form_state['input']; 3223 3224 $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE); 3225 $accounts = user_load_multiple(array_keys(array_filter($edit['accounts']))); 3226 foreach ($accounts as $uid => $account) { 3227 // Prevent user 1 from being canceled. 3228 if ($uid <= 1) { 3229 continue; 3230 } 3231 $form['accounts'][$uid] = array( 3232 '#type' => 'hidden', 3233 '#value' => $uid, 3234 '#prefix' => '<li>', 3235 '#suffix' => check_plain($account->name) . "</li>\n", 3236 ); 3237 } 3238 3239 // Output a notice that user 1 cannot be canceled. 3240 if (isset($accounts[1])) { 3241 $redirect = (count($accounts) == 1); 3242 $message = t('The user account %name cannot be cancelled.', array('%name' => $accounts[1]->name)); 3243 drupal_set_message($message, $redirect ? 'error' : 'warning'); 3244 // If only user 1 was selected, redirect to the overview. 3245 if ($redirect) { 3246 drupal_goto('admin/people'); 3247 } 3248 } 3249 3250 $form['operation'] = array('#type' => 'hidden', '#value' => 'cancel'); 3251 3252 module_load_include('inc', 'user', 'user.pages'); 3253 $form['user_cancel_method'] = array( 3254 '#type' => 'item', 3255 '#title' => t('When cancelling these accounts'), 3256 ); 3257 $form['user_cancel_method'] += user_cancel_methods(); 3258 // Remove method descriptions. 3259 foreach (element_children($form['user_cancel_method']) as $element) { 3260 unset($form['user_cancel_method'][$element]['#description']); 3261 } 3262 3263 // Allow to send the account cancellation confirmation mail. 3264 $form['user_cancel_confirm'] = array( 3265 '#type' => 'checkbox', 3266 '#title' => t('Require e-mail confirmation to cancel account.'), 3267 '#default_value' => FALSE, 3268 '#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'), 3269 ); 3270 // Also allow to send account canceled notification mail, if enabled. 3271 $form['user_cancel_notify'] = array( 3272 '#type' => 'checkbox', 3273 '#title' => t('Notify user when account is canceled.'), 3274 '#default_value' => FALSE, 3275 '#access' => variable_get('user_mail_status_canceled_notify', FALSE), 3276 '#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'), 3277 ); 3278 3279 return confirm_form($form, 3280 t('Are you sure you want to cancel these user accounts?'), 3281 'admin/people', t('This action cannot be undone.'), 3282 t('Cancel accounts'), t('Cancel')); 3283 } 3284 3285 /** 3286 * Submit handler for mass-account cancellation form. 3287 * 3288 * @see user_multiple_cancel_confirm() 3289 * @see user_cancel_confirm_form_submit() 3290 */ 3291 function user_multiple_cancel_confirm_submit($form, &$form_state) { 3292 global $user; 3293 3294 if ($form_state['values']['confirm']) { 3295 foreach ($form_state['values']['accounts'] as $uid => $value) { 3296 // Prevent programmatic form submissions from cancelling user 1. 3297 if ($uid <= 1) { 3298 continue; 3299 } 3300 // Prevent user administrators from deleting themselves without confirmation. 3301 if ($uid == $user->uid) { 3302 $admin_form_state = $form_state; 3303 unset($admin_form_state['values']['user_cancel_confirm']); 3304 $admin_form_state['values']['_account'] = $user; 3305 user_cancel_confirm_form_submit(array(), $admin_form_state); 3306 } 3307 else { 3308 user_cancel($form_state['values'], $uid, $form_state['values']['user_cancel_method']); 3309 } 3310 } 3311 } 3312 $form_state['redirect'] = 'admin/people'; 3313 } 3314 3315 /** 3316 * Retrieve a list of all user setting/information categories and sort them by weight. 3317 */ 3318 function _user_categories() { 3319 $categories = module_invoke_all('user_categories'); 3320 usort($categories, '_user_sort'); 3321 3322 return $categories; 3323 } 3324 3325 function _user_sort($a, $b) { 3326 $a = (array) $a + array('weight' => 0, 'title' => ''); 3327 $b = (array) $b + array('weight' => 0, 'title' => ''); 3328 return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1)); 3329 } 3330 3331 /** 3332 * List user administration filters that can be applied. 3333 */ 3334 function user_filters() { 3335 // Regular filters 3336 $filters = array(); 3337 $roles = user_roles(TRUE); 3338 unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role. 3339 if (count($roles)) { 3340 $filters['role'] = array( 3341 'title' => t('role'), 3342 'field' => 'ur.rid', 3343 'options' => array( 3344 '[any]' => t('any'), 3345 ) + $roles, 3346 ); 3347 } 3348 3349 $options = array(); 3350 foreach (module_implements('permission') as $module) { 3351 $function = $module . '_permission'; 3352 if ($permissions = $function('permission')) { 3353 asort($permissions); 3354 foreach ($permissions as $permission => $description) { 3355 $options[t('@module module', array('@module' => $module))][$permission] = t($permission); 3356 } 3357 } 3358 } 3359 ksort($options); 3360 $filters['permission'] = array( 3361 'title' => t('permission'), 3362 'options' => array( 3363 '[any]' => t('any'), 3364 ) + $options, 3365 ); 3366 3367 $filters['status'] = array( 3368 'title' => t('status'), 3369 'field' => 'u.status', 3370 'options' => array( 3371 '[any]' => t('any'), 3372 1 => t('active'), 3373 0 => t('blocked'), 3374 ), 3375 ); 3376 return $filters; 3377 } 3378 3379 /** 3380 * Extends a query object for user administration filters based on session. 3381 * 3382 * @param $query 3383 * Query object that should be filtered. 3384 */ 3385 function user_build_filter_query(SelectQuery $query) { 3386 $filters = user_filters(); 3387 // Extend Query with filter conditions. 3388 foreach (isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array() as $filter) { 3389 list($key, $value) = $filter; 3390 // This checks to see if this permission filter is an enabled permission for 3391 // the authenticated role. If so, then all users would be listed, and we can 3392 // skip adding it to the filter query. 3393 if ($key == 'permission') { 3394 $account = new stdClass(); 3395 $account->uid = 'user_filter'; 3396 $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1); 3397 if (user_access($value, $account)) { 3398 continue; 3399 } 3400 $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); 3401 $permission_alias = $query->join('role_permission', 'p', $users_roles_alias . '.rid = %alias.rid'); 3402 $query->condition($permission_alias . '.permission', $value); 3403 } 3404 elseif ($key == 'role') { 3405 $users_roles_alias = $query->join('users_roles', 'ur', '%alias.uid = u.uid'); 3406 $query->condition($users_roles_alias . '.rid' , $value); 3407 } 3408 else { 3409 $query->condition($filters[$key]['field'], $value); 3410 } 3411 } 3412 } 3413 3414 /** 3415 * Implements hook_comment_view(). 3416 */ 3417 function user_comment_view($comment) { 3418 if (variable_get('user_signatures', 0) && !empty($comment->signature)) { 3419 // @todo This alters and replaces the original object value, so a 3420 // hypothetical process of loading, viewing, and saving will hijack the 3421 // stored data. Consider renaming to $comment->signature_safe or similar 3422 // here and elsewhere in Drupal 8. 3423 $comment->signature = check_markup($comment->signature, $comment->signature_format, '', TRUE); 3424 } 3425 else { 3426 $comment->signature = ''; 3427 } 3428 } 3429 3430 /** 3431 * Returns HTML for a user signature. 3432 * 3433 * @param $variables 3434 * An associative array containing: 3435 * - signature: The user's signature. 3436 * 3437 * @ingroup themeable 3438 */ 3439 function theme_user_signature($variables) { 3440 $signature = $variables['signature']; 3441 $output = ''; 3442 3443 if ($signature) { 3444 $output .= '<div class="clear">'; 3445 $output .= '<div>—</div>'; 3446 $output .= $signature; 3447 $output .= '</div>'; 3448 } 3449 3450 return $output; 3451 } 3452 3453 /** 3454 * Get the language object preferred by the user. This user preference can 3455 * be set on the user account editing page, and is only available if there 3456 * are more than one languages enabled on the site. If the user did not 3457 * choose a preferred language, or is the anonymous user, the $default 3458 * value, or if it is not set, the site default language will be returned. 3459 * 3460 * @param $account 3461 * User account to look up language for. 3462 * @param $default 3463 * Optional default language object to return if the account 3464 * has no valid language. 3465 */ 3466 function user_preferred_language($account, $default = NULL) { 3467 $language_list = language_list(); 3468 if (!empty($account->language) && isset($language_list[$account->language])) { 3469 return $language_list[$account->language]; 3470 } 3471 else { 3472 return $default ? $default : language_default(); 3473 } 3474 } 3475 3476 /** 3477 * Conditionally create and send a notification email when a certain 3478 * operation happens on the given user account. 3479 * 3480 * @see user_mail_tokens() 3481 * @see drupal_mail() 3482 * 3483 * @param $op 3484 * The operation being performed on the account. Possible values: 3485 * - 'register_admin_created': Welcome message for user created by the admin. 3486 * - 'register_no_approval_required': Welcome message when user 3487 * self-registers. 3488 * - 'register_pending_approval': Welcome message, user pending admin 3489 * approval. 3490 * - 'password_reset': Password recovery request. 3491 * - 'status_activated': Account activated. 3492 * - 'status_blocked': Account blocked. 3493 * - 'cancel_confirm': Account cancellation request. 3494 * - 'status_canceled': Account canceled. 3495 * 3496 * @param $account 3497 * The user object of the account being notified. Must contain at 3498 * least the fields 'uid', 'name', and 'mail'. 3499 * @param $language 3500 * Optional language to use for the notification, overriding account language. 3501 * 3502 * @return 3503 * The return value from drupal_mail_system()->mail(), if ends up being 3504 * called. 3505 */ 3506 function _user_mail_notify($op, $account, $language = NULL) { 3507 // By default, we always notify except for canceled and blocked. 3508 $default_notify = ($op != 'status_canceled' && $op != 'status_blocked'); 3509 $notify = variable_get('user_mail_' . $op . '_notify', $default_notify); 3510 if ($notify) { 3511 $params['account'] = $account; 3512 $language = $language ? $language : user_preferred_language($account); 3513 $mail = drupal_mail('user', $op, $account->mail, $language, $params); 3514 if ($op == 'register_pending_approval') { 3515 // If a user registered requiring admin approval, notify the admin, too. 3516 // We use the site default language for this. 3517 drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params); 3518 } 3519 } 3520 return empty($mail) ? NULL : $mail['result']; 3521 } 3522 3523 /** 3524 * Form element process handler for client-side password validation. 3525 * 3526 * This #process handler is automatically invoked for 'password_confirm' form 3527 * elements to add the JavaScript and string translations for dynamic password 3528 * validation. 3529 * 3530 * @see system_element_info() 3531 */ 3532 function user_form_process_password_confirm($element) { 3533 global $user; 3534 3535 $js_settings = array( 3536 'password' => array( 3537 'strengthTitle' => t('Password strength:'), 3538 'hasWeaknesses' => t('To make your password stronger:'), 3539 'tooShort' => t('Make it at least 6 characters'), 3540 'addLowerCase' => t('Add lowercase letters'), 3541 'addUpperCase' => t('Add uppercase letters'), 3542 'addNumbers' => t('Add numbers'), 3543 'addPunctuation' => t('Add punctuation'), 3544 'sameAsUsername' => t('Make it different from your username'), 3545 'confirmSuccess' => t('yes'), 3546 'confirmFailure' => t('no'), 3547 'weak' => t('Weak'), 3548 'fair' => t('Fair'), 3549 'good' => t('Good'), 3550 'strong' => t('Strong'), 3551 'confirmTitle' => t('Passwords match:'), 3552 'username' => (isset($user->name) ? $user->name : ''), 3553 ), 3554 ); 3555 3556 $element['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.js'; 3557 // Ensure settings are only added once per page. 3558 static $already_added = FALSE; 3559 if (!$already_added) { 3560 $already_added = TRUE; 3561 $element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting'); 3562 } 3563 3564 return $element; 3565 } 3566 3567 /** 3568 * Implements hook_node_load(). 3569 */ 3570 function user_node_load($nodes, $types) { 3571 // Build an array of all uids for node authors, keyed by nid. 3572 $uids = array(); 3573 foreach ($nodes as $nid => $node) { 3574 $uids[$nid] = $node->uid; 3575 } 3576 3577 // Fetch name, picture, and data for these users. 3578 $user_fields = db_query("SELECT uid, name, picture, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid'); 3579 3580 // Add these values back into the node objects. 3581 foreach ($uids as $nid => $uid) { 3582 $nodes[$nid]->name = $user_fields[$uid]->name; 3583 $nodes[$nid]->picture = $user_fields[$uid]->picture; 3584 $nodes[$nid]->data = $user_fields[$uid]->data; 3585 } 3586 } 3587 3588 /** 3589 * Implements hook_image_style_delete(). 3590 */ 3591 function user_image_style_delete($style) { 3592 // If a style is deleted, update the variables. 3593 // Administrators choose a replacement style when deleting. 3594 user_image_style_save($style); 3595 } 3596 3597 /** 3598 * Implements hook_image_style_save(). 3599 */ 3600 function user_image_style_save($style) { 3601 // If a style is renamed, update the variables that use it. 3602 if (isset($style['old_name']) && $style['old_name'] == variable_get('user_picture_style', '')) { 3603 variable_set('user_picture_style', $style['name']); 3604 } 3605 } 3606 3607 /** 3608 * Implements hook_action_info(). 3609 */ 3610 function user_action_info() { 3611 return array( 3612 'user_block_user_action' => array( 3613 'label' => t('Block current user'), 3614 'type' => 'user', 3615 'configurable' => FALSE, 3616 'triggers' => array('any'), 3617 ), 3618 ); 3619 } 3620 3621 /** 3622 * Blocks the current user. 3623 * 3624 * @ingroup actions 3625 */ 3626 function user_block_user_action(&$entity, $context = array()) { 3627 // First priority: If there is a $entity->uid, block that user. 3628 // This is most likely a user object or the author if a node or comment. 3629 if (isset($entity->uid)) { 3630 $uid = $entity->uid; 3631 } 3632 elseif (isset($context['uid'])) { 3633 $uid = $context['uid']; 3634 } 3635 // If neither of those are valid, then block the current user. 3636 else { 3637 $uid = $GLOBALS['user']->uid; 3638 } 3639 $account = user_load($uid); 3640 $account = user_save($account, array('status' => 0)); 3641 watchdog('action', 'Blocked user %name.', array('%name' => $account->name)); 3642 } 3643 3644 /** 3645 * Implements hook_form_FORM_ID_alter(). 3646 * 3647 * Add a checkbox for the 'user_register_form' instance settings on the 'Edit 3648 * field instance' form. 3649 */ 3650 function user_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) { 3651 $instance = $form['#instance']; 3652 3653 if ($instance['entity_type'] == 'user') { 3654 $form['instance']['settings']['user_register_form'] = array( 3655 '#type' => 'checkbox', 3656 '#title' => t('Display on user registration form.'), 3657 '#description' => t("This is compulsory for 'required' fields."), 3658 // Field instances created in D7 beta releases before the setting was 3659 // introduced might be set as 'required' and 'not shown on user_register 3660 // form'. We make sure the checkbox comes as 'checked' for those. 3661 '#default_value' => $instance['settings']['user_register_form'] || $instance['required'], 3662 // Display just below the 'required' checkbox. 3663 '#weight' => $form['instance']['required']['#weight'] + .1, 3664 // Disabled when the 'required' checkbox is checked. 3665 '#states' => array( 3666 'enabled' => array('input[name="instance[required]"]' => array('checked' => FALSE)), 3667 ), 3668 // Checked when the 'required' checkbox is checked. This is done through 3669 // a custom behavior, since the #states system would also synchronize on 3670 // uncheck. 3671 '#attached' => array( 3672 'js' => array(drupal_get_path('module', 'user') . '/user.js'), 3673 ), 3674 ); 3675 3676 array_unshift($form['#submit'], 'user_form_field_ui_field_edit_form_submit'); 3677 } 3678 } 3679 3680 /** 3681 * Additional submit handler for the 'Edit field instance' form. 3682 * 3683 * Make sure the 'user_register_form' setting is set for required fields. 3684 */ 3685 function user_form_field_ui_field_edit_form_submit($form, &$form_state) { 3686 $instance = $form_state['values']['instance']; 3687 3688 if (!empty($instance['required'])) { 3689 form_set_value($form['instance']['settings']['user_register_form'], 1, $form_state); 3690 } 3691 } 3692 3693 /** 3694 * Form builder; the user registration form. 3695 * 3696 * @ingroup forms 3697 * @see user_account_form() 3698 * @see user_account_form_validate() 3699 * @see user_register_submit() 3700 */ 3701 function user_register_form($form, &$form_state) { 3702 global $user; 3703 3704 $admin = user_access('administer users'); 3705 3706 // Pass access information to the submit handler. Running an access check 3707 // inside the submit function interferes with form processing and breaks 3708 // hook_form_alter(). 3709 $form['administer_users'] = array( 3710 '#type' => 'value', 3711 '#value' => $admin, 3712 ); 3713 3714 // If we aren't admin but already logged on, go to the user page instead. 3715 if (!$admin && $user->uid) { 3716 drupal_goto('user/' . $user->uid); 3717 } 3718 3719 $form['#user'] = drupal_anonymous_user(); 3720 $form['#user_category'] = 'register'; 3721 3722 $form['#attached']['library'][] = array('system', 'jquery.cookie'); 3723 $form['#attributes']['class'][] = 'user-info-from-cookie'; 3724 3725 // Start with the default user account fields. 3726 user_account_form($form, $form_state); 3727 3728 // Attach field widgets, and hide the ones where the 'user_register_form' 3729 // setting is not on. 3730 $langcode = entity_language('user', $form['#user']); 3731 field_attach_form('user', $form['#user'], $form, $form_state, $langcode); 3732 foreach (field_info_instances('user', 'user') as $field_name => $instance) { 3733 if (empty($instance['settings']['user_register_form'])) { 3734 $form[$field_name]['#access'] = FALSE; 3735 } 3736 } 3737 3738 if ($admin) { 3739 // Redirect back to page which initiated the create request; 3740 // usually admin/people/create. 3741 $form_state['redirect'] = $_GET['q']; 3742 } 3743 3744 $form['actions'] = array('#type' => 'actions'); 3745 $form['actions']['submit'] = array( 3746 '#type' => 'submit', 3747 '#value' => t('Create new account'), 3748 ); 3749 3750 $form['#validate'][] = 'user_register_validate'; 3751 // Add the final user registration form submit handler. 3752 $form['#submit'][] = 'user_register_submit'; 3753 3754 return $form; 3755 } 3756 3757 /** 3758 * Validation function for the user registration form. 3759 */ 3760 function user_register_validate($form, &$form_state) { 3761 entity_form_field_validate('user', $form, $form_state); 3762 } 3763 3764 /** 3765 * Submit handler for the user registration form. 3766 * 3767 * This function is shared by the installation form and the normal registration form, 3768 * which is why it can't be in the user.pages.inc file. 3769 * 3770 * @see user_register_form() 3771 */ 3772 function user_register_submit($form, &$form_state) { 3773 $admin = $form_state['values']['administer_users']; 3774 3775 if (!variable_get('user_email_verification', TRUE) || $admin) { 3776 $pass = $form_state['values']['pass']; 3777 } 3778 else { 3779 $pass = user_password(); 3780 } 3781 $notify = !empty($form_state['values']['notify']); 3782 3783 // Remove unneeded values. 3784 form_state_values_clean($form_state); 3785 3786 $form_state['values']['pass'] = $pass; 3787 $form_state['values']['init'] = $form_state['values']['mail']; 3788 3789 $account = $form['#user']; 3790 3791 entity_form_submit_build_entity('user', $account, $form, $form_state); 3792 3793 // Populate $edit with the properties of $account, which have been edited on 3794 // this form by taking over all values, which appear in the form values too. 3795 $edit = array_intersect_key((array) $account, $form_state['values']); 3796 $account = user_save($account, $edit); 3797 3798 // Terminate if an error occurred during user_save(). 3799 if (!$account) { 3800 drupal_set_message(t("Error saving user account."), 'error'); 3801 $form_state['redirect'] = ''; 3802 return; 3803 } 3804 $form_state['user'] = $account; 3805 $form_state['values']['uid'] = $account->uid; 3806 3807 watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit')); 3808 3809 // Add plain text password into user account to generate mail tokens. 3810 $account->password = $pass; 3811 3812 // New administrative account without notification. 3813 $uri = entity_uri('user', $account); 3814 if ($admin && !$notify) { 3815 drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name))); 3816 } 3817 // No e-mail verification required; log in user immediately. 3818 elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) { 3819 _user_mail_notify('register_no_approval_required', $account); 3820 $form_state['uid'] = $account->uid; 3821 user_login_submit(array(), $form_state); 3822 drupal_set_message(t('Registration successful. You are now logged in.')); 3823 $form_state['redirect'] = ''; 3824 } 3825 // No administrator approval required. 3826 elseif ($account->status || $notify) { 3827 $op = $notify ? 'register_admin_created' : 'register_no_approval_required'; 3828 _user_mail_notify($op, $account); 3829 if ($notify) { 3830 drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name))); 3831 } 3832 else { 3833 drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.')); 3834 $form_state['redirect'] = ''; 3835 } 3836 } 3837 // Administrator approval required. 3838 else { 3839 _user_mail_notify('register_pending_approval', $account); 3840 drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.<br />In the meantime, a welcome message with further instructions has been sent to your e-mail address.')); 3841 $form_state['redirect'] = ''; 3842 } 3843 } 3844 3845 /** 3846 * Implements hook_modules_installed(). 3847 */ 3848 function user_modules_installed($modules) { 3849 // Assign all available permissions to the administrator role. 3850 $rid = variable_get('user_admin_role', 0); 3851 if ($rid) { 3852 $permissions = array(); 3853 foreach ($modules as $module) { 3854 if ($module_permissions = module_invoke($module, 'permission')) { 3855 $permissions = array_merge($permissions, array_keys($module_permissions)); 3856 } 3857 } 3858 if (!empty($permissions)) { 3859 user_role_grant_permissions($rid, $permissions); 3860 } 3861 } 3862 } 3863 3864 /** 3865 * Implements hook_modules_uninstalled(). 3866 */ 3867 function user_modules_uninstalled($modules) { 3868 db_delete('role_permission') 3869 ->condition('module', $modules, 'IN') 3870 ->execute(); 3871 } 3872 3873 /** 3874 * Helper function to rewrite the destination to avoid redirecting to login page after login. 3875 * 3876 * Third-party authentication modules may use this function to determine the 3877 * proper destination after a user has been properly logged in. 3878 */ 3879 function user_login_destination() { 3880 $destination = drupal_get_destination(); 3881 if ($destination['destination'] == 'user/login') { 3882 $destination['destination'] = 'user'; 3883 } 3884 return $destination; 3885 } 3886 3887 /** 3888 * Saves visitor information as a cookie so it can be reused. 3889 * 3890 * @param $values 3891 * An array of key/value pairs to be saved into a cookie. 3892 */ 3893 function user_cookie_save(array $values) { 3894 foreach ($values as $field => $value) { 3895 // Set cookie for 365 days. 3896 setrawcookie('Drupal.visitor.' . $field, rawurlencode($value), REQUEST_TIME + 31536000, '/'); 3897 } 3898 } 3899 3900 /** 3901 * Delete a visitor information cookie. 3902 * 3903 * @param $cookie_name 3904 * A cookie name such as 'homepage'. 3905 */ 3906 function user_cookie_delete($cookie_name) { 3907 setrawcookie('Drupal.visitor.' . $cookie_name, '', REQUEST_TIME - 3600, '/'); 3908 } 3909 3910 /** 3911 * Implements hook_rdf_mapping(). 3912 */ 3913 function user_rdf_mapping() { 3914 return array( 3915 array( 3916 'type' => 'user', 3917 'bundle' => RDF_DEFAULT_BUNDLE, 3918 'mapping' => array( 3919 'rdftype' => array('sioc:UserAccount'), 3920 'name' => array( 3921 'predicates' => array('foaf:name'), 3922 ), 3923 'homepage' => array( 3924 'predicates' => array('foaf:page'), 3925 'type' => 'rel', 3926 ), 3927 ), 3928 ), 3929 ); 3930 } 3931 3932 /** 3933 * Implements hook_file_download_access(). 3934 */ 3935 function user_file_download_access($field, $entity_type, $entity) { 3936 if ($entity_type == 'user') { 3937 return user_view_access($entity); 3938 } 3939 } 3940 3941 /** 3942 * Implements hook_system_info_alter(). 3943 * 3944 * Drupal 7 ships with two methods to add additional fields to users: Profile 3945 * module, a legacy module dating back from 2002, and Field API integration 3946 * with users. While Field API support for users currently provides less end 3947 * user features, the inefficient data storage mechanism of Profile module, as 3948 * well as its lack of consistency with the rest of the entity / field based 3949 * systems in Drupal 7, make this a sub-optimal solution to those who were not 3950 * using it in previous releases of Drupal. 3951 * 3952 * To prevent new Drupal 7 sites from installing Profile module, and 3953 * unwittingly ending up with two completely different and incompatible methods 3954 * of extending users, only make the Profile module available if the profile_* 3955 * tables are present. 3956 * 3957 * @todo: Remove in D8, pending upgrade path. 3958 */ 3959 function user_system_info_alter(&$info, $file, $type) { 3960 if ($type == 'module' && $file->name == 'profile' && db_table_exists('profile_field')) { 3961 $info['hidden'] = FALSE; 3962 } 3963 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title