user.module 57.6 KB
Newer Older
1 2
<?php

3
use Drupal\Component\Utility\Crypt;
4
use Drupal\Component\Utility\String;
5
use Drupal\Component\Utility\Unicode;
6
use Drupal\Core\Entity\EntityInterface;
7
use Drupal\Core\Routing\RouteMatchInterface;
8
use Drupal\Core\Session\AccountInterface;
9
use Drupal\Core\Session\AnonymousUserSession;
10
use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;
11
use Drupal\Core\Url;
12
use Drupal\file\Entity\File;
13
use Drupal\user\Entity\Role;
14
use Drupal\user\Entity\User;
15
use Drupal\user\UserInterface;
16
use Drupal\user\RoleInterface;
17
use Drupal\Core\Template\Attribute;
18
use Drupal\Core\TypedData\DataDefinition;
19
use Symfony\Component\HttpFoundation\RedirectResponse;
20
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
21
use Drupal\menu_link\Entity\MenuLink;
Crell's avatar
Crell committed
22

23 24 25 26 27
/**
 * @file
 * Enables the user registration and login system.
 */

28 29 30
/**
 * Maximum length of username text field.
 */
31
const USERNAME_MAX_LENGTH = 60;
32

33 34 35
/**
 * Only administrators can create user accounts.
 */
36
const USER_REGISTER_ADMINISTRATORS_ONLY = 'admin_only';
37 38 39 40

/**
 * Visitors can create their own accounts.
 */
41
const USER_REGISTER_VISITORS = 'visitors';
42 43 44 45 46

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

49 50 51
/**
 * Implement hook_help().
 */
52
function user_help($route_name, RouteMatchInterface $route_match) {
53 54
  switch ($route_name) {
    case 'help.page.user':
55 56
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
57
      $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 and permissions. For more information, see the <a href="!user_docs">online documentation for the User module</a>.', array('!user_docs' => 'https://drupal.org/documentation/modules/user')) . '</p>';
58 59 60
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Creating and managing users') . '</dt>';
61 62 63 64 65 66
      $output .= '<dd>' . t('Through the <a href="!people">People administration page</a> you can add and cancel user accounts and assign users to roles. By editing one particular user you can change their user name, email address, password, and information in other fields.', array('!people' => \Drupal::url('user.admin_account'))) . '</dd>';
      $output .= '<dt>' . t('Configuring user roles') . '</dt>';
      $output .= '<dd>' . t('<em>Roles</em> are used to group and classify users; each user can be assigned one or more roles. Typically there are two pre-defined 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 how your site was set up, an <em>Administrator</em> role may also be available: users with this role will automatically be assigned any new permissions whenever a module is enabled. You can create additional roles on the <a href="!roles">Roles administration page</a>.', array('!roles' => \Drupal::url('user.role_list'))) . '</dd>';
      $output .= '<dt>' . t('Setting permissions') . '</dt>';
      $output .= '<dd>' . t('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 content, editing or creating  a particular type of content, administering settings for a particular module, or using a particular function of the site (such as search).', array('!permissions_user' => \Drupal::url('user.admin_permissions'))) . '</dd>';
      $output .= '<dt>' . t('Managing account settings') . '</dt>';
67
      $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 settings, and account cancellation settings. On this page you can also manage settings for account personalization (including signatures), and adapt the text for the email messages that users receive when they register or request a password recovery. You may also set which role is automatically assigned new permissions whenever a module is enabled (the Administrator role).', array('!accounts'  => \Drupal::url('user.account_settings'))) . '</dd>';
68 69
      $output .= '<dt>' . t('Managing user account fields') . '</dt>';
      $output .= '<dd>' . t('Because User accounts are an <a href="!entity_help">entity type</a>, you can extend them by adding <a href="!field_help">fields</a> through the Manage fields tab on the <a href="!accounts">Account settings page</a>. By adding fields for e.g., a picture, a biography, or address, you can a create a custom profile for the users of the website.', array('!entity_help' => \Drupal::url('help.page', array('name' => 'entity')),'!field_help'=>\Drupal::url('help.page', array('name' => 'field')), '!accounts' => \Drupal::url('user.account_settings'))) . '</dd>';
70 71
      $output .= '</dl>';
      return $output;
72 73

    case 'user.admin_create':
74
      return '<p>' . t("This web page allows administrators to register new users. Users' email addresses and usernames must be unique.") . '</p>';
75 76

    case 'user.admin_permissions':
77
      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.) Any permissions granted to the Authenticated user role will be given to any user who is logged in to your site. From the <a href="!settings">Account settings</a> page, you can make any role into an Administrator role for the site, meaning that role will be granted all new permissions automatically. You should be careful to ensure that only trusted users are given this access and level of control of your site.', array('!role' => \Drupal::url('user.role_list'), '!settings' => \Drupal::url('user.account_settings'))) . '</p>';
78 79

    case 'user.role_list':
80
      return '<p>' . t('A role defines a group of users that have certain privileges. These privileges are defined on the <a href="!permissions">Permissions page</a>. Here, you can define the names and the display sort order of the roles on your site. It is recommended to order roles from least permissive (for example, Anonymous user) to most permissive (for example, Administrator user). Users who are not logged in have the Anonymous user role. Users who are logged in have the Authenticated user role, plus any other roles granted to their user account.', array('!permissions' => \Drupal::url('user.admin_permissions'))) . '</p>';
81 82

    case 'field_ui.overview_user':
83
      return '<p>' . t('This form lets administrators add and edit fields for storing user data.') . '</p>';
84 85

    case 'field_ui.form_display_overview_user':
86
      return '<p>' . t('This form lets administrators configure how form fields should be displayed when editing a user profile.') . '</p>';
87 88

    case 'field_ui.display_overview_user':
89 90 91
      return '<p>' . t('This form lets administrators configure how fields should be displayed when rendering a user profile page.') . '</p>';
  }
}
92

93
/**
94
 * Implements hook_theme().
95 96 97
 */
function user_theme() {
  return array(
98
    'user' => array(
99
      'render element' => 'elements',
100
      'file' => 'user.pages.inc',
101
      'template' => 'user',
102
    ),
103
    'username' => array(
104 105
      'variables' => array('account' => NULL, 'attributes' => array(), 'link_options' => array()),
      'template' => 'username',
106
    ),
107 108 109
  );
}

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

/**
 * Implements hook_js_alter().
 */
function user_js_alter(&$javascript) {
  // If >=1 JavaScript asset has declared a dependency on drupalSettings, the
  // 'settings' key will exist. Thus when that key does not exist, return early.
  if (!isset($javascript['settings'])) {
    return;
  }
127 128 129 130

  // Provide the user ID in drupalSettings to allow JavaScript code to customize
  // the experience for the end user, rather than the server side, which would
  // break the render cache.
131 132 133
  // Similarly, provide a permissions hash, so that permission-dependent data
  // can be reliably cached on the client side.
  $user = \Drupal::currentUser();
134 135
  $javascript['settings']['data'][] = array(
    'user' => array(
136 137
      'uid' => $user->id(),
      'permissionsHash' => \Drupal::service('user.permissions_hash')->generate($user),
138
    ),
139
  );
140 141
}

142
/**
143
 * Entity URI callback.
144
 */
145
function user_uri($user) {
146 147 148
  return new Url('user.view', array(
    'user' => $user->id(),
  ));
149 150
}

151 152 153
/**
 * Populates $entity->account for each prepared entity.
 *
154
 * Called by Drupal\Core\Entity\EntityViewBuilderInterface::buildComponents()
155
 * implementations.
156
 *
157 158
 * @param array &$build
 *   A renderable array representing the entity content.
159
 * @param \Drupal\user\EntityOwnerInterface[] $entities
160 161
 *   The entities keyed by entity ID.
 */
162
function user_attach_accounts(array &$build, array $entities) {
163 164
  $uids = array();
  foreach ($entities as $entity) {
165
    $uids[] = $entity->getOwnerId();
166 167 168
  }
  $uids = array_unique($uids);
  $accounts = user_load_multiple($uids);
169
  $anonymous = entity_create('user', array('uid' => 0));
170

171
  foreach ($entities as $id => $entity) {
172 173
    if (isset($accounts[$entity->getOwnerId()])) {
      $entities[$id]->setOwner($accounts[$entity->getOwnerId()]);
174 175
    }
    else {
176
      $entities[$id]->setOwner($anonymous);
177 178 179 180 181 182 183 184 185 186 187 188 189
    }
  }
}

