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..5cdbfd7 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,23 @@ public static function schema(FieldDefinitionInterface $field_definition) { ); } + /** + * {@inheritdoc} + */ + public function getConstraints() { + $constraints = parent::getConstraints(); + + // Not all plugins that inherit from this class have a configurable maximum + // length, so we only apply the length constraint conditionally. Example: + // UuidItem has a fixed length of 128. + if ($max_length = $this->getFieldSetting('max_length')) { + $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager(); + $constraints[] = $constraint_manager->create('ComplexData', array( + 'value' => array('Length' => array('max' => $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..e451b01 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php @@ -8,7 +8,6 @@ namespace Drupal\Core\Field\Plugin\Field\FieldType; use Drupal\Core\Field\FieldDefinitionInterface; -use Drupal\Core\Field\FieldItemBase; use Drupal\Core\TypedData\DataDefinition; /** @@ -21,7 +20,7 @@ * configurable = FALSE * ) */ -class UriItem extends FieldItemBase { +class UriItem extends StringItem { /** * Field definitions of the contained properties. diff --git a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php index 3c8b696..8aef5c6 100644 --- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php @@ -383,7 +383,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')) @@ -396,19 +397,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') @@ -427,7 +436,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..221486a --- /dev/null +++ b/core/modules/comment/lib/Drupal/comment/Plugin/Validation/Constraint/CommentNameConstraint.php @@ -0,0 +1,25 @@ +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..b204eb2 --- /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('The name %name you used 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/2171539 + $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))); + } +}