783 lines
28 KiB
PHP
783 lines
28 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file
|
|
*/
|
|
|
|
use Drupal\Component\Utility\Crypt;
|
|
use Drupal\Component\Utility\Unicode;
|
|
use Drupal\Core\Access\AccessibleInterface;
|
|
use Drupal\Core\Batch\BatchBuilder;
|
|
use Drupal\Core\Form\FormStateInterface;
|
|
use Drupal\Core\Render\Element;
|
|
use Drupal\Core\Session\AccountInterface;
|
|
use Drupal\Core\Session\AnonymousUserSession;
|
|
use Drupal\Core\Site\Settings;
|
|
use Drupal\Core\Url;
|
|
use Drupal\user\Entity\User;
|
|
use Drupal\user\UserInterface;
|
|
|
|
/**
|
|
* 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() {
|
|
$field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('user', 'user');
|
|
return isset($field_definitions['user_picture']);
|
|
}
|
|
|
|
/**
|
|
* Fetches a user object by email address.
|
|
*
|
|
* @param string $mail
|
|
* String with the account's email address.
|
|
*
|
|
* @return \Drupal\user\UserInterface|false
|
|
* A fully-loaded $user object upon successful user load or FALSE if user
|
|
* cannot be loaded.
|
|
*
|
|
* @see \Drupal\user\Entity\User::loadMultiple()
|
|
*/
|
|
function user_load_by_mail($mail) {
|
|
$users = \Drupal::entityTypeManager()->getStorage('user')
|
|
->loadByProperties(['mail' => $mail]);
|
|
return $users ? reset($users) : FALSE;
|
|
}
|
|
|
|
/**
|
|
* Fetches a user object by account name.
|
|
*
|
|
* @param string $name
|
|
* String with the account's user name.
|
|
*
|
|
* @return \Drupal\user\UserInterface|false
|
|
* A fully-loaded $user object upon successful user load or FALSE if user
|
|
* cannot be loaded.
|
|
*
|
|
* @see \Drupal\user\Entity\User::loadMultiple()
|
|
*/
|
|
function user_load_by_name($name) {
|
|
$users = \Drupal::entityTypeManager()->getStorage('user')
|
|
->loadByProperties(['name' => $name]);
|
|
return $users ? reset($users) : FALSE;
|
|
}
|
|
|
|
/**
|
|
* Verify the syntax of the given name.
|
|
*
|
|
* @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.
|
|
*
|
|
* @deprecated in drupal:10.3.0 and is removed from drupal:12.0.0. Use
|
|
* \Drupal\user\UserNameValidator::validateName() instead.
|
|
*
|
|
* @see https://www.drupal.org/node/3431205
|
|
*/
|
|
function user_validate_name($name) {
|
|
@trigger_error(__METHOD__ . '() is deprecated in drupal:10.3.0 and is removed from drupal:12.0.0. Use \Drupal\user\UserNameValidator::validateName() instead. See https://www.drupal.org/node/3431205', E_USER_DEPRECATED);
|
|
$violations = \Drupal::service('user.name_validator')->validateName($name);
|
|
if (count($violations) > 0) {
|
|
return $violations[0]->getMessage();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks for usernames blocked by user administration.
|
|
*
|
|
* @param string $name
|
|
* A string containing a name of the user.
|
|
*
|
|
* @return bool
|
|
* TRUE if the user is blocked, FALSE otherwise.
|
|
*
|
|
* @deprecated in drupal:11.0.0 and is removed from drupal:12.0.0. Use
|
|
* Drupal\user\UserInterface::isBlocked() instead.
|
|
* @see https://www.drupal.org/node/3411040
|
|
*/
|
|
function user_is_blocked($name) {
|
|
@trigger_error('user_is_blocked() is deprecated in drupal:11.0.0 and is removed from drupal:12.0.0. Use \Drupal\user\UserInterface::isBlocked() instead. See https://www.drupal.org/node/3411040', E_USER_DEPRECATED);
|
|
return (bool) \Drupal::entityQuery('user')
|
|
->accessCheck(FALSE)
|
|
->condition('name', $name)
|
|
->condition('status', 0)
|
|
->execute();
|
|
}
|
|
|
|
/**
|
|
* Implements hook_preprocess_HOOK() for block templates.
|
|
*/
|
|
function user_preprocess_block(&$variables): void {
|
|
if ($variables['configuration']['provider'] == 'user') {
|
|
switch ($variables['elements']['#plugin_id']) {
|
|
case 'user_login_block':
|
|
$variables['attributes']['role'] = 'form';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prepares variables for username templates.
|
|
*
|
|
* Default template: username.html.twig.
|
|
*
|
|
* Modules that make any changes to variables like 'name' or 'extra' must ensure
|
|
* that the final string is safe.
|
|
*
|
|
* @param array $variables
|
|
* An associative array containing:
|
|
* - account: The user account (\Drupal\Core\Session\AccountInterface).
|
|
*/
|
|
function template_preprocess_username(&$variables): void {
|
|
$account = $variables['account'] ?: new AnonymousUserSession();
|
|
|
|
$variables['extra'] = '';
|
|
$variables['uid'] = $account->id();
|
|
if (empty($variables['uid'])) {
|
|
if (theme_get_setting('features.comment_user_verification')) {
|
|
$variables['extra'] = ' (' . t('not verified') . ')';
|
|
}
|
|
}
|
|
|
|
// Set the name to a formatted name that is safe for printing and
|
|
// that won't break tables by being too long. Keep an un-shortened,
|
|
// 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.
|
|
$name = $account->getDisplayName();
|
|
$variables['name_raw'] = $account->getAccountName();
|
|
if (mb_strlen($name) > 20) {
|
|
$name = Unicode::truncate($name, 15, FALSE, TRUE);
|
|
$variables['truncated'] = TRUE;
|
|
}
|
|
else {
|
|
$variables['truncated'] = FALSE;
|
|
}
|
|
$variables['name'] = $name;
|
|
if ($account instanceof AccessibleInterface) {
|
|
$variables['profile_access'] = $account->access('view');
|
|
}
|
|
else {
|
|
$variables['profile_access'] = \Drupal::currentUser()->hasPermission('access user profiles');
|
|
}
|
|
|
|
$external = FALSE;
|
|
// Populate link path and attributes if appropriate.
|
|
if ($variables['uid'] && $variables['profile_access']) {
|
|
// We are linking to a local user.
|
|
$variables['attributes']['title'] = t('View user profile.');
|
|
$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.
|
|
$variables['attributes']['rel'] = 'nofollow';
|
|
$variables['link_path'] = $account->homepage;
|
|
$variables['homepage'] = $account->homepage;
|
|
$external = TRUE;
|
|
}
|
|
// We have a link path, so we should generate a URL.
|
|
if (isset($variables['link_path'])) {
|
|
if ($external) {
|
|
$variables['attributes']['href'] = Url::fromUri($variables['link_path'], $variables['link_options'])
|
|
->toString();
|
|
}
|
|
else {
|
|
$variables['attributes']['href'] = Url::fromRoute('entity.user.canonical', [
|
|
'user' => $variables['uid'],
|
|
])->toString();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finalizes the login process and logs in a user.
|
|
*
|
|
* 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.
|
|
*
|
|
* The current user is replaced with the passed in account.
|
|
*
|
|
* @param \Drupal\user\UserInterface $account
|
|
* The account to log in.
|
|
*
|
|
* @see hook_user_login()
|
|
* @see \Drupal\user\Authentication\Provider\Cookie
|
|
*/
|
|
function user_login_finalize(UserInterface $account): void {
|
|
\Drupal::currentUser()->setAccount($account);
|
|
\Drupal::logger('user')->info('Session opened for %name.', ['%name' => $account->getAccountName()]);
|
|
// Update the user table timestamp noting user has logged in.
|
|
// This is also used to invalidate one-time login links.
|
|
$account->setLastLoginTime(\Drupal::time()->getRequestTime());
|
|
\Drupal::entityTypeManager()
|
|
->getStorage('user')
|
|
->updateLastLoginTimestamp($account);
|
|
|
|
// Regenerate the session ID to prevent against session fixation attacks.
|
|
// This is called before hook_user_login() in case one of those functions
|
|
// fails or incorrectly does a redirect which would leave the old session
|
|
// in place.
|
|
/** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
|
|
$session = \Drupal::service('session');
|
|
$session->migrate();
|
|
$session->set('uid', $account->id());
|
|
$session->set('check_logged_in', TRUE);
|
|
\Drupal::moduleHandler()->invokeAll('user_login', [$account]);
|
|
}
|
|
|
|
/**
|
|
* Generates a unique URL for a user to log in and reset their password.
|
|
*
|
|
* @param \Drupal\user\UserInterface $account
|
|
* An object containing the user account.
|
|
* @param array $options
|
|
* (optional) A keyed array of settings. Supported options are:
|
|
* - langcode: A language code to be used when generating locale-sensitive
|
|
* URLs. If langcode is NULL the users preferred language is used.
|
|
*
|
|
* @return string
|
|
* A unique URL that provides a one-time log in for the user, from which
|
|
* they can change their password.
|
|
*/
|
|
function user_pass_reset_url($account, $options = []) {
|
|
$timestamp = \Drupal::time()->getCurrentTime();
|
|
$langcode = $options['langcode'] ?? $account->getPreferredLangcode();
|
|
return Url::fromRoute('user.reset',
|
|
[
|
|
'uid' => $account->id(),
|
|
'timestamp' => $timestamp,
|
|
'hash' => user_pass_rehash($account, $timestamp),
|
|
],
|
|
[
|
|
'absolute' => TRUE,
|
|
'language' => \Drupal::languageManager()->getLanguage($langcode),
|
|
]
|
|
)->toString();
|
|
}
|
|
|
|
/**
|
|
* Generates a URL to confirm an account cancellation request.
|
|
*
|
|
* @param \Drupal\user\UserInterface $account
|
|
* The user account object.
|
|
* @param array $options
|
|
* (optional) A keyed array of settings. Supported options are:
|
|
* - langcode: A language code to be used when generating locale-sensitive
|
|
* URLs. If langcode is NULL the users preferred language is used.
|
|
*
|
|
* @return string
|
|
* A unique URL that may be used to confirm the cancellation of the user
|
|
* account.
|
|
*
|
|
* @see user_mail_tokens()
|
|
* @see \Drupal\user\Controller\UserController::confirmCancel()
|
|
*/
|
|
function user_cancel_url(UserInterface $account, $options = []) {
|
|
$timestamp = \Drupal::time()->getRequestTime();
|
|
$langcode = $options['langcode'] ?? $account->getPreferredLangcode();
|
|
$url_options = ['absolute' => TRUE, 'language' => \Drupal::languageManager()->getLanguage($langcode)];
|
|
return Url::fromRoute('user.cancel_confirm', [
|
|
'user' => $account->id(),
|
|
'timestamp' => $timestamp,
|
|
'hashed_pass' => user_pass_rehash($account, $timestamp),
|
|
], $url_options)->toString();
|
|
}
|
|
|
|
/**
|
|
* 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 hash
|
|
* contains the time stamp, the user's last login time, the numeric user ID,
|
|
* and the user's email address.
|
|
* For a usage example, see user_cancel_url() and
|
|
* \Drupal\user\Controller\UserController::confirmCancel().
|
|
*
|
|
* @param \Drupal\user\UserInterface $account
|
|
* An object containing the user account.
|
|
* @param int $timestamp
|
|
* A UNIX timestamp, typically \Drupal::time()->getRequestTime().
|
|
*
|
|
* @return string
|
|
* A string that is safe for use in URLs and SQL statements.
|
|
*/
|
|
function user_pass_rehash(UserInterface $account, $timestamp) {
|
|
$data = $timestamp;
|
|
$data .= ':' . $account->getLastLoginTime();
|
|
$data .= ':' . $account->id();
|
|
$data .= ':' . $account->getEmail();
|
|
return Crypt::hmacBase64($data, Settings::getHashSalt() . $account->getPassword());
|
|
}
|
|
|
|
/**
|
|
* 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 array $edit
|
|
* An array of submitted form values.
|
|
* @param int $uid
|
|
* The user ID of the user account to cancel.
|
|
* @param string $method
|
|
* The account cancellation method to use.
|
|
*
|
|
* @see _user_cancel()
|
|
*/
|
|
function user_cancel($edit, $uid, $method): void {
|
|
$account = User::load($uid);
|
|
|
|
if (!$account) {
|
|
\Drupal::messenger()->addError(t('The user account %id does not exist.', ['%id' => $uid]));
|
|
\Drupal::logger('user')->error('Attempted to cancel non-existing user account: %id.', ['%id' => $uid]);
|
|
return;
|
|
}
|
|
|
|
// Initialize batch (to set title).
|
|
$batch_builder = (new BatchBuilder())
|
|
->setTitle(t('Cancelling account'));
|
|
batch_set($batch_builder->toArray());
|
|
|
|
// When the 'user_cancel_delete' method is used, user_delete() is called,
|
|
// which invokes hook_ENTITY_TYPE_predelete() and hook_ENTITY_TYPE_delete()
|
|
// for the user entity. Modules should use those hooks to respond to the
|
|
// account deletion.
|
|
if ($method != 'user_cancel_delete') {
|
|
// Allow modules to add further sets to this batch.
|
|
\Drupal::moduleHandler()->invokeAll('user_cancel', [$edit, $account, $method]);
|
|
}
|
|
|
|
// Finish the batch and actually cancel the account.
|
|
$batch_builder = (new BatchBuilder())
|
|
->setTitle(t('Cancelling user account'))
|
|
->addOperation('_user_cancel', [$edit, $account, $method]);
|
|
|
|
// After cancelling account, ensure that user is logged out.
|
|
if ($account->id() == \Drupal::currentUser()->id()) {
|
|
// Batch API stores data in the session, so use the finished operation to
|
|
// manipulate the current user's session id.
|
|
$batch_builder->setFinishCallback('_user_cancel_session_regenerate');
|
|
}
|
|
|
|
batch_set($batch_builder->toArray());
|
|
|
|
// Batch processing is either handled via Form API or has to be invoked
|
|
// manually.
|
|
}
|
|
|
|
/**
|
|
* Implements callback_batch_operation().
|
|
*
|
|
* Last 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.
|
|
*
|
|
* @param array $edit
|
|
* An array of submitted form values.
|
|
* @param \Drupal\user\UserInterface $account
|
|
* The user ID of the user account to cancel.
|
|
* @param string $method
|
|
* The account cancellation method to use.
|
|
*
|
|
* @see user_cancel()
|
|
*/
|
|
function _user_cancel($edit, $account, $method): void {
|
|
$logger = \Drupal::logger('user');
|
|
|
|
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);
|
|
}
|
|
$account->block();
|
|
$account->save();
|
|
\Drupal::messenger()->addStatus(t('Account %name has been disabled.', ['%name' => $account->getDisplayName()]));
|
|
$logger->notice('Blocked user: %name %email.', ['%name' => $account->getAccountName(), '%email' => '<' . $account->getEmail() . '>']);
|
|
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);
|
|
}
|
|
$account->delete();
|
|
\Drupal::messenger()->addStatus(t('Account %name has been deleted.', ['%name' => $account->getDisplayName()]));
|
|
$logger->notice('Deleted user: %name %email.', ['%name' => $account->getAccountName(), '%email' => '<' . $account->getEmail() . '>']);
|
|
break;
|
|
}
|
|
|
|
// 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().
|
|
if ($account->id() == \Drupal::currentUser()->id()) {
|
|
\Drupal::currentUser()->setAccount(new AnonymousUserSession());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements callback_batch_finished().
|
|
*
|
|
* Finished batch processing callback for cancelling a user account.
|
|
*
|
|
* @see user_cancel()
|
|
*/
|
|
function _user_cancel_session_regenerate(): void {
|
|
// Regenerate the users session instead of calling session_destroy() as we
|
|
// want to preserve any messages that might have been set.
|
|
\Drupal::service('session')->migrate();
|
|
}
|
|
|
|
/**
|
|
* Helper function to return available account cancellation methods.
|
|
*
|
|
* See documentation of hook_user_cancel_methods_alter().
|
|
*
|
|
* @return array
|
|
* An array containing all account cancellation methods as form elements.
|
|
*
|
|
* @see hook_user_cancel_methods_alter()
|
|
* @see user_admin_settings()
|
|
*/
|
|
function user_cancel_methods(): array {
|
|
$user_settings = \Drupal::config('user.settings');
|
|
$anonymous_name = $user_settings->get('anonymous');
|
|
$methods = [
|
|
'user_cancel_block' => [
|
|
'title' => t('Disable the account and keep its content.'),
|
|
'description' => t('Your account will be blocked and you will no longer be able to log in. All of your content will remain attributed to your username.'),
|
|
],
|
|
'user_cancel_block_unpublish' => [
|
|
'title' => t('Disable the account and unpublish its content.'),
|
|
'description' => t('Your account will be blocked and you will no longer be able to log in. All of your content will be hidden from everyone but administrators.'),
|
|
],
|
|
'user_cancel_reassign' => [
|
|
'title' => t('Delete the account and make its content belong to the %anonymous-name user. This action cannot be undone.', ['%anonymous-name' => $anonymous_name]),
|
|
'description' => t('Your account will be removed and all account information deleted. All of your content will be assigned to the %anonymous-name user.', ['%anonymous-name' => $anonymous_name]),
|
|
],
|
|
'user_cancel_delete' => [
|
|
'title' => t('Delete the account and its content. This action cannot be undone.'),
|
|
'description' => t('Your account will be removed and all account information deleted. All of your content will also be deleted.'),
|
|
'access' => \Drupal::currentUser()->hasPermission('administer users'),
|
|
],
|
|
];
|
|
// Allow modules to customize account cancellation methods.
|
|
\Drupal::moduleHandler()->alter('user_cancel_methods', $methods);
|
|
|
|
// Turn all methods into real form elements.
|
|
$form = [
|
|
'#options' => [],
|
|
'#default_value' => $user_settings->get('cancel_method'),
|
|
];
|
|
foreach ($methods as $name => $method) {
|
|
$form['#options'][$name] = $method['title'];
|
|
// Add the description for the confirmation form. This description is never
|
|
// shown for the cancel method option, only on the confirmation form.
|
|
// Therefore, we use a custom #confirm_description property.
|
|
if (isset($method['description'])) {
|
|
$form[$name]['#confirm_description'] = $method['description'];
|
|
}
|
|
if (isset($method['access'])) {
|
|
$form[$name]['#access'] = $method['access'];
|
|
}
|
|
}
|
|
return $form;
|
|
}
|
|
|
|
/**
|
|
* Token callback to add unsafe tokens for user mails.
|
|
*
|
|
* This function is used by \Drupal\Core\Utility\Token::replace() to set up
|
|
* some additional tokens that can be used in email messages generated by
|
|
* user_mail().
|
|
*
|
|
* @param array $replacements
|
|
* An associative array variable containing mappings from token names to
|
|
* values (for use with strtr()).
|
|
* @param array $data
|
|
* An associative array of token replacement values. If the 'user' element
|
|
* exists, it must contain a user account object with the following
|
|
* properties:
|
|
* - login: The UNIX timestamp of the user's last login.
|
|
* - pass: The hashed account login password.
|
|
* @param array $options
|
|
* A keyed array of settings and flags to control the token replacement
|
|
* process. See \Drupal\Core\Utility\Token::replace().
|
|
*/
|
|
function user_mail_tokens(&$replacements, $data, $options): void {
|
|
if (isset($data['user'])) {
|
|
$replacements['[user:one-time-login-url]'] = user_pass_reset_url($data['user'], $options);
|
|
$replacements['[user:cancel-url]'] = user_cancel_url($data['user'], $options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Change permissions for a user role.
|
|
*
|
|
* This function may be used to grant and revoke multiple permissions at once.
|
|
* For example, when a form exposes checkboxes to configure permissions for a
|
|
* role, the form submit handler may directly pass the submitted values for the
|
|
* checkboxes form element to this function.
|
|
*
|
|
* @param mixed $rid
|
|
* The ID of a user role to alter.
|
|
* @param array $permissions
|
|
* (optional) An associative array, where the key holds the permission name
|
|
* and the value determines whether to grant or revoke that permission. Any
|
|
* value that evaluates to TRUE will cause the permission to be granted.
|
|
* Any value that evaluates to FALSE will cause the permission to be
|
|
* revoked.
|
|
* @code
|
|
* [
|
|
* 'administer nodes' => 0, // Revoke 'administer nodes'
|
|
* 'administer blocks' => FALSE, // Revoke 'administer blocks'
|
|
* 'access user profiles' => 1, // Grant 'access user profiles'
|
|
* 'access content' => TRUE, // Grant 'access content'
|
|
* 'access comments' => 'access comments', // Grant 'access comments'
|
|
* ]
|
|
* @endcode
|
|
* Existing permissions are not changed, unless specified in $permissions.
|
|
*
|
|
* @see user_role_grant_permissions()
|
|
* @see user_role_revoke_permissions()
|
|
*/
|
|
function user_role_change_permissions($rid, array $permissions = []): void {
|
|
// Grant new permissions for the role.
|
|
$grant = array_filter($permissions);
|
|
if (!empty($grant)) {
|
|
user_role_grant_permissions($rid, array_keys($grant));
|
|
}
|
|
// Revoke permissions for the role.
|
|
$revoke = array_diff_assoc($permissions, $grant);
|
|
if (!empty($revoke)) {
|
|
user_role_revoke_permissions($rid, array_keys($revoke));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Grant permissions to a user role.
|
|
*
|
|
* @param mixed $rid
|
|
* The ID of a user role to alter.
|
|
* @param array $permissions
|
|
* (optional) A list of permission names to grant.
|
|
*
|
|
* @see user_role_change_permissions()
|
|
* @see user_role_revoke_permissions()
|
|
*/
|
|
function user_role_grant_permissions($rid, array $permissions = []): void {
|
|
// Grant new permissions for the role.
|
|
$role_storage = \Drupal::entityTypeManager()->getStorage('user_role');
|
|
if ($role = $role_storage->loadOverrideFree($rid)) {
|
|
foreach ($permissions as $permission) {
|
|
$role->grantPermission($permission);
|
|
}
|
|
$role->trustData()->save();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Revoke permissions from a user role.
|
|
*
|
|
* @param mixed $rid
|
|
* The ID of a user role to alter.
|
|
* @param array $permissions
|
|
* (optional) A list of permission names to revoke.
|
|
*
|
|
* @see user_role_change_permissions()
|
|
* @see user_role_grant_permissions()
|
|
*/
|
|
function user_role_revoke_permissions($rid, array $permissions = []): void {
|
|
// Revoke permissions for the role.
|
|
$role_storage = \Drupal::entityTypeManager()->getStorage('user_role');
|
|
$role = $role_storage->loadOverrideFree($rid);
|
|
foreach ($permissions as $permission) {
|
|
$role->revokePermission($permission);
|
|
}
|
|
$role->trustData()->save();
|
|
}
|
|
|
|
/**
|
|
* Creates and sends a notification email following a change to a user account.
|
|
*
|
|
* @param string $op
|
|
* The operation being performed on the account. Possible values:
|
|
* - 'register_admin_created': Welcome message for user created by the admin.
|
|
* - 'register_no_approval_required': Welcome message when user
|
|
* self-registers.
|
|
* - 'register_pending_approval': Welcome message, user pending admin
|
|
* approval.
|
|
* - 'password_reset': Password recovery request.
|
|
* - 'status_activated': Account activated.
|
|
* - 'status_blocked': Account blocked.
|
|
* - 'cancel_confirm': Account cancellation request.
|
|
* - 'status_canceled': Account canceled.
|
|
* @param \Drupal\Core\Session\AccountInterface $account
|
|
* The user object of the account being notified. Must contain at
|
|
* least the fields 'uid', 'name', and 'mail'.
|
|
*
|
|
* @return array
|
|
* An array containing various information about the message.
|
|
* See \Drupal\Core\Mail\MailManagerInterface::mail() for details.
|
|
*
|
|
* @see user_mail_tokens()
|
|
*/
|
|
function _user_mail_notify($op, AccountInterface $account) {
|
|
|
|
if (\Drupal::config('user.settings')->get('notify.' . $op)) {
|
|
$params['account'] = $account;
|
|
// Get the custom site notification email to use as the from email address
|
|
// if it has been set.
|
|
$site_mail = \Drupal::config('system.site')->get('mail_notification');
|
|
// If the custom site notification email has not been set, we use the site
|
|
// default for this.
|
|
if (empty($site_mail)) {
|
|
$site_mail = \Drupal::config('system.site')->get('mail');
|
|
}
|
|
if (empty($site_mail)) {
|
|
$site_mail = ini_get('sendmail_from');
|
|
}
|
|
$mail = \Drupal::service('plugin.manager.mail')->mail('user', $op, $account->getEmail(), $account->getPreferredLangcode(), $params, $site_mail);
|
|
if ($op == 'register_pending_approval') {
|
|
// If a user registered requiring admin approval, notify the admin, too.
|
|
// We use the site default language for this.
|
|
\Drupal::service('plugin.manager.mail')->mail('user', 'register_pending_approval_admin', $site_mail, \Drupal::languageManager()->getDefaultLanguage()->getId(), $params);
|
|
}
|
|
}
|
|
return empty($mail) ? NULL : $mail['result'];
|
|
}
|
|
|
|
/**
|
|
* Form element process handler for client-side password validation.
|
|
*
|
|
* This #process handler is automatically invoked for 'password_confirm' form
|
|
* elements to add the JavaScript and string translations for dynamic password
|
|
* validation.
|
|
*/
|
|
function user_form_process_password_confirm($element) {
|
|
$password_settings = [
|
|
'confirmTitle' => t('Passwords match:'),
|
|
'confirmSuccess' => t('yes'),
|
|
'confirmFailure' => t('no'),
|
|
'showStrengthIndicator' => FALSE,
|
|
];
|
|
|
|
if (\Drupal::config('user.settings')->get('password_strength')) {
|
|
$password_settings['showStrengthIndicator'] = TRUE;
|
|
$password_settings += [
|
|
'strengthTitle' => t('Password strength:'),
|
|
'hasWeaknesses' => t('Recommendations to make your password stronger:'),
|
|
'tooShort' => t('Make it at least 12 characters'),
|
|
'addLowerCase' => t('Add lowercase letters'),
|
|
'addUpperCase' => t('Add uppercase letters'),
|
|
'addNumbers' => t('Add numbers'),
|
|
'addPunctuation' => t('Add punctuation'),
|
|
'sameAsUsername' => t('Make it different from your username'),
|
|
'weak' => t('Weak'),
|
|
'fair' => t('Fair'),
|
|
'good' => t('Good'),
|
|
'strong' => t('Strong'),
|
|
'username' => \Drupal::currentUser()->getAccountName(),
|
|
];
|
|
}
|
|
|
|
$element['#attached']['library'][] = 'user/drupal.user';
|
|
$element['#attached']['drupalSettings']['password'] = $password_settings;
|
|
|
|
return $element;
|
|
}
|
|
|
|
/**
|
|
* Saves visitor information as a cookie so it can be reused.
|
|
*
|
|
* @param array $values
|
|
* An array of key/value pairs to be saved into a cookie.
|
|
*/
|
|
function user_cookie_save(array $values): void {
|
|
$request_time = \Drupal::time()->getRequestTime();
|
|
foreach ($values as $field => $value) {
|
|
// Set cookie for 365 days.
|
|
setrawcookie('Drupal.visitor.' . $field, rawurlencode($value), $request_time + 31536000, '/');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a visitor information cookie.
|
|
*
|
|
* @param string $cookie_name
|
|
* A cookie name such as 'homepage'.
|
|
*/
|
|
function user_cookie_delete($cookie_name): void {
|
|
setrawcookie('Drupal.visitor.' . $cookie_name, '', \Drupal::time()->getRequestTime() - 3600, '/');
|
|
}
|
|
|
|
/**
|
|
* Logs the current user out.
|
|
*/
|
|
function user_logout(): void {
|
|
$user = \Drupal::currentUser();
|
|
|
|
\Drupal::logger('user')->info('Session closed for %name.', ['%name' => $user->getAccountName()]);
|
|
|
|
\Drupal::moduleHandler()->invokeAll('user_logout', [$user]);
|
|
|
|
// Destroy the current session, and reset $user to the anonymous user.
|
|
// Note: In Symfony the session is intended to be destroyed with
|
|
// Session::invalidate(). Regrettably this method is currently broken and may
|
|
// lead to the creation of spurious session records in the database.
|
|
// @see https://github.com/symfony/symfony/issues/12375
|
|
\Drupal::service('session_manager')->destroy();
|
|
$user->setAccount(new AnonymousUserSession());
|
|
}
|
|
|
|
/**
|
|
* Prepares variables for user templates.
|
|
*
|
|
* Default template: user.html.twig.
|
|
*
|
|
* @param array $variables
|
|
* An associative array containing:
|
|
* - elements: An associative array containing the user information and any
|
|
* fields attached to the user. Properties used:
|
|
* - #user: A \Drupal\user\Entity\User object. The user account of the
|
|
* profile being viewed.
|
|
* - attributes: HTML attributes for the containing element.
|
|
*/
|
|
function template_preprocess_user(&$variables): void {
|
|
$variables['user'] = $variables['elements']['#user'];
|
|
// Helpful $content variable for templates.
|
|
foreach (Element::children($variables['elements']) as $key) {
|
|
$variables['content'][$key] = $variables['elements'][$key];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Additional submit handler for \Drupal\system\Form\RegionalForm.
|
|
*/
|
|
function user_form_system_regional_settings_submit($form, FormStateInterface $form_state): void {
|
|
\Drupal::configFactory()->getEditable('system.date')
|
|
->set('timezone.user.configurable', $form_state->getValue('configurable_timezones'))
|
|
->set('timezone.user.warn', $form_state->getValue('empty_timezone_message'))
|
|
->set('timezone.user.default', $form_state->getValue('user_default_timezone'))
|
|
->save();
|
|
}
|