/**
 * 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() {
190 191
  $field_definitions = \Drupal::entityManager()->getFieldDefinitions('user', 'user');
  return isset($field_definitions['user_picture']);
192 193
}

194
/**
195
 * Implements hook_entity_extra_field_info().
196
 */
197
function user_entity_extra_field_info() {
198 199 200 201 202
  $fields['user']['user']['form']['account'] = array(
    'label' => t('User name and password'),
    'description' => t('User module account form elements.'),
    'weight' => -10,
  );
203
  if (\Drupal::config('user.settings')->get('signatures')) {
204 205 206 207 208 209 210 211 212 213 214
    $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,
  );
215
  if (\Drupal::config('system.date')->get('timezone.user.configurable')) {
216 217 218 219 220 221 222 223 224 225 226
    $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,
227
  );
228

229
  return $fields;
230 231
}

232
/**
233
 * Loads multiple users based on certain conditions.
234
 *
235 236 237
 * 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.
238
 *
239 240
 * @param array $uids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
241
 * @param bool $reset
242 243
 *   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.
244
 *
245
 * @return array
246 247
 *   An array of user objects, indexed by uid.
 *
248
 * @see entity_load_multiple()
249 250 251
 * @see user_load()
 * @see user_load_by_mail()
 * @see user_load_by_name()
252
 * @see \Drupal\Core\Entity\Query\QueryInterface
253 254 255
 *
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\user\Entity\User::loadMultiple().
256
 */
257
function user_load_multiple(array $uids = NULL, $reset = FALSE) {
258 259 260 261
  if ($reset) {
    \Drupal::entityManager()->getStorage('user')->resetCache($uids);
  }
  return User::loadMultiple($uids);
262
}
263 264

/**
265 266 267 268 269 270
 * 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
271 272 273 274
 * 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.
275
 *
276
 * @param int $uid
277
 *   Integer specifying the user ID to load.
278
 * @param bool $reset
279 280 281
 *   TRUE to reset the internal cache and load from the database; FALSE
 *   (default) to load from the internal cache, if set.
 *
282
 * @return \Drupal\user\UserInterface
283
 *   A fully-loaded user object upon successful user load, or NULL if the user
284 285
 *   cannot be loaded.
 *
286 287 288
 * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
 *   Use \Drupal\user\Entity\User::load().
 *
289 290 291
 * @see user_load_multiple()
 */
function user_load($uid, $reset = FALSE) {
292 293 294 295
  if ($reset) {
    \Drupal::entityManager()->getStorage('user')->resetCache(array($uid));
  }
  return User::load($uid);
296 297 298
}

/**
299
 * Fetches a user object by email address.
300
 *
301
 * @param string $mail
302
 *   String with the account's email address.
303
 * @return object|bool
304 305 306 307 308 309
 *   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) {
310
  $users = entity_load_multiple_by_properties('user', array('mail' => $mail));
311
  return $users ? reset($users) : FALSE;
312 313 314
}

/**
315
 * Fetches a user object by account name.
316
 *
317
 * @param string $name
318
 *   String with the account's user name.
319
 * @return object|bool
320 321 322 323 324 325
 *   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) {
326
  $users = entity_load_multiple_by_properties('user', array('name' => $name));
327
  return $users ? reset($users) : FALSE;
328 329
}

330 331
/**
 * Verify the syntax of the given name.
332 333 334 335 336 337 338 339
 *
 * @param string $name
 *   The user name to validate.
 *
 * @return string|null
 *   A translated violation message if the name is invalid or NULL if the name
 *   is valid.
 *
340
 */
341
function user_validate_name($name) {
342
  $definition = DataDefinition::create('string')
343
    ->addConstraint('UserName', array());
344
  $data = \Drupal::typedDataManager()->create($definition);
345 346 347 348
  $data->setValue($name);
  $violations = $data->validate();
  if (count($violations) > 0) {
    return $violations[0]->getMessage();
349
  }
350 351
}

352 353 354
/**
 * Generate a random alphanumeric password.
 */
