diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php index ce78a40..b9ace2f 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php @@ -62,4 +62,18 @@ public static function schema(FieldDefinitionInterface $field_definition) { ); } + /** + * {@inheritdoc} + */ + public function getConstraints() { + $constraints = parent::getConstraints(); + + $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager(); + $constraints[] = $constraint_manager->create('ComplexData', array( + 'value' => array('Length' => array('max' => $this->getFieldSetting('max_length'))) + )); + + return $constraints; + } + } diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php index 4cad163..d8c0b32 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php @@ -8,20 +8,26 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\Core\Field\FieldItemBase; use Drupal\Core\TypedData\DataDefinition; /** * Defines the 'uri' entity field type. * + * URIs are not length limited by RFC 2616, but we need to provide a sensible + * default. There is a de-facto limit of 2000 characters in browsers and other + * implementors, so we go with 2048. + * * @FieldType( * id = "uri", * label = @Translation("URI"), * description = @Translation("An entity field containing a URI."), + * settings = { + * "max_length" = "2048" + * }, * configurable = FALSE * ) */ -class UriItem extends FieldItemBase { +class UriItem extends StringItem { /** * Field definitions of the contained properties. diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php index 147c3fd..27ca9e9 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UuidItem.php @@ -16,12 +16,10 @@ * id = "uuid", * label = @Translation("UUID"), * description = @Translation("An entity field containing a UUID."), - * configurable = FALSE, - * constraints = { - * "ComplexData" = { - * "value" = {"Length" = {"max" = 128}} - * } - * } + * settings = { + * "max_length" = "128" + * }, + * configurable = FALSE * ) */ class UuidItem extends StringItem { diff --git a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php index 6bcfd95..203c1fd 100644 --- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php @@ -380,7 +380,8 @@ public static function baseFieldDefinitions($entity_type) { $fields['subject'] = FieldDefinition::create('string') ->setLabel(t('Subject')) - ->setDescription(t('The comment title or subject.')); + ->setDescription(t('The comment title or subject.')) + ->setSetting('max_length', 64); $fields['uid'] = FieldDefinition::create('entity_reference') ->setLabel(t('User ID')) @@ -393,19 +394,27 @@ public static function baseFieldDefinitions($entity_type) { $fields['name'] = FieldDefinition::create('string') ->setLabel(t('Name')) ->setDescription(t("The comment author's name.")) - ->setSetting('default_value', ''); + ->setSettings(array( + 'default_value' => '', + 'max_length' => 60, + )) + ->setConstraints(array('CommentName' => array())); $fields['mail'] = FieldDefinition::create('email') ->setLabel(t('Email')) ->setDescription(t("The comment author's e-mail address.")); - $fields['homepage'] = FieldDefinition::create('string') + $fields['homepage'] = FieldDefinition::create('uri') ->setLabel(t('Homepage')) - ->setDescription(t("The comment author's home page address.")); + ->setDescription(t("The comment author's home page address.")) + // URIs are not length limited by RFC 2616, but we can only store 255 + // characters in our comment DB schema. + ->setSetting('max_length', 255); $fields['hostname'] = FieldDefinition::create('string') ->setLabel(t('Hostname')) - ->setDescription(t("The comment author's hostname.")); + ->setDescription(t("The comment author's hostname.")) + ->setSetting('max_length', 128); // @todo Convert to a "created" field in https://drupal.org/node/2145103. $fields['created'] = FieldDefinition::create('integer') @@ -424,7 +433,8 @@ public static function baseFieldDefinitions($entity_type) { $fields['thread'] = FieldDefinition::create('string') ->setLabel(t('Thread place')) - ->setDescription(t("The alphadecimal representation of the comment's place in a thread, consisting of a base 36 string prefixed by an integer indicating its length.")); + ->setDescription(t("The alphadecimal representation of the comment's place in a thread, consisting of a base 36 string prefixed by an integer indicating its length.")) + ->setSetting('max_length', 255); $fields['entity_type'] = FieldDefinition::create('string') ->setLabel(t('Entity type')) diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Validation/Constraint/CommentNameConstraint.php b/core/modules/comment/lib/Drupal/comment/Plugin/Validation/Constraint/CommentNameConstraint.php new file mode 100644 index 0000000..51a3a64 --- /dev/null +++ b/core/modules/comment/lib/Drupal/comment/Plugin/Validation/Constraint/CommentNameConstraint.php @@ -0,0 +1,23 @@ +value; + if (isset($author_name) && ($author_name !== '')) { + // @todo Improve DX of this after https://drupal.org/node/2078387. + $author_is_unauthenticated = ($field_item->getEntity()->uid->value === 0); + + // Do not allow unauthenticated comment authors to use a name that is + // taken by a registered user. + if ($author_is_unauthenticated) { + $users = \Drupal::entityManager()->getStorageController('user')->loadByProperties(array('name' => $author_name)); + if (!empty($users)) { + $this->context->addViolation($constraint->message, array('%name' => $author_name)); + } + } + } + } +} diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php new file mode 100644 index 0000000..d62525d --- /dev/null +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentValidationTest.php @@ -0,0 +1,124 @@ + 'Comment Validation', + 'description' => 'Tests the comment validation constraints.', + 'group' => 'Comment', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_revision')); + } + + /** + * Tests the comment validation constraints. + */ + public function testValidation() { + $node = $this->entityManager->getStorageController('node')->create(array( + 'type' => 'page', + 'title' => 'test', + )); + $node->save(); + + $comment = $this->entityManager->getStorageController('comment')->create(array( + 'entity_id' => $node->id(), + // Just use some non-existing dummy field ID here, we are not testing + // that. + 'field_id' => 'test', + )); + $violations = $comment->validate(); + $this->assertEqual(count($violations), 0, 'No violations when validating a default comment.'); + + $comment->set('subject', $this->randomString(65)); + $this->assertLengthViolation($comment, 'subject', 64); + // Make the subject valid. + $comment->set('subject', $this->randomString()); + + $comment->set('name', $this->randomString(61)); + $this->assertLengthViolation($comment, 'name', 60); + // Validate a name collision between an anonymous comment author name and an + // existing user account name. + $user = entity_create('user', array('name' => 'test')); + $user->save(); + $comment->set('name', 'test'); + $violations = $comment->validate(); + $this->assertEqual(count($violations), 1, "Violation found on author name collision"); + $this->assertEqual($violations[0]->getPropertyPath(), "name"); + $this->assertEqual($violations[0]->getMessage(), t('%name belongs to a registered user.', array('%name' => 'test'))); + + // Make the name valid. + $comment->set('name', $this->randomString()); + + $comment->set('mail', 'invalid'); + $violations = $comment->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.')); + $comment->set('mail', NULL); + + $comment->set('homepage', 'http://example.com/' . $this->randomName(237)); + $this->assertLengthViolation($comment, 'homepage', 255); + + $comment->set('homepage', 'invalid'); + $violations = $comment->validate(); + $this->assertEqual(count($violations), 1, 'Violation found when homepage is invalid'); + $this->assertEqual($violations[0]->getPropertyPath(), 'homepage.0.value'); + // @todo This message should be improved in https://drupal.org/node/2012690 + $this->assertEqual($violations[0]->getMessage(), t('This value should be of the correct primitive type.')); + $comment->set('homepage', NULL); + + $comment->set('hostname', $this->randomString(129)); + $this->assertLengthViolation($comment, 'hostname', 128); + $comment->set('hostname', NULL); + + $comment->set('thread', $this->randomString(256)); + $this->assertLengthViolation($comment, 'thread', 255); + } + + /** + * Verifies that a length violation exists for the given field. + * + * @param \Drupal\Core\Entity\ContentEntityInterface\CommentInterface $comment + * The comment 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($comment, $field_name, $length) { + $violations = $comment->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))); + } +} diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php index 9798c4f..9c38ddc 100644 --- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php +++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php @@ -142,7 +142,7 @@ public static function baseFieldDefinitions($entity_type) { ->setLabel(t('Name')) ->setDescription(t('The name of the test entity.')) ->setTranslatable(TRUE) - ->setPropertyConstraints('value', array('Length' => array('max' => 32))); + ->setSetting('max_length', 32); // @todo: Add allowed values validation. $fields['type'] = FieldDefinition::create('string') diff --git a/core/modules/user/lib/Drupal/user/Entity/User.php b/core/modules/user/lib/Drupal/user/Entity/User.php index 2954df5..e61e9a7 100644 --- a/core/modules/user/lib/Drupal/user/Entity/User.php +++ b/core/modules/user/lib/Drupal/user/Entity/User.php @@ -471,8 +471,7 @@ public static function baseFieldDefinitions($entity_type) { // @todo Convert to a text field in https://drupal.org/node/1548204. $fields['signature'] = FieldDefinition::create('string') ->setLabel(t('Signature')) - ->setDescription(t('The signature of this user.')) - ->setPropertyConstraints('value', array('Length' => array('max' => 255))); + ->setDescription(t('The signature of this user.')); $fields['signature_format'] = FieldDefinition::create('string') ->setLabel(t('Signature format')) ->setDescription(t('The signature format of this user.')); @@ -480,7 +479,7 @@ public static function baseFieldDefinitions($entity_type) { $fields['timezone'] = FieldDefinition::create('string') ->setLabel(t('Timezone')) ->setDescription(t('The timezone of this user.')) - ->setPropertyConstraints('value', array('Length' => array('max' => 32))); + ->setSetting('max_length', 32); $fields['status'] = FieldDefinition::create('boolean') ->setLabel(t('User status'))