Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\user\Access\LoginStatusCheck.
*/
namespace Drupal\user\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
/**
* Determines access to routes based on login status of current user.
*/
class LoginStatusCheck implements AccessInterface {
/**
* Checks access.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
* @param \Symfony\Component\Routing\Route $route
* The route to check against.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(AccountInterface $account, Route $route) {
$required_status = filter_var($route->getRequirement('_user_is_logged_in'), FILTER_VALIDATE_BOOLEAN);
$actual_status = $account->isAuthenticated();
return AccessResult::allowedIf($required_status === $actual_status)->addCacheContexts(['user.roles:authenticated']);
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* @file
* Contains \Drupal\user\Access\PermissionAccessCheck.
*/
namespace Drupal\user\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
/**
* Determines access to routes based on permissions defined via
* $module.permissions.yml files.
*/
class PermissionAccessCheck implements AccessInterface {
/**
* Checks access.
*
* @param \Symfony\Component\Routing\Route $route
* The route to check against.
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(Route $route, AccountInterface $account) {
$permission = $route->getRequirement('_permission');
if ($permission === NULL) {
return AccessResult::neutral();
}
// Allow to conjunct the permissions with OR ('+') or AND (',').
$split = explode(',', $permission);
if (count($split) > 1) {
return AccessResult::allowedIfHasPermissions($account, $split, 'AND');
}
else {
$split = explode('+', $permission);
return AccessResult::allowedIfHasPermissions($account, $split, 'OR');
}
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* @file
* Contains \Drupal\user\Access\RegisterAccessCheck.
*/
namespace Drupal\user\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Access check for user registration routes.
*/
class RegisterAccessCheck implements AccessInterface {
/**
* Checks access.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(AccountInterface $account) {
$user_settings = \Drupal::config('user.settings');
return AccessResult::allowedIf($account->isAnonymous() && $user_settings->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY)->cacheUntilConfigurationChanges($user_settings);
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\user\Access\RoleAccessCheck.
*/
namespace Drupal\user\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
/**
* Determines access to routes based on roles.
*
* You can specify the '_role' key on route requirements. If you specify a
* single role, users with that role with have access. If you specify multiple
* ones you can conjunct them with AND by using a "," and with OR by using "+".
*/
class RoleAccessCheck implements AccessInterface {
/**
* Checks access.
*
* @param \Symfony\Component\Routing\Route $route
* The route to check against.
* @param \Drupal\Core\Session\AccountInterface $account
* The currently logged in account.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(Route $route, AccountInterface $account) {
// Requirements just allow strings, so this might be a comma separated list.
$rid_string = $route->getRequirement('_role');
$explode_and = array_filter(array_map('trim', explode(',', $rid_string)));
if (count($explode_and) > 1) {
$diff = array_diff($explode_and, $account->getRoles());
if (empty($diff)) {
return AccessResult::allowed()->addCacheContexts(['user.roles']);
}
}
else {
$explode_or = array_filter(array_map('trim', explode('+', $rid_string)));
$intersection = array_intersect($explode_or, $account->getRoles());
if (!empty($intersection)) {
return AccessResult::allowed()->addCacheContexts(['user.roles']);
}
}
// If there is no allowed role, give other access checks a chance.
return AccessResult::neutral()->addCacheContexts(['user.roles']);
}
}

View file

@ -0,0 +1,410 @@
<?php
/**
* @file
* Contains \Drupal\user\AccountForm.
*/
namespace Drupal\user;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityConstraintViolationListInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Url;
use Drupal\language\ConfigurableLanguageManagerInterface;
use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser;
use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for the user account forms.
*/
abstract class AccountForm extends ContentEntityForm {
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The entity query factory service.
*
* @var \Drupal\Core\Entity\Query\QueryFactory
*/
protected $entityQuery;
/**
* Constructs a new EntityForm object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
* The entity query factory.
*/
public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, QueryFactory $entity_query) {
parent::__construct($entity_manager);
$this->languageManager = $language_manager;
$this->entityQuery = $entity_query;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('language_manager'),
$container->get('entity.query')
);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
/** @var \Drupal\user\UserInterface $account */
$account = $this->entity;
$user = $this->currentUser();
$config = \Drupal::config('user.settings');
$form['#cache']['tags'] = $config->getCacheTags();
$language_interface = \Drupal::languageManager()->getCurrentLanguage();
$register = $account->isAnonymous();
$admin = $user->hasPermission('administer users');
// Account information.
$form['account'] = array(
'#type' => 'container',
'#weight' => -10,
);
// The mail field is NOT required if account originally had no mail set
// and the user performing the edit has 'administer users' permission.
// This allows users without email address to be edited and deleted.
// Also see \Drupal\user\Plugin\Validation\Constraint\UserMailRequired.
$form['account']['mail'] = array(
'#type' => 'email',
'#title' => $this->t('Email address'),
'#description' => $this->t('A valid email address. All emails from the system will be sent to this address. The email address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by email.'),
'#required' => !(!$account->getEmail() && $user->hasPermission('administer users')),
'#default_value' => (!$register ? $account->getEmail() : ''),
);
// Only show name field on registration form or user can change own username.
$form['account']['name'] = array(
'#type' => 'textfield',
'#title' => $this->t('Username'),
'#maxlength' => USERNAME_MAX_LENGTH,
'#description' => $this->t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
'#required' => TRUE,
'#attributes' => array(
'class' => array('username'),
'autocorrect' => 'off',
'autocapitalize' => 'off',
'spellcheck' => 'false',
),
'#default_value' => (!$register ? $account->getUsername() : ''),
'#access' => ($register || ($user->id() == $account->id() && $user->hasPermission('change own username')) || $admin),
);
// Display password field only for existing users or when user is allowed to
// assign a password during registration.
if (!$register) {
$form['account']['pass'] = array(
'#type' => 'password_confirm',
'#size' => 25,
'#description' => $this->t('To change the current user password, enter the new password in both fields.'),
);
// To skip the current password field, the user must have logged in via a
// one-time link and have the token in the URL. Store this in $form_state
// so it persists even on subsequent Ajax requests.
if (!$form_state->get('user_pass_reset')) {
$user_pass_reset = $pass_reset = isset($_SESSION['pass_reset_' . $account->id()]) && (\Drupal::request()->query->get('pass-reset-token') == $_SESSION['pass_reset_' . $account->id()]);
$form_state->set('user_pass_reset', $user_pass_reset);
}
$protected_values = array();
$current_pass_description = '';
// The user may only change their own password without their current
// password if they logged in via a one-time login link.
if (!$form_state->get('user_pass_reset')) {
$protected_values['mail'] = $form['account']['mail']['#title'];
$protected_values['pass'] = $this->t('Password');
$request_new = $this->l($this->t('Reset your password'), new Url('user.pass',
array(), array('attributes' => array('title' => $this->t('Send password reset instructions via e-mail.'))))
);
$current_pass_description = $this->t('Required if you want to change the %mail or %pass below. !request_new.',
array(
'%mail' => $protected_values['mail'],
'%pass' => $protected_values['pass'],
'!request_new' => $request_new,
)
);
}
// The user must enter their current password to change to a new one.
if ($user->id() == $account->id()) {
$form['account']['current_pass'] = array(
'#type' => 'password',
'#title' => $this->t('Current password'),
'#size' => 25,
'#access' => !empty($protected_values),
'#description' => $current_pass_description,
'#weight' => -5,
// Do not let web browsers remember this password, since we are
// trying to confirm that the person submitting the form actually
// knows the current one.
'#attributes' => array('autocomplete' => 'off'),
);
$form_state->set('user', $account);
}
}
elseif (!$config->get('verify_mail') || $admin) {
$form['account']['pass'] = array(
'#type' => 'password_confirm',
'#size' => 25,
'#description' => $this->t('Provide a password for the new account in both fields.'),
'#required' => TRUE,
);
}
// When not building the user registration form, prevent web browsers from
// autofilling/prefilling the email, username, and password fields.
if ($this->getOperation() != 'register') {
foreach (array('mail', 'name', 'pass') as $key) {
if (isset($form['account'][$key])) {
$form['account'][$key]['#attributes']['autocomplete'] = 'off';
}
}
}
if ($admin) {
$status = $account->isActive();
}
else {
$status = $register ? $config->get('register') == USER_REGISTER_VISITORS : $account->isActive();
}
$form['account']['status'] = array(
'#type' => 'radios',
'#title' => $this->t('Status'),
'#default_value' => $status,
'#options' => array($this->t('Blocked'), $this->t('Active')),
'#access' => $admin,
);
$roles = array_map(array('\Drupal\Component\Utility\SafeMarkup', 'checkPlain'), user_role_names(TRUE));
$form['account']['roles'] = array(
'#type' => 'checkboxes',
'#title' => $this->t('Roles'),
'#default_value' => (!$register ? $account->getRoles() : array()),
'#options' => $roles,
'#access' => $roles && $user->hasPermission('administer permissions'),
);
// Special handling for the inevitable "Authenticated user" role.
$form['account']['roles'][RoleInterface::AUTHENTICATED_ID] = array(
'#default_value' => TRUE,
'#disabled' => TRUE,
);
$form['account']['notify'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Notify user of new account'),
'#access' => $register && $admin,
);
$user_preferred_langcode = $register ? $language_interface->getId() : $account->getPreferredLangcode();
$user_preferred_admin_langcode = $register ? $language_interface->getId() : $account->getPreferredAdminLangcode(FALSE);
// Is the user preferred language added?
$user_language_added = FALSE;
if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) {
$negotiator = $this->languageManager->getNegotiator();
$user_language_added = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUser::METHOD_ID, LanguageInterface::TYPE_INTERFACE);
}
$form['language'] = array(
'#type' => $this->languageManager->isMultilingual() ? 'details' : 'container',
'#title' => $this->t('Language settings'),
'#open' => TRUE,
// Display language selector when either creating a user on the admin
// interface or editing a user account.
'#access' => !$register || $user->hasPermission('administer users'),
);
$form['language']['preferred_langcode'] = array(
'#type' => 'language_select',
'#title' => $this->t('Site language'),
'#languages' => LanguageInterface::STATE_CONFIGURABLE,
'#default_value' => $user_preferred_langcode,
'#description' => $user_language_added ? $this->t("This account's preferred language for emails and site presentation.") : $this->t("This account's preferred language for emails."),
// This is used to explain that user preferred language and entity
// language are synchronized. It can be removed if a different behavior is
// desired.
'#pre_render' => ['user_langcode' => [$this, 'alterPreferredLangcodeDescription']],
);
// Only show the account setting for Administration pages language to users
// if one of the detection and selection methods uses it.
$show_admin_language = FALSE;
if ($account->hasPermission('access administration pages') && $this->languageManager instanceof ConfigurableLanguageManagerInterface) {
$negotiator = $this->languageManager->getNegotiator();
$show_admin_language = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUserAdmin::METHOD_ID);
}
$form['language']['preferred_admin_langcode'] = array(
'#type' => 'language_select',
'#title' => $this->t('Administration pages language'),
'#languages' => LanguageInterface::STATE_CONFIGURABLE,
'#default_value' => $user_preferred_admin_langcode,
'#access' => $show_admin_language,
'#empty_option' => $this->t('- No preference -'),
'#empty_value' => '',
);
// User entities contain both a langcode property (for identifying the
// language of the entity data) and a preferred_langcode property (see
// above). Rather than provide a UI forcing the user to choose both
// separately, assume that the user profile data is in the user's preferred
// language. This entity builder provides that synchronization. For
// use-cases where this synchronization is not desired, a module can alter
// or remove this item.
$form['#entity_builders']['sync_user_langcode'] = [$this, 'syncUserLangcode'];
return parent::form($form, $form_state, $account);
}
/**
* Alters the preferred language widget description.
*
* @param array $element
* The preferred language form element.
*
* @return array
* The preferred language form element.
*/
public function alterPreferredLangcodeDescription(array $element) {
// Only add to the description if the form element has a description.
if (isset($element['#description'])) {
$element['#description'] .= ' ' . $this->t("This is also assumed to be the primary language of this account's profile information.");
}
return $element;
}
/**
* Synchronizes preferred language and entity language.
*
* @param string $entity_type_id
* The entity type identifier.
* @param \Drupal\user\UserInterface $user
* The entity updated with the submitted values.
* @param array $form
* The complete form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public function syncUserLangcode($entity_type_id, UserInterface $user, array &$form, FormStateInterface &$form_state) {
$user->getUntranslated()->langcode = $user->preferred_langcode;
}
/**
* {@inheritdoc}
*/
public function buildEntity(array $form, FormStateInterface $form_state) {
// Change the roles array to a list of enabled roles.
// @todo: Alter the form state as the form values are directly extracted and
// set on the field, which throws an exception as the list requires
// numeric keys. Allow to override this per field. As this function is
// called twice, we have to prevent it from getting the array keys twice.
if (is_string(key($form_state->getValue('roles')))) {
$form_state->setValue('roles', array_keys(array_filter($form_state->getValue('roles'))));
}
/** @var \Drupal\user\UserInterface $account */
$account = parent::buildEntity($form, $form_state);
// Translate the empty value '' of language selects to an unset field.
foreach (array('preferred_langcode', 'preferred_admin_langcode') as $field_name) {
if ($form_state->getValue($field_name) === '') {
$account->$field_name = NULL;
}
}
// Set existing password if set in the form state.
if ($current_pass = $form_state->getValue('current_pass')) {
$account->setExistingPassword($current_pass);
}
// Skip the protected user field constraint if the user came from the
// password recovery page.
$account->_skipProtectedUserFieldConstraint = $form_state->get('user_pass_reset');
return $account;
}
/**
* {@inheritdoc}
*/
protected function getEditedFieldNames(FormStateInterface $form_state) {
return array_merge(array(
'name',
'pass',
'mail',
'timezone',
'langcode',
'preferred_langcode',
'preferred_admin_langcode'
), parent::getEditedFieldNames($form_state));
}
/**
* {@inheritdoc}
*/
protected function flagViolations(EntityConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
// Manually flag violations of fields not handled by the form display. This
// is necessary as entity form displays only flag violations for fields
// contained in the display.
$field_names = array(
'name',
'pass',
'mail',
'timezone',
'langcode',
'preferred_langcode',
'preferred_admin_langcode'
);
foreach ($violations->getByFields($field_names) as $violation) {
list($field_name) = explode('.', $violation->getPropertyPath(), 2);
$form_state->setErrorByName($field_name, $violation->getMessage());
}
parent::flagViolations($violations, $form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$user = $this->getEntity($form_state);
// If there's a session set to the users id, remove the password reset tag
// since a new password was saved.
if (isset($_SESSION['pass_reset_'. $user->id()])) {
unset($_SESSION['pass_reset_'. $user->id()]);
}
}
}

View file

@ -0,0 +1,482 @@
<?php
/**
* @file
* Contains \Drupal\user\AccountSettingsForm.
*/
namespace Drupal\user;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configure user settings for this site.
*/
class AccountSettingsForm extends ConfigFormBase {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The role storage used when changing the admin role.
*
* @var \Drupal\user\RoleStorageInterface
*/
protected $roleStorage;
/**
* Constructs a \Drupal\user\AccountSettingsForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\user\RoleStorageInterface $role_storage
* The role storage.
*/
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, RoleStorageInterface $role_storage) {
parent::__construct($config_factory);
$this->moduleHandler = $module_handler;
$this->roleStorage = $role_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.factory'),
$container->get('module_handler'),
$container->get('entity.manager')->getStorage('user_role')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'user_admin_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return [
'system.site',
'user.mail',
'user.settings',
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$config = $this->config('user.settings');
$mail_config = $this->config('user.mail');
$site_config = $this->config('system.site');
$form['#attached']['library'][] = 'user/drupal.user.admin';
// Settings for anonymous users.
$form['anonymous_settings'] = array(
'#type' => 'details',
'#title' => $this->t('Anonymous users'),
'#open' => TRUE,
);
$form['anonymous_settings']['anonymous'] = array(
'#type' => 'textfield',
'#title' => $this->t('Name'),
'#default_value' => $config->get('anonymous'),
'#description' => $this->t('The name used to indicate anonymous users.'),
'#required' => TRUE,
);
// Administrative role option.
$form['admin_role'] = array(
'#type' => 'details',
'#title' => $this->t('Administrator role'),
'#open' => TRUE,
);
// Do not allow users to set the anonymous or authenticated user roles as the
// administrator role.
$roles = user_role_names(TRUE);
unset($roles[RoleInterface::AUTHENTICATED_ID]);
$admin_roles = $this->roleStorage->getQuery()
->condition('is_admin', TRUE)
->execute();
$default_value = reset($admin_roles);
$form['admin_role']['user_admin_role'] = array(
'#type' => 'select',
'#title' => $this->t('Administrator role'),
'#empty_value' => '',
'#default_value' => $default_value,
'#options' => $roles,
'#description' => $this->t('This role will be automatically assigned new permissions whenever a module is enabled. Changing this setting will not affect existing permissions.'),
// Don't allow to select a single admin role in case multiple roles got
// marked as admin role already.
'#access' => count($admin_roles) <= 1,
);
// @todo Remove this check once language settings are generalized.
if ($this->moduleHandler->moduleExists('content_translation')) {
$form['language'] = array(
'#type' => 'details',
'#title' => $this->t('Language settings'),
'#open' => TRUE,
'#tree' => TRUE,
);
$form_state->set(['content_translation', 'key'], 'language');
$form['language'] += content_translation_enable_widget('user', 'user', $form, $form_state);
}
// User registration settings.
$form['registration_cancellation'] = array(
'#type' => 'details',
'#title' => $this->t('Registration and cancellation'),
'#open' => TRUE,
);
$form['registration_cancellation']['user_register'] = array(
'#type' => 'radios',
'#title' => $this->t('Who can register accounts?'),
'#default_value' => $config->get('register'),
'#options' => array(
USER_REGISTER_ADMINISTRATORS_ONLY => $this->t('Administrators only'),
USER_REGISTER_VISITORS => $this->t('Visitors'),
USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL => $this->t('Visitors, but administrator approval is required'),
)
);
$form['registration_cancellation']['user_email_verification'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Require email verification when a visitor creates an account'),
'#default_value' => $config->get('verify_mail'),
'#description' => $this->t('New users will be required to validate their email address prior to logging into the site, and will be assigned a system-generated password. With this setting disabled, users will be logged in immediately upon registering, and may select their own passwords during registration.')
);
$form['registration_cancellation']['user_password_strength'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Enable password strength indicator'),
'#default_value' => $config->get('password_strength'),
);
$form['registration_cancellation']['user_cancel_method'] = array(
'#type' => 'radios',
'#title' => $this->t('When cancelling a user account'),
'#default_value' => $config->get('cancel_method'),
'#description' => $this->t('Users with the %select-cancel-method or %administer-users <a href="@permissions-url">permissions</a> can override this default method.', array('%select-cancel-method' => $this->t('Select method for cancelling account'), '%administer-users' => $this->t('Administer users'), '@permissions-url' => $this->url('user.admin_permissions'))),
);
$form['registration_cancellation']['user_cancel_method'] += user_cancel_methods();
foreach (Element::children($form['registration_cancellation']['user_cancel_method']) as $key) {
// All account cancellation methods that specify #access cannot be
// configured as default method.
// @see hook_user_cancel_methods_alter()
if (isset($form['registration_cancellation']['user_cancel_method'][$key]['#access'])) {
$form['registration_cancellation']['user_cancel_method'][$key]['#access'] = FALSE;
}
}
// Default notifications address.
$form['mail_notification_address'] = array(
'#type' => 'email',
'#title' => $this->t('Notification email address'),
'#default_value' => $site_config->get('mail_notification'),
'#description' => $this->t("The email address to be used as the 'from' address for all account notifications listed below. If <em>'Visitors, but administrator approval is required'</em> is selected above, a notification email will also be sent to this address for any new registrations. Leave empty to use the default system email address <em>(%site-email).</em>", array('%site-email' => $site_config->get('mail'))),
'#maxlength' => 180,
);
$form['email'] = array(
'#type' => 'vertical_tabs',
'#title' => $this->t('Emails'),
);
// These email tokens are shared for all settings, so just define
// the list once to help ensure they stay in sync.
$email_token_help = $this->t('Available variables are: [site:name], [site:url], [user:name], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url].');
$form['email_admin_created'] = array(
'#type' => 'details',
'#title' => $this->t('Welcome (new user created by administrator)'),
'#open' => $config->get('register') == USER_REGISTER_ADMINISTRATORS_ONLY,
'#description' => $this->t('Edit the welcome email messages sent to new member accounts created by an administrator.') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_admin_created']['user_mail_register_admin_created_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('register_admin_created.subject'),
'#maxlength' => 180,
);
$form['email_admin_created']['user_mail_register_admin_created_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('register_admin_created.body'),
'#rows' => 15,
);
$form['email_pending_approval'] = array(
'#type' => 'details',
'#title' => $this->t('Welcome (awaiting approval)'),
'#open' => $config->get('register') == USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL,
'#description' => $this->t('Edit the welcome email messages sent to new members upon registering, when administrative approval is required.') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_pending_approval']['user_mail_register_pending_approval_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('register_pending_approval.subject'),
'#maxlength' => 180,
);
$form['email_pending_approval']['user_mail_register_pending_approval_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('register_pending_approval.body'),
'#rows' => 8,
);
$form['email_pending_approval_admin'] = array(
'#type' => 'details',
'#title' => $this->t('Admin (user awaiting approval)'),
'#open' => $config->get('register') == USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL,
'#description' => $this->t('Edit the email notifying the site administrator that there are new members awaiting administrative approval.') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_pending_approval_admin']['register_pending_approval_admin_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('register_pending_approval_admin.subject'),
'#maxlength' => 180,
);
$form['email_pending_approval_admin']['register_pending_approval_admin_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('register_pending_approval_admin.body'),
'#rows' => 8,
);
$form['email_no_approval_required'] = array(
'#type' => 'details',
'#title' => $this->t('Welcome (no approval required)'),
'#open' => $config->get('register') == USER_REGISTER_VISITORS,
'#description' => $this->t('Edit the welcome email messages sent to new members upon registering, when no administrator approval is required.') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_no_approval_required']['user_mail_register_no_approval_required_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('register_no_approval_required.subject'),
'#maxlength' => 180,
);
$form['email_no_approval_required']['user_mail_register_no_approval_required_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('register_no_approval_required.body'),
'#rows' => 15,
);
$form['email_password_reset'] = array(
'#type' => 'details',
'#title' => $this->t('Password recovery'),
'#description' => $this->t('Edit the email messages sent to users who request a new password.') . ' ' . $email_token_help,
'#group' => 'email',
'#weight' => 10,
);
$form['email_password_reset']['user_mail_password_reset_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('password_reset.subject'),
'#maxlength' => 180,
);
$form['email_password_reset']['user_mail_password_reset_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('password_reset.body'),
'#rows' => 12,
);
$form['email_activated'] = array(
'#type' => 'details',
'#title' => $this->t('Account activation'),
'#description' => $this->t('Enable and edit email messages sent to users upon account activation (when an administrator activates an account of a user who has already registered, on a site where administrative approval is required).') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_activated']['user_mail_status_activated_notify'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Notify user when account is activated'),
'#default_value' => $config->get('notify.status_activated'),
);
$form['email_activated']['settings'] = array(
'#type' => 'container',
'#states' => array(
// Hide the additional settings when this email is disabled.
'invisible' => array(
'input[name="user_mail_status_activated_notify"]' => array('checked' => FALSE),
),
),
);
$form['email_activated']['settings']['user_mail_status_activated_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('status_activated.subject'),
'#maxlength' => 180,
);
$form['email_activated']['settings']['user_mail_status_activated_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('status_activated.body'),
'#rows' => 15,
);
$form['email_blocked'] = array(
'#type' => 'details',
'#title' => $this->t('Account blocked'),
'#description' => $this->t('Enable and edit email messages sent to users when their accounts are blocked.') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_blocked']['user_mail_status_blocked_notify'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Notify user when account is blocked'),
'#default_value' => $config->get('notify.status_blocked'),
);
$form['email_blocked']['settings'] = array(
'#type' => 'container',
'#states' => array(
// Hide the additional settings when the blocked email is disabled.
'invisible' => array(
'input[name="user_mail_status_blocked_notify"]' => array('checked' => FALSE),
),
),
);
$form['email_blocked']['settings']['user_mail_status_blocked_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('status_blocked.subject'),
'#maxlength' => 180,
);
$form['email_blocked']['settings']['user_mail_status_blocked_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('status_blocked.body'),
'#rows' => 3,
);
$form['email_cancel_confirm'] = array(
'#type' => 'details',
'#title' => $this->t('Account cancellation confirmation'),
'#description' => $this->t('Edit the email messages sent to users when they attempt to cancel their accounts.') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_cancel_confirm']['user_mail_cancel_confirm_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('cancel_confirm.subject'),
'#maxlength' => 180,
);
$form['email_cancel_confirm']['user_mail_cancel_confirm_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('cancel_confirm.body'),
'#rows' => 3,
);
$form['email_canceled'] = array(
'#type' => 'details',
'#title' => $this->t('Account canceled'),
'#description' => $this->t('Enable and edit email messages sent to users when their accounts are canceled.') . ' ' . $email_token_help,
'#group' => 'email',
);
$form['email_canceled']['user_mail_status_canceled_notify'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Notify user when account is canceled'),
'#default_value' => $config->get('notify.status_canceled'),
);
$form['email_canceled']['settings'] = array(
'#type' => 'container',
'#states' => array(
// Hide the settings when the cancel notify checkbox is disabled.
'invisible' => array(
'input[name="user_mail_status_canceled_notify"]' => array('checked' => FALSE),
),
),
);
$form['email_canceled']['settings']['user_mail_status_canceled_subject'] = array(
'#type' => 'textfield',
'#title' => $this->t('Subject'),
'#default_value' => $mail_config->get('status_canceled.subject'),
'#maxlength' => 180,
);
$form['email_canceled']['settings']['user_mail_status_canceled_body'] = array(
'#type' => 'textarea',
'#title' => $this->t('Body'),
'#default_value' => $mail_config->get('status_canceled.body'),
'#rows' => 3,
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$this->config('user.settings')
->set('anonymous', $form_state->getValue('anonymous'))
->set('register', $form_state->getValue('user_register'))
->set('password_strength', $form_state->getValue('user_password_strength'))
->set('verify_mail', $form_state->getValue('user_email_verification'))
->set('cancel_method', $form_state->getValue('user_cancel_method'))
->set('notify.status_activated', $form_state->getValue('user_mail_status_activated_notify'))
->set('notify.status_blocked', $form_state->getValue('user_mail_status_blocked_notify'))
->set('notify.status_canceled', $form_state->getValue('user_mail_status_canceled_notify'))
->save();
$this->config('user.mail')
->set('cancel_confirm.body', $form_state->getValue('user_mail_cancel_confirm_body'))
->set('cancel_confirm.subject', $form_state->getValue('user_mail_cancel_confirm_subject'))
->set('password_reset.body', $form_state->getValue('user_mail_password_reset_body'))
->set('password_reset.subject', $form_state->getValue('user_mail_password_reset_subject'))
->set('register_admin_created.body', $form_state->getValue('user_mail_register_admin_created_body'))
->set('register_admin_created.subject', $form_state->getValue('user_mail_register_admin_created_subject'))
->set('register_no_approval_required.body', $form_state->getValue('user_mail_register_no_approval_required_body'))
->set('register_no_approval_required.subject', $form_state->getValue('user_mail_register_no_approval_required_subject'))
->set('register_pending_approval.body', $form_state->getValue('user_mail_register_pending_approval_body'))
->set('register_pending_approval.subject', $form_state->getValue('user_mail_register_pending_approval_subject'))
->set('status_activated.body', $form_state->getValue('user_mail_status_activated_body'))
->set('status_activated.subject', $form_state->getValue('user_mail_status_activated_subject'))
->set('status_blocked.body', $form_state->getValue('user_mail_status_blocked_body'))
->set('status_blocked.subject', $form_state->getValue('user_mail_status_blocked_subject'))
->set('status_canceled.body', $form_state->getValue('user_mail_status_canceled_body'))
->set('status_canceled.subject', $form_state->getValue('user_mail_status_canceled_subject'))
->save();
$this->config('system.site')
->set('mail_notification', $form_state->getValue('mail_notification_address'))
->save();
// Change the admin role.
if ($form_state->hasValue('user_admin_role')) {
$admin_roles = $this->roleStorage->getQuery()
->condition('is_admin', TRUE)
->execute();
foreach ($admin_roles as $rid) {
$this->roleStorage->load($rid)->setIsAdmin(FALSE)->save();
}
$new_admin_role = $form_state->getValue('user_admin_role');
if ($new_admin_role) {
$this->roleStorage->load($new_admin_role)->setIsAdmin(TRUE)->save();
}
}
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* @file
* Contains \Drupal\user\Authentication\Provider\Cookie.
*/
namespace Drupal\user\Authentication\Provider;
use Drupal\Core\Authentication\AuthenticationProviderInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\UserSession;
use Drupal\Core\Session\SessionConfigurationInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
/**
* Cookie based authentication provider.
*/
class Cookie implements AuthenticationProviderInterface {
/**
* The session configuration.
*
* @var \Drupal\Core\Session\SessionConfigurationInterface
*/
protected $sessionConfiguration;
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Constructs a new cookie authentication provider.
*
* @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
* The session configuration.
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(SessionConfigurationInterface $session_configuration, Connection $connection) {
$this->sessionConfiguration = $session_configuration;
$this->connection = $connection;
}
/**
* {@inheritdoc}
*/
public function applies(Request $request) {
return $request->hasSession() && $this->sessionConfiguration->hasSession($request);
}
/**
* {@inheritdoc}
*/
public function authenticate(Request $request) {
return $this->getUserFromSession($request->getSession());
}
/**
* Returns the UserSession object for the given session.
*
* @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
* The session.
*
* @return \Drupal\Core\Session\AccountInterface|NULL
* The UserSession object for the current user, or NULL if this is an
* anonymous session.
*/
protected function getUserFromSession(SessionInterface $session) {
if ($uid = $session->get('uid')) {
// @todo Load the User entity in SessionHandler so we don't need queries.
// @see https://www.drupal.org/node/2345611
$values = $this->connection
->query('SELECT * FROM {users_field_data} u WHERE u.uid = :uid AND u.default_langcode = 1', [':uid' => $uid])
->fetchAssoc();
// Check if the user data was found and the user is active.
if (!empty($values) && $values['status'] == 1) {
// Add the user's roles.
$rids = $this->connection
->query('SELECT roles_target_id FROM {user__roles} WHERE entity_id = :uid', [':uid' => $values['uid']])
->fetchCol();
$values['roles'] = array_merge([AccountInterface::AUTHENTICATED_ROLE], $rids);
return new UserSession($values);
}
}
// This is an anonymous session.
return NULL;
}
}

View file

@ -0,0 +1,218 @@
<?php
/**
* @file
* Contains \Drupal\user\Controller\UserController.
*/
namespace Drupal\user\Controller;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\user\UserDataInterface;
use Drupal\user\UserInterface;
use Drupal\user\UserStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Controller routines for user routes.
*/
class UserController extends ControllerBase {
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatter
*/
protected $dateFormatter;
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* The user data service.
*
* @var \Drupal\user\UserDataInterface
*/
protected $userData;
/**
* Constructs a UserController object.
*
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* The date formatter service.
* @param \Drupal\user\UserStorageInterface $user_storage
* The user storage.
* @param \Drupal\user\UserDataInterface $user_data
* The user data service.
*/
public function __construct(DateFormatter $date_formatter, UserStorageInterface $user_storage, UserDataInterface $user_data) {
$this->dateFormatter = $date_formatter;
$this->userStorage = $user_storage;
$this->userData = $user_data;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('date.formatter'),
$container->get('entity.manager')->getStorage('user'),
$container->get('user.data')
);
}
/**
* Returns the user password reset page.
*
* @param int $uid
* UID of user requesting reset.
* @param int $timestamp
* The current timestamp.
* @param string $hash
* Login link hash.
*
* @return array|\Symfony\Component\HttpFoundation\RedirectResponse
* The form structure or a redirect response.
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* If the login link is for a blocked user or invalid user ID.
*/
public function resetPass($uid, $timestamp, $hash) {
$account = $this->currentUser();
$config = $this->config('user.settings');
// When processing the one-time login link, we have to make sure that a user
// isn't already logged in.
if ($account->isAuthenticated()) {
// The current user is already logged in.
if ($account->id() == $uid) {
user_logout();
}
// A different user is already logged in on the computer.
else {
if ($reset_link_user = $this->userStorage->load($uid)) {
drupal_set_message($this->t('Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="@logout">logout</a> and try using the link again.',
array('%other_user' => $account->getUsername(), '%resetting_user' => $reset_link_user->getUsername(), '@logout' => $this->url('user.logout'))), 'warning');
}
else {
// Invalid one-time link specifies an unknown user.
drupal_set_message($this->t('The one-time login link you clicked is invalid.'));
}
return $this->redirect('<front>');
}
}
// The current user is not logged in, so check the parameters.
// Time out, in seconds, until login URL expires.
$timeout = $config->get('password_reset_timeout');
$current = REQUEST_TIME;
/* @var \Drupal\user\UserInterface $user */
$user = $this->userStorage->load($uid);
// Verify that the user exists and is active.
if ($user && $user->isActive()) {
// No time out for first time login.
if ($user->getLastLoginTime() && $current - $timestamp > $timeout) {
drupal_set_message($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'error');
return $this->redirect('user.pass');
}
elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && ($hash === user_pass_rehash($user->getPassword(), $timestamp, $user->getLastLoginTime(), $user->id()))) {
$expiration_date = $user->getLastLoginTime() ? $this->dateFormatter->format($timestamp + $timeout) : NULL;
return $this->formBuilder()->getForm('Drupal\user\Form\UserPasswordResetForm', $user, $expiration_date, $timestamp, $hash);
}
else {
drupal_set_message($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'error');
return $this->redirect('user.pass');
}
}
// Blocked or invalid user ID, so deny access. The parameters will be in the
// watchdog's URL for the administrator to check.
throw new AccessDeniedHttpException();
}
/**
* Redirects users to their profile page.
*
* This controller assumes that it is only invoked for authenticated users.
* This is enforced for the 'user.page' route with the '_user_is_logged_in'
* requirement.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* Returns a redirect to the profile of the currently logged in user.
*/
public function userPage() {
return $this->redirect('entity.user.canonical', array('user' => $this->currentUser()->id()));
}
/**
* Route title callback.
*
* @param \Drupal\user\UserInterface $user
* The user account.
*
* @return string
* The user account name.
*/
public function userTitle(UserInterface $user = NULL) {
return $user ? Xss::filter($user->getUsername()) : '';
}
/**
* Logs the current user out.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirection to home page.
*/
public function logout() {
user_logout();
return $this->redirect('<front>');
}
/**
* Confirms cancelling a user account via an email link.
*
* @param \Drupal\user\UserInterface $user
* The user account.
* @param int $timestamp
* The timestamp.
* @param string $hashed_pass
* The hashed password.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirect response.
*/
public function confirmCancel(UserInterface $user, $timestamp = 0, $hashed_pass = '') {
// Time out in seconds until cancel URL expires; 24 hours = 86400 seconds.
$timeout = 86400;
$current = REQUEST_TIME;
// Basic validation of arguments.
$account_data = $this->userData->get('user', $user->id());
if (isset($account_data['cancel_method']) && !empty($timestamp) && !empty($hashed_pass)) {
// Validate expiration and hashed password/login.
if ($timestamp <= $current && $current - $timestamp < $timeout && $user->id() && $timestamp >= $user->getLastLoginTime() && $hashed_pass == user_pass_rehash($user->getPassword(), $timestamp, $user->getLastLoginTime(), $user->id())) {
$edit = array(
'user_cancel_notify' => isset($account_data['cancel_notify']) ? $account_data['cancel_notify'] : $this->config('user.settings')->get('notify.status_canceled'),
);
user_cancel($edit, $user->id(), $account_data['cancel_method']);
// Since user_cancel() is not invoked via Form API, batch processing
// needs to be invoked manually and should redirect to the front page
// after completion.
return batch_process('');
}
else {
drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'));
return $this->redirect('entity.user.cancel_form', ['user' => $user->id()], ['absolute' => TRUE]);
}
}
throw new AccessDeniedHttpException();
}
}

View file

@ -0,0 +1,189 @@
<?php
/**
* @file
* Contains \Drupal\user\Entity\Role.
*/
namespace Drupal\user\Entity;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\user\RoleInterface;
/**
* Defines the user role entity class.
*
* @ConfigEntityType(
* id = "user_role",
* label = @Translation("Role"),
* handlers = {
* "storage" = "Drupal\user\RoleStorage",
* "access" = "Drupal\user\RoleAccessControlHandler",
* "list_builder" = "Drupal\user\RoleListBuilder",
* "form" = {
* "default" = "Drupal\user\RoleForm",
* "delete" = "Drupal\Core\Entity\EntityDeleteForm"
* }
* },
* admin_permission = "administer permissions",
* config_prefix = "role",
* static_cache = TRUE,
* entity_keys = {
* "id" = "id",
* "weight" = "weight",
* "label" = "label"
* },
* links = {
* "delete-form" = "/admin/people/roles/manage/{user_role}/delete",
* "edit-form" = "/admin/people/roles/manage/{user_role}",
* "edit-permissions-form" = "/admin/people/permissions/{user_role}",
* "collection" = "/admin/people/roles",
* },
* config_export = {
* "id",
* "label",
* "weight",
* "is_admin",
* "permissions",
* }
* )
*/
class Role extends ConfigEntityBase implements RoleInterface {
/**
* The machine name of this role.
*
* @var string
*/
protected $id;
/**
* The human-readable label of this role.
*
* @var string
*/
protected $label;
/**
* The weight of this role in administrative listings.
*
* @var int
*/
protected $weight;
/**
* The permissions belonging to this role.
*
* @var array
*/
protected $permissions = array();
/**
* An indicator whether the role has all permissions.
*
* @var bool
*/
protected $is_admin;
/**
* {@inheritdoc}
*/
public function getPermissions() {
if ($this->isAdmin()) {
return [];
}
return $this->permissions;
}
/**
* {@inheritdoc}
*/
public function getWeight() {
return $this->get('weight');
}
/**
* {@inheritdoc}
*/
public function setWeight($weight) {
$this->set('weight', $weight);
return $this;
}
/**
* {@inheritdoc}
*/
public function hasPermission($permission) {
if ($this->isAdmin()) {
return TRUE;
}
return in_array($permission, $this->permissions);
}
/**
* {@inheritdoc}
*/
public function grantPermission($permission) {
if ($this->isAdmin()) {
return $this;
}
if (!$this->hasPermission($permission)) {
$this->permissions[] = $permission;
}
return $this;
}
/**
* {@inheritdoc}
*/
public function revokePermission($permission) {
if ($this->isAdmin()) {
return $this;
}
$this->permissions = array_diff($this->permissions, array($permission));
return $this;
}
/**
* {@inheritdoc}
*/
public function isAdmin() {
return (bool) $this->is_admin;
}
/**
* {@inheritdoc}
*/
public function setIsAdmin($is_admin) {
$this->is_admin = $is_admin;
return $this;
}
/**
* {@inheritdoc}
*/
public static function postLoad(EntityStorageInterface $storage, array &$entities) {
parent::postLoad($storage, $entities);
// Sort the queried roles by their weight.
// See \Drupal\Core\Config\Entity\ConfigEntityBase::sort().
uasort($entities, 'static::sort');
}
/**
* {@inheritdoc}
*/
public function preSave(EntityStorageInterface $storage) {
parent::preSave($storage);
if (!isset($this->weight) && ($roles = $storage->loadMultiple())) {
// Set a role weight to make this new role last.
$max = array_reduce($roles, function($max, $role) {
return $max > $role->weight ? $max : $role->weight;
});
$this->weight = $max + 1;
}
}
}

View file

@ -0,0 +1,565 @@
<?php
/**
* @file
* Contains \Drupal\user\Entity\User.
*/
namespace Drupal\user\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Language\LanguageInterface;
use Drupal\user\RoleInterface;
use Drupal\user\UserInterface;
/**
* Defines the user entity class.
*
* The base table name here is plural, despite Drupal table naming standards,
* because "user" is a reserved word in many databases.
*
* @ContentEntityType(
* id = "user",
* label = @Translation("User"),
* handlers = {
* "storage" = "Drupal\user\UserStorage",
* "storage_schema" = "Drupal\user\UserStorageSchema",
* "access" = "Drupal\user\UserAccessControlHandler",
* "list_builder" = "Drupal\user\UserListBuilder",
* "views_data" = "Drupal\user\UserViewsData",
* "route_provider" = {
* "html" = "Drupal\user\Entity\UserRouteProvider",
* },
* "form" = {
* "default" = "Drupal\user\ProfileForm",
* "cancel" = "Drupal\user\Form\UserCancelForm",
* "register" = "Drupal\user\RegisterForm"
* },
* "translation" = "Drupal\user\ProfileTranslationHandler"
* },
* admin_permission = "administer users",
* base_table = "users",
* data_table = "users_field_data",
* label_callback = "user_format_name",
* translatable = TRUE,
* entity_keys = {
* "id" = "uid",
* "langcode" = "langcode",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "/user/{user}",
* "edit-form" = "/user/{user}/edit",
* "cancel-form" = "/user/{user}/cancel",
* "collection" = "/admin/people",
* },
* field_ui_base_route = "entity.user.admin_form",
* common_reference_target = TRUE
* )
*/
class User extends ContentEntityBase implements UserInterface {
use EntityChangedTrait;
/**
* Stores a reference for a reusable anonymous user entity.
*
* @var \Drupal\user\UserInterface
*/
protected static $anonymousUser;
/**
* {@inheritdoc}
*/
public function isNew() {
return !empty($this->enforceIsNew) || $this->id() === NULL;
}
/**
* {@inheritdoc}
*/
public function preSave(EntityStorageInterface $storage) {
parent::preSave($storage);
// Make sure that the authenticated/anonymous roles are not persisted.
foreach ($this->get('roles') as $index => $item) {
if (in_array($item->target_id, array(RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID))) {
$this->get('roles')->offsetUnset($index);
}
}
// Store account cancellation information.
foreach (array('user_cancel_method', 'user_cancel_notify') as $key) {
if (isset($this->{$key})) {
\Drupal::service('user.data')->set('user', $this->id(), substr($key, 5), $this->{$key});
}
}
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
if ($update) {
$session_manager = \Drupal::service('session_manager');
// If the password has been changed, delete all open sessions for the
// user and recreate the current one.
if ($this->pass->value != $this->original->pass->value) {
$session_manager->delete($this->id());
if ($this->id() == \Drupal::currentUser()->id()) {
\Drupal::service('session')->migrate();
}
}
// If the user was blocked, delete the user's sessions to force a logout.
if ($this->original->status->value != $this->status->value && $this->status->value == 0) {
$session_manager->delete($this->id());
}
// Send emails after we have the new user object.
if ($this->status->value != $this->original->status->value) {
// The user's status is changing; conditionally send notification email.
$op = $this->status->value == 1 ? 'status_activated' : 'status_blocked';
_user_mail_notify($op, $this);
}
}
}
/**
* {@inheritdoc}
*/
public static function postDelete(EntityStorageInterface $storage, array $entities) {
parent::postDelete($storage, $entities);
$uids = array_keys($entities);
\Drupal::service('user.data')->delete(NULL, $uids);
}
/**
* {@inheritdoc}
*/
public function getRoles($exclude_locked_roles = FALSE) {
$roles = array();
// Users with an ID always have the authenticated user role.
if (!$exclude_locked_roles) {
if ($this->isAuthenticated()) {
$roles[] = RoleInterface::AUTHENTICATED_ID;
}
else {
$roles[] = RoleInterface::ANONYMOUS_ID;
}
}
foreach ($this->get('roles') as $role) {
if ($role->target_id) {
$roles[] = $role->target_id;
}
}
return $roles;
}
/**
* {@inheritdoc}
*/
public function hasRole($rid) {
return in_array($rid, $this->getRoles());
}
/**
* {@inheritdoc}
*/
public function addRole($rid) {
if (in_array($rid, [RoleInterface::AUTHENTICATED_ID, RoleInterface::ANONYMOUS_ID])) {
throw new \InvalidArgumentException('Anonymous or authenticated role ID must not be assigned manually.');
}
$roles = $this->getRoles(TRUE);
$roles[] = $rid;
$this->set('roles', array_unique($roles));
}
/**
* {@inheritdoc}
*/
public function removeRole($rid) {
$this->set('roles', array_diff($this->getRoles(TRUE), array($rid)));
}
/**
* {@inheritdoc}
*/
public function hasPermission($permission) {
// User #1 has all privileges.
if ((int) $this->id() === 1) {
return TRUE;
}
return $this->getRoleStorage()->isPermissionInRoles($permission, $this->getRoles());
}
/**
* {@inheritdoc}
*/
public function getPassword() {
return $this->get('pass')->value;
}
/**
* {@inheritdoc}
*/
public function setPassword($password) {
$this->get('pass')->value = $password;
return $this;
}
/**
* {@inheritdoc}
*/
public function getEmail() {
return $this->get('mail')->value;
}
/**
* {@inheritdoc}
*/
public function setEmail($mail) {
$this->get('mail')->value = $mail;
return $this;
}
/**
* {@inheritdoc}
*/
public function getCreatedTime() {
return $this->get('created')->value;
}
/**
* {@inheritdoc}
*/
public function getLastAccessedTime() {
return $this->get('access')->value;
}
/**
* {@inheritdoc}
*/
public function setLastAccessTime($timestamp) {
$this->get('access')->value = $timestamp;
return $this;
}
/**
* {@inheritdoc}
*/
public function getLastLoginTime() {
return $this->get('login')->value;
}
/**
* {@inheritdoc}
*/
public function setLastLoginTime($timestamp) {
$this->get('login')->value = $timestamp;
return $this;
}
/**
* {@inheritdoc}
*/
public function isActive() {
return $this->get('status')->value == 1;
}
/**
* {@inheritdoc}
*/
public function isBlocked() {
return $this->get('status')->value == 0;
}
/**
* {@inheritdoc}
*/
public function activate() {
$this->get('status')->value = 1;
return $this;
}
/**
* {@inheritdoc}
*/
public function block() {
$this->get('status')->value = 0;
return $this;
}
/**
* {@inheritdoc}
*/
public function getTimeZone() {
return $this->get('timezone')->value;
}
/**
* {@inheritdoc}
*/
function getPreferredLangcode($fallback_to_default = TRUE) {
$language_list = $this->languageManager()->getLanguages();
$preferred_langcode = $this->get('preferred_langcode')->value;
if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) {
return $language_list[$preferred_langcode]->getId();
}
else {
return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
}
}
/**
* {@inheritdoc}
*/
function getPreferredAdminLangcode($fallback_to_default = TRUE) {
$language_list = $this->languageManager()->getLanguages();
$preferred_langcode = $this->get('preferred_admin_langcode')->value;
if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) {
return $language_list[$preferred_langcode]->getId();
}
else {
return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
}
}
/**
* {@inheritdoc}
*/
public function getInitialEmail() {
return $this->get('init')->value;
}
/**
* {@inheritdoc}
*/
public function isAuthenticated() {
return $this->id() > 0;
}
/**
* {@inheritdoc}
*/
public function isAnonymous() {
return $this->id() == 0;
}
/**
* {@inheritdoc}
*/
public function getUsername() {
$name = $this->get('name')->value ?: \Drupal::config('user.settings')->get('anonymous');
\Drupal::moduleHandler()->alter('user_format_name', $name, $this);
return $name;
}
/**
* {@inheritdoc}
*/
public function setUsername($username) {
$this->set('name', $username);
return $this;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
public function setExistingPassword($password) {
$this->get('pass')->existing = $password;
}
/**
* {@inheritdoc}
*/
public function checkExistingPassword(UserInterface $account_unchanged) {
return !empty($this->get('pass')->existing) && \Drupal::service('password')->check(trim($this->get('pass')->existing), $account_unchanged->getPassword());
}
/**
* Returns an anonymous user entity.
*
* @return \Drupal\user\UserInterface
* An anonymous user entity.
*/
public static function getAnonymousUser() {
if (!isset(static::$anonymousUser)) {
// @todo Use the entity factory once available, see
// https://www.drupal.org/node/1867228.
$entity_manager = \Drupal::entityManager();
$entity_type = $entity_manager->getDefinition('user');
$class = $entity_type->getClass();
static::$anonymousUser = new $class(['uid' => [LanguageInterface::LANGCODE_DEFAULT => 0]], $entity_type->id());
}
return clone static::$anonymousUser;
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['uid'] = BaseFieldDefinition::create('integer')
->setLabel(t('User ID'))
->setDescription(t('The user ID.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The user UUID.'))
->setReadOnly(TRUE);
$fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language code'))
->setDescription(t('The user language code.'))
->setTranslatable(TRUE);
$fields['preferred_langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Preferred language code'))
->setDescription(t("The user's preferred language code for receiving emails and viewing the site."))
// @todo: Define this via an options provider once
// https://www.drupal.org/node/2329937 is completed.
->addPropertyConstraints('value', array(
'AllowedValues' => array('callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'),
));
$fields['preferred_admin_langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Preferred admin language code'))
->setDescription(t("The user's preferred language code for viewing administration pages."))
// @todo: A default value of NULL is ignored, so we have to specify
// an empty field item structure instead. Fix this in
// https://www.drupal.org/node/2318605.
->setDefaultValue(array(0 => array ('value' => NULL)))
// @todo: Define this via an options provider once
// https://www.drupal.org/node/2329937 is completed.
->addPropertyConstraints('value', array(
'AllowedValues' => array('callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'),
));
// The name should not vary per language. The username is the visual
// identifier for a user and needs to be consistent in all languages.
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setDescription(t('The name of this user.'))
->setDefaultValue('')
->setConstraints(array(
// No Length constraint here because the UserName constraint also covers
// that.
'UserName' => array(),
'UserNameUnique' => array(),
));
$fields['pass'] = BaseFieldDefinition::create('password')
->setLabel(t('Password'))
->setDescription(t('The password of this user (hashed).'))
->addConstraint('ProtectedUserField');
$fields['mail'] = BaseFieldDefinition::create('email')
->setLabel(t('Email'))
->setDescription(t('The email of this user.'))
->setDefaultValue('')
->addConstraint('UserMailUnique')
->addConstraint('UserMailRequired')
->addConstraint('ProtectedUserField');
$fields['timezone'] = BaseFieldDefinition::create('string')
->setLabel(t('Timezone'))
->setDescription(t('The timezone of this user.'))
->setSetting('max_length', 32)
// @todo: Define this via an options provider once
// https://www.drupal.org/node/2329937 is completed.
->addPropertyConstraints('value', array(
'AllowedValues' => array('callback' => __CLASS__ . '::getAllowedTimezones'),
));
$fields['status'] = BaseFieldDefinition::create('boolean')
->setLabel(t('User status'))
->setDescription(t('Whether the user is active or blocked.'))
->setDefaultValue(FALSE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Created'))
->setDescription(t('The time that the user was created.'));
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the user was last edited.'))
->setTranslatable(TRUE);
$fields['access'] = BaseFieldDefinition::create('timestamp')
->setLabel(t('Last access'))
->setDescription(t('The time that the user last accessed the site.'))
->setDefaultValue(0);
$fields['login'] = BaseFieldDefinition::create('timestamp')
->setLabel(t('Last login'))
->setDescription(t('The time that the user last logged in.'))
->setDefaultValue(0);
$fields['init'] = BaseFieldDefinition::create('email')
->setLabel(t('Initial email'))
->setDescription(t('The email address used for initial account creation.'))
->setDefaultValue('');
$fields['roles'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Roles'))
->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
->setDescription(t('The roles the user has.'))
->setSetting('target_type', 'user_role');
return $fields;
}
/**
* Returns the role storage object.
*
* @return \Drupal\user\RoleStorageInterface
* The role storage object.
*/
protected function getRoleStorage() {
return \Drupal::entityManager()->getStorage('user_role');
}
/**
* Defines allowed timezones for the field's AllowedValues constraint.
*
* @return string[]
* The allowed values.
*/
public static function getAllowedTimezones() {
return array_keys(system_time_zones());
}
/**
* Defines allowed configurable language codes for AllowedValues constraints.
*
* @return string[]
* The allowed values.
*/
public static function getAllowedConfigurableLanguageCodes() {
return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE));
}
}

View file

@ -0,0 +1,54 @@
<?php
/**
* @file
* Contains \Drupal\user\Entity\UserRouteProvider.
*/
namespace Drupal\user\Entity;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Provides routes for the user entity.
*/
class UserRouteProvider implements EntityRouteProviderInterface {
/**
* {@inheritdoc}
*/
public function getRoutes(EntityTypeInterface $entity_type) {
$route_collection = new RouteCollection();
$route = (new Route('/user/{user}'))
->setDefaults([
'_entity_view' => 'user.full',
'_title_callback' => 'Drupal\user\Controller\UserController::userTitle',
])
->setRequirement('_entity_access', 'user.view');
$route_collection->add('entity.user.canonical', $route);
$route = (new Route('/user/{user}/edit'))
->setDefaults([
'_entity_form' => 'user.default',
'_title_callback' => 'Drupal\user\Controller\UserController::userTitle',
])
->setOption('_admin_route', TRUE)
->setRequirement('_entity_access', 'user.update');
$route_collection->add('entity.user.edit_form', $route);
$route = (new Route('/user/{user}/cancel'))
->setDefaults([
'_title' => 'Cancel account',
'_entity_form' => 'user.cancel',
])
->setOption('_admin_route', TRUE)
->setRequirement('_entity_access', 'user.delete');
$route_collection->add('entity.user.cancel_form', $route);
return $route_collection;
}
}

View file

@ -0,0 +1,56 @@
<?php
/**
* @file
* Contains \Drupal\user\EntityOwnerInterface.
*/
namespace Drupal\user;
/**
* Defines a common interface for entities that have an owner.
*
* An owner is someone who has primary control over an entity, similar to
* owners in Unix file system access. This may or may not be the entity's
* original author. The owner may also have less permissions than other users,
* such as administrators.
*/
interface EntityOwnerInterface {
/**
* Returns the entity owner's user entity.
*
* @return \Drupal\user\UserInterface
* The owner user entity.
*/
public function getOwner();
/**
* Sets the entity owner's user entity.
*
* @param \Drupal\user\UserInterface $account
* The owner user entity.
*
* @return $this
*/
public function setOwner(UserInterface $account);
/**
* Returns the entity owner's user ID.
*
* @return int
* The owner user ID.
*/
public function getOwnerId();
/**
* Sets the entity owner's user ID.
*
* @param int $uid
* The owner user id.
*
* @return $this
*/
public function setOwnerId($uid);
}

View file

@ -0,0 +1,91 @@
<?php
/**
* @file
* Contains \Drupal\user\EventSubscriber\AccessDeniedSubscriber.
*/
namespace Drupal\user\EventSubscriber;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\UrlGeneratorTrait;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Redirects users when access is denied.
*
* Anonymous users are taken to the login page when attempting to access the
* user profile pages. Authenticated users are redirected from the login form to
* their profile page and from the user registration form to their profile edit
* form.
*/
class AccessDeniedSubscriber implements EventSubscriberInterface {
use UrlGeneratorTrait;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* Constructs a new redirect subscriber.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
* The URL generator.
*/
public function __construct(AccountInterface $account, URLGeneratorInterface $url_generator) {
$this->account = $account;
$this->setUrlGenerator($url_generator);
}
/**
* Redirects users when access is denied.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The event to process.
*/
public function onException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
if ($exception instanceof AccessDeniedHttpException) {
$route_name = RouteMatch::createFromRequest($event->getRequest())->getRouteName();
if ($this->account->isAuthenticated()) {
switch ($route_name) {
case 'user.login';
// Redirect an authenticated user to the profile page.
$event->setResponse($this->redirect('entity.user.canonical', ['user' => $this->account->id()]));
break;
case 'user.register';
// Redirect an authenticated user to the profile form.
$event->setResponse($this->redirect('entity.user.edit_form', ['user' => $this->account->id()]));
break;
}
}
elseif ($route_name === 'user.page') {
$event->setResponse($this->redirect('user.login'));
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
// Use a higher priority than
// \Drupal\Core\EventSubscriber\ExceptionLoggingSubscriber, because there's
// no need to log the exception if we can redirect.
$events[KernelEvents::EXCEPTION][] = ['onException', 75];
return $events;
}
}

View file

@ -0,0 +1,79 @@
<?php
/**
* @file
* Contains \Drupal\user\EventSubscriber\MaintenanceModeSubscriber.
*/
namespace Drupal\user\EventSubscriber;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\UrlGeneratorTrait;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\MaintenanceModeInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Maintenance mode subscriber to logout users.
*/
class MaintenanceModeSubscriber implements EventSubscriberInterface {
use UrlGeneratorTrait;
/**
* The maintenance mode.
*
* @var \Drupal\Core\Site\MaintenanceMode
*/
protected $maintenanceMode;
/**
* The current account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* Constructs a new MaintenanceModeSubscriber.
*
* @param \Drupal\Core\Site\MaintenanceModeInterface $maintenance_mode
* The maintenance mode.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
*/
public function __construct(MaintenanceModeInterface $maintenance_mode, AccountInterface $account) {
$this->maintenanceMode = $maintenance_mode;
$this->account = $account;
}
/**
* Logout users if site is in maintenance mode.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The event to process.
*/
public function onKernelRequestMaintenance(GetResponseEvent $event) {
$request = $event->getRequest();
$route_match = RouteMatch::createFromRequest($request);
if ($this->maintenanceMode->applies($route_match)) {
// If the site is offline, log out unprivileged users.
if ($this->account->isAuthenticated() && !$this->maintenanceMode->exempt($this->account)) {
user_logout();
// Redirect to homepage.
$event->setResponse($this->redirect($this->url('<front>')));
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = ['onKernelRequestMaintenance', 31];
return $events;
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* @file
* Contains \Drupal\user\EventSubscriber\UserRequestSubscriber.
*/
namespace Drupal\user\EventSubscriber;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\Settings;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Updates the current user's last access time.
*/
class UserRequestSubscriber implements EventSubscriberInterface {
/**
* The current account.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $account;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs a new UserRequestSubscriber.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The current user.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(AccountInterface $account, EntityManagerInterface $entity_manager) {
$this->account = $account;
$this->entityManager = $entity_manager;
}
/**
* Updates the current user's last access time.
*
* @param \Symfony\Component\HttpKernel\Event\PostResponseEvent $event
* The event to process.
*/
public function onKernelTerminate(PostResponseEvent $event) {
if ($this->account->isAuthenticated() && REQUEST_TIME - $this->account->getLastAccessedTime() > Settings::get('session_write_interval', 180)) {
// Do that no more than once per 180 seconds.
/** @var \Drupal\user\UserStorageInterface $storage */
$storage = $this->entityManager->getStorage('user');
$storage->updateLastAccessTimestamp($this->account, REQUEST_TIME);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
// Should go before other subscribers start to write their caches. Notably
// before \Drupal\Core\EventSubscriber\KernelDestructionSubscriber to
// prevent instantiation of destructed services.
$events[KernelEvents::TERMINATE][] = ['onKernelTerminate', 300];
return $events;
}
}

View file

@ -0,0 +1,155 @@
<?php
/**
* @file
* Contains \Drupal\user\Form\UserCancelForm.
*/
namespace Drupal\user\Form;
use Drupal\Core\Entity\ContentEntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a confirmation form for cancelling user account.
*/
class UserCancelForm extends ContentEntityConfirmFormBase {
/**
* Available account cancellation methods.
*
* @var array
*/
protected $cancelMethods;
/**
* The user being cancelled.
*
* @var \Drupal\user\UserInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
public function getQuestion() {
if ($this->entity->id() == $this->currentUser()->id()) {
return $this->t('Are you sure you want to cancel your account?');
}
return $this->t('Are you sure you want to cancel the account %name?', array('%name' => $this->entity->label()));
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return $this->entity->urlInfo();
}
/**
* {@inheritdoc}
*/
public function getDescription() {
$description = '';
$default_method = $this->config('user.settings')->get('cancel_method');
if ($this->currentUser()->hasPermission('administer users') || $this->currentUser()->hasPermission('select account cancellation method')) {
$description = $this->t('Select the method to cancel the account above.');
}
// Options supplied via user_cancel_methods() can have a custom
// #confirm_description property for the confirmation form description.
elseif (isset($this->cancelMethods[$default_method]['#confirm_description'])) {
$description = $this->cancelMethods[$default_method]['#confirm_description'];
}
return $description . ' ' . $this->t('This action cannot be undone.');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Cancel account');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$user = $this->currentUser();
$this->cancelMethods = user_cancel_methods();
// Display account cancellation method selection, if allowed.
$admin_access = $user->hasPermission('administer users');
$form['user_cancel_method'] = array(
'#type' => 'radios',
'#title' => ($this->entity->id() == $user->id() ? $this->t('When cancelling your account') : $this->t('When cancelling the account')),
'#access' => $admin_access || $user->hasPermission('select account cancellation method'),
);
$form['user_cancel_method'] += $this->cancelMethods;
// Allow user administrators to skip the account cancellation confirmation
// mail (by default), as long as they do not attempt to cancel their own
// account.
$override_access = $admin_access && ($this->entity->id() != $user->id());
$form['user_cancel_confirm'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Require email confirmation to cancel account'),
'#default_value' => !$override_access,
'#access' => $override_access,
'#description' => $this->t('When enabled, the user must confirm the account cancellation via email.'),
);
// Also allow to send account canceled notification mail, if enabled.
$default_notify = $this->config('user.settings')->get('notify.status_canceled');
$form['user_cancel_notify'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Notify user when account is canceled'),
'#default_value' => ($override_access ? FALSE : $default_notify),
'#access' => $override_access && $default_notify,
'#description' => $this->t('When enabled, the user will receive an email notification after the account has been canceled.'),
);
// Always provide entity id in the same form key as in the entity edit form.
$form['uid'] = array('#type' => 'value', '#value' => $this->entity->id());
// Store the user permissions so that it can be altered in hook_form_alter()
// if desired.
$form['access'] = array(
'#type' => 'value',
'#value' => $user->hasPermission('administer users'),
);
$form = parent::buildForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Cancel account immediately, if the current user has administrative
// privileges, no confirmation mail shall be sent, and the user does not
// attempt to cancel the own account.
if (!$form_state->isValueEmpty('access') && $form_state->isValueEmpty('user_cancel_confirm') && $this->entity->id() != $this->currentUser()->id()) {
user_cancel($form_state->getValues(), $this->entity->id(), $form_state->getValue('user_cancel_method'));
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));
}
else {
// Store cancelling method and whether to notify the user in
// $this->entity for
// \Drupal\user\Controller\UserController::confirmCancel().
$this->entity->user_cancel_method = $form_state->getValue('user_cancel_method');
$this->entity->user_cancel_notify = $form_state->getValue('user_cancel_notify');
$this->entity->save();
_user_mail_notify('cancel_confirm', $this->entity);
drupal_set_message($this->t('A confirmation request to cancel your account has been sent to your email address.'));
$this->logger('user')->notice('Sent account cancellation request to %name %email.', array('%name' => $this->entity->label(), '%email' => '<' . $this->entity->getEmail() . '>'));
$form_state->setRedirect(
'entity.user.canonical',
array('user' => $this->entity->id())
);
}
}
}

View file

@ -0,0 +1,254 @@
<?php
/**
* @file
* Contains \Drupal\user\Form\UserLoginForm.
*/
namespace Drupal\user\Form;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\user\UserAuthInterface;
use Drupal\user\UserStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a user login form.
*/
class UserLoginForm extends FormBase {
/**
* The flood service.
*
* @var \Drupal\Core\Flood\FloodInterface
*/
protected $flood;
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* The user authentication object.
*
* @var \Drupal\user\UserAuthInterface
*/
protected $userAuth;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* Constructs a new UserLoginForm.
*
* @param \Drupal\Core\Flood\FloodInterface $flood
* The flood service.
* @param \Drupal\user\UserStorageInterface $user_storage
* The user storage.
* @param \Drupal\user\UserAuthInterface $user_auth
* The user authentication object.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
*/
public function __construct(FloodInterface $flood, UserStorageInterface $user_storage, UserAuthInterface $user_auth, RendererInterface $renderer) {
$this->flood = $flood;
$this->userStorage = $user_storage;
$this->userAuth = $user_auth;
$this->renderer = $renderer;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('flood'),
$container->get('entity.manager')->getStorage('user'),
$container->get('user.auth'),
$container->get('renderer')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'user_login_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('system.site');
// Display login form:
$form['name'] = array(
'#type' => 'textfield',
'#title' => $this->t('Username'),
'#size' => 60,
'#maxlength' => USERNAME_MAX_LENGTH,
'#description' => $this->t('Enter your @s username.', array('@s' => $config->get('name'))),
'#required' => TRUE,
'#attributes' => array(
'autocorrect' => 'none',
'autocapitalize' => 'none',
'spellcheck' => 'false',
'autofocus' => 'autofocus',
),
);
$form['pass'] = array(
'#type' => 'password',
'#title' => $this->t('Password'),
'#size' => 60,
'#description' => $this->t('Enter the password that accompanies your username.'),
'#required' => TRUE,
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Log in'));
$form['#validate'][] = '::validateName';
$form['#validate'][] = '::validateAuthentication';
$form['#validate'][] = '::validateFinal';
$this->renderer->addCacheableDependency($form, $config);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$account = $this->userStorage->load($form_state->get('uid'));
// A destination was set, probably on an exception controller,
if (!$this->getRequest()->request->has('destination')) {
$form_state->setRedirect(
'entity.user.canonical',
array('user' => $account->id())
);
}
else {
$this->getRequest()->query->set('destination', $this->getRequest()->request->get('destination'));
}
user_login_finalize($account);
}
/**
* Sets an error if supplied username has been blocked.
*/
public function validateName(array &$form, FormStateInterface $form_state) {
if (!$form_state->isValueEmpty('name') && user_is_blocked($form_state->getValue('name'))) {
// Blocked in user administration.
$form_state->setErrorByName('name', $this->t('The username %name has not been activated or is blocked.', array('%name' => $form_state->getValue('name'))));
}
}
/**
* Checks supplied username/password against local users table.
*
* If successful, $form_state->get('uid') is set to the matching user ID.
*/
public function validateAuthentication(array &$form, FormStateInterface $form_state) {
$password = trim($form_state->getValue('pass'));
$flood_config = $this->config('user.flood');
if (!$form_state->isValueEmpty('name') && !empty($password)) {
// Do not allow any login from the current user's IP if the limit has been
// reached. Default is 50 failed attempts allowed in one hour. This is
// independent of the per-user limit to catch attempts from one IP to log
// in to many different user accounts. We have a reasonably high limit
// since there may be only one apparent IP for all users at an institution.
if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
$form_state->set('flood_control_triggered', 'ip');
return;
}
$accounts = $this->userStorage->loadByProperties(array('name' => $form_state->getValue('name'), 'status' => 1));
$account = reset($accounts);
if ($account) {
if ($flood_config->get('uid_only')) {
// Register flood events based on the uid only, so they apply for any
// IP address. This is the most secure option.
$identifier = $account->id();
}
else {
// The default identifier is a combination of uid and IP address. This
// is less secure but more resistant to denial-of-service attacks that
// could lock out all users with public user names.
$identifier = $account->id() . '-' . $this->getRequest()->getClientIP();
}
$form_state->set('flood_control_user_identifier', $identifier);
// Don't allow login if the limit for this user has been reached.
// Default is to allow 5 failed attempts every 6 hours.
if (!$this->flood->isAllowed('user.failed_login_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
$form_state->set('flood_control_triggered', 'user');
return;
}
}
// We are not limited by flood control, so try to authenticate.
// Store $uid in form state as a flag for self::validateFinal().
$uid = $this->userAuth->authenticate($form_state->getValue('name'), $password);
$form_state->set('uid', $uid);
}
}
/**
* Checks if user was not authenticated, or if too many logins were attempted.
*
* This validation function should always be the last one.
*/
public function validateFinal(array &$form, FormStateInterface $form_state) {
$flood_config = $this->config('user.flood');
if (!$form_state->get('uid')) {
// Always register an IP-based failed login event.
$this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
// Register a per-user failed login event.
if ($flood_control_user_identifier = $form_state->get('flood_control_user_identifier')) {
$this->flood->register('user.failed_login_user', $flood_config->get('user_window'), $flood_control_user_identifier);
}
if ($flood_control_triggered = $form_state->get('flood_control_triggered')) {
if ($flood_control_triggered == 'user') {
$form_state->setErrorByName('name', $this->formatPlural($flood_config->get('user_limit'), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => $this->url('user.pass'))));
}
else {
// We did not find a uid, so the limit is IP-based.
$form_state->setErrorByName('name', $this->t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => $this->url('user.pass'))));
}
}
else {
$form_state->setErrorByName('name', $this->t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => $this->url('user.pass', [], array('query' => array('name' => $form_state->getValue('name')))))));
$accounts = $this->userStorage->loadByProperties(array('name' => $form_state->getValue('name')));
if (!empty($accounts)) {
$this->logger('user')->notice('Login attempt failed for %user.', array('%user' => $form_state->getValue('name')));
}
else {
// If the username entered is not a valid user,
// only store the IP address.
$this->logger('user')->notice('Login attempt failed from %ip.', array('%ip' => $this->getRequest()->getClientIp()));
}
}
}
elseif ($flood_control_user_identifier = $form_state->get('flood_control_user_identifier')) {
// Clear past failures for this user so as not to block a user who might
// log in and out more than once in an hour.
$this->flood->clear('user.failed_login_user', $flood_control_user_identifier);
}
}
}

View file

@ -0,0 +1,205 @@
<?php
/**
* @file
* Contains \Drupal\user\Form\UserMultipleCancelConfirm.
*/
namespace Drupal\user\Form;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\user\PrivateTempStoreFactory;
use Drupal\user\UserStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a confirmation form for cancelling multiple user accounts.
*/
class UserMultipleCancelConfirm extends ConfirmFormBase {
/**
* The temp store factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs a new UserMultipleCancelConfirm.
*
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* The temp store factory.
* @param \Drupal\user\UserStorageInterface $user_storage
* The user storage.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, UserStorageInterface $user_storage, EntityManagerInterface $entity_manager) {
$this->tempStoreFactory = $temp_store_factory;
$this->userStorage = $user_storage;
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('user.private_tempstore'),
$container->get('entity.manager')->getStorage('user'),
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'user_multiple_cancel_confirm';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to cancel these user accounts?');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.user.collection');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Cancel accounts');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Retrieve the accounts to be canceled from the temp store.
$accounts = $this->tempStoreFactory
->get('user_user_operations_cancel')
->get($this->currentUser()->id());
if (!$accounts) {
return $this->redirect('entity.user.collection');
}
$root = NULL;
$form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
foreach ($accounts as $account) {
$uid = $account->id();
// Prevent user 1 from being canceled.
if ($uid <= 1) {
$root = intval($uid) === 1 ? $account : $root;
continue;
}
$form['accounts'][$uid] = array(
'#type' => 'hidden',
'#value' => $uid,
'#prefix' => '<li>',
'#suffix' => $account->label() . "</li>\n",
);
}
// Output a notice that user 1 cannot be canceled.
if (isset($root)) {
$redirect = (count($accounts) == 1);
$message = $this->t('The user account %name cannot be canceled.', array('%name' => $root->label()));
drupal_set_message($message, $redirect ? 'error' : 'warning');
// If only user 1 was selected, redirect to the overview.
if ($redirect) {
return $this->redirect('entity.user.collection');
}
}
$form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
$form['user_cancel_method'] = array(
'#type' => 'radios',
'#title' => $this->t('When cancelling these accounts'),
);
$form['user_cancel_method'] += user_cancel_methods();
// Allow to send the account cancellation confirmation mail.
$form['user_cancel_confirm'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Require email confirmation to cancel account'),
'#default_value' => FALSE,
'#description' => $this->t('When enabled, the user must confirm the account cancellation via email.'),
);
// Also allow to send account canceled notification mail, if enabled.
$form['user_cancel_notify'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Notify user when account is canceled'),
'#default_value' => FALSE,
'#access' => $this->config('user.settings')->get('notify.status_canceled'),
'#description' => $this->t('When enabled, the user will receive an email notification after the account has been canceled.'),
);
$form = parent::buildForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$current_user_id = $this->currentUser()->id();
// Clear out the accounts from the temp store.
$this->tempStoreFactory->get('user_user_operations_cancel')->delete($current_user_id);
if ($form_state->getValue('confirm')) {
foreach ($form_state->getValue('accounts') as $uid => $value) {
// Prevent programmatic form submissions from cancelling user 1.
if ($uid <= 1) {
continue;
}
// Prevent user administrators from deleting themselves without confirmation.
if ($uid == $current_user_id) {
$admin_form_mock = array();
$admin_form_state = $form_state;
$admin_form_state->unsetValue('user_cancel_confirm');
// The $user global is not a complete user entity, so load the full
// entity.
$account = $this->userStorage->load($uid);
$admin_form = $this->entityManager->getFormObject('user', 'cancel');
$admin_form->setEntity($account);
// Calling this directly required to init form object with $account.
$admin_form->buildForm($admin_form_mock, $admin_form_state);
$admin_form->submitForm($admin_form_mock, $admin_form_state);
}
else {
user_cancel($form_state->getValues(), $uid, $form_state->getValue('user_cancel_method'));
}
}
}
$form_state->setRedirect('entity.user.collection');
}
}

View file

@ -0,0 +1,156 @@
<?php
/**
* @file
* Contains \Drupal\user\Form\UserPasswordForm.
*/
namespace Drupal\user\Form;
use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\Element\Email;
use Drupal\user\UserStorageInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a user password reset form.
*/
class UserPasswordForm extends FormBase {
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* Constructs a UserPasswordForm object.
*
* @param \Drupal\user\UserStorageInterface $user_storage
* The user storage.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
public function __construct(UserStorageInterface $user_storage, LanguageManagerInterface $language_manager) {
$this->userStorage = $user_storage;
$this->languageManager = $language_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('user'),
$container->get('language_manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'user_pass';
}
/**
* {@inheritdoc}
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['name'] = array(
'#type' => 'textfield',
'#title' => $this->t('Username or email address'),
'#size' => 60,
'#maxlength' => max(USERNAME_MAX_LENGTH, Email::EMAIL_MAX_LENGTH),
'#required' => TRUE,
'#attributes' => array(
'autocorrect' => 'off',
'autocapitalize' => 'off',
'spellcheck' => 'false',
'autofocus' => 'autofocus',
),
);
// Allow logged in users to request this also.
$user = $this->currentUser();
if ($user->isAuthenticated()) {
$form['name']['#type'] = 'value';
$form['name']['#value'] = $user->getEmail();
$form['mail'] = array(
'#prefix' => '<p>',
'#markup' => $this->t('Password reset instructions will be mailed to %email. You must log out to use the password reset link in the email.', array('%email' => $user->getEmail())),
'#suffix' => '</p>',
);
}
else {
$form['mail'] = array(
'#prefix' => '<p>',
'#markup' => $this->t('Password reset instructions will be sent to your registered e-mail address.'),
'#suffix' => '</p>',
);
$form['name']['#default_value'] = $this->getRequest()->query->get('name');
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Submit'));
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$name = trim($form_state->getValue('name'));
// Try to load by email.
$users = $this->userStorage->loadByProperties(array('mail' => $name));
if (empty($users)) {
// No success, try to load by name.
$users = $this->userStorage->loadByProperties(array('name' => $name));
}
$account = reset($users);
if ($account && $account->id()) {
// Blocked accounts cannot request a new password.
if (!$account->isActive()) {
$form_state->setErrorByName('name', $this->t('%name is blocked or has not been activated yet.', array('%name' => $name)));
}
else {
$form_state->setValueForElement(array('#parents' => array('account')), $account);
}
}
else {
$form_state->setErrorByName('name', $this->t('Sorry, %name is not recognized as a username or an email address.', array('%name' => $name)));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$langcode = $this->languageManager->getCurrentLanguage()->getId();
$account = $form_state->getValue('account');
// Mail one time login URL and instructions using current language.
$mail = _user_mail_notify('password_reset', $account, $langcode);
if (!empty($mail)) {
$this->logger('user')->notice('Password reset instructions mailed to %name at %email.', array('%name' => $account->getUsername(), '%email' => $account->getEmail()));
drupal_set_message($this->t('Further instructions have been sent to your email address.'));
}
$form_state->setRedirect('user.page');
}
}

View file

@ -0,0 +1,123 @@
<?php
/**
* @file
* Contains \Drupal\user\Form\UserPasswordResetForm.
*/
namespace Drupal\user\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Form\FormBase;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for the user password forms.
*/
class UserPasswordResetForm extends FormBase {
/**
* A logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* Constructs a new UserPasswordResetForm.
*
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
*/
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('logger.factory')->get('user')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'user_pass_reset';
}
/**
* {@inheritdoc}
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\Core\Session\AccountInterface $user
* User requesting reset.
* @param string $expiration_date
* Formatted expiration date for the login link, or NULL if the link does
* not expire.
* @param int $timestamp
* The current timestamp.
* @param string $hash
* Login link hash.
*/
public function buildForm(array $form, FormStateInterface $form_state, AccountInterface $user = NULL, $expiration_date = NULL, $timestamp = NULL, $hash = NULL) {
if ($expiration_date) {
$form['message'] = array('#markup' => $this->t('<p>This is a one-time login for %user_name and will expire on %expiration_date.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $user->getUsername(), '%expiration_date' => $expiration_date)));
$form['#title'] = $this->t('Reset password');
}
else {
// No expiration for first time login.
$form['message'] = array('#markup' => $this->t('<p>This is a one-time login for %user_name.</p><p>Click on this button to log in to the site and change your password.</p>', array('%user_name' => $user->getUsername())));
$form['#title'] = $this->t('Set password');
}
$form['user'] = array(
'#type' => 'value',
'#value' => $user,
);
$form['timestamp'] = array(
'#type' => 'value',
'#value' => $timestamp,
);
$form['help'] = array('#markup' => '<p>' . $this->t('This login can be used only once.') . '</p>');
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Log in'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
/** @var $user \Drupal\user\UserInterface */
$user = $form_state->getValue('user');
user_login_finalize($user);
$this->logger->notice('User %name used one-time login link at time %timestamp.', array('%name' => $user->getUsername(), '%timestamp' => $form_state->getValue('timestamp')));
drupal_set_message($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
// Let the user's password be changed without the current password check.
$token = Crypt::randomBytesBase64(55);
$_SESSION['pass_reset_' . $user->id()] = $token;
$form_state->setRedirect(
'entity.user.edit_form',
array('user' => $user->id()),
array(
'query' => array('pass-reset-token' => $token),
'absolute' => TRUE,
)
);
}
}

View file

@ -0,0 +1,212 @@
<?php
/**
* @file
* Contains \Drupal\user\Form\UserPermissionsForm.
*/
namespace Drupal\user\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\PermissionHandlerInterface;
use Drupal\user\RoleStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides the user permissions administration form.
*/
class UserPermissionsForm extends FormBase {
/**
* The permission handler.
*
* @var \Drupal\user\PermissionHandlerInterface
*/
protected $permissionHandler;
/**
* The role storage.
*
* @var \Drupal\user\RoleStorageInterface
*/
protected $roleStorage;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a new UserPermissionsForm.
*
* @param \Drupal\user\PermissionHandlerInterface $permission_handler
* The permission handler.
* @param \Drupal\user\RoleStorageInterface $role_storage
* The role storage.
* @param \Drupal\Core\Extension\ModuleHandlerInterface
* The module handler.
*/
public function __construct(PermissionHandlerInterface $permission_handler, RoleStorageInterface $role_storage, ModuleHandlerInterface $module_handler) {
$this->permissionHandler = $permission_handler;
$this->roleStorage = $role_storage;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('user.permissions'),
$container->get('entity.manager')->getStorage('user_role'),
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'user_admin_permissions';
}
/**
* Gets the roles to display in this form.
*
* @return \Drupal\user\RoleInterface[]
* An array of role objects.
*/
protected function getRoles() {
return $this->roleStorage->loadMultiple();
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$role_names = array();
$role_permissions = array();
$admin_roles = array();
foreach ($this->getRoles() as $role_name => $role) {
// Retrieve role names for columns.
$role_names[$role_name] = SafeMarkup::checkPlain($role->label());
// Fetch permissions for the roles.
$role_permissions[$role_name] = $role->getPermissions();
$admin_roles[$role_name] = $role->isAdmin();
}
// Store $role_names for use when saving the data.
$form['role_names'] = array(
'#type' => 'value',
'#value' => $role_names,
);
// Render role/permission overview:
$options = array();
$hide_descriptions = system_admin_compact_mode();
$form['system_compact_link'] = array(
'#id' => FALSE,
'#type' => 'system_compact_link',
);
$form['permissions'] = array(
'#type' => 'table',
'#header' => array($this->t('Permission')),
'#id' => 'permissions',
'#attributes' => ['class' => ['permissions', 'js-permissions']],
'#sticky' => TRUE,
);
foreach ($role_names as $name) {
$form['permissions']['#header'][] = array(
'data' => $name,
'class' => array('checkbox'),
);
}
$permissions = $this->permissionHandler->getPermissions();
$permissions_by_provider = array();
foreach ($permissions as $permission_name => $permission) {
$permissions_by_provider[$permission['provider']][$permission_name] = $permission;
}
foreach ($permissions_by_provider as $provider => $permissions) {
// Module name.
$form['permissions'][$provider] = array(array(
'#wrapper_attributes' => array(
'colspan' => count($role_names) + 1,
'class' => array('module'),
'id' => 'module-' . $provider,
),
'#markup' => $this->moduleHandler->getName($provider),
));
foreach ($permissions as $perm => $perm_item) {
// Fill in default values for the permission.
$perm_item += array(
'description' => '',
'restrict access' => FALSE,
'warning' => !empty($perm_item['restrict access']) ? $this->t('Warning: Give to trusted roles only; this permission has security implications.') : '',
);
$options[$perm] = $perm_item['title'];
$form['permissions'][$perm]['description'] = array(
'#type' => 'inline_template',
'#template' => '<div class="permission"><span class="title">{{ title }}</span>{% if description or warning %}<div class="description">{% if warning %}<em class="permission-warning">{{ warning }}</em> {% endif %}{{ description }}</div>{% endif %}</div>',
'#context' => array(
'title' => $perm_item['title'],
),
);
// Show the permission description.
if (!$hide_descriptions) {
$form['permissions'][$perm]['description']['#context']['description'] = $perm_item['description'];
$form['permissions'][$perm]['description']['#context']['warning'] = $perm_item['warning'];
}
$options[$perm] = '';
foreach ($role_names as $rid => $name) {
$form['permissions'][$perm][$rid] = array(
'#title' => $name . ': ' . $perm_item['title'],
'#title_display' => 'invisible',
'#wrapper_attributes' => array(
'class' => array('checkbox'),
),
'#type' => 'checkbox',
'#default_value' => in_array($perm, $role_permissions[$rid]) ? 1 : 0,
'#attributes' => array('class' => array('rid-' . $rid, 'js-rid-' . $rid)),
'#parents' => array($rid, $perm),
);
// Show a column of disabled but checked checkboxes.
if ($admin_roles[$rid]) {
$form['permissions'][$perm][$rid]['#disabled'] = TRUE;
$form['permissions'][$perm][$rid]['#default_value'] = TRUE;
}
}
}
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save permissions'),
'#button_type' => 'primary',
);
$form['#attached']['library'][] = 'user/drupal.user.permissions';
return $form;
}
/**
* {@inheritdoc}
*/
function submitForm(array &$form, FormStateInterface $form_state) {
foreach ($form_state->getValue('role_names') as $role_name => $name) {
user_role_change_permissions($role_name, (array) $form_state->getValue($role_name));
}
drupal_set_message($this->t('The changes have been saved.'));
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\user\Form\UserPermissionsRoleSpecificForm.
*/
namespace Drupal\user\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\RoleInterface;
/**
* Provides the user permissions administration form for a specific role.
*/
class UserPermissionsRoleSpecificForm extends UserPermissionsForm {
/**
* The specific role for this form.
*
* @var \Drupal\user\RoleInterface
*/
protected $userRole;
/**
* {@inheritdoc}
*/
protected function getRoles() {
return array($this->userRole->id() => $this->userRole);
}
/**
* {@inheritdoc}
*
* @param string $role_id
* The user role ID used for this form.
*/
public function buildForm(array $form, FormStateInterface $form_state, RoleInterface $user_role = NULL) {
$this->userRole = $user_role;
return parent::buildForm($form, $form_state);
}
}

View file

@ -0,0 +1,246 @@
<?php
/**
* @file
* Contains \Drupal\user\PermissionHandler.
*/
namespace Drupal\user;
use Drupal\Component\Discovery\YamlDiscovery;
use Drupal\Core\Controller\ControllerResolverInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
/**
* Provides the available permissions based on yml files.
*
* To define permissions you can use a $module.permissions.yml file. This file
* defines machine names, human-readable names, restrict access (if required for
* security warning), and optionally descriptions for each permission type. The
* machine names are the canonical way to refer to permissions for access
* checking.
*
* If your module needs to define dynamic permissions you can use the
* permission_callbacks key to declare a callable that will return an array of
* permissions, keyed by machine name. Each item in the array can contain the
* same keys as an entry in $module.permissions.yml.
*
* Here is an example from the core filter module (comments have been added):
* @code
* # The key is the permission machine name, and is required.
* administer filters:
* # (required) Human readable name of the permission used in the UI.
* title: 'Administer text formats and filters'
* # (optional) Additional description fo the permission used in the UI.
* description: 'Define how text is handled by combining filters into text formats.'
* # (optional) Boolean, when set to true a warning about site security will
* # be displayed on the Permissions page. Defaults to false.
* restrict access: false
*
* # An array of callables used to generate dynamic permissions.
* permission_callbacks:
* # Each item in the array should return an associative array with one or
* # more permissions following the same keys as the permission defined above.
* - Drupal\filter\FilterPermissions::permissions
* @endcode
*
* @see filter.permissions.yml
* @see \Drupal\filter\FilterPermissions
* @see user_api
*/
class PermissionHandler implements PermissionHandlerInterface {
use StringTranslationTrait;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The YAML discovery class to find all .permissions.yml files.
*
* @var \Drupal\Component\Discovery\YamlDiscovery
*/
protected $yamlDiscovery;
/**
* The controller resolver.
*
* @var \Drupal\Core\Controller\ControllerResolverInterface
*/
protected $controllerResolver;
/**
* Constructs a new PermissionHandler.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation.
* @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
* The controller resolver.
*/
public function __construct(ModuleHandlerInterface $module_handler, TranslationInterface $string_translation, ControllerResolverInterface $controller_resolver) {
// @todo It would be nice if you could pull all module directories from the
// container.
$this->moduleHandler = $module_handler;
$this->stringTranslation = $string_translation;
$this->controllerResolver = $controller_resolver;
}
/**
* Gets the YAML discovery.
*
* @return \Drupal\Component\Discovery\YamlDiscovery
* The YAML discovery.
*/
protected function getYamlDiscovery() {
if (!isset($this->yamlDiscovery)) {
$this->yamlDiscovery = new YamlDiscovery('permissions', $this->moduleHandler->getModuleDirectories());
}
return $this->yamlDiscovery;
}
/**
* {@inheritdoc}
*/
public function getPermissions() {
$all_permissions = $this->buildPermissionsYaml();
return $this->sortPermissions($all_permissions);
}
/**
* {@inheritdoc}
*/
public function moduleProvidesPermissions($module_name) {
// @TODO Static cache this information, see
// https://www.drupal.org/node/2339487
$permissions = $this->getPermissions();
foreach ($permissions as $permission) {
if ($permission['provider'] == $module_name) {
return TRUE;
}
}
return FALSE;
}
/**
* Builds all permissions provided by .permissions.yml files.
*
* @return array[]
* Each return permission is an array with the following keys:
* - title: The title of the permission.
* - description: The description of the permission, defaults to NULL.
* - provider: The provider of the permission.
*/
protected function buildPermissionsYaml() {
$all_permissions = array();
$all_callback_permissions = array();
foreach ($this->getYamlDiscovery()->findAll() as $provider => $permissions) {
// The top-level 'permissions_callback' is a list of methods in controller
// syntax, see \Drupal\Core\Controller\ControllerResolver. These methods
// should return an array of permissions in the same structure.
if (isset($permissions['permission_callbacks'])) {
foreach ($permissions['permission_callbacks'] as $permission_callback) {
$callback = $this->controllerResolver->getControllerFromDefinition($permission_callback);
if ($callback_permissions = call_user_func($callback)) {
// Add any callback permissions to the array of permissions. Any
// defaults can then get processed below.
foreach ($callback_permissions as $name => $callback_permission) {
if (!is_array($callback_permission)) {
$callback_permission = array(
'title' => $callback_permission,
);
}
$callback_permission += array(
'description' => NULL,
);
$callback_permission['provider'] = $provider;
$all_callback_permissions[$name] = $callback_permission;
}
}
}
unset($permissions['permission_callbacks']);
}
foreach ($permissions as &$permission) {
if (!is_array($permission)) {
$permission = array(
'title' => $permission,
);
}
$permission['title'] = $this->t($permission['title']);
$permission['description'] = isset($permission['description']) ? $this->t($permission['description']) : NULL;
$permission['provider'] = $provider;
}
$all_permissions += $permissions;
}
return $all_permissions + $all_callback_permissions;
}
/**
* Sorts the given permissions by provider name and title.
*
* @param array $all_permissions
* The permissions to be sorted.
*
* @return array[]
* Each return permission is an array with the following keys:
* - title: The title of the permission.
* - description: The description of the permission, defaults to NULL.
* - provider: The provider of the permission.
*/
protected function sortPermissions(array $all_permissions = array()) {
// Get a list of all the modules providing permissions and sort by
// display name.
$modules = $this->getModuleNames();
uasort($all_permissions, function (array $permission_a, array $permission_b) use ($modules) {
if ($modules[$permission_a['provider']] == $modules[$permission_b['provider']]) {
return $permission_a['title'] > $permission_b['title'];
}
else {
return $modules[$permission_a['provider']] > $modules[$permission_b['provider']];
}
});
return $all_permissions;
}
/**
* Returns all module names.
*
* @return string[]
* Returns the human readable names of all modules keyed by machine name.
*/
protected function getModuleNames() {
$modules = array();
foreach (array_keys($this->moduleHandler->getModuleList()) as $module) {
$modules[$module] = $this->moduleHandler->getName($module);
}
asort($modules);
return $modules;
}
/**
* Wraps system_rebuild_module_data()
*
* @return \Drupal\Core\Extension\Extension[]
*/
protected function systemRebuildModuleData() {
return system_rebuild_module_data();
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* @file
* Contains \Drupal\user\PermissionHandlerInterface.
*/
namespace Drupal\user;
/**
* Defines an interface to list available permissions.
*/
interface PermissionHandlerInterface {
/**
* Gets all available permissions.
*
* @return array
* An array whose keys are permission names and whose corresponding values
* are arrays containing the following key-value pairs:
* - title: The human-readable name of the permission, to be shown on the
* permission administration page. This should be wrapped in the t()
* function so it can be translated.
* - description: (optional) A description of what the permission does. This
* should be wrapped in the t() function so it can be translated.
* - restrict access: (optional) A boolean which can be set to TRUE to
* indicate that site administrators should restrict access to this
* permission to trusted users. This should be used for permissions that
* have inherent security risks across a variety of potential use cases
* (for example, the "administer filters" and "bypass node access"
* permissions provided by Drupal core). When set to TRUE, a standard
* warning message defined in user_admin_permissions() will be displayed
* with the permission on the permission administration page. Defaults
* to FALSE.
* - warning: (optional) A translated warning message to display for this
* permission on the permission administration page. This warning
* overrides the automatic warning generated by 'restrict access' being
* set to TRUE. This should rarely be used, since it is important for all
* permissions to have a clear, consistent security warning that is the
* same across the site. Use the 'description' key instead to provide any
* information that is specific to the permission you are defining.
* - provider: (optional) The provider name of the permission.
*/
public function getPermissions();
/**
* Determines whether a module provides some permissions.
*
* @param string $module_name
* The module name.
*
* @return bool
* Returns TRUE if the module provides some permissions, otherwise FALSE.
*/
public function moduleProvidesPermissions($module_name);
}

View file

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Action\AddRoleUser.
*/
namespace Drupal\user\Plugin\Action;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Plugin\Action\ChangeUserRoleBase;
/**
* Adds a role to a user.
*
* @Action(
* id = "user_add_role_action",
* label = @Translation("Add a role to the selected users"),
* type = "user"
* )
*/
class AddRoleUser extends ChangeUserRoleBase {
/**
* {@inheritdoc}
*/
public function execute($account = NULL) {
$rid = $this->configuration['rid'];
// Skip adding the role to the user if they already have it.
if ($account !== FALSE && !$account->hasRole($rid)) {
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
$account->addRole($rid);
$account->save();
}
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Action\BlockUser.
*/
namespace Drupal\user\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
/**
* Blocks a user.
*
* @Action(
* id = "user_block_user_action",
* label = @Translation("Block the selected users"),
* type = "user"
* )
*/
class BlockUser extends ActionBase {
/**
* {@inheritdoc}
*/
public function execute($account = NULL) {
// Skip blocking user if they are already blocked.
if ($account !== FALSE && $account->isActive()) {
// For efficiency manually save the original account before applying any
// changes.
$account->original = clone $account;
$account->block();
$account->save();
}
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\user\UserInterface $object */
$access = $object->status->access('edit', $account, TRUE)
->andIf($object->access('update', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Action\CancelUser.
*/
namespace Drupal\user\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Cancels a user account.
*
* @Action(
* id = "user_cancel_user_action",
* label = @Translation("Cancel the selected user accounts"),
* type = "user",
* confirm_form_route_name = "user.multiple_cancel_confirm"
* )
*/
class CancelUser extends ActionBase implements ContainerFactoryPluginInterface {
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a DeleteNode object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param AccountInterface $current_user
* Current user.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
$this->currentUser = $current_user;
$this->tempStoreFactory = $temp_store_factory;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('user.private_tempstore'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
$this->tempStoreFactory->get('user_user_operations_cancel')->set($this->currentUser->id(), $entities);
}
/**
* {@inheritdoc}
*/
public function execute($object = NULL) {
$this->executeMultiple(array($object));
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\user\UserInterface $object */
return $object->access('delete', $account, $return_as_object);
}
}

View file

@ -0,0 +1,107 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Action\ChangeUserRoleBase.
*/
namespace Drupal\user\Plugin\Action;
use Drupal\Core\Action\ConfigurableActionBase;
use Drupal\Core\Entity\DependencyTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\RoleInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a base class for operations to change a user's role.
*/
abstract class ChangeUserRoleBase extends ConfigurableActionBase implements ContainerFactoryPluginInterface {
use DependencyTrait;
/**
* The user role entity type.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeInterface $entity_type) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityType = $entity_type;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')->getDefinition('user_role')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array(
'rid' => '',
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$roles = user_role_names(TRUE);
unset($roles[RoleInterface::AUTHENTICATED_ID]);
$form['rid'] = array(
'#type' => 'radios',
'#title' => t('Role'),
'#options' => $roles,
'#default_value' => $this->configuration['rid'],
'#required' => TRUE,
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['rid'] = $form_state->getValue('rid');
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
if (!empty($this->configuration['rid'])) {
$prefix = $this->entityType->getConfigPrefix() . '.';
$this->addDependency('config', $prefix . $this->configuration['rid']);
}
return $this->dependencies;
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\user\UserInterface $object */
$access = $object->access('update', $account, TRUE)
->andIf($object->roles->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Action\RemoveRoleUser.
*/
namespace Drupal\user\Plugin\Action;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Plugin\Action\ChangeUserRoleBase;
/**
* Removes a role from a user.
*
* @Action(
* id = "user_remove_role_action",
* label = @Translation("Remove a role from the selected users"),
* type = "user"
* )
*/
class RemoveRoleUser extends ChangeUserRoleBase {
/**
* {@inheritdoc}
*/
public function execute($account = NULL) {
$rid = $this->configuration['rid'];
// Skip removing the role from the user if they already don't have it.
if ($account !== FALSE && $account->hasRole($rid)) {
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
$account->removeRole($rid);
$account->save();
}
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Action\UnblockUser.
*/
namespace Drupal\user\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
/**
* Unblocks a user.
*
* @Action(
* id = "user_unblock_user_action",
* label = @Translation("Unblock the selected users"),
* type = "user"
* )
*/
class UnblockUser extends ActionBase {
/**
* {@inheritdoc}
*/
public function execute($account = NULL) {
// Skip unblocking user if they are already unblocked.
if ($account !== FALSE && $account->isBlocked()) {
$account->activate();
$account->save();
}
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\user\UserInterface $object */
$access = $object->status->access('edit', $account, TRUE)
->andIf($object->access('update', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
}
}

View file

@ -0,0 +1,132 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Block\UserLoginBlock.
*/
namespace Drupal\user\Plugin\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\RedirectDestinationTrait;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\UrlGeneratorTrait;
use Drupal\Core\Url;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Block\BlockBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a 'User login' block.
*
* @Block(
* id = "user_login_block",
* admin_label = @Translation("User login"),
* category = @Translation("Forms")
* )
*/
class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterface {
use UrlGeneratorTrait;
use RedirectDestinationTrait;
/**
* The route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Constructs a new UserLoginBlock instance.
*
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
$route_name = $this->routeMatch->getRouteName();
if ($account->isAnonymous() && !in_array($route_name, array('user.register', 'user.login', 'user.logout'))) {
return AccessResult::allowed()
->addCacheContexts(['route', 'user.roles:anonymous']);
}
return AccessResult::forbidden();
}
/**
* {@inheritdoc}
*/
public function build() {
$form = \Drupal::formBuilder()->getForm('Drupal\user\Form\UserLoginForm');
unset($form['name']['#attributes']['autofocus']);
unset($form['name']['#description']);
unset($form['pass']['#description']);
$form['name']['#size'] = 15;
$form['pass']['#size'] = 15;
$form['#action'] = $this->url('<current>', [], ['query' => $this->getDestinationArray(), 'external' => FALSE]);
// Build action links.
$items = array();
if (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) {
$items['create_account'] = \Drupal::l($this->t('Create new account'), new Url('user.register', array(), array(
'attributes' => array(
'title' => $this->t('Create a new user account.'),
'class' => array('create-account-link'),
),
)));
}
$items['request_password'] = \Drupal::l($this->t('Reset your password'), new Url('user.pass', array(), array(
'attributes' => array(
'title' => $this->t('Send password reset instructions via e-mail.'),
'class' => array('request-password-link'),
),
)));
return array(
'user_login_form' => $form,
'user_links' => array(
'#theme' => 'item_list',
'#items' => $items,
),
);
}
/**
* {@inheritdoc}
*
* @todo Make cacheable once https://www.drupal.org/node/2351015 lands.
*/
public function getCacheMaxAge() {
return 0;
}
}

View file

@ -0,0 +1,90 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Condition\UserRole.
*/
namespace Drupal\user\Plugin\Condition;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\Context\ContextDefinition;
/**
* Provides a 'User Role' condition.
*
* @Condition(
* id = "user_role",
* label = @Translation("User Role"),
* context = {
* "user" = @ContextDefinition("entity:user", label = @Translation("User"))
* }
* )
*
*/
class UserRole extends ConditionPluginBase {
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['roles'] = array(
'#type' => 'checkboxes',
'#title' => $this->t('When the user has the following roles'),
'#default_value' => $this->configuration['roles'],
'#options' => array_map('\Drupal\Component\Utility\SafeMarkup::checkPlain', user_role_names()),
'#description' => $this->t('If you select no roles, the condition will evaluate to TRUE for all users.'),
);
return parent::buildConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array(
'roles' => array(),
) + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['roles'] = array_filter($form_state->getValue('roles'));
parent::submitConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function summary() {
// Use the role labels. They will be sanitized below.
$roles = array_intersect_key(user_role_names(), $this->configuration['roles']);
if (count($roles) > 1) {
$roles = implode(', ', $roles);
}
else {
$roles = reset($roles);
}
if (!empty($this->configuration['negate'])) {
return $this->t('The user is not a member of @roles', array('@roles' => $roles));
}
else {
return $this->t('The user is a member of @roles', array('@roles' => $roles));
}
}
/**
* {@inheritdoc}
*/
public function evaluate() {
if (empty($this->configuration['roles']) && !$this->isNegated()) {
return TRUE;
}
$user = $this->getContextValue('user');
return (bool) array_intersect($this->configuration['roles'], $user->getRoles());
}
}

View file

@ -0,0 +1,214 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\EntityReferenceSelection\UserSelection.
*/
namespace Drupal\user\Plugin\EntityReferenceSelection;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\RoleInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides specific access control for the user entity type.
*
* @EntityReferenceSelection(
* id = "default:user",
* label = @Translation("User selection"),
* entity_types = {"user"},
* group = "default",
* weight = 1
* )
*/
class UserSelection extends SelectionBase {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* The user storage.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* Constructs a new UserSelection object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
* @param \Drupal\Core\Database\Connection $connection
* The database connection.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user, Connection $connection) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager, $module_handler, $current_user);
$this->connection = $connection;
$this->userStorage = $entity_manager->getStorage('user');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager'),
$container->get('module_handler'),
$container->get('current_user'),
$container->get('database')
);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$selection_handler_settings = $this->configuration['handler_settings'];
// Merge in default values.
$selection_handler_settings += array(
'filter' => array(
'type' => '_none',
),
'include_anonymous' => TRUE,
);
$form['include_anonymous'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Include the anonymous user.'),
'#default_value' => $selection_handler_settings['include_anonymous'],
);
// Add user specific filter options.
$form['filter']['type'] = array(
'#type' => 'select',
'#title' => $this->t('Filter by'),
'#options' => array(
'_none' => $this->t('- None -'),
'role' => $this->t('User role'),
),
'#ajax' => TRUE,
'#limit_validation_errors' => array(),
'#default_value' => $selection_handler_settings['filter']['type'],
);
$form['filter']['settings'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('entity_reference-settings')),
'#process' => array('_entity_reference_form_process_merge_parent'),
);
if ($selection_handler_settings['filter']['type'] == 'role') {
// Merge in default values.
$selection_handler_settings['filter'] += array(
'role' => NULL,
);
$form['filter']['settings']['role'] = array(
'#type' => 'checkboxes',
'#title' => $this->t('Restrict to the selected roles'),
'#required' => TRUE,
'#options' => array_diff_key(user_role_names(TRUE), array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID)),
'#default_value' => $selection_handler_settings['filter']['role'],
);
}
$form += parent::buildConfigurationForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
$query = parent::buildEntityQuery($match, $match_operator);
// The user entity doesn't have a label column.
if (isset($match)) {
$query->condition('name', $match, $match_operator);
}
// Filter by role.
$handler_settings = $this->configuration['handler_settings'];
if (!empty($handler_settings['filter']['role'])) {
$query->condition('roles', $handler_settings['filter']['role'], 'IN');
}
// Adding the permission check is sadly insufficient for users: core
// requires us to also know about the concept of 'blocked' and 'active'.
if (!$this->currentUser->hasPermission('administer users')) {
$query->condition('status', 1);
}
return $query;
}
/**
* {@inheritdoc}
*/
public function entityQueryAlter(SelectInterface $query) {
// Bail out early if we do not need to match the Anonymous user.
$handler_settings = $this->configuration['handler_settings'];
if (isset($handler_settings['include_anonymous']) && !$handler_settings['include_anonymous']) {
return;
}
if ($this->currentUser->hasPermission('administer users')) {
// In addition, if the user is administrator, we need to make sure to
// match the anonymous user, that doesn't actually have a name in the
// database.
$conditions = &$query->conditions();
foreach ($conditions as $key => $condition) {
if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users_field_data.name') {
// Remove the condition.
unset($conditions[$key]);
// Re-add the condition and a condition on uid = 0 so that we end up
// with a query in the form:
// WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0)
$or = db_or();
$or->condition($condition['field'], $condition['value'], $condition['operator']);
// Sadly, the Database layer doesn't allow us to build a condition
// in the form ':placeholder = :placeholder2', because the 'field'
// part of a condition is always escaped.
// As a (cheap) workaround, we separately build a condition with no
// field, and concatenate the field and the condition separately.
$value_part = db_and();
$value_part->condition('anonymous_name', $condition['value'], $condition['operator']);
$value_part->compile($this->connection, $query);
$or->condition(db_and()
->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => user_format_name($this->userStorage->load(0))))
->condition('base_table.uid', 0)
);
$query->condition($or);
}
}
}
}
}

View file

@ -0,0 +1,67 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Field\FieldFormatter\AuthorFormatter.
*/
namespace Drupal\user\Plugin\Field\FieldFormatter;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase;
/**
* Plugin implementation of the 'author' formatter.
*
* @FieldFormatter(
* id = "author",
* label = @Translation("Author"),
* description = @Translation("Display the referenced author user entity."),
* field_types = {
* "entity_reference"
* }
* )
*/
class AuthorFormatter extends EntityReferenceFormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = array();
foreach ($this->getEntitiesToView($items) as $delta => $entity) {
/** @var $referenced_user \Drupal\user\UserInterface */
$elements[$delta] = array(
'#theme' => 'username',
'#account' => $entity,
'#link_options' => array('attributes' => array('rel' => 'author')),
'#cache' => array(
'tags' => $entity->getCacheTags(),
),
);
}
return $elements;
}
/**
* {@inheritdoc}
*/
public static function isApplicable(FieldDefinitionInterface $field_definition) {
return $field_definition->getFieldStorageDefinition()->getSetting('target_type') == 'user';
}
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity) {
// Always allow an entity author's username to be read, even if the current
// user does not have permission to view the entity author's profile.
return AccessResult::allowed();
}
}

View file

@ -0,0 +1,94 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Field\FieldFormatter\UserNameFormatter.
*/
namespace Drupal\user\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'user_name' formatter.
*
* @FieldFormatter(
* id = "user_name",
* label = @Translation("User name"),
* description = @Translation("Display the user or author name."),
* field_types = {
* "string"
* }
* )
*/
class UserNameFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$options = parent::defaultSettings();
$options['link_to_entity'] = TRUE;
return $options;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
$form['link_to_entity'] = [
'#type' => 'checkbox',
'#title' => $this->t('Link to the user'),
'#default_value' => $this->getSetting('link_to_entity'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items) {
$elements = [];
foreach ($items as $delta => $item) {
/** @var $user \Drupal\user\UserInterface */
if ($user = $item->getEntity()) {
if ($this->getSetting('link_to_entity')) {
$elements[$delta] = [
'#theme' => 'username',
'#account' => $user,
'#link_options' => ['attributes' => ['rel' => 'user']],
'#cache' => [
'tags' => $user->getCacheTags(),
],
];
}
else {
$elements[$delta] = [
'#markup' => $user->getUsername(),
'#cache' => [
'tags' => $user->getCacheTags(),
],
];
}
}
}
return $elements;
}
/**
* {@inheritdoc}
*/
public static function isApplicable(FieldDefinitionInterface $field_definition) {
return $field_definition->getTargetEntityTypeId() === 'user' && $field_definition->getName() === 'name';
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser.
*/
namespace Drupal\user\Plugin\LanguageNegotiation;
use Drupal\language\LanguageNegotiationMethodBase;
use Symfony\Component\HttpFoundation\Request;
/**
* Class for identifying language from the user preferences.
*
* @LanguageNegotiation(
* id = \Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser::METHOD_ID,
* weight = -4,
* name = @Translation("User"),
* description = @Translation("Follow the user's language preference.")
* )
*/
class LanguageNegotiationUser extends LanguageNegotiationMethodBase {
/**
* The language negotiation method id.
*/
const METHOD_ID = 'language-user';
/**
* {@inheritdoc}
*/
public function getLangcode(Request $request = NULL) {
$langcode = NULL;
// User preference (only for authenticated users).
if ($this->languageManager && $this->currentUser->isAuthenticated()) {
$preferred_langcode = $this->currentUser->getPreferredLangcode();
$default_langcode = $this->languageManager->getDefaultLanguage()->getId();
$languages = $this->languageManager->getLanguages();
if (!empty($preferred_langcode) && $preferred_langcode != $default_langcode && isset($languages[$preferred_langcode])) {
$langcode = $preferred_langcode;
}
}
// No language preference from the user.
return $langcode;
}
}

View file

@ -0,0 +1,155 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin.
*/
namespace Drupal\user\Plugin\LanguageNegotiation;
use Drupal\Core\PathProcessor\PathProcessorManager;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Routing\AdminContext;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Routing\StackedRouteMatchInterface;
use Drupal\language\LanguageNegotiationMethodBase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
/**
* Identifies admin language from the user preferences.
*
* @LanguageNegotiation(
* id = Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin::METHOD_ID,
* types = {Drupal\Core\Language\LanguageInterface::TYPE_INTERFACE},
* weight = -10,
* name = @Translation("Account administration pages"),
* description = @Translation("Account administration pages language setting.")
* )
*/
class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase implements ContainerFactoryPluginInterface {
/**
* The language negotiation method id.
*/
const METHOD_ID = 'language-user-admin';
/**
* The admin context.
*
* @var \Drupal\Core\Routing\AdminContext
*/
protected $adminContext;
/**
* The router.
*
* This is only used when called from an event subscriber, before the request
* has been populated with the route info.
*
* @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface
*/
protected $router;
/**
* The path processor manager.
*
* @var \Drupal\Core\PathProcessor\PathProcessorManager
*/
protected $pathProcessorManager;
/**
* The stacked route match.
*
* @var \Drupal\Core\Routing\StackedRouteMatchInterface
*/
protected $stackedRouteMatch;
/**
* Constructs a new LanguageNegotiationUserAdmin instance.
*
* @param \Drupal\Core\Routing\AdminContext $admin_context
* The admin context.
* @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $router
* The router.
* @param \Drupal\Core\PathProcessor\PathProcessorManager $path_processor_manager
* The path processor manager.
* @param \Drupal\Core\Routing\StackedRouteMatchInterface $stacked_route_match
* The stacked route match.
*/
public function __construct(AdminContext $admin_context, UrlMatcherInterface $router, PathProcessorManager $path_processor_manager, StackedRouteMatchInterface $stacked_route_match) {
$this->adminContext = $admin_context;
$this->router = $router;
$this->pathProcessorManager = $path_processor_manager;
$this->stackedRouteMatch = $stacked_route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('router.admin_context'),
$container->get('router'),
$container->get('path_processor_manager'),
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
public function getLangcode(Request $request = NULL) {
$langcode = NULL;
// User preference (only for administrators).
if ($this->currentUser->hasPermission('access administration pages') && ($preferred_admin_langcode = $this->currentUser->getPreferredAdminLangcode(FALSE)) && $this->isAdminPath($request)) {
$langcode = $preferred_admin_langcode;
}
// Not an admin, no admin language preference or not on an admin path.
return $langcode;
}
/**
* Checks whether the given path is an administrative one.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*
* @return bool
* TRUE if the path is administrative, FALSE otherwise.
*/
protected function isAdminPath(Request $request) {
$result = FALSE;
if ($request && $this->adminContext) {
// If called from an event subscriber, the request may not have the route
// object yet (it is still being built), so use the router to look up
// based on the path.
$route_match = $this->stackedRouteMatch->getRouteMatchFromRequest($request);
if ($route_match && !$route_object = $route_match->getRouteObject()) {
try {
// Process the path as an inbound path. This will remove any language
// prefixes and other path components that inbound processing would
// clear out, so we can attempt to load the route clearly.
$path = $this->pathProcessorManager->processInbound(urldecode(rtrim($request->getPathInfo(), '/')), $request);
$attributes = $this->router->match($path);
}
catch (ResourceNotFoundException $e) {
return FALSE;
}
catch (AccessDeniedHttpException $e) {
return FALSE;
}
$route_object = $attributes[RouteObjectInterface::ROUTE_OBJECT];
}
$result = $this->adminContext->isAdminRoute($route_object);
}
return $result;
}
}

View file

@ -0,0 +1,178 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Search\UserSearch.
*/
namespace Drupal\user\Plugin\Search;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\search\Plugin\SearchPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Executes a keyword search for users against the {users} database table.
*
* @SearchPlugin(
* id = "user_search",
* title = @Translation("Users")
* )
*/
class UserSearch extends SearchPluginBase implements AccessibleInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* {@inheritdoc}
*/
static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$container->get('database'),
$container->get('entity.manager'),
$container->get('module_handler'),
$container->get('current_user'),
$configuration,
$plugin_id,
$plugin_definition
);
}
/**
* Creates a UserSearch object.
*
* @param Connection $database
* The database connection.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
*/
public function __construct(Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, AccountInterface $current_user, array $configuration, $plugin_id, $plugin_definition) {
$this->database = $database;
$this->entityManager = $entity_manager;
$this->moduleHandler = $module_handler;
$this->currentUser = $current_user;
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
$result = AccessResult::allowedIf(!empty($account) && $account->hasPermission('access user profiles'))->cachePerPermissions();
return $return_as_object ? $result : $result->isAllowed();
}
/**
* {@inheritdoc}
*/
public function execute() {
$results = array();
if (!$this->isSearchExecutable()) {
return $results;
}
// Process the keywords.
$keys = $this->keywords;
// Escape for LIKE matching.
$keys = $this->database->escapeLike($keys);
// Replace wildcards with MySQL/PostgreSQL wildcards.
$keys = preg_replace('!\*+!', '%', $keys);
// Run the query to find matching users.
$query = $this->database
->select('users_field_data', 'users')
->extend('Drupal\Core\Database\Query\PagerSelectExtender');
$query->fields('users', array('uid'));
$query->condition('default_langcode', 1);
if ($this->currentUser->hasPermission('administer users')) {
// Administrators can also search in the otherwise private email field,
// and they don't need to be restricted to only active users.
$query->fields('users', array('mail'));
$query->condition($query->orConditionGroup()
->condition('name', '%' . $keys . '%', 'LIKE')
->condition('mail', '%' . $keys . '%', 'LIKE')
);
}
else {
// Regular users can only search via usernames, and we do not show them
// blocked accounts.
$query->condition('name', '%' . $keys . '%', 'LIKE')
->condition('status', 1);
}
$uids = $query
->limit(15)
->execute()
->fetchCol();
$accounts = $this->entityManager->getStorage('user')->loadMultiple($uids);
foreach ($accounts as $account) {
$result = array(
'title' => $account->getUsername(),
'link' => $account->url('canonical', array('absolute' => TRUE)),
);
if ($this->currentUser->hasPermission('administer users')) {
$result['title'] .= ' (' . $account->getEmail() . ')';
}
$results[] = $result;
}
return $results;
}
/*
* {@inheritdoc}
*/
public function getHelp() {
$help = array('list' => array(
'#theme' => 'item_list',
'#items' => array(
$this->t('User search looks for user names and partial user names. Example: mar would match usernames mar, delmar, and maryjane.'),
$this->t('You can use * as a wildcard within your keyword. Example: m*r would match user names mar, delmar, and elementary.'),
),
));
return $help;
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Validation\Constraint\ProtectedUserFieldConstraint.
*/
namespace Drupal\user\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Checks if the plain text password is provided for editing a protected field.
*
* @Constraint(
* id = "ProtectedUserField",
* label = @Translation("Password required for protected field change", context = "Validation")
* )
*/
class ProtectedUserFieldConstraint extends Constraint {
/**
* Violation message.
*
* @var string
*/
public $message = "Your current password is missing or incorrect; it's required to change the %name.";
}

View file

@ -0,0 +1,98 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Validation\Constraint\ProtectedUserFieldConstraintValidator.
*/
namespace Drupal\user\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\user\UserStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the ProtectedUserFieldConstraint constraint.
*/
class ProtectedUserFieldConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
/**
* User storage handler.
*
* @var \Drupal\user\UserStorageInterface
*/
protected $userStorage;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* Constructs the object.
*
* @param \Drupal\user\UserStorageInterface $user_storage
* The user storage handler.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* The current user.
*/
public function __construct(UserStorageInterface $user_storage, AccountProxyInterface $current_user) {
$this->userStorage = $user_storage;
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('user'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function validate($items, Constraint $constraint) {
if (!isset($items)) {
return;
}
/* @var \Drupal\Core\Field\FieldItemListInterface $items */
$field = $items->getFieldDefinition();
/* @var \Drupal\user\UserInterface $account */
$account = $items->getEntity();
if (!isset($account) || !empty($account->_skipProtectedUserFieldConstraint)) {
// Looks like we are validating a field not being part of a user, or the
// constraint should be skipped, so do nothing.
return;
}
// Only validate for existing entities and if this is the current user.
if (!$account->isNew() && $account->id() == $this->currentUser->id()) {
/* @var \Drupal\user\UserInterface $account_unchanged */
$account_unchanged = $this->userStorage
->loadUnchanged($account->id());
$changed = FALSE;
// Special case for the password, it being empty means that the existing
// password should not be changed, ignore empty password fields.
$value = $items->value;
if ($field->getName() != 'pass' || !empty($value)) {
// Compare the values of the field this is being validated on.
$changed = $items->getValue() != $account_unchanged->get($field->getName())->getValue();
}
if ($changed && (!$account->checkExistingPassword($account_unchanged))) {
$this->context->addViolation($constraint->message, array('%name' => $field->getLabel()));
}
}
}
}

View file

@ -0,0 +1,77 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Validation\Constraint\UserMailRequired.
*/
namespace Drupal\user\Plugin\Validation\Constraint;
use Drupal\Component\Utility\SafeMarkup;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\ExecutionContextInterface;
/**
* Checks if the user's email address is provided if required.
*
* The user mail field is NOT required if account originally had no mail set
* and the user performing the edit has 'administer users' permission.
* This allows users without email address to be edited and deleted.
*
* @Constraint(
* id = "UserMailRequired",
* label = @Translation("User email required", context = "Validation")
* )
*/
class UserMailRequired extends Constraint implements ConstraintValidatorInterface {
/**
* Violation message. Use the same message as FormValidator.
*
* @var string
*/
public $message = '!name field is required.';
/**
* @var \Symfony\Component\Validator\ExecutionContextInterface
*/
protected $context;
/**
* {@inheritDoc}
*/
public function initialize(ExecutionContextInterface $context) {
$this->context = $context;
}
/**
* {@inheritdoc}
*/
public function validatedBy() {
return get_class($this);
}
/**
* {@inheritdoc}
*/
public function validate($items, Constraint $constraint) {
/** @var \Drupal\Core\Field\FieldItemListInterface $items */
/** @var \Drupal\user\UserInterface $account */
$account = $items->getEntity();
$existing_value = NULL;
if ($account->id()) {
$account_unchanged = \Drupal::entityManager()
->getStorage('user')
->loadUnchanged($account->id());
$existing_value = $account_unchanged->getEmail();
}
$required = !(!$existing_value && \Drupal::currentUser()->hasPermission('administer users'));
if ($required && (!isset($items) || $items->isEmpty())) {
$this->context->addViolation($this->message, array('!name' => SafeMarkup::placeholder($account->getFieldDefinition('mail')->getLabel())));
}
}
}

View file

@ -0,0 +1,30 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Validation\Constraint\UserMailUnique.
*/
namespace Drupal\user\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Checks if a user's email address is unique on the site.
*
* @Constraint(
* id = "UserMailUnique",
* label = @Translation("User email unique", context = "Validation")
* )
*/
class UserMailUnique extends Constraint {
public $message = 'The email address %value is already taken.';
/**
* {@inheritdoc}
*/
public function validatedBy() {
return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator';
}
}

View file

@ -0,0 +1,28 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Validation\Constraint\UserNameConstraint.
*/
namespace Drupal\user\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Checks if a value is a valid user name.
*
* @Constraint(
* id = "UserName",
* label = @Translation("User name", context = "Validation"),
* )
*/
class UserNameConstraint extends Constraint {
public $emptyMessage = 'You must enter a username.';
public $spaceBeginMessage = 'The username cannot begin with a space.';
public $spaceEndMessage = 'The username cannot end with a space.';
public $multipleSpacesMessage = 'The username cannot contain multiple spaces in a row.';
public $illegalMessage = 'The username contains an illegal character.';
public $tooLongMessage = 'The username %name is too long: it must be %max characters or less.';
}

View file

@ -0,0 +1,56 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Validation\Constraint\UserNameConstraintValidator.
*/
namespace Drupal\user\Plugin\Validation\Constraint;
use Drupal\Component\Utility\Unicode;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the UserName constraint.
*/
class UserNameConstraintValidator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
public function validate($items, Constraint $constraint) {
if (!isset($items) || !$items->value) {
$this->context->addViolation($constraint->emptyMessage);
return;
}
$name = $items->first()->value;
if (substr($name, 0, 1) == ' ') {
$this->context->addViolation($constraint->spaceBeginMessage);
}
if (substr($name, -1) == ' ') {
$this->context->addViolation($constraint->spaceEndMessage);
}
if (strpos($name, ' ') !== FALSE) {
$this->context->addViolation($constraint->multipleSpacesMessage);
}
if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)
|| preg_match(
'/[\x{80}-\x{A0}' . // Non-printable ISO-8859-1 + NBSP
'\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)
) {
$this->context->addViolation($constraint->illegalMessage);
}
if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
$this->context->addViolation($constraint->tooLongMessage, array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
}
}
}

View file

@ -0,0 +1,30 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\Validation\Constraint\UserNameUnique.
*/
namespace Drupal\user\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Checks if a user name is unique on the site.
*
* @Constraint(
* id = "UserNameUnique",
* label = @Translation("User name unique", context = "Validation"),
* )
*/
class UserNameUnique extends Constraint {
public $message = 'The username %value is already taken.';
/**
* {@inheritdoc}
*/
public function validatedBy() {
return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator';
}
}

View file

@ -0,0 +1,150 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\access\Permission.
*/
namespace Drupal\user\Plugin\views\access;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PermissionHandlerInterface;
use Drupal\views\Plugin\CacheablePluginInterface;
use Drupal\views\Plugin\views\access\AccessPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
/**
* Access plugin that provides permission-based access control.
*
* @ingroup views_access_plugins
*
* @ViewsAccess(
* id = "perm",
* title = @Translation("Permission"),
* help = @Translation("Access will be granted to users with the specified permission string.")
* )
*/
class Permission extends AccessPluginBase implements CacheablePluginInterface {
/**
* Overrides Drupal\views\Plugin\Plugin::$usesOptions.
*/
protected $usesOptions = TRUE;
/**
* The permission handler.
*
* @var \Drupal\user\PermissionHandlerInterface
*/
protected $permissionHandler;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a Permission object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\PermissionHandlerInterface $permission_handler
* The permission handler.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, PermissionHandlerInterface $permission_handler, ModuleHandlerInterface $module_handler) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->permissionHandler = $permission_handler;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('user.permissions'),
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
*/
public function access(AccountInterface $account) {
return $account->hasPermission($this->options['perm']);
}
/**
* {@inheritdoc}
*/
public function alterRouteDefinition(Route $route) {
$route->setRequirement('_permission', $this->options['perm']);
}
public function summaryTitle() {
$permissions = $this->permissionHandler->getPermissions();
if (isset($permissions[$this->options['perm']])) {
return $permissions[$this->options['perm']]['title'];
}
return $this->t($this->options['perm']);
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['perm'] = array('default' => 'access content');
return $options;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
// Get list of permissions
$perms = [];
$permissions = $this->permissionHandler->getPermissions();
foreach ($permissions as $perm => $perm_item) {
$provider = $perm_item['provider'];
$display_name = $this->moduleHandler->getName($provider);
$perms[$display_name][$perm] = SafeMarkup::checkPlain(strip_tags($perm_item['title']));
}
$form['perm'] = array(
'#type' => 'select',
'#options' => $perms,
'#title' => $this->t('Permission'),
'#default_value' => $this->options['perm'],
'#description' => $this->t('Only users with the selected permission flag will be able to access this display.'),
);
}
/**
* {@inheritdoc}
*/
public function isCacheable() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return ['user.permissions'];
}
}

View file

@ -0,0 +1,164 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\access\Role.
*/
namespace Drupal\user\Plugin\views\access;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\RoleStorageInterface;
use Drupal\views\Plugin\CacheablePluginInterface;
use Drupal\views\Plugin\views\access\AccessPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
use Drupal\Core\Session\AccountInterface;
/**
* Access plugin that provides role-based access control.
*
* @ingroup views_access_plugins
*
* @ViewsAccess(
* id = "role",
* title = @Translation("Role"),
* help = @Translation("Access will be granted to users with any of the specified roles.")
* )
*/
class Role extends AccessPluginBase implements CacheablePluginInterface {
/**
* Overrides Drupal\views\Plugin\Plugin::$usesOptions.
*/
protected $usesOptions = TRUE;
/**
* The role storage.
*
* @var \Drupal\user\RoleStorageInterface
*/
protected $roleStorage;
/**
* Constructs a Role object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\RoleStorageInterface $role_storage
* The role storage.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, RoleStorageInterface $role_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->roleStorage = $role_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')->getStorage('user_role')
);
}
/**
* {@inheritdoc}
*/
public function access(AccountInterface $account) {
return array_intersect(array_filter($this->options['role']), $account->getRoles());
}
/**
* {@inheritdoc}
*/
public function alterRouteDefinition(Route $route) {
if ($this->options['role']) {
$route->setRequirement('_role', (string) implode('+', $this->options['role']));
}
}
public function summaryTitle() {
$count = count($this->options['role']);
if ($count < 1) {
return $this->t('No role(s) selected');
}
elseif ($count > 1) {
return $this->t('Multiple roles');
}
else {
$rids = user_role_names();
$rid = reset($this->options['role']);
return SafeMarkup::checkPlain($rids[$rid]);
}
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['role'] = array('default' => array());
return $options;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$form['role'] = array(
'#type' => 'checkboxes',
'#title' => $this->t('Role'),
'#default_value' => $this->options['role'],
'#options' => array_map('\Drupal\Component\Utility\SafeMarkup::checkPlain', user_role_names()),
'#description' => $this->t('Only the checked roles will be able to access this display.'),
);
}
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
$role = $form_state->getValue(array('access_options', 'role'));
$role = array_filter($role);
if (!$role) {
$form_state->setError($form['role'], $this->t('You must select at least one role if type is "by role"'));
}
$form_state->setValue(array('access_options', 'role'), $role);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$dependencies = parent::calculateDependencies();
foreach (array_keys($this->options['role']) as $rid) {
if ($role = $this->roleStorage->load($rid)) {
$dependencies[$role->getConfigDependencyKey()][] = $role->getConfigDependencyName();
}
}
return $dependencies;
}
/**
* {@inheritdoc}
*/
public function isCacheable() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return ['user.roles'];
}
}

View file

@ -0,0 +1,68 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\argument\RolesRid.
*/
namespace Drupal\user\Plugin\views\argument;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\views\Plugin\views\argument\ManyToOne;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Allow role ID(s) as argument.
*
* @ingroup views_argument_handlers
*
* @ViewsArgument("user__roles_rid")
*/
class RolesRid extends ManyToOne {
/**
* The role entity storage
*
* @var \Drupal\user\RoleStorage
*/
protected $roleStorage;
/**
* Constructs a \Drupal\user\Plugin\views\argument\RolesRid object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->roleStorage = $entity_manager->getStorage('user_role');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return parent::create($container, $configuration, $plugin_id, $plugin_definition, $container->get('entity.manager'));
}
/**
* {@inheritdoc}
*/
public function title_query() {
$entities = $this->roleStorage->loadMultiple($this->value);
$titles = array();
foreach ($entities as $entity) {
$titles[] = SafeMarkup::checkPlain($entity->label());
}
return $titles;
}
}

View file

@ -0,0 +1,68 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\argument\Uid.
*/
namespace Drupal\user\Plugin\views\argument;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\views\Plugin\views\argument\NumericArgument;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Argument handler to accept a user id.
*
* @ingroup views_argument_handlers
*
* @ViewsArgument("user_uid")
*/
class Uid extends NumericArgument {
/**
* The user storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;
/**
* Constructs a Drupal\Component\Plugin\PluginBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The user storage.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->storage = $storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition,
$container->get('entity.manager')->getStorage('user'));
}
/**
* Override the behavior of title(). Get the name of the user.
*
* @return array
* A list of usernames.
*/
public function titleQuery() {
return array_map(function($account) {
return SafeMarkup::checkPlain($account->label());
}, $this->storage->loadMultiple($this->value));
}
}

View file

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\argument_default\CurrentUser.
*/
namespace Drupal\user\Plugin\views\argument_default;
use Drupal\views\Plugin\CacheablePluginInterface;
use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
/**
* Default argument plugin to extract the current user
*
* This plugin actually has no options so it odes not need to do a great deal.
*
* @ViewsArgumentDefault(
* id = "current_user",
* title = @Translation("User ID from logged in user")
* )
*/
class CurrentUser extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
public function getArgument() {
return \Drupal::currentUser()->id();
}
/**
* {@inheritdoc}
*/
public function isCacheable() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return ['user'];
}
}

View file

@ -0,0 +1,122 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\argument_default\User.
*/
namespace Drupal\user\Plugin\views\argument_default;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\Plugin\CacheablePluginInterface;
use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\user\UserInterface;
use Drupal\node\NodeInterface;
/**
* Default argument plugin to extract a user from request.
*
* @ViewsArgumentDefault(
* id = "user",
* title = @Translation("User ID from route context")
* )
*/
class User extends ArgumentDefaultPluginBase implements CacheablePluginInterface {
/**
* The route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Constructs a new User instance.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['user'] = array('default' => '');
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
$form['user'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Also look for a node and use the node author'),
'#default_value' => $this->options['user'],
);
}
/**
* {@inheritdoc}
*/
public function getArgument() {
// If there is a user object in the current route.
if ($user = $this->routeMatch->getParameter('user')) {
if ($user instanceof UserInterface) {
return $user->id();
}
}
// If option to use node author; and node in current route.
if (!empty($this->options['user']) && $node = $this->routeMatch->getParameter('node')) {
if ($node instanceof NodeInterface) {
return $node->getOwnerId();
}
}
}
/**
* {@inheritdoc}
*/
public function isCacheable() {
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return ['url'];
}
}

View file

@ -0,0 +1,118 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\argument_validator\User.
*/
namespace Drupal\user\Plugin\views\argument_validator;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\views\Plugin\views\argument\ArgumentPluginBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\argument_validator\Entity;
/**
* Validate whether an argument is a valid user.
*
* This supports either numeric arguments (UID) or strings (username) and
* converts either one into the user's UID. This validator also sets the
* argument's title to the username.
*/
class User extends Entity {
/**
* The user storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $userStorage;
/**
* {@inheritdoc}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_manager);
$this->userStorage = $entity_manager->getStorage('user');
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['restrict_roles'] = array('default' => FALSE);
$options['roles'] = array('default' => array());
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$sanitized_id = ArgumentPluginBase::encodeValidatorId($this->definition['id']);
$form['restrict_roles'] = array(
'#type' => 'checkbox',
'#title' => $this->t('Restrict user based on role'),
'#default_value' => $this->options['restrict_roles'],
);
$form['roles'] = array(
'#type' => 'checkboxes',
'#title' => $this->t('Restrict to the selected roles'),
'#options' => array_map(array('\Drupal\Component\Utility\SafeMarkup', 'checkPlain'), user_role_names(TRUE)),
'#default_value' => $this->options['roles'],
'#description' => $this->t('If no roles are selected, users from any role will be allowed.'),
'#states' => array(
'visible' => array(
':input[name="options[validate][options][' . $sanitized_id . '][restrict_roles]"]' => array('checked' => TRUE),
),
),
);
}
/**
* {@inheritdoc}
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state, &$options = array()) {
// filter trash out of the options so we don't store giant unnecessary arrays
$options['roles'] = array_filter($options['roles']);
}
/**
* {@inheritdoc}
*/
protected function validateEntity(EntityInterface $entity) {
/** @var \Drupal\user\UserInterface $entity */
$role_check_success = TRUE;
// See if we're filtering users based on roles.
if (!empty($this->options['restrict_roles']) && !empty($this->options['roles'])) {
$roles = $this->options['roles'];
if (!(bool) array_intersect($entity->getRoles(), $roles)) {
$role_check_success = FALSE;
}
}
return $role_check_success && parent::validateEntity($entity);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$dependencies = parent::calculateDependencies();
foreach ($this->entityManager->getStorage('user_role')->loadMultiple(array_keys($this->options['roles'])) as $role) {
$dependencies[$role->getConfigDependencyKey()][] = $role->getConfigDependencyName();
}
return $dependencies;
}
}

View file

@ -0,0 +1,85 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\argument_validator\UserName.
*/
namespace Drupal\user\Plugin\views\argument_validator;
use Drupal\Core\Form\FormStateInterface;
/**
* Validates whether a user name is valid.
*
* @ViewsArgumentValidator(
* id = "user_name",
* title = @Translation("User name"),
* entity_type = "user"
* )
*/
class UserName extends User {
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$entity_type = $this->entityManager->getDefinition('user');
$form['multiple']['#options'] = array(
0 => $this->t('Single name', array('%type' => $entity_type->getLabel())),
1 => $this->t('One or more names separated by , or +', array('%type' => $entity_type->getLabel())),
);
}
/**
* {@inheritdoc}
*/
public function validateArgument($argument) {
if ($this->multipleCapable && $this->options['multiple']) {
// At this point only interested in individual IDs no matter what type,
// just splitting by the allowed delimiters.
$names = array_filter(preg_split('/[,+ ]/', $argument));
}
elseif ($argument) {
$names = array($argument);
}
// No specified argument should be invalid.
else {
return FALSE;
}
$accounts = $this->userStorage->loadByProperties(array('name' => $names));
// If there are no accounts, return FALSE now. As we will not enter the
// loop below otherwise.
if (empty($accounts)) {
return FALSE;
}
// Validate each account. If any fails break out and return false.
foreach ($accounts as $account) {
if (!in_array($account->getUserName(), $names) || !$this->validateEntity($account)) {
return FALSE;
}
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function processSummaryArguments(&$args) {
// If the validation says the input is an username, we should reverse the
// argument so it works for example for generation summary urls.
$uids_arg_keys = array_flip($args);
foreach ($this->userStorage->loadMultiple($args) as $uid => $account) {
$args[$uids_arg_keys[$uid]] = $account->label();
}
}
}

View file

@ -0,0 +1,132 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\field\Permissions.
*/
namespace Drupal\user\Plugin\views\field;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\field\PrerenderList;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Field handler to provide a list of permissions.
*
* @ingroup views_field_handlers
*
* @ViewsField("user_permissions")
*/
class Permissions extends PrerenderList {
/**
* The role storage.
*
* @var \Drupal\user\RoleStorageInterface
*/
protected $roleStorage;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a Drupal\Component\Plugin\PluginBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ModuleHandlerInterface $module_handler, EntityManagerInterface $entity_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->roleStorage = $entity_manager->getStorage('user_role');
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('module_handler'), $container->get('entity.manager'));
}
/**
* Overrides Drupal\views\Plugin\views\field\FieldPluginBase::init().
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->additional_fields['uid'] = array('table' => 'users_field_data', 'field' => 'uid');
}
public function query() {
$this->addAdditionalFields();
$this->field_alias = $this->aliases['uid'];
}
public function preRender(&$values) {
$uids = array();
$this->items = array();
$permission_names = \Drupal::service('user.permissions')->getPermissions();
$rids = array();
foreach ($values as $result) {
$user_rids = $this->getEntity($result)->getRoles();
$uid = $this->getValue($result);
foreach ($user_rids as $rid) {
$rids[$rid][] = $uid;
}
}
if ($rids) {
$roles = $this->roleStorage->loadMultiple(array_keys($rids));
foreach ($rids as $rid => $role_uids) {
foreach ($roles[$rid]->getPermissions() as $permission) {
foreach ($role_uids as $uid) {
$this->items[$uid][$permission]['permission'] = $permission_names[$permission]['title'];
}
}
}
foreach ($uids as $uid) {
if (isset($this->items[$uid])) {
ksort($this->items[$uid]);
}
}
}
}
function render_item($count, $item) {
return $item['permission'];
}
/*
protected function documentSelfTokens(&$tokens) {
$tokens['[' . $this->options['id'] . '-role' . ']'] = $this->t('The name of the role.');
$tokens['[' . $this->options['id'] . '-rid' . ']'] = $this->t('The role ID of the role.');
}
protected function addSelfTokens(&$tokens, $item) {
$tokens['[' . $this->options['id'] . '-role' . ']'] = $item['role'];
$tokens['[' . $this->options['id'] . '-rid' . ']'] = $item['rid'];
}
*/
}

View file

@ -0,0 +1,115 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\field\Roles.
*/
namespace Drupal\user\Plugin\views\field;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Database\Connection;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\field\PrerenderList;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Field handler to provide a list of roles.
*
* @ingroup views_field_handlers
*
* @ViewsField("user_roles")
*/
class Roles extends PrerenderList {
/**
* Database Service Object.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* Constructs a Drupal\Component\Plugin\PluginBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Database\Connection $database
* Database Service Object.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->database = $database;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('database'));
}
/**
* Overrides Drupal\views\Plugin\views\field\FieldPluginBase::init().
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->additional_fields['uid'] = array('table' => 'users_field_data', 'field' => 'uid');
}
public function query() {
$this->addAdditionalFields();
$this->field_alias = $this->aliases['uid'];
}
public function preRender(&$values) {
$uids = array();
$this->items = array();
foreach ($values as $result) {
$uids[] = $this->getValue($result);
}
if ($uids) {
$roles = user_roles();
$result = $this->database->query('SELECT u.entity_id as uid, u.roles_target_id as rid FROM {user__roles} u WHERE u.entity_id IN ( :uids[] ) AND u.roles_target_id IN ( :rids[] )', array(':uids[]' => $uids, ':rids[]' => array_keys($roles)));
foreach ($result as $role) {
$this->items[$role->uid][$role->rid]['role'] = SafeMarkup::checkPlain($roles[$role->rid]->label());
$this->items[$role->uid][$role->rid]['rid'] = $role->rid;
}
// Sort the roles for each user by role weight.
$ordered_roles = array_flip(array_keys($roles));
foreach ($this->items as &$user_roles) {
// Create an array of rids that the user has in the role weight order.
$sorted_keys = array_intersect_key($ordered_roles, $user_roles);
// Merge with the unsorted array of role information which has the
// effect of sorting it.
$user_roles = array_merge($sorted_keys, $user_roles);
}
}
}
function render_item($count, $item) {
return $item['role'];
}
protected function documentSelfTokens(&$tokens) {
$tokens['[' . $this->options['id'] . '-role' . ']'] = $this->t('The name of the role.');
$tokens['[' . $this->options['id'] . '-rid' . ']'] = $this->t('The role machine-name of the role.');
}
protected function addSelfTokens(&$tokens, $item) {
if (!empty($item['role'])) {
$tokens['[' . $this->options['id'] . '-role' . ']'] = $item['role'];
$tokens['[' . $this->options['id'] . '-rid' . ']'] = $item['rid'];
}
}
}

View file

@ -0,0 +1,47 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\field\UserBulkForm.
*/
namespace Drupal\user\Plugin\views\field;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\system\Plugin\views\field\BulkForm;
use Drupal\user\UserInterface;
/**
* Defines a user operations bulk form element.
*
* @ViewsField("user_bulk_form")
*/
class UserBulkForm extends BulkForm {
/**
* {@inheritdoc}
*
* Provide a more useful title to improve the accessibility.
*/
public function viewsForm(&$form, FormStateInterface $form_state) {
parent::viewsForm($form, $form_state);
if (!empty($this->view->result)) {
foreach ($this->view->result as $row_index => $result) {
$account = $result->_entity;
if ($account instanceof UserInterface) {
$form[$this->options['id']][$row_index]['#title'] = $this->t('Update the user %name', array('%name' => $account->label()));
}
}
}
}
/**
* {@inheritdoc}
*/
protected function emptySelectedMessage() {
return $this->t('No users selected.');
}
}

View file

@ -0,0 +1,115 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\field\UserData.
*/
namespace Drupal\user\Plugin\views\field;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
use Drupal\user\UserDataInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides access to the user data service.
*
* @ingroup views_field_handlers
*
* @see \Drupal\user\UserDataInterface
*
* @ViewsField("user_data")
*/
class UserData extends FieldPluginBase {
/**
* Provides the user data service object.
*
* @var \Drupal\user\UserDataInterface
*/
protected $userData;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('user.data'), $container->get('module_handler'));
}
/**
* Constructs a UserData object.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, UserDataInterface $user_data, ModuleHandlerInterface $module_handler) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->userData = $user_data;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['data_module'] = array('default' => '');
$options['data_name'] = array('default' => '');
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$modules = $this->moduleHandler->getModuleList();
$names = array();
foreach (array_keys($modules) as $name) {
$names[$name] = $this->moduleHandler->getName($name);
}
$form['data_module'] = array(
'#title' => $this->t('Module name'),
'#type' => 'select',
'#description' => $this->t('The module which sets this user data.'),
'#default_value' => $this->options['data_module'],
'#options' => $names,
);
$form['data_name'] = array(
'#title' => $this->t('Name'),
'#type' => 'textfield',
'#description' => $this->t('The name of the data key.'),
'#default_value' => $this->options['data_name'],
);
}
/**
* {@inheritdoc}
*/
public function render(ResultRow $values) {
$uid = $this->getValue($values);
$data = $this->userData->get($this->options['data_module'], $uid, $this->options['data_name']);
// Don't sanitize if no value was found.
if (isset($data)) {
return $this->sanitizeValue($data);
}
}
}

View file

@ -0,0 +1,62 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\filter\Current.
*/
namespace Drupal\user\Plugin\views\filter;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\filter\BooleanOperator;
/**
* Filter handler for the current user.
*
* @ingroup views_filter_handlers
*
* @ViewsFilter("user_current")
*/
class Current extends BooleanOperator {
/**
* Overrides Drupal\views\Plugin\views\filter\BooleanOperator::init().
*/
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
$this->value_value = $this->t('Is the logged in user');
}
public function query() {
$this->ensureMyTable();
$field = $this->tableAlias . '.' . $this->realField . ' ';
$or = db_or();
if (empty($this->value)) {
$or->condition($field, '***CURRENT_USER***', '<>');
if ($this->accept_null) {
$or->isNull($field);
}
}
else {
$or->condition($field, '***CURRENT_USER***', '=');
}
$this->query->addWhere($this->options['group'], $or);
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
$contexts = parent::getCacheContexts();
// This filter depends on the current user.
$contexts[] = 'user';
return $contexts;
}
}

View file

@ -0,0 +1,127 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\filter\Name.
*/
namespace Drupal\user\Plugin\views\filter;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Entity\Element\EntityAutocomplete;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\Entity\User;
use Drupal\views\Plugin\views\filter\InOperator;
/**
* Filter handler for usernames.
*
* @ingroup views_filter_handlers
*
* @ViewsFilter("user_name")
*/
class Name extends InOperator {
protected $alwaysMultiple = TRUE;
protected function valueForm(&$form, FormStateInterface $form_state) {
$users = $this->value ? User::loadMultiple($this->value) : array();
$default_value = EntityAutocomplete::getEntityLabels($users);
$form['value'] = array(
'#type' => 'entity_autocomplete',
'#title' => $this->t('Usernames'),
'#description' => $this->t('Enter a comma separated list of user names.'),
'#target_type' => 'user',
'#tags' => TRUE,
'#default_value' => $default_value,
'#process_default_value' => FALSE,
);
$user_input = $form_state->getUserInput();
if ($form_state->get('exposed') && !isset($user_input[$this->options['expose']['identifier']])) {
$user_input[$this->options['expose']['identifier']] = $default_value;
$form_state->setUserInput($user_input);
}
}
protected function valueValidate($form, FormStateInterface $form_state) {
$uids = [];
if ($values = $form_state->getValue(array('options', 'value'))) {
foreach ($values as $value) {
$uids[] = $value['target_id'];
}
sort($uids);
}
$form_state->setValue(array('options', 'value'), $uids);
}
public function acceptExposedInput($input) {
$rc = parent::acceptExposedInput($input);
if ($rc) {
// If we have previously validated input, override.
if (isset($this->validated_exposed_input)) {
$this->value = $this->validated_exposed_input;
}
}
return $rc;
}
public function validateExposed(&$form, FormStateInterface $form_state) {
if (empty($this->options['exposed'])) {
return;
}
if (empty($this->options['expose']['identifier'])) {
return;
}
$identifier = $this->options['expose']['identifier'];
$input = $form_state->getValue($identifier);
if ($this->options['is_grouped'] && isset($this->options['group_info']['group_items'][$input])) {
$this->operator = $this->options['group_info']['group_items'][$input]['operator'];
$input = $this->options['group_info']['group_items'][$input]['value'];
}
$uids = [];
$values = $form_state->getValue($identifier);
if ($values && (!$this->options['is_grouped'] || ($this->options['is_grouped'] && ($input != 'All')))) {
foreach ($values as $value) {
$uids[] = $value['target_id'];
}
}
if ($uids) {
$this->validated_exposed_input = $uids;
}
}
protected function valueSubmit($form, FormStateInterface $form_state) {
// prevent array filter from removing our anonymous user.
}
// Override to do nothing.
public function getValueOptions() { }
public function adminSummary() {
// set up $this->valueOptions for the parent summary
$this->valueOptions = array();
if ($this->value) {
$result = entity_load_multiple_by_properties('user', array('uid' => $this->value));
foreach ($result as $account) {
if ($account->id()) {
$this->valueOptions[$account->id()] = $account->label();
}
else {
$this->valueOptions[$account->id()] = 'Anonymous'; // Intentionally NOT translated.
}
}
}
return parent::adminSummary();
}
}

View file

@ -0,0 +1,108 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\filter\Permissions.
*/
namespace Drupal\user\Plugin\views\filter;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\user\PermissionHandlerInterface;
use Drupal\views\Plugin\views\filter\ManyToOne;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Filter handler for user roles.
*
* @ingroup views_filter_handlers
*
* @ViewsFilter("user_permissions")
*/
class Permissions extends ManyToOne {
/**
* The permission handler.
*
* @var \Drupal\user\PermissionHandlerInterface
*/
protected $permissionHandler;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Constructs a Permissions object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\PermissionHandlerInterface $permission_handler
* The permission handler.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, PermissionHandlerInterface $permission_handler, ModuleHandlerInterface $module_handler) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->permissionHandler = $permission_handler;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('user.permissions'),
$container->get('module_handler')
);
}
public function getValueOptions() {
if (!isset($this->valueOptions)) {
$permissions = $this->permissionHandler->getPermissions();
foreach ($permissions as $perm => $perm_item) {
$provider = $perm_item['provider'];
$display_name = $this->moduleHandler->getName($provider);
$this->valueOptions[$display_name][$perm] = SafeMarkup::checkPlain(strip_tags($perm_item['title']));
}
}
else {
return $this->valueOptions;
}
}
/**
* {@inheritdoc}
*
* Replace the configured permission with a filter by all roles that have this
* permission.
*/
public function query() {
// @todo user_role_names() should maybe support multiple permissions.
$rids = array();
// Get all roles, that have the configured permissions.
foreach ($this->value as $permission) {
$roles = user_role_names(FALSE, $permission);
$rids += array_keys($roles);
}
$rids = array_unique($rids);
$this->value = $rids;
// $this->value contains the role IDs that have the configured permission.
parent::query();
}
}

View file

@ -0,0 +1,87 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\filter\Roles.
*/
namespace Drupal\user\Plugin\views\filter;
use Drupal\user\RoleInterface;
use Drupal\user\RoleStorageInterface;
use Drupal\views\Plugin\views\filter\ManyToOne;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Filter handler for user roles.
*
* @ingroup views_filter_handlers
*
* @ViewsFilter("user_roles")
*/
class Roles extends ManyToOne {
/**
* The role storage.
*
* @var \Drupal\user\RoleStorageInterface
*/
protected $roleStorage;
/**
* Constructs a Roles object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\RoleStorageInterface $role_storage
* The role storage.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, RoleStorageInterface $role_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->roleStorage = $role_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')->getStorage('user_role')
);
}
public function getValueOptions() {
$this->valueOptions = user_role_names(TRUE);
unset($this->valueOptions[RoleInterface::AUTHENTICATED_ID]);
}
/**
* Override empty and not empty operator labels to be clearer for user roles.
*/
function operators() {
$operators = parent::operators();
$operators['empty']['title'] = $this->t("Only has the 'authenticated user' role");
$operators['not empty']['title'] = $this->t("Has roles in addition to 'authenticated user'");
return $operators;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
$dependencies = array();
foreach ($this->value as $role_id) {
$role = $this->roleStorage->load($role_id);
$dependencies[$role->getConfigDependencyKey()][] = $role->getConfigDependencyName();
}
return $dependencies;
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\row\UserRow.
*/
namespace Drupal\user\Plugin\views\row;
use Drupal\views\Plugin\views\row\EntityRow;
/**
* A row plugin which renders a user.
*
* @ingroup views_row_plugins
*
* @ViewsRow(
* id = "entity:user",
* )
*/
class UserRow extends EntityRow {
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['view_mode']['default'] = 'full';
return $options;
}
}

View file

@ -0,0 +1,81 @@
<?php
/**
* @file
* Contains \Drupal\user\Plugin\views\wizard\Users.
*/
namespace Drupal\user\Plugin\views\wizard;
use Drupal\views\Plugin\views\wizard\WizardPluginBase;
/**
* @todo: replace numbers with constants.
*/
/**
* Tests creating user views with the wizard.
*
* @ViewsWizard(
* id = "users",
* base_table = "users_field_data",
* title = @Translation("Users")
* )
*/
class Users extends WizardPluginBase {
/**
* Set the created column.
*/
protected $createdColumn = 'created';
/**
* Set default values for the filters.
*/
protected $filters = array(
'status' => array(
'value' => TRUE,
'table' => 'users_field_data',
'field' => 'status',
'plugin_id' => 'boolean',
'entity_type' => 'user',
'entity_field' => 'status',
)
);
/**
* Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::defaultDisplayOptions().
*/
protected function defaultDisplayOptions() {
$display_options = parent::defaultDisplayOptions();
// Add permission-based access control.
$display_options['access']['type'] = 'perm';
$display_options['access']['options']['perm'] = 'access user profiles';
// Remove the default fields, since we are customizing them here.
unset($display_options['fields']);
/* Field: User: Name */
$display_options['fields']['name']['id'] = 'name';
$display_options['fields']['name']['table'] = 'users_field_data';
$display_options['fields']['name']['field'] = 'name';
$display_options['fields']['name']['entity_type'] = 'user';
$display_options['fields']['name']['entity_field'] = 'name';
$display_options['fields']['name']['label'] = '';
$display_options['fields']['name']['alter']['alter_text'] = 0;
$display_options['fields']['name']['alter']['make_link'] = 0;
$display_options['fields']['name']['alter']['absolute'] = 0;
$display_options['fields']['name']['alter']['trim'] = 0;
$display_options['fields']['name']['alter']['word_boundary'] = 0;
$display_options['fields']['name']['alter']['ellipsis'] = 0;
$display_options['fields']['name']['alter']['strip_tags'] = 0;
$display_options['fields']['name']['alter']['html'] = 0;
$display_options['fields']['name']['hide_empty'] = 0;
$display_options['fields']['name']['empty_zero'] = 0;
$display_options['fields']['name']['plugin_id'] = 'field';
return $display_options;
}
}

View file

@ -0,0 +1,216 @@
<?php
/**
* @file
* Contains \Drupal\user\PrivateTempStore.
*/
namespace Drupal\user;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Stores and retrieves temporary data for a given owner.
*
* A PrivateTempStore can be used to make temporary, non-cache data available
* across requests. The data for the PrivateTempStore is stored in one
* key/value collection. PrivateTempStore data expires automatically after a
* given timeframe.
*
* The PrivateTempStore is different from a cache, because the data in it is not
* yet saved permanently and so it cannot be rebuilt. Typically, the
* PrivateTempStore might be used to store work in progress that is later saved
* permanently elsewhere, e.g. autosave data, multistep forms, or in-progress
* changes to complex configuration that are not ready to be saved.
*
* The PrivateTempStore differs from the SharedTempStore in that all keys are
* ensured to be unique for a particular user and users can never share data. If
* you want to be able to share data between users or use it for locking, use
* \Drupal\user\SharedTempStore.
*/
class PrivateTempStore {
/**
* The key/value storage object used for this data.
*
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
*/
protected $storage;
/**
* The lock object used for this data.
*
* @var \Drupal\Core\Lock\LockBackendInterface
*/
protected $lockBackend;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The time to live for items in seconds.
*
* By default, data is stored for one week (604800 seconds) before expiring.
*
* @var int
*/
protected $expire;
/**
* Constructs a new object for accessing data from a key/value store.
*
* @param KeyValueStoreExpirableInterface $storage
* The key/value storage object used for this data. Each storage object
* represents a particular collection of data and will contain any number
* of key/value pairs.
* @param \Drupal\Core\Lock\LockBackendInterface $lockBackend
* The lock object used for this data.
* @param mixed $owner
* The owner key to store along with the data (e.g. a user or session ID).
* @param int $expire
* The time to live for items, in seconds.
*/
public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lockBackend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) {
$this->storage = $storage;
$this->lockBackend = $lockBackend;
$this->currentUser = $current_user;
$this->requestStack = $request_stack;
$this->expire = $expire;
}
/**
* Retrieves a value from this PrivateTempStore for a given key.
*
* @param string $key
* The key of the data to retrieve.
*
* @return mixed
* The data associated with the key, or NULL if the key does not exist.
*/
public function get($key) {
$key = $this->createkey($key);
if (($object = $this->storage->get($key)) && ($object->owner == $this->getOwner())) {
return $object->data;
}
}
/**
* Stores a particular key/value pair in this PrivateTempStore.
*
* @param string $key
* The key of the data to store.
* @param mixed $value
* The data to store.
*/
public function set($key, $value) {
$key = $this->createkey($key);
if (!$this->lockBackend->acquire($key)) {
$this->lockBackend->wait($key);
if (!$this->lockBackend->acquire($key)) {
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to update item %key in %collection temporary storage.", array(
'%key' => $key,
'%collection' => $this->storage->getCollectionName(),
)));
}
}
$value = (object) array(
'owner' => $this->getOwner(),
'data' => $value,
'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
);
$this->storage->setWithExpire($key, $value, $this->expire);
$this->lockBackend->release($key);
}
/**
* Returns the metadata associated with a particular key/value pair.
*
* @param string $key
* The key of the data to store.
*
* @return mixed
* An object with the owner and updated time if the key has a value, or
* NULL otherwise.
*/
public function getMetadata($key) {
$key = $this->createkey($key);
// Fetch the key/value pair and its metadata.
$object = $this->storage->get($key);
if ($object) {
// Don't keep the data itself in memory.
unset($object->data);
return $object;
}
}
/**
* Deletes data from the store for a given key and releases the lock on it.
*
* @param string $key
* The key of the data to delete.
*
* @return bool
* TRUE if the object was deleted or does not exist, FALSE if it exists but
* is not owned by $this->owner.
*/
public function delete($key) {
$key = $this->createkey($key);
if (!$object = $this->storage->get($key)) {
return TRUE;
}
elseif ($object->owner != $this->getOwner()) {
return FALSE;
}
if (!$this->lockBackend->acquire($key)) {
$this->lockBackend->wait($key);
if (!$this->lockBackend->acquire($key)) {
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to delete item %key from %collection temporary storage.", array(
'%key' => $key,
'%collection' => $this->storage->getCollectionName(),
)));
}
}
$this->storage->delete($key);
$this->lockBackend->release($key);
return TRUE;
}
/**
* Ensures that the key is unique for a user.
*
* @param string $key
* The key.
*
* @return string
* The unique key for the user.
*/
protected function createkey($key) {
return $this->getOwner() . ':' . $key;
}
/**
* Gets the current owner based on the current user or the session ID.
*
* @return string
* The owner.
*/
protected function getOwner() {
return $this->currentUser->id() ?: $this->requestStack->getCurrentRequest()->getSession()->getId();
}
}

View file

@ -0,0 +1,89 @@
<?php
/**
* @file
* Contains \Drupal\user\PrivateTempStoreFactory.
*/
namespace Drupal\user;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Creates a PrivateTempStore object for a given collection.
*/
class PrivateTempStoreFactory {
/**
* The storage factory creating the backend to store the data.
*
* @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface
*/
protected $storageFactory;
/**
* The lock object used for this data.
*
* @var \Drupal\Core\Lock\LockBackendInterface $lockBackend
*/
protected $lockBackend;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The time to live for items in seconds.
*
* @var int
*/
protected $expire;
/**
* Constructs a Drupal\user\PrivateTempStoreFactory object.
*
* @param \Drupal\Core\Database\Connection $connection
* The connection object used for this data.
* @param \Drupal\Core\Lock\LockBackendInterface $lockBackend
* The lock object used for this data.
* @param int $expire
* The time to live for items, in seconds.
*/
function __construct(KeyValueExpirableFactoryInterface $storage_factory, LockBackendInterface $lockBackend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) {
$this->storageFactory = $storage_factory;
$this->lockBackend = $lockBackend;
$this->currentUser = $current_user;
$this->requestStack = $request_stack;
$this->expire = $expire;
}
/**
* Creates a PrivateTempStore.
*
* @param string $collection
* The collection name to use for this key/value store. This is typically
* a shared namespace or module name, e.g. 'views', 'entity', etc.
*
* @return \Drupal\user\PrivateTempStore
* An instance of the key/value store.
*/
function get($collection) {
// Store the data for this collection in the database.
$storage = $this->storageFactory->get("user.private_tempstore.$collection");
return new PrivateTempStore($storage, $this->lockBackend, $this->currentUser, $this->requestStack, $this->expire);
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* @file
* Contains \Drupal\user\ProfileForm.
*/
namespace Drupal\user;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
/**
* Form controller for the profile forms.
*/
class ProfileForm extends AccountForm {
/**
* {@inheritdoc}
*/
public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, QueryFactory $entity_query) {
parent::__construct($entity_manager, $language_manager, $entity_query);
}
/**
* {@inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
$element = parent::actions($form, $form_state);
// The user account being edited.
$account = $this->entity;
// The user doing the editing.
$user = $this->currentUser();
$element['delete']['#type'] = 'submit';
$element['delete']['#value'] = $this->t('Cancel account');
$element['delete']['#submit'] = array('::editCancelSubmit');
$element['delete']['#access'] = $account->id() > 1 && (($account->id() == $user->id() && $user->hasPermission('cancel account')) || $user->hasPermission('administer users'));
return $element;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$account = $this->entity;
$account->save();
$form_state->setValue('uid', $account->id());
drupal_set_message($this->t('The changes have been saved.'));
}
/**
* Provides a submit handler for the 'Cancel account' button.
*/
public function editCancelSubmit($form, FormStateInterface $form_state) {
$destination = array();
$query = $this->getRequest()->query;
if ($query->has('destination')) {
$destination = array('destination' => $query->get('destination'));
$query->remove('destination');
}
// We redirect from user/%/edit to user/%/cancel to make the tabs disappear.
$form_state->setRedirect(
'entity.user.cancel_form',
array('user' => $this->entity->id()),
array('query' => $destination)
);
}
}

View file

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains \Drupal\user\ProfileTranslationHandler.
*/
namespace Drupal\user;
use Drupal\Core\Entity\EntityInterface;
use Drupal\content_translation\ContentTranslationHandler;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines the translation handler for users.
*/
class ProfileTranslationHandler extends ContentTranslationHandler {
/**
* {@inheritdoc}
*/
protected function hasPublishedStatus() {
// User status has nothing to do with translations visibility.
return FALSE;
}
/**
* {@inheritdoc}
*/
protected function hasCreatedTime() {
// User creation date has nothing to do with translation creation date.
return FALSE;
}
/**
* {@inheritdoc}
*/
public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
parent::entityFormAlter($form, $form_state, $entity);
$form['actions']['submit']['#submit'][] = array($this, 'entityFormSave');
}
/**
* Form submission handler for ProfileTranslationHandler::entityFormAlter().
*
* This handles the save action.
*
* @see \Drupal\Core\Entity\EntityForm::build().
*/
public function entityFormSave(array $form, FormStateInterface $form_state) {
if ($this->getSourceLangcode($form_state)) {
$entity = $form_state->getFormObject()->getEntity();
// We need a redirect here, otherwise we would get an access denied page
// since the current URL would be preserved and we would try to add a
// translation for a language that already has a translation.
$form_state->setRedirectUrl($entity->urlInfo());
}
}
}

View file

@ -0,0 +1,153 @@
<?php
/**
* @file
* Contains \Drupal\user\RegisterForm.
*/
namespace Drupal\user;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
/**
* Form controller for the user register forms.
*/
class RegisterForm extends AccountForm {
/**
* {@inheritdoc}
*/
public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, QueryFactory $entity_query) {
parent::__construct($entity_manager, $language_manager, $entity_query);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$user = $this->currentUser();
/** @var \Drupal\user\UserInterface $account */
$account = $this->entity;
$admin = $user->hasPermission('administer users');
// Pass access information to the submit handler. Running an access check
// inside the submit function interferes with form processing and breaks
// hook_form_alter().
$form['administer_users'] = array(
'#type' => 'value',
'#value' => $admin,
);
$form['#attached']['library'][] = 'core/drupal.form';
// For non-admin users, populate the form fields using data from the
// browser.
if (!$admin) {
$form['#attributes']['data-user-info-from-browser'] = TRUE;
}
// Because the user status has security implications, users are blocked by
// default when created programmatically and need to be actively activated
// if needed. When administrators create users from the user interface,
// however, we assume that they should be created as activated by default.
if ($admin) {
$account->activate();
}
// Start with the default user account fields.
$form = parent::form($form, $form_state, $account);
return $form;
}
/**
* Overrides Drupal\Core\Entity\EntityForm::actions().
*/
protected function actions(array $form, FormStateInterface $form_state) {
$element = parent::actions($form, $form_state);
$element['submit']['#value'] = $this->t('Create new account');
return $element;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$admin = $form_state->getValue('administer_users');
if (!\Drupal::config('user.settings')->get('verify_mail') || $admin) {
$pass = $form_state->getValue('pass');
}
else {
$pass = user_password();
}
// Remove unneeded values.
$form_state->cleanValues();
$form_state->setValue('pass', $pass);
$form_state->setValue('init', $form_state->getValue('mail'));
parent::submitForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$account = $this->entity;
$pass = $account->getPassword();
$admin = $form_state->getValue('administer_users');
$notify = !$form_state->isValueEmpty('notify');
// Save has no return value so this cannot be tested.
// Assume save has gone through correctly.
$account->save();
$form_state->set('user', $account);
$form_state->setValue('uid', $account->id());
$this->logger('user')->notice('New user: %name %email.', array('%name' => $form_state->getValue('name'), '%email' => '<' . $form_state->getValue('mail') . '>', 'type' => $account->link($this->t('Edit'), 'edit-form')));
// Add plain text password into user account to generate mail tokens.
$account->password = $pass;
// New administrative account without notification.
if ($admin && !$notify) {
drupal_set_message($this->t('Created a new user account for <a href="@url">%name</a>. No email has been sent.', array('@url' => $account->url(), '%name' => $account->getUsername())));
}
// No email verification required; log in user immediately.
elseif (!$admin && !\Drupal::config('user.settings')->get('verify_mail') && $account->isActive()) {
_user_mail_notify('register_no_approval_required', $account);
user_login_finalize($account);
drupal_set_message($this->t('Registration successful. You are now logged in.'));
$form_state->setRedirect('<front>');
}
// No administrator approval required.
elseif ($account->isActive() || $notify) {
if (!$account->getEmail() && $notify) {
drupal_set_message($this->t('The new user <a href="@url">%name</a> was created without an email address, so no welcome message was sent.', array('@url' => $account->url(), '%name' => $account->getUsername())));
}
else {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
if (_user_mail_notify($op, $account)) {
if ($notify) {
drupal_set_message($this->t('A welcome message with further instructions has been emailed to the new user <a href="@url">%name</a>.', array('@url' => $account->url(), '%name' => $account->getUsername())));
}
else {
drupal_set_message($this->t('A welcome message with further instructions has been sent to your email address.'));
$form_state->setRedirect('<front>');
}
}
}
}
// Administrator approval required.
else {
_user_mail_notify('register_pending_approval', $account);
drupal_set_message($this->t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.<br />In the meantime, a welcome message with further instructions has been sent to your email address.'));
$form_state->setRedirect('<front>');
}
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\user\RoleAccessControlHandler.
*/
namespace Drupal\user;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Defines the access control handler for the user role entity type.
*
* @see \Drupal\user\Entity\Role
*/
class RoleAccessControlHandler extends EntityAccessControlHandler {
/**
* {@inheritdoc}
*/
protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
switch ($operation) {
case 'delete':
if ($entity->id() == RoleInterface::ANONYMOUS_ID || $entity->id() == RoleInterface::AUTHENTICATED_ID) {
return AccessResult::forbidden();
}
default:
return parent::checkAccess($entity, $operation, $langcode, $account);
}
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* @file
* Contains \Drupal\user\RoleForm.
*/
namespace Drupal\user;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
/**
* Form controller for the role entity edit forms.
*/
class RoleForm extends EntityForm {
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$entity = $this->entity;
$form['label'] = array(
'#type' => 'textfield',
'#title' => $this->t('Role name'),
'#default_value' => $entity->label(),
'#size' => 30,
'#required' => TRUE,
'#maxlength' => 64,
'#description' => $this->t('The name for this role. Example: "Moderator", "Editorial board", "Site architect".'),
);
$form['id'] = array(
'#type' => 'machine_name',
'#default_value' => $entity->id(),
'#required' => TRUE,
'#disabled' => !$entity->isNew(),
'#size' => 30,
'#maxlength' => 64,
'#machine_name' => array(
'exists' => ['\Drupal\user\Entity\Role', 'load'],
),
);
$form['weight'] = array(
'#type' => 'value',
'#value' => $entity->getWeight(),
);
return parent::form($form, $form_state, $entity);
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$entity = $this->entity;
// Prevent leading and trailing spaces in role names.
$entity->set('label', trim($entity->label()));
$status = $entity->save();
$edit_link = $this->entity->link($this->t('Edit'));
if ($status == SAVED_UPDATED) {
drupal_set_message($this->t('Role %label has been updated.', array('%label' => $entity->label())));
$this->logger('user')->notice('Role %label has been updated.', array('%label' => $entity->label(), 'link' => $edit_link));
}
else {
drupal_set_message($this->t('Role %label has been added.', array('%label' => $entity->label())));
$this->logger('user')->notice('Role %label has been added.', array('%label' => $entity->label(), 'link' => $edit_link));
}
$form_state->setRedirect('entity.user_role.collection');
}
}

View file

@ -0,0 +1,105 @@
<?php
/**
* @file
* Contains \Drupal\user\RoleInterface.
*/
namespace Drupal\user;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Provides an interface defining a user role entity.
*
* @ingroup user_api
*/
interface RoleInterface extends ConfigEntityInterface {
/**
* Role ID for anonymous users; should match what's in the "role" table.
*/
const ANONYMOUS_ID = AccountInterface::ANONYMOUS_ROLE;
/**
* Role ID for authenticated users; should match what's in the "role" table.
*/
const AUTHENTICATED_ID = AccountInterface::AUTHENTICATED_ROLE;
/**
* Returns a list of permissions assigned to the role.
*
* @return array
* The permissions assigned to the role.
*/
public function getPermissions();
/**
* Checks if the role has a permission.
*
* @param string $permission
* The permission to check for.
*
* @return bool
* TRUE if the role has the permission, FALSE if not.
*/
public function hasPermission($permission);
/**
* Grant permissions to the role.
*
* @param string $permission
* The permission to grant.
*
* @return $this
*/
public function grantPermission($permission);
/**
* Revokes a permissions from the user role.
*
* @param string $permission
* The permission to revoke.
*
* @return $this
*/
public function revokePermission($permission);
/**
* Indicates that a role has all available permissions.
*
* @return bool
* TRUE if the role has all permissions.
*/
public function isAdmin();
/**
* Sets the role to be an admin role.
*
* @param bool $is_admin
* TRUE, if the role should be an admin role.
*
* return $this
*/
public function setIsAdmin($is_admin);
/**
* Returns the weight.
*
* @return int
* The weight of this role.
*/
public function getWeight();
/**
* Sets the weight to the given value.
*
* @param int $weight
* The desired weight.
*
* @return $this
*/
public function setWeight($weight);
}

View file

@ -0,0 +1,69 @@
<?php
/**
* @file
* Contains \Drupal\user\RoleListBuilder.
*/
namespace Drupal\user;
use Drupal\Core\Config\Entity\DraggableListBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines a class to build a listing of user role entities.
*
* @see \Drupal\user\Entity\Role
*/
class RoleListBuilder extends DraggableListBuilder {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'user_admin_roles_form';
}
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['label'] = t('Name');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
$row['label'] = $this->getLabel($entity);
return $row + parent::buildRow($entity);
}
/**
* {@inheritdoc}
*/
public function getDefaultOperations(EntityInterface $entity) {
$operations = parent::getDefaultOperations($entity);
if ($entity->hasLinkTemplate('edit-permissions-form')) {
$operations['permissions'] = array(
'title' => t('Edit permissions'),
'weight' => 20,
'url' => $entity->urlInfo('edit-permissions-form'),
);
}
return $operations;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
drupal_set_message(t('The role settings have been updated.'));
}
}

View file

@ -0,0 +1,33 @@
<?php
/**
* @file
* Contains \Drupal\user\RoleStorage.
*/
namespace Drupal\user;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
/**
* Controller class for user roles.
*/
class RoleStorage extends ConfigEntityStorage implements RoleStorageInterface {
/**
* {@inheritdoc}
*/
public function isPermissionInRoles($permission, array $rids) {
$has_permission = FALSE;
foreach ($this->loadMultiple($rids) as $role) {
/** @var \Drupal\user\RoleInterface $role */
if ($role->isAdmin() || $role->hasPermission($permission)) {
$has_permission = TRUE;
break;
}
}
return $has_permission;
}
}

View file

@ -0,0 +1,30 @@
<?php
/**
* @file
* Contains \Drupal\user\RoleStorageInterface.
*/
namespace Drupal\user;
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
/**
* Defines an interface for role entity storage classes.
*/
interface RoleStorageInterface extends ConfigEntityStorageInterface {
/**
* Returns whether a permission is in one of the passed in roles.
*
* @param string $permission
* The permission.
* @param array $rids
* The list of role IDs to check.
*
* @return bool
* TRUE is the permission is in at least one of the roles. FALSE otherwise.
*/
public function isPermissionInRoles($permission, array $rids);
}

View file

@ -0,0 +1,279 @@
<?php
/**
* @file
* Contains \Drupal\user\SharedTempStore.
*/
namespace Drupal\user;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Stores and retrieves temporary data for a given owner.
*
* A SharedTempStore can be used to make temporary, non-cache data available
* across requests. The data for the SharedTempStore is stored in one key/value
* collection. SharedTempStore data expires automatically after a given
* timeframe.
*
* The SharedTempStore is different from a cache, because the data in it is not
* yet saved permanently and so it cannot be rebuilt. Typically, the
* SharedTempStore might be used to store work in progress that is later saved
* permanently elsewhere, e.g. autosave data, multistep forms, or in-progress
* changes to complex configuration that are not ready to be saved.
*
* Each SharedTempStore belongs to a particular owner (e.g. a user, session, or
* process). Multiple owners may use the same key/value collection, and the
* owner is stored along with the key/value pair.
*
* Every key is unique within the collection, so the SharedTempStore can check
* whether a particular key is already set by a different owner. This is
* useful for informing one owner that the data is already in use by another;
* for example, to let one user know that another user is in the process of
* editing certain data, or even to restrict other users from editing it at
* the same time. It is the responsibility of the implementation to decide
* when and whether one owner can use or update another owner's data.
*
* If you want to be able to ensure that the data belongs to the current user,
* use \Drupal\user\PrivateTempStore.
*/
class SharedTempStore {
/**
* The key/value storage object used for this data.
*
* @var \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
*/
protected $storage;
/**
* The lock object used for this data.
*
* @var \Drupal\Core\Lock\LockBackendInterface
*/
protected $lockBackend;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The owner key to store along with the data (e.g. a user or session ID).
*
* @var mixed
*/
protected $owner;
/**
* The time to live for items in seconds.
*
* By default, data is stored for one week (604800 seconds) before expiring.
*
* @var int
*/
protected $expire;
/**
* Constructs a new object for accessing data from a key/value store.
*
* @param KeyValueStoreExpirableInterface $storage
* The key/value storage object used for this data. Each storage object
* represents a particular collection of data and will contain any number
* of key/value pairs.
* @param \Drupal\Core\Lock\LockBackendInterface $lockBackend
* The lock object used for this data.
* @param mixed $owner
* The owner key to store along with the data (e.g. a user or session ID).
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
* @param int $expire
* The time to live for items, in seconds.
*/
public function __construct(KeyValueStoreExpirableInterface $storage, LockBackendInterface $lockBackend, $owner, RequestStack $request_stack, $expire = 604800) {
$this->storage = $storage;
$this->lockBackend = $lockBackend;
$this->owner = $owner;
$this->requestStack = $request_stack;
$this->expire = $expire;
}
/**
* Retrieves a value from this SharedTempStore for a given key.
*
* @param string $key
* The key of the data to retrieve.
*
* @return mixed
* The data associated with the key, or NULL if the key does not exist.
*/
public function get($key) {
if ($object = $this->storage->get($key)) {
return $object->data;
}
}
/**
* Retrieves a value from this SharedTempStore for a given key.
*
* Only returns the value if the value is owned by $this->owner.
*
* @param string $key
* The key of the data to retrieve.
*
* @return mixed
* The data associated with the key, or NULL if the key does not exist.
*/
public function getIfOwner($key) {
if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) {
return $object->data;
}
}
/**
* Stores a particular key/value pair only if the key doesn't already exist.
*
* @param string $key
* The key of the data to check and store.
* @param mixed $value
* The data to store.
*
* @return bool
* TRUE if the data was set, or FALSE if it already existed.
*/
public function setIfNotExists($key, $value) {
$value = (object) array(
'owner' => $this->owner,
'data' => $value,
'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
);
return $this->storage->setWithExpireIfNotExists($key, $value, $this->expire);
}
/**
* Stores a particular key/value pair in this SharedTempStore.
*
* Only stores the given key/value pair if it does not exist yet or is owned
* by $this->owner.
*
* @param string $key
* The key of the data to store.
* @param mixed $value
* The data to store.
*
* @return bool
* TRUE if the data was set, or FALSE if it already exists and is not owned
* by $this->user.
*/
public function setIfOwner($key, $value) {
if ($this->setIfNotExists($key, $value)) {
return TRUE;
}
if (($object = $this->storage->get($key)) && ($object->owner == $this->owner)) {
$this->set($key, $value);
return TRUE;
}
return FALSE;
}
/**
* Stores a particular key/value pair in this SharedTempStore.
*
* @param string $key
* The key of the data to store.
* @param mixed $value
* The data to store.
*/
public function set($key, $value) {
if (!$this->lockBackend->acquire($key)) {
$this->lockBackend->wait($key);
if (!$this->lockBackend->acquire($key)) {
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to update item %key in %collection temporary storage.", array(
'%key' => $key,
'%collection' => $this->storage->getCollectionName(),
)));
}
}
$value = (object) array(
'owner' => $this->owner,
'data' => $value,
'updated' => (int) $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME'),
);
$this->storage->setWithExpire($key, $value, $this->expire);
$this->lockBackend->release($key);
}
/**
* Returns the metadata associated with a particular key/value pair.
*
* @param string $key
* The key of the data to store.
*
* @return mixed
* An object with the owner and updated time if the key has a value, or
* NULL otherwise.
*/
public function getMetadata($key) {
// Fetch the key/value pair and its metadata.
$object = $this->storage->get($key);
if ($object) {
// Don't keep the data itself in memory.
unset($object->data);
return $object;
}
}
/**
* Deletes data from the store for a given key and releases the lock on it.
*
* @param string $key
* The key of the data to delete.
*/
public function delete($key) {
if (!$this->lockBackend->acquire($key)) {
$this->lockBackend->wait($key);
if (!$this->lockBackend->acquire($key)) {
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to delete item %key from %collection temporary storage.", array(
'%key' => $key,
'%collection' => $this->storage->getCollectionName(),
)));
}
}
$this->storage->delete($key);
$this->lockBackend->release($key);
}
/**
* Deletes data from the store for a given key and releases the lock on it.
*
* Only delete the given key if it is owned by $this->owner.
*
* @param string $key
* The key of the data to delete.
*
* @return bool
* TRUE if the object was deleted or does not exist, FALSE if it exists but
* is not owned by $this->owner.
*/
public function deleteIfOwner($key) {
if (!$object = $this->storage->get($key)) {
return TRUE;
}
elseif ($object->owner == $this->owner) {
$this->delete($key);
return TRUE;
}
return FALSE;
}
}

View file

@ -0,0 +1,93 @@
<?php
/**
* @file
* Contains \Drupal\user\SharedTempStoreFactory.
*/
namespace Drupal\user;
use Drupal\Component\Serialization\SerializationInterface;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Creates a shared temporary storage for a collection.
*/
class SharedTempStoreFactory {
/**
* The storage factory creating the backend to store the data.
*
* @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface
*/
protected $storageFactory;
/**
* The lock object used for this data.
*
* @var \Drupal\Core\Lock\LockBackendInterface $lockBackend
*/
protected $lockBackend;
/**
* The request stack.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The time to live for items in seconds.
*
* @var int
*/
protected $expire;
/**
* Constructs a Drupal\user\SharedTempStoreFactory object.
*
* @param \Drupal\Core\Database\Connection $connection
* The connection object used for this data.
* @param \Drupal\Core\Lock\LockBackendInterface $lockBackend
* The lock object used for this data.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
* @param int $expire
* The time to live for items, in seconds.
*/
function __construct(KeyValueExpirableFactoryInterface $storage_factory, LockBackendInterface $lockBackend, RequestStack $request_stack, $expire = 604800) {
$this->storageFactory = $storage_factory;
$this->lockBackend = $lockBackend;
$this->requestStack = $request_stack;
$this->expire = $expire;
}
/**
* Creates a SharedTempStore for the current user or anonymous session.
*
* @param string $collection
* The collection name to use for this key/value store. This is typically
* a shared namespace or module name, e.g. 'views', 'entity', etc.
* @param mixed $owner
* (optional) The owner of this SharedTempStore. By default, the
* SharedTempStore is owned by the currently authenticated user, or by the
* active anonymous session if no user is logged in.
*
* @return \Drupal\user\SharedTempStore
* An instance of the key/value store.
*/
function get($collection, $owner = NULL) {
// Use the currently authenticated user ID or the active user ID unless
// the owner is overridden.
if (!isset($owner)) {
$owner = \Drupal::currentUser()->id() ?: session_id();
}
// Store the data for this collection in the database.
$storage = $this->storageFactory->get("user.shared_tempstore.$collection");
return new SharedTempStore($storage, $this->lockBackend, $owner, $this->requestStack, $this->expire);
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* @file
* Contains \Drupal\user\TempStoreException.
*/
namespace Drupal\user;
/**
* Thrown by SharedTempStore and PrivateTempStore if they cannot acquire a lock.
*
* @see \Drupal\user\SharedTempStore
* @see \Drupal\user\PrivateTempStore
*/
class TempStoreException extends \Exception {}

View file

@ -0,0 +1,161 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\Condition\UserRoleConditionTest.
*/
namespace Drupal\user\Tests\Condition;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
/**
* Tests the user role condition.
*
* @group user
*/
class UserRoleConditionTest extends KernelTestBase {
/**
* The condition plugin manager.
*
* @var \Drupal\Core\Condition\ConditionManager
*/
protected $manager;
/**
* An anonymous user for testing purposes.
*
* @var \Drupal\user\UserInterface
*/
protected $anonymous;
/**
* An authenticated user for testing purposes.
*
* @var \Drupal\user\UserInterface
*/
protected $authenticated;
/**
* A custom role for testing purposes.
*
* @var \Drupal\user\Entity\RoleInterface
*/
protected $role;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user', 'field');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'sequences');
$this->installEntitySchema('user');
$this->manager = $this->container->get('plugin.manager.condition');
// Set up the authenticated and anonymous roles.
Role::create(array(
'id' => RoleInterface::ANONYMOUS_ID,
'label' => 'Anonymous user',
))->save();
Role::create(array(
'id' => RoleInterface::AUTHENTICATED_ID,
'label' => 'Authenticated user',
))->save();
// Create new role.
$rid = strtolower($this->randomMachineName(8));
$label = $this->randomString(8);
$role = Role::create(array(
'id' => $rid,
'label' => $label,
));
$role->save();
$this->role = $role;
// Setup an anonymous user for our tests.
$this->anonymous = User::create(array(
'uid' => 0,
));
$this->anonymous->save();
// Loading the anonymous user adds the correct role.
$this->anonymous = User::load($this->anonymous->id());
// Setup an authenticated user for our tests.
$this->authenticated = User::create(array(
'name' => $this->randomMachineName(),
));
$this->authenticated->save();
// Add the custom role.
$this->authenticated->addRole($this->role->id());
}
/**
* Test the user_role condition.
*/
public function testConditions() {
// Grab the user role condition and configure it to check against
// authenticated user roles.
/** @var $condition \Drupal\Core\Condition\ConditionInterface */
$condition = $this->manager->createInstance('user_role')
->setConfig('roles', array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID))
->setContextValue('user', $this->anonymous);
$this->assertFalse($condition->execute(), 'Anonymous users fail role checks for authenticated.');
// Check for the proper summary.
// Summaries require an extra space due to negate handling in summary().
$this->assertEqual($condition->summary(), 'The user is a member of Authenticated user');
// Set the user role to anonymous.
$condition->setConfig('roles', array(RoleInterface::ANONYMOUS_ID => RoleInterface::ANONYMOUS_ID));
$this->assertTrue($condition->execute(), 'Anonymous users pass role checks for anonymous.');
// Check for the proper summary.
$this->assertEqual($condition->summary(), 'The user is a member of Anonymous user');
// Set the user role to check anonymous or authenticated.
$condition->setConfig('roles', array(RoleInterface::ANONYMOUS_ID => RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID));
$this->assertTrue($condition->execute(), 'Anonymous users pass role checks for anonymous or authenticated.');
// Check for the proper summary.
$this->assertEqual($condition->summary(), 'The user is a member of Anonymous user, Authenticated user');
// Set the context to the authenticated user and check that they also pass
// against anonymous or authenticated roles.
$condition->setContextValue('user', $this->authenticated);
$this->assertTrue($condition->execute(), 'Authenticated users pass role checks for anonymous or authenticated.');
// Set the role to just authenticated and recheck.
$condition->setConfig('roles', array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID));
$this->assertTrue($condition->execute(), 'Authenticated users pass role checks for authenticated.');
// Test Constructor injection.
$condition = $this->manager->createInstance('user_role', array('roles' => array(RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID), 'context' => array('user' => $this->authenticated)));
$this->assertTrue($condition->execute(), 'Constructor injection of context and configuration working as anticipated.');
// Check the negated summary.
$condition->setConfig('negate', TRUE);
$this->assertEqual($condition->summary(), 'The user is not a member of Authenticated user');
// Check the complex negated summary.
$condition->setConfig('roles', array(RoleInterface::ANONYMOUS_ID => RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID));
$this->assertEqual($condition->summary(), 'The user is not a member of Anonymous user, Authenticated user');
// Check a custom role.
$condition->setConfig('roles', array($this->role->id() => $this->role->id()));
$condition->setConfig('negate', FALSE);
$this->assertTrue($condition->execute(), 'Authenticated user is a member of the custom role.');
$this->assertEqual($condition->summary(), SafeMarkup::format('The user is a member of @roles', array('@roles' => $this->role->label())));
}
}

View file

@ -0,0 +1,93 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\Field\UserNameFormatterTest.
*/
namespace Drupal\user\Tests\Field;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\User;
/**
* Tests the user_name formatter.
*
* @group field
*/
class UserNameFormatterTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['field', 'user', 'system'];
/**
* @var string
*/
protected $entityType;
/**
* @var string
*/
protected $bundle;
/**
* @var string
*/
protected $fieldName;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['field']);
$this->installEntitySchema('user');
$this->installSchema('system', ['sequences']);
$this->entityType = 'user';
$this->bundle = $this->entityType;
$this->fieldName = 'name';
}
/**
* Renders fields of a given entity with a given display.
*
* @param \Drupal\Core\Entity\FieldableEntityInterface $entity
* The entity object with attached fields to render.
* @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
* The display to render the fields in.
*
* @return string
* The rendered entity fields.
*/
protected function renderEntityFields(FieldableEntityInterface $entity, EntityViewDisplayInterface $display) {
$content = $display->build($entity);
$content = $this->render($content);
return $content;
}
/**
* Tests the formatter output.
*/
public function testFormatter() {
$user = User::create([
'name' => 'test name',
]);
$user->save();
$result = $user->{$this->fieldName}->view(['type' => 'user_name']);
$this->assertEqual('username', $result[0]['#theme']);
$this->assertEqual(spl_object_hash($user), spl_object_hash($result[0]['#account']));
$result = $user->{$this->fieldName}->view(['type' => 'user_name', 'settings' => ['link_to_entity' => FALSE]]);
$this->assertEqual($user->getUsername(), $result[0]['#markup']);
}
}

View file

@ -0,0 +1,152 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\TempStoreDatabaseTest.
*/
namespace Drupal\user\Tests;
use Drupal\Component\Serialization\PhpSerialize;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactory;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\SharedTempStoreFactory;
use Drupal\Core\Lock\DatabaseLockBackend;
use Drupal\Core\Database\Database;
/**
* Tests the temporary object storage system.
*
* @group user
* @see \Drupal\Core\TempStore\TempStore.
*/
class TempStoreDatabaseTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user');
/**
* A key/value store factory.
*
* @var \Drupal\user\SharedTempStoreFactory
*/
protected $storeFactory;
/**
* The name of the key/value collection to set and retrieve.
*
* @var string
*/
protected $collection;
/**
* An array of (fake) user IDs.
*
* @var array
*/
protected $users = array();
/**
* An array of random stdClass objects.
*
* @var array
*/
protected $objects = array();
protected function setUp() {
parent::setUp();
// Install system tables to test the key/value storage without installing a
// full Drupal environment.
$this->installSchema('system', array('semaphore', 'key_value_expire'));
// Create several objects for testing.
for ($i = 0; $i <= 3; $i++) {
$this->objects[$i] = $this->randomObject();
}
}
/**
* Tests the UserTempStore API.
*/
public function testUserTempStore() {
// Create a key/value collection.
$factory = new SharedTempStoreFactory(new KeyValueExpirableFactory(\Drupal::getContainer()), new DatabaseLockBackend(Database::getConnection()), $this->container->get('request_stack'));
$collection = $this->randomMachineName();
// Create two mock users.
for ($i = 0; $i <= 1; $i++) {
$users[$i] = mt_rand(500, 5000000);
// Storing the SharedTempStore objects in a class member variable causes a
// fatal exception, because in that situation garbage collection is not
// triggered until the test class itself is destructed, after tearDown()
// has deleted the database tables. Store the objects locally instead.
$stores[$i] = $factory->get($collection, $users[$i]);
}
$key = $this->randomMachineName();
// Test that setIfNotExists() succeeds only the first time.
for ($i = 0; $i <= 1; $i++) {
// setIfNotExists() should be TRUE the first time (when $i is 0) and
// FALSE the second time (when $i is 1).
$this->assertEqual(!$i, $stores[0]->setIfNotExists($key, $this->objects[$i]));
$metadata = $stores[0]->getMetadata($key);
$this->assertEqual($users[0], $metadata->owner);
$this->assertIdenticalObject($this->objects[0], $stores[0]->get($key));
// Another user should get the same result.
$metadata = $stores[1]->getMetadata($key);
$this->assertEqual($users[0], $metadata->owner);
$this->assertIdenticalObject($this->objects[0], $stores[1]->get($key));
}
// Remove the item and try to set it again.
$stores[0]->delete($key);
$stores[0]->setIfNotExists($key, $this->objects[1]);
// This time it should succeed.
$this->assertIdenticalObject($this->objects[1], $stores[0]->get($key));
// This user can update the object.
$stores[0]->set($key, $this->objects[2]);
$this->assertIdenticalObject($this->objects[2], $stores[0]->get($key));
// The object is the same when another user loads it.
$this->assertIdenticalObject($this->objects[2], $stores[1]->get($key));
// This user should be allowed to get, update, delete.
$this->assertTrue($stores[0]->getIfOwner($key) instanceof \stdClass);
$this->assertTrue($stores[0]->setIfOwner($key, $this->objects[1]));
$this->assertTrue($stores[0]->deleteIfOwner($key));
// Another user can update the object and become the owner.
$stores[1]->set($key, $this->objects[3]);
$this->assertIdenticalObject($this->objects[3], $stores[0]->get($key));
$this->assertIdenticalObject($this->objects[3], $stores[1]->get($key));
$metadata = $stores[1]->getMetadata($key);
$this->assertEqual($users[1], $metadata->owner);
// The first user should be informed that the second now owns the data.
$metadata = $stores[0]->getMetadata($key);
$this->assertEqual($users[1], $metadata->owner);
// The first user should no longer be allowed to get, update, delete.
$this->assertNull($stores[0]->getIfOwner($key));
$this->assertFalse($stores[0]->setIfOwner($key, $this->objects[1]));
$this->assertFalse($stores[0]->deleteIfOwner($key));
// Now manually expire the item (this is not exposed by the API) and then
// assert it is no longer accessible.
db_update('key_value_expire')
->fields(array('expire' => REQUEST_TIME - 1))
->condition('collection', "user.shared_tempstore.$collection")
->condition('name', $key)
->execute();
$this->assertFalse($stores[0]->get($key));
$this->assertFalse($stores[1]->get($key));
}
}

View file

@ -0,0 +1,152 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserAccountFormFieldsTest.
*/
namespace Drupal\user\Tests;
use Drupal\Core\Form\FormState;
use Drupal\simpletest\KernelTestBase;
/**
* Verifies that the field order in user account forms is compatible with
* password managers of web browsers.
*
* @group user
*/
class UserAccountFormFieldsTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user', 'field');
/**
* Tests the root user account form section in the "Configure site" form.
*/
function testInstallConfigureForm() {
require_once \Drupal::root() . '/core/includes/install.core.inc';
require_once \Drupal::root() . '/core/includes/install.inc';
$install_state = install_state_defaults();
$form_state = new FormState();
$form_state->addBuildInfo('args', [&$install_state]);
$form = $this->container->get('form_builder')
->buildForm('Drupal\Core\Installer\Form\SiteConfigureForm', $form_state);
// Verify name and pass field order.
$this->assertFieldOrder($form['admin_account']['account']);
// Verify that web browsers may autocomplete the email value and
// autofill/prefill the name and pass values.
foreach (array('mail', 'name', 'pass') as $key) {
$this->assertFalse(isset($form['account'][$key]['#attributes']['autocomplete']), "'$key' field: 'autocomplete' attribute not found.");
}
}
/**
* Tests the user registration form.
*/
function testUserRegistrationForm() {
// Install default configuration; required for AccountFormController.
$this->installConfig(array('user'));
// Disable email confirmation to unlock the password field.
$this->config('user.settings')
->set('verify_mail', FALSE)
->save();
$form = $this->buildAccountForm('register');
// Verify name and pass field order.
$this->assertFieldOrder($form['account']);
// Verify that web browsers may autocomplete the email value and
// autofill/prefill the name and pass values.
foreach (array('mail', 'name', 'pass') as $key) {
$this->assertFalse(isset($form['account'][$key]['#attributes']['autocomplete']), "'$key' field: 'autocomplete' attribute not found.");
}
}
/**
* Tests the user edit form.
*/
function testUserEditForm() {
// Install default configuration; required for AccountFormController.
$this->installConfig(array('user'));
// Install the router table and then rebuild.
$this->installSchema('system', ['router']);
\Drupal::service('router.builder')->rebuild();
$form = $this->buildAccountForm('default');
// Verify name and pass field order.
$this->assertFieldOrder($form['account']);
// Verify that autocomplete is off on all account fields.
foreach (array('mail', 'name', 'pass') as $key) {
$this->assertIdentical($form['account'][$key]['#attributes']['autocomplete'], 'off', "'$key' field: 'autocomplete' attribute is 'off'.");
}
}
/**
* Asserts that the 'name' form element is directly before the 'pass' element.
*
* @param array $elements
* A form array section that contains the user account form elements.
*/
protected function assertFieldOrder(array $elements) {
$name_index = 0;
$name_weight = 0;
$pass_index = 0;
$pass_weight = 0;
$index = 0;
foreach ($elements as $key => $element) {
if ($key === 'name') {
$name_index = $index;
$name_weight = $element['#weight'];
$this->assertTrue($element['#sorted'], "'name' field is #sorted.");
}
elseif ($key === 'pass') {
$pass_index = $index;
$pass_weight = $element['#weight'];
$this->assertTrue($element['#sorted'], "'pass' field is #sorted.");
}
$index++;
}
$this->assertEqual($name_index, $pass_index - 1, "'name' field ($name_index) appears before 'pass' field ($pass_index).");
$this->assertTrue($name_weight < $pass_weight, "'name' field weight ($name_weight) is smaller than 'pass' field weight ($pass_weight).");
}
/**
* Builds the user account form for a given operation.
*
* @param string $operation
* The entity operation; one of 'register' or 'default'.
*
* @return array
* The form array.
*/
protected function buildAccountForm($operation) {
// @see HtmlEntityFormController::getFormObject()
$entity_type = 'user';
$fields = array();
if ($operation != 'register') {
$fields['uid'] = 2;
}
$entity = $this->container->get('entity.manager')
->getStorage($entity_type)
->create($fields);
$form_object = $this->container->get('entity.manager')
->getFormObject($entity_type, $operation)
->setEntity($entity);
// @see EntityFormBuilder::getForm()
return $this->container->get('entity.form_builder')->getForm($entity, $operation);
}
}

View file

@ -0,0 +1,140 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserAccountLinksTest.
*/
namespace Drupal\user\Tests;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\simpletest\WebTestBase;
/**
* Tests user-account links.
*
* @group user
*/
class UserAccountLinksTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('menu_ui', 'block', 'test_page_test');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:account');
// Make test-page default.
$this->config('system.site')->set('page.front', '/test-page')->save();
}
/**
* Tests the secondary menu.
*/
function testSecondaryMenu() {
// Create a regular user.
$user = $this->drupalCreateUser(array());
// Log in and get the homepage.
$this->drupalLogin($user);
$this->drupalGet('<front>');
// For a logged-in user, expect the secondary menu to have links for "My
// account" and "Log out".
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', array(
':menu_class' => 'menu',
':href' => 'user',
':text' => 'My account',
));
$this->assertEqual(count($link), 1, 'My account link is in secondary menu.');
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', array(
':menu_class' => 'menu',
':href' => 'user/logout',
':text' => 'Log out',
));
$this->assertEqual(count($link), 1, 'Log out link is in secondary menu.');
// Log out and get the homepage.
$this->drupalLogout();
$this->drupalGet('<front>');
// For a logged-out user, expect no secondary links.
$menu = $this->xpath('//ul[@class=:menu_class]', array(
':menu_class' => 'menu',
));
$this->assertEqual(count($menu), 0, 'The secondary links menu is not rendered, because none of its menu links are accessible for the anonymous user.');
}
/**
* Tests disabling the 'My account' link.
*/
function testDisabledAccountLink() {
// Create an admin user and log in.
$this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer menu')));
// Verify that the 'My account' link exists before we check for its
// disappearance.
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', array(
':menu_class' => 'menu',
':href' => 'user',
':text' => 'My account',
));
$this->assertEqual(count($link), 1, 'My account link is in the secondary menu.');
// Verify that the 'My account' link is enabled. Do not assume the value of
// auto-increment is 1. Use XPath to obtain input element id and name using
// the consistent label text.
$this->drupalGet('admin/structure/menu/manage/account');
$label = $this->xpath('//label[contains(.,:text)]/@for', array(':text' => 'Enable My account menu link'));
$this->assertFieldChecked((string) $label[0], "The 'My account' link is enabled by default.");
// Disable the 'My account' link.
$edit['links[menu_plugin_id:user.page][enabled]'] = FALSE;
$this->drupalPostForm('admin/structure/menu/manage/account', $edit, t('Save'));
// Get the homepage.
$this->drupalGet('<front>');
// Verify that the 'My account' link does not appear when disabled.
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', array(
':menu_class' => 'menu',
':href' => 'user',
':text' => 'My account',
));
$this->assertEqual(count($link), 0, 'My account link is not in the secondary menu.');
}
/**
* Tests page title is set correctly on user account tabs.
*/
function testAccountPageTitles() {
// Default page titles are suffixed with the site name - Drupal.
$title_suffix = ' | Drupal';
$this->drupalGet('user');
$this->assertTitle('Log in' . $title_suffix, "Page title of /user is 'Log in'");
$this->drupalGet('user/login');
$this->assertTitle('Log in' . $title_suffix, "Page title of /user/login is 'Log in'");
$this->drupalGet('user/register');
$this->assertTitle('Create new account' . $title_suffix, "Page title of /user/register is 'Create new account' for anonymous users.");
$this->drupalGet('user/password');
$this->assertTitle('Reset your password' . $title_suffix, "Page title of /user/register is 'Reset your password' for anonymous users.");
// Check the page title for registered users is "My Account" in menus.
$this->drupalLogin($this->drupalCreateUser());
// After login, the client is redirected to /user.
$this->assertLink(t('My account'), 0, "Page title of /user is 'My Account' in menus for registered users");
$this->assertLinkByHref(\Drupal::urlGenerator()->generate('user.page'), 0);
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserActionConfigSchemaTest.
*/
namespace Drupal\user\Tests;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\Role;
/**
* Ensures the user action for adding and removing roles have valid config
* schema.
*
* @group user
*/
class UserActionConfigSchemaTest extends KernelTestBase {
use SchemaCheckTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user');
/**
* Tests whether the user action config schema are valid.
*/
function testValidUserActionConfigSchema() {
$rid = strtolower($this->randomMachineName(8));
Role::create(array('id' => $rid))->save();
// Test user_add_role_action configuration.
$config = $this->config('system.action.user_add_role_action.' . $rid);
$this->assertEqual($config->get('id'), 'user_add_role_action.' . $rid);
$this->assertConfigSchema(\Drupal::service('config.typed'), $config->getName(), $config->get());
// Test user_remove_role_action configuration.
$config = $this->config('system.action.user_remove_role_action.' . $rid);
$this->assertEqual($config->get('id'), 'user_remove_role_action.' . $rid);
$this->assertConfigSchema(\Drupal::service('config.typed'), $config->getName(), $config->get());
}
}

View file

@ -0,0 +1,189 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserAdminLanguageTest.
*/
namespace Drupal\user\Tests;
use Drupal\Core\Language\LanguageInterface;
use Drupal\simpletest\WebTestBase;
/**
* Tests users' ability to change their own administration language.
*
* @group user
*/
class UserAdminLanguageTest extends WebTestBase {
/**
* A user with permission to access admin pages and administer languages.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* A non-administrator user for this test.
*
* @var \Drupal\user\UserInterface
*/
protected $regularUser;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('user', 'language', 'language_test');
protected function setUp() {
parent::setUp();
// User to add and remove language.
$this->adminUser = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
// User to check non-admin access.
$this->regularUser = $this->drupalCreateUser();
}
/**
* Tests that admin language is not configurable in single language sites.
*/
function testUserAdminLanguageConfigurationNotAvailableWithOnlyOneLanguage() {
$this->drupalLogin($this->adminUser);
$this->setLanguageNegotiation();
$path = 'user/' . $this->adminUser->id() . '/edit';
$this->drupalGet($path);
// Ensure administration pages language settings widget is not available.
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
}
/**
* Tests that admin language negotiation is configurable only if enabled.
*/
function testUserAdminLanguageConfigurationAvailableWithAdminLanguageNegotiation() {
$this->drupalLogin($this->adminUser);
$this->addCustomLanguage();
$path = 'user/' . $this->adminUser->id() . '/edit';
// Checks with user administration pages language negotiation disabled.
$this->drupalGet($path);
// Ensure administration pages language settings widget is not available.
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
// Checks with user administration pages language negotiation enabled.
$this->setLanguageNegotiation();
$this->drupalGet($path);
// Ensure administration pages language settings widget is available.
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector is available.');
}
/**
* Tests that the admin language is configurable only for administrators.
*
* If a user has the permission "access administration pages", they should
* be able to see the setting to pick the language they want those pages in.
*
* If a user does not have that permission, it would confusing for them to
* have a setting for pages they cannot access, so they should not be able to
* set a language for those pages.
*/
function testUserAdminLanguageConfigurationAvailableIfAdminLanguageNegotiationIsEnabled() {
$this->drupalLogin($this->adminUser);
// Adds a new language, because with only one language, setting won't show.
$this->addCustomLanguage();
$this->setLanguageNegotiation();
$path = 'user/' . $this->adminUser->id() . '/edit';
$this->drupalGet($path);
// Ensure administration pages language setting is visible for admin.
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector available for admins.');
// Ensure administration pages language setting is hidden for non-admins.
$this->drupalLogin($this->regularUser);
$path = 'user/' . $this->regularUser->id() . '/edit';
$this->drupalGet($path);
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available for regular user.');
}
/**
* Tests the actual language negotiation.
*/
function testActualNegotiation() {
$this->drupalLogin($this->adminUser);
$this->addCustomLanguage();
$this->setLanguageNegotiation();
// Even though we have admin language negotiation, so long as the user has
// no preference set, negotiation will fall back further.
$path = 'user/' . $this->adminUser->id() . '/edit';
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-default');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-url');
// Set a preferred language code for the user.
$edit = array();
$edit['preferred_admin_langcode'] = 'xx';
$this->drupalPostForm($path, $edit, t('Save'));
// Test negotiation with the URL method first. The admin method will only
// be used if the URL method did not match.
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-user-admin');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-url');
// Test negotiation with the admin language method first. The admin method
// will be used at all times.
$this->setLanguageNegotiation(TRUE);
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-user-admin');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-user-admin');
// Unset the preferred language code for the user.
$edit = array();
$edit['preferred_admin_langcode'] = '';
$this->drupalPostForm($path, $edit, t('Save'));
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-default');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-url');
}
/**
* Sets the User interface negotiation detection method.
*
* @param bool $admin_first
* Whether the admin negotiation should be first.
*
* Enables the "Account preference for administration pages" language
* detection method for the User interface language negotiation type.
*/
function setLanguageNegotiation($admin_first = FALSE) {
$edit = array(
'language_interface[enabled][language-user-admin]' => TRUE,
'language_interface[enabled][language-url]' => TRUE,
'language_interface[weight][language-user-admin]' => ($admin_first ? -12 : -8),
'language_interface[weight][language-url]' => -10,
);
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
}
/**
* Helper method for adding a custom language.
*/
function addCustomLanguage() {
$langcode = 'xx';
// The English name for the language.
$name = $this->randomMachineName(16);
$edit = array(
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'label' => $name,
'direction' => LanguageInterface::DIRECTION_LTR,
);
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
}
}

View file

@ -0,0 +1,97 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserAdminListingTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\user\Entity\User;
/**
* Tests the user admin listing if views is not enabled.
*
* @group user
* @see user_admin_account()
*/
class UserAdminListingTest extends WebTestBase {
/**
* Tests the listing.
*/
public function testUserListing() {
$this->drupalGet('admin/people');
$this->assertResponse(403, 'Anonymous user does not have access to the user admin listing.');
// Create a bunch of users.
$accounts = array();
for ($i = 0; $i < 3; $i++) {
$account = $this->drupalCreateUser();
$accounts[$account->label()] = $account;
}
// Create a blocked user.
$account = $this->drupalCreateUser();
$account->block();
$account->save();
$accounts[$account->label()] = $account;
// Create a user at a certain timestamp.
$account = $this->drupalCreateUser();
$account->created = 1363219200;
$account->save();
$accounts[$account->label()] = $account;
$timestamp_user = $account->label();
$rid_1 = $this->drupalCreateRole(array(), 'custom_role_1', 'custom_role_1');
$rid_2 = $this->drupalCreateRole(array(), 'custom_role_2', 'custom_role_2');
$account = $this->drupalCreateUser();
$account->addRole($rid_1);
$account->addRole($rid_2);
$account->save();
$accounts[$account->label()] = $account;
$role_account_name = $account->label();
// Create an admin user and look at the listing.
$admin_user = $this->drupalCreateUser(array('administer users'));
$accounts[$admin_user->label()] = $admin_user;
$accounts['admin'] = User::load(1);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/people');
$this->assertResponse(200, 'The admin user has access to the user admin listing.');
$result = $this->xpath('//table[contains(@class, "responsive-enabled")]/tbody/tr');
$result_accounts = array();
foreach ($result as $account) {
$name = (string) $account->td[0]->span;
$roles = array();
if (isset($account->td[2]->div->ul)) {
foreach ($account->td[2]->div->ul->li as $element) {
$roles[] = (string) $element;
}
}
$result_accounts[$name] = array(
'name' => $name,
'status' => (string) $account->td[1],
'roles' => $roles,
'member_for' => (string) $account->td[3],
);
}
$this->assertFalse(array_keys(array_diff_key($result_accounts, $accounts)), 'Ensure all accounts are listed.');
foreach ($result_accounts as $name => $values) {
$this->assertEqual($values['status'] == t('active'), $accounts[$name]->status->value, 'Ensure the status is displayed properly.');
}
$expected_roles = array('custom_role_1', 'custom_role_2');
$this->assertEqual($result_accounts[$role_account_name]['roles'], $expected_roles, 'Ensure roles are listed properly.');
$this->assertEqual($result_accounts[$timestamp_user]['member_for'], \Drupal::service('date.formatter')->formatTimeDiffSince($accounts[$timestamp_user]->created->value), 'Ensure the right member time is displayed.');
}
}

View file

@ -0,0 +1,42 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserAdminSettingsFormTest.
*/
namespace Drupal\user\Tests;
use Drupal\system\Tests\System\SystemConfigFormTestBase;
use Drupal\user\AccountSettingsForm;
/**
* Configuration object user.mail and user.settings save test.
*
* @group user
*/
class UserAdminSettingsFormTest extends SystemConfigFormTestBase {
protected function setUp() {
parent::setUp();
$this->form = AccountSettingsForm::create($this->container);
$this->values = array(
'anonymous' => array(
'#value' => $this->randomString(10),
'#config_name' => 'user.settings',
'#config_key' => 'anonymous',
),
'user_mail_cancel_confirm_body' => array(
'#value' => $this->randomString(),
'#config_name' => 'user.mail',
'#config_key' => 'cancel_confirm.body',
),
'user_mail_cancel_confirm_subject' => array(
'#value' => $this->randomString(20),
'#config_name' => 'user.mail',
'#config_key' => 'cancel_confirm.subject',
),
);
}
}

View file

@ -0,0 +1,198 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserAdminTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\user\RoleInterface;
/**
* Tests user administration page functionality.
*
* @group user
*/
class UserAdminTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('taxonomy', 'views');
/**
* Registers a user and deletes it.
*/
function testUserAdmin() {
$user_a = $this->drupalCreateUser();
$user_a->name = 'User A';
$user_a->mail = $this->randomMachineName() . '@example.com';
$user_a->save();
$user_b = $this->drupalCreateUser(array('administer taxonomy'));
$user_b->name = 'User B';
$user_b->save();
$user_c = $this->drupalCreateUser(array('administer taxonomy'));
$user_c->name = 'User C';
$user_c->save();
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create admin user to delete registered user.
$admin_user = $this->drupalCreateUser(array('administer users'));
// Use a predictable name so that we can reliably order the user admin page
// by name.
$admin_user->name = 'Admin user';
$admin_user->save();
$this->drupalLogin($admin_user);
$this->drupalGet('admin/people');
$this->assertText($user_a->getUsername(), 'Found user A on admin users page');
$this->assertText($user_b->getUsername(), 'Found user B on admin users page');
$this->assertText($user_c->getUsername(), 'Found user C on admin users page');
$this->assertText($admin_user->getUsername(), 'Found Admin user on admin users page');
// Test for existence of edit link in table.
$link = $user_a->link(t('Edit'), 'edit-form', array('query' => array('destination' => $user_a->url('collection'))));
$this->assertRaw($link, 'Found user A edit link on admin users page');
// Test exposed filter elements.
foreach (array('user', 'role', 'permission', 'status') as $field) {
$this->assertField("edit-$field", "$field exposed filter found.");
}
// Make sure the reduce duplicates element from the ManyToOneHelper is not
// displayed.
$this->assertNoField('edit-reduce-duplicates', 'Reduce duplicates form element not found in exposed filters.');
// Filter the users by name/email.
$this->drupalGet('admin/people', array('query' => array('user' => $user_a->getUsername())));
$result = $this->xpath('//table/tbody/tr');
$this->assertEqual(1, count($result), 'Filter by username returned the right amount.');
$this->assertEqual($user_a->getUsername(), (string) $result[0]->td[1]->span, 'Filter by username returned the right user.');
$this->drupalGet('admin/people', array('query' => array('user' => $user_a->getEmail())));
$result = $this->xpath('//table/tbody/tr');
$this->assertEqual(1, count($result), 'Filter by username returned the right amount.');
$this->assertEqual($user_a->getUsername(), (string) $result[0]->td[1]->span, 'Filter by username returned the right user.');
// Filter the users by permission 'administer taxonomy'.
$this->drupalGet('admin/people', array('query' => array('permission' => 'administer taxonomy')));
// Check if the correct users show up.
$this->assertNoText($user_a->getUsername(), 'User A not on filtered by perm admin users page');
$this->assertText($user_b->getUsername(), 'Found user B on filtered by perm admin users page');
$this->assertText($user_c->getUsername(), 'Found user C on filtered by perm admin users page');
// Filter the users by role. Grab the system-generated role name for User C.
$roles = $user_c->getRoles();
unset($roles[array_search(RoleInterface::AUTHENTICATED_ID, $roles)]);
$this->drupalGet('admin/people', array('query' => array('role' => reset($roles))));
// Check if the correct users show up when filtered by role.
$this->assertNoText($user_a->getUsername(), 'User A not on filtered by role on admin users page');
$this->assertNoText($user_b->getUsername(), 'User B not on filtered by role on admin users page');
$this->assertText($user_c->getUsername(), 'User C on filtered by role on admin users page');
// Test blocking of a user.
$account = $user_storage->load($user_c->id());
$this->assertTrue($account->isActive(), 'User C not blocked');
$edit = array();
$edit['action'] = 'user_block_user_action';
$edit['user_bulk_form[4]'] = TRUE;
$this->drupalPostForm('admin/people', $edit, t('Apply'), array(
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
'query' => array('order' => 'name', 'sort' => 'asc')
));
$user_storage->resetCache(array($user_c->id()));
$account = $user_storage->load($user_c->id());
$this->assertTrue($account->isBlocked(), 'User C blocked');
// Test filtering on admin page for blocked users
$this->drupalGet('admin/people', array('query' => array('status' => 2)));
$this->assertNoText($user_a->getUsername(), 'User A not on filtered by status on admin users page');
$this->assertNoText($user_b->getUsername(), 'User B not on filtered by status on admin users page');
$this->assertText($user_c->getUsername(), 'User C on filtered by status on admin users page');
// Test unblocking of a user from /admin/people page and sending of activation mail
$editunblock = array();
$editunblock['action'] = 'user_unblock_user_action';
$editunblock['user_bulk_form[4]'] = TRUE;
$this->drupalPostForm('admin/people', $editunblock, t('Apply'), array(
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
'query' => array('order' => 'name', 'sort' => 'asc')
));
$user_storage->resetCache(array($user_c->id()));
$account = $user_storage->load($user_c->id());
$this->assertTrue($account->isActive(), 'User C unblocked');
$this->assertMail("to", $account->getEmail(), "Activation mail sent to user C");
// Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail
$user_d = $this->drupalCreateUser(array());
$user_storage->resetCache(array($user_d->id()));
$account1 = $user_storage->load($user_d->id());
$this->drupalPostForm('user/' . $account1->id() . '/edit', array('status' => 0), t('Save'));
$user_storage->resetCache(array($user_d->id()));
$account1 = $user_storage->load($user_d->id());
$this->assertTrue($account1->isBlocked(), 'User D blocked');
$this->drupalPostForm('user/' . $account1->id() . '/edit', array('status' => TRUE), t('Save'));
$user_storage->resetCache(array($user_d->id()));
$account1 = $user_storage->load($user_d->id());
$this->assertTrue($account1->isActive(), 'User D unblocked');
$this->assertMail("to", $account1->getEmail(), "Activation mail sent to user D");
}
/**
* Tests the alternate notification email address for user mails.
*/
function testNotificationEmailAddress() {
// Test that the Notification Email address field is on the config page.
$admin_user = $this->drupalCreateUser(array('administer users', 'administer account settings'));
$this->drupalLogin($admin_user);
$this->drupalGet('admin/config/people/accounts');
$this->assertRaw('id="edit-mail-notification-address"', 'Notification Email address field exists');
$this->drupalLogout();
// Test custom user registration approval email address(es).
$config = $this->config('user.settings');
// Allow users to register with admin approval.
$config
->set('verify_mail', TRUE)
->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)
->save();
// Set the site and notification email addresses.
$system = $this->config('system.site');
$server_address = $this->randomMachineName() . '@example.com';
$notify_address = $this->randomMachineName() . '@example.com';
$system
->set('mail', $server_address)
->set('mail_notification', $notify_address)
->save();
// Register a new user account.
$edit = array();
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$subject = 'Account details for ' . $edit['name'] . ' at ' . $system->get('name') . ' (pending admin approval)';
// Ensure that admin notification mail is sent to the configured
// Notification Email address.
$admin_mail = $this->drupalGetMails(array(
'to' => $notify_address,
'from' => $server_address,
'subject' => $subject,
));
$this->assertTrue(count($admin_mail), 'New user mail to admin is sent to configured Notification Email address');
// Ensure that user notification mail is sent from the configured
// Notification Email address.
$user_mail = $this->drupalGetMails(array(
'to' => $edit['mail'],
'from' => $server_address,
'reply-to' => $notify_address,
'subject' => $subject,
));
$this->assertTrue(count($user_mail), 'New user mail to user is sent from configured Notification Email address');
}
}

View file

@ -0,0 +1,125 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserBlocksTest.
*/
namespace Drupal\user\Tests;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\simpletest\WebTestBase;
/**
* Tests user blocks.
*
* @group user
*/
class UserBlocksTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('block', 'views');
/**
* A user with the 'administer blocks' permission.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(array('administer blocks'));
$this->drupalLogin($this->adminUser);
$this->drupalPlaceBlock('user_login_block');
$this->drupalLogout($this->adminUser);
}
/**
* Test the user login block.
*/
function testUserLoginBlock() {
// Make sure the validation error is displayed when try to login with
// invalid username/password.
$edit['name'] = $this->randomMachineName();
$edit['pass'] = $this->randomMachineName();
$this->drupalPostForm('node', $edit, t('Log in'));
$this->assertRaw(\Drupal::translation()->formatPlural(1, '1 error has been found: !errors', '@count errors have been found: !errors', [
'!errors' => SafeMarkup::set('<a href="#edit-name">Username</a>')
]));
$this->assertText(t('Sorry, unrecognized username or password.'));
// Create a user with some permission that anonymous users lack.
$user = $this->drupalCreateUser(array('administer permissions'));
// Log in using the block.
$edit = array();
$edit['name'] = $user->getUsername();
$edit['pass'] = $user->pass_raw;
$this->drupalPostForm('admin/people/permissions', $edit, t('Log in'));
$this->assertNoText(t('User login'), 'Logged in.');
// Check that we are still on the same page.
$this->assertUrl(\Drupal::url('user.admin_permissions', [], ['absolute' => TRUE]), [], 'Still on the same page after login for access denied page');
// Now, log out and repeat with a non-403 page.
$this->drupalLogout();
$this->drupalPostForm('filter/tips', $edit, t('Log in'));
$this->assertNoText(t('User login'), 'Logged in.');
$this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
// Check that the user login block is not vulnerable to information
// disclosure to third party sites.
$this->drupalLogout();
$this->drupalPostForm('http://example.com/', $edit, t('Log in'), array('external' => FALSE));
// Check that we remain on the site after login.
$this->assertUrl($user->url('canonical', ['absolute' => TRUE]), [], 'Redirected to user profile page after login from the frontpage');
}
/**
* Test the Who's Online block.
*/
function testWhosOnlineBlock() {
$block = $this->drupalPlaceBlock('views_block:who_s_online-who_s_online_block');
// Generate users.
$user1 = $this->drupalCreateUser(array('access user profiles'));
$user2 = $this->drupalCreateUser(array());
$user3 = $this->drupalCreateUser(array());
// Update access of two users to be within the active timespan.
$this->updateAccess($user1->id());
$this->updateAccess($user2->id(), REQUEST_TIME + 1);
// Insert an inactive user who should not be seen in the block, and ensure
// that the admin user used in setUp() does not appear.
$inactive_time = REQUEST_TIME - (15 * 60) - 1;
$this->updateAccess($user3->id(), $inactive_time);
$this->updateAccess($this->adminUser->id(), $inactive_time);
// Test block output.
\Drupal::currentUser()->setAccount($user1);
$content = entity_view($block, 'block');
$this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
$this->assertRaw(t('2 users'), 'Correct number of online users (2 users).');
$this->assertText($user1->getUsername(), 'Active user 1 found in online list.');
$this->assertText($user2->getUsername(), 'Active user 2 found in online list.');
$this->assertNoText($user3->getUsername(), 'Inactive user not found in online list.');
$this->assertTrue(strpos($this->getRawContent(), $user1->getUsername()) > strpos($this->getRawContent(), $user2->getUsername()), 'Online users are ordered correctly.');
}
/**
* Updates the access column for a user.
*/
private function updateAccess($uid, $access = REQUEST_TIME) {
db_update('users_field_data')
->condition('uid', $uid)
->fields(array('access' => $access))
->execute();
}
}

View file

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserCacheTagsTest.
*/
namespace Drupal\user\Tests;
use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the User entity's cache tags.
*
* @group user
*/
class UserCacheTagsTest extends EntityWithUriCacheTagsTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('user');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Give anonymous users permission to view user profiles, so that we can
// verify the cache tags of cached versions of user profile pages.
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
$user_role->grantPermission('access user profiles');
$user_role->save();
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Llama" user.
$user = entity_create('user', array(
'name' => 'Llama',
'status' => TRUE,
));
$user->save();
return $user;
}
/**
* {@inheritdoc}
*/
protected function getAdditionalCacheTagsForEntityListing() {
return ['user:0', 'user:1'];
}
}

View file

@ -0,0 +1,555 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserCancelTest.
*/
namespace Drupal\user\Tests;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\simpletest\WebTestBase;
use Drupal\comment\CommentInterface;
use Drupal\comment\Entity\Comment;
use Drupal\user\Entity\User;
/**
* Ensure that account cancellation methods work as expected.
*
* @group user
*/
class UserCancelTest extends WebTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'comment');
protected function setUp() {
parent::setUp();
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
}
/**
* Attempt to cancel account without permission.
*/
function testUserCancelWithoutPermission() {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create a user.
$account = $this->drupalCreateUser(array());
$this->drupalLogin($account);
// Load a real user object.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
// Create a node.
$node = $this->drupalCreateNode(array('uid' => $account->id()));
// Attempt to cancel account.
$this->drupalGet('user/' . $account->id() . '/edit');
$this->assertNoRaw(t('Cancel account'), 'No cancel account button displayed.');
// Attempt bogus account cancellation request confirmation.
$timestamp = $account->getLastLoginTime();
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
$this->assertResponse(403, 'Bogus cancelling request rejected.');
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
$this->assertTrue($account->isActive(), 'User account was not canceled.');
// Confirm user's content has not been altered.
$node_storage->resetCache(array($node->id()));
$test_node = $node_storage->load($node->id());
$this->assertTrue(($test_node->getOwnerId() == $account->id() && $test_node->isPublished()), 'Node of the user has not been altered.');
}
/**
* Test ability to change the permission for canceling users.
*/
public function testUserCancelChangePermission() {
\Drupal::service('module_installer')->install(array('user_form_test'));
\Drupal::service('router.builder')->rebuild();
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
// Create a regular user.
$account = $this->drupalCreateUser(array());
$admin_user = $this->drupalCreateUser(array('cancel other accounts'));
$this->drupalLogin($admin_user);
// Delete regular user.
$this->drupalPostForm('user_form_test_cancel/' . $account->id(), array(), t('Cancel account'));
// Confirm deletion.
$this->assertRaw(t('%name has been deleted.', array('%name' => $account->getUsername())), 'User deleted.');
$this->assertFalse(User::load($account->id()), 'User is not found in the database.');
}
/**
* Tests that user account for uid 1 cannot be cancelled.
*
* This should never be possible, or the site owner would become unable to
* administer the site.
*/
function testUserCancelUid1() {
$user_storage = $this->container->get('entity.manager')->getStorage('user');
\Drupal::service('module_installer')->install(array('views'));
\Drupal::service('router.builder')->rebuild();
// Update uid 1's name and password to we know it.
$password = user_password();
$account = array(
'name' => 'user1',
'pass' => $this->container->get('password')->hash(trim($password)),
);
// We cannot use $account->save() here, because this would result in the
// password being hashed again.
db_update('users_field_data')
->fields($account)
->condition('uid', 1)
->execute();
// Reload and log in uid 1.
$user_storage->resetCache(array(1));
$user1 = $user_storage->load(1);
$user1->pass_raw = $password;
// Try to cancel uid 1's account with a different user.
$admin_user = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($admin_user);
$edit = array(
'action' => 'user_cancel_user_action',
'user_bulk_form[0]' => TRUE,
);
$this->drupalPostForm('admin/people', $edit, t('Apply'));
// Verify that uid 1's account was not cancelled.
$user_storage->resetCache(array(1));
$user1 = $user_storage->load(1);
$this->assertTrue($user1->isActive(), 'User #1 still exists and is not blocked.');
}
/**
* Attempt invalid account cancellations.
*/
function testUserCancelInvalid() {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create a user.
$account = $this->drupalCreateUser(array('cancel account'));
$this->drupalLogin($account);
// Load a real user object.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
// Create a node.
$node = $this->drupalCreateNode(array('uid' => $account->id()));
// Attempt to cancel account.
$this->drupalPostForm('user/' . $account->id() . '/edit', NULL, t('Cancel account'));
// Confirm account cancellation.
$timestamp = time();
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
// Attempt bogus account cancellation request confirmation.
$bogus_timestamp = $timestamp + 60;
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->getPassword(), $bogus_timestamp, $account->getLastLoginTime(), $account->id()));
$this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Bogus cancelling request rejected.');
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
$this->assertTrue($account->isActive(), 'User account was not canceled.');
// Attempt expired account cancellation request confirmation.
$bogus_timestamp = $timestamp - 86400 - 60;
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->getPassword(), $bogus_timestamp, $account->getLastLoginTime(), $account->id()));
$this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Expired cancel account request rejected.');
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
$this->assertTrue($account->isActive(), 'User account was not canceled.');
// Confirm user's content has not been altered.
$node_storage->resetCache(array($node->id()));
$test_node = $node_storage->load($node->id());
$this->assertTrue(($test_node->getOwnerId() == $account->id() && $test_node->isPublished()), 'Node of the user has not been altered.');
}
/**
* Disable account and keep all content.
*/
function testUserBlock() {
$this->config('user.settings')->set('cancel_method', 'user_cancel_block')->save();
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create a user.
$web_user = $this->drupalCreateUser(array('cancel account'));
$this->drupalLogin($web_user);
// Load a real user object.
$user_storage->resetCache(array($web_user->id()));
$account = $user_storage->load($web_user->id());
// Attempt to cancel account.
$this->drupalGet('user/' . $account->id() . '/edit');
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
$this->assertText(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.'), 'Informs that all content will be remain as is.');
$this->assertNoText(t('Select the method to cancel the account above.'), 'Does not allow user to select account cancellation method.');
// Confirm account cancellation.
$timestamp = time();
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
// Confirm account cancellation request.
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
// Confirm that the confirmation message made it through to the end user.
$this->assertRaw(t('%name has been disabled.', array('%name' => $account->getUsername())), "Confirmation message displayed to user.");
}
/**
* Disable account and unpublish all content.
*/
function testUserBlockUnpublish() {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
$this->config('user.settings')->set('cancel_method', 'user_cancel_block_unpublish')->save();
// Create comment field on page.
$this->addDefaultCommentField('node', 'page');
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create a user.
$account = $this->drupalCreateUser(array('cancel account'));
$this->drupalLogin($account);
// Load a real user object.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
// Create a node with two revisions.
$node = $this->drupalCreateNode(array('uid' => $account->id()));
$settings = get_object_vars($node);
$settings['revision'] = 1;
$node = $this->drupalCreateNode($settings);
// Add a comment to the page.
$comment_subject = $this->randomMachineName(8);
$comment_body = $this->randomMachineName(8);
$comment = entity_create('comment', array(
'subject' => $comment_subject,
'comment_body' => $comment_body,
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'status' => CommentInterface::PUBLISHED,
'uid' => $account->id(),
));
$comment->save();
// Attempt to cancel account.
$this->drupalGet('user/' . $account->id() . '/edit');
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
$this->assertText(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.'), 'Informs that all content will be unpublished.');
// Confirm account cancellation.
$timestamp = time();
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
// Confirm account cancellation request.
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
// Confirm user's content has been unpublished.
$node_storage->resetCache(array($node->id()));
$test_node = $node_storage->load($node->id());
$this->assertFalse($test_node->isPublished(), 'Node of the user has been unpublished.');
$test_node = node_revision_load($node->getRevisionId());
$this->assertFalse($test_node->isPublished(), 'Node revision of the user has been unpublished.');
$storage = \Drupal::entityManager()->getStorage('comment');
$storage->resetCache(array($comment->id()));
$comment = $storage->load($comment->id());
$this->assertFalse($comment->isPublished(), 'Comment of the user has been unpublished.');
// Confirm that the confirmation message made it through to the end user.
$this->assertRaw(t('%name has been disabled.', array('%name' => $account->getUsername())), "Confirmation message displayed to user.");
}
/**
* Delete account and anonymize all content.
*/
function testUserAnonymize() {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
// Create comment field on page.
$this->addDefaultCommentField('node', 'page');
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create a user.
$account = $this->drupalCreateUser(array('cancel account'));
$this->drupalLogin($account);
// Load a real user object.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
// Create a simple node.
$node = $this->drupalCreateNode(array('uid' => $account->id()));
// Add a comment to the page.
$comment_subject = $this->randomMachineName(8);
$comment_body = $this->randomMachineName(8);
$comment = entity_create('comment', array(
'subject' => $comment_subject,
'comment_body' => $comment_body,
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'status' => CommentInterface::PUBLISHED,
'uid' => $account->id(),
));
$comment->save();
// Create a node with two revisions, the initial one belonging to the
// cancelling user.
$revision_node = $this->drupalCreateNode(array('uid' => $account->id()));
$revision = $revision_node->getRevisionId();
$settings = get_object_vars($revision_node);
$settings['revision'] = 1;
$settings['uid'] = 1; // Set new/current revision to someone else.
$revision_node = $this->drupalCreateNode($settings);
// Attempt to cancel account.
$this->drupalGet('user/' . $account->id() . '/edit');
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
$this->assertRaw(t('Your account will be removed and all account information deleted. All of your content will be assigned to the %anonymous-name user.', array('%anonymous-name' => $this->config('user.settings')->get('anonymous'))), 'Informs that all content will be attributed to anonymous account.');
// Confirm account cancellation.
$timestamp = time();
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
// Confirm account cancellation request.
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
$user_storage->resetCache(array($account->id()));
$this->assertFalse($user_storage->load($account->id()), 'User is not found in the database.');
// Confirm that user's content has been attributed to anonymous user.
$anonymous_user = User::getAnonymousUser();
$node_storage->resetCache(array($node->id()));
$test_node = $node_storage->load($node->id());
$this->assertTrue(($test_node->getOwnerId() == 0 && $test_node->isPublished()), 'Node of the user has been attributed to anonymous user.');
$test_node = node_revision_load($revision, TRUE);
$this->assertTrue(($test_node->getRevisionAuthor()->id() == 0 && $test_node->isPublished()), 'Node revision of the user has been attributed to anonymous user.');
$node_storage->resetCache(array($revision_node->id()));
$test_node = $node_storage->load($revision_node->id());
$this->assertTrue(($test_node->getOwnerId() != 0 && $test_node->isPublished()), "Current revision of the user's node was not attributed to anonymous user.");
$storage = \Drupal::entityManager()->getStorage('comment');
$storage->resetCache(array($comment->id()));
$test_comment = $storage->load($comment->id());
$this->assertTrue(($test_comment->getOwnerId() == 0 && $test_comment->isPublished()), 'Comment of the user has been attributed to anonymous user.');
$this->assertEqual($test_comment->getAuthorName(), $anonymous_user->getUsername(), 'Comment of the user has been attributed to anonymous user name.');
// Confirm that the confirmation message made it through to the end user.
$this->assertRaw(t('%name has been deleted.', array('%name' => $account->getUsername())), "Confirmation message displayed to user.");
}
/**
* Delete account and remove all content.
*/
function testUserDelete() {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
$this->config('user.settings')->set('cancel_method', 'user_cancel_delete')->save();
\Drupal::service('module_installer')->install(array('comment'));
$this->resetAll();
$this->addDefaultCommentField('node', 'page');
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create a user.
$account = $this->drupalCreateUser(array('cancel account', 'post comments', 'skip comment approval'));
$this->drupalLogin($account);
// Load a real user object.
$user_storage->resetCache(array($account->id()));
$account = $user_storage->load($account->id());
// Create a simple node.
$node = $this->drupalCreateNode(array('uid' => $account->id()));
// Create comment.
$edit = array();
$edit['subject[0][value]'] = $this->randomMachineName(8);
$edit['comment_body[0][value]'] = $this->randomMachineName(16);
$this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit, t('Preview'));
$this->drupalPostForm(NULL, array(), t('Save'));
$this->assertText(t('Your comment has been posted.'));
$comments = entity_load_multiple_by_properties('comment', array('subject' => $edit['subject[0][value]']));
$comment = reset($comments);
$this->assertTrue($comment->id(), 'Comment found.');
// Create a node with two revisions, the initial one belonging to the
// cancelling user.
$revision_node = $this->drupalCreateNode(array('uid' => $account->id()));
$revision = $revision_node->getRevisionId();
$settings = get_object_vars($revision_node);
$settings['revision'] = 1;
$settings['uid'] = 1; // Set new/current revision to someone else.
$revision_node = $this->drupalCreateNode($settings);
// Attempt to cancel account.
$this->drupalGet('user/' . $account->id() . '/edit');
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
$this->assertText(t('Your account will be removed and all account information deleted. All of your content will also be deleted.'), 'Informs that all content will be deleted.');
// Confirm account cancellation.
$timestamp = time();
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
// Confirm account cancellation request.
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
$user_storage->resetCache(array($account->id()));
$this->assertFalse($user_storage->load($account->id()), 'User is not found in the database.');
// Confirm that user's content has been deleted.
$node_storage->resetCache(array($node->id()));
$this->assertFalse($node_storage->load($node->id()), 'Node of the user has been deleted.');
$this->assertFalse(node_revision_load($revision), 'Node revision of the user has been deleted.');
$node_storage->resetCache(array($revision_node->id()));
$this->assertTrue($node_storage->load($revision_node->id()), "Current revision of the user's node was not deleted.");
\Drupal::entityManager()->getStorage('comment')->resetCache(array($comment->id()));
$this->assertFalse(Comment::load($comment->id()), 'Comment of the user has been deleted.');
// Confirm that the confirmation message made it through to the end user.
$this->assertRaw(t('%name has been deleted.', array('%name' => $account->getUsername())), "Confirmation message displayed to user.");
}
/**
* Create an administrative user and delete another user.
*/
function testUserCancelByAdmin() {
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
// Create a regular user.
$account = $this->drupalCreateUser(array());
// Create administrative user.
$admin_user = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($admin_user);
// Delete regular user.
$this->drupalGet('user/' . $account->id() . '/edit');
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->getUsername())), 'Confirmation form to cancel account displayed.');
$this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.');
// Confirm deletion.
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertRaw(t('%name has been deleted.', array('%name' => $account->getUsername())), 'User deleted.');
$this->assertFalse(User::load($account->id()), 'User is not found in the database.');
}
/**
* Tests deletion of a user account without an email address.
*/
function testUserWithoutEmailCancelByAdmin() {
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
// Create a regular user.
$account = $this->drupalCreateUser(array());
// This user has no email address.
$account->mail = '';
$account->save();
// Create administrative user.
$admin_user = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($admin_user);
// Delete regular user without email address.
$this->drupalGet('user/' . $account->id() . '/edit');
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->getUsername())), 'Confirmation form to cancel account displayed.');
$this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.');
// Confirm deletion.
$this->drupalPostForm(NULL, NULL, t('Cancel account'));
$this->assertRaw(t('%name has been deleted.', array('%name' => $account->getUsername())), 'User deleted.');
$this->assertFalse(User::load($account->id()), 'User is not found in the database.');
}
/**
* Create an administrative user and mass-delete other users.
*/
function testMassUserCancelByAdmin() {
\Drupal::service('module_installer')->install(array('views'));
\Drupal::service('router.builder')->rebuild();
$this->config('user.settings')->set('cancel_method', 'user_cancel_reassign')->save();
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Enable account cancellation notification.
$this->config('user.settings')->set('notify.status_canceled', TRUE)->save();
// Create administrative user.
$admin_user = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($admin_user);
// Create some users.
$users = array();
for ($i = 0; $i < 3; $i++) {
$account = $this->drupalCreateUser(array());
$users[$account->id()] = $account;
}
// Cancel user accounts, including own one.
$edit = array();
$edit['action'] = 'user_cancel_user_action';
for ($i = 0; $i <= 4; $i++) {
$edit['user_bulk_form[' . $i . ']'] = TRUE;
}
$this->drupalPostForm('admin/people', $edit, t('Apply'));
$this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.');
$this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.');
$this->assertText(t('Require email confirmation to cancel account'), 'Allows to send confirmation mail.');
$this->assertText(t('Notify user when account is canceled'), 'Allows to send notification mail.');
// Confirm deletion.
$this->drupalPostForm(NULL, NULL, t('Cancel accounts'));
$status = TRUE;
foreach ($users as $account) {
$status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->getUsername()))) !== FALSE);
$user_storage->resetCache(array($account->id()));
$status = $status && !$user_storage->load($account->id());
}
$this->assertTrue($status, 'Users deleted and not found in the database.');
// Ensure that admin account was not cancelled.
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
$admin_user = $user_storage->load($admin_user->id());
$this->assertTrue($admin_user->isActive(), 'Administrative user is found in the database and enabled.');
// Verify that uid 1's account was not cancelled.
$user_storage->resetCache(array(1));
$user1 = $user_storage->load(1);
$this->assertTrue($user1->isActive(), 'User #1 still exists and is not blocked.');
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserCreateFailMailTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests the create user administration page.
*
* @group user
*/
class UserCreateFailMailTest extends WebTestBase {
/**
* Modules to enable
*
* @var array
*/
public static $modules = array('system_mail_failure_test');
/**
* Tests the create user administration page.
*/
public function testUserAdd() {
$user = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($user);
// Replace the mail functionality with a fake, malfunctioning service.
$this->config('system.mail')->set('interface.default', 'test_php_mail_failure')->save();
// Create a user, but fail to send an email.
$name = $this->randomMachineName();
$edit = array(
'name' => $name,
'mail' => $this->randomMachineName() . '@example.com',
'pass[pass1]' => $pass = $this->randomString(),
'pass[pass2]' => $pass,
'notify' => TRUE,
);
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
$this->assertText(t('Unable to send email. Contact the site administrator if the problem persists.'));
$this->assertNoText(t('A welcome message with further instructions has been emailed to the new user @name.', array('@name' => $edit['name'])));
}
}

View file

@ -0,0 +1,118 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserCreateTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests the create user administration page.
*
* @group user
*/
class UserCreateTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('image');
/**
* Create a user through the administration interface and ensure that it
* displays in the user list.
*/
public function testUserAdd() {
$user = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($user);
$this->assertEqual($user->getCreatedTime(), REQUEST_TIME, 'Creating a user sets default "created" timestamp.');
$this->assertEqual($user->getChangedTime(), REQUEST_TIME, 'Creating a user sets default "changed" timestamp.');
// Create a field.
$field_name = 'test_field';
entity_create('field_storage_config', array(
'field_name' => $field_name,
'entity_type' => 'user',
'module' => 'image',
'type' => 'image',
'cardinality' => 1,
'locked' => FALSE,
'indexes' => array('target_id' => array('target_id')),
'settings' => array(
'uri_scheme' => 'public',
),
))->save();
entity_create('field_config', array(
'field_name' => $field_name,
'entity_type' => 'user',
'label' => 'Picture',
'bundle' => 'user',
'description' => t('Your virtual face or picture.'),
'required' => FALSE,
'settings' => array(
'file_extensions' => 'png gif jpg jpeg',
'file_directory' => 'pictures',
'max_filesize' => '30 KB',
'alt_field' => 0,
'title_field' => 0,
'max_resolution' => '85x85',
'min_resolution' => '',
),
))->save();
// Test user creation page for valid fields.
$this->drupalGet('admin/people/create');
$this->assertFieldbyId('edit-status-0', 0, 'The user status option Blocked exists.', 'User login');
$this->assertFieldbyId('edit-status-1', 1, 'The user status option Active exists.', 'User login');
$this->assertFieldByXPath('//input[@type="radio" and @id="edit-status-1" and @checked="checked"]', NULL, 'Default setting for user status is active.');
// Test that browser autocomplete behavior does not occur.
$this->assertNoRaw('data-user-info-from-browser', 'Ensure form attribute, data-user-info-from-browser, does not exist.');
// Test that the password strength indicator displays.
$config = $this->config('user.settings');
$config->set('password_strength', TRUE)->save();
$this->drupalGet('admin/people/create');
$this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
$config->set('password_strength', FALSE)->save();
$this->drupalGet('admin/people/create');
$this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
// We create two users, notifying one and not notifying the other, to
// ensure that the tests work in both cases.
foreach (array(FALSE, TRUE) as $notify) {
$name = $this->randomMachineName();
$edit = array(
'name' => $name,
'mail' => $this->randomMachineName() . '@example.com',
'pass[pass1]' => $pass = $this->randomString(),
'pass[pass2]' => $pass,
'notify' => $notify,
);
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
if ($notify) {
$this->assertText(t('A welcome message with further instructions has been emailed to the new user @name.', array('@name' => $edit['name'])), 'User created');
$this->assertEqual(count($this->drupalGetMails()), 1, 'Notification email sent');
}
else {
$this->assertText(t('Created a new user account for @name. No email has been sent.', array('@name' => $edit['name'])), 'User created');
$this->assertEqual(count($this->drupalGetMails()), 0, 'Notification email not sent');
}
$this->drupalGet('admin/people');
$this->assertText($edit['name'], 'User found in list of users');
$user = user_load_by_name($name);
$this->assertEqual($user->isActive(), 'User is not blocked');
}
}
}

View file

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserDeleteTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
use Drupal\user\Entity\User;
/**
* Tests account deleting of users.
*
* @group user
*/
class UserDeleteTest extends WebTestBase {
/**
* Test deleting multiple users.
*/
function testUserDeleteMultiple() {
// Create a few users with permissions, so roles will be created.
$user_a = $this->drupalCreateUser(array('access user profiles'));
$user_b = $this->drupalCreateUser(array('access user profiles'));
$user_c = $this->drupalCreateUser(array('access user profiles'));
$uids = array($user_a->id(), $user_b->id(), $user_c->id());
// These users should have a role
$query = db_select('user__roles', 'r');
$roles_created = $query
->fields('r', array('entity_id'))
->condition('entity_id', $uids, 'IN')
->countQuery()
->execute()
->fetchField();
$this->assertTrue($roles_created > 0, 'Role assignments created for new users and deletion of role assignments can be tested');
// We should be able to load one of the users.
$this->assertTrue(User::load($user_a->id()), 'User is created and deletion of user can be tested');
// Delete the users.
user_delete_multiple($uids);
// Test if the roles assignments are deleted.
$query = db_select('user__roles', 'r');
$roles_after_deletion = $query
->fields('r', array('entity_id'))
->condition('entity_id', $uids, 'IN')
->countQuery()
->execute()
->fetchField();
$this->assertTrue($roles_after_deletion == 0, 'Role assignments deleted along with users');
// Test if the users are deleted, User::load() will return NULL.
$this->assertNull(User::load($user_a->id()), format_string('User with id @uid deleted.', array('@uid' => $user_a->id())));
$this->assertNull(User::load($user_b->id()), format_string('User with id @uid deleted.', array('@uid' => $user_b->id())));
$this->assertNull(User::load($user_c->id()), format_string('User with id @uid deleted.', array('@uid' => $user_c->id())));
}
}

View file

@ -0,0 +1,105 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserEditTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests user edit page.
*
* @group user
*/
class UserEditTest extends WebTestBase {
/**
* Test user edit page.
*/
function testUserEdit() {
// Test user edit functionality.
$user1 = $this->drupalCreateUser(array('change own username'));
$user2 = $this->drupalCreateUser(array());
$this->drupalLogin($user1);
// Test that error message appears when attempting to use a non-unique user name.
$edit['name'] = $user2->getUsername();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t('The username %name is already taken.', array('%name' => $edit['name'])));
// Check that filling out a single password field does not validate.
$edit = array();
$edit['pass[pass1]'] = '';
$edit['pass[pass2]'] = $this->randomMachineName();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
$edit['pass[pass1]'] = $this->randomMachineName();
$edit['pass[pass2]'] = '';
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
// Test that the error message appears when attempting to change the mail or
// pass without the current password.
$edit = array();
$edit['mail'] = $this->randomMachineName() . '@new.example.com';
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Email'))));
$edit['current_pass'] = $user1->pass_raw;
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("The changes have been saved."));
// Test that the user must enter current password before changing passwords.
$edit = array();
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
$edit['pass[pass2]'] = $new_pass;
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Password'))));
// Try again with the current password.
$edit['current_pass'] = $user1->pass_raw;
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("The changes have been saved."));
// Make sure the changed timestamp is updated.
$this->assertEqual($user1->getChangedTime(), REQUEST_TIME, 'Changing a user sets "changed" timestamp.');
// Make sure the user can log in with their new password.
$this->drupalLogout();
$user1->pass_raw = $new_pass;
$this->drupalLogin($user1);
$this->drupalLogout();
// Test that the password strength indicator displays.
$config = $this->config('user.settings');
$this->drupalLogin($user1);
$config->set('password_strength', TRUE)->save();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
$config->set('password_strength', FALSE)->save();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
}
/**
* Tests editing of a user account without an email address.
*/
function testUserWithoutEmailEdit() {
// Test that an admin can edit users without an email address.
$admin = $this->drupalCreateUser(array('administer users'));
$this->drupalLogin($admin);
// Create a regular user.
$user1 = $this->drupalCreateUser(array());
// This user has no email address.
$user1->mail = '';
$user1->save();
$this->drupalPostForm("user/" . $user1->id() . "/edit", array('mail' => ''), t('Save'));
$this->assertRaw(t("The changes have been saved."));
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserEditedOwnAccountTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests user edited own account can still log in.
*
* @group user
*/
class UserEditedOwnAccountTest extends WebTestBase {
function testUserEditedOwnAccount() {
// Change account setting 'Who can register accounts?' to Administrators
// only.
$this->config('user.settings')->set('register', USER_REGISTER_ADMINISTRATORS_ONLY)->save();
// Create a new user account and log in.
$account = $this->drupalCreateUser(array('change own username'));
$this->drupalLogin($account);
// Change own username.
$edit = array();
$edit['name'] = $this->randomMachineName();
$this->drupalPostForm('user/' . $account->id() . '/edit', $edit, t('Save'));
// Log out.
$this->drupalLogout();
// Set the new name on the user account and attempt to log back in.
$account->name = $edit['name'];
$this->drupalLogin($account);
}
}

View file

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserEntityCallbacksTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests specific parts of the user entity like the URI callback and the label
* callback.
*
* @group user
*/
class UserEntityCallbacksTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('user');
/**
* An authenticated user to use for testing.
*
* @var \Drupal\user\UserInterface
*/
protected $account;
/**
* An anonymous user to use for testing.
*
* @var \Drupal\user\UserInterface
*/
protected $anonymous;
protected function setUp() {
parent::setUp();
$this->account = $this->drupalCreateUser();
$this->anonymous = entity_create('user', array('uid' => 0));
}
/**
* Test label callback.
*/
function testLabelCallback() {
$this->assertEqual($this->account->label(), $this->account->getUsername(), 'The username should be used as label');
// Setup a random anonymous name to be sure the name is used.
$name = $this->randomMachineName();
$this->config('user.settings')->set('anonymous', $name)->save();
$this->assertEqual($this->anonymous->label(), $name, 'The variable anonymous should be used for name of uid 0');
}
}

View file

@ -0,0 +1,108 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserEntityReferenceTest.
*/
namespace Drupal\user\Tests;
use Drupal\entity_reference\Tests\EntityReferenceTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\system\Tests\Entity\EntityUnitTestBase;
/**
* Tests the user reference field functionality.
*
* @group user
*/
class UserEntityReferenceTest extends EntityUnitTestBase {
use EntityReferenceTestTrait;
/**
* A randomly-generated role for testing purposes.
*
* @var \Drupal\user\Entity\RoleInterface
*/
protected $role1;
/**
* A randomly-generated role for testing purposes.
*
* @var \Drupal\user\Entity\RoleInterface
*/
protected $role2;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('entity_reference', 'user');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->role1 = entity_create('user_role', array(
'id' => strtolower($this->randomMachineName(8)),
'label' => $this->randomMachineName(8),
));
$this->role1->save();
$this->role2 = entity_create('user_role', array(
'id' => strtolower($this->randomMachineName(8)),
'label' => $this->randomMachineName(8),
));
$this->role2->save();
$this->createEntityReferenceField('user', 'user', 'user_reference', 'User reference', 'user');
}
/**
* Tests user selection by roles.
*/
function testUserSelectionByRole() {
$field_definition = FieldConfig::loadByName('user', 'user', 'user_reference');
$handler_settings = $field_definition->getSetting('handler_settings');
$handler_settings['filter']['role'] = array(
$this->role1->id() => $this->role1->id(),
$this->role2->id() => 0,
);
$handler_settings['filter']['type'] = 'role';
$field_definition->setSetting('handler_settings', $handler_settings);
$field_definition->save();
$user1 = $this->createUser(array('name' => 'aabb'));
$user1->addRole($this->role1->id());
$user1->save();
$user2 = $this->createUser(array('name' => 'aabbb'));
$user2->addRole($this->role1->id());
$user2->save();
$user3 = $this->createUser(array('name' => 'aabbbb'));
$user3->addRole($this->role2->id());
$user3->save();
/** @var \Drupal\Core\Entity\EntityAutocompleteMatcher $autocomplete */
$autocomplete = \Drupal::service('entity.autocomplete_matcher');
$matches = $autocomplete->getMatches('user', 'default', $field_definition->getSetting('handler_settings'), 'aabb');
$this->assertEqual(count($matches), 2);
$users = array();
foreach ($matches as $match) {
$users[] = $match['label'];
}
$this->assertTrue(in_array($user1->label(), $users));
$this->assertTrue(in_array($user2->label(), $users));
$this->assertFalse(in_array($user3->label(), $users));
$matches = $autocomplete->getMatches('user', 'default', $field_definition->getSetting('handler_settings'), 'aabbbb');
$this->assertEqual(count($matches), 0, '');
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserEntityTest.
*/
namespace Drupal\user\Tests;
use Drupal\Core\Language\LanguageInterface;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
/**
* Tests the user entity class.
*
* @group user
* @see \Drupal\user\Entity\User
*/
class UserEntityTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'user', 'field');
/**
* Tests some of the methods.
*
* @see \Drupal\user\Entity\User::getRoles()
* @see \Drupal\user\Entity\User::addRole()
* @see \Drupal\user\Entity\User::removeRole()
*/
public function testUserMethods() {
$role_storage = $this->container->get('entity.manager')->getStorage('user_role');
$role_storage->create(array('id' => 'test_role_one'))->save();
$role_storage->create(array('id' => 'test_role_two'))->save();
$role_storage->create(array('id' => 'test_role_three'))->save();
$values = array(
'uid' => 1,
'roles' => array('test_role_one'),
);
$user = User::create($values);
$this->assertTrue($user->hasRole('test_role_one'));
$this->assertFalse($user->hasRole('test_role_two'));
$this->assertEqual(array(RoleInterface::AUTHENTICATED_ID, 'test_role_one'), $user->getRoles());
$user->addRole('test_role_one');
$this->assertTrue($user->hasRole('test_role_one'));
$this->assertFalse($user->hasRole('test_role_two'));
$this->assertEqual(array(RoleInterface::AUTHENTICATED_ID, 'test_role_one'), $user->getRoles());
$user->addRole('test_role_two');
$this->assertTrue($user->hasRole('test_role_one'));
$this->assertTrue($user->hasRole('test_role_two'));
$this->assertEqual(array(RoleInterface::AUTHENTICATED_ID, 'test_role_one', 'test_role_two'), $user->getRoles());
$user->removeRole('test_role_three');
$this->assertTrue($user->hasRole('test_role_one'));
$this->assertTrue($user->hasRole('test_role_two'));
$this->assertEqual(array(RoleInterface::AUTHENTICATED_ID, 'test_role_one', 'test_role_two'), $user->getRoles());
$user->removeRole('test_role_one');
$this->assertFalse($user->hasRole('test_role_one'));
$this->assertTrue($user->hasRole('test_role_two'));
$this->assertEqual(array(RoleInterface::AUTHENTICATED_ID, 'test_role_two'), $user->getRoles());
}
}

View file

@ -0,0 +1,57 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserFieldsTest.
*/
namespace Drupal\user\Tests;
use Drupal\user\Entity\User;
use Drupal\simpletest\KernelTestBase;
/**
* Tests available user fields in twig.
*
* @group user
*/
class UserFieldsTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
// Set up a test theme that prints the user's mail field.
\Drupal::service('theme_handler')->install(array('user_test_theme'));
\Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->initTheme('user_test_theme'));
// Clear the theme registry.
$this->container->set('theme.registry', NULL);
}
/**
* Tests account's available fields.
*/
function testUserFields() {
// Create the user to test the user fields.
$user = User::create([
'name' => 'foobar',
'mail' => 'foobar@example.com',
]);
$build = user_view($user);
$output = \Drupal::service('renderer')->renderRoot($build);
$this->setRawContent($output);
$userEmail = $user->getEmail();
$this->assertText($userEmail, "User's mail field is found in the twig template");
}
}

View file

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserInstallTest.
*/
namespace Drupal\user\Tests;
use Drupal\simpletest\KernelTestBase;
/**
* Tests user_install().
*
* @group user
*/
class UserInstallTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('user');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container->get('module_handler')->loadInclude('user', 'install');
$this->installEntitySchema('user');
user_install();
}
/**
* Test that the initial users have correct values.
*/
public function testUserInstall() {
$result = db_query('SELECT u.uid, u.uuid, u.langcode, uf.status FROM {users} u INNER JOIN {users_field_data} uf ON u.uid=uf.uid ORDER BY u.uid')
->fetchAllAssoc('uid');
$anon = $result[0];
$admin = $result[1];
$this->assertFalse(empty($anon->uuid), 'Anon user has a UUID');
$this->assertFalse(empty($admin->uuid), 'Admin user has a UUID');
// Test that the anonymous and administrators languages are equal to the
// site's default language.
$this->assertEqual($anon->langcode, \Drupal::languageManager()->getDefaultLanguage()->getId());
$this->assertEqual($admin->langcode, \Drupal::languageManager()->getDefaultLanguage()->getId());
// Test that the administrator is active.
$this->assertEqual($admin->status, 1);
// Test that the anonymous user is blocked.
$this->assertEqual($anon->status, 0);
}
}

Some files were not shown because too many files have changed in this diff Show more