355 356
function user_password($length = 10) {
  // This variable contains the list of allowable characters for the
357 358
  // password. Note that the number 0 and the letter 'O' have been
  // removed to avoid confusion between the two. The same is true
359
  // of 'I', 1, and 'l'.
360
  $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
361

362 363
  // Zero-based count of characters in the allowable list:
  $len = strlen($allowable_characters) - 1;
364

365 366
  // Declare the password as a blank string.
  $pass = '';
367

368
  // Loop the number of times specified by $length.
369
  for ($i = 0; $i < $length; $i++) {
370 371 372 373
    do {
      // Find a secure random number within the range needed.
      $index = ord(Crypt::randomBytes(1));
    } while ($index > $len);
374 375 376

    // Each iteration, pick a random character from the
    // allowable string and append it to the password:
377
    $pass .= $allowable_characters[$index];
378 379 380
  }

  return $pass;
381 382
}

383 384 385
/**
 * Determine the permissions for one or more roles.
 *
386 387
 * @param array $roles
 *   An array of role IDs.
388
 *
389 390 391
 * @return array
 *   An array indexed by role ID. Each value is an array of permission strings
 *   for the given role.
392
 */
393 394 395 396 397 398
function user_role_permissions(array $roles) {
  if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
    return _user_role_permissions_update($roles);
  }
  $entities = entity_load_multiple('user_role', $roles);
  $role_permissions = array();
399
  foreach ($roles as $rid) {
400
    $role_permissions[$rid] = isset($entities[$rid]) ? $entities[$rid]->getPermissions() : array();
401
  }
402 403
  return $role_permissions;
}
404

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
/**
 * Determine the permissions for one or more roles during update.
 *
 * A separate version is needed because during update the entity system can't
 * be used and in non-update situations the entity system is preferred because
 * of the hook system.
 *
 * @param array $roles
 *   An array of role IDs.
 *
 * @return array
 *   An array indexed by role ID. Each value is an array of permission strings
 *   for the given role.
 */
function _user_role_permissions_update($roles) {
  $role_permissions = array();
  foreach ($roles as $rid) {
422
    $role_permissions[$rid] = \Drupal::config("user.role.$rid")->get('permissions') ?: array();
423 424 425 426
  }
  return $role_permissions;
}

427 428 429 430 431
/**
 * Determine whether the user has a given privilege.
 *
 * @param $string
 *   The permission, such as "administer nodes", being checked for.
432
 * @param \Drupal\Core\Session\AccountInterface $account
433
 *   (optional) The account to check, if not given use currently logged in user.
434
 *
435
 * @return bool
436
 *   Boolean TRUE if the current user has the requested permission.
437
 *
438 439
 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
 *   Use \Drupal\Core\Session\AccountInterface::hasPermission().
440
 */
441
function user_access($string, AccountInterface $account = NULL) {
442
  global $user;
443

444
  if (!isset($account)) {
445 446
    // In the installer request session is not set, so we have to fall back
    // to the global $user. In all other cases the session key is preferred.
447
    $account = \Drupal::currentUser() ?: $user;
448 449
  }

450
  return $account->hasPermission($string);
451 452
}

453
/**
454
 * Checks for usernames blocked by user administration.
455
 *
456 457 458
 * @param $name
 *   A string containing a name of the user.
 *
459 460
 * @return bool
 *   TRUE if the user is blocked, FALSE otherwise.
461 462
 */
function user_is_blocked($name) {
463 464
  return (bool) \Drupal::entityQuery('user')
    ->condition('name', $name)
465
    ->condition('status', 0)
466
    ->execute();
467 468
}

469
/**
470
 * Implements hook_permission().
471
 */
472
function user_permission() {
473 474 475
  return array(
    'administer permissions' =>  array(
      'title' => t('Administer permissions'),
476
      'restrict access' => TRUE,
477
    ),
478 479 480 481 482
    'administer account settings' => array(
      'title' => t('Administer account settings'),
      'description' => t('Configure site-wide settings and behavior for <a href="@url">user accounts and registration</a>.', array('@url' => url('admin/config/people'))),
      'restrict access' => TRUE,
    ),
483 484
    'administer users' => array(
      'title' => t('Administer users'),
485
      'restrict access' => TRUE,
486 487
    ),
    'access user profiles' => array(
488
      'title' => t('View user profiles'),
489 490 491 492 493
    ),
    'change own username' => array(
      'title' => t('Change own username'),
    ),
    'cancel account' => array(
494
      'title' => t('Cancel own user account'),
495
      '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' => \Drupal::config('user.settings')->get('anonymous'), '@user-settings-url' => url('admin/config/people/accounts'))),
496 497 498
    ),
    'select account cancellation method' => array(
      'title' => t('Select method for cancelling own account'),
499
      'restrict access' => TRUE,
500 501
    ),
  );
502 503
}

504
/**
505
 * Implements hook_user_view().
506
 */
507
function user_user_view(array &$build, UserInterface $account, EntityViewDisplayInterface $display) {
508
  if ($display->getComponent('member_for')) {
509
    $build['member_for'] = array(
510 511
      '#type' => 'item',
      '#title' => t('Member for'),
512
      '#markup' => format_interval(REQUEST_TIME - $account->getCreatedTime()),
513 514
    );
  }
515 516
}

517 518 519 520 521 522 523 524 525
/**
 * 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'];
526 527
}

528
/**
529
 * Form validation handler for the current password on the user account form.
530
 *
531
 * @see AccountForm::form()
532 533
 */
function user_validate_current_pass(&$form, &$form_state) {
534
  $account = $form_state['user'];
535 536 537 538
  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.
539
    $current_value = $account->hasField($key) ? $account->get($key)->value : $account->$key;
540
    if ((strlen(trim($form_state['values'][$key])) > 0) && ($form_state['values'][$key] != $current_value)) {
541
      $current_pass_failed = empty($form_state['values']['current_pass']) || !\Drupal::service('password')->check($form_state['values']['current_pass'], $account);
542
      if ($current_pass_failed) {
543 544
        form_set_error('current_pass', $form_state, t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => $name)));
        form_set_error($key, $form_state);
545 546 547 548 549 550 551
      }
      // We only need to check the password once.
      break;
    }
  }
}

552
/**
553
 * Implements hook_preprocess_HOOK() for block templates.
554 555
 */
function user_preprocess_block(&$variables) {
556
  if ($variables['configuration']['provider'] == 'user') {
557
    switch ($variables['elements']['#plugin_id']) {
558
      case 'user_login_block':
559
        $variables['attributes']['role'] = 'form';
560 561 562 563 564
        break;
    }
  }
}

565 566 567
/**
 * Format a username.
 *
568
 * @param \Drupal\Core\Session\Interface $account
569 570 571 572
 *   The account object for the user whose name is to be formatted.
 *
 * @return
 *   An unsanitized string with the username to display. The code receiving
573 574
 *   this result must ensure that \Drupal\Component\Utility\String::checkPlain()
 *   is called on it before it is printed to the page.
575
 *
576 577
 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
 *   Use \Drupal\Core\Session\Interface::getUsername().
578
 */
579 580
function user_format_name(AccountInterface $account) {
  return $account->getUsername();
581 582
}

583 584 585 586 587 588 589
/**
 * Implements hook_template_preprocess_default_variables_alter().
 *
 * @see user_user_login()
 * @see user_user_logout()
 */
function user_template_preprocess_default_variables_alter(&$variables) {
590
  $user = \Drupal::currentUser();
591

592 593 594 595 596 597
  // If this function is called from the installer after Drupal has been
  // installed then $user will not be set.
  if (!is_object($user)) {
    return;
  }

598
  $variables['user'] = clone $user;
599
  // Remove password and session IDs, $form_state, since themes should not need nor see them.
600 601
  unset($variables['user']->pass, $variables['user']->sid, $variables['user']->ssid);

602
  $variables['is_admin'] = $user->hasPermission('access administration pages');
603
  $variables['logged_in'] = $user->isAuthenticated();
604 605
}

606
/**
607 608 609 610 611 612 613
 * Prepares variables for username templates.
 *
 * Default template: username.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - account: The user account (Drupal\user\Plugin\Core\Entity\User).
614
 *
615
 * Modules that make any changes to variables like 'name' or 'extra' must ensure
616
 * that the final string is safe to include directly in the output by using
617 618
 * \Drupal\Component\Utility\String::checkPlain() or
 * \Drupal\Component\Utility\Xss::filter().
619 620
 */
function template_preprocess_username(&$variables) {
621
  $account = $variables['account'] ?: new AnonymousUserSession();
622 623

  $variables['extra'] = '';
624 625 626 627 628
  $variables['uid'] = $account->id();
  if (empty($variables['uid'])) {
    if (theme_get_setting('features.comment_user_verification')) {
      $variables['extra'] = ' (' . t('not verified') . ')';
    }
629 630 631 632 633 634 635
  }

  // 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.
636
  $name = $variables['name_raw'] = $account->getUsername();
637
  if (drupal_strlen($name) > 20) {
638
    $name = Unicode::truncate($name, 15, FALSE, TRUE);
639 640 641 642
    $variables['truncated'] = TRUE;
  }
  else {
    $variables['truncated'] = FALSE;
643
  }
644
  $variables['name'] = String::checkPlain($name);
645
  $variables['profile_access'] = \Drupal::currentUser()->hasPermission('access user profiles');
646

647 648 649
  // Populate link path and attributes if appropriate.
  if ($variables['uid'] && $variables['profile_access']) {
    // We are linking to a local user.
650
    $variables['attributes']['title'] = t('View user profile.');
651 652 653 654 655 656
    $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.
657
    $variables['attributes']['rel'] = 'nofollow';
658 659 660 661
    $variables['link_path'] = $account->homepage;
    $variables['homepage'] = $account->homepage;
  }
  // Set a default class.
662 663 664 665
  $variables['attributes']['class'] = array('username');
  // We have a link path, so we should generate a link using url().
  // Additional classes may be added as array elements like
  // $variables['attributes']['class'][] = 'myclass';
666
  if (isset($variables['link_path'])) {
667
    $variables['attributes']['href'] = url($variables['link_path'], $variables['link_options']);
668 669 670
  }
}

671
/**
672
 * Implements hook_menu_link_presave().
673
 */
674
function user_menu_link_presave(MenuLink $menu_link) {
675 676 677
  // The path 'user' must be accessible for anonymous users, but only visible
  // for authenticated users. Authenticated users should see "My account", but
  // anonymous users should not see it at all. Therefore, invoke
678
  // user_menu_link_load() to conditionally hide the link.
679
  if ($menu_link->machine_name == 'user.page') {
680
    $menu_link->options['alter'] = TRUE;
681
  }
682 683
}

684 685 686 687 688 689
/**
 * Implements hook_menu_breadcrumb_alter().
 */
function user_menu_breadcrumb_alter(&$active_trail, $item) {
  // Remove "My account" from the breadcrumb when $item is descendant-or-self
  // of system path user/%.
690
  if (isset($active_trail[1]['module']) && $active_trail[1]['machine_name'] == 'user.page' && strpos($item['path'], 'user/%') === 0) {
691 692 693 694
    array_splice($active_trail, 1, 1);
  }
}

695
/**
696
 * Implements hook_translated_menu_link_alter().
697
 */
698
function user_translated_menu_link_alter(MenuLink &$menu_link) {
699
  // Hide the "User account" link for anonymous users.
700
  if ($menu_link->machine_name == 'user.page' && \Drupal::currentUser()->isAnonymous()) {
701
    $menu_link->hidden = 1;
702 703 704
  }
}

705
/**
706
 * Try to validate the user's login credentials locally.
707
 *
708 709 710 711 712 713
 * @param $name
 *   User name to authenticate.
 * @param $password
 *   A plain-text password, such as trimmed text from form values.
 * @return
 *   The user's uid on success, or FALSE on failure to authenticate.
714 715 716
 *
 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
 *   Use \Drupal\user\UserAuth::authenticate() instead.
717
 */
718
function user_authenticate($name, $password) {
719
  return \Drupal::service('user.auth')->authenticate($name, $password);
720 721
}

722
/**
723
 * Finalizes the login process and logs in a user.
724
 *
725 726 727
 * The function logs in the user, records a watchdog message about the new
 * session, saves the login timestamp, calls hook_user_login(), and generates a
 * new session.
728
 *
729 730
 * The global $user object is replaced with the passed in account.
 *
731
 * @param \Drupal\user\UserInterface $account
732
 *   The account to log in.
733 734
 *
 * @see hook_user_login()
735
 */
736
function user_login_finalize(UserInterface $account) {
737
  global $user;
738
  $user = $account;
739
  \Drupal::logger('user')->notice('Session opened for %name.', array('%name' => $account->getUsername()));
740 741
  // Update the user table timestamp noting user has logged in.
  // This is also used to invalidate one-time login links.
742
  $account->setLastLoginTime(REQUEST_TIME);
743
  \Drupal::entityManager()
744
    ->getStorage('user')
745
    ->updateLastLoginTimestamp($account);
746

747 748 749
  // Regenerate the session ID to prevent against session fixation attacks.
  // This is called before hook_user in case one of those functions fails
  // or incorrectly does a redirect which would leave the old session in place.
750
  \Drupal::service('session_manager')->regenerate();
751

752
  \Drupal::moduleHandler()->invokeAll('user_login', array($account));
753 754
}

755 756 757
/**
 * Implements hook_user_login().
 */
758
function user_user_login($account) {
759 760 761 762 763 764 765 766 767 768 769 770 771 772
  // Reset static cache of default variables in template_preprocess() to reflect
  // the new user.
  drupal_static_reset('template_preprocess');
}

/**
 * Implements hook_user_logout().
 */
function user_user_logout($account) {
  // Reset static cache of default variables in template_preprocess() to reflect
  // the new user.
  drupal_static_reset('template_preprocess');
}

773 774 775 776
/**
 * Generates a unique URL for a user to login and reset their password.
 *
 * @param object $account
777 778 779 780
 *   An object containing the user account, which must contain at least the
 *   following properties:
 *   - uid: The user ID number.
 *   - login: The UNIX timestamp of the user's last login.
781 782 783
 * @param array $options
 *   (optional) A keyed array of settings. Supported options are:
 *   - langcode: A language code to be used when generating locale-sensitive
784
 *    URLs. If langcode is NULL the users preferred language is used.
785 786 787 788 789
 *
 * @return
 *   A unique URL that provides a one-time log in for the user, from which
 *   they can change their password.
 */
790
function user_pass_reset_url($account, $options = array()) {
791
  $timestamp = REQUEST_TIME;
792
  $langcode = isset($options['langcode']) ? $options['langcode'] : $account->getPreferredLangcode();
793
  $url_options = array('absolute' => TRUE, 'language' => \Drupal::languageManager()->getLanguage($langcode));
794
  return url("user/reset/" . $account->id() . "/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime()), $url_options);
795 796
}

797
/**
798 799 800 801 802
 * Generates a URL to confirm an account cancellation request.
 *
 * @param object $account
 *   The user account object, which must contain at least the following
 *   properties:
803
 *   - uid: The user ID number.
804
 *   - pass: The hashed user password string.
805
 *   - login: The UNIX timestamp of the user's last login.
806 807 808
 * @param array $options
 *   (optional) A keyed array of settings. Supported options are:
 *   - langcode: A language code to be used when generating locale-sensitive
809
 *     URLs. If langcode is NULL the users preferred language is used.
810 811 812 813
 *
 * @return
 *   A unique URL that may be used to confirm the cancellation of the user
 *   account.
814 815 816 817
 *
 * @see user_mail_tokens()
 * @see user_cancel_confirm()
 */
818
function user_cancel_url($account, $options = array()) {
819
  $timestamp = REQUEST_TIME;
820
  $langcode = isset($options['langcode']) ? $options['langcode'] : $account->getPreferredLangcode();
821
  $url_options = array('absolute' => TRUE, 'language' => \Drupal::languageManager()->getLanguage($langcode));
822
  return url("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime()), $url_options);
823 824
}

825 826 827 828 829 830 831 832
/**
 * Creates a unique hash value for use in time-dependent per-user URLs.
 *
 * This hash is normally used to build a unique and secure URL that is sent to
 * the user by email for purposes such as resetting the user's password. In
 * order to validate the URL, the same hash can be generated again, from the
 * same information, and compared to the hash value from the URL. The URL
 * normally contains both the time stamp and the numeric user ID. The login
833 834
 * timestamp and hashed password are retrieved from the database as necessary.
 * For a usage example, see user_cancel_url() and user_cancel_confirm().
835
 *
836
 * @param string $password
837
 *   The hashed user account password value.
838 839 840 841
 * @param int $timestamp
 *   A UNIX timestamp, typically REQUEST_TIME.
 * @param int $login
 *   The UNIX timestamp of the user's last login.
842 843 844 845
 *
 * @return
 *   A string that is safe for use in URLs and SQL statements.
 */
846
function user_pass_rehash($password, $timestamp, $login) {
847
  return Crypt::hmacBase64($timestamp . $login, drupal_get_hash_salt() . $password);
848 849
}

850
/**
851 852 853 854 855 856 857 858 859 860 861 862
 * Cancel a user account.
 *
 * Since the user cancellation process needs to be run in a batch, either
 * Form API will invoke it, or batch_process() needs to be invoked after calling
 * this function and should define the path to redirect to.
 *
 * @param $edit
 *   An array of submitted form values.
 * @param $uid
 *   The user ID of the user account to cancel.
 * @param $method
 *   The account cancellation method to use.
863
 *
864
 * @see _user_cancel()
865
 */
866
function user_cancel($edit, $uid, $method) {
867
  $account = user_load($uid);
868 869 870

  if (!$account) {
    drupal_set_message(t('The user account %id does not exist.', array('%id' => $uid)), 'error');
871
    \Drupal::logger('user')->error('Attempted to cancel non-existing user account: %id.', array('%id' => $uid));
872 873 874 875 876 877 878 879 880 881
    return;
  }

  // Initialize batch (to set title).
  $batch = array(
    'title' => t('Cancelling account'),
    'operations' => array(),
  );
  batch_set($batch);

882 883 884
  // When the 'user_cancel_delete' method is used, user_delete() is called,
  // which invokes hook_user_predelete() and hook_user_delete(). Modules
  // should use those hooks to respond to the account deletion.
885 886
  if ($method != 'user_cancel_delete') {
    // Allow modules to add further sets to this batch.
887
    \Drupal::moduleHandler()->invokeAll('user_cancel', array($edit, $account, $method));
888
  }
889 890 891 892 893 894 895 896

  // Finish the batch and actually cancel the account.
  $batch = array(
    'title' => t('Cancelling user account'),
    'operations' => array(
      array('_user_cancel', array($edit, $account, $method)),
    ),
  );
897 898

  // After cancelling account, ensure that user is logged out.
899
  if ($account->id() == \Drupal::currentUser()->id()) {
900 901 902 903 904
    // Batch API stores data in the session, so use the finished operation to
    // manipulate the current user's session id.
    $batch['finished'] = '_user_cancel_session_regenerate';
  }

905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
  batch_set($batch);

  // Batch processing is either handled via Form API or has to be invoked
  // manually.
}

/**
 * Last batch processing step for cancelling a user account.
 *
 * Since batch and session API require a valid user account, the actual
 * cancellation of a user account needs to happen last.
 *
 * @see user_cancel()
 */
function _user_cancel($edit, $account, $method) {
  global $user;
921
  $logger = \Drupal::logger('user');
922 923 924 925 926 927 928 929 930

  switch ($method) {
    case 'user_cancel_block':
    case 'user_cancel_block_unpublish':
    default:
      // Send account blocked notification if option was checked.
      if (!empty($edit['user_cancel_notify'])) {
        _user_mail_notify('status_blocked', $account);
      }
931
      $account->block();
932
      $account->save();
933
      drupal_set_message(t('%name has been disabled.', array('%name' => $account->getUsername())));
934
      $logger->notice('Blocked user: %name %email.', array('%name' => $account->getUsername(), '%email' => '<' . $account->getEmail() . '>'));
935 936 937 938 939 940 941 942
      break;

    case 'user_cancel_reassign':
    case 'user_cancel_delete':
      // Send account canceled notification if option was checked.
      if (!empty($edit['user_cancel_notify'])) {
        _user_mail_notify('status_canceled', $account);
      }
943
      $account->delete();
944
      drupal_set_message(t('%name has been deleted.', array('%name' => $account->getUsername())));
945
      $logger->notice('Deleted user: %name %email.', array('%name' => $account->getUsername(), '%email' => '<' . $account->getEmail() . '>'));
946 947 948
      break;
  }

949 950 951 952
  // After cancelling account, ensure that user is logged out. We can't destroy
  // their session though, as we might have information in it, and we can't
  // regenerate it because batch API uses the session ID, we will regenerate it
  // in _user_cancel_session_regenerate().
953
  if ($account->id() == $user->id()) {