diff --git a/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php new file mode 100644 index 0000000..5227ed2 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php @@ -0,0 +1,73 @@ +create('NotNull', array()); } + + // If the definition provides a class check for further validation criteria. + $class = isset($definition['class']) ? $definition['class'] : $type_definition['class']; + // Check if the class provides allowed values. + if (array_key_exists('Drupal\Core\TypedData\AllowedValuesInterface', class_implements($class))) { + $constraints[] = $validation_manager->create('AllowedValues', array()); + } + return $constraints; } } diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php index 106f2d0..73bfc8b 100644 --- a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php +++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php @@ -91,4 +91,14 @@ public function getPropertyName() { public function getPropertyValue($container) { return $this->typedData->getValue(); } + + /** + * Returns the typed data object. + * + * @return \Drupal\Core\TypedData\TypedDataInterface + * The typed data object. + */ + public function getTypedData() { + return $this->typedData; + } } diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php new file mode 100644 index 0000000..08f9279 --- /dev/null +++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php @@ -0,0 +1,28 @@ +context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) { + $allowed_values = $this->context->getMetadata()->getTypedData()->getValues(); + $constraint->choices = $allowed_values; + } + return parent::validate($value, $constraint); + } +} diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php index 649f033..c288284 100644 --- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php +++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php @@ -19,7 +19,7 @@ class EditTestBase extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'text', 'edit'); + public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'filter', 'user', 'text', 'edit'); /** * Sets the default field storage backend for fields created during tests. */ @@ -28,7 +28,7 @@ function setUp() { $this->installSchema('system', 'variable'); $this->installSchema('entity_test', array('entity_test', 'entity_test_rev')); - $this->installConfig(array('field')); + $this->installConfig(array('field', 'filter')); } /** diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index 1b90cde..c8f8561 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -9,6 +9,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Template\Attribute; use Drupal\filter\Plugin\Core\Entity\FilterFormat; +use Drupal\Core\TypedData\Primitive; /** * Non-HTML markup language filters that generate HTML. @@ -108,6 +109,19 @@ function filter_element_info() { } /** + * Implements hook_data_type_info(). + */ +function filter_data_type_info() { + return array( + 'filter_format' => array( + 'label' => t('Filter format'), + 'class' => 'Drupal\filter\Type\FilterFormat', + 'primitive type' => Primitive::STRING, + ), + ); +} + +/** * Implements hook_menu(). */ function filter_menu() { diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php index f32d30b..d4b1bda 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php @@ -8,6 +8,8 @@ namespace Drupal\filter\Tests; use Drupal\simpletest\DrupalUnitTestBase; +use Drupal\filter\Type\FilterFormat; +use Symfony\Component\Validator\ConstraintViolationListInterface; /** * Tests the behavior of Filter's API. @@ -184,4 +186,90 @@ function testFilterFormatAPI() { ); } + /** + * Tests the function of the typed data type. + */ + function testTypedDataAPI() { + $definition = array('type' => 'filter_format'); + $data = \Drupal::typedData()->create($definition); + + if ($this->assertTrue($data instanceof \Drupal\Core\TypedData\AllowedValuesInterface, 'Typed data object implements \Drupal\Core\TypedData\AllowedValuesInterface')) { + $this->checkPermissions(array(), TRUE); + $filtered_html_user = $this->drupalCreateUser(array( + filter_permission_name(filter_format_load('filtered_html')), + )); + // Test with anonymous user. + $GLOBALS['user'] = drupal_anonymous_user(); + + $available_values = $data->getAvailableValues(); + $this->assertEqual( + $available_values, + array('filtered_html', 'full_html', 'plain_text'), + 'Expected available format values found' + ); + $available_options = $data->getAvailableOptions(); + $expected_available_options = array( + 'filtered_html' => 'Filtered HTML', + 'full_html' => 'Full HTML', + 'plain_text' => 'Plain text', + ); + $this->assertEqual($available_options, $expected_available_options); + $allowed_values = $data->getValues(); + $this->assertEqual($allowed_values, array('plain_text')); + $allowed_options = $data->getOptions(); + $this->assertEqual($allowed_options, array('plain_text' => 'Plain text')); + + $data->setValue('foo'); + $violations = $data->validate(); + $this->assertFilterFormatViolation($violations, 'foo'); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getRoot(), $data, 'Violation root is filter format.'); + $this->assertEqual($violation->getPropertyPath(), '', 'Violation property path is correct.'); + $this->assertEqual($violation->getInvalidValue(), 'foo', 'Violation contains invalid value.'); + + $data->setValue('plain_text'); + $violations = $data->validate(); + $this->assertEqual(count($violations), 0, "No validation violation for format 'plain_text' found"); + + // Anonymous doesn't have access to the 'filtered_html' format. + $data->setValue('filtered_html'); + $violations = $data->validate(); + $this->assertFilterFormatViolation($violations, 'filtered_html'); + + // Set user with access to 'filtered_html' format. + $GLOBALS['user'] = $filtered_html_user; + $violations = $data->validate(); + $this->assertEqual(count($violations), 0, "No validation violation for accessible format 'filtered_html' found."); + + $allowed_values = $data->getValues(); + $this->assertEqual($allowed_values, array('filtered_html', 'plain_text')); + $allowed_options = $data->getOptions(); + $expected_allowed_options = array( + 'filtered_html' => 'Filtered HTML', + 'plain_text' => 'Plain text', + ); + $this->assertEqual($allowed_options, $expected_allowed_options); + } + } + + /** + * Checks if an expected violation exists in the given violations. + * + * @param \Symfony\Component\Validator\ConstraintViolationListInterface $violations + * The violations to assert. + * @param mixed $invalid_value + * The expected invalid value. + */ + public function assertFilterFormatViolation(ConstraintViolationListInterface $violations, $invalid_value) { + $filter_format_violation_found = FALSE; + foreach ($violations as $violation) { + if ($violation->getRoot() instanceof FilterFormat && $violation->getInvalidValue() === $invalid_value) { + $filter_format_violation_found = TRUE; + break; + } + } + $this->assertTrue($filter_format_violation_found, format_string('Validation violation for invalid value "%invalid_value" found', array('%invalid_value' => $invalid_value))); + } } diff --git a/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php new file mode 100644 index 0000000..cf3aba8 --- /dev/null +++ b/core/modules/filter/lib/Drupal/filter/Type/FilterFormat.php @@ -0,0 +1,55 @@ +getAvailableOptions()); + } + + /** + * {@inheritdoc} + */ + public function getAvailableOptions($account = NULL) { + $values = array(); + foreach (filter_formats() as $format) { + $values[$format->id()] = $format->label(); + } + return $values; + } + + /** + * {@inheritdoc} + */ + public function getValues($account = NULL) { + return array_keys($this->getOptions($account)); + } + + /** + * {@inheritdoc} + */ + public function getOptions($user = NULL) { + $user = empty($user) ? $GLOBALS['user'] : $user; + $values = array(); + // @todo: Avoid calling functions but move to injected dependencies. + foreach (filter_formats($user) as $format) { + $values[$format->id()] = $format->label(); + } + return $values; + } +} diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php index ae7e582..118a313 100644 --- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php +++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php @@ -30,7 +30,7 @@ * * @var array */ - public static $modules = array('entity', 'entity_test', 'entity_reference', 'field', 'field_sql_storage', 'hal', 'language', 'rest', 'serialization', 'system', 'text', 'user'); + public static $modules = array('entity', 'entity_test', 'entity_reference', 'field', 'field_sql_storage', 'hal', 'language', 'rest', 'serialization', 'system', 'text', 'user', 'filter'); /** * The mock serializer. diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php index 74fa8d9..b7710cf 100644 --- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php +++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php @@ -16,7 +16,7 @@ * * @var array */ - public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'field_sql_storage'); + public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'filter', 'field_sql_storage'); protected function setUp() { parent::setUp(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php index 4d24a51..93506e2 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\Field\FieldInterface; use Drupal\Core\Entity\Field\FieldItemInterface; use Drupal\Core\Language\Language; +use Drupal\Core\TypedData\Type\String; use Drupal\Core\TypedData\TypedDataInterface; /** @@ -382,7 +383,7 @@ protected function assertIntrospection($entity_type) { $textfield_properties = $entity->field_test_text->getPropertyDefinitions(); $this->assertEqual($textfield_properties['value']['type'], 'string', $entity_type .': String value property of the test-text field found.'); - $this->assertEqual($textfield_properties['format']['type'], 'string', $entity_type .': String format field of the test-text field found.'); + $this->assertEqual($textfield_properties['format']['type'], 'filter_format', $entity_type .': String format field of the test-text field found.'); $this->assertEqual($textfield_properties['processed']['type'], 'string', $entity_type .': String processed property of the test-text field found.'); // @todo: Once the user entity has definitions, continue testing getting @@ -507,7 +508,8 @@ protected function assertDataStructureInterfaces($entity_type) { */ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) { - if ($wrapper->getType() == 'string') { + // @todo Switch to check StringInterface as soon as it is in place. + if ($wrapper instanceof String) { $strings[] = $wrapper->getValue(); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php index 5e73dbc..893e902 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -19,7 +19,7 @@ * * @var array */ - public static $modules = array('entity', 'user', 'system', 'field', 'text', 'field_sql_storage', 'entity_test'); + public static $modules = array('entity', 'user', 'system', 'field', 'text', 'filter', 'field_sql_storage', 'entity_test'); public function setUp() { parent::setUp(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php index f1a0fdb..a6d362d 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php @@ -133,6 +133,19 @@ protected function checkValidation($entity_type) { $this->assertEqual($violation->getRoot(), $test_entity, 'Violation root is entity.'); $this->assertEqual($violation->getPropertyPath(), 'name.0.value', 'Violation property path is correct.'); $this->assertEqual($violation->getInvalidValue(), $test_entity->name->value, 'Violation contains invalid value.'); + + + $test_entity = clone $entity; + $test_entity->field_test_text->format = $this->randomString(33); + $violations = $test_entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.')); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getRoot(), $test_entity, 'Violation root is entity.'); + $this->assertEqual($violation->getPropertyPath(), 'field_test_text.0.format', 'Violation property path is correct.'); + $this->assertEqual($violation->getInvalidValue(), $test_entity->field_test_text->format, 'Violation contains invalid value.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php index a5db46a..27834a3 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php @@ -19,7 +19,7 @@ class FieldAccessTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('entity', 'entity_test', 'field', 'field_sql_storage', 'system', 'text', 'user'); + public static $modules = array('entity', 'entity_test', 'field', 'field_sql_storage', 'system', 'text', 'filter', 'user'); /** * Holds the currently active global user ID that initiated the test run. diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php index 0a2b900..2733c05 100644 --- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php @@ -26,7 +26,7 @@ class TextPlainUnitTest extends DrupalUnitTestBase { * * @var array */ - public static $modules = array('entity', 'field', 'field_sql_storage', 'text', 'entity_test', 'system'); + public static $modules = array('entity', 'field', 'field_sql_storage', 'text', 'entity_test', 'system', 'filter'); /** * Contains rendered content. diff --git a/core/modules/text/lib/Drupal/text/Type/TextItem.php b/core/modules/text/lib/Drupal/text/Type/TextItem.php index 0f3842a..281e74a 100644 --- a/core/modules/text/lib/Drupal/text/Type/TextItem.php +++ b/core/modules/text/lib/Drupal/text/Type/TextItem.php @@ -34,7 +34,7 @@ public function getPropertyDefinitions() { 'label' => t('Text value'), ); static::$propertyDefinitions['format'] = array( - 'type' => 'string', + 'type' => 'filter_format', 'label' => t('Text format'), ); static::$propertyDefinitions['processed'] = array(