Update to Drupal 8.0.0 beta 14. For more information, see https://drupal.org/node/2544542
This commit is contained in:
parent
3b2511d96d
commit
81ccda77eb
2155 changed files with 54307 additions and 46870 deletions
78
core/modules/user/src/ContextProvider/CurrentUserContext.php
Normal file
78
core/modules/user/src/ContextProvider/CurrentUserContext.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\ContextProvider\CurrentUserContext.
|
||||
*/
|
||||
|
||||
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\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* Sets the current user as a context.
|
||||
*/
|
||||
class CurrentUserContext implements ContextProviderInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* The user storage.
|
||||
*
|
||||
* @var \Drupal\user\UserStorageInterface
|
||||
*/
|
||||
protected $userStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new CurrentUserContext.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The current user.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
*/
|
||||
public function __construct(AccountInterface $account, EntityManagerInterface $entity_manager) {
|
||||
$this->account = $account;
|
||||
$this->userStorage = $entity_manager->getStorage('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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')));
|
||||
$context->setContextValue($current_user);
|
||||
$cacheability = new CacheableMetadata();
|
||||
$cacheability->setCacheContexts(['user']);
|
||||
$context->addCacheableDependency($cacheability);
|
||||
|
||||
$result = [
|
||||
'current_user' => $context,
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAvailableContexts() {
|
||||
return $this->getRuntimeContexts([]);
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\user\Controller;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Datetime\DateFormatter;
|
||||
|
@ -123,7 +124,7 @@ class UserController extends ControllerBase {
|
|||
drupal_set_message($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'error');
|
||||
return $this->redirect('user.pass');
|
||||
}
|
||||
elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && ($hash === user_pass_rehash($user->getPassword(), $timestamp, $user->getLastLoginTime(), $user->id()))) {
|
||||
elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && ($hash === user_pass_rehash($user, $timestamp))) {
|
||||
$expiration_date = $user->getLastLoginTime() ? $this->dateFormatter->format($timestamp + $timeout) : NULL;
|
||||
return $this->formBuilder()->getForm('Drupal\user\Form\UserPasswordResetForm', $user, $expiration_date, $timestamp, $hash);
|
||||
}
|
||||
|
@ -161,7 +162,7 @@ class UserController extends ControllerBase {
|
|||
* The user account name.
|
||||
*/
|
||||
public function userTitle(UserInterface $user = NULL) {
|
||||
return $user ? Xss::filter($user->getUsername()) : '';
|
||||
return $user ? SafeMarkup::xssFilter($user->getUsername()) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,7 +198,7 @@ class UserController extends ControllerBase {
|
|||
$account_data = $this->userData->get('user', $user->id());
|
||||
if (isset($account_data['cancel_method']) && !empty($timestamp) && !empty($hashed_pass)) {
|
||||
// Validate expiration and hashed password/login.
|
||||
if ($timestamp <= $current && $current - $timestamp < $timeout && $user->id() && $timestamp >= $user->getLastLoginTime() && $hashed_pass == user_pass_rehash($user->getPassword(), $timestamp, $user->getLastLoginTime(), $user->id())) {
|
||||
if ($timestamp <= $current && $current - $timestamp < $timeout && $user->id() && $timestamp >= $user->getLastLoginTime() && $hashed_pass == user_pass_rehash($user, $timestamp)) {
|
||||
$edit = array(
|
||||
'user_cancel_notify' => isset($account_data['cancel_notify']) ? $account_data['cancel_notify'] : $this->config('user.settings')->get('notify.status_canceled'),
|
||||
);
|
||||
|
|
|
@ -79,7 +79,7 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac
|
|||
$route_name = $this->routeMatch->getRouteName();
|
||||
if ($account->isAnonymous() && !in_array($route_name, array('user.register', 'user.login', 'user.logout'))) {
|
||||
return AccessResult::allowed()
|
||||
->addCacheContexts(['route', 'user.roles:anonymous']);
|
||||
->addCacheContexts(['route.name', 'user.roles:anonymous']);
|
||||
}
|
||||
return AccessResult::forbidden();
|
||||
}
|
||||
|
|
|
@ -87,4 +87,17 @@ class UserRole extends ConditionPluginBase {
|
|||
return (bool) array_intersect($this->configuration['roles'], $user->getRoles());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
// Optimize cache context, if a user cache context is provided, only use
|
||||
// user.roles, since that's the only part this condition cares about.
|
||||
$contexts = [];
|
||||
foreach (parent::getCacheContexts() as $context) {
|
||||
$contexts[] = $context == 'user' ? 'user.roles' : $context;
|
||||
}
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ class RolesRid extends ManyToOne {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return parent::create($container, $configuration, $plugin_id, $plugin_definition, $container->get('entity.manager'));
|
||||
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity.manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
|
@ -122,10 +121,7 @@ class PrivateTempStore {
|
|||
if (!$this->lockBackend->acquire($key)) {
|
||||
$this->lockBackend->wait($key);
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to update item %key in %collection temporary storage.", array(
|
||||
'%key' => $key,
|
||||
'%collection' => $this->storage->getCollectionName(),
|
||||
)));
|
||||
throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,10 +176,7 @@ class PrivateTempStore {
|
|||
if (!$this->lockBackend->acquire($key)) {
|
||||
$this->lockBackend->wait($key);
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to delete item %key from %collection temporary storage.", array(
|
||||
'%key' => $key,
|
||||
'%collection' => $this->storage->getCollectionName(),
|
||||
)));
|
||||
throw new TempStoreException("Couldn't acquire lock to delete item '$key' from '{$this->storage->getCollectionName()}' temporary storage.");
|
||||
}
|
||||
}
|
||||
$this->storage->delete($key);
|
||||
|
|
|
@ -78,9 +78,9 @@ interface RoleInterface extends ConfigEntityInterface {
|
|||
* Sets the role to be an admin role.
|
||||
*
|
||||
* @param bool $is_admin
|
||||
* TRUE, if the role should be an admin role.
|
||||
* TRUE if the role should be an admin role.
|
||||
*
|
||||
* return $this
|
||||
* @return $this
|
||||
*/
|
||||
public function setIsAdmin($is_admin);
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
@ -196,10 +195,7 @@ class SharedTempStore {
|
|||
if (!$this->lockBackend->acquire($key)) {
|
||||
$this->lockBackend->wait($key);
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to update item %key in %collection temporary storage.", array(
|
||||
'%key' => $key,
|
||||
'%collection' => $this->storage->getCollectionName(),
|
||||
)));
|
||||
throw new TempStoreException("Couldn't acquire lock to update item '$key' in '{$this->storage->getCollectionName()}' temporary storage.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,10 +238,7 @@ class SharedTempStore {
|
|||
if (!$this->lockBackend->acquire($key)) {
|
||||
$this->lockBackend->wait($key);
|
||||
if (!$this->lockBackend->acquire($key)) {
|
||||
throw new TempStoreException(SafeMarkup::format("Couldn't acquire lock to delete item %key from %collection temporary storage.", array(
|
||||
'%key' => $key,
|
||||
'%collection' => $this->storage->getCollectionName(),
|
||||
)));
|
||||
throw new TempStoreException("Couldn't acquire lock to delete item '$key' from {$this->storage->getCollectionName()} temporary storage.");
|
||||
}
|
||||
}
|
||||
$this->storage->delete($key);
|
||||
|
|
|
@ -59,7 +59,7 @@ class UserCancelTest extends WebTestBase {
|
|||
|
||||
// Attempt bogus account cancellation request confirmation.
|
||||
$timestamp = $account->getLastLoginTime();
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$this->assertResponse(403, 'Bogus cancelling request rejected.');
|
||||
$user_storage->resetCache(array($account->id()));
|
||||
$account = $user_storage->load($account->id());
|
||||
|
@ -165,7 +165,7 @@ class UserCancelTest extends WebTestBase {
|
|||
|
||||
// Attempt bogus account cancellation request confirmation.
|
||||
$bogus_timestamp = $timestamp + 60;
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->getPassword(), $bogus_timestamp, $account->getLastLoginTime(), $account->id()));
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account, $bogus_timestamp));
|
||||
$this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Bogus cancelling request rejected.');
|
||||
$user_storage->resetCache(array($account->id()));
|
||||
$account = $user_storage->load($account->id());
|
||||
|
@ -173,7 +173,7 @@ class UserCancelTest extends WebTestBase {
|
|||
|
||||
// Attempt expired account cancellation request confirmation.
|
||||
$bogus_timestamp = $timestamp - 86400 - 60;
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->getPassword(), $bogus_timestamp, $account->getLastLoginTime(), $account->id()));
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account, $bogus_timestamp));
|
||||
$this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Expired cancel account request rejected.');
|
||||
$user_storage->resetCache(array($account->id()));
|
||||
$account = $user_storage->load($account->id());
|
||||
|
@ -214,7 +214,7 @@ class UserCancelTest extends WebTestBase {
|
|||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache(array($account->id()));
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
|
||||
|
@ -272,7 +272,7 @@ class UserCancelTest extends WebTestBase {
|
|||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache(array($account->id()));
|
||||
$account = $user_storage->load($account->id());
|
||||
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
|
||||
|
@ -348,7 +348,7 @@ class UserCancelTest extends WebTestBase {
|
|||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache(array($account->id()));
|
||||
$this->assertFalse($user_storage->load($account->id()), 'User is not found in the database.');
|
||||
|
||||
|
@ -427,7 +427,7 @@ class UserCancelTest extends WebTestBase {
|
|||
$this->assertText(t('A confirmation request to cancel your account has been sent to your email address.'), 'Account cancellation request mailed message displayed.');
|
||||
|
||||
// Confirm account cancellation request.
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()));
|
||||
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
|
||||
$user_storage->resetCache(array($account->id()));
|
||||
$this->assertFalse($user_storage->load($account->id()), 'User is not found in the database.');
|
||||
|
||||
|
|
|
@ -144,14 +144,14 @@ class UserPasswordResetTest extends PageCacheTagsTestBase {
|
|||
$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->getPassword(), $bogus_timestamp, $this->account->getLastLoginTime(), $this->account->id()));
|
||||
$this->drupalGet("user/reset/$_uid/$bogus_timestamp/" . user_pass_rehash($this->account, $bogus_timestamp));
|
||||
$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->getPassword(), $timestamp, $blocked_account->getLastLoginTime(), $this->account->id()));
|
||||
$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.
|
||||
|
@ -162,6 +162,16 @@ class UserPasswordResetTest extends PageCacheTagsTestBase {
|
|||
$this->drupalPostForm(NULL, $edit, t('Submit'));
|
||||
$this->assertRaw(t('%name is blocked or has not been activated yet.', array('%name' => $blocked_account->getUsername())), 'Notified user blocked accounts can not request a new password');
|
||||
$this->assertTrue(count($this->drupalGetMails(array('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 = array('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->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.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\file\Entity\File;
|
||||
|
||||
/**
|
||||
* Tests user picture functionality.
|
||||
|
@ -75,7 +76,7 @@ class UserPictureTest extends WebTestBase {
|
|||
\Drupal::service('cron')->run();
|
||||
|
||||
// Verify that the image has been deleted.
|
||||
$this->assertFalse(file_load($file->id(), TRUE), 'File was removed from the database.');
|
||||
$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.');
|
||||
|
@ -133,6 +134,6 @@ class UserPictureTest extends WebTestBase {
|
|||
$user_storage = $this->container->get('entity.manager')->getStorage('user');
|
||||
$user_storage->resetCache(array($this->webUser->id()));
|
||||
$account = $user_storage->load($this->webUser->id());
|
||||
return file_load($account->user_picture->target_id, TRUE);
|
||||
return File::load($account->user_picture->target_id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
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;
|
||||
|
@ -264,6 +265,24 @@ class UserRegistrationTest extends WebTestBase {
|
|||
$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.
|
||||
*/
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
@ -66,15 +67,58 @@ class UserTokenReplaceTest extends WebTestBase {
|
|||
$tests['[user:created:short]'] = format_date($account->getCreatedTime(), 'short', '', NULL, $language_interface->getId());
|
||||
$tests['[current-user:name]'] = SafeMarkup::checkPlain(user_format_name($global_account));
|
||||
|
||||
$base_bubbleable_metadata = BubbleableMetadata::createFromObject($account);
|
||||
$metadata_tests = [];
|
||||
$metadata_tests['[user:uid]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:name]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:mail]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:url]'] = $base_bubbleable_metadata;
|
||||
$metadata_tests['[user:edit-url]'] = $base_bubbleable_metadata;
|
||||
$bubbleable_metadata = clone $base_bubbleable_metadata;
|
||||
// This test runs with the Language module enabled, which means config is
|
||||
// overridden by LanguageConfigFactoryOverride (to provide translations of
|
||||
// config). This causes the interface language cache context to be added for
|
||||
// config entities. The four next tokens use DateFormat Config entities, and
|
||||
// therefore have the interface language cache context.
|
||||
$bubbleable_metadata->addCacheContexts(['languages:language_interface']);
|
||||
$metadata_tests['[user:last-login]'] = $bubbleable_metadata->addCacheTags(['rendered']);
|
||||
$metadata_tests['[user:last-login:short]'] = $bubbleable_metadata;
|
||||
$metadata_tests['[user:created]'] = $bubbleable_metadata;
|
||||
$metadata_tests['[user:created:short]'] = $bubbleable_metadata;
|
||||
$metadata_tests['[current-user:name]'] = $base_bubbleable_metadata->merge(BubbleableMetadata::createFromObject($global_account)->addCacheContexts(['user']));
|
||||
|
||||
// Test to make sure that we generated something for each token.
|
||||
$this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$output = $token_service->replace($input, array('user' => $account), array('langcode' => $language_interface->getId()));
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$output = $token_service->replace($input, array('user' => $account), array('langcode' => $language_interface->getId()), $bubbleable_metadata);
|
||||
$this->assertEqual($output, $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input)));
|
||||
$this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
|
||||
}
|
||||
|
||||
// Generate tokens for the anonymous user.
|
||||
$anonymous_user = User::load(0);
|
||||
$tests = [];
|
||||
$tests['[user:uid]'] = t('not yet assigned');
|
||||
$tests['[user:name]'] = SafeMarkup::checkPlain(user_format_name($anonymous_user));
|
||||
|
||||
$base_bubbleable_metadata = BubbleableMetadata::createFromObject($anonymous_user);
|
||||
$metadata_tests = [];
|
||||
$metadata_tests['[user:uid]'] = $base_bubbleable_metadata;
|
||||
$bubbleable_metadata = clone $base_bubbleable_metadata;
|
||||
$bubbleable_metadata->addCacheableDependency(\Drupal::config('user.settings'));
|
||||
$metadata_tests['[user:name]'] = $bubbleable_metadata;
|
||||
|
||||
foreach ($tests as $input => $expected) {
|
||||
$bubbleable_metadata = new BubbleableMetadata();
|
||||
$output = $token_service->replace($input, array('user' => $anonymous_user), array('langcode' => $language_interface->getId()), $bubbleable_metadata);
|
||||
$this->assertEqual($output, $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input)));
|
||||
$this->assertEqual($bubbleable_metadata, $metadata_tests[$input]);
|
||||
}
|
||||
|
||||
// Generate and test unsanitized tokens.
|
||||
$tests = [];
|
||||
$tests['[user:name]'] = user_format_name($account);
|
||||
$tests['[user:mail]'] = $account->getEmail();
|
||||
$tests['[current-user:name]'] = user_format_name($global_account);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\user\Tests\Views;
|
||||
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
|
@ -25,6 +26,9 @@ class HandlerFieldUserNameTest extends UserTestBase {
|
|||
public static $testViews = array('test_views_handler_field_user_name');
|
||||
|
||||
public function testUserName() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array('access user profiles')));
|
||||
|
||||
// Set defaults.
|
||||
|
@ -37,13 +41,17 @@ class HandlerFieldUserNameTest extends UserTestBase {
|
|||
$this->executeView($view);
|
||||
|
||||
$anon_name = $this->config('user.settings')->get('anonymous');
|
||||
$render = $view->field['name']->advancedRender($view->result[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.');
|
||||
|
||||
$username = $this->randomMachineName();
|
||||
$view->result[0]->_entity->setUsername($username);
|
||||
$view->result[0]->_entity->uid->value = 1;
|
||||
$render = $view->field['name']->advancedRender($view->result[0]);
|
||||
$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.');
|
||||
$this->assertTrue(strpos($render, 'user/1') !== FALSE, 'If link to user is checked the link to the user should appear as well.');
|
||||
|
||||
|
@ -52,7 +60,9 @@ class HandlerFieldUserNameTest extends UserTestBase {
|
|||
$username = $this->randomMachineName();
|
||||
$view->result[0]->_entity->setUsername($username);
|
||||
$view->result[0]->_entity->uid->value = 1;
|
||||
$render = $view->field['name']->advancedRender($view->result[0]);
|
||||
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
|
||||
return $view->field['name']->advancedRender($view->result[0]);
|
||||
});
|
||||
$this->assertIdentical($render, $username, 'If the user is not linked the username should be printed out for a normal user.');
|
||||
|
||||
}
|
||||
|
@ -61,13 +71,18 @@ class HandlerFieldUserNameTest extends UserTestBase {
|
|||
* 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 = $view->field['name']->advancedRender($view->result[0]);
|
||||
$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.');
|
||||
}
|
||||
|
||||
|
|
|
@ -80,8 +80,11 @@ class UserAccessControlHandler extends EntityAccessControlHandler {
|
|||
$is_own_account = $items ? $items->getEntity()->id() == $account->id() : FALSE;
|
||||
switch ($field_definition->getName()) {
|
||||
case 'name':
|
||||
// Allow view access to anyone with access to the entity.
|
||||
if ($operation == 'view') {
|
||||
// Allow view access to anyone with access to the entity. Anonymous
|
||||
// users should be able to access the username field during the
|
||||
// registration process, otherwise the username and email constraints
|
||||
// are not checked.
|
||||
if ($operation == 'view' || ($items && $account->isAnonymous() && $items->getEntity()->isAnonymous())) {
|
||||
return AccessResult::allowed()->cachePerPermissions();
|
||||
}
|
||||
// Allow edit access for the own user name if the permission is
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace Drupal\user;
|
|||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
|
||||
|
@ -72,14 +72,14 @@ class UserStorage extends SqlContentEntityStorage implements UserStorageInterfac
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(EntityInterface $entity) {
|
||||
protected function doSaveFieldItems(ContentEntityInterface $entity, array $names = []) {
|
||||
// The anonymous user account is saved with the fixed user ID of 0.
|
||||
// Therefore we need to check for NULL explicitly.
|
||||
if ($entity->id() === NULL) {
|
||||
$entity->uid->value = $this->database->nextId($this->database->query('SELECT MAX(uid) FROM {users}')->fetchField());
|
||||
$entity->enforceIsNew();
|
||||
}
|
||||
return parent::save($entity);
|
||||
return parent::doSaveFieldItems($entity, $names);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,14 +8,8 @@
|
|||
*
|
||||
* Available variables:
|
||||
* - content: A list of content items. Use 'content' to print all content, or
|
||||
* print a subset such as 'content.field_example'.
|
||||
* - Field variables: For each field attached to the user a corresponding
|
||||
* variable is defined; e.g., account.field_example has a variable
|
||||
* 'field_example' defined. When needing to access a field's raw values,
|
||||
* developers/themers are strongly encouraged to use these variables.
|
||||
* Otherwise they will have to explicitly specify the desired field language,
|
||||
* e.g. account.field_example.en, thus overriding any language negotiation
|
||||
* rule that was previously applied.
|
||||
* print a subset such as 'content.field_example'. Fields attached to a user
|
||||
* such as 'user_picture' are available as 'content.user_picture'.
|
||||
* - attributes: HTML attributes for the container element.
|
||||
* - user: A Drupal User entity.
|
||||
*
|
||||
|
|
|
@ -168,35 +168,41 @@ class PermissionHandlerTest extends UnitTestCase {
|
|||
->method('getModuleDirectories')
|
||||
->willReturn([
|
||||
'module_a' => vfsStream::url('modules/module_a'),
|
||||
'module_b' => vfsStream::url('modules/module_b'),
|
||||
'module_c' => vfsStream::url('modules/module_c'),
|
||||
]);
|
||||
$this->moduleHandler->expects($this->exactly(3))
|
||||
->method('getName')
|
||||
->will($this->returnValueMap([
|
||||
['module_a', 'Module a'],
|
||||
['module_b', 'Module b'],
|
||||
['module_c', 'A Module'],
|
||||
]));
|
||||
|
||||
$url = vfsStream::url('modules');
|
||||
mkdir($url . '/module_a');
|
||||
file_put_contents($url . '/module_a/module_a.permissions.yml',
|
||||
"access_module_a2: single_description
|
||||
access_module_a1: single_description"
|
||||
"access_module_a2: single_description2
|
||||
access_module_a1: single_description1"
|
||||
);
|
||||
mkdir($url . '/module_b');
|
||||
file_put_contents($url . '/module_b/module_b.permissions.yml',
|
||||
"access_module_a3: single_description"
|
||||
);
|
||||
mkdir($url . '/module_c');
|
||||
file_put_contents($url . '/module_c/module_c.permissions.yml',
|
||||
"access_module_a4: single_description"
|
||||
);
|
||||
$modules = ['module_a'];
|
||||
$extensions = [
|
||||
'module_a' => $this->mockModuleExtension('module_a', 'Module a'),
|
||||
];
|
||||
$this->moduleHandler->expects($this->any())
|
||||
->method('getImplementations')
|
||||
->with('permission')
|
||||
->willReturn([]);
|
||||
|
||||
$this->moduleHandler->expects($this->any())
|
||||
$modules = ['module_a', 'module_b', 'module_c'];
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('getModuleList')
|
||||
->willReturn(array_flip($modules));
|
||||
|
||||
$this->permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
|
||||
|
||||
// Setup system_rebuild_module_data().
|
||||
$this->permissionHandler->setSystemRebuildModuleData($extensions);
|
||||
|
||||
$actual_permissions = $this->permissionHandler->getPermissions();
|
||||
|
||||
$this->assertEquals(['access_module_a1', 'access_module_a2'], array_keys($actual_permissions));
|
||||
$permissionHandler = new TestPermissionHandler($this->moduleHandler, $this->stringTranslation, $this->controllerResolver);
|
||||
$actual_permissions = $permissionHandler->getPermissions();
|
||||
$this->assertEquals(['access_module_a4', 'access_module_a1', 'access_module_a2', 'access_module_a3'],
|
||||
array_keys($actual_permissions));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,14 +8,8 @@
|
|||
*
|
||||
* Available variables:
|
||||
* - content: A list of content items. Use 'content' to print all content, or
|
||||
* print a subset such as 'content.field_example'.
|
||||
* - Field variables: For each field attached to the user a corresponding
|
||||
* variable is defined; e.g., account.field_example has a variable
|
||||
* 'field_example' defined. When needing to access a field's raw values,
|
||||
* developers/themers are strongly encouraged to use these variables.
|
||||
* Otherwise they will have to explicitly specify the desired field language,
|
||||
* e.g. account.field_example.en, thus overriding any language negotiation
|
||||
* rule that was previously applied.
|
||||
* print a subset such as 'content.field_example'. Fields attached to a user
|
||||
* such as 'user_picture' are available as 'content.user_picture'.
|
||||
* - attributes: HTML attributes for the container element.
|
||||
* - user: A Drupal User entity.
|
||||
*
|
||||
|
|
|
@ -417,12 +417,6 @@ function user_format_name(AccountInterface $account) {
|
|||
function user_template_preprocess_default_variables_alter(&$variables) {
|
||||
$user = \Drupal::currentUser();
|
||||
|
||||
// If this function is called from the installer after Drupal has been
|
||||
// installed then $user will not be set.
|
||||
if (!is_object($user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$variables['user'] = clone $user;
|
||||
// Remove password and session IDs, $form_state, since themes should not need nor see them.
|
||||
unset($variables['user']->pass, $variables['user']->sid, $variables['user']->ssid);
|
||||
|
@ -574,7 +568,7 @@ function user_pass_reset_url($account, $options = array()) {
|
|||
array(
|
||||
'uid' => $account->id(),
|
||||
'timestamp' => $timestamp,
|
||||
'hash' => user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id()),
|
||||
'hash' => user_pass_rehash($account, $timestamp),
|
||||
),
|
||||
array(
|
||||
'absolute' => TRUE,
|
||||
|
@ -587,11 +581,7 @@ function user_pass_reset_url($account, $options = array()) {
|
|||
* Generates a URL to confirm an account cancellation request.
|
||||
*
|
||||
* @param \Drupal\user\UserInterface $account
|
||||
* The user account object, which must contain at least the following
|
||||
* properties:
|
||||
* - uid: The user ID number.
|
||||
* - pass: The hashed user password string.
|
||||
* - login: The UNIX timestamp of the user's last login.
|
||||
* The user account object.
|
||||
* @param array $options
|
||||
* (optional) A keyed array of settings. Supported options are:
|
||||
* - langcode: A language code to be used when generating locale-sensitive
|
||||
|
@ -604,14 +594,14 @@ function user_pass_reset_url($account, $options = array()) {
|
|||
* @see user_mail_tokens()
|
||||
* @see \Drupal\user\Controller\UserController::confirmCancel()
|
||||
*/
|
||||
function user_cancel_url($account, $options = array()) {
|
||||
function user_cancel_url(UserInterface $account, $options = array()) {
|
||||
$timestamp = REQUEST_TIME;
|
||||
$langcode = isset($options['langcode']) ? $options['langcode'] : $account->getPreferredLangcode();
|
||||
$url_options = array('absolute' => TRUE, 'language' => \Drupal::languageManager()->getLanguage($langcode));
|
||||
return \Drupal::url('user.cancel_confirm', [
|
||||
'user' => $account->id(),
|
||||
'timestamp' => $timestamp,
|
||||
'hashed_pass' => user_pass_rehash($account->getPassword(), $timestamp, $account->getLastLoginTime(), $account->id())
|
||||
'hashed_pass' => user_pass_rehash($account, $timestamp)
|
||||
], $url_options);
|
||||
}
|
||||
|
||||
|
@ -621,26 +611,26 @@ function user_cancel_url($account, $options = array()) {
|
|||
* This hash is normally used to build a unique and secure URL that is sent to
|
||||
* the user by email for purposes such as resetting the user's password. In
|
||||
* order to validate the URL, the same hash can be generated again, from the
|
||||
* same information, and compared to the hash value from the URL. The URL
|
||||
* normally contains both the time stamp and the numeric user ID. The login
|
||||
* timestamp and hashed password are retrieved from the database as necessary.
|
||||
* same information, and compared to the hash value from the URL. The hash
|
||||
* contains the time stamp, the user's last login time, the numeric user ID,
|
||||
* and the user's email address.
|
||||
* For a usage example, see user_cancel_url() and
|
||||
* \Drupal\user\Controller\UserController::confirmCancel().
|
||||
*
|
||||
* @param string $password
|
||||
* The hashed user account password value.
|
||||
* @param \Drupal\user\UserInterface $account
|
||||
* An object containing the user account.
|
||||
* @param int $timestamp
|
||||
* A UNIX timestamp, typically REQUEST_TIME.
|
||||
* @param int $login
|
||||
* The UNIX timestamp of the user's last login.
|
||||
* @param int $uid
|
||||
* The user ID.
|
||||
*
|
||||
* @return string
|
||||
* A string that is safe for use in URLs and SQL statements.
|
||||
*/
|
||||
function user_pass_rehash($password, $timestamp, $login, $uid) {
|
||||
return Crypt::hmacBase64($timestamp . $login . $uid, Settings::getHashSalt() . $password);
|
||||
function user_pass_rehash(UserInterface $account, $timestamp) {
|
||||
$data = $timestamp;
|
||||
$data .= $account->getLastLoginTime();
|
||||
$data .= $account->id();
|
||||
$data .= $account->getEmail();
|
||||
return Crypt::hmacBase64($data, Settings::getHashSalt() . $account->getPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,6 +61,11 @@ services:
|
|||
user.permissions:
|
||||
class: Drupal\user\PermissionHandler
|
||||
arguments: ['@module_handler', '@string_translation', '@controller_resolver']
|
||||
user.current_user_context:
|
||||
class: Drupal\user\ContextProvider\CurrentUserContext
|
||||
arguments: ['@current_user', '@entity.manager']
|
||||
tags:
|
||||
- { name: 'context_provider' }
|
||||
|
||||
parameters:
|
||||
user.tempstore.expire: 604800
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
|
@ -64,7 +66,7 @@ function user_token_info() {
|
|||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function user_tokens($type, $tokens, array $data = array(), array $options = array()) {
|
||||
function user_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
|
||||
|
||||
$token_service = \Drupal::token();
|
||||
$url_options = array('absolute' => TRUE);
|
||||
|
@ -80,6 +82,7 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
|
|||
$replacements = array();
|
||||
|
||||
if ($type == 'user' && !empty($data['user'])) {
|
||||
/** @var \Drupal\user\UserInterface $account */
|
||||
$account = $data['user'];
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
|
@ -91,6 +94,9 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
|
|||
|
||||
case 'name':
|
||||
$name = user_format_name($account);
|
||||
if ($account->isAnonymous()) {
|
||||
$bubbleable_metadata->addCacheableDependency(\Drupal::config('user.settings'));
|
||||
}
|
||||
$replacements[$original] = $sanitize ? SafeMarkup::checkPlain($name) : $name;
|
||||
break;
|
||||
|
||||
|
@ -108,10 +114,14 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
|
|||
|
||||
// These tokens are default variations on the chained tokens handled below.
|
||||
case 'last-login':
|
||||
$date_format = DateFormat::load('medium');
|
||||
$bubbleable_metadata->addCacheableDependency($date_format);
|
||||
$replacements[$original] = $account->getLastLoginTime() ? format_date($account->getLastLoginTime(), 'medium', '', NULL, $langcode) : t('never');
|
||||
break;
|
||||
|
||||
case 'created':
|
||||
$date_format = DateFormat::load('medium');
|
||||
$bubbleable_metadata->addCacheableDependency($date_format);
|
||||
// In the case of user_presave the created date may not yet be set.
|
||||
$replacements[$original] = $account->getCreatedTime() ? format_date($account->getCreatedTime(), 'medium', '', NULL, $langcode) : t('not yet created');
|
||||
break;
|
||||
|
@ -119,17 +129,18 @@ function user_tokens($type, $tokens, array $data = array(), array $options = arr
|
|||
}
|
||||
|
||||
if ($login_tokens = $token_service->findWithPrefix($tokens, 'last-login')) {
|
||||
$replacements += $token_service->generate('date', $login_tokens, array('date' => $account->getLastLoginTime()), $options);
|
||||
$replacements += $token_service->generate('date', $login_tokens, array('date' => $account->getLastLoginTime()), $options, $bubbleable_metadata);
|
||||
}
|
||||
|
||||
if ($registered_tokens = $token_service->findWithPrefix($tokens, 'created')) {
|
||||
$replacements += $token_service->generate('date', $registered_tokens, array('date' => $account->getCreatedTime()), $options);
|
||||
$replacements += $token_service->generate('date', $registered_tokens, array('date' => $account->getCreatedTime()), $options, $bubbleable_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
if ($type == 'current-user') {
|
||||
$account = User::load(\Drupal::currentUser()->id());
|
||||
$replacements += $token_service->generate('user', $tokens, array('user' => $account), $options);
|
||||
$bubbleable_metadata->addCacheContexts(['user']);
|
||||
$replacements += $token_service->generate('user', $tokens, array('user' => $account), $options, $bubbleable_metadata);
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
|
|
Reference in a new issue