Issue #2002162 by klausi, eugene.ilyin: Convert form validation of users to entity validation.
parent
b00eeb7df8
commit
fe0fd73a2f
|
@ -190,7 +190,8 @@ abstract class EntityStorageControllerBase implements EntityStorageControllerInt
|
|||
// of making LegacyConfigFieldItem implement PrepareCacheInterface.
|
||||
// @todo Remove once all core field types have been converted (see
|
||||
// http://drupal.org/node/2014671).
|
||||
|| (is_subclass_of($type_definition['class'], '\Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem') && function_exists($type_definition['provider'] . '_field_load'))) {
|
||||
|| (is_subclass_of($type_definition['class'], '\Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem')
|
||||
&& isset($type_definition['provider']) && function_exists($type_definition['provider'] . '_field_load'))) {
|
||||
|
||||
// Call the prepareCache() method directly on each item
|
||||
// individually.
|
||||
|
|
|
@ -446,37 +446,59 @@ class User extends EntityNG implements UserInterface {
|
|||
'description' => t('The name of this user'),
|
||||
'type' => 'string_field',
|
||||
'settings' => array('default_value' => ''),
|
||||
'property_constraints' => array(
|
||||
// No Length contraint here because the UserName constraint also covers
|
||||
// that.
|
||||
'value' => array(
|
||||
'UserName' => array(),
|
||||
'UserNameUnique' => array(),
|
||||
),
|
||||
),
|
||||
);
|
||||
$properties['pass'] = array(
|
||||
'label' => t('Name'),
|
||||
'label' => t('Password'),
|
||||
'description' => t('The password of this user (hashed)'),
|
||||
'type' => 'string_field',
|
||||
);
|
||||
$properties['mail'] = array(
|
||||
'label' => t('Name'),
|
||||
'label' => t('E-mail'),
|
||||
'description' => t('The e-mail of this user'),
|
||||
'type' => 'string_field',
|
||||
'type' => 'email_field',
|
||||
'settings' => array('default_value' => ''),
|
||||
'property_constraints' => array(
|
||||
'value' => array('UserMailUnique' => array()),
|
||||
),
|
||||
);
|
||||
$properties['signature'] = array(
|
||||
'label' => t('Name'),
|
||||
'label' => t('Signature'),
|
||||
'description' => t('The signature of this user'),
|
||||
'type' => 'string_field',
|
||||
'property_constraints' => array(
|
||||
'value' => array('Length' => array('max' => 255)),
|
||||
),
|
||||
);
|
||||
$properties['signature_format'] = array(
|
||||
'label' => t('Name'),
|
||||
'label' => t('Signature format'),
|
||||
'description' => t('The signature format of this user'),
|
||||
// @todo Convert the type to filter_format once
|
||||
// https://drupal.org/node/1758622 is comitted
|
||||
'type' => 'string_field',
|
||||
);
|
||||
$properties['theme'] = array(
|
||||
'label' => t('Theme'),
|
||||
'description' => t('The default theme of this user'),
|
||||
'type' => 'string_field',
|
||||
'property_constraints' => array(
|
||||
'value' => array('Length' => array('max' => DRUPAL_EXTENSION_NAME_MAX_LENGTH)),
|
||||
),
|
||||
);
|
||||
$properties['timezone'] = array(
|
||||
'label' => t('Timezone'),
|
||||
'description' => t('The timezone of this user'),
|
||||
'type' => 'string_field',
|
||||
'property_constraints' => array(
|
||||
'value' => array('Length' => array('max' => 32)),
|
||||
),
|
||||
);
|
||||
$properties['status'] = array(
|
||||
'label' => t('User status'),
|
||||
|
@ -504,12 +526,14 @@ class User extends EntityNG implements UserInterface {
|
|||
$properties['init'] = array(
|
||||
'label' => t('Init'),
|
||||
'description' => t('The email address used for initial account creation.'),
|
||||
'type' => 'string_field',
|
||||
'type' => 'email_field',
|
||||
'settings' => array('default_value' => ''),
|
||||
);
|
||||
$properties['roles'] = array(
|
||||
'label' => t('Roles'),
|
||||
'description' => t('The roles the user has.'),
|
||||
// @todo Convert this to entity_reference_field, see
|
||||
// https://drupal.org/node/2044859
|
||||
'type' => 'string_field',
|
||||
);
|
||||
return $properties;
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Validation\Constraint\UserMailUnique.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a user's e-mail address is unique on the site.
|
||||
*
|
||||
* @Plugin(
|
||||
* id = "UserMailUnique",
|
||||
* label = @Translation("User e-mail unique", context = "Validation")
|
||||
* )
|
||||
*/
|
||||
class UserMailUnique extends Constraint {
|
||||
|
||||
public $message = 'The e-mail address %value is already taken.';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validatedBy() {
|
||||
return '\Drupal\user\Plugin\Validation\Constraint\UserUniqueValidator';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Validation\Constraint\UserNameConstraint.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a value is a valid user name.
|
||||
*
|
||||
* @Plugin(
|
||||
* id = "UserName",
|
||||
* label = @Translation("User name", context = "Validation")
|
||||
* )
|
||||
*/
|
||||
class UserNameConstraint extends Constraint {
|
||||
|
||||
public $emptyMessage = 'You must enter a username.';
|
||||
public $spaceBeginMessage = 'The username cannot begin with a space.';
|
||||
public $spaceEndMessage = 'The username cannot end with a space.';
|
||||
public $multipleSpacesMessage = 'The username cannot contain multiple spaces in a row.';
|
||||
public $illegalMessage = 'The username contains an illegal character.';
|
||||
public $tooLongMessage = 'The username %name is too long: it must be %max characters or less.';
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Validation\Constraint\UserNameConstraintValidator.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Validates the UserName constraint.
|
||||
*/
|
||||
class UserNameConstraintValidator extends ConstraintValidator {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($name, Constraint $constraint) {
|
||||
if (!$name) {
|
||||
$this->context->addViolation($constraint->emptyMessage);
|
||||
return;
|
||||
}
|
||||
if (substr($name, 0, 1) == ' ') {
|
||||
$this->context->addViolation($constraint->spaceBeginMessage);
|
||||
}
|
||||
if (substr($name, -1) == ' ') {
|
||||
$this->context->addViolation($constraint->spaceEndMessage);
|
||||
}
|
||||
if (strpos($name, ' ') !== FALSE) {
|
||||
$this->context->addViolation($constraint->multipleSpacesMessage);
|
||||
}
|
||||
if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)
|
||||
|| preg_match(
|
||||
'/[\x{80}-\x{A0}' . // Non-printable ISO-8859-1 + NBSP
|
||||
'\x{AD}' . // Soft-hyphen
|
||||
'\x{2000}-\x{200F}' . // Various space characters
|
||||
'\x{2028}-\x{202F}' . // Bidirectional text overrides
|
||||
'\x{205F}-\x{206F}' . // Various text hinting characters
|
||||
'\x{FEFF}' . // Byte order mark
|
||||
'\x{FF01}-\x{FF60}' . // Full-width latin
|
||||
'\x{FFF9}-\x{FFFD}' . // Replacement characters
|
||||
'\x{0}-\x{1F}]/u', // NULL byte and control characters
|
||||
$name)
|
||||
) {
|
||||
$this->context->addViolation($constraint->illegalMessage);
|
||||
}
|
||||
if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
|
||||
$this->context->addViolation($constraint->tooLongMessage, array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Validation\Constraint\UserNameUnique.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Drupal\Component\Annotation\Plugin;
|
||||
use Drupal\Core\Annotation\Translation;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a user name is unique on the site.
|
||||
*
|
||||
* @Plugin(
|
||||
* id = "UserNameUnique",
|
||||
* label = @Translation("User name unique", context = "Validation")
|
||||
* )
|
||||
*/
|
||||
class UserNameUnique extends Constraint {
|
||||
|
||||
public $message = 'The name %value is already taken.';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validatedBy() {
|
||||
return '\Drupal\user\Plugin\Validation\Constraint\UserUniqueValidator';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\user\Plugin\Validation\Constraint\UserUniqueValidator.
|
||||
*/
|
||||
|
||||
namespace Drupal\user\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Validates the unique user property constraint, such as name and e-mail.
|
||||
*/
|
||||
class UserUniqueValidator extends ConstraintValidator {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($value, Constraint $constraint) {
|
||||
$field = $this->context->getMetadata()->getTypedData()->getParent();
|
||||
$uid = $field->getParent()->id();
|
||||
|
||||
$value_taken = (bool) db_select('users')
|
||||
->fields('users', array('uid'))
|
||||
// The UID could be NULL, so we cast it to 0 in that case.
|
||||
->condition('uid', (int) $uid, '<>')
|
||||
->condition($field->getName(), db_like($value), 'LIKE')
|
||||
->range(0, 1)
|
||||
->execute()
|
||||
->fetchField();
|
||||
|
||||
if ($value_taken) {
|
||||
$this->context->addViolation($constraint->message, array("%value" => $value));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,18 +7,41 @@
|
|||
|
||||
namespace Drupal\user\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
|
||||
/**
|
||||
* Performs validation tests on user fields.
|
||||
*/
|
||||
class UserValidationTest extends DrupalUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field', 'user', 'system');
|
||||
|
||||
class UserValidationTest extends WebTestBase {
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Username/e-mail validation',
|
||||
'description' => 'Verify that username/email validity checks behave as designed.',
|
||||
'name' => 'User validation',
|
||||
'description' => 'Verify that user validity checks behave as designed.',
|
||||
'group' => 'User'
|
||||
);
|
||||
}
|
||||
|
||||
// Username validation.
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('user', array('users'));
|
||||
$this->installSchema('system', array('sequences'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user name validation.
|
||||
*/
|
||||
function testUsernames() {
|
||||
$test_cases = array( // '<username>' => array('<description>', 'assert<testName>'),
|
||||
'foo' => array('Valid username', 'assertNull'),
|
||||
|
@ -44,4 +67,96 @@ class UserValidationTest extends WebTestBase {
|
|||
$this->$test($result, $description . ' (' . $name . ')');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs entity validation checks.
|
||||
*/
|
||||
function testValidation() {
|
||||
$user = entity_create('user', array('name' => 'test'));
|
||||
$violations = $user->validate();
|
||||
$this->assertEqual(count($violations), 0, 'No violations when validating a default user.');
|
||||
|
||||
// Only test one example invalid name here, the rest is already covered in
|
||||
// the testUsernames() method in this class.
|
||||
$name = $this->randomName(61);
|
||||
$user->set('name', $name);
|
||||
$violations = $user->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when name is too long.');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'name.0.value');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => 60)));
|
||||
|
||||
// Create a second test user to provoke a name collision.
|
||||
$user2 = entity_create('user', array(
|
||||
'name' => 'existing',
|
||||
'mail' => 'existing@exmaple.com',
|
||||
));
|
||||
$user2->save();
|
||||
$user->set('name', 'existing');
|
||||
$violations = $user->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found on name collision.');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'name.0.value');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The name %name is already taken.', array('%name' => 'existing')));
|
||||
|
||||
// Make the name valid.
|
||||
$user->set('name', $this->randomName());
|
||||
|
||||
$user->set('mail', 'invalid');
|
||||
$violations = $user->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when email is invalid');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value is not a valid email address.'));
|
||||
|
||||
$mail = $this->randomName(EMAIL_MAX_LENGTH - 11) . '@example.com';
|
||||
$user->set('mail', $mail);
|
||||
$violations = $user->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when email is too long');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value is not a valid email address.'));
|
||||
|
||||
// Provoke a e-mail collision with an exsiting user.
|
||||
$user->set('mail', 'existing@exmaple.com');
|
||||
$violations = $user->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when e-mail already exists.');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The e-mail address %mail is already taken.', array('%mail' => 'existing@exmaple.com')));
|
||||
$user->set('mail', NULL);
|
||||
|
||||
$user->set('signature', $this->randomString(256));
|
||||
$this->assertLengthViolation($user, 'signature', 255);
|
||||
$user->set('signature', NULL);
|
||||
|
||||
$user->set('theme', $this->randomString(DRUPAL_EXTENSION_NAME_MAX_LENGTH + 1));
|
||||
$this->assertLengthViolation($user, 'theme', DRUPAL_EXTENSION_NAME_MAX_LENGTH);
|
||||
$user->set('theme', NULL);
|
||||
|
||||
$user->set('timezone', $this->randomString(33));
|
||||
$this->assertLengthViolation($user, 'timezone', 32);
|
||||
$user->set('timezone', NULL);
|
||||
|
||||
$user->set('init', 'invalid');
|
||||
$violations = $user->validate();
|
||||
$this->assertEqual(count($violations), 1, 'Violation found when init email is invalid');
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), 'init.0.value');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value is not a valid email address.'));
|
||||
|
||||
// @todo Test user role validation once https://drupal.org/node/2015701 got
|
||||
// committed.
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a length violation exists for the given field.
|
||||
*
|
||||
* @param \Drupal\core\Entity\EntityInterface $entity
|
||||
* The entity object to validate.
|
||||
* @param string $field_name
|
||||
* The field that violates the maximum length.
|
||||
* @param int $length
|
||||
* Number of characters that was exceeded.
|
||||
*/
|
||||
protected function assertLengthViolation(EntityInterface $entity, $field_name, $length) {
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual(count($violations), 1, "Violation found when $field_name is too long.");
|
||||
$this->assertEqual($violations[0]->getPropertyPath(), "$field_name.0.value");
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => $length)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ const USERNAME_MAX_LENGTH = 60;
|
|||
/**
|
||||
* Maximum length of user e-mail text field.
|
||||
*/
|
||||
const EMAIL_MAX_LENGTH = 254;
|
||||
const EMAIL_MAX_LENGTH = 255;
|
||||
|
||||
/**
|
||||
* Only administrators can create user accounts.
|
||||
|
@ -321,37 +321,24 @@ function user_load_by_name($name) {
|
|||
|
||||
/**
|
||||
* Verify the syntax of the given name.
|
||||
*
|
||||
* @param string $name
|
||||
* The user name to validate.
|
||||
*
|
||||
* @return string|null
|
||||
* A translated violation message if the name is invalid or NULL if the name
|
||||
* is valid.
|
||||
*
|
||||
*/
|
||||
function user_validate_name($name) {
|
||||
if (!$name) {
|
||||
return t('You must enter a username.');
|
||||
}
|
||||
if (substr($name, 0, 1) == ' ') {
|
||||
return t('The username cannot begin with a space.');
|
||||
}
|
||||
if (substr($name, -1) == ' ') {
|
||||
return t('The username cannot end with a space.');
|
||||
}
|
||||
if (strpos($name, ' ') !== FALSE) {
|
||||
return t('The username cannot contain multiple spaces in a row.');
|
||||
}
|
||||
if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) {
|
||||
return t('The username contains an illegal character.');
|
||||
}
|
||||
if (preg_match('/[\x{80}-\x{A0}' . // Non-printable ISO-8859-1 + NBSP
|
||||
'\x{AD}' . // Soft-hyphen
|
||||
'\x{2000}-\x{200F}' . // Various space characters
|
||||
'\x{2028}-\x{202F}' . // Bidirectional text overrides
|
||||
'\x{205F}-\x{206F}' . // Various text hinting characters
|
||||
'\x{FEFF}' . // Byte order mark
|
||||
'\x{FF01}-\x{FF60}' . // Full-width latin
|
||||
'\x{FFF9}-\x{FFFD}' . // Replacement characters
|
||||
'\x{0}-\x{1F}]/u', // NULL byte and control characters
|
||||
$name)) {
|
||||
return t('The username contains an illegal character.');
|
||||
}
|
||||
if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
|
||||
return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
|
||||
$data = \Drupal::typedData()->create(array(
|
||||
'type' => 'string',
|
||||
'constraints' => array('UserName' => array()),
|
||||
));
|
||||
$data->setValue($name);
|
||||
$violations = $data->validate();
|
||||
if (count($violations) > 0) {
|
||||
return $violations[0]->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue