user.module 92.5 KB
Newer Older
Dries's avatar
 
Dries committed
1 2
<?php

3
use Drupal\Core\Database\Query\SelectInterface;
4
use Drupal\Core\Entity\EntityInterface;
5 6
use Drupal\comment\Plugin\Core\Entity\Comment;
use Drupal\entity\Plugin\Core\Entity\EntityDisplay;
7
use Drupal\file\Plugin\Core\Entity\File;
8
use Drupal\user\Plugin\Core\Entity\User;
9
use Drupal\user\UserRole;
10
use Drupal\Core\Template\Attribute;
11
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
12
use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
Crell's avatar
Crell committed
13

Dries's avatar
 
Dries committed
14 15 16 17 18
/**
 * @file
 * Enables the user registration and login system.
 */

19 20 21
/**
 * Maximum length of username text field.
 */
22
const USERNAME_MAX_LENGTH = 60;
23 24 25 26

/**
 * Maximum length of user e-mail text field.
 */
27
const EMAIL_MAX_LENGTH = 254;
28

29 30 31
/**
 * Only administrators can create user accounts.
 */
32
const USER_REGISTER_ADMINISTRATORS_ONLY = 'admin_only';
33 34 35 36

/**
 * Visitors can create their own accounts.
 */
37
const USER_REGISTER_VISITORS = 'visitors';
38 39 40 41 42

/**
 * Visitors can create accounts, but they don't become active without
 * administrative approval.
 */
43
const USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL = 'visitors_admin_approval';
44

45 46 47 48 49 50 51 52 53 54
/**
 * Implement hook_help().
 */
function user_help($path, $arg) {
  global $user;

  switch ($path) {
    case 'admin/help#user':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
55
      $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>';
56 57 58
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Creating and managing users') . '</dt>';
59
      $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>';
60
      $output .= '<dt>' . t('User roles and permissions') . '</dt>';
61
      $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/roles'))) . '</dd>';
62
      $output .= '<dt>' . t('Account settings') . '</dt>';
63
      $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 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>';
64 65 66 67
      $output .= '</dl>';
      return $output;
    case 'admin/people/create':
      return '<p>' . t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") . '</p>';
68
    case 'admin/people/permissions':
69 70
      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/roles'), '@settings' => url('admin/config/people/accounts'))) . '</p>';
    case 'admin/people/roles':
71
      $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>';
72
      $output .= '<p>' . t('Drupal has three special user roles:') . '</p>';
73 74 75
      $output .= '<ul>';
      $output .= '<li>' . t("Anonymous user: this role is used for users that don't have a user account or that are not authenticated.") . '</li>';
      $output .= '<li>' . t('Authenticated user: this role is automatically granted to all logged in users.') . '</li>';
76
      $output .= '<li>' . t('Administrator role: this role is automatically granted all new permissions when you install a new module. Configure which role is the administrator role on the <a href="@account_settings">Account settings page</a>.', array('@account_settings' => url('admin/config/people/accounts'))) . '</li>';
77 78 79 80 81 82 83 84 85 86
      $output .= '</ul>';
      return $output;
    case 'admin/config/people/accounts/fields':
      return '<p>' . t('This form lets administrators add, edit, and arrange fields for storing user data.') . '</p>';
    case 'admin/config/people/accounts/display':
      return '<p>' . t('This form lets administrators configure how fields should be displayed when rendering a user profile page.') . '</p>';
    case 'admin/people/search':
      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>';
  }
}
87

88
/**
89
 * Implements hook_theme().
90 91 92
 */
function user_theme() {
  return array(
93
    'user' => array(
94
      'render element' => 'elements',
95
      'template' => 'user',
96
      'file' => 'user.pages.inc',
97
    ),
98
    'user_admin_permissions' => array(
99
      'render element' => 'form',
100
      'file' => 'user.admin.inc',
101
    ),
102
    'user_admin_roles' => array(
103
      'render element' => 'form',
104
      'file' => 'user.admin.inc',
105
    ),
106 107 108 109
    'user_permission_description' => array(
      'variables' => array('permission_item' => NULL, 'hide' => NULL),
      'file' => 'user.admin.inc',
    ),
110
    'user_signature' => array(
111
      'variables' => array('signature' => NULL),
112
    ),
113 114 115
    'username' => array(
      'variables' => array('account' => NULL),
    ),
116 117 118
  );
}

119 120 121 122 123 124 125 126
/**
 * Implements hook_page_build().
 */
function user_page_build(&$page) {
  $path = drupal_get_path('module', 'user');
  $page['#attached']['css'][$path . '/user.css'] = array('every_page' => TRUE);
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
/**
 * Implements hook_entity_view_mode_info().
 */
function user_entity_view_mode_info() {
  $view_modes['user']['full'] = array(
    'label' => t('User account'),
  );
  $view_modes['user']['compact'] = array(
    'label' => t('Compact'),
    'custom_settings' => TRUE,
  );
  return $view_modes;
}

/**
 * Implements hook_entity_bundle_info().
 */
function user_entity_bundle_info() {
  $bundles['user']['user'] = array(
    'label' => t('User'),
    'admin' => array(
      'path' => 'admin/config/people/accounts',
    ),
  );
  return $bundles;
}

154
/**
155
 * Entity URI callback.
156
 */
157 158 159 160
function user_uri($user) {
  return array(
    'path' => 'user/' . $user->uid,
  );
161 162
}

163 164 165
/**
 * Entity label callback.
 *
166 167 168
 * This label callback has langcode for security reasons. The username is the
 * visual identifier for a user and needs to be consistent in all languages.
 *
169 170 171 172 173 174 175 176
 * @param $entity_type
 *   The entity type.
 * @param $entity
 *   The entity object.
 *
 * @return
 *   The name of the user.
 *
177
 * @see user_format_name()
178 179
 */
function user_label($entity_type, $entity) {
180
  return user_format_name($entity);
181 182
}

183 184 185
/**
 * Populates $entity->account for each prepared entity.
 *
186 187
 * Called by Drupal\Core\Entity\EntityRenderControllerInterface::buildContent()
 * implementations.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
 *
 * @param array $entities
 *   The entities keyed by entity ID.
 */
function user_attach_accounts(array $entities) {
  $uids = array();
  foreach ($entities as $entity) {
    $uids[] = $entity->uid;
  }
  $uids = array_unique($uids);
  $accounts = user_load_multiple($uids);
  $anonymous = drupal_anonymous_user();
  foreach ($entities as $id => $entity) {
    if (isset($accounts[$entity->uid])) {
      $entities[$id]->account = $accounts[$entity->uid];
    }
    else {
      $entities[$id]->account = $anonymous;
    }
  }
}

/**
 * Returns whether this site supports the default user picture feature.
 *
 * This approach preserves compatibility with node/comment templates. Alternate
 * user picture implementations (e.g., Gravatar) should provide their own
 * add/edit/delete forms and populate the 'picture' variable during the
 * preprocess stage.
 */
function user_picture_enabled() {
  return (bool) field_info_instance('user', 'user_picture', 'user');
}

222 223 224 225 226
/**
 * Implements hook_field_info_alter().
 */
function user_field_info_alter(&$info) {
  // Add the 'user_register_form' instance setting to all field types.
227 228 229
  foreach ($info as $field_type => &$field_type_info) {
    $field_type_info += array('instance_settings' => array());
    $field_type_info['instance_settings'] += array(
230 231 232 233 234
      'user_register_form' => FALSE,
    );
  }
}

235
/**
236
 * Implements hook_field_extra_fields().
237
 */
238
function user_field_extra_fields() {
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
  $fields['user']['user']['form']['account'] = array(
    'label' => t('User name and password'),
    'description' => t('User module account form elements.'),
    'weight' => -10,
  );
  if (config('user.settings')->get('signatures')) {
    $fields['user']['user']['form']['signature_settings'] = array(
      'label' => t('Signature settings'),
      'description' => t('User module form element.'),
      'weight' => 1,
    );
  }
  $fields['user']['user']['form']['language'] = array(
    'label' => t('Language settings'),
    'description' => t('User module form element.'),
    'weight' => 0,
  );
  if (config('system.timezone')->get('user.configurable')) {
    $fields['user']['user']['form']['timezone'] = array(
      'label' => t('Timezone'),
      'description' => t('System module form element.'),
      'weight' => 6,
    );
  }

  $fields['user']['user']['display']['member_for'] = array(
    'label' => t('Member for'),
    'description' => t('User module \'member for\' view element.'),
    'weight' => 5,
268
  );
269

270
  return $fields;
271 272
}

Dries's avatar
Dries committed
273
/**
274
 * Loads multiple users based on certain conditions.
Dries's avatar
Dries committed
275
 *
276 277 278
 * This function should be used whenever you need to load more than one user
 * from the database. Users are loaded into memory and will not require
 * database access if loaded again during the same page request.
Dries's avatar
Dries committed
279
 *
280 281
 * @param array $uids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
282
 * @param bool $reset
283 284
 *   A boolean indicating that the internal cache should be reset. Use this if
 *   loading a user object which has been altered during the page request.
285
 *
286
 * @return array
287 288
 *   An array of user objects, indexed by uid.
 *
289
 * @see entity_load_multiple()
290 291 292
 * @see user_load()
 * @see user_load_by_mail()
 * @see user_load_by_name()
293
 * @see \Drupal\Core\Entity\Query\QueryInterface
Dries's avatar
Dries committed
294
 */
295 296
function user_load_multiple(array $uids = NULL, $reset = FALSE) {
  return entity_load_multiple('user', $uids, $reset);
297
}
298 299

/**
300 301 302 303 304 305
 * Loads a user object.
 *
 * Drupal has a global $user object, which represents the currently-logged-in
 * user. So to avoid confusion and to avoid clobbering the global $user object,
 * it is a good idea to assign the result of this function to a different local
 * variable, generally $account. If you actually do want to act as the user you
306 307 308 309
 * are loading, it is essential to call drupal_save_session(FALSE); first.
 * See
 * @link http://drupal.org/node/218104 Safely impersonating another user @endlink
 * for more information.
310
 *
311
 * @param int $uid
312
 *   Integer specifying the user ID to load.
313
 * @param bool $reset
314 315 316
 *   TRUE to reset the internal cache and load from the database; FALSE
 *   (default) to load from the internal cache, if set.
 *
317
 * @return object
318
 *   A fully-loaded user object upon successful user load, or FALSE if the user
319 320 321 322 323
 *   cannot be loaded.
 *
 * @see user_load_multiple()
 */
function user_load($uid, $reset = FALSE) {
324
  return entity_load('user', $uid, $reset);
325 326 327
}

/**
328
 * Fetches a user object by email address.
329
 *
330
 * @param string $mail
331
 *   String with the account's e-mail address.
332
 * @return object|bool
333 334 335 336 337 338
 *   A fully-loaded $user object upon successful user load or FALSE if user
 *   cannot be loaded.
 *
 * @see user_load_multiple()
 */
function user_load_by_mail($mail) {
339
  $users = entity_load_multiple_by_properties('user', array('mail' => $mail));
340 341 342 343
  return reset($users);
}

/**
344
 * Fetches a user object by account name.
345
 *
346
 * @param string $name
347
 *   String with the account's user name.
348
 * @return object|bool
349 350 351 352 353 354
 *   A fully-loaded $user object upon successful user load or FALSE if user
 *   cannot be loaded.
 *
 * @see user_load_multiple()
 */
function user_load_by_name($name) {
355
  $users = entity_load_multiple_by_properties('user', array('name' => $name));
356
  return reset($users);
Dries's avatar
 
Dries committed
357 358
}

Dries's avatar
Dries committed
359 360 361
/**
 * Verify the syntax of the given name.
 */
Dries's avatar
 
Dries committed
362
function user_validate_name($name) {
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
  if (!$name) {
    return t('You must enter a username.');
  }
  if (substr($name, 0, 1) == ' ') {
    return t('The username cannot begin with a space.');
  }
  if (substr($name, -1) == ' ') {
    return t('The username cannot end with a space.');
  }
  if (strpos($name, '  ') !== FALSE) {
    return t('The username cannot contain multiple spaces in a row.');
  }
  if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) {
    return t('The username contains an illegal character.');
  }
378
  if (preg_match('/[\x{80}-\x{A0}' .         // Non-printable ISO-8859-1 + NBSP
379 380 381 382 383 384 385 386 387
                  '\x{AD}' .                // Soft-hyphen
                  '\x{2000}-\x{200F}' .     // Various space characters
                  '\x{2028}-\x{202F}' .     // Bidirectional text overrides
                  '\x{205F}-\x{206F}' .     // Various text hinting characters
                  '\x{FEFF}' .              // Byte order mark
                  '\x{FF01}-\x{FF60}' .     // Full-width latin
                  '\x{FFF9}-\x{FFFD}' .     // Replacement characters
                  '\x{0}-\x{1F}]/u',        // NULL byte and control characters
                  $name)) {
388 389
    return t('The username contains an illegal character.');
  }
390 391 392
  if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
    return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
  }
Dries's avatar
 
Dries committed
393 394
}

Dries's avatar
Dries committed
395 396 397
/**
 * Generate a random alphanumeric password.
 */
Dries's avatar
 
Dries committed
398 399
function user_password($length = 10) {
  // This variable contains the list of allowable characters for the
400 401
  // password. Note that the number 0 and the letter 'O' have been
  // removed to avoid confusion between the two. The same is true
402
  // of 'I', 1, and 'l'.
403
  $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
Dries's avatar
Dries committed
404

405 406
  // Zero-based count of characters in the allowable list:
  $len = strlen($allowable_characters) - 1;
Dries's avatar
 
Dries committed
407

Dries's avatar
Dries committed
408 409
  // Declare the password as a blank string.
  $pass = '';
Dries's avatar
 
Dries committed
410

Dries's avatar
Dries committed
411
  // Loop the number of times specified by $length.
Dries's avatar
 
Dries committed
412 413 414 415
  for ($i = 0; $i < $length; $i++) {

    // Each iteration, pick a random character from the
    // allowable string and append it to the password:
416
    $pass .= $allowable_characters[mt_rand(0, $len)];
Dries's avatar
 
Dries committed
417 418 419
  }

  return $pass;
Dries's avatar
 
Dries committed
420 421
}

422 423 424 425 426 427 428 429 430 431
/**
 * Determine the permissions for one or more roles.
 *
 * @param $roles
 *   An array whose keys are the role IDs of interest, such as $user->roles.
 *
 * @return
 *   An array indexed by role ID. Each value is an array whose keys are the
 *   permission strings for the given role ID.
 */
432
function user_role_permissions($roles) {
433
  $cache = &drupal_static(__FUNCTION__, array());
434 435 436

  $role_permissions = $fetch = array();

437 438 439
  foreach ($roles as $rid => $name) {
    if (isset($cache[$rid])) {
      $role_permissions[$rid] = $cache[$rid];
440
    }
441 442 443 444 445 446 447
    else {
      // Add this rid to the list of those needing to be fetched.
      $fetch[] = $rid;
      // Prepare in case no permissions are returned.
      $cache[$rid] = array();
    }
  }
448

449 450 451 452 453 454 455 456 457 458 459
  if ($fetch) {
    // Get from the database permissions that were not in the static variable.
    // Only role IDs with at least one permission assigned will return rows.
    $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch));

    foreach ($result as $row) {
      $cache[$row->rid][$row->permission] = TRUE;
    }
    foreach ($fetch as $rid) {
      // For every rid, we know we at least assigned an empty array.
      $role_permissions[$rid] = $cache[$rid];
460 461 462 463 464 465
    }
  }

  return $role_permissions;
}

Dries's avatar
Dries committed
466 467 468 469 470
/**
 * Determine whether the user has a given privilege.
 *
 * @param $string
 *   The permission, such as "administer nodes", being checked for.
Dries's avatar
 
Dries committed
471 472
 * @param $account
 *   (optional) The account to check, if not given use currently logged in user.
Dries's avatar
Dries committed
473 474
 *
 * @return
475
 *   Boolean TRUE if the current user has the requested permission.
Dries's avatar
Dries committed
476 477 478 479 480
 *
 * All permission checks in Drupal should go through this function. This
 * way, we guarantee consistent behavior, and ensure that the superuser
 * can perform all actions.
 */
481
function user_access($string, $account = NULL) {
Dries's avatar
 
Dries committed
482
  global $user;
483

484
  if (!isset($account)) {
485 486 487
    $account = $user;
  }

488
  // User #1 has all privileges:
489
  if ($account->uid == 1) {
490
    return TRUE;
Dries's avatar
 
Dries committed
491 492
  }

Dries's avatar
Dries committed
493 494
  // To reduce the number of SQL queries, we cache the user's permissions
  // in a static variable.
495
  // Use the advanced drupal_static() pattern, since this is called very often.
496 497 498 499 500
  static $drupal_static_fast;
  if (!isset($drupal_static_fast)) {
    $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__);
  }
  $perm = &$drupal_static_fast['perm'];
501
  if (!isset($perm[$account->uid])) {
502
    $role_permissions = user_role_permissions($account->roles);
Dries's avatar
 
Dries committed
503

504
    $perms = array();
505 506
    foreach ($role_permissions as $one_role) {
      $perms += $one_role;
Dries's avatar
 
Dries committed
507
    }
508
    $perm[$account->uid] = $perms;
Dries's avatar
 
Dries committed
509
  }
510

511
  return isset($perm[$account->uid][$string]);
Dries's avatar
 
Dries committed
512 513
}

514
/**
515
 * Checks for usernames blocked by user administration.
516
 *
517 518 519 520 521 522
 * @param $name
 *   A string containing a name of the user.
 *
 * @return
 *   Object with property 'name' (the user name), if the user is blocked;
 *   FALSE if the user is not blocked.
523 524
 */
function user_is_blocked($name) {
525 526 527 528 529
  return db_select('users')
    ->fields('users', array('name'))
    ->condition('name', db_like($name), 'LIKE')
    ->condition('status', 0)
    ->execute()->fetchObject();
530 531
}

Dries's avatar
Dries committed
532
/**
533
 * Implements hook_permission().
Dries's avatar
Dries committed
534
 */
535
function user_permission() {
536 537 538
  return array(
    'administer permissions' =>  array(
      'title' => t('Administer permissions'),
539
      'restrict access' => TRUE,
540 541 542
    ),
    'administer users' => array(
      'title' => t('Administer users'),
543
      'restrict access' => TRUE,
544 545
    ),
    'access user profiles' => array(
546
      'title' => t('View user profiles'),
547 548 549 550 551
    ),
    'change own username' => array(
      'title' => t('Change own username'),
    ),
    'cancel account' => array(
552
      'title' => t('Cancel own user account'),
553
      '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' => config('user.settings')->get('anonymous'), '@user-settings-url' => url('admin/config/people/accounts'))),
554 555 556
    ),
    'select account cancellation method' => array(
      'title' => t('Select method for cancelling own account'),
557
      'restrict access' => TRUE,
558 559
    ),
  );
Dries's avatar
 
Dries committed
560 561
}

Dries's avatar
Dries committed
562
/**
563
 * Implements hook_search_info().
Dries's avatar
Dries committed
564
 */
565 566 567 568 569 570 571
function user_search_info() {
  return array(
    'title' => 'Users',
  );
}

/**
572
 * Implements hook_search_access().
573 574 575 576 577 578
 */
function user_search_access() {
  return user_access('access user profiles');
}

/**
579
 * Implements hook_search_execute().
580
 */
581
function user_search_execute($keys = NULL, $conditions = NULL) {
582 583 584
  $find = array();
  // Replace wildcards with MySQL/PostgreSQL wildcards.
  $keys = preg_replace('!\*+!', '%', $keys);
585 586
  $query = db_select('users')
    ->extend('Drupal\Core\Database\Query\PagerSelectExtender');
587
  $query->fields('users', array('uid'));
588
  if (user_access('administer users')) {
589 590
    // Administrators can also search in the otherwise private email field, and
    // they don't need to be restricted to only active users.
591
    $query->fields('users', array('mail'));
592
    $query->condition(db_or()->
593 594
      condition('name', '%' . db_like($keys) . '%', 'LIKE')->
      condition('mail', '%' . db_like($keys) . '%', 'LIKE'));
595 596
  }
  else {
597 598 599 600
    // Regular users can only search via usernames, and we do not show them
    // blocked accounts.
    $query->condition('name', '%' . db_like($keys) . '%', 'LIKE')
      ->condition('status', 1);
601
  }
602
  $uids = $query
603
    ->limit(15)
604 605 606 607 608 609 610
    ->execute()
    ->fetchCol();
  $accounts = user_load_multiple($uids);

  $results = array();
  foreach ($accounts as $account) {
    $result = array(
611
      'title' => user_format_name($account),
612 613 614 615
      'link' => url('user/' . $account->uid, array('absolute' => TRUE)),
    );
    if (user_access('administer users')) {
      $result['title'] .= ' (' . $account->mail . ')';
616
    }
617
    $results[] = $result;
Dries's avatar
 
Dries committed
618
  }
619 620

  return $results;
Dries's avatar
 
Dries committed
621 622
}

Dries's avatar
Dries committed
623
/**
624
 * Implements hook_user_view().
Dries's avatar
Dries committed
625
 */
626 627 628 629 630 631 632 633
function user_user_view(User $account, EntityDisplay $display) {
  if ($display->getComponent('member_for')) {
    $account->content['member_for'] = array(
      '#type' => 'item',
      '#title' => t('Member for'),
      '#markup' => format_interval(REQUEST_TIME - $account->created),
    );
  }
634 635
}

636 637 638 639 640 641 642 643 644
/**
 * Sets the value of the user register and profile forms' langcode element.
 */
function _user_language_selector_langcode_value($element, $input, &$form_state) {
  // Only add to the description if the form element have a description.
  if (isset($form_state['complete_form']['language']['preferred_langcode']['#description'])) {
    $form_state['complete_form']['language']['preferred_langcode']['#description'] .= ' ' . t("This is also assumed to be the primary language of this account's profile information.");
  }
  return $form_state['values']['preferred_langcode'];
645 646
}

647
/**
648
 * Form validation handler for the current password on the user account form.
649
 *
650
 * @see AccountFormController::form()
651 652
 */
function user_validate_current_pass(&$form, &$form_state) {
653
  $account = $form_state['user'];
654 655 656 657 658
  foreach ($form_state['values']['current_pass_required_values'] as $key => $name) {
    // This validation only works for required textfields (like mail) or
    // form values like password_confirm that have their own validation
    // that prevent them from being empty if they are changed.
    if ((strlen(trim($form_state['values'][$key])) > 0) && ($form_state['values'][$key] != $account->$key)) {
659
      $current_pass_failed = empty($form_state['values']['current_pass']) || !drupal_container()->get('password')->check($form_state['values']['current_pass'], $account);
660 661 662 663 664 665 666 667 668 669
      if ($current_pass_failed) {
        form_set_error('current_pass', t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => $name)));
        form_set_error($key);
      }
      // We only need to check the password once.
      break;
    }
  }
}

670
/**
671
 * Implements hook_preprocess_HOOK() for block.tpl.php.
672 673 674
 */
function user_preprocess_block(&$variables) {
  if ($variables['block']->module == 'user') {
675 676
    switch ($variables['block']->id) {
      case 'user_login_block':
677
        $variables['attributes']['role'] = 'form';
678
        break;
679
      case 'user_new_block':
680
        $variables['attributes']['role'] = 'complementary';
681
        break;
682
      case 'user_online_block':
683
        $variables['attributes']['role'] = 'complementary';
684 685 686 687 688
        break;
    }
  }
}

689 690 691 692 693
/**
 * Format a username.
 *
 * By default, the passed-in object's 'name' property is used if it exists, or
 * else, the site-defined value for the 'anonymous' variable. However, a module
694 695
 * may override this by implementing
 * hook_user_format_name_alter(&$name, $account).
696
 *
697
 * @see hook_user_format_name_alter()
698 699 700 701 702 703 704 705 706
 *
 * @param $account
 *   The account object for the user whose name is to be formatted.
 *
 * @return
 *   An unsanitized string with the username to display. The code receiving
 *   this result must ensure that check_plain() is called on it before it is
 *   printed to the page.
 */
707
function user_format_name($account) {
708
  $name = !empty($account->name) ? $account->name : config('user.settings')->get('anonymous');
709
  drupal_alter('user_format_name', $name, $account);
710 711 712
  return $name;
}

713 714 715 716 717 718 719 720 721
/**
 * Implements hook_template_preprocess_default_variables_alter().
 *
 * @see user_user_login()
 * @see user_user_logout()
 */
function user_template_preprocess_default_variables_alter(&$variables) {
  global $user;

722 723 724 725 726 727
  // If this function is called from the installer after Drupal has been
  // installed then $user will not be set.
  if (!is_object($user)) {
    return;
  }

728 729 730 731 732 733 734 735
  $variables['user'] = clone $user;
  // Remove password and session IDs, since themes should not need nor see them.
  unset($variables['user']->pass, $variables['user']->sid, $variables['user']->ssid);

  $variables['is_admin'] = user_access('access administration pages');
  $variables['logged_in'] = ($user->uid > 0);
}

736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
/**
 * Preprocesses variables for theme_username().
 *
 * Modules that make any changes to variables like 'name' or 'extra' must insure
 * that the final string is safe to include directly in the output by using
 * check_plain() or filter_xss().
 *
 * @see template_process_username()
 */
function template_preprocess_username(&$variables) {
  $account = $variables['account'];

  $variables['extra'] = '';
  if (empty($account->uid)) {
   $variables['uid'] = 0;
   if (theme_get_setting('toggle_comment_user_verification')) {
     $variables['extra'] = ' (' . t('not verified') . ')';
   }
  }
  else {
    $variables['uid'] = (int) $account->uid;
  }

  // Set the name to a formatted name that is safe for printing and
  // that won't break tables by being too long. Keep an unshortened,
  // unsanitized version, in case other preprocess functions want to implement
  // their own shortening logic or add markup. If they do so, they must ensure
  // that $variables['name'] is safe for printing.
764
  $name = $variables['name_raw'] = user_format_name($account);
765 766 767 768 769
  if (drupal_strlen($name) > 20) {
    $name = drupal_substr($name, 0, 15) . '...';
  }
  $variables['name'] = check_plain($name);
  $variables['profile_access'] = user_access('access user profiles');
770

771 772 773
  // Populate link path and attributes if appropriate.
  if ($variables['uid'] && $variables['profile_access']) {
    // We are linking to a local user.
774
    $variables['link_attributes']['title'] = t('View user profile.');
775 776 777 778 779 780
    $variables['link_path'] = 'user/' . $variables['uid'];
  }
  elseif (!empty($account->homepage)) {
    // Like the 'class' attribute, the 'rel' attribute can hold a
    // space-separated set of values, so initialize it as an array to make it
    // easier for other preprocess functions to append to it.
781
    $variables['link_attributes']['rel'] = 'nofollow';
782 783 784 785 786 787
    $variables['link_path'] = $account->homepage;
    $variables['homepage'] = $account->homepage;
  }
  // We do not want the l() function to check_plain() a second time.
  $variables['link_options']['html'] = TRUE;
  // Set a default class.
788
  $variables['attributes'] = array('class' => array('username'));
789 790 791 792 793 794 795 796 797 798 799 800
}

/**
 * Processes variables for theme_username().
 *
 * @see template_preprocess_username()
 */
function template_process_username(&$variables) {
  // Finalize the link_options array for passing to the l() function.
  // This is done in the process phase so that attributes may be added by
  // modules or the theme during the preprocess phase.
  if (isset($variables['link_path'])) {
801
    // $variables['attributes'] contains attributes that should be applied
802 803 804 805 806 807
    // regardless of whether a link is being rendered or not.
    // $variables['link_attributes'] contains attributes that should only be
    // applied if a link is being rendered. Preprocess functions are encouraged
    // to use the former unless they want to add attributes on the link only.
    // If a link is being rendered, these need to be merged. Some attributes are
    // themselves arrays, so the merging needs to be recursive.
808 809 810
    // This purposefully does not use
    // \Drupal\Component\Utility\NestedArray::mergeDeep() for performance
    // reasons, since it is potentially called very often.
811
    $variables['link_options']['attributes'] = array_merge_recursive($variables['link_attributes'], $variables['attributes']);
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
  }
}

/**
 * Returns HTML for a username, potentially linked to the user's page.
 *
 * @param $variables
 *   An associative array containing:
 *   - account: The user object to format.
 *   - name: The user's name, sanitized.
 *   - extra: Additional text to append to the user's name, sanitized.
 *   - link_path: The path or URL of the user's profile page, home page, or
 *     other desired page to link to for more information about the user.
 *   - link_options: An array of options to pass to the l() function's $options
 *     parameter if linking the user's name to the user's page.
827 828
 *   - attributes: An array of attributes to instantiate the
 *     Drupal\Core\Template\Attribute class if not linking to the user's page.
829 830 831 832 833 834 835 836 837 838 839 840 841 842
 *
 * @see template_preprocess_username()
 * @see template_process_username()
 */
function theme_username($variables) {
  if (isset($variables['link_path'])) {
    // We have a link path, so we should generate a link using l().
    // Additional classes may be added as array elements like
    // $variables['link_options']['attributes']['class'][] = 'myclass';
    $output = l($variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
  }
  else {
    // Modules may have added important attributes so they must be included
    // in the output. Additional classes may be added as array elements like
843
    // $variables['attributes']['class'][] = 'myclass';
844
    $output = '<span' . new Attribute($variables['attributes']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
845 846 847 848
  }
  return $output;
}

849 850 851 852 853 854
/**
 * Determines if the current user is anonymous.
 *
 * @return bool
 *   TRUE if the user is anonymous, FALSE if the user is authenticated.
 */
855
function user_is_anonymous() {
856 857
  // Menu administrators can see items for anonymous when administering.
  return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']);
858 859
}

860 861 862 863 864 865
/**
 * Determines if the current user is logged in.
 *
 * @return bool
 *   TRUE if the user is logged in, FALSE if the user is anonymous.
 */
866
function user_is_logged_in() {
867
  return (bool) $GLOBALS['user']->uid;
868 869
}

870 871 872 873 874 875
/**
 * Determines if the current user has access to the user registration page.
 *
 * @return bool
 *   TRUE if the user is not already logged in and can register for an account.
 */
876
function user_register_access() {
877
  return user_is_anonymous() && (config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY);
878 879
}

Dries's avatar
 
Dries committed
880
/**
881
 * Implements hook_menu().
Dries's avatar
 
Dries committed
882
 */
883 884
function user_menu() {
  // Registration and login pages.
885
  $items['user'] = array(
886
    'title' => 'User account',
887
    'title callback' => 'user_menu_title',
888 889
    'page callback' => 'user_page',
    'access callback' => TRUE,
890
    'file' => 'user.pages.inc',
891
    'weight' => -10,
892
    'menu_name' => 'account',
893 894 895
  );

  $items['user/login'] = array(
896
    'title' => 'Log in',
897
    'access callback' => 'user_is_anonymous',
898 899
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
900 901 902 903 904
  // Other authentication methods may add pages below user/login/.
  $items['user/login/default'] = array(
    'title' => 'Username and password',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
Dries's avatar
 
Dries committed
905

906
  $items['user/register'] = array(
907
    'title' => 'Create new account',
908
    'type' => MENU_LOCAL_TASK,
909
    'route_name' => 'user_register',
910 911 912
  );

  $items['user/password'] = array(
913
    'title' => 'Request new password',
914 915
    'page callback' => 'drupal_get_form',
    'page arguments' => array('user_pass'),
916
    'access callback' => TRUE,
917
    'type' => MENU_LOCAL_TASK,
918
    'file' => 'user.pages.inc',
919 920
  );
  $items['user/reset/%/%/%'] = array(
921
    'title' => 'Reset password',
922 923 924 925
    'page callback' => 'drupal_get_form',
    'page arguments' => array('user_pass_reset', 2, 3, 4),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
926
    'file' => 'user.pages.inc',
927 928
  );

929 930 931 932 933
  $items['user/logout'] = array(
    'title' => 'Log out',
    'access callback' => 'user_is_logged_in',
    'page callback' => 'user_logout',
    'weight' => 10,
934
    'menu_name' => 'account',
935
    'file' => 'user.pages.inc',
936 937
  );

938
  // User listing pages.
939 940
  $items['admin/people'] = array(
    'title' => 'People',
941
    'description' => 'Manage user accounts, roles, and permissions.',
942 943
    'page callback' => 'user_admin',
    'page arguments' => array('list'),