Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -26,7 +26,11 @@ class LoginStatusCheck implements AccessInterface {
|
|||
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']);
|
||||
$access_result = AccessResult::allowedIf($required_status === $actual_status)->addCacheContexts(['user.roles:authenticated']);
|
||||
if (!$access_result->isAllowed()) {
|
||||
$access_result->setReason($required_status === TRUE ? 'This route can only be accessed by authenticated users.' : 'This route can only be accessed by anonymous users.');
|
||||
}
|
||||
return $access_result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use Drupal\Component\Datetime\TimeInterface;
|
|||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Entity\ContentEntityForm;
|
||||
use Drupal\Core\Entity\EntityConstraintViolationListInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
|
@ -31,8 +31,8 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
/**
|
||||
* Constructs a new EntityForm object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
|
||||
* The entity repository.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
|
||||
|
@ -40,8 +40,8 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
* @param \Drupal\Component\Datetime\TimeInterface $time
|
||||
* The time service.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
|
||||
parent::__construct($entity_manager, $entity_type_bundle_info, $time);
|
||||
public function __construct(EntityRepositoryInterface $entity_repository, LanguageManagerInterface $language_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
|
||||
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('entity.repository'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('datetime.time')
|
||||
|
@ -85,7 +85,7 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
'#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')),
|
||||
'#required' => !(!$account->getEmail() && $admin),
|
||||
'#default_value' => (!$register ? $account->getEmail() : ''),
|
||||
];
|
||||
|
||||
|
@ -161,7 +161,7 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
|
||||
// When not building the user registration form, prevent web browsers from
|
||||
// autofilling/prefilling the email, username, and password fields.
|
||||
if ($this->getOperation() != 'register') {
|
||||
if (!$register) {
|
||||
foreach (['mail', 'name', 'pass'] as $key) {
|
||||
if (isset($form['account'][$key])) {
|
||||
$form['account'][$key]['#attributes']['autocomplete'] = 'off';
|
||||
|
@ -222,7 +222,7 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
'#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'),
|
||||
'#access' => !$register || $admin,
|
||||
];
|
||||
|
||||
$form['language']['preferred_langcode'] = [
|
||||
|
@ -347,7 +347,7 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
'timezone',
|
||||
'langcode',
|
||||
'preferred_langcode',
|
||||
'preferred_admin_langcode'
|
||||
'preferred_admin_langcode',
|
||||
], parent::getEditedFieldNames($form_state));
|
||||
}
|
||||
|
||||
|
@ -365,7 +365,7 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
'timezone',
|
||||
'langcode',
|
||||
'preferred_langcode',
|
||||
'preferred_admin_langcode'
|
||||
'preferred_admin_langcode',
|
||||
];
|
||||
foreach ($violations->getByFields($field_names) as $violation) {
|
||||
list($field_name) = explode('.', $violation->getPropertyPath(), 2);
|
||||
|
|
|
@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Configure user settings for this site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class AccountSettingsForm extends ConfigFormBase {
|
||||
|
||||
|
@ -152,13 +154,13 @@ class AccountSettingsForm extends ConfigFormBase {
|
|||
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'] = [
|
||||
'#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.')
|
||||
'#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'] = [
|
||||
'#type' => 'checkbox',
|
||||
|
|
|
@ -4,9 +4,8 @@ namespace Drupal\user\ContextProvider;
|
|||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\Context;
|
||||
use Drupal\Core\Plugin\Context\ContextDefinition;
|
||||
use Drupal\Core\Plugin\Context\ContextProviderInterface;
|
||||
use Drupal\Core\Plugin\Context\EntityContext;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
|
@ -50,7 +49,13 @@ class CurrentUserContext implements ContextProviderInterface {
|
|||
public function getRuntimeContexts(array $unqualified_context_ids) {
|
||||
$current_user = $this->userStorage->load($this->account->id());
|
||||
|
||||
$context = new Context(new ContextDefinition('entity:user', $this->t('Current user')), $current_user);
|
||||
if ($current_user) {
|
||||
// @todo Do not validate protected fields to avoid bug in TypedData,
|
||||
// remove this in https://www.drupal.org/project/drupal/issues/2934192.
|
||||
$current_user->_skipProtectedUserFieldConstraint = TRUE;
|
||||
}
|
||||
|
||||
$context = EntityContext::fromEntity($current_user, $this->t('Current user'));
|
||||
$cacheability = new CacheableMetadata();
|
||||
$cacheability->setCacheContexts(['user']);
|
||||
$context->addCacheableDependency($cacheability);
|
||||
|
|
|
@ -10,6 +10,7 @@ use Drupal\Core\Routing\RouteProviderInterface;
|
|||
use Drupal\user\UserAuthInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
use Drupal\user\UserStorageInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
@ -86,6 +87,13 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
|
|||
*/
|
||||
protected $serializerFormats = [];
|
||||
|
||||
/**
|
||||
* A logger instance.
|
||||
*
|
||||
* @var \Psr\Log\LoggerInterface
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
/**
|
||||
* Constructs a new UserAuthenticationController object.
|
||||
*
|
||||
|
@ -103,8 +111,10 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
|
|||
* The serializer.
|
||||
* @param array $serializer_formats
|
||||
* The available serialization formats.
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* A logger instance.
|
||||
*/
|
||||
public function __construct(FloodInterface $flood, UserStorageInterface $user_storage, CsrfTokenGenerator $csrf_token, UserAuthInterface $user_auth, RouteProviderInterface $route_provider, Serializer $serializer, array $serializer_formats) {
|
||||
public function __construct(FloodInterface $flood, UserStorageInterface $user_storage, CsrfTokenGenerator $csrf_token, UserAuthInterface $user_auth, RouteProviderInterface $route_provider, Serializer $serializer, array $serializer_formats, LoggerInterface $logger) {
|
||||
$this->flood = $flood;
|
||||
$this->userStorage = $user_storage;
|
||||
$this->csrfToken = $csrf_token;
|
||||
|
@ -112,6 +122,7 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
|
|||
$this->serializer = $serializer;
|
||||
$this->serializerFormats = $serializer_formats;
|
||||
$this->routeProvider = $route_provider;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,7 +146,8 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
|
|||
$container->get('user.auth'),
|
||||
$container->get('router.route_provider'),
|
||||
$serializer,
|
||||
$formats
|
||||
$formats,
|
||||
$container->get('logger.factory')->get('user')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -207,6 +219,56 @@ class UserAuthenticationController extends ControllerBase implements ContainerIn
|
|||
throw new BadRequestHttpException('Sorry, unrecognized username or password.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets a user password.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* The response object.
|
||||
*/
|
||||
public function resetPassword(Request $request) {
|
||||
$format = $this->getRequestFormat($request);
|
||||
|
||||
$content = $request->getContent();
|
||||
$credentials = $this->serializer->decode($content, $format);
|
||||
|
||||
// Check if a name or mail is provided.
|
||||
if (!isset($credentials['name']) && !isset($credentials['mail'])) {
|
||||
throw new BadRequestHttpException('Missing credentials.name or credentials.mail');
|
||||
}
|
||||
|
||||
// Load by name if provided.
|
||||
if (isset($credentials['name'])) {
|
||||
$users = $this->userStorage->loadByProperties(['name' => trim($credentials['name'])]);
|
||||
}
|
||||
elseif (isset($credentials['mail'])) {
|
||||
$users = $this->userStorage->loadByProperties(['mail' => trim($credentials['mail'])]);
|
||||
}
|
||||
|
||||
/** @var \Drupal\Core\Session\AccountInterface $account */
|
||||
$account = reset($users);
|
||||
if ($account && $account->id()) {
|
||||
if ($this->userIsBlocked($account->getAccountName())) {
|
||||
throw new BadRequestHttpException('The user has not been activated or is blocked.');
|
||||
}
|
||||
|
||||
// Send the password reset email.
|
||||
$mail = _user_mail_notify('password_reset', $account, $account->getPreferredLangcode());
|
||||
if (empty($mail)) {
|
||||
throw new BadRequestHttpException('Unable to send email. Contact the site administrator if the problem persists.');
|
||||
}
|
||||
else {
|
||||
$this->logger->notice('Password reset instructions mailed to %name at %email.', ['%name' => $account->getAccountName(), '%email' => $account->getEmail()]);
|
||||
return new Response();
|
||||
}
|
||||
}
|
||||
|
||||
// Error if no users found with provided name or mail.
|
||||
throw new BadRequestHttpException('Unrecognized username or email address.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the user is blocked.
|
||||
*
|
||||
|
|
|
@ -120,12 +120,17 @@ class UserController extends ControllerBase {
|
|||
else {
|
||||
/** @var \Drupal\user\UserInterface $reset_link_user */
|
||||
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">log out</a> and try using the link again.',
|
||||
['%other_user' => $account->getUsername(), '%resetting_user' => $reset_link_user->getUsername(), ':logout' => $this->url('user.logout')]), 'warning');
|
||||
$this->messenger()
|
||||
->addWarning($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">log out</a> and try using the link again.',
|
||||
[
|
||||
'%other_user' => $account->getUsername(),
|
||||
'%resetting_user' => $reset_link_user->getUsername(),
|
||||
':logout' => $this->url('user.logout'),
|
||||
]));
|
||||
}
|
||||
else {
|
||||
// Invalid one-time link specifies an unknown user.
|
||||
drupal_set_message($this->t('The one-time login link you clicked is invalid.'), 'error');
|
||||
$this->messenger()->addError($this->t('The one-time login link you clicked is invalid.'));
|
||||
}
|
||||
return $this->redirect('<front>');
|
||||
}
|
||||
|
@ -218,13 +223,13 @@ class UserController extends ControllerBase {
|
|||
$timeout = $this->config('user.settings')->get('password_reset_timeout');
|
||||
// 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');
|
||||
$this->messenger()->addError($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
|
||||
return $this->redirect('user.pass');
|
||||
}
|
||||
elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && Crypt::hashEquals($hash, user_pass_rehash($user, $timestamp))) {
|
||||
user_login_finalize($user);
|
||||
$this->logger->notice('User %name used one-time login link at time %timestamp.', ['%name' => $user->getDisplayName(), '%timestamp' => $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.'));
|
||||
$this->messenger()->addStatus($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);
|
||||
|
@ -239,7 +244,7 @@ class UserController extends ControllerBase {
|
|||
);
|
||||
}
|
||||
|
||||
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');
|
||||
$this->messenger()->addError($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.'));
|
||||
return $this->redirect('user.pass');
|
||||
}
|
||||
|
||||
|
@ -268,7 +273,7 @@ class UserController extends ControllerBase {
|
|||
* NULL.
|
||||
*/
|
||||
public function userTitle(UserInterface $user = NULL) {
|
||||
return $user ? ['#markup' => $user->getUsername(), '#allowed_tags' => Xss::getHtmlTagList()] : '';
|
||||
return $user ? ['#markup' => $user->getDisplayName(), '#allowed_tags' => Xss::getHtmlTagList()] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -312,10 +317,10 @@ class UserController extends ControllerBase {
|
|||
// 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('');
|
||||
return batch_process('<front>');
|
||||
}
|
||||
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.'), 'error');
|
||||
$this->messenger()->addError($this->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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,13 @@ use Drupal\user\RoleInterface;
|
|||
* @ConfigEntityType(
|
||||
* id = "user_role",
|
||||
* label = @Translation("Role"),
|
||||
* label_collection = @Translation("Roles"),
|
||||
* label_singular = @Translation("role"),
|
||||
* label_plural = @Translation("roles"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count role",
|
||||
* plural = "@count roles",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\user\RoleStorage",
|
||||
* "access" = "Drupal\user\RoleAccessControlHandler",
|
||||
|
@ -173,7 +180,7 @@ class Role extends ConfigEntityBase implements RoleInterface {
|
|||
|
||||
if (!isset($this->weight) && ($roles = $storage->loadMultiple())) {
|
||||
// Set a role weight to make this new role last.
|
||||
$max = array_reduce($roles, function($max, $role) {
|
||||
$max = array_reduce($roles, function ($max, $role) {
|
||||
return $max > $role->weight ? $max : $role->weight;
|
||||
});
|
||||
$this->weight = $max + 1;
|
||||
|
|
|
@ -9,6 +9,8 @@ use Drupal\Core\Entity\EntityTypeInterface;
|
|||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\user\StatusItem;
|
||||
use Drupal\user\TimeZoneItem;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
|
@ -20,6 +22,13 @@ use Drupal\user\UserInterface;
|
|||
* @ContentEntityType(
|
||||
* id = "user",
|
||||
* label = @Translation("User"),
|
||||
* label_collection = @Translation("Users"),
|
||||
* label_singular = @Translation("user"),
|
||||
* label_plural = @Translation("users"),
|
||||
* label_count = @PluralTranslation(
|
||||
* singular = "@count user",
|
||||
* plural = "@count users",
|
||||
* ),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\user\UserStorage",
|
||||
* "storage_schema" = "Drupal\user\UserStorageSchema",
|
||||
|
@ -346,6 +355,7 @@ class User extends ContentEntityBase implements UserInterface {
|
|||
public function isAuthenticated() {
|
||||
return $this->id() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -418,7 +428,7 @@ class User extends ContentEntityBase implements UserInterface {
|
|||
'name' => [LanguageInterface::LANGCODE_DEFAULT => ''],
|
||||
// Explicitly set the langcode to ensure that field definitions do not
|
||||
// need to be fetched to figure out a default.
|
||||
'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED]
|
||||
'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED],
|
||||
], $entity_type->id());
|
||||
}
|
||||
return clone static::$anonymousUser;
|
||||
|
@ -498,11 +508,13 @@ class User extends ContentEntityBase implements UserInterface {
|
|||
->addPropertyConstraints('value', [
|
||||
'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedTimezones'],
|
||||
]);
|
||||
$fields['timezone']->getItemDefinition()->setClass(TimeZoneItem::class);
|
||||
|
||||
$fields['status'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('User status'))
|
||||
->setDescription(t('Whether the user is active or blocked.'))
|
||||
->setDefaultValue(FALSE);
|
||||
$fields['status']->getItemDefinition()->setClass(StatusItem::class);
|
||||
|
||||
$fields['created'] = BaseFieldDefinition::create('created')
|
||||
->setLabel(t('Created'))
|
||||
|
|
|
@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Provides a confirmation form for cancelling user account.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UserCancelForm extends ContentEntityConfirmFormBase {
|
||||
|
||||
|
@ -137,7 +139,7 @@ class UserCancelForm extends ContentEntityConfirmFormBase {
|
|||
$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->messenger()->addStatus($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.', ['%name' => $this->entity->label(), '%email' => '<' . $this->entity->getEmail() . '>']);
|
||||
|
||||
$form_state->setRedirect(
|
||||
|
|
|
@ -12,6 +12,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Provides a user login form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UserLoginForm extends FormBase {
|
||||
|
||||
|
|
|
@ -5,20 +5,23 @@ namespace Drupal\user\Form;
|
|||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\user\PrivateTempStoreFactory;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Drupal\user\UserStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a confirmation form for cancelling multiple user accounts.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UserMultipleCancelConfirm extends ConfirmFormBase {
|
||||
|
||||
/**
|
||||
* The temp store factory.
|
||||
*
|
||||
* @var \Drupal\user\PrivateTempStoreFactory
|
||||
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
|
@ -39,7 +42,7 @@ class UserMultipleCancelConfirm extends ConfirmFormBase {
|
|||
/**
|
||||
* Constructs a new UserMultipleCancelConfirm.
|
||||
*
|
||||
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The temp store factory.
|
||||
* @param \Drupal\user\UserStorageInterface $user_storage
|
||||
* The user storage.
|
||||
|
@ -57,7 +60,7 @@ class UserMultipleCancelConfirm extends ConfirmFormBase {
|
|||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('user.private_tempstore'),
|
||||
$container->get('tempstore.private'),
|
||||
$container->get('entity.manager')->getStorage('user'),
|
||||
$container->get('entity.manager')
|
||||
);
|
||||
|
@ -130,7 +133,7 @@ class UserMultipleCancelConfirm extends ConfirmFormBase {
|
|||
if (isset($root)) {
|
||||
$redirect = (count($accounts) == 1);
|
||||
$message = $this->t('The user account %name cannot be canceled.', ['%name' => $root->label()]);
|
||||
drupal_set_message($message, $redirect ? 'error' : 'warning');
|
||||
$this->messenger()->addMessage($message, $redirect ? MessengerInterface::TYPE_ERROR : MessengerInterface::TYPE_WARNING);
|
||||
// If only user 1 was selected, redirect to the overview.
|
||||
if ($redirect) {
|
||||
return $this->redirect('entity.user.collection');
|
||||
|
|
|
@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Provides a user password reset form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UserPasswordForm extends FormBase {
|
||||
|
||||
|
@ -60,9 +62,6 @@ class UserPasswordForm extends FormBase {
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\Request $request
|
||||
* The request object.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['name'] = [
|
||||
|
@ -141,7 +140,7 @@ class UserPasswordForm extends FormBase {
|
|||
$mail = _user_mail_notify('password_reset', $account, $langcode);
|
||||
if (!empty($mail)) {
|
||||
$this->logger('user')->notice('Password reset instructions mailed to %name at %email.', ['%name' => $account->getUsername(), '%email' => $account->getEmail()]);
|
||||
drupal_set_message($this->t('Further instructions have been sent to your email address.'));
|
||||
$this->messenger()->addStatus($this->t('Further instructions have been sent to your email address.'));
|
||||
}
|
||||
|
||||
$form_state->setRedirect('user.page');
|
||||
|
|
|
@ -9,6 +9,8 @@ use Drupal\Core\Url;
|
|||
|
||||
/**
|
||||
* Form controller for the user password forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UserPasswordResetForm extends FormBase {
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
|
||||
/**
|
||||
* Provides the user permissions administration form.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UserPermissionsForm extends FormBase {
|
||||
|
||||
|
@ -127,16 +129,33 @@ class UserPermissionsForm extends FormBase {
|
|||
$permissions_by_provider[$permission['provider']][$permission_name] = $permission;
|
||||
}
|
||||
|
||||
// Move the access content permission to the Node module if it is installed.
|
||||
if ($this->moduleHandler->moduleExists('node')) {
|
||||
// Insert 'access content' before the 'view own unpublished content' key
|
||||
// in order to maintain the UI even though the permission is provided by
|
||||
// the system module.
|
||||
$keys = array_keys($permissions_by_provider['node']);
|
||||
$offset = (int) array_search('view own unpublished content', $keys);
|
||||
$permissions_by_provider['node'] = array_merge(
|
||||
array_slice($permissions_by_provider['node'], 0, $offset),
|
||||
['access content' => $permissions_by_provider['system']['access content']],
|
||||
array_slice($permissions_by_provider['node'], $offset)
|
||||
);
|
||||
unset($permissions_by_provider['system']['access content']);
|
||||
}
|
||||
|
||||
foreach ($permissions_by_provider as $provider => $permissions) {
|
||||
// Module name.
|
||||
$form['permissions'][$provider] = [[
|
||||
'#wrapper_attributes' => [
|
||||
'colspan' => count($role_names) + 1,
|
||||
'class' => ['module'],
|
||||
'id' => 'module-' . $provider,
|
||||
$form['permissions'][$provider] = [
|
||||
[
|
||||
'#wrapper_attributes' => [
|
||||
'colspan' => count($role_names) + 1,
|
||||
'class' => ['module'],
|
||||
'id' => 'module-' . $provider,
|
||||
],
|
||||
'#markup' => $this->moduleHandler->getName($provider),
|
||||
],
|
||||
'#markup' => $this->moduleHandler->getName($provider),
|
||||
]];
|
||||
];
|
||||
foreach ($permissions as $perm => $perm_item) {
|
||||
// Fill in default values for the permission.
|
||||
$perm_item += [
|
||||
|
@ -197,7 +216,7 @@ class UserPermissionsForm extends FormBase {
|
|||
user_role_change_permissions($role_name, (array) $form_state->getValue($role_name));
|
||||
}
|
||||
|
||||
drupal_set_message($this->t('The changes have been saved.'));
|
||||
$this->messenger()->addStatus($this->t('The changes have been saved.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ use Drupal\user\RoleInterface;
|
|||
|
||||
/**
|
||||
* Provides the user permissions administration form for a specific role.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UserPermissionsRoleSpecificForm extends UserPermissionsForm {
|
||||
|
||||
|
@ -25,10 +27,14 @@ class UserPermissionsRoleSpecificForm extends UserPermissionsForm {
|
|||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Builds the user permissions administration form for a specific role.
|
||||
*
|
||||
* @param string $role_id
|
||||
* The user role ID used for this form.
|
||||
* @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\user\RoleInterface|null $user_role
|
||||
* (optional) The user role used for this form. Defaults to NULL.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, RoleInterface $user_role = NULL) {
|
||||
$this->userRole = $user_role;
|
||||
|
|
|
@ -5,7 +5,7 @@ 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 Drupal\Core\TempStore\PrivateTempStoreFactory;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,7 @@ class CancelUser extends ActionBase implements ContainerFactoryPluginInterface {
|
|||
/**
|
||||
* The tempstore factory.
|
||||
*
|
||||
* @var \Drupal\user\PrivateTempStoreFactory
|
||||
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
|
||||
*/
|
||||
protected $tempStoreFactory;
|
||||
|
||||
|
@ -35,7 +35,7 @@ class CancelUser extends ActionBase implements ContainerFactoryPluginInterface {
|
|||
protected $currentUser;
|
||||
|
||||
/**
|
||||
* Constructs a DeleteNode object.
|
||||
* Constructs a CancelUser object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
|
@ -43,9 +43,9 @@ class CancelUser extends ActionBase implements ContainerFactoryPluginInterface {
|
|||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
|
||||
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
|
||||
* The tempstore factory.
|
||||
* @param AccountInterface $current_user
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* Current user.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
|
||||
|
@ -63,7 +63,7 @@ class CancelUser extends ActionBase implements ContainerFactoryPluginInterface {
|
|||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('user.private_tempstore'),
|
||||
$container->get('tempstore.private'),
|
||||
$container->get('current_user')
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ 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;
|
||||
|
@ -23,7 +22,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
*/
|
||||
class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
|
||||
use UrlGeneratorTrait;
|
||||
use RedirectDestinationTrait;
|
||||
|
||||
/**
|
||||
|
@ -66,7 +64,6 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -94,7 +91,24 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac
|
|||
unset($form['pass']['#attributes']['aria-describedby']);
|
||||
$form['name']['#size'] = 15;
|
||||
$form['pass']['#size'] = 15;
|
||||
$form['#action'] = $this->url('<current>', [], ['query' => $this->getDestinationArray(), 'external' => FALSE]);
|
||||
|
||||
// Instead of setting an actual action URL, we set the placeholder, which
|
||||
// will be replaced at the very last moment. This ensures forms with
|
||||
// dynamically generated action URLs don't have poor cacheability.
|
||||
// Use the proper API to generate the placeholder, when we have one. See
|
||||
// https://www.drupal.org/node/2562341. The placeholder uses a fixed string
|
||||
// that is
|
||||
// Crypt::hashBase64('\Drupal\user\Plugin\Block\UserLoginBlock::build');
|
||||
// This is based on the implementation in
|
||||
// \Drupal\Core\Form\FormBuilder::prepareForm(), but the user login block
|
||||
// requires different behavior for the destination query argument.
|
||||
$placeholder = 'form_action_p_4r8ITd22yaUvXM6SzwrSe9rnQWe48hz9k1Sxto3pBvE';
|
||||
|
||||
$form['#attached']['placeholders'][$placeholder] = [
|
||||
'#lazy_builder' => ['\Drupal\user\Plugin\Block\UserLoginBlock::renderPlaceholderFormAction', []],
|
||||
];
|
||||
$form['#action'] = $placeholder;
|
||||
|
||||
// Build action links.
|
||||
$items = [];
|
||||
if (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) {
|
||||
|
@ -128,4 +142,20 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; renders a form action URL including destination.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array representing the form action.
|
||||
*
|
||||
* @see \Drupal\Core\Form\FormBuilder::renderPlaceholderFormAction()
|
||||
*/
|
||||
public static function renderPlaceholderFormAction() {
|
||||
return [
|
||||
'#type' => 'markup',
|
||||
'#markup' => Url::fromRoute('<current>', [], ['query' => \Drupal::destination()->getAsArray(), 'external' => FALSE])->toString(),
|
||||
'#cache' => ['contexts' => ['url.path', 'url.query_args']],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\user\Plugin\EntityReferenceSelection;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
|
||||
|
@ -82,21 +83,26 @@ class UserSelection extends DefaultSelection {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$selection_handler_settings = $this->configuration['handler_settings'];
|
||||
|
||||
// Merge in default values.
|
||||
$selection_handler_settings += [
|
||||
public function defaultConfiguration() {
|
||||
return [
|
||||
'filter' => [
|
||||
'type' => '_none',
|
||||
'role' => NULL,
|
||||
],
|
||||
'include_anonymous' => TRUE,
|
||||
];
|
||||
] + parent::defaultConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
$form['include_anonymous'] = [
|
||||
'#type' => 'checkbox',
|
||||
'#title' => $this->t('Include the anonymous user.'),
|
||||
'#default_value' => $selection_handler_settings['include_anonymous'],
|
||||
'#default_value' => $configuration['include_anonymous'],
|
||||
];
|
||||
|
||||
// Add user specific filter options.
|
||||
|
@ -109,7 +115,7 @@ class UserSelection extends DefaultSelection {
|
|||
],
|
||||
'#ajax' => TRUE,
|
||||
'#limit_validation_errors' => [],
|
||||
'#default_value' => $selection_handler_settings['filter']['type'],
|
||||
'#default_value' => $configuration['filter']['type'],
|
||||
];
|
||||
|
||||
$form['filter']['settings'] = [
|
||||
|
@ -118,18 +124,13 @@ class UserSelection extends DefaultSelection {
|
|||
'#process' => [['\Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem', 'formProcessMergeParent']],
|
||||
];
|
||||
|
||||
if ($selection_handler_settings['filter']['type'] == 'role') {
|
||||
// Merge in default values.
|
||||
$selection_handler_settings['filter'] += [
|
||||
'role' => NULL,
|
||||
];
|
||||
|
||||
if ($configuration['filter']['type'] == 'role') {
|
||||
$form['filter']['settings']['role'] = [
|
||||
'#type' => 'checkboxes',
|
||||
'#title' => $this->t('Restrict to the selected roles'),
|
||||
'#required' => TRUE,
|
||||
'#options' => array_diff_key(user_role_names(TRUE), [RoleInterface::AUTHENTICATED_ID => RoleInterface::AUTHENTICATED_ID]),
|
||||
'#default_value' => $selection_handler_settings['filter']['role'],
|
||||
'#default_value' => $configuration['filter']['role'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -143,11 +144,12 @@ class UserSelection extends DefaultSelection {
|
|||
*/
|
||||
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
|
||||
$query = parent::buildEntityQuery($match, $match_operator);
|
||||
$handler_settings = $this->configuration['handler_settings'];
|
||||
|
||||
$configuration = $this->getConfiguration();
|
||||
|
||||
// Filter out the Anonymous user if the selection handler is configured to
|
||||
// exclude it.
|
||||
if (isset($handler_settings['include_anonymous']) && !$handler_settings['include_anonymous']) {
|
||||
if (!$configuration['include_anonymous']) {
|
||||
$query->condition('uid', 0, '<>');
|
||||
}
|
||||
|
||||
|
@ -157,8 +159,8 @@ class UserSelection extends DefaultSelection {
|
|||
}
|
||||
|
||||
// Filter by role.
|
||||
if (!empty($handler_settings['filter']['role'])) {
|
||||
$query->condition('roles', $handler_settings['filter']['role'], 'IN');
|
||||
if (!empty($configuration['filter']['role'])) {
|
||||
$query->condition('roles', $configuration['filter']['role'], 'IN');
|
||||
}
|
||||
|
||||
// Adding the permission check is sadly insufficient for users: core
|
||||
|
@ -190,10 +192,10 @@ class UserSelection extends DefaultSelection {
|
|||
public function validateReferenceableNewEntities(array $entities) {
|
||||
$entities = parent::validateReferenceableNewEntities($entities);
|
||||
// Mirror the conditions checked in buildEntityQuery().
|
||||
if (!empty($this->configuration['handler_settings']['filter']['role'])) {
|
||||
$entities = array_filter($entities, function ($user) {
|
||||
if ($role = $this->getConfiguration()['filter']['role']) {
|
||||
$entities = array_filter($entities, function ($user) use ($role) {
|
||||
/** @var \Drupal\user\UserInterface $user */
|
||||
return !empty(array_intersect($user->getRoles(), $this->configuration['handler_settings']['filter']['role']));
|
||||
return !empty(array_intersect($user->getRoles(), $role));
|
||||
});
|
||||
}
|
||||
if (!$this->currentUser->hasPermission('administer users')) {
|
||||
|
@ -209,9 +211,10 @@ class UserSelection extends DefaultSelection {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function entityQueryAlter(SelectInterface $query) {
|
||||
parent::entityQueryAlter($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']) {
|
||||
if (!$this->getConfiguration()['include_anonymous']) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -228,18 +231,18 @@ class UserSelection extends DefaultSelection {
|
|||
// 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 = new Condition('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 = new Condition('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() + [':anonymous_name' => \Drupal::config('user.settings')->get('anonymous')])
|
||||
$or->condition((new Condition('AND'))
|
||||
->where(str_replace($query->escapeField('anonymous_name'), ':anonymous_name', (string) $value_part), $value_part->arguments() + [':anonymous_name' => \Drupal::config('user.settings')->get('anonymous')])
|
||||
->condition('base_table.uid', 0)
|
||||
);
|
||||
$query->condition($or);
|
||||
|
|
|
@ -30,10 +30,9 @@ class LanguageNegotiationUser extends LanguageNegotiationMethodBase {
|
|||
|
||||
// User preference (only for authenticated users).
|
||||
if ($this->languageManager && $this->currentUser->isAuthenticated()) {
|
||||
$preferred_langcode = $this->currentUser->getPreferredLangcode();
|
||||
$default_langcode = $this->languageManager->getDefaultLanguage()->getId();
|
||||
$preferred_langcode = $this->currentUser->getPreferredLangcode(FALSE);
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
if (!empty($preferred_langcode) && $preferred_langcode != $default_langcode && isset($languages[$preferred_langcode])) {
|
||||
if (!empty($preferred_langcode) && isset($languages[$preferred_langcode])) {
|
||||
$langcode = $preferred_langcode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,10 +127,14 @@ class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase impleme
|
|||
$route_match = $this->stackedRouteMatch->getRouteMatchFromRequest($request);
|
||||
if ($route_match && !$route_object = $route_match->getRouteObject()) {
|
||||
try {
|
||||
// Some inbound path processors make changes to the request. Make a
|
||||
// copy as we're not actually routing the request so we do not want to
|
||||
// make changes.
|
||||
$cloned_request = clone $request;
|
||||
// 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);
|
||||
$path = $this->pathProcessorManager->processInbound(urldecode(rtrim($cloned_request->getPathInfo(), '/')), $cloned_request);
|
||||
$attributes = $this->router->match($path);
|
||||
}
|
||||
catch (ResourceNotFoundException $e) {
|
||||
|
|
|
@ -52,7 +52,7 @@ class UserSearch extends SearchPluginBase implements AccessibleInterface {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$container->get('database'),
|
||||
$container->get('entity.manager'),
|
||||
|
@ -67,11 +67,11 @@ class UserSearch extends SearchPluginBase implements AccessibleInterface {
|
|||
/**
|
||||
* Creates a UserSearch object.
|
||||
*
|
||||
* @param Connection $database
|
||||
* @param \Drupal\Core\Database\Connection $database
|
||||
* The database connection.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param ModuleHandlerInterface $module_handler
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\Session\AccountInterface $current_user
|
||||
* The current user.
|
||||
|
@ -162,13 +162,15 @@ class UserSearch extends SearchPluginBase implements AccessibleInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHelp() {
|
||||
$help = ['list' => [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => [
|
||||
$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.'),
|
||||
$help = [
|
||||
'list' => [
|
||||
'#theme' => 'item_list',
|
||||
'#items' => [
|
||||
$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;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Drupal\user\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
|
@ -53,7 +52,7 @@ class UserNameConstraintValidator extends ConstraintValidator {
|
|||
) {
|
||||
$this->context->addViolation($constraint->illegalMessage);
|
||||
}
|
||||
if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
|
||||
if (mb_strlen($name) > USERNAME_MAX_LENGTH) {
|
||||
$this->context->addViolation($constraint->tooLongMessage, ['%name' => $name, '%max' => USERNAME_MAX_LENGTH]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
namespace Drupal\user\Plugin\migrate;
|
||||
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\Plugin\Migration;
|
||||
|
||||
/**
|
||||
|
@ -28,13 +30,31 @@ class ProfileValues extends Migration {
|
|||
'ignore_map' => TRUE,
|
||||
] + $this->source;
|
||||
$definition['destination']['plugin'] = 'null';
|
||||
$definition['idMap']['plugin'] = 'null';
|
||||
try {
|
||||
$profile_field_migration = $this->migrationPluginManager->createStubMigration($definition);
|
||||
$migrate_executable = new MigrateExecutable($profile_field_migration);
|
||||
$source_plugin = $profile_field_migration->getSourcePlugin();
|
||||
$source_plugin->checkRequirements();
|
||||
foreach ($source_plugin as $row) {
|
||||
$name = $row->getSourceProperty('name');
|
||||
$this->process[$name] = $name;
|
||||
$fid = $row->getSourceProperty('fid');
|
||||
// The user profile field name can be greater than 32 characters. Use
|
||||
// the migrated profile field name in the process pipeline.
|
||||
$configuration =
|
||||
[
|
||||
'migration' => 'user_profile_field',
|
||||
'source_ids' => $fid,
|
||||
];
|
||||
$plugin = $this->processPluginManager->createInstance('migration_lookup', $configuration, $profile_field_migration);
|
||||
$new_value = $plugin->transform($fid, $migrate_executable, $row, 'tmp');
|
||||
if (isset($new_value[1])) {
|
||||
// Set the destination to the migrated profile field name.
|
||||
$this->process[$new_value[1]] = $name;
|
||||
}
|
||||
else {
|
||||
throw new MigrateSkipRowException("Can't migrate source field $name.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (RequirementsException $e) {
|
||||
|
|
|
@ -21,6 +21,7 @@ class User extends FieldMigration {
|
|||
'ignore_map' => TRUE,
|
||||
] + $this->source;
|
||||
$definition['destination']['plugin'] = 'null';
|
||||
$definition['idMap']['plugin'] = 'null';
|
||||
if (\Drupal::moduleHandler()->moduleExists('field')) {
|
||||
$definition['source']['plugin'] = 'd7_field_instance';
|
||||
$field_migration = $this->migrationPluginManager->createStubMigration($definition);
|
||||
|
@ -36,7 +37,7 @@ class User extends FieldMigration {
|
|||
}
|
||||
$info = $row->getSource();
|
||||
$this->fieldPluginCache[$field_type]
|
||||
->processFieldValues($this, $field_name, $info);
|
||||
->defineValueProcessPipeline($this, $field_name, $info);
|
||||
}
|
||||
else {
|
||||
if ($this->cckPluginManager->hasDefinition($field_type)) {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Drupal\user\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
|
@ -15,6 +14,52 @@ use Drupal\migrate\Row;
|
|||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a destination plugin for migrating user entities.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* The example below migrates users and preserves original passwords from a
|
||||
* source that has passwords as MD5 hashes without salt. The passwords will be
|
||||
* salted and re-hashed before they are saved to the destination Drupal
|
||||
* database. The MD5 hash used in the example is a hash of 'password'.
|
||||
*
|
||||
* The example uses the EmbeddedDataSource source plugin for the sake of
|
||||
* simplicity. The mapping between old user_ids and new Drupal uids is saved in
|
||||
* the migration map table.
|
||||
* @code
|
||||
* id: custom_user_migration
|
||||
* label: Custom user migration
|
||||
* source:
|
||||
* plugin: embedded_data
|
||||
* data_rows:
|
||||
* -
|
||||
* user_id: 1
|
||||
* name: johnsmith
|
||||
* mail: johnsmith@example.com
|
||||
* hash: '5f4dcc3b5aa765d61d8327deb882cf99'
|
||||
* ids:
|
||||
* user_id:
|
||||
* type: integer
|
||||
* process:
|
||||
* name: name
|
||||
* mail: mail
|
||||
* pass: hash
|
||||
* status:
|
||||
* plugin: default_value
|
||||
* default_value: 1
|
||||
* destination:
|
||||
* plugin: entity:user
|
||||
* md5_passwords: true
|
||||
* @endcode
|
||||
*
|
||||
* For configuration options inherited from the parent class, refer to
|
||||
* \Drupal\migrate\Plugin\migrate\destination\EntityContentBase.
|
||||
*
|
||||
* The example above is about migrating an MD5 password hash. For more examples
|
||||
* on different password hash types and a list of other user properties, refer
|
||||
* to the handbook documentation:
|
||||
* @see https://www.drupal.org/docs/8/api/migrate-api/migrate-destination-plugins-examples/migrating-users
|
||||
*
|
||||
* @MigrateDestination(
|
||||
* id = "entity:user"
|
||||
* )
|
||||
|
@ -39,7 +84,7 @@ class EntityUser extends EntityContentBase {
|
|||
* The plugin implementation definition.
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration.
|
||||
* @param EntityStorageInterface $storage
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The storage for this entity type.
|
||||
* @param array $bundles
|
||||
* The list of bundles this entity type has.
|
||||
|
@ -122,9 +167,23 @@ class EntityUser extends EntityContentBase {
|
|||
if (is_array($name)) {
|
||||
$name = reset($name);
|
||||
}
|
||||
if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
|
||||
$row->setDestinationProperty('name', Unicode::substr($name, 0, USERNAME_MAX_LENGTH));
|
||||
if (mb_strlen($name) > USERNAME_MAX_LENGTH) {
|
||||
$row->setDestinationProperty('name', mb_substr($name, 0, USERNAME_MAX_LENGTH));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHighestId() {
|
||||
$highest_id = parent::getHighestId();
|
||||
|
||||
// Every Drupal site must have a user with UID of 1 and it's normal for
|
||||
// migrations to overwrite this user.
|
||||
if ($highest_id === 1) {
|
||||
return 0;
|
||||
}
|
||||
return $highest_id;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class UserLangcode extends ProcessPluginBase implements ContainerFactoryPluginIn
|
|||
* @param string $plugin_id
|
||||
* The plugin ID.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin definiiton.
|
||||
* The plugin definition.
|
||||
* @param \Drupal\Core\Language\LanguageManager $language_manager
|
||||
* The language manager service.
|
||||
*/
|
||||
|
@ -74,7 +74,7 @@ class UserLangcode extends ProcessPluginBase implements ContainerFactoryPluginIn
|
|||
return 'en';
|
||||
}
|
||||
}
|
||||
// If the user's language does not exists, use the default language.
|
||||
// If the user's language does not exist, use the default language.
|
||||
elseif ($this->languageManager->getLanguage($value) === NULL) {
|
||||
return $this->languageManager->getDefaultLanguage()->getId();
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ class UserUpdate7002 extends ProcessPluginBase implements ContainerFactoryPlugin
|
|||
$container->get('config.factory')->get('system.date')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,7 @@ use Drupal\migrate\Row;
|
|||
*
|
||||
* @MigrateSource(
|
||||
* id = "profile_field",
|
||||
* source_provider = "profile"
|
||||
* source_module = "profile"
|
||||
* )
|
||||
*/
|
||||
class ProfileField extends DrupalSqlBase {
|
||||
|
|
|
@ -11,7 +11,8 @@ use Drupal\migrate\Plugin\migrate\source\DummyQueryTrait;
|
|||
* @todo Support default picture?
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "user_picture_instance"
|
||||
* id = "user_picture_instance",
|
||||
* source_module = "user"
|
||||
* )
|
||||
*/
|
||||
class UserPictureInstance extends DrupalSqlBase {
|
||||
|
@ -28,7 +29,8 @@ class UserPictureInstance extends DrupalSqlBase {
|
|||
'file_directory' => $this->variableGet('user_picture_path', 'pictures'),
|
||||
'max_filesize' => $this->variableGet('user_picture_file_size', '30') . 'KB',
|
||||
'max_resolution' => $this->variableGet('user_picture_dimensions', '85x85'),
|
||||
]]);
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
|||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_profile_field_values",
|
||||
* source_provider = "profile"
|
||||
* source_module = "profile"
|
||||
* )
|
||||
*/
|
||||
class ProfileFieldValues extends DrupalSqlBase {
|
||||
|
|
|
@ -9,7 +9,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
|||
* Drupal 6 role source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_user_role"
|
||||
* id = "d6_user_role",
|
||||
* source_module = "user"
|
||||
* )
|
||||
*/
|
||||
class Role extends DrupalSqlBase {
|
||||
|
|
|
@ -9,7 +9,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
|||
* Drupal 6 user source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_user"
|
||||
* id = "d6_user",
|
||||
* source_module = "user"
|
||||
* )
|
||||
*/
|
||||
class User extends DrupalSqlBase {
|
||||
|
@ -32,14 +33,6 @@ class User extends DrupalSqlBase {
|
|||
// Add roles field.
|
||||
$fields['roles'] = $this->t('Roles');
|
||||
|
||||
// Profile fields.
|
||||
if ($this->moduleExists('profile')) {
|
||||
$fields += $this->select('profile_fields', 'pf')
|
||||
->fields('pf', ['name', 'title'])
|
||||
->execute()
|
||||
->fetchAllKeyed();
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
|||
* @todo Support default picture?
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_user_picture"
|
||||
* id = "d6_user_picture",
|
||||
* source_module = "user"
|
||||
* )
|
||||
*/
|
||||
class UserPicture extends DrupalSqlBase {
|
||||
|
@ -36,6 +37,7 @@ class UserPicture extends DrupalSqlBase {
|
|||
'picture' => "Path to the user's uploaded picture.",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,8 @@ use Drupal\migrate\Row;
|
|||
* Drupal 6 user picture source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_user_picture_file"
|
||||
* id = "d6_user_picture_file",
|
||||
* source_module = "user"
|
||||
* )
|
||||
*/
|
||||
class UserPictureFile extends DrupalSqlBase {
|
||||
|
@ -67,6 +68,7 @@ class UserPictureFile extends DrupalSqlBase {
|
|||
'filename' => 'The picture filename.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
|||
* Drupal 7 role source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_user_role"
|
||||
* id = "d7_user_role",
|
||||
* source_module = "user"
|
||||
* )
|
||||
*/
|
||||
class Role extends DrupalSqlBase {
|
||||
|
|
|
@ -9,7 +9,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
|
|||
* Drupal 7 user source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_user"
|
||||
* id = "d7_user",
|
||||
* source_module = "user"
|
||||
* )
|
||||
*/
|
||||
class User extends FieldableEntity {
|
||||
|
@ -61,18 +62,32 @@ class User extends FieldableEntity {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$uid = $row->getSourceProperty('uid');
|
||||
|
||||
$roles = $this->select('users_roles', 'ur')
|
||||
->fields('ur', ['rid'])
|
||||
->condition('ur.uid', $row->getSourceProperty('uid'))
|
||||
->condition('ur.uid', $uid)
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$row->setSourceProperty('roles', $roles);
|
||||
|
||||
$row->setSourceProperty('data', unserialize($row->getSourceProperty('data')));
|
||||
|
||||
// If this entity was translated using Entity Translation, we need to get
|
||||
// its source language to get the field values in the right language.
|
||||
// The translations will be migrated by the d7_user_entity_translation
|
||||
// migration.
|
||||
$entity_translatable = $this->isEntityTranslatable('user');
|
||||
$source_language = $this->getEntityTranslationSourceLanguage('user', $uid);
|
||||
$language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language');
|
||||
$row->setSourceProperty('entity_language', $language);
|
||||
|
||||
// Get Field API field values.
|
||||
foreach (array_keys($this->getFields('user')) as $field) {
|
||||
$row->setSourceProperty($field, $this->getFieldValues('user', $field, $row->getSourceProperty('uid')));
|
||||
foreach ($this->getFields('user') as $field_name => $field) {
|
||||
// Ensure we're using the right language if the entity and the field are
|
||||
// translatable.
|
||||
$field_language = $entity_translatable && $field['translatable'] ? $language : NULL;
|
||||
$row->setSourceProperty($field_name, $this->getFieldValues('user', $field_name, $uid, NULL, $field_language));
|
||||
}
|
||||
|
||||
// Get profile field values. This code is lifted directly from the D6
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
|
||||
|
||||
/**
|
||||
* Provides Drupal 7 user entity translations source plugin.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_user_entity_translation",
|
||||
* source_module = "entity_translation"
|
||||
* )
|
||||
*/
|
||||
class UserEntityTranslation extends FieldableEntity {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('entity_translation', 'et')
|
||||
->fields('et')
|
||||
->condition('et.entity_type', 'user')
|
||||
->condition('et.source', '', '<>');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$uid = $row->getSourceProperty('entity_id');
|
||||
$language = $row->getSourceProperty('language');
|
||||
|
||||
// Get Field API field values.
|
||||
foreach ($this->getFields('user') as $field_name => $field) {
|
||||
// Ensure we're using the right language if the entity is translatable.
|
||||
$field_language = $field['translatable'] ? $language : NULL;
|
||||
$row->setSourceProperty($field_name, $this->getFieldValues('user', $field_name, $uid, NULL, $field_language));
|
||||
}
|
||||
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'entity_type' => $this->t('The entity type this translation relates to'),
|
||||
'entity_id' => $this->t('The entity id this translation relates to'),
|
||||
'revision_id' => $this->t('The entity revision id this translation relates to'),
|
||||
'language' => $this->t('The target language for this translation.'),
|
||||
'source' => $this->t('The source language from which this translation was created.'),
|
||||
'uid' => $this->t('The author of this translation.'),
|
||||
'status' => $this->t('Boolean indicating whether the translation is published (visible to non-administrators).'),
|
||||
'translate' => $this->t('A boolean indicating whether this translation needs to be updated.'),
|
||||
'created' => $this->t('The Unix timestamp when the translation was created.'),
|
||||
'changed' => $this->t('The Unix timestamp when the translation was most recently saved.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'entity_id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
'language' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -100,7 +100,6 @@ class Permission extends AccessPluginBase implements CacheableDependencyInterfac
|
|||
return $this->t($this->options['perm']);
|
||||
}
|
||||
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['perm'] = ['default' => 'access content'];
|
||||
|
|
|
@ -96,7 +96,6 @@ class Role extends AccessPluginBase implements CacheableDependencyInterface {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
protected function defineOptions() {
|
||||
$options = parent::defineOptions();
|
||||
$options['role'] = ['default' => []];
|
||||
|
|
|
@ -23,7 +23,7 @@ class Uid extends NumericArgument {
|
|||
protected $storage;
|
||||
|
||||
/**
|
||||
* Constructs a Drupal\Component\Plugin\PluginBase object.
|
||||
* Constructs a \Drupal\user\Plugin\views\argument\Uid object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
|
@ -51,10 +51,10 @@ class Uid extends NumericArgument {
|
|||
* Override the behavior of title(). Get the name of the user.
|
||||
*
|
||||
* @return array
|
||||
* A list of usernames.
|
||||
* A list of usernames.
|
||||
*/
|
||||
public function titleQuery() {
|
||||
return array_map(function($account) {
|
||||
return array_map(function ($account) {
|
||||
return $account->label();
|
||||
}, $this->storage->loadMultiple($this->value));
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class Permissions extends PrerenderList {
|
|||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* Constructs a Drupal\Component\Plugin\PluginBase object.
|
||||
* Constructs a \Drupal\user\Plugin\views\field\Permissions object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
|
|
|
@ -25,7 +25,7 @@ class Roles extends PrerenderList {
|
|||
protected $database;
|
||||
|
||||
/**
|
||||
* Constructs a Drupal\Component\Plugin\PluginBase object.
|
||||
* Constructs a \Drupal\user\Plugin\views\field\Roles object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
|
@ -82,7 +82,7 @@ class Roles extends PrerenderList {
|
|||
$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);
|
||||
$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);
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
namespace Drupal\user\Plugin\views\field;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\system\Plugin\views\field\BulkForm;
|
||||
use Drupal\user\UserInterface;
|
||||
use Drupal\views\Plugin\views\field\BulkForm;
|
||||
|
||||
/**
|
||||
* Defines a user operations bulk form element.
|
||||
|
|
|
@ -34,7 +34,6 @@ class UserData extends FieldPluginBase {
|
|||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\user\Plugin\views\filter;
|
||||
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Plugin\views\filter\BooleanOperator;
|
||||
|
@ -28,7 +29,7 @@ class Current extends BooleanOperator {
|
|||
$this->ensureMyTable();
|
||||
|
||||
$field = $this->tableAlias . '.' . $this->realField . ' ';
|
||||
$or = db_or();
|
||||
$or = new Condition('OR');
|
||||
|
||||
if (empty($this->value)) {
|
||||
$or->condition($field, '***CURRENT_USER***', '<>');
|
||||
|
|
|
@ -115,7 +115,8 @@ class Name extends InOperator {
|
|||
$this->valueOptions[$account->id()] = $account->label();
|
||||
}
|
||||
else {
|
||||
$this->valueOptions[$account->id()] = 'Anonymous'; // Intentionally NOT translated.
|
||||
// Intentionally NOT translated.
|
||||
$this->valueOptions[$account->id()] = 'Anonymous';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,10 +74,20 @@ class Roles extends ManyToOne {
|
|||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = [];
|
||||
|
||||
if (in_array($this->operator, ['empty', 'not empty'])) {
|
||||
return $dependencies;
|
||||
}
|
||||
foreach ($this->value as $role_id) {
|
||||
|
||||
// The value might be a string due to the wrong plugin being used for role
|
||||
// field data, and subsequently the incorrect config schema object and
|
||||
// value. In the empty case stop early. Otherwise we cast it to an array
|
||||
// later.
|
||||
if (is_string($this->value) && $this->value === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ((array) $this->value as $role_id) {
|
||||
$role = $this->roleStorage->load($role_id);
|
||||
$dependencies[$role->getConfigDependencyKey()][] = $role->getConfigDependencyName();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ class Users extends WizardPluginBase {
|
|||
|
||||
/**
|
||||
* Set the created column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $createdColumn = 'created';
|
||||
|
||||
|
@ -35,7 +37,7 @@ class Users extends WizardPluginBase {
|
|||
'plugin_id' => 'boolean',
|
||||
'entity_type' => 'user',
|
||||
'entity_field' => 'status',
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,212 +2,25 @@
|
|||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\Core\TempStore\PrivateTempStore as CorePrivateTempStore;
|
||||
|
||||
@trigger_error('\Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* In order to preserve BC alias the core exception.
|
||||
*/
|
||||
if (!class_exists('\Drupal\user\TempStoreException')) {
|
||||
class_alias('\Drupal\Core\TempStore\TempStoreException', '\Drupal\user\TempStoreException');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
|
||||
* Use \Drupal\Core\TempStore\PrivateTempStore instead.
|
||||
*
|
||||
* 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.
|
||||
* @see \Drupal\Core\TempStore\PrivateTempStore
|
||||
* @see https://www.drupal.org/node/2935639
|
||||
*/
|
||||
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 $lock_backend
|
||||
* The lock object used for this data.
|
||||
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
|
||||
* The current user account.
|
||||
* @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 $lock_backend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) {
|
||||
$this->storage = $storage;
|
||||
$this->lockBackend = $lock_backend;
|
||||
$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.
|
||||
*
|
||||
* @throws \Drupal\user\TempStoreException
|
||||
* Thrown when a lock for the backend storage could not be acquired.
|
||||
*/
|
||||
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("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage.");
|
||||
}
|
||||
}
|
||||
|
||||
$value = (object) [
|
||||
'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.
|
||||
*
|
||||
* @throws \Drupal\user\TempStoreException
|
||||
* Thrown when a lock for the backend storage could not be acquired.
|
||||
*/
|
||||
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("Couldn't acquire lock to delete item '$key' from '{$this->storage->getCollectionName()}' temporary storage.");
|
||||
}
|
||||
}
|
||||
$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();
|
||||
}
|
||||
|
||||
class PrivateTempStore extends CorePrivateTempStore {
|
||||
}
|
||||
|
|
|
@ -2,72 +2,20 @@
|
|||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\Core\TempStore\PrivateTempStoreFactory as CorePrivateTempStoreFactory;
|
||||
|
||||
@trigger_error('\Drupal\user\PrivateTempStoreFactory is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStoreFactory instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Creates a PrivateTempStore object for a given collection.
|
||||
*
|
||||
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
|
||||
* Use \Drupal\Core\TempStore\PrivateTempStoreFactory instead.
|
||||
*
|
||||
* @see \Drupal\Core\TempStore\PrivateTempStoreFactory
|
||||
* @see https://www.drupal.org/node/2935639
|
||||
*/
|
||||
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\KeyValueStore\KeyValueExpirableFactoryInterface $storage_factory
|
||||
* The key/value store factory.
|
||||
* @param \Drupal\Core\Lock\LockBackendInterface $lock_backend
|
||||
* The lock object used for this data.
|
||||
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
|
||||
* The current account.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
* @param int $expire
|
||||
* The time to live for items, in seconds.
|
||||
*/
|
||||
public function __construct(KeyValueExpirableFactoryInterface $storage_factory, LockBackendInterface $lock_backend, AccountProxyInterface $current_user, RequestStack $request_stack, $expire = 604800) {
|
||||
$this->storageFactory = $storage_factory;
|
||||
$this->lockBackend = $lock_backend;
|
||||
$this->currentUser = $current_user;
|
||||
$this->requestStack = $request_stack;
|
||||
$this->expire = $expire;
|
||||
}
|
||||
class PrivateTempStoreFactory extends CorePrivateTempStoreFactory {
|
||||
|
||||
/**
|
||||
* Creates a PrivateTempStore.
|
||||
|
|
|
@ -6,6 +6,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Form handler for the profile forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ProfileForm extends AccountForm {
|
||||
|
||||
|
@ -36,7 +38,7 @@ class ProfileForm extends AccountForm {
|
|||
$account->save();
|
||||
$form_state->setValue('uid', $account->id());
|
||||
|
||||
drupal_set_message($this->t('The changes have been saved.'));
|
||||
$this->messenger()->addStatus($this->t('The changes have been saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Form handler for the user register forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RegisterForm extends AccountForm {
|
||||
|
||||
|
@ -101,28 +103,28 @@ class RegisterForm extends AccountForm {
|
|||
|
||||
// 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.', [':url' => $account->url(), '%name' => $account->getUsername()]));
|
||||
$this->messenger()->addStatus($this->t('Created a new user account for <a href=":url">%name</a>. No email has been sent.', [':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.'));
|
||||
$this->messenger()->addStatus($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.', [':url' => $account->url(), '%name' => $account->getUsername()]));
|
||||
$this->messenger()->addStatus($this->t('The new user <a href=":url">%name</a> was created without an email address, so no welcome message was sent.', [':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>.', [':url' => $account->url(), '%name' => $account->getUsername()]));
|
||||
$this->messenger()->addStatus($this->t('A welcome message with further instructions has been emailed to the new user <a href=":url">%name</a>.', [':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.'));
|
||||
$this->messenger()->addStatus($this->t('A welcome message with further instructions has been sent to your email address.'));
|
||||
$form_state->setRedirect('<front>');
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +133,7 @@ class RegisterForm extends AccountForm {
|
|||
// 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.'));
|
||||
$this->messenger()->addStatus($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>');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
|
||||
/**
|
||||
* Form controller for the role entity edit forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RoleForm extends EntityForm {
|
||||
|
||||
|
@ -55,11 +57,11 @@ class RoleForm extends EntityForm {
|
|||
|
||||
$edit_link = $this->entity->link($this->t('Edit'));
|
||||
if ($status == SAVED_UPDATED) {
|
||||
drupal_set_message($this->t('Role %label has been updated.', ['%label' => $entity->label()]));
|
||||
$this->messenger()->addStatus($this->t('Role %label has been updated.', ['%label' => $entity->label()]));
|
||||
$this->logger('user')->notice('Role %label has been updated.', ['%label' => $entity->label(), 'link' => $edit_link]);
|
||||
}
|
||||
else {
|
||||
drupal_set_message($this->t('Role %label has been added.', ['%label' => $entity->label()]));
|
||||
$this->messenger()->addStatus($this->t('Role %label has been added.', ['%label' => $entity->label()]));
|
||||
$this->logger('user')->notice('Role %label has been added.', ['%label' => $entity->label(), 'link' => $edit_link]);
|
||||
}
|
||||
$form_state->setRedirect('entity.user_role.collection');
|
||||
|
|
|
@ -4,7 +4,11 @@ namespace Drupal\user;
|
|||
|
||||
use Drupal\Core\Config\Entity\DraggableListBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a class to build a listing of user role entities.
|
||||
|
@ -13,6 +17,41 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
*/
|
||||
class RoleListBuilder extends DraggableListBuilder {
|
||||
|
||||
/**
|
||||
* The messenger.
|
||||
*
|
||||
* @var \Drupal\Core\Messenger\MessengerInterface
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* RoleListBuilder constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entityType
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage class.
|
||||
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
|
||||
* The messenger.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entityType,
|
||||
EntityStorageInterface $storage,
|
||||
MessengerInterface $messenger) {
|
||||
parent::__construct($entityType, $storage);
|
||||
$this->messenger = $messenger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('entity.manager')->getStorage($entity_type->id()),
|
||||
$container->get('messenger')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -58,7 +97,7 @@ class RoleListBuilder extends DraggableListBuilder {
|
|||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::submitForm($form, $form_state);
|
||||
|
||||
drupal_set_message(t('The role settings have been updated.'));
|
||||
$this->messenger->addStatus($this->t('The role settings have been updated.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,278 +2,25 @@
|
|||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\Core\TempStore\SharedTempStore as CoreSharedTempStore;
|
||||
|
||||
@trigger_error('\Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* In order to preserve BC alias the core exception.
|
||||
*/
|
||||
if (!class_exists('\Drupal\user\TempStoreException')) {
|
||||
class_alias('\Drupal\Core\TempStore\TempStoreException', '\Drupal\user\TempStoreException');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
|
||||
* Use \Drupal\Core\TempStore\SharedTempStore instead.
|
||||
*
|
||||
* 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.
|
||||
* @see \Drupal\Core\TempStore\SharedTempStore
|
||||
* @see https://www.drupal.org/node/2935639
|
||||
*/
|
||||
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 $lock_backend
|
||||
* 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 $lock_backend, $owner, RequestStack $request_stack, $expire = 604800) {
|
||||
$this->storage = $storage;
|
||||
$this->lockBackend = $lock_backend;
|
||||
$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) [
|
||||
'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.
|
||||
*
|
||||
* @throws \Drupal\user\TempStoreException
|
||||
* Thrown when a lock for the backend storage could not be acquired.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @throws \Drupal\user\TempStoreException
|
||||
* Thrown when a lock for the backend storage could not be acquired.
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
$this->lockBackend->wait($key);
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage.");
|
||||
}
|
||||
}
|
||||
|
||||
$value = (object) [
|
||||
'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.
|
||||
*
|
||||
* @throws \Drupal\user\TempStoreException
|
||||
* Thrown when a lock for the backend storage could not be acquired.
|
||||
*/
|
||||
public function delete($key) {
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
$this->lockBackend->wait($key);
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
throw new TempStoreException("Couldn't acquire lock to delete item '$key' from {$this->storage->getCollectionName()} temporary storage.");
|
||||
}
|
||||
}
|
||||
$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.
|
||||
*
|
||||
* @throws \Drupal\user\TempStoreException
|
||||
* Thrown when a lock for the backend storage could not be acquired.
|
||||
*/
|
||||
public function deleteIfOwner($key) {
|
||||
if (!$object = $this->storage->get($key)) {
|
||||
return TRUE;
|
||||
}
|
||||
elseif ($object->owner == $this->owner) {
|
||||
$this->delete($key);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
class SharedTempStore extends CoreSharedTempStore {
|
||||
}
|
||||
|
|
|
@ -2,61 +2,20 @@
|
|||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\Core\TempStore\SharedTempStoreFactory as CoreSharedTempStoreFactory;
|
||||
|
||||
@trigger_error('\Drupal\user\SharedTempStoreFactory is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStoreFactory instead. See https://www.drupal.org/node/2935639.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Creates a shared temporary storage for a collection.
|
||||
*
|
||||
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
|
||||
* Use \Drupal\Core\TempStore\SharedTempStoreFactory instead.
|
||||
*
|
||||
* @see \Drupal\Core\TempStore\SharedTempStoreFactory
|
||||
* @see https://www.drupal.org/node/2935639
|
||||
*/
|
||||
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\KeyValueStore\KeyValueExpirableFactoryInterface $storage_factory
|
||||
* The key/value store factory.
|
||||
* @param \Drupal\Core\Lock\LockBackendInterface $lock_backend
|
||||
* 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.
|
||||
*/
|
||||
public function __construct(KeyValueExpirableFactoryInterface $storage_factory, LockBackendInterface $lock_backend, RequestStack $request_stack, $expire = 604800) {
|
||||
$this->storageFactory = $storage_factory;
|
||||
$this->lockBackend = $lock_backend;
|
||||
$this->requestStack = $request_stack;
|
||||
$this->expire = $expire;
|
||||
}
|
||||
class SharedTempStoreFactory extends CoreSharedTempStoreFactory {
|
||||
|
||||
/**
|
||||
* Creates a SharedTempStore for the current user or anonymous session.
|
||||
|
|
25
web/core/modules/user/src/StatusItem.php
Normal file
25
web/core/modules/user/src/StatusItem.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem;
|
||||
|
||||
/**
|
||||
* Defines the 'status' entity field type.
|
||||
*
|
||||
* @todo Consider making this a full field type plugin in
|
||||
* https://www.drupal.org/project/drupal/issues/2936864.
|
||||
*/
|
||||
class StatusItem extends BooleanItem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
|
||||
// Always generate a sample with an enabled status.
|
||||
$values['value'] = 1;
|
||||
return $values;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
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 {}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\rest\Tests\RESTTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
@ -117,7 +118,7 @@ class RestRegisterUserTest extends RESTTestBase {
|
|||
* @param bool $include_password
|
||||
* Whether to include a password in the user values.
|
||||
*
|
||||
* @return string Serialized user values.
|
||||
* @return string
|
||||
* Serialized user values.
|
||||
*/
|
||||
protected function createSerializedUser($name, $include_password = TRUE) {
|
||||
|
@ -166,7 +167,7 @@ class RestRegisterUserTest extends RESTTestBase {
|
|||
*/
|
||||
protected function registerRequest($name, $include_password = TRUE) {
|
||||
$serialized = $this->createSerializedUser($name, $include_password);
|
||||
$this->httpRequest('/user/register', 'POST', $serialized, 'application/hal+json');
|
||||
$this->httpRequest(Url::fromRoute('rest.user_registration.POST', ['_format' => 'hal_json']), 'POST', $serialized, 'application/hal+json');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests user email token upgrade path.
|
||||
*
|
||||
* @group Update
|
||||
*/
|
||||
class UserUpdateEmailToken extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../tests/fixtures/update/drupal-8.user-email-token-2587275.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that email token in status_blocked of user.mail is updated.
|
||||
*/
|
||||
public function testEmailToken() {
|
||||
$mail = \Drupal::config('user.mail')->get('status_blocked');
|
||||
$this->assertTrue(strpos($mail['body'], '[site:account-name]'));
|
||||
$this->runUpdates();
|
||||
$mail = \Drupal::config('user.mail')->get('status_blocked');
|
||||
$this->assertFalse(strpos($mail['body'], '[site:account-name]'));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Tests user permissions sort upgrade path.
|
||||
*
|
||||
* @group Update
|
||||
*/
|
||||
class UserUpdateOrderPermissionsTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that permissions are ordered by machine name.
|
||||
*/
|
||||
public function testPermissionsOrder() {
|
||||
$authenticated = Role::load('authenticated');
|
||||
$permissions = $authenticated->getPermissions();
|
||||
sort($permissions);
|
||||
$this->assertNotIdentical($permissions, $authenticated->getPermissions());
|
||||
|
||||
$this->runUpdates();
|
||||
$authenticated = Role::load('authenticated');
|
||||
$this->assertIdentical($permissions, $authenticated->getPermissions());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests user-account links.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserAccountLinksTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['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.
|
||||
*/
|
||||
public function testSecondaryMenu() {
|
||||
// Create a regular user.
|
||||
$user = $this->drupalCreateUser([]);
|
||||
|
||||
// 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]', [
|
||||
':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]', [
|
||||
':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 the secondary menu to have a "Log in" link.
|
||||
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
|
||||
':menu_class' => 'menu',
|
||||
':href' => 'user/login',
|
||||
':text' => 'Log in',
|
||||
]);
|
||||
$this->assertEqual(count($link), 1, 'Log in link is in secondary menu.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests disabling the 'My account' link.
|
||||
*/
|
||||
public function testDisabledAccountLink() {
|
||||
// Create an admin user and log in.
|
||||
$this->drupalLogin($this->drupalCreateUser(['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]', [
|
||||
':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', [':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]', [
|
||||
':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.
|
||||
*/
|
||||
public 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
<?php
|
||||
|
||||
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 = ['user', 'language', 'language_test'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// User to add and remove language.
|
||||
$this->adminUser = $this->drupalCreateUser(['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.
|
||||
*/
|
||||
public 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.
|
||||
*/
|
||||
public 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.
|
||||
*/
|
||||
public 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.
|
||||
*/
|
||||
public 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 = [];
|
||||
$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 = [];
|
||||
$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.
|
||||
*
|
||||
* Enables the "Account preference for administration pages" language
|
||||
* detection method for the User interface language negotiation type.
|
||||
*
|
||||
* @param bool $admin_first
|
||||
* Whether the admin negotiation should be first.
|
||||
*/
|
||||
public function setLanguageNegotiation($admin_first = FALSE) {
|
||||
$edit = [
|
||||
'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.
|
||||
*/
|
||||
public function addCustomLanguage() {
|
||||
$langcode = 'xx';
|
||||
// The English name for the language.
|
||||
$name = $this->randomMachineName(16);
|
||||
$edit = [
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
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 = [];
|
||||
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([], 'custom_role_1', 'custom_role_1');
|
||||
$rid_2 = $this->drupalCreateRole([], '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(['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 = [];
|
||||
foreach ($result as $account) {
|
||||
$name = (string) $account->td[0]->span;
|
||||
$roles = [];
|
||||
if (isset($account->td[2]->div->ul)) {
|
||||
foreach ($account->td[2]->div->ul->li as $element) {
|
||||
$roles[] = (string) $element;
|
||||
}
|
||||
}
|
||||
$result_accounts[$name] = [
|
||||
'name' => $name,
|
||||
'status' => (string) $account->td[1],
|
||||
'roles' => $roles,
|
||||
'member_for' => (string) $account->td[3],
|
||||
'last_access' => (string) $account->td[4],
|
||||
];
|
||||
}
|
||||
|
||||
$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 = ['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.');
|
||||
|
||||
$this->assertEqual($result_accounts[$timestamp_user]['last_access'], 'never', 'Ensure the last access time is "never".');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
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 = [
|
||||
'anonymous' => [
|
||||
'#value' => $this->randomString(10),
|
||||
'#config_name' => 'user.settings',
|
||||
'#config_key' => 'anonymous',
|
||||
],
|
||||
'user_mail_cancel_confirm_body' => [
|
||||
'#value' => $this->randomString(),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'cancel_confirm.body',
|
||||
],
|
||||
'user_mail_cancel_confirm_subject' => [
|
||||
'#value' => $this->randomString(20),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'cancel_confirm.subject',
|
||||
],
|
||||
'register_pending_approval_admin_body' => [
|
||||
'#value' => $this->randomString(),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'register_pending_approval_admin.body',
|
||||
],
|
||||
'register_pending_approval_admin_subject' => [
|
||||
'#value' => $this->randomString(20),
|
||||
'#config_name' => 'user.mail',
|
||||
'#config_key' => 'register_pending_approval_admin.subject',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -1,200 +0,0 @@
|
|||
<?php
|
||||
|
||||
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 = ['taxonomy', 'views'];
|
||||
|
||||
/**
|
||||
* Registers a user and deletes it.
|
||||
*/
|
||||
public function testUserAdmin() {
|
||||
$config = $this->config('user.settings');
|
||||
$user_a = $this->drupalCreateUser();
|
||||
$user_a->name = 'User A';
|
||||
$user_a->mail = $this->randomMachineName() . '@example.com';
|
||||
$user_a->save();
|
||||
$user_b = $this->drupalCreateUser(['administer taxonomy']);
|
||||
$user_b->name = 'User B';
|
||||
$user_b->save();
|
||||
$user_c = $this->drupalCreateUser(['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(['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', ['query' => ['destination' => $user_a->url('collection')]]);
|
||||
$this->assertRaw($link, 'Found user A edit link on admin users page');
|
||||
|
||||
// Test exposed filter elements.
|
||||
foreach (['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', ['query' => ['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', ['query' => ['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', ['query' => ['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', ['query' => ['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 = [];
|
||||
$edit['action'] = 'user_block_user_action';
|
||||
$edit['user_bulk_form[4]'] = TRUE;
|
||||
$config
|
||||
->set('notify.status_blocked', TRUE)
|
||||
->save();
|
||||
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'), [
|
||||
// Sort the table by username so that we know reliably which user will be
|
||||
// targeted with the blocking action.
|
||||
'query' => ['order' => 'name', 'sort' => 'asc']
|
||||
]);
|
||||
$site_name = $this->config('system.site')->get('name');
|
||||
$this->assertMailString('body', 'Your account on ' . $site_name . ' has been blocked.', 1, 'Blocked message found in the mail sent to user C.');
|
||||
$user_storage->resetCache([$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', ['query' => ['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 = [];
|
||||
$editunblock['action'] = 'user_unblock_user_action';
|
||||
$editunblock['user_bulk_form[4]'] = TRUE;
|
||||
$this->drupalPostForm('admin/people', $editunblock, t('Apply to selected items'), [
|
||||
// Sort the table by username so that we know reliably which user will be
|
||||
// targeted with the blocking action.
|
||||
'query' => ['order' => 'name', 'sort' => 'asc']
|
||||
]);
|
||||
$user_storage->resetCache([$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([]);
|
||||
$user_storage->resetCache([$user_d->id()]);
|
||||
$account1 = $user_storage->load($user_d->id());
|
||||
$this->drupalPostForm('user/' . $account1->id() . '/edit', ['status' => 0], t('Save'));
|
||||
$user_storage->resetCache([$user_d->id()]);
|
||||
$account1 = $user_storage->load($user_d->id());
|
||||
$this->assertTrue($account1->isBlocked(), 'User D blocked');
|
||||
$this->drupalPostForm('user/' . $account1->id() . '/edit', ['status' => TRUE], t('Save'));
|
||||
$user_storage->resetCache([$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.
|
||||
*/
|
||||
public function testNotificationEmailAddress() {
|
||||
// Test that the Notification Email address field is on the config page.
|
||||
$admin_user = $this->drupalCreateUser(['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 = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['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([
|
||||
'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([
|
||||
'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');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests user blocks.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserBlocksTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'views'];
|
||||
|
||||
/**
|
||||
* A user with the 'administer blocks' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer blocks']);
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPlaceBlock('user_login_block');
|
||||
$this->drupalLogout($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user login block is hidden from user/login.
|
||||
*/
|
||||
public function testUserLoginBlockVisibility() {
|
||||
// Array keyed list where key being the URL address and value being expected
|
||||
// visibility as boolean type.
|
||||
$paths = [
|
||||
'node' => TRUE,
|
||||
'user/login' => FALSE,
|
||||
'user/register' => TRUE,
|
||||
'user/password' => TRUE,
|
||||
];
|
||||
foreach ($paths as $path => $expected_visibility) {
|
||||
$this->drupalGet($path);
|
||||
$elements = $this->xpath('//div[contains(@class,"block-user-login-block") and @role="form"]');
|
||||
if ($expected_visibility) {
|
||||
$this->assertTrue(!empty($elements), 'User login block in path "' . $path . '" should be visible');
|
||||
}
|
||||
else {
|
||||
$this->assertTrue(empty($elements), 'User login block in path "' . $path . '" should not be visible');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the user login block.
|
||||
*/
|
||||
public function testUserLoginBlock() {
|
||||
// Create a user with some permission that anonymous users lack.
|
||||
$user = $this->drupalCreateUser(['administer permissions']);
|
||||
|
||||
// Log in using the block.
|
||||
$edit = [];
|
||||
$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'), ['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');
|
||||
|
||||
// Verify that form validation errors are displayed immediately for forms
|
||||
// in blocks and not on subsequent page requests.
|
||||
$this->drupalLogout();
|
||||
$edit = [];
|
||||
$edit['name'] = 'foo';
|
||||
$edit['pass'] = 'invalid password';
|
||||
$this->drupalPostForm('filter/tips', $edit, t('Log in'));
|
||||
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
|
||||
$this->drupalGet('filter/tips');
|
||||
$this->assertNoText(t('Unrecognized username or password. Forgot your password?'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Who's Online block.
|
||||
*/
|
||||
public function testWhosOnlineBlock() {
|
||||
$block = $this->drupalPlaceBlock('views_block:who_s_online-who_s_online_block');
|
||||
|
||||
// Generate users.
|
||||
$user1 = $this->drupalCreateUser(['access user profiles']);
|
||||
$user2 = $this->drupalCreateUser([]);
|
||||
$user3 = $this->drupalCreateUser([]);
|
||||
|
||||
// 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(['access' => $access])
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the create user administration page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserCreateTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['image'];
|
||||
|
||||
/**
|
||||
* Create a user through the administration interface and ensure that it
|
||||
* displays in the user list.
|
||||
*/
|
||||
public function testUserAdd() {
|
||||
$user = $this->drupalCreateUser(['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';
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'user',
|
||||
'module' => 'image',
|
||||
'type' => 'image',
|
||||
'cardinality' => 1,
|
||||
'locked' => FALSE,
|
||||
'indexes' => ['target_id' => ['target_id']],
|
||||
'settings' => [
|
||||
'uri_scheme' => 'public',
|
||||
],
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'user',
|
||||
'label' => 'Picture',
|
||||
'bundle' => 'user',
|
||||
'description' => t('Your virtual face or picture.'),
|
||||
'required' => FALSE,
|
||||
'settings' => [
|
||||
'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 ([FALSE, TRUE] as $notify) {
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'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.', ['@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.', ['@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');
|
||||
}
|
||||
|
||||
// Test that the password '0' is considered a password.
|
||||
// @see https://www.drupal.org/node/2563751.
|
||||
$name = $this->randomMachineName();
|
||||
$edit = [
|
||||
'name' => $name,
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
'pass[pass1]' => 0,
|
||||
'pass[pass2]' => 0,
|
||||
'notify' => FALSE,
|
||||
];
|
||||
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
|
||||
$this->assertText("Created a new user account for $name. No email has been sent");
|
||||
$this->assertNoText('Password field is required');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests user edit page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserEditTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Test user edit page.
|
||||
*/
|
||||
public function testUserEdit() {
|
||||
// Test user edit functionality.
|
||||
$user1 = $this->drupalCreateUser(['change own username']);
|
||||
$user2 = $this->drupalCreateUser([]);
|
||||
$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.', ['%name' => $edit['name']]));
|
||||
|
||||
// Check that the default value in user name field
|
||||
// is the raw value and not a formatted one.
|
||||
\Drupal::state()->set('user_hooks_test_user_format_name_alter', TRUE);
|
||||
\Drupal::service('module_installer')->install(['user_hooks_test']);
|
||||
$this->drupalGet('user/' . $user1->id() . '/edit');
|
||||
$this->assertFieldByName('name', $user1->getAccountName());
|
||||
|
||||
// Check that filling out a single password field does not validate.
|
||||
$edit = [];
|
||||
$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 = [];
|
||||
$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.", ['%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 = [];
|
||||
$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.", ['%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.');
|
||||
|
||||
// Check that the user status field has the correct value and that it is
|
||||
// properly displayed.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->drupalGet('user/' . $user1->id() . '/edit');
|
||||
$this->assertNoFieldChecked('edit-status-0');
|
||||
$this->assertFieldChecked('edit-status-1');
|
||||
|
||||
$edit = ['status' => 0];
|
||||
$this->drupalPostForm('user/' . $user1->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'));
|
||||
$this->assertFieldChecked('edit-status-0');
|
||||
$this->assertNoFieldChecked('edit-status-1');
|
||||
|
||||
$edit = ['status' => 1];
|
||||
$this->drupalPostForm('user/' . $user1->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'));
|
||||
$this->assertNoFieldChecked('edit-status-0');
|
||||
$this->assertFieldChecked('edit-status-1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the password to "0".
|
||||
*
|
||||
* We discovered in https://www.drupal.org/node/2563751 that logging in with a
|
||||
* password that is literally "0" was not possible. This test ensures that
|
||||
* this regression can't happen again.
|
||||
*/
|
||||
public function testUserWith0Password() {
|
||||
$admin = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin);
|
||||
// Create a regular user.
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
|
||||
$edit = ['pass[pass1]' => '0', 'pass[pass2]' => '0'];
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertRaw(t("The changes have been saved."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editing of a user account without an email address.
|
||||
*/
|
||||
public function testUserWithoutEmailEdit() {
|
||||
// Test that an admin can edit users without an email address.
|
||||
$admin = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin);
|
||||
// Create a regular user.
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
// This user has no email address.
|
||||
$user1->mail = '';
|
||||
$user1->save();
|
||||
$this->drupalPostForm("user/" . $user1->id() . "/edit", ['mail' => ''], t('Save'));
|
||||
$this->assertRaw(t("The changes have been saved."));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests whether proper language is stored for new users and access to language
|
||||
* selector.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserLanguageCreationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'language'];
|
||||
|
||||
/**
|
||||
* Functional test for language handling during user creation.
|
||||
*/
|
||||
public function testLocalUserCreation() {
|
||||
// User to add and remove language and create new users.
|
||||
$admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages', 'administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Add predefined language.
|
||||
$langcode = 'fr';
|
||||
ConfigurableLanguage::createFromLangcode($langcode)->save();
|
||||
|
||||
// Set language negotiation.
|
||||
$edit = [
|
||||
'language_interface[enabled][language-url]' => TRUE,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
|
||||
$this->assertText(t('Language detection configuration saved.'), 'Set language negotiation.');
|
||||
|
||||
// Check if the language selector is available on admin/people/create and
|
||||
// set to the currently active language.
|
||||
$this->drupalGet($langcode . '/admin/people/create');
|
||||
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Global language set in the language selector.');
|
||||
|
||||
// Create a user with the admin/people/create form and check if the correct
|
||||
// language is set.
|
||||
$username = $this->randomMachineName(10);
|
||||
$edit = [
|
||||
'name' => $username,
|
||||
'mail' => $this->randomMachineName(4) . '@example.com',
|
||||
'pass[pass1]' => $username,
|
||||
'pass[pass2]' => $username,
|
||||
];
|
||||
|
||||
$this->drupalPostForm($langcode . '/admin/people/create', $edit, t('Create new account'));
|
||||
|
||||
$user = user_load_by_name($username);
|
||||
$this->assertEqual($user->getPreferredLangcode(), $langcode, 'New user has correct preferred language set.');
|
||||
$this->assertEqual($user->language()->getId(), $langcode, 'New user has correct profile language set.');
|
||||
|
||||
// Register a new user and check if the language selector is hidden.
|
||||
$this->drupalLogout();
|
||||
|
||||
$this->drupalGet($langcode . '/user/register');
|
||||
$this->assertNoFieldByName('language[fr]', 'Language selector is not accessible.');
|
||||
|
||||
$username = $this->randomMachineName(10);
|
||||
$edit = [
|
||||
'name' => $username,
|
||||
'mail' => $this->randomMachineName(4) . '@example.com',
|
||||
];
|
||||
|
||||
$this->drupalPostForm($langcode . '/user/register', $edit, t('Create new account'));
|
||||
|
||||
$user = user_load_by_name($username);
|
||||
$this->assertEqual($user->getPreferredLangcode(), $langcode, 'New user has correct preferred language set.');
|
||||
$this->assertEqual($user->language()->getId(), $langcode, 'New user has correct profile language set.');
|
||||
|
||||
// Test if the admin can use the language selector and if the
|
||||
// correct language is was saved.
|
||||
$user_edit = $langcode . '/user/' . $user->id() . '/edit';
|
||||
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet($user_edit);
|
||||
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Language selector is accessible and correct language is selected.');
|
||||
|
||||
// Set pass_raw so we can log in the new user.
|
||||
$user->pass_raw = $this->randomMachineName(10);
|
||||
$edit = [
|
||||
'pass[pass1]' => $user->pass_raw,
|
||||
'pass[pass2]' => $user->pass_raw,
|
||||
];
|
||||
|
||||
$this->drupalPostForm($user_edit, $edit, t('Save'));
|
||||
|
||||
$this->drupalLogin($user);
|
||||
$this->drupalGet($user_edit);
|
||||
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Language selector is accessible and correct language is selected.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that login works as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserLoginTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Tests login with destination.
|
||||
*/
|
||||
public function testLoginCacheTagsAndDestination() {
|
||||
$this->drupalGet('user/login');
|
||||
// The user login form says "Enter your <site name> username.", hence it
|
||||
// depends on config:system.site, and its cache tags should be present.
|
||||
$this->assertCacheTag('config:system.site');
|
||||
|
||||
$user = $this->drupalCreateUser([]);
|
||||
$this->drupalGet('user/login', ['query' => ['destination' => 'foo']]);
|
||||
$edit = ['name' => $user->getUserName(), 'pass' => $user->pass_raw];
|
||||
$this->drupalPostForm(NULL, $edit, t('Log in'));
|
||||
$this->assertUrl('foo', [], 'Redirected to the correct URL');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the global login flood control.
|
||||
*/
|
||||
public function testGlobalLoginFloodControl() {
|
||||
$this->config('user.flood')
|
||||
->set('ip_limit', 10)
|
||||
// Set a high per-user limit out so that it is not relevant in the test.
|
||||
->set('user_limit', 4000)
|
||||
->save();
|
||||
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
$incorrect_user1 = clone $user1;
|
||||
$incorrect_user1->pass_raw .= 'incorrect';
|
||||
|
||||
// Try 2 failed logins.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// A successful login will not reset the IP-based flood control count.
|
||||
$this->drupalLogin($user1);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try 8 more failed logins, they should not trigger the flood control
|
||||
// mechanism.
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// The next login trial should result in an IP-based flood error message.
|
||||
$this->assertFailedLogin($incorrect_user1, 'ip');
|
||||
|
||||
// A login with the correct password should also result in a flood error
|
||||
// message.
|
||||
$this->assertFailedLogin($user1, 'ip');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the per-user login flood control.
|
||||
*/
|
||||
public function testPerUserLoginFloodControl() {
|
||||
$this->config('user.flood')
|
||||
// Set a high global limit out so that it is not relevant in the test.
|
||||
->set('ip_limit', 4000)
|
||||
->set('user_limit', 3)
|
||||
->save();
|
||||
|
||||
$user1 = $this->drupalCreateUser([]);
|
||||
$incorrect_user1 = clone $user1;
|
||||
$incorrect_user1->pass_raw .= 'incorrect';
|
||||
|
||||
$user2 = $this->drupalCreateUser([]);
|
||||
|
||||
// Try 2 failed logins.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// A successful login will reset the per-user flood control count.
|
||||
$this->drupalLogin($user1);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try 3 failed logins for user 1, they will not trigger flood control.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$this->assertFailedLogin($incorrect_user1);
|
||||
}
|
||||
|
||||
// Try one successful attempt for user 2, it should not trigger any
|
||||
// flood control.
|
||||
$this->drupalLogin($user2);
|
||||
$this->drupalLogout();
|
||||
|
||||
// Try one more attempt for user 1, it should be rejected, even if the
|
||||
// correct password has been used.
|
||||
$this->assertFailedLogin($user1, 'user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that user password is re-hashed upon login after changing $count_log2.
|
||||
*/
|
||||
public function testPasswordRehashOnLogin() {
|
||||
// Determine default log2 for phpass hashing algorithm
|
||||
$default_count_log2 = 16;
|
||||
|
||||
// Retrieve instance of password hashing algorithm
|
||||
$password_hasher = $this->container->get('password');
|
||||
|
||||
// Create a new user and authenticate.
|
||||
$account = $this->drupalCreateUser([]);
|
||||
$password = $account->pass_raw;
|
||||
$this->drupalLogin($account);
|
||||
$this->drupalLogout();
|
||||
// Load the stored user. The password hash should reflect $default_count_log2.
|
||||
$user_storage = $this->container->get('entity.manager')->getStorage('user');
|
||||
$account = User::load($account->id());
|
||||
$this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $default_count_log2);
|
||||
|
||||
// Change the required number of iterations by loading a test-module
|
||||
// containing the necessary container builder code and then verify that the
|
||||
// users password gets rehashed during the login.
|
||||
$overridden_count_log2 = 19;
|
||||
\Drupal::service('module_installer')->install(['user_custom_phpass_params_test']);
|
||||
$this->resetAll();
|
||||
|
||||
$account->pass_raw = $password;
|
||||
$this->drupalLogin($account);
|
||||
// Load the stored user, which should have a different password hash now.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $overridden_count_log2);
|
||||
$this->assertTrue($password_hasher->check($password, $account->getPassword()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an unsuccessful login attempt.
|
||||
*
|
||||
* @param \Drupal\user\Entity\User $account
|
||||
* A user object with name and pass_raw attributes for the login attempt.
|
||||
* @param mixed $flood_trigger
|
||||
* (optional) Whether or not to expect that the flood control mechanism
|
||||
* will be triggered. Defaults to NULL.
|
||||
* - Set to 'user' to expect a 'too many failed logins error.
|
||||
* - Set to any value to expect an error for too many failed logins per IP
|
||||
* .
|
||||
* - Set to NULL to expect a failed login.
|
||||
*/
|
||||
public function assertFailedLogin($account, $flood_trigger = NULL) {
|
||||
$edit = [
|
||||
'name' => $account->getUsername(),
|
||||
'pass' => $account->pass_raw,
|
||||
];
|
||||
$this->drupalPostForm('user/login', $edit, t('Log in'));
|
||||
$this->assertNoFieldByXPath("//input[@name='pass' and @value!='']", NULL, 'Password value attribute is blank.');
|
||||
if (isset($flood_trigger)) {
|
||||
if ($flood_trigger == 'user') {
|
||||
$this->assertRaw(\Drupal::translation()->formatPlural($this->config('user.flood')->get('user_limit'), '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>.', '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>.', [':url' => \Drupal::url('user.pass')]));
|
||||
}
|
||||
else {
|
||||
// No uid, so the limit is IP-based.
|
||||
$this->assertRaw(t('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>.', [':url' => \Drupal::url('user.pass')]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,340 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\system\Tests\Cache\PageCacheTagsTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Ensure that password reset methods work as expected.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPasswordResetTest extends PageCacheTagsTestBase {
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* This test uses the standard profile to test the password reset in
|
||||
* combination with an ajax request provided by the user picture configuration
|
||||
* in the standard profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* The user object to test password resetting.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_menu_block:account');
|
||||
|
||||
// Create a user.
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Activate user by logging in.
|
||||
$this->drupalLogin($account);
|
||||
|
||||
$this->account = User::load($account->id());
|
||||
$this->account->pass_raw = $account->pass_raw;
|
||||
$this->drupalLogout();
|
||||
|
||||
// Set the last login time that is used to generate the one-time link so
|
||||
// that it is definitely over a second ago.
|
||||
$account->login = REQUEST_TIME - mt_rand(10, 100000);
|
||||
db_update('users_field_data')
|
||||
->fields(['login' => $account->getLastLoginTime()])
|
||||
->condition('uid', $account->id())
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests password reset functionality.
|
||||
*/
|
||||
public function testUserPasswordReset() {
|
||||
// Verify that accessing the password reset form without having the session
|
||||
// variables set results in an access denied message.
|
||||
$this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Try to reset the password for an invalid account.
|
||||
$this->drupalGet('user/password');
|
||||
|
||||
$edit = ['name' => $this->randomMachineName(32)];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
|
||||
$this->assertText(t('@name is not recognized as a username or an email address.', ['@name' => $edit['name']]), 'Validation error message shown when trying to request password for invalid account.');
|
||||
$this->assertEqual(count($this->drupalGetMails(['id' => 'user_password_reset'])), 0, 'No email was sent when requesting a password for an invalid account.');
|
||||
|
||||
// Reset the password by username via the password reset page.
|
||||
$edit['name'] = $this->account->getUsername();
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
|
||||
// Verify that the user was sent an email.
|
||||
$this->assertMail('to', $this->account->getEmail(), 'Password email sent to user.');
|
||||
$subject = t('Replacement login information for @username at @site', ['@username' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]);
|
||||
$this->assertMail('subject', $subject, 'Password reset email subject is correct.');
|
||||
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
// Ensure that the current url does not contain the hash and timestamp.
|
||||
$this->assertUrl(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
|
||||
|
||||
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
|
||||
|
||||
// Ensure the password reset URL is not cached.
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
|
||||
|
||||
// Check the one-time login page.
|
||||
$this->assertText($this->account->getUsername(), 'One-time login page contains the correct username.');
|
||||
$this->assertText(t('This login can be used only once.'), 'Found warning about one-time login.');
|
||||
$this->assertTitle(t('Reset password | Drupal'), 'Page title is "Reset password".');
|
||||
|
||||
// Check successful login.
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertLink(t('Log out'));
|
||||
$this->assertTitle(t('@name | @site', ['@name' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]), 'Logged in using password reset link.');
|
||||
|
||||
// Make sure the ajax request from uploading a user picture does not
|
||||
// invalidate the reset token.
|
||||
$image = current($this->drupalGetTestFiles('image'));
|
||||
$edit = [
|
||||
'files[user_picture_0]' => drupal_realpath($image->uri),
|
||||
];
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'user_picture_0_upload_button');
|
||||
|
||||
// Change the forgotten password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Forgotten password changed.');
|
||||
|
||||
// Verify that the password reset session has been destroyed.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('Your current password is missing or incorrect; it\'s required to change the Password.'), 'Password needed to make profile changes.');
|
||||
|
||||
// Log out, and try to log in again using the same one-time link.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(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.'), 'One-time link is no longer valid.');
|
||||
|
||||
// Request a new password again, this time using the email address.
|
||||
$this->drupalGet('user/password');
|
||||
// Count email messages before to compare with after.
|
||||
$before = count($this->drupalGetMails(['id' => 'user_password_reset']));
|
||||
$edit = ['name' => $this->account->getEmail()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertTrue( count($this->drupalGetMails(['id' => 'user_password_reset'])) === $before + 1, 'Email sent when requesting password reset using email address.');
|
||||
|
||||
// Visit the user edit page without pass-reset-token and make sure it does
|
||||
// not cause an error.
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->drupalGet('user/' . $this->account->id() . '/edit');
|
||||
$this->assertNoText('Expected user_string to be a string, NULL given');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Create a password reset link as if the request time was 60 seconds older than the allowed limit.
|
||||
$timeout = $this->config('user.settings')->get('password_reset_timeout');
|
||||
$bogus_timestamp = REQUEST_TIME - $timeout - 60;
|
||||
$_uid = $this->account->id();
|
||||
$this->drupalGet("user/reset/$_uid/$bogus_timestamp/" . user_pass_rehash($this->account, $bogus_timestamp));
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.');
|
||||
|
||||
// Create a user, block the account, and verify that a login link is denied.
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$blocked_account = $this->drupalCreateUser()->block();
|
||||
$blocked_account->save();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp));
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Verify a blocked user can not request a new password.
|
||||
$this->drupalGet('user/password');
|
||||
// Count email messages before to compare with after.
|
||||
$before = count($this->drupalGetMails(['id' => 'user_password_reset']));
|
||||
$edit = ['name' => $blocked_account->getUsername()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertRaw(t('%name is blocked or has not been activated yet.', ['%name' => $blocked_account->getUsername()]), 'Notified user blocked accounts can not request a new password');
|
||||
$this->assertTrue(count($this->drupalGetMails(['id' => 'user_password_reset'])) === $before, 'No email was sent when requesting password reset for a blocked account');
|
||||
|
||||
// Verify a password reset link is invalidated when the user's email address changes.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getUsername()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$old_email_reset_link = $this->getResetURL();
|
||||
$this->account->setEmail("1" . $this->account->getEmail());
|
||||
$this->account->save();
|
||||
$this->drupalGet($old_email_reset_link);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertText(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.'), 'One-time link is no longer valid.');
|
||||
|
||||
// Verify a password reset link will automatically log a user when /login is
|
||||
// appended.
|
||||
$this->drupalGet('user/password');
|
||||
$edit = ['name' => $this->account->getUsername()];
|
||||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$reset_url = $this->getResetURL();
|
||||
$this->drupalGet($reset_url . '/login');
|
||||
$this->assertLink(t('Log out'));
|
||||
$this->assertTitle(t('@name | @site', ['@name' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]), 'Logged in using password reset link.');
|
||||
|
||||
// Ensure blocked and deleted accounts can't access the user.reset.login
|
||||
// route.
|
||||
$this->drupalLogout();
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$blocked_account = $this->drupalCreateUser()->block();
|
||||
$blocked_account->save();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
|
||||
$this->assertResponse(403);
|
||||
|
||||
$blocked_account->delete();
|
||||
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves password reset email and extracts the login link.
|
||||
*/
|
||||
public function getResetURL() {
|
||||
// Assume the most recent email.
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
|
||||
return $urls[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user password reset while logged in.
|
||||
*/
|
||||
public function testUserPasswordResetLoggedIn() {
|
||||
$another_account = $this->drupalCreateUser();
|
||||
$this->drupalLogin($another_account);
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, NULL, t('Submit'));
|
||||
|
||||
// Click the reset URL while logged and change our password.
|
||||
$resetURL = $this->getResetURL();
|
||||
// Log in as a different user.
|
||||
$this->drupalLogin($this->account);
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertRaw(new FormattableMarkup(
|
||||
'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">log out</a> and try using the link again.',
|
||||
['%other_user' => $this->account->getUsername(), '%resetting_user' => $another_account->getUsername(), ':logout' => Url::fromRoute('user.logout')->toString()]
|
||||
));
|
||||
|
||||
$another_account->delete();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertText('The one-time login link you clicked is invalid.');
|
||||
|
||||
// Log in.
|
||||
$this->drupalLogin($this->account);
|
||||
|
||||
// Reset the password by username via the password reset page.
|
||||
$this->drupalGet('user/password');
|
||||
$this->drupalPostForm(NULL, NULL, t('Submit'));
|
||||
|
||||
// Click the reset URL while logged and change our password.
|
||||
$resetURL = $this->getResetURL();
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
|
||||
// Change the password.
|
||||
$password = user_password();
|
||||
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Password changed.');
|
||||
|
||||
// Logged in users should not be able to access the user.reset.login or the
|
||||
// user.reset.form routes.
|
||||
$timestamp = REQUEST_TIME - 1;
|
||||
$this->drupalGet("user/reset/" . $this->account->id() . "/$timestamp/" . user_pass_rehash($this->account, $timestamp) . '/login');
|
||||
$this->assertResponse(403);
|
||||
$this->drupalGet("user/reset/" . $this->account->id());
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefill the text box on incorrect login via link to password reset page.
|
||||
*/
|
||||
public function testUserResetPasswordTextboxFilled() {
|
||||
$this->drupalGet('user/login');
|
||||
$edit = [
|
||||
'name' => $this->randomMachineName(),
|
||||
'pass' => $this->randomMachineName(),
|
||||
];
|
||||
$this->drupalPostForm('user/login', $edit, t('Log in'));
|
||||
$this->assertRaw(t('Unrecognized username or password. <a href=":password">Forgot your password?</a>',
|
||||
[':password' => \Drupal::url('user.pass', [], ['query' => ['name' => $edit['name']]])]));
|
||||
unset($edit['pass']);
|
||||
$this->drupalGet('user/password', ['query' => ['name' => $edit['name']]]);
|
||||
$this->assertFieldByName('name', $edit['name'], 'User name found.');
|
||||
// Ensure the name field value is not cached.
|
||||
$this->drupalGet('user/password');
|
||||
$this->assertNoFieldByName('name', $edit['name'], 'User name not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that users cannot forge password reset URLs of other users.
|
||||
*/
|
||||
public function testResetImpersonation() {
|
||||
// Create two identical user accounts except for the user name. They must
|
||||
// have the same empty password, so we can't use $this->drupalCreateUser().
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['status'] = 1;
|
||||
$user1 = User::create($edit);
|
||||
$user1->save();
|
||||
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$user2 = User::create($edit);
|
||||
$user2->save();
|
||||
|
||||
// Unique password hashes are automatically generated, the only way to
|
||||
// change that is to update it directly in the database.
|
||||
db_update('users_field_data')
|
||||
->fields(['pass' => NULL])
|
||||
->condition('uid', [$user1->id(), $user2->id()], 'IN')
|
||||
->execute();
|
||||
\Drupal::entityManager()->getStorage('user')->resetCache();
|
||||
$user1 = User::load($user1->id());
|
||||
$user2 = User::load($user2->id());
|
||||
|
||||
$this->assertEqual($user1->getPassword(), $user2->getPassword(), 'Both users have the same password hash.');
|
||||
|
||||
// The password reset URL must not be valid for the second user when only
|
||||
// the user ID is changed in the URL.
|
||||
$reset_url = user_pass_reset_url($user1);
|
||||
$attack_reset_url = str_replace("user/reset/{$user1->id()}", "user/reset/{$user2->id()}", $reset_url);
|
||||
$this->drupalGet($attack_reset_url);
|
||||
$this->drupalPostForm(NULL, NULL, t('Log in'));
|
||||
$this->assertNoText($user2->getUsername(), 'The invalid password reset page does not show the user name.');
|
||||
$this->assertUrl('user/password', [], 'The user is redirected to the password reset request page.');
|
||||
$this->assertText('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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\user\Entity\Role;
|
||||
|
||||
/**
|
||||
* Verify that role permissions can be added and removed via the permissions
|
||||
* page.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPermissionsTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* User with admin privileges.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* User's role ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rid;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer permissions', 'access user profiles', 'administer site configuration', 'administer modules', 'administer account settings']);
|
||||
|
||||
// Find the new role ID.
|
||||
$all_rids = $this->adminUser->getRoles();
|
||||
unset($all_rids[array_search(RoleInterface::AUTHENTICATED_ID, $all_rids)]);
|
||||
$this->rid = reset($all_rids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test changing user permissions through the permissions page.
|
||||
*/
|
||||
public function testUserPermissionChanges() {
|
||||
$permissions_hash_generator = $this->container->get('user_permissions_hash_generator');
|
||||
|
||||
$storage = $this->container->get('entity.manager')->getStorage('user_role');
|
||||
|
||||
// Create an additional role and mark it as admin role.
|
||||
Role::create(['is_admin' => TRUE, 'id' => 'administrator', 'label' => 'Administrator'])->save();
|
||||
$storage->resetCache();
|
||||
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$rid = $this->rid;
|
||||
$account = $this->adminUser;
|
||||
$previous_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertIdentical($previous_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
|
||||
|
||||
// Add a permission.
|
||||
$this->assertFalse($account->hasPermission('administer users'), 'User does not have "administer users" permission.');
|
||||
$edit = [];
|
||||
$edit[$rid . '[administer users]'] = TRUE;
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
|
||||
$storage->resetCache();
|
||||
$this->assertTrue($account->hasPermission('administer users'), 'User now has "administer users" permission.');
|
||||
$current_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertIdentical($current_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
|
||||
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
|
||||
$previous_permissions_hash = $current_permissions_hash;
|
||||
|
||||
// Remove a permission.
|
||||
$this->assertTrue($account->hasPermission('access user profiles'), 'User has "access user profiles" permission.');
|
||||
$edit = [];
|
||||
$edit[$rid . '[access user profiles]'] = FALSE;
|
||||
$this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
|
||||
$storage->resetCache();
|
||||
$this->assertFalse($account->hasPermission('access user profiles'), 'User no longer has "access user profiles" permission.');
|
||||
$current_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertIdentical($current_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
|
||||
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
|
||||
|
||||
// Ensure that the admin role doesn't have any checkboxes.
|
||||
$this->drupalGet('admin/people/permissions');
|
||||
foreach (array_keys($this->container->get('user.permissions')->getPermissions()) as $permission) {
|
||||
$this->assertNoFieldByName('administrator[' . $permission . ']');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test assigning of permissions for the administrator role.
|
||||
*/
|
||||
public function testAdministratorRole() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
|
||||
// Verify that the administration role is none by default.
|
||||
$this->assertOptionSelected('edit-user-admin-role', '', 'Administration role defaults to none.');
|
||||
|
||||
$this->assertFalse(Role::load($this->rid)->isAdmin());
|
||||
|
||||
// Set the user's role to be the administrator role.
|
||||
$edit = [];
|
||||
$edit['user_admin_role'] = $this->rid;
|
||||
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
|
||||
|
||||
\Drupal::entityManager()->getStorage('user_role')->resetCache();
|
||||
$this->assertTrue(Role::load($this->rid)->isAdmin());
|
||||
|
||||
// Enable aggregator module and ensure the 'administer news feeds'
|
||||
// permission is assigned by default.
|
||||
\Drupal::service('module_installer')->install(['aggregator']);
|
||||
|
||||
$this->assertTrue($this->adminUser->hasPermission('administer news feeds'), 'The permission was automatically assigned to the administrator role');
|
||||
|
||||
// Ensure that selecting '- None -' removes the admin role.
|
||||
$edit = [];
|
||||
$edit['user_admin_role'] = '';
|
||||
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
|
||||
|
||||
\Drupal::entityManager()->getStorage('user_role')->resetCache();
|
||||
\Drupal::configFactory()->reset();
|
||||
$this->assertFalse(Role::load($this->rid)->isAdmin());
|
||||
|
||||
// Manually create two admin roles, in that case the single select should be
|
||||
// hidden.
|
||||
Role::create(['id' => 'admin_role_0', 'is_admin' => TRUE, 'label' => 'Admin role 0'])->save();
|
||||
Role::create(['id' => 'admin_role_1', 'is_admin' => TRUE, 'label' => 'Admin role 1'])->save();
|
||||
$this->drupalGet('admin/config/people/accounts');
|
||||
$this->assertNoFieldByName('user_admin_role');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify proper permission changes by user_role_change_permissions().
|
||||
*/
|
||||
public function testUserRoleChangePermissions() {
|
||||
$permissions_hash_generator = $this->container->get('user_permissions_hash_generator');
|
||||
|
||||
$rid = $this->rid;
|
||||
$account = $this->adminUser;
|
||||
$previous_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
|
||||
// Verify current permissions.
|
||||
$this->assertFalse($account->hasPermission('administer users'), 'User does not have "administer users" permission.');
|
||||
$this->assertTrue($account->hasPermission('access user profiles'), 'User has "access user profiles" permission.');
|
||||
$this->assertTrue($account->hasPermission('administer site configuration'), 'User has "administer site configuration" permission.');
|
||||
|
||||
// Change permissions.
|
||||
$permissions = [
|
||||
'administer users' => 1,
|
||||
'access user profiles' => 0,
|
||||
];
|
||||
user_role_change_permissions($rid, $permissions);
|
||||
|
||||
// Verify proper permission changes.
|
||||
$this->assertTrue($account->hasPermission('administer users'), 'User now has "administer users" permission.');
|
||||
$this->assertFalse($account->hasPermission('access user profiles'), 'User no longer has "access user profiles" permission.');
|
||||
$this->assertTrue($account->hasPermission('administer site configuration'), 'User still has "administer site configuration" permission.');
|
||||
|
||||
// Verify the permissions hash has changed.
|
||||
$current_permissions_hash = $permissions_hash_generator->generate($account);
|
||||
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\file\Entity\File;
|
||||
|
||||
/**
|
||||
* Tests user picture functionality.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserPictureTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* Using the standard profile to test user picture config provided by the
|
||||
* standard profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* A regular user.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser([
|
||||
'access content',
|
||||
'access comments',
|
||||
'post comments',
|
||||
'skip comment approval',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation, display, and deletion of user pictures.
|
||||
*/
|
||||
public function testCreateDeletePicture() {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Save a new picture.
|
||||
$image = current($this->drupalGetTestFiles('image'));
|
||||
$file = $this->saveUserPicture($image);
|
||||
|
||||
// Verify that the image is displayed on the user account page.
|
||||
$this->drupalGet('user');
|
||||
$this->assertRaw(file_uri_target($file->getFileUri()), 'User picture found on user account page.');
|
||||
|
||||
// Delete the picture.
|
||||
$edit = [];
|
||||
$this->drupalPostForm('user/' . $this->webUser->id() . '/edit', $edit, t('Remove'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
|
||||
// Call file_cron() to clean up the file. Make sure the timestamp
|
||||
// of the file is older than the system.file.temporary_maximum_age
|
||||
// configuration value.
|
||||
db_update('file_managed')
|
||||
->fields([
|
||||
'changed' => REQUEST_TIME - ($this->config('system.file')->get('temporary_maximum_age') + 1),
|
||||
])
|
||||
->condition('fid', $file->id())
|
||||
->execute();
|
||||
\Drupal::service('cron')->run();
|
||||
|
||||
// Verify that the image has been deleted.
|
||||
$this->assertFalse(File::load($file->id()), 'File was removed from the database.');
|
||||
// Clear out PHP's file stat cache so we see the current value.
|
||||
clearstatcache(TRUE, $file->getFileUri());
|
||||
$this->assertFalse(is_file($file->getFileUri()), 'File was removed from the file system.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests embedded users on node pages.
|
||||
*/
|
||||
public function testPictureOnNodeComment() {
|
||||
$this->drupalLogin($this->webUser);
|
||||
|
||||
// Save a new picture.
|
||||
$image = current($this->drupalGetTestFiles('image'));
|
||||
$file = $this->saveUserPicture($image);
|
||||
|
||||
$node = $this->drupalCreateNode(['type' => 'article']);
|
||||
|
||||
// Enable user pictures on nodes.
|
||||
$this->config('system.theme.global')->set('features.node_user_picture', TRUE)->save();
|
||||
|
||||
$image_style_id = $this->config('core.entity_view_display.user.user.compact')->get('content.user_picture.settings.image_style');
|
||||
$style = ImageStyle::load($image_style_id);
|
||||
$image_url = file_url_transform_relative($style->buildUrl($file->getfileUri()));
|
||||
$alt_text = 'Profile picture for user ' . $this->webUser->getUsername();
|
||||
|
||||
// Verify that the image is displayed on the node page.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$elements = $this->cssSelect('.node__meta .field--name-user-picture img[alt="' . $alt_text . '"][src="' . $image_url . '"]');
|
||||
$this->assertEqual(count($elements), 1, 'User picture with alt text found on node page.');
|
||||
|
||||
// Enable user pictures on comments, instead of nodes.
|
||||
$this->config('system.theme.global')
|
||||
->set('features.node_user_picture', FALSE)
|
||||
->set('features.comment_user_picture', TRUE)
|
||||
->save();
|
||||
|
||||
$edit = [
|
||||
'comment_body[0][value]' => $this->randomString(),
|
||||
];
|
||||
$this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit, t('Save'));
|
||||
$elements = $this->cssSelect('.comment__meta .field--name-user-picture img[alt="' . $alt_text . '"][src="' . $image_url . '"]');
|
||||
$this->assertEqual(count($elements), 1, 'User picture with alt text found on the comment.');
|
||||
|
||||
// Disable user pictures on comments and nodes.
|
||||
$this->config('system.theme.global')
|
||||
->set('features.node_user_picture', FALSE)
|
||||
->set('features.comment_user_picture', FALSE)
|
||||
->save();
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertNoRaw(file_uri_target($file->getFileUri()), 'User picture not found on node and comment.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the user picture for the test user.
|
||||
*/
|
||||
public function saveUserPicture($image) {
|
||||
$edit = ['files[user_picture_0]' => drupal_realpath($image->uri)];
|
||||
$this->drupalPostForm('user/' . $this->webUser->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Load actual user data from database.
|
||||
$user_storage = $this->container->get('entity.manager')->getStorage('user');
|
||||
$user_storage->resetCache([$this->webUser->id()]);
|
||||
$account = $user_storage->load($this->webUser->id());
|
||||
return File::load($account->user_picture->target_id);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,392 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests registration of user under different configurations.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserRegistrationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_test'];
|
||||
|
||||
public function testRegistrationWithEmailVerification() {
|
||||
$config = $this->config('user.settings');
|
||||
// Require email verification.
|
||||
$config->set('verify_mail', TRUE)->save();
|
||||
|
||||
// Set registration to administrator only.
|
||||
$config->set('register', USER_REGISTER_ADMINISTRATORS_ONLY)->save();
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertResponse(403, 'Registration page is inaccessible when only administrators can create accounts.');
|
||||
|
||||
// Allow registration by site visitors without administrator approval.
|
||||
$config->set('register', USER_REGISTER_VISITORS)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('A welcome message with further instructions has been sent to your email address.'), 'User registered successfully.');
|
||||
|
||||
/** @var EntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('user');
|
||||
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertTrue($new_user->isActive(), 'New account is active after registration.');
|
||||
$resetURL = user_pass_reset_url($new_user);
|
||||
$this->drupalGet($resetURL);
|
||||
$this->assertTitle(t('Set password | Drupal'), 'Page title is "Set password".');
|
||||
|
||||
// Allow registration by site visitors, but require administrator approval.
|
||||
$config->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->container->get('entity.manager')->getStorage('user')->resetCache();
|
||||
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertFalse($new_user->isActive(), 'New account is blocked until approved by an administrator.');
|
||||
}
|
||||
|
||||
public function testRegistrationWithoutEmailVerification() {
|
||||
$config = $this->config('user.settings');
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$config
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
|
||||
// Try entering a mismatching password.
|
||||
$edit['pass[pass1]'] = '99999.0';
|
||||
$edit['pass[pass2]'] = '99999';
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The specified passwords do not match.'), 'Typing mismatched passwords displays an error message.');
|
||||
|
||||
// Enter a correct password.
|
||||
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $new_pass;
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->container->get('entity.manager')->getStorage('user')->resetCache();
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertNotNull($new_user, 'New account successfully created with matching passwords.');
|
||||
$this->assertText(t('Registration successful. You are now logged in.'), 'Users are logged in after registering.');
|
||||
$this->drupalLogout();
|
||||
|
||||
// Allow registration by site visitors, but require administrator approval.
|
||||
$config->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass1]'] = $pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $pass;
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), 'Users are notified of pending approval');
|
||||
|
||||
// Try to log in before administrator approval.
|
||||
$auth = [
|
||||
'name' => $name,
|
||||
'pass' => $pass,
|
||||
];
|
||||
$this->drupalPostForm('user/login', $auth, t('Log in'));
|
||||
$this->assertText(t('The username @name has not been activated or is blocked.', ['@name' => $name]), 'User cannot log in yet.');
|
||||
|
||||
// Activate the new account.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
$edit = [
|
||||
'status' => 1,
|
||||
];
|
||||
$this->drupalPostForm('user/' . $new_user->id() . '/edit', $edit, t('Save'));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Log in after administrator approval.
|
||||
$this->drupalPostForm('user/login', $auth, t('Log in'));
|
||||
$this->assertText(t('Member for'), 'User can log in after administrator approval.');
|
||||
}
|
||||
|
||||
public function testRegistrationEmailDuplicates() {
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
// Set up a user to check for duplicates.
|
||||
$duplicate_user = $this->drupalCreateUser();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $duplicate_user->getEmail();
|
||||
|
||||
// Attempt to create a new account using an existing email address.
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying an exact duplicate email address displays an error message');
|
||||
|
||||
// Attempt to bypass duplicate email registration validation by adding spaces.
|
||||
$edit['mail'] = ' ' . $duplicate_user->getEmail() . ' ';
|
||||
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying a duplicate email address with added whitespace displays an error message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that UUID isn't cached in form state on register form.
|
||||
*
|
||||
* This is a regression test for https://www.drupal.org/node/2500527 to ensure
|
||||
* that the form is not cached on GET requests.
|
||||
*/
|
||||
public function testUuidFormState() {
|
||||
\Drupal::service('module_installer')->install(['image']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
// Add a picture field in order to ensure that no form cache is written,
|
||||
// which breaks registration of more than 1 user every 6 hours.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'user_picture',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'image',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_name' => 'user_picture',
|
||||
'entity_type' => 'user',
|
||||
'bundle' => 'user',
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
$form_display = EntityFormDisplay::create([
|
||||
'targetEntityType' => 'user',
|
||||
'bundle' => 'user',
|
||||
'mode' => 'default',
|
||||
'status' => TRUE,
|
||||
]);
|
||||
$form_display->setComponent('user_picture', [
|
||||
'type' => 'image_image',
|
||||
]);
|
||||
$form_display->save();
|
||||
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
|
||||
|
||||
// Create one account.
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$user_storage = \Drupal::entityManager()->getStorage('user');
|
||||
|
||||
$this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
|
||||
$this->drupalLogout();
|
||||
|
||||
// Create a second account.
|
||||
$edit['name'] = $this->randomMachineName();
|
||||
$edit['mail'] = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
|
||||
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
|
||||
}
|
||||
|
||||
public function testRegistrationDefaultValues() {
|
||||
// Don't require email verification and allow registration by site visitors
|
||||
// without administrator approval.
|
||||
$config_user_settings = $this->config('user.settings')
|
||||
->set('verify_mail', FALSE)
|
||||
->set('register', USER_REGISTER_VISITORS)
|
||||
->save();
|
||||
|
||||
// Set the default timezone to Brussels.
|
||||
$config_system_date = $this->config('system.date')
|
||||
->set('timezone.user.configurable', 1)
|
||||
->set('timezone.default', 'Europe/Brussels')
|
||||
->save();
|
||||
|
||||
// Check the presence of expected cache tags.
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
|
||||
$edit['pass[pass2]'] = $new_pass;
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->getUsername(), $name, 'Username matches.');
|
||||
$this->assertEqual($new_user->getEmail(), $mail, 'Email address matches.');
|
||||
$this->assertTrue(($new_user->getCreatedTime() > REQUEST_TIME - 20 ), 'Correct creation time.');
|
||||
$this->assertEqual($new_user->isActive(), $config_user_settings->get('register') == USER_REGISTER_VISITORS ? 1 : 0, 'Correct status field.');
|
||||
$this->assertEqual($new_user->getTimezone(), $config_system_date->get('timezone.default'), 'Correct time zone field.');
|
||||
$this->assertEqual($new_user->langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct language field.');
|
||||
$this->assertEqual($new_user->preferred_langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct preferred language field.');
|
||||
$this->assertEqual($new_user->init->value, $mail, 'Correct init field.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests username and email field constraints on user registration.
|
||||
*
|
||||
* @see \Drupal\user\Plugin\Validation\Constraint\UserNameUnique
|
||||
* @see \Drupal\user\Plugin\Validation\Constraint\UserMailUnique
|
||||
*/
|
||||
public function testUniqueFields() {
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
$edit = ['mail' => 'test@example.com', 'name' => $account->getUsername()];
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertRaw(SafeMarkup::format('The username %value is already taken.', ['%value' => $account->getUsername()]));
|
||||
|
||||
$edit = ['mail' => $account->getEmail(), 'name' => $this->randomString()];
|
||||
$this->drupalPostForm('user/register', $edit, t('Create new account'));
|
||||
$this->assertRaw(SafeMarkup::format('The email address %value is already taken.', ['%value' => $account->getEmail()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field API fields on user registration forms.
|
||||
*/
|
||||
public function testRegistrationWithUserFields() {
|
||||
// Create a field on 'user' entity type.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'test_user_field',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
]);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => 'Some user field',
|
||||
'bundle' => 'user',
|
||||
'required' => TRUE,
|
||||
]);
|
||||
$field->save();
|
||||
entity_get_form_display('user', 'user', 'default')
|
||||
->setComponent('test_user_field', ['type' => 'test_field_widget'])
|
||||
->save();
|
||||
entity_get_form_display('user', 'user', 'register')
|
||||
->save();
|
||||
|
||||
// Check that the field does not appear on the registration form.
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertNoText($field->label(), 'The field does not appear on user registration form');
|
||||
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
|
||||
// Have the field appear on the registration form.
|
||||
entity_get_form_display('user', 'user', 'register')
|
||||
->setComponent('test_user_field', ['type' => 'test_field_widget'])
|
||||
->save();
|
||||
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertText($field->label(), 'The field appears on user registration form');
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
|
||||
// Check that validation errors are correctly reported.
|
||||
$edit = [];
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
// Missing input in required field.
|
||||
$edit['test_user_field[0][value]'] = '';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
$this->assertRaw(t('@name field is required.', ['@name' => $field->label()]), 'Field validation error was correctly reported.');
|
||||
// Invalid input.
|
||||
$edit['test_user_field[0][value]'] = '-1';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
$this->assertRaw(t('%name does not accept the value -1.', ['%name' => $field->label()]), 'Field validation error was correctly reported.');
|
||||
|
||||
// Submit with valid data.
|
||||
$value = rand(1, 255);
|
||||
$edit['test_user_field[0][value]'] = $value;
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->test_user_field->value, $value, 'The field value was correctly saved.');
|
||||
|
||||
// Check that the 'add more' button works.
|
||||
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$field_storage->save();
|
||||
foreach (['js', 'nojs'] as $js) {
|
||||
$this->drupalGet('user/register');
|
||||
$this->assertRegistrationFormCacheTagsWithUserFields();
|
||||
// Add two inputs.
|
||||
$value = rand(1, 255);
|
||||
$edit = [];
|
||||
$edit['test_user_field[0][value]'] = $value;
|
||||
if ($js == 'js') {
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'test_user_field_add_more');
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'test_user_field_add_more');
|
||||
}
|
||||
else {
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
}
|
||||
// Submit with three values.
|
||||
$edit['test_user_field[1][value]'] = $value + 1;
|
||||
$edit['test_user_field[2][value]'] = $value + 2;
|
||||
$edit['name'] = $name = $this->randomMachineName();
|
||||
$edit['mail'] = $mail = $edit['name'] . '@example.com';
|
||||
$this->drupalPostForm(NULL, $edit, t('Create new account'));
|
||||
// Check user fields.
|
||||
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
|
||||
->loadByProperties(['name' => $name, 'mail' => $mail]);
|
||||
$new_user = reset($accounts);
|
||||
$this->assertEqual($new_user->test_user_field[0]->value, $value, format_string('@js : The field value was correctly saved.', ['@js' => $js]));
|
||||
$this->assertEqual($new_user->test_user_field[1]->value, $value + 1, format_string('@js : The field value was correctly saved.', ['@js' => $js]));
|
||||
$this->assertEqual($new_user->test_user_field[2]->value, $value + 2, format_string('@js : The field value was correctly saved.', ['@js' => $js]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the presence of cache tags on registration form with user fields.
|
||||
*/
|
||||
protected function assertRegistrationFormCacheTagsWithUserFields() {
|
||||
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
|
||||
$this->assertCacheTag('config:field.field.user.user.test_user_field');
|
||||
$this->assertCacheTag('config:field.storage.user.test_user_field');
|
||||
$this->assertCacheTag('config:user.settings');
|
||||
}
|
||||
|
||||
}
|
36
web/core/modules/user/src/Tests/UserResetEmailTestTrait.php
Normal file
36
web/core/modules/user/src/Tests/UserResetEmailTestTrait.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
@trigger_error(__NAMESPACE__ . '\UserResetEmailTestTrait is deprecated and scheduled for removal before Drupal 9.0.0. Add the method to the test class instead, see https://www.drupal.org/node/2999766', E_USER_DEPRECATED);
|
||||
|
||||
use Drupal\Core\Test\AssertMailTrait;
|
||||
|
||||
/**
|
||||
* Helper function for logging in from reset password email.
|
||||
*
|
||||
* @deprecated Scheduled for removal before Drupal 9.0.0.
|
||||
* Add the method to the test class instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2999766
|
||||
*/
|
||||
trait UserResetEmailTestTrait {
|
||||
|
||||
use AssertMailTrait {
|
||||
getMails as drupalGetMails;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login from reset password email.
|
||||
*/
|
||||
protected function loginFromResetEmail() {
|
||||
$_emails = $this->drupalGetMails();
|
||||
$email = end($_emails);
|
||||
$urls = [];
|
||||
preg_match('#.+user/reset/.+#', $email['body'], $urls);
|
||||
$resetURL = $urls[0];
|
||||
$this->drupalGet($resetURL);
|
||||
$this->drupalPostForm(NULL, NULL, 'Log in');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests adding, editing and deleting user roles and changing role weights.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserRoleAdminTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* User with admin privileges.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->adminUser = $this->drupalCreateUser(['administer permissions', 'administer users']);
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding, renaming and deleting roles.
|
||||
*/
|
||||
public function testRoleAdministration() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
|
||||
// Test presence of tab.
|
||||
$this->drupalGet('admin/people/permissions');
|
||||
$tabs = $this->xpath('//ul[@class=:classes and //a[contains(., :text)]]', [
|
||||
':classes' => 'tabs primary',
|
||||
':text' => t('Roles'),
|
||||
]);
|
||||
$this->assertEqual(count($tabs), 1, 'Found roles tab');
|
||||
|
||||
// Test adding a role. (In doing so, we use a role name that happens to
|
||||
// correspond to an integer, to test that the role administration pages
|
||||
// correctly distinguish between role names and IDs.)
|
||||
$role_name = '123';
|
||||
$edit = ['label' => $role_name, 'id' => $role_name];
|
||||
$this->drupalPostForm('admin/people/roles/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('Role %label has been added.', ['%label' => 123]));
|
||||
$role = Role::load($role_name);
|
||||
$this->assertTrue(is_object($role), 'The role was successfully retrieved from the database.');
|
||||
|
||||
// Check that the role was created in site default language.
|
||||
$this->assertEqual($role->language()->getId(), $default_langcode);
|
||||
|
||||
// Try adding a duplicate role.
|
||||
$this->drupalPostForm('admin/people/roles/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('The machine-readable name is already in use. It must be unique.'), 'Duplicate role warning displayed.');
|
||||
|
||||
// Test renaming a role.
|
||||
$role_name = '456';
|
||||
$edit = ['label' => $role_name];
|
||||
$this->drupalPostForm("admin/people/roles/manage/{$role->id()}", $edit, t('Save'));
|
||||
$this->assertRaw(t('Role %label has been updated.', ['%label' => $role_name]));
|
||||
\Drupal::entityManager()->getStorage('user_role')->resetCache([$role->id()]);
|
||||
$new_role = Role::load($role->id());
|
||||
$this->assertEqual($new_role->label(), $role_name, 'The role name has been successfully changed.');
|
||||
|
||||
// Test deleting a role.
|
||||
$this->drupalGet("admin/people/roles/manage/{$role->id()}");
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
$this->assertRaw(t('The role %label has been deleted.', ['%label' => $role_name]));
|
||||
$this->assertNoLinkByHref("admin/people/roles/manage/{$role->id()}", 'Role edit link removed.');
|
||||
\Drupal::entityManager()->getStorage('user_role')->resetCache([$role->id()]);
|
||||
$this->assertFalse(Role::load($role->id()), 'A deleted role can no longer be loaded.');
|
||||
|
||||
// Make sure that the system-defined roles can be edited via the user
|
||||
// interface.
|
||||
$this->drupalGet('admin/people/roles/manage/' . RoleInterface::ANONYMOUS_ID);
|
||||
$this->assertResponse(200, 'Access granted when trying to edit the built-in anonymous role.');
|
||||
$this->assertNoText(t('Delete role'), 'Delete button for the anonymous role is not present.');
|
||||
$this->drupalGet('admin/people/roles/manage/' . RoleInterface::AUTHENTICATED_ID);
|
||||
$this->assertResponse(200, 'Access granted when trying to edit the built-in authenticated role.');
|
||||
$this->assertNoText(t('Delete role'), 'Delete button for the authenticated role is not present.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test user role weight change operation and ordering.
|
||||
*/
|
||||
public function testRoleWeightOrdering() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$roles = user_roles();
|
||||
$weight = count($roles);
|
||||
$new_role_weights = [];
|
||||
$saved_rids = [];
|
||||
|
||||
// Change the role weights to make the roles in reverse order.
|
||||
$edit = [];
|
||||
foreach ($roles as $role) {
|
||||
$edit['entities[' . $role->id() . '][weight]'] = $weight;
|
||||
$new_role_weights[$role->id()] = $weight;
|
||||
$saved_rids[] = $role->id();
|
||||
$weight--;
|
||||
}
|
||||
$this->drupalPostForm('admin/people/roles', $edit, t('Save'));
|
||||
$this->assertText(t('The role settings have been updated.'), 'The role settings form submitted successfully.');
|
||||
|
||||
// Load up the user roles with the new weights.
|
||||
drupal_static_reset('user_roles');
|
||||
$roles = user_roles();
|
||||
$rids = [];
|
||||
// Test that the role weights have been correctly saved.
|
||||
foreach ($roles as $role) {
|
||||
$this->assertEqual($role->getWeight(), $new_role_weights[$role->id()]);
|
||||
$rids[] = $role->id();
|
||||
}
|
||||
// The order of the roles should be reversed.
|
||||
$this->assertIdentical($rids, array_reverse($saved_rids));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Set a user time zone and verify that dates are displayed in local time.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserTimeZoneTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'system_test'];
|
||||
|
||||
/**
|
||||
* Tests the display of dates and time when user-configurable time zones are set.
|
||||
*/
|
||||
public function testUserTimeZone() {
|
||||
// Setup date/time settings for Los Angeles time.
|
||||
$this->config('system.date')
|
||||
->set('timezone.user.configurable', 1)
|
||||
->set('timezone.default', 'America/Los_Angeles')
|
||||
->save();
|
||||
|
||||
// Load the 'medium' date format, which is the default for node creation
|
||||
// time, and override it. Since we are testing time zones with Daylight
|
||||
// Saving Time, and need to future proof against changes to the zoneinfo
|
||||
// database, we choose the 'I' format placeholder instead of a
|
||||
// human-readable zone name. With 'I', a 1 means the date is in DST, and 0
|
||||
// if not.
|
||||
DateFormat::load('medium')
|
||||
->setPattern('Y-m-d H:i I')
|
||||
->save();
|
||||
|
||||
// Create a user account and login.
|
||||
$web_user = $this->drupalCreateUser();
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create some nodes with different authored-on dates.
|
||||
// Two dates in PST (winter time):
|
||||
$date1 = '2007-03-09 21:00:00 -0800';
|
||||
$date2 = '2007-03-11 01:00:00 -0800';
|
||||
// One date in PDT (summer time):
|
||||
$date3 = '2007-03-20 21:00:00 -0700';
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
$node1 = $this->drupalCreateNode(['created' => strtotime($date1), 'type' => 'article']);
|
||||
$node2 = $this->drupalCreateNode(['created' => strtotime($date2), 'type' => 'article']);
|
||||
$node3 = $this->drupalCreateNode(['created' => strtotime($date3), 'type' => 'article']);
|
||||
|
||||
// Confirm date format and time zone.
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText('2007-03-09 21:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node2->id());
|
||||
$this->assertText('2007-03-11 01:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node3->id());
|
||||
$this->assertText('2007-03-20 21:00 1', 'Date should be PDT.');
|
||||
|
||||
// Change user time zone to Santiago time.
|
||||
$edit = [];
|
||||
$edit['mail'] = $web_user->getEmail();
|
||||
$edit['timezone'] = 'America/Santiago';
|
||||
$this->drupalPostForm("user/" . $web_user->id() . "/edit", $edit, t('Save'));
|
||||
$this->assertText(t('The changes have been saved.'), 'Time zone changed to Santiago time.');
|
||||
|
||||
// Confirm date format and time zone.
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText('2007-03-10 02:00 1', 'Date should be Chile summer time; five hours ahead of PST.');
|
||||
$this->drupalGet('node/' . $node2->id());
|
||||
$this->assertText('2007-03-11 05:00 0', 'Date should be Chile time; four hours ahead of PST');
|
||||
$this->drupalGet('node/' . $node3->id());
|
||||
$this->assertText('2007-03-21 00:00 0', 'Date should be Chile time; three hours ahead of PDT.');
|
||||
|
||||
// Ensure that anonymous users also use the default timezone.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('node/' . $node1->id());
|
||||
$this->assertText('2007-03-09 21:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node2->id());
|
||||
$this->assertText('2007-03-11 01:00 0', 'Date should be PST.');
|
||||
$this->drupalGet('node/' . $node3->id());
|
||||
$this->assertText('2007-03-20 21:00 1', 'Date should be PDT.');
|
||||
|
||||
// Format a date without accessing the current user at all and
|
||||
// ensure that it uses the default timezone.
|
||||
$this->drupalGet('/system-test/date');
|
||||
$this->assertText('2016-01-13 08:29 0', 'Date should be PST.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\content_translation\Tests\ContentTranslationUITestBase;
|
||||
|
||||
/**
|
||||
* Tests the User Translation UI.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserTranslationUITest extends ContentTranslationUITestBase {
|
||||
|
||||
/**
|
||||
* The user name of the test user.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'content_translation', 'user', 'views'];
|
||||
|
||||
protected function setUp() {
|
||||
$this->entityTypeId = 'user';
|
||||
$this->testLanguageSelector = FALSE;
|
||||
$this->name = $this->randomMachineName();
|
||||
parent::setUp();
|
||||
|
||||
\Drupal::entityManager()->getStorage('user')->resetCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getTranslatorPermissions() {
|
||||
return array_merge(parent::getTranslatorPermissions(), ['administer users']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNewEntityValues($langcode) {
|
||||
// User name is not translatable hence we use a fixed value.
|
||||
return ['name' => $this->name] + parent::getNewEntityValues($langcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
// We only want to test the title for non-english translations.
|
||||
if ($langcode != 'en') {
|
||||
$options = ['language' => $languages[$langcode]];
|
||||
$url = $entity->urlInfo('edit-form', $options);
|
||||
$this->drupalGet($url);
|
||||
|
||||
$title = t('@title [%language translation]', [
|
||||
'@title' => $entity->getTranslation($langcode)->label(),
|
||||
'%language' => $languages[$langcode]->getName(),
|
||||
]);
|
||||
$this->assertRaw($title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\user\Plugin\views\access\Permission;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests views perm access plugin.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\access\Permission
|
||||
*/
|
||||
class AccessPermissionTest extends AccessTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_access_perm'];
|
||||
|
||||
/**
|
||||
* Tests perm access plugin.
|
||||
*/
|
||||
public function testAccessPerm() {
|
||||
$view = Views::getView('test_access_perm');
|
||||
$view->setDisplay();
|
||||
|
||||
$access_plugin = $view->display_handler->getPlugin('access');
|
||||
$this->assertTrue($access_plugin instanceof Permission, 'Make sure the right class got instantiated.');
|
||||
$this->assertEqual($access_plugin->pluginTitle(), t('Permission'));
|
||||
|
||||
$this->assertFalse($view->display_handler->access($this->webUser));
|
||||
$this->assertTrue($view->display_handler->access($this->normalUser));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access on render caching.
|
||||
*/
|
||||
public function testRenderCaching() {
|
||||
$view = Views::getView('test_access_perm');
|
||||
$display = &$view->storage->getDisplay('default');
|
||||
$display['display_options']['cache'] = [
|
||||
'type' => 'tag',
|
||||
];
|
||||
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
/** @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */
|
||||
$account_switcher = \Drupal::service('account_switcher');
|
||||
|
||||
// First access as user without access.
|
||||
$build = DisplayPluginBase::buildBasicRenderable('test_access_perm', 'default');
|
||||
$account_switcher->switchTo($this->normalUser);
|
||||
$result = $renderer->renderPlain($build);
|
||||
$this->assertNotEqual($result, '');
|
||||
|
||||
// Then with access.
|
||||
$build = DisplayPluginBase::buildBasicRenderable('test_access_perm', 'default');
|
||||
$account_switcher->switchTo($this->webUser);
|
||||
$result = $renderer->renderPlain($build);
|
||||
$this->assertEqual($result, '');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\user\Plugin\views\access\Role;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests views role access plugin.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\access\Role
|
||||
*/
|
||||
class AccessRoleTest extends AccessTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_access_role'];
|
||||
|
||||
/**
|
||||
* Tests role access plugin.
|
||||
*/
|
||||
public function testAccessRole() {
|
||||
/** @var \Drupal\views\ViewEntityInterface $view */
|
||||
$view = \Drupal::entityManager()->getStorage('view')->load('test_access_role');
|
||||
$display = &$view->getDisplay('default');
|
||||
$display['display_options']['access']['options']['role'] = [
|
||||
$this->normalRole => $this->normalRole,
|
||||
];
|
||||
$view->save();
|
||||
$this->container->get('router.builder')->rebuildIfNeeded();
|
||||
$expected = [
|
||||
'config' => ['user.role.' . $this->normalRole],
|
||||
'module' => ['user'],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->calculateDependencies()->getDependencies());
|
||||
|
||||
$executable = Views::executableFactory()->get($view);
|
||||
$executable->setDisplay('page_1');
|
||||
|
||||
$access_plugin = $executable->display_handler->getPlugin('access');
|
||||
$this->assertTrue($access_plugin instanceof Role, 'Make sure the right class got instantiated.');
|
||||
|
||||
// Test the access() method on the access plugin.
|
||||
$this->assertFalse($executable->display_handler->access($this->webUser));
|
||||
$this->assertTrue($executable->display_handler->access($this->normalUser));
|
||||
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertResponse(403);
|
||||
$this->assertCacheContext('user.roles');
|
||||
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertResponse(200);
|
||||
$this->assertCacheContext('user.roles');
|
||||
|
||||
// Test allowing multiple roles.
|
||||
$view = Views::getView('test_access_role')->storage;
|
||||
$display = &$view->getDisplay('default');
|
||||
$display['display_options']['access']['options']['role'] = [
|
||||
$this->normalRole => $this->normalRole,
|
||||
'anonymous' => 'anonymous',
|
||||
];
|
||||
$view->save();
|
||||
$this->container->get('router.builder')->rebuildIfNeeded();
|
||||
|
||||
// Ensure that the list of roles is sorted correctly, if the generated role
|
||||
// ID comes before 'anonymous', see https://www.drupal.org/node/2398259.
|
||||
$roles = ['user.role.anonymous', 'user.role.' . $this->normalRole];
|
||||
sort($roles);
|
||||
$expected = [
|
||||
'config' => $roles,
|
||||
'module' => ['user'],
|
||||
];
|
||||
$this->assertIdentical($expected, $view->calculateDependencies()->getDependencies());
|
||||
$this->drupalLogin($this->webUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertResponse(403);
|
||||
$this->assertCacheContext('user.roles');
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertResponse(200);
|
||||
$this->assertCacheContext('user.roles');
|
||||
$this->drupalLogin($this->normalUser);
|
||||
$this->drupalGet('test-role');
|
||||
$this->assertResponse(200);
|
||||
$this->assertCacheContext('user.roles');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access on render caching.
|
||||
*/
|
||||
public function testRenderCaching() {
|
||||
$view = Views::getView('test_access_role');
|
||||
$display = &$view->storage->getDisplay('default');
|
||||
$display['display_options']['cache'] = [
|
||||
'type' => 'tag',
|
||||
];
|
||||
$display['display_options']['access']['options']['role'] = [
|
||||
$this->normalRole => $this->normalRole,
|
||||
];
|
||||
$view->save();
|
||||
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
/** @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */
|
||||
$account_switcher = \Drupal::service('account_switcher');
|
||||
|
||||
// First access as user with access.
|
||||
$build = DisplayPluginBase::buildBasicRenderable('test_access_role', 'default');
|
||||
$account_switcher->switchTo($this->normalUser);
|
||||
$result = $renderer->renderPlain($build);
|
||||
$this->assertTrue(in_array('user.roles', $build['#cache']['contexts']));
|
||||
$this->assertEqual(['config:views.view.test_access_role'], $build['#cache']['tags']);
|
||||
$this->assertEqual(Cache::PERMANENT, $build['#cache']['max-age']);
|
||||
$this->assertNotEqual($result, '');
|
||||
|
||||
// Then without access.
|
||||
$build = DisplayPluginBase::buildBasicRenderable('test_access_role', 'default');
|
||||
$account_switcher->switchTo($this->webUser);
|
||||
$result = $renderer->renderPlain($build);
|
||||
// @todo Fix this in https://www.drupal.org/node/2551037,
|
||||
// DisplayPluginBase::applyDisplayCachablityMetadata() is not invoked when
|
||||
// using buildBasicRenderable() and a Views access plugin returns FALSE.
|
||||
//$this->assertTrue(in_array('user.roles', $build['#cache']['contexts']));
|
||||
//$this->assertEqual([], $build['#cache']['tags']);
|
||||
$this->assertEqual(Cache::PERMANENT, $build['#cache']['max-age']);
|
||||
$this->assertEqual($result, '');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\views_ui\Tests\UITestBase;
|
||||
|
||||
/**
|
||||
* Tests views role access plugin UI.
|
||||
*
|
||||
* @group user
|
||||
* @see Drupal\user\Plugin\views\access\Role
|
||||
*/
|
||||
class AccessRoleUITest extends UITestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_access_role'];
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'user_test_views'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the role access plugin UI.
|
||||
*/
|
||||
public function testAccessRoleUI() {
|
||||
$entity_manager = $this->container->get('entity.manager');
|
||||
$entity_manager->getStorage('user_role')->create(['id' => 'custom_role', 'label' => 'Custom role'])->save();
|
||||
$access_url = "admin/structure/views/nojs/display/test_access_role/default/access_options";
|
||||
$this->drupalPostForm($access_url, ['access_options[role][custom_role]' => 1], t('Apply'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$view = $entity_manager->getStorage('view')->load('test_access_role');
|
||||
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertEqual($display['display_options']['access']['options']['role'], ['custom_role' => 'custom_role']);
|
||||
|
||||
// Test changing access plugin from role to none.
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_access_role/default/access', ['access[type]' => 'none'], t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
// Verify that role option is not set.
|
||||
$view = $entity_manager->getStorage('view')->load('test_access_role');
|
||||
$display = $view->getDisplay('default');
|
||||
$this->assertFalse(isset($display['display_options']['access']['options']['role']));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
/**
|
||||
* A common test base class for the user access plugin tests.
|
||||
*/
|
||||
abstract class AccessTestBase extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block'];
|
||||
|
||||
/**
|
||||
* Contains a user object that has no special permissions.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Contains a user object that has the 'views_test_data test permission'.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $normalUser;
|
||||
|
||||
/**
|
||||
* Contains a role ID that is used by the webUser.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $webRole;
|
||||
|
||||
/**
|
||||
* Contains a role ID that is used by the normalUser.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $normalRole;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser();
|
||||
$roles = $this->webUser->getRoles();
|
||||
$this->webRole = $roles[0];
|
||||
|
||||
$this->normalRole = $this->drupalCreateRole([]);
|
||||
$this->normalUser = $this->drupalCreateUser(['views_test_data test permission']);
|
||||
$this->normalUser->addRole($this->normalRole);
|
||||
$this->normalUser->save();
|
||||
// @todo when all the plugin information is cached make a reset function and
|
||||
// call it here.
|
||||
}
|
||||
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests views user argument default plugin.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class ArgumentDefaultTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_plugin_argument_default_current_user'];
|
||||
|
||||
public function test_plugin_argument_default_current_user() {
|
||||
// Create a user to test.
|
||||
$account = $this->drupalCreateUser();
|
||||
|
||||
// Switch the user.
|
||||
\Drupal::service('account_switcher')->switchTo($account);
|
||||
|
||||
$view = Views::getView('test_plugin_argument_default_current_user');
|
||||
$view->initHandlers();
|
||||
|
||||
$this->assertEqual($view->argument['null']->getDefaultArgument(), $account->id(), 'Uid of the current user is used.');
|
||||
// Switch back.
|
||||
\Drupal::service('account_switcher')->switchBack();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\views\Plugin\views\argument\ArgumentPluginBase;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests user argument validators for ID and name.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class ArgumentValidateTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_view_argument_validate_user', 'test_view_argument_validate_username'];
|
||||
|
||||
/**
|
||||
* A user for this test.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->account = $this->drupalCreateUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the User (ID) argument validator.
|
||||
*/
|
||||
public function testArgumentValidateUserUid() {
|
||||
$account = $this->account;
|
||||
|
||||
$view = Views::getView('test_view_argument_validate_user');
|
||||
$this->executeView($view);
|
||||
|
||||
$this->assertTrue($view->argument['null']->validateArgument($account->id()));
|
||||
// Reset argument validation.
|
||||
$view->argument['null']->argument_validated = NULL;
|
||||
// Fail for a valid numeric, but for a user that doesn't exist
|
||||
$this->assertFalse($view->argument['null']->validateArgument(32));
|
||||
|
||||
$form = [];
|
||||
$form_state = new FormState();
|
||||
$view->argument['null']->buildOptionsForm($form, $form_state);
|
||||
$sanitized_id = ArgumentPluginBase::encodeValidatorId('entity:user');
|
||||
$this->assertTrue($form['validate']['options'][$sanitized_id]['roles']['#states']['visible'][':input[name="options[validate][options][' . $sanitized_id . '][restrict_roles]"]']['checked']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the UserName argument validator.
|
||||
*/
|
||||
public function testArgumentValidateUserName() {
|
||||
$account = $this->account;
|
||||
|
||||
$view = Views::getView('test_view_argument_validate_username');
|
||||
$this->executeView($view);
|
||||
|
||||
$this->assertTrue($view->argument['null']->validateArgument($account->getUsername()));
|
||||
// Reset argument validation.
|
||||
$view->argument['null']->argument_validated = NULL;
|
||||
// Fail for a valid string, but for a user that doesn't exist
|
||||
$this->assertFalse($view->argument['null']->validateArgument($this->randomMachineName()));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests if entity access is respected on a user bulk form.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\field\UserBulkForm
|
||||
* @see \Drupal\user\Tests\Views\BulkFormTest
|
||||
*/
|
||||
class BulkFormAccessTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user_access_test'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_bulk_form'];
|
||||
|
||||
/**
|
||||
* Tests if users that may not be edited, can not be edited in bulk.
|
||||
*/
|
||||
public function testUserEditAccess() {
|
||||
// Create an authenticated user.
|
||||
$no_edit_user = $this->drupalCreateUser([], 'no_edit');
|
||||
// Ensure this account is not blocked.
|
||||
$this->assertFalse($no_edit_user->isBlocked(), 'The user is not blocked.');
|
||||
|
||||
// Log in as user admin.
|
||||
$admin_user = $this->drupalCreateUser(['administer users']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
// Ensure that the account "no_edit" can not be edited.
|
||||
$this->drupalGet('user/' . $no_edit_user->id() . '/edit');
|
||||
$this->assertFalse($no_edit_user->access('update', $admin_user));
|
||||
$this->assertResponse(403, 'The user may not be edited.');
|
||||
|
||||
// Test blocking the account "no_edit".
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($no_edit_user->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$this->assertRaw(SafeMarkup::format('No access to execute %action on the @entity_type_label %entity_label.', [
|
||||
'%action' => 'Block the selected user(s)',
|
||||
'@entity_type_label' => 'User',
|
||||
'%entity_label' => $no_edit_user->label(),
|
||||
]));
|
||||
|
||||
// Re-load the account "no_edit" and ensure it is not blocked.
|
||||
$no_edit_user = User::load($no_edit_user->id());
|
||||
$this->assertFalse($no_edit_user->isBlocked(), 'The user is not blocked.');
|
||||
|
||||
// Create a normal user which can be edited by the admin user
|
||||
$normal_user = $this->drupalCreateUser();
|
||||
$this->assertTrue($normal_user->access('update', $admin_user));
|
||||
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
|
||||
$normal_user = User::load($normal_user->id());
|
||||
$this->assertTrue($normal_user->isBlocked(), 'The user is blocked.');
|
||||
|
||||
// Log in as user without the 'administer users' permission.
|
||||
$this->drupalLogin($this->drupalCreateUser());
|
||||
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_unblock_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
|
||||
// Re-load the normal user and ensure it is still blocked.
|
||||
$normal_user = User::load($normal_user->id());
|
||||
$this->assertTrue($normal_user->isBlocked(), 'The user is still blocked.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if users that may not be deleted, can not be deleted in bulk.
|
||||
*/
|
||||
public function testUserDeleteAccess() {
|
||||
// Create two authenticated users.
|
||||
$account = $this->drupalCreateUser([], 'no_delete');
|
||||
$account2 = $this->drupalCreateUser([], 'may_delete');
|
||||
|
||||
// Log in as user admin.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer users']));
|
||||
|
||||
// Ensure that the account "no_delete" can not be deleted.
|
||||
$this->drupalGet('user/' . $account->id() . '/cancel');
|
||||
$this->assertResponse(403, 'The user "no_delete" may not be deleted.');
|
||||
// Ensure that the account "may_delete" *can* be deleted.
|
||||
$this->drupalGet('user/' . $account2->id() . '/cancel');
|
||||
$this->assertResponse(200, 'The user "may_delete" may be deleted.');
|
||||
|
||||
// Test deleting the accounts "no_delete" and "may_delete".
|
||||
$edit = [
|
||||
'user_bulk_form[' . ($account->id() - 1) . ']' => TRUE,
|
||||
'user_bulk_form[' . ($account2->id() - 1) . ']' => TRUE,
|
||||
'action' => 'user_cancel_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
$edit = [
|
||||
'user_cancel_method' => 'user_cancel_delete',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Cancel accounts'));
|
||||
|
||||
// Ensure the account "no_delete" still exists.
|
||||
$account = User::load($account->id());
|
||||
$this->assertNotNull($account, 'The user "no_delete" is not deleted.');
|
||||
// Ensure the account "may_delete" no longer exists.
|
||||
$account = User::load($account2->id());
|
||||
$this->assertNull($account, 'The user "may_delete" is deleted.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests a user bulk form.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\field\UserBulkForm
|
||||
*/
|
||||
class BulkFormTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views_ui'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_bulk_form', 'test_user_bulk_form_combine_filter'];
|
||||
|
||||
/**
|
||||
* Tests the user bulk form.
|
||||
*/
|
||||
public function testBulkForm() {
|
||||
// Log in as a user without 'administer users'.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer permissions']));
|
||||
$user_storage = $this->container->get('entity.manager')->getStorage('user');
|
||||
|
||||
// Create an user which actually can change users.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer users']));
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$result = $this->cssSelect('#edit-action option');
|
||||
$this->assertTrue(count($result) > 0);
|
||||
|
||||
// Test submitting the page with no selection.
|
||||
$edit = [
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
|
||||
$this->assertText(t('No users selected.'));
|
||||
|
||||
// Assign a role to a user.
|
||||
$account = $user_storage->load($this->users[0]->id());
|
||||
$roles = user_role_names(TRUE);
|
||||
unset($roles[RoleInterface::AUTHENTICATED_ID]);
|
||||
$role = key($roles);
|
||||
|
||||
$this->assertFalse($account->hasRole($role), 'The user currently does not have a custom role.');
|
||||
$edit = [
|
||||
'user_bulk_form[1]' => TRUE,
|
||||
'action' => 'user_add_role_action.' . $role,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
// Re-load the user and check their roles.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->hasRole($role), 'The user now has the custom role.');
|
||||
|
||||
$edit = [
|
||||
'user_bulk_form[1]' => TRUE,
|
||||
'action' => 'user_remove_role_action.' . $role,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
// Re-load the user and check their roles.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertFalse($account->hasRole($role), 'The user no longer has the custom role.');
|
||||
|
||||
// Block a user using the bulk form.
|
||||
$this->assertTrue($account->isActive(), 'The user is not blocked.');
|
||||
$this->assertRaw($account->label(), 'The user is found in the table.');
|
||||
$edit = [
|
||||
'user_bulk_form[1]' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
// Re-load the user and check their status.
|
||||
$user_storage->resetCache([$account->id()]);
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isBlocked(), 'The user is blocked.');
|
||||
$this->assertNoRaw($account->label(), 'The user is not found in the table.');
|
||||
|
||||
// Remove the user status filter from the view.
|
||||
$view = Views::getView('test_user_bulk_form');
|
||||
$view->removeHandler('default', 'filter', 'status');
|
||||
$view->storage->save();
|
||||
|
||||
// Ensure the anonymous user is found.
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$this->assertText($this->config('user.settings')->get('anonymous'));
|
||||
|
||||
// Attempt to block the anonymous user.
|
||||
$edit = [
|
||||
'user_bulk_form[0]' => TRUE,
|
||||
'action' => 'user_block_user_action',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
$anonymous_account = $user_storage->load(0);
|
||||
$this->assertTrue($anonymous_account->isBlocked(), 'Ensure the anonymous user got blocked.');
|
||||
|
||||
// Test the list of available actions with a value that contains a dot.
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer permissions', 'administer views', 'administer users']));
|
||||
$action_id = 'user_add_role_action.' . $role;
|
||||
$edit = [
|
||||
'options[include_exclude]' => 'exclude',
|
||||
"options[selected_actions][$action_id]" => $action_id,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_user_bulk_form/default/field/user_bulk_form', $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$this->assertNoOption('edit-action', $action_id);
|
||||
$edit['options[include_exclude]'] = 'include';
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_user_bulk_form/default/field/user_bulk_form', $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->drupalGet('test-user-bulk-form');
|
||||
$this->assertOption('edit-action', $action_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the user bulk form with a combined field filter on the bulk column.
|
||||
*/
|
||||
public function testBulkFormCombineFilter() {
|
||||
// Add a user.
|
||||
User::load($this->users[0]->id());
|
||||
$view = Views::getView('test_user_bulk_form_combine_filter');
|
||||
$errors = $view->validate();
|
||||
$this->assertEqual(reset($errors['default']), t('Field %field set in %filter is not usable for this filter type. Combined field filter only works for simple fields.', ['%field' => 'User: Bulk update', '%filter' => 'Global: Combine fields filter']));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests the permission field handler ui.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\filter\Permissions
|
||||
*/
|
||||
class FilterPermissionUiTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_filter_permission'];
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user', 'user_test_views', 'views_ui'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp(TRUE);
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
$this->enableViewsTestModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic filter handler settings in the UI.
|
||||
*/
|
||||
public function testHandlerUI() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer views', 'administer users']));
|
||||
|
||||
$this->drupalGet('admin/structure/views/view/test_filter_permission/edit/default');
|
||||
// Verify that the handler summary is correctly displaying the selected
|
||||
// permission.
|
||||
$this->assertLink('User: Permission (= View user information)');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
// Verify that we can save the view.
|
||||
$this->assertNoText('No valid values found on filter: User: Permission.');
|
||||
$this->assertText('The view test_filter_permission has been saved.');
|
||||
|
||||
// Verify that the handler summary is also correct when multiple values are
|
||||
// selected in the filter.
|
||||
$edit = [
|
||||
'options[value][]' => [
|
||||
'access user profiles',
|
||||
'administer views',
|
||||
],
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_filter_permission/default/filter/permission', $edit, 'Apply');
|
||||
$this->assertLink('User: Permission (or View us…)');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
// Verify that we can save the view.
|
||||
$this->assertNoText('No valid values found on filter: User: Permission.');
|
||||
$this->assertText('The view test_filter_permission has been saved.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: uid Argument.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class HandlerArgumentUserUidTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_uid_argument'];
|
||||
|
||||
/**
|
||||
* Tests the generated title of an user: uid argument.
|
||||
*/
|
||||
public function testArgumentTitle() {
|
||||
$view = Views::getView('test_user_uid_argument');
|
||||
|
||||
// Tests an invalid user uid.
|
||||
$this->executeView($view, [rand(1000, 10000)]);
|
||||
$this->assertFalse($view->getTitle());
|
||||
$view->destroy();
|
||||
|
||||
// Tests a valid user.
|
||||
$account = $this->drupalCreateUser();
|
||||
$this->executeView($view, [$account->id()]);
|
||||
$this->assertEqual($view->getTitle(), $account->label());
|
||||
$view->destroy();
|
||||
|
||||
// Tests the anonymous user.
|
||||
$anonymous = $this->config('user.settings')->get('anonymous');
|
||||
$this->executeView($view, [0]);
|
||||
$this->assertEqual($view->getTitle(), $anonymous);
|
||||
$view->destroy();
|
||||
|
||||
$view->getDisplay()->getHandler('argument', 'uid')->options['break_phrase'] = TRUE;
|
||||
$this->executeView($view, [$account->id() . ',0']);
|
||||
$this->assertEqual($view->getTitle(), $account->label() . ', ' . $anonymous);
|
||||
$view->destroy();
|
||||
|
||||
$view->getDisplay()->getHandler('argument', 'uid')->options['break_phrase'] = TRUE;
|
||||
$this->executeView($view, ['0,' . $account->id()]);
|
||||
$this->assertEqual($view->getTitle(), $anonymous . ', ' . $account->label());
|
||||
$view->destroy();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: role field.
|
||||
*
|
||||
* @group user
|
||||
* @see views_handler_field_user_name
|
||||
*/
|
||||
class HandlerFieldRoleTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_views_handler_field_role'];
|
||||
|
||||
public function testRole() {
|
||||
// Create a couple of roles for the view.
|
||||
$rolename_a = 'a' . $this->randomMachineName(8);
|
||||
$this->drupalCreateRole(['access content'], $rolename_a, '<em>' . $rolename_a . '</em>', 9);
|
||||
|
||||
$rolename_b = 'b' . $this->randomMachineName(8);
|
||||
$this->drupalCreateRole(['access content'], $rolename_b, $rolename_b, 8);
|
||||
|
||||
$rolename_not_assigned = $this->randomMachineName(8);
|
||||
$this->drupalCreateRole(['access content'], $rolename_not_assigned, $rolename_not_assigned);
|
||||
|
||||
// Add roles to user 1.
|
||||
$user = User::load(1);
|
||||
$user->addRole($rolename_a);
|
||||
$user->addRole($rolename_b);
|
||||
$user->save();
|
||||
|
||||
$this->drupalLogin($this->createUser(['access user profiles']));
|
||||
$this->drupalGet('/test-views-handler-field-role');
|
||||
$this->assertText($rolename_b . Html::escape('<em>' . $rolename_a . '</em>'), 'View test_views_handler_field_role renders role assigned to user in the correct order and markup in role names is escaped.');
|
||||
$this->assertNoText($rolename_not_assigned, 'View test_views_handler_field_role does not render a role not assigned to a user.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: name field.
|
||||
*
|
||||
* @group user
|
||||
* @see views_handler_field_user_name
|
||||
*/
|
||||
class HandlerFieldUserNameTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_views_handler_field_user_name'];
|
||||
|
||||
public function testUserName() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
$new_user = $this->drupalCreateUser(['access user profiles']);
|
||||
$this->drupalLogin($new_user);
|
||||
|
||||
// Set defaults.
|
||||
$view = Views::getView('test_views_handler_field_user_name');
|
||||
$view->initHandlers();
|
||||
$view->field['name']->options['link_to_user'] = TRUE;
|
||||
$view->field['name']->options['type'] = 'user_name';
|
||||
$view->field['name']->init($view, $view->getDisplay('default'));
|
||||
$view->field['name']->options['id'] = 'name';
|
||||
$this->executeView($view);
|
||||
|
||||
$anon_name = $this->config('user.settings')->get('anonymous');
|
||||
$view->result[0]->_entity->setUsername('');
|
||||
$view->result[0]->_entity->uid->value = 0;
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
|
||||
return $view->field['name']->advancedRender($view->result[0]);
|
||||
});
|
||||
$this->assertTrue(strpos($render, $anon_name) !== FALSE, 'For user 0 it should use the default anonymous name by default.');
|
||||
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view, $new_user) {
|
||||
return $view->field['name']->advancedRender($view->result[$new_user->id()]);
|
||||
});
|
||||
$this->assertTrue(strpos($render, $new_user->getDisplayName()) !== FALSE, 'If link to user is checked the username should be part of the output.');
|
||||
$this->assertTrue(strpos($render, 'user/' . $new_user->id()) !== FALSE, 'If link to user is checked the link to the user should appear as well.');
|
||||
|
||||
$view->field['name']->options['link_to_user'] = FALSE;
|
||||
$view->field['name']->options['type'] = 'string';
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view, $new_user) {
|
||||
return $view->field['name']->advancedRender($view->result[$new_user->id()]);
|
||||
});
|
||||
$this->assertEqual($render, $new_user->getDisplayName(), 'If the user is not linked the username should be printed out for a normal user.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the field handler works when no additional fields are added.
|
||||
*/
|
||||
public function testNoAdditionalFields() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
$view = Views::getView('test_views_handler_field_user_name');
|
||||
$this->executeView($view);
|
||||
|
||||
$username = $this->randomMachineName();
|
||||
$view->result[0]->_entity->setUsername($username);
|
||||
$view->result[0]->_entity->uid->value = 1;
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
|
||||
return $view->field['name']->advancedRender($view->result[0]);
|
||||
});
|
||||
$this->assertTrue(strpos($render, $username) !== FALSE, 'If link to user is checked the username should be part of the output.');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: name filter.
|
||||
*
|
||||
* @group user
|
||||
* @see Views\user\Plugin\views\filter\Name
|
||||
*/
|
||||
class HandlerFilterUserNameTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views_ui', 'user_test_views'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_name'];
|
||||
|
||||
/**
|
||||
* Accounts used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $accounts = [];
|
||||
|
||||
/**
|
||||
* Usernames of $accounts.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $names = [];
|
||||
|
||||
/**
|
||||
* Stores the column map for this testCase.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $columnMap = [
|
||||
'uid' => 'uid',
|
||||
];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
|
||||
$this->accounts = [];
|
||||
$this->names = [];
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$this->accounts[] = $account = $this->drupalCreateUser();
|
||||
$this->names[] = $account->label();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests just using the filter.
|
||||
*/
|
||||
public function testUserNameApi() {
|
||||
$view = Views::getView('test_user_name');
|
||||
|
||||
$view->initHandlers();
|
||||
$view->filter['uid']->value = [$this->accounts[0]->id()];
|
||||
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, [['uid' => $this->accounts[0]->id()]], $this->columnMap);
|
||||
|
||||
$this->assertEqual($view->filter['uid']->getValueOptions(), NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using the user interface.
|
||||
*/
|
||||
public function testAdminUserInterface() {
|
||||
$admin_user = $this->drupalCreateUser(['administer views', 'administer site configuration']);
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$path = 'admin/structure/views/nojs/handler/test_user_name/default/filter/uid';
|
||||
$this->drupalGet($path);
|
||||
|
||||
// Pass in an invalid username, the validation should catch it.
|
||||
$users = [$this->randomMachineName()];
|
||||
$users = array_map('strtolower', $users);
|
||||
$edit = [
|
||||
'options[value]' => implode(', ', $users)
|
||||
];
|
||||
$this->drupalPostForm($path, $edit, t('Apply'));
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in an invalid username and a valid username.
|
||||
$random_name = $this->randomMachineName();
|
||||
$users = [$random_name, $this->names[0]];
|
||||
$users = array_map('strtolower', $users);
|
||||
$edit = [
|
||||
'options[value]' => implode(', ', $users)
|
||||
];
|
||||
$users = [$users[0]];
|
||||
$this->drupalPostForm($path, $edit, t('Apply'));
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in just valid usernames.
|
||||
$users = $this->names;
|
||||
$users = array_map('strtolower', $users);
|
||||
$edit = [
|
||||
'options[value]' => implode(', ', $users)
|
||||
];
|
||||
$this->drupalPostForm($path, $edit, t('Apply'));
|
||||
$this->assertNoRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests exposed filters.
|
||||
*/
|
||||
public function testExposedFilter() {
|
||||
$path = 'test_user_name';
|
||||
|
||||
$options = [];
|
||||
|
||||
// Pass in an invalid username, the validation should catch it.
|
||||
$users = [$this->randomMachineName()];
|
||||
$users = array_map('strtolower', $users);
|
||||
$options['query']['uid'] = implode(', ', $users);
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in an invalid target_id in for the entity_autocomplete value format.
|
||||
// There should be no errors, but all results should be returned as the
|
||||
// default value for the autocomplete will not match any users so should
|
||||
// be empty.
|
||||
$options['query']['uid'] = [['target_id' => 9999]];
|
||||
$this->drupalGet($path, $options);
|
||||
// The actual result should contain all of the user ids.
|
||||
foreach ($this->accounts as $account) {
|
||||
$this->assertRaw($account->id());
|
||||
}
|
||||
|
||||
// Pass in an invalid username and a valid username.
|
||||
$users = [$this->randomMachineName(), $this->names[0]];
|
||||
$users = array_map('strtolower', $users);
|
||||
$options['query']['uid'] = implode(', ', $users);
|
||||
$users = [$users[0]];
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
|
||||
|
||||
// Pass in just valid usernames.
|
||||
$users = $this->names;
|
||||
$options['query']['uid'] = implode(', ', $users);
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertNoRaw('Unable to find user');
|
||||
// The actual result should contain all of the user ids.
|
||||
foreach ($this->accounts as $account) {
|
||||
$this->assertRaw($account->id());
|
||||
}
|
||||
|
||||
// Pass in just valid user IDs in the entity_autocomplete target_id format.
|
||||
$options['query']['uid'] = array_map(function($account) {
|
||||
return ['target_id' => $account->id()];
|
||||
}, $this->accounts);
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
$this->assertNoRaw('Unable to find user');
|
||||
// The actual result should contain all of the user ids.
|
||||
foreach ($this->accounts as $account) {
|
||||
$this->assertRaw($account->id());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the representative node relationship for users.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class RelationshipRepresentativeNodeTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_groupwise_user'];
|
||||
|
||||
/**
|
||||
* Tests the relationship.
|
||||
*/
|
||||
public function testRelationship() {
|
||||
$view = Views::getView('test_groupwise_user');
|
||||
$this->executeView($view);
|
||||
$map = ['node_field_data_users_field_data_nid' => 'nid', 'uid' => 'uid'];
|
||||
$expected_result = [
|
||||
[
|
||||
'uid' => $this->users[1]->id(),
|
||||
'nid' => $this->nodes[1]->id(),
|
||||
],
|
||||
[
|
||||
'uid' => $this->users[0]->id(),
|
||||
'nid' => $this->nodes[0]->id(),
|
||||
],
|
||||
];
|
||||
$this->assertIdenticalResultset($view, $expected_result, $map);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
/**
|
||||
* Tests the handler of the user: roles argument.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\argument\RolesRid
|
||||
*/
|
||||
class RolesRidArgumentTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_roles_rid'];
|
||||
|
||||
/**
|
||||
* Tests the generated title of a user: roles argument.
|
||||
*/
|
||||
public function testArgumentTitle() {
|
||||
$role_id = $this->createRole([], 'markup_role_name', '<em>Role name with markup</em>');
|
||||
$user = $this->createUser();
|
||||
$user->addRole($role_id);
|
||||
$user->save();
|
||||
|
||||
$this->drupalGet('/user_roles_rid_test/markup_role_name');
|
||||
$this->assertEscaped('<em>Role name with markup</em>');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests the changed field.
|
||||
*
|
||||
* @group user
|
||||
*/
|
||||
class UserChangedTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['views_ui', 'user_test_views'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_changed'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
|
||||
$this->enableViewsTestModule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests changed field.
|
||||
*/
|
||||
public function testChangedField() {
|
||||
$path = 'test_user_changed';
|
||||
|
||||
$options = [];
|
||||
|
||||
$this->drupalGet($path, $options);
|
||||
|
||||
$this->assertText(t('Updated date') . ': ' . date('Y-m-d', REQUEST_TIME));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the user data service field handler.
|
||||
*
|
||||
* @group user
|
||||
* @see \Drupal\user\Plugin\views\field\UserData
|
||||
*/
|
||||
class UserDataTest extends UserTestBase {
|
||||
|
||||
/**
|
||||
* Provides the user data service object.
|
||||
*
|
||||
* @var \Drupal\user\UserDataInterface
|
||||
*/
|
||||
protected $userData;
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_user_data'];
|
||||
|
||||
/**
|
||||
* Tests field handler.
|
||||
*/
|
||||
public function testDataField() {
|
||||
// But some random values into the user data service.
|
||||
$this->userData = $this->container->get('user.data');
|
||||
$random_value = $this->randomMachineName();
|
||||
$this->userData->set('views_test_config', $this->users[0]->id(), 'test_value_name', $random_value);
|
||||
|
||||
$view = Views::getView('test_user_data');
|
||||
$this->executeView($view);
|
||||
|
||||
$output = $view->field['data']->render($view->result[0]);
|
||||
$this->assertEqual($output, $random_value, 'A valid user data got rendered.');
|
||||
|
||||
$view->field['data']->options['data_name'] = $this->randomMachineName();
|
||||
$output = $view->field['data']->render($view->result[0]);
|
||||
$this->assertFalse($output, 'An invalid configuration does not return anything');
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* @todo.
|
||||
*/
|
||||
abstract class UserTestBase extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['user_test_views', 'node'];
|
||||
|
||||
/**
|
||||
* Users to use during this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $users = [];
|
||||
|
||||
/**
|
||||
* Nodes to use during this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodes = [];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
|
||||
|
||||
$this->users[] = $this->drupalCreateUser();
|
||||
$this->users[] = User::load(1);
|
||||
$this->nodes[] = $this->drupalCreateNode(['uid' => $this->users[0]->id()]);
|
||||
$this->nodes[] = $this->drupalCreateNode(['uid' => 1]);
|
||||
}
|
||||
|
||||
}
|
24
web/core/modules/user/src/TimeZoneItem.php
Normal file
24
web/core/modules/user/src/TimeZoneItem.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Defines a custom field item class for the 'timezone' user entity field.
|
||||
*/
|
||||
class TimeZoneItem extends StringItem {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
|
||||
$timezones = User::getAllowedTimezones();
|
||||
// We need to vary the selected timezones since we're generating a sample.
|
||||
$key = rand(0, count($timezones) - 1);
|
||||
return $timezones[$key];
|
||||
}
|
||||
|
||||
}
|
86
web/core/modules/user/src/ToolbarLinkBuilder.php
Normal file
86
web/core/modules/user/src/ToolbarLinkBuilder.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* ToolbarLinkBuilder fills out the placeholders generated in user_toolbar().
|
||||
*/
|
||||
class ToolbarLinkBuilder {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountProxyInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* ToolbarHandler constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountProxyInterface $account
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(AccountProxyInterface $account) {
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy builder callback for rendering toolbar links.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array as expected by the renderer service.
|
||||
*/
|
||||
public function renderToolbarLinks() {
|
||||
$links = [
|
||||
'account' => [
|
||||
'title' => $this->t('View profile'),
|
||||
'url' => Url::fromRoute('user.page'),
|
||||
'attributes' => [
|
||||
'title' => $this->t('User account'),
|
||||
],
|
||||
],
|
||||
'account_edit' => [
|
||||
'title' => $this->t('Edit profile'),
|
||||
'url' => Url::fromRoute('entity.user.edit_form', ['user' => $this->account->id()]),
|
||||
'attributes' => [
|
||||
'title' => $this->t('Edit user account'),
|
||||
],
|
||||
],
|
||||
'logout' => [
|
||||
'title' => $this->t('Log out'),
|
||||
'url' => Url::fromRoute('user.logout'),
|
||||
],
|
||||
];
|
||||
$build = [
|
||||
'#theme' => 'links__toolbar_user',
|
||||
'#links' => $links,
|
||||
'#attributes' => [
|
||||
'class' => ['toolbar-menu'],
|
||||
],
|
||||
'#cache' => [
|
||||
'contexts' => ['user'],
|
||||
],
|
||||
];
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy builder callback for rendering the username.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array as expected by the renderer service.
|
||||
*/
|
||||
public function renderDisplayName() {
|
||||
return [
|
||||
'#markup' => $this->account->getDisplayName(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -58,7 +58,7 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
|
|||
return AccessResult::allowed()->cachePerUser();
|
||||
}
|
||||
else {
|
||||
return AccessResultNeutral::neutral("The 'access user profiles' permission is required and the user must be active.");
|
||||
return AccessResultNeutral::neutral("The 'access user profiles' permission is required and the user must be active.")->cachePerPermissions()->addCacheableDependency($entity);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -106,7 +106,7 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
|
|||
return AccessResult::allowed()->cachePerPermissions()->cachePerUser();
|
||||
}
|
||||
else {
|
||||
return AccessResult::forbidden();
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
case 'preferred_langcode':
|
||||
|
@ -116,7 +116,7 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
|
|||
// Allow view access to own mail address and other personalization
|
||||
// settings.
|
||||
if ($operation == 'view') {
|
||||
return $is_own_account ? AccessResult::allowed()->cachePerUser() : AccessResult::forbidden();
|
||||
return $is_own_account ? AccessResult::allowed()->cachePerUser() : AccessResult::neutral();
|
||||
}
|
||||
// Anyone that can edit the user can also edit this field.
|
||||
return AccessResult::allowed()->cachePerPermissions();
|
||||
|
@ -127,14 +127,14 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
|
|||
|
||||
case 'created':
|
||||
// Allow viewing the created date, but not editing it.
|
||||
return ($operation == 'view') ? AccessResult::allowed() : AccessResult::forbidden();
|
||||
return ($operation == 'view') ? AccessResult::allowed() : AccessResult::neutral();
|
||||
|
||||
case 'roles':
|
||||
case 'status':
|
||||
case 'access':
|
||||
case 'login':
|
||||
case 'init':
|
||||
return AccessResult::forbidden();
|
||||
return AccessResult::neutral();
|
||||
}
|
||||
|
||||
return parent::checkFieldAccess($operation, $field_definition, $account, $items);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue