diff -u b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequiredValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequiredValidator.php --- b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequiredValidator.php +++ b/core/modules/user/src/Plugin/Validation/Constraint/UserMailRequiredValidator.php @@ -2,8 +2,12 @@ namespace Drupal\user\Plugin\Validation\Constraint; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Session\AccountProxyInterface; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Constraint; +use Drupal\user\UserStorageInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Checks if the user's email address is provided if required. @@ -12,7 +16,44 @@ * and the user performing the edit has 'administer users' permission. * This allows users without email address to be edited and deleted. */ -class UserMailRequiredValidator extends ConstraintValidator { +class UserMailRequiredValidator extends ConstraintValidator implements ContainerInjectionInterface { + + /** + * User storage handler. + * + * @var \Drupal\user\UserStorageInterface + */ + protected $userStorage; + + /** + * The current user. + * + * @var \Drupal\Core\Session\AccountProxyInterface + */ + protected $currentUser; + + /** + * Constructs the object. + * + * @param \Drupal\user\UserStorageInterface $user_storage + * The user storage handler. + * @param \Drupal\Core\Session\AccountProxyInterface $current_user + * The current user. + */ + public function __construct(UserStorageInterface $user_storage, AccountProxyInterface $current_user) { + $this->userStorage = $user_storage; + $this->currentUser = $current_user; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.manager')->getStorage('user'), + $container->get('current_user') + ); + } /** * {@inheritdoc} @@ -25,18 +66,16 @@ return; } - $account = $items->getEntity(); $existing_value = NULL; // Only validate for existing user. if (!$account->isNew() && $account->id()) { - $account_unchanged = \Drupal::entityTypeManager() - ->getStorage('user') + $account_unchanged = $this->userStorage ->loadUnchanged($account->id()); $existing_value = $account_unchanged->getEmail(); } - $required = !(!$existing_value && \Drupal::currentUser()->hasPermission('administer users')); + $required = !(!$existing_value && $this->currentUser->hasPermission('administer users')); if ($required && (!isset($items) || $items->isEmpty())) { $this->context->addViolation($constraint->message, ['@name' => $account->getFieldDefinition('mail')->getLabel()]); only in patch2: unchanged: --- /dev/null +++ b/core/modules/user/tests/src/Unit/Plugin/Validation/Constraint/UserMailRequiredValidatorTest.php @@ -0,0 +1,208 @@ +getMock('Drupal\Core\Field\FieldItemListInterface'); + $unchanged_field->expects($this->any()) + ->method('getValue') + ->willReturn('unchanged-value'); + + $unchanged_account = $this->getMock('Drupal\user\UserInterface'); + $unchanged_account->expects($this->any()) + ->method('get') + ->willReturn($unchanged_field); + $user_storage = $this->getMock('Drupal\user\UserStorageInterface'); + $user_storage->expects($this->any()) + ->method('loadUnchanged') + ->willReturn($unchanged_account); + $current_user = $this->getMock('Drupal\Core\Session\AccountProxyInterface'); + $current_user->expects($this->any()) + ->method('id') + ->willReturn('current-user'); + $current_user->expects($this->any()) + ->method('hasPermission') + ->willReturn($is_admin); + return new UserMailRequiredValidator($user_storage, $current_user); + } + + /** + * @covers ::validate + * + * @dataProvider providerTestValidate + */ + public function testValidate($items, $expected_violation, $is_admin = FALSE) { + $constraint = new UserMailRequired(); + + // If a violation is expected, then the context's addViolation method will + // be called, otherwise it should not be called. + $context = $this->getMock(ExecutionContextInterface::class); + + if ($expected_violation) { + $context->expects($this->once()) + ->method('addViolation'); + } + else { + $context->expects($this->never()) + ->method('addViolation'); + } + + $validator = $this->createValidator($is_admin); + $validator->initialize($context); + $validator->validate($items, $constraint); + } + + /** + * Data provider for ::testValidate(). + */ + public function providerTestValidate() { + $cases = []; + + // Case 1: Empty user should be ignored. + $items = $this->getMock('Drupal\Core\Field\FieldItemListInterface'); + $items->expects($this->once()) + ->method('getEntity') + ->willReturn(NULL); + $cases[] = [$items, FALSE]; + + // Case 2: New users without an email should add a violation. + $items = $this->getMock('Drupal\Core\Field\FieldItemListInterface'); + $account = $this->getMock('Drupal\user\UserInterface'); + $account->expects($this->once()) + ->method('isNew') + ->willReturn(TRUE); + $account->expects($this->never()) + ->method('id'); + $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); + $field_definition->expects($this->once()) + ->method('getLabel') + ->willReturn('label'); + $account->expects($this->once()) + ->method('getFieldDefinition') + ->willReturn($field_definition); + $items->expects($this->once()) + ->method('getEntity') + ->willReturn($account); + $items->expects($this->once()) + ->method('isEmpty') + ->willReturn(TRUE); + $cases[] = [$items, TRUE]; + + // Case 3: Existing users without an email should add a violation. + $items = $this->getMock('Drupal\Core\Field\FieldItemListInterface'); + $account = $this->getMock('Drupal\user\UserInterface'); + $account->expects($this->once()) + ->method('isNew') + ->willReturn(FALSE); + $account->expects($this->exactly(2)) + ->method('id') + ->willReturn('current-user'); + $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); + $field_definition->expects($this->once()) + ->method('getLabel') + ->willReturn('label'); + $account->expects($this->once()) + ->method('getFieldDefinition') + ->willReturn($field_definition); + $items->expects($this->once()) + ->method('getEntity') + ->willReturn($account); + $items->expects($this->once()) + ->method('isEmpty') + ->willReturn(TRUE); + $cases[] = [$items, TRUE]; + + // Case 4: New users with an email should be ignored. + $items = $this->getMock('Drupal\Core\Field\FieldItemListInterface'); + $account = $this->getMock('Drupal\user\UserInterface'); + $account->expects($this->once()) + ->method('isNew') + ->willReturn(TRUE); + $account->expects($this->never()) + ->method('id'); + $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); + $field_definition->expects($this->once()) + ->method('getLabel') + ->willReturn('label'); + $account->expects($this->once()) + ->method('getFieldDefinition') + ->willReturn($field_definition); + $items->expects($this->once()) + ->method('getEntity') + ->willReturn($account); + $items->expects($this->once()) + ->method('isEmpty') + ->willReturn(FALSE); + $cases[] = [$items, FALSE]; + + // Case 5: Existing users with an email should be ignored. + $items = $this->getMock('Drupal\Core\Field\FieldItemListInterface'); + $account = $this->getMock('Drupal\user\UserInterface'); + $account->expects($this->once()) + ->method('isNew') + ->willReturn(FALSE); + $account->expects($this->exactly(2)) + ->method('id') + ->willReturn('current-user'); + $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); + $field_definition->expects($this->once()) + ->method('getLabel') + ->willReturn('label'); + $account->expects($this->once()) + ->method('getFieldDefinition') + ->willReturn($field_definition); + $items->expects($this->once()) + ->method('getEntity') + ->willReturn($account); + $items->expects($this->once()) + ->method('isEmpty') + ->willReturn(FALSE); + $cases[] = [$items, FALSE]; + + // Case 6: Existing users without an email should be ignored if the current + // user is an administrator. + $items = $this->getMock('Drupal\Core\Field\FieldItemListInterface'); + $account = $this->getMock('Drupal\user\UserInterface'); + $account->expects($this->once()) + ->method('isNew') + ->willReturn(FALSE); + $account->expects($this->exactly(2)) + ->method('id') + ->willReturn('current-user'); + $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); + $field_definition->expects($this->once()) + ->method('getLabel') + ->willReturn('label'); + $account->expects($this->once()) + ->method('getFieldDefinition') + ->willReturn($field_definition); + $items->expects($this->once()) + ->method('getEntity') + ->willReturn($account); + $cases[] = [$items, FALSE, TRUE]; + + return $cases; + } + +}