diff --git a/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php new file mode 100644 index 0000000..e8955d8 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php @@ -0,0 +1,98 @@ +create('NotNull', array()); } + + // If the definition does not provide a class use the class from the type + // definition for performing interface checks. + $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/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) { + $account = \Drupal::request()->attributes->get('_account'); + $allowed_values = $this->context->getMetadata()->getTypedData()->getSettableValues($account); + $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/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php index c451fdb..92002ce 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php @@ -148,6 +148,24 @@ public function instanceSettingsForm(array $form, array &$form_state) { } /** + * Returns options provided via the legacy callback hook_options_list(). + * + * @todo: Convert all legacy callback implementations to methods. + * + * @see \Drupal\Core\TypedData\AllowedValuesInterface + */ + public function getSettableOptions() { + $definition = $this->getPluginDefinition(); + $callback = "{$definition['module']}_options_list"; + if (function_exists($callback)) { + // We are at the field item level, so we need to go two levels up to get + // to the entity object. + $entity = $this->getParent()->getParent(); + return $callback($this->getInstance(), $entity); + } + } + + /** * Returns the legacy callback for a given field type "hook". * * Copied from \Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem, diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php index 7c7a343..4cf4556 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php @@ -370,7 +370,9 @@ public function flagErrors(EntityInterface $entity, $langcode, FieldInterface $i foreach ($delta_violations as $violation) { // @todo: Pass $violation->arrayPropertyPath as property path. $error_element = $this->errorElement($delta_element, $violation, $form, $form_state); - form_error($error_element, $violation->getMessage()); + if ($error_element !== FALSE) { + form_error($error_element, $violation->getMessage()); + } } } // Reinitialize the errors list for the next submit. diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php index 6c9e88d..c3e6b6a 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php @@ -126,18 +126,19 @@ public function formElement(FieldInterface $items, $delta, array $element, $lang * @param array $element * An array containing the form element for the widget, as generated by * formElement(). - * @param \Symfony\Component\Validator\ConstraintViolationInterface $violations - * The list of constraint violations reported during the validation phase. + * @param \Symfony\Component\Validator\ConstraintViolationInterface $violation + * A constraint violation reported during the validation phase. * @param array $form * The form structure where field elements are attached to. This might be a * full form structure, or a sub-element of a larger form. * @param array $form_state * An associative array containing the current state of the form. * - * @return array - * The element on which the error should be flagged. + * @return array|false + * The element on which the error should be flagged, or FALSE to completely + * ignore the violation (use with care!). */ - public function errorElement(array $element, ConstraintViolationInterface $violations, array $form, array &$form_state); + public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, array &$form_state); /** * Massages the form values into the format expected for field values. diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php index f5ab447..3ed53e2 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php +++ b/core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php @@ -108,6 +108,22 @@ public function prepareCache() { } /** + * Returns options provided via the legacy callback hook_options_list(). + * + * @todo: Convert all legacy callback implementations to methods. + * + * @see \Drupal\Core\TypedData\AllowedValuesInterface + */ + public function getSettableOptions() { + $definition = $this->getPluginDefinition(); + $callback = "{$definition['module']}_options_list"; + if (function_exists($callback)) { + $entity = $this->getParent()->getParent(); + return $callback($this->getInstance(), $entity); + } + } + + /** * Returns the legacy callback for a given field type "hook". * * @param string $hook diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/DataType/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Plugin/DataType/FilterFormat.php new file mode 100644 index 0000000..eb0738f --- /dev/null +++ b/core/modules/filter/lib/Drupal/filter/Plugin/DataType/FilterFormat.php @@ -0,0 +1,62 @@ +getPossibleOptions()); + } + + /** + * {@inheritdoc} + */ + public function getPossibleOptions(AccountInterface $account = NULL) { + $values = array(); + foreach (filter_formats() as $format) { + $values[$format->id()] = $format->label(); + } + return $values; + } + + /** + * {@inheritdoc} + */ + public function getSettableValues(AccountInterface $account = NULL) { + return array_keys($this->getSettableOptions($account)); + } + + /** + * {@inheritdoc} + */ + public function getSettableOptions(AccountInterface $account = NULL) { + $values = array(); + // @todo: Avoid calling functions but move to injected dependencies. + foreach (filter_formats($account) as $format) { + $values[$format->id()] = $format->label(); + } + return $values; + } +} diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php index f32d30b..e8c97db 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php @@ -7,14 +7,17 @@ namespace Drupal\filter\Tests; -use Drupal\simpletest\DrupalUnitTestBase; +use Drupal\Core\TypedData\AllowedValuesInterface; +use Drupal\filter\Plugin\DataType\FilterFormat; +use Drupal\system\Tests\Entity\EntityUnitTestBase; +use Symfony\Component\Validator\ConstraintViolationListInterface; /** * Tests the behavior of Filter's API. */ -class FilterAPITest extends DrupalUnitTestBase { +class FilterAPITest extends EntityUnitTestBase { - public static $modules = array('system', 'filter', 'filter_test'); + public static $modules = array('system', 'filter', 'filter_test', 'user'); public static function getInfo() { return array( @@ -27,7 +30,8 @@ public static function getInfo() { function setUp() { parent::setUp(); - $this->installConfig(array('system')); + $this->installConfig(array('system', 'filter')); + $this->installSchema('user', array('users_roles')); // Create Filtered HTML format. $filtered_html_format = entity_create('filter_format', array( @@ -184,4 +188,87 @@ function testFilterFormatAPI() { ); } + /** + * Tests the function of the typed data type. + */ + function testTypedDataAPI() { + $definition = array('type' => 'filter_format'); + $data = \Drupal::typedData()->create($definition); + + $this->assertTrue($data instanceof AllowedValuesInterface, 'Typed data object implements \Drupal\Core\TypedData\AllowedValuesInterface'); + + $filtered_html_user = $this->createUser(array('uid' => 2), array( + filter_permission_name(filter_format_load('filtered_html')), + )); + + // Test with anonymous user. + $user = drupal_anonymous_user(); + \Drupal::request()->attributes->set('_account', $user); + + $available_values = $data->getPossibleValues(); + $this->assertEqual($available_values, array('filtered_html', 'full_html', 'plain_text')); + $available_options = $data->getPossibleOptions(); + $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->getSettableValues($user); + $this->assertEqual($allowed_values, array('plain_text')); + $allowed_options = $data->getSettableOptions($user); + $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. + \Drupal::request()->attributes->set('_account', $filtered_html_user); + $violations = $data->validate(); + $this->assertEqual(count($violations), 0, "No validation violation for accessible format 'filtered_html' found."); + + $allowed_values = $data->getSettableValues($filtered_html_user); + $this->assertEqual($allowed_values, array('filtered_html', 'plain_text')); + $allowed_options = $data->getSettableOptions($filtered_html_user); + $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/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php index ea55fd9..59eab53 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/options/lib/Drupal/options/Plugin/field/widget/OptionsWidgetBase.php b/core/modules/options/lib/Drupal/options/Plugin/field/widget/OptionsWidgetBase.php index b74147e..5895349 100644 --- a/core/modules/options/lib/Drupal/options/Plugin/field/widget/OptionsWidgetBase.php +++ b/core/modules/options/lib/Drupal/options/Plugin/field/widget/OptionsWidgetBase.php @@ -13,6 +13,13 @@ /** * Base class for the 'options_*' widgets. + * + * Field types willing to enable one or several of the widgets defined in + * options.module (select, radios/checkboxes, on/off checkbox) need to + * implement the AllowedValuesInterface to specify the list of options to + * display in the widgets. + * + * @see \Drupal\Core\TypedData\AllowedValuesInterface */ abstract class OptionsWidgetBase extends WidgetBase { @@ -107,17 +114,20 @@ public static function validateElement(array $element, array &$form_state) { /** * Returns the array of options for the widget. * + * @param int $delta + * (optional) The delta of the item to get options for. Defaults to 0. + * * @return array * The array of options for the widget. */ - protected function getOptions() { + protected function getOptions($delta = 0) { if (!isset($this->options)) { $module_handler = \Drupal::moduleHandler(); // Get the list of options from the field type module, and sanitize them. - $field_type_info = field_info_field_types($this->fieldDefinition->getFieldType()); - $module = $field_type_info['provider']; - $options = (array) $module_handler->invoke($module, 'options_list', array($this->fieldDefinition, $this->entity)); + $items = $this->entity->getNGEntity()->get($this->fieldDefinition->getFieldName()); + // Limit the settable options for the current user account. + $options = $items[$delta]->getSettableOptions(\Drupal::request()->attributes->get('_account')); // Add an empty option if the widget needs one. if ($empty_option = $this->getEmptyOption()) { @@ -158,13 +168,15 @@ protected function getOptions() { * * @param FieldInterface $items * The field values. + * @param int $delta + * (optional) The delta of the item to get options for. Defaults to 0. * * @return array * The array of corresponding selected options. */ - protected function getSelectedOptions(FieldInterface $items) { + protected function getSelectedOptions(FieldInterface $items, $delta = 0) { // We need to check against a flat list of options. - $flat_options = $this->flattenOptions($this->getOptions()); + $flat_options = $this->flattenOptions($this->getOptions($delta)); $selected_options = array(); foreach ($items as $item) { diff --git a/core/modules/options/lib/Drupal/options/Plugin/field/widget/SelectWidget.php b/core/modules/options/lib/Drupal/options/Plugin/field/widget/SelectWidget.php index f01035a..5a5e99c 100644 --- a/core/modules/options/lib/Drupal/options/Plugin/field/widget/SelectWidget.php +++ b/core/modules/options/lib/Drupal/options/Plugin/field/widget/SelectWidget.php @@ -35,8 +35,8 @@ public function formElement(FieldInterface $items, $delta, array $element, $lang $element += array( '#type' => 'select', - '#options' => $this->getOptions(), - '#default_value' => $this->getSelectedOptions($items), + '#options' => $this->getOptions($delta), + '#default_value' => $this->getSelectedOptions($items, $delta), // Do not display a 'multiple' select box if there is only one option. '#multiple' => $this->multiple && count($this->options) > 1, ); diff --git a/core/modules/options/options.api.php b/core/modules/options/options.api.php index 5e8c694..152ed70 100644 --- a/core/modules/options/options.api.php +++ b/core/modules/options/options.api.php @@ -6,66 +6,6 @@ */ /** - * Returns the list of options to be displayed for a field. - * - * Field types willing to enable one or several of the widgets defined in - * options.module (select, radios/checkboxes, on/off checkbox) need to - * implement this hook to specify the list of options to display in the - * widgets. - * - * @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition - * The field definition. - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity object the field is attached to. - * - * @return - * The array of options for the field. Array keys are the values to be - * stored, and should be of the data type (string, number...) expected by - * the first 'column' for the field type. Array values are the labels to - * display within the widgets. The labels should NOT be sanitized, - * options.module takes care of sanitation according to the needs of each - * widget. The HTML tags defined in _field_filter_xss_allowed_tags() are - * allowed, other tags will be filtered. - */ -function hook_options_list(\Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Entity\EntityInterface $entity) { - // Sample structure. - $options = array( - 0 => t('Zero'), - 1 => t('One'), - 2 => t('Two'), - 3 => t('Three'), - ); - - // Sample structure with groups. Only one level of nesting is allowed. This - // is only supported by the 'options_select' widget. Other widgets will - // flatten the array. - $options = array( - t('First group') => array( - 0 => t('Zero'), - ), - t('Second group') => array( - 1 => t('One'), - 2 => t('Two'), - ), - 3 => t('Three'), - ); - - // In actual implementations, the array of options will most probably depend - // on properties of the field. Example from taxonomy.module: - $options = array(); - foreach ($field_definition->getFieldSetting('allowed_values') as $tree) { - $terms = taxonomy_get_tree($tree['vid'], $tree['parent'], NULL, TRUE); - if ($terms) { - foreach ($terms as $term) { - $options[$term->id()] = str_repeat('-', $term->depth) . $term->label(); - } - } - } - - return $options; -} - -/** * Alters the list of options to be displayed for a field. * * This hook can notably be used to change the label of the empty option. 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 77c816c..0df1027 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,8 @@ 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\Type\StringInterface; use Drupal\Core\TypedData\TypedDataInterface; /** @@ -376,7 +378,7 @@ protected function checkIntrospection($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 @@ -492,7 +494,7 @@ protected function assertDataStructureInterfaces($entity_type) { */ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) { - if ($wrapper->getType() == 'string') { + if ($wrapper instanceof StringInterface) { $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 34fb4cc..c792e94 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php @@ -20,7 +20,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'); /** * The entity manager service. 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 f00a35b..c3b12b7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php @@ -115,7 +115,6 @@ protected function checkValidation($entity_type) { $this->assertEqual($violations->count(), 1, 'Validation failed.'); $this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => '12'))); - $test_entity = clone $entity; $test_entity->type->value = NULL; $violations = $test_entity->validate(); @@ -133,6 +132,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/Plugin/field/field_type/TextItemBase.php b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php index df9cea0..61e7a46 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php @@ -32,7 +32,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( diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php b/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php index 298ce8d..0bed872 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php @@ -11,6 +11,7 @@ use Drupal\Core\Annotation\Translation; use Drupal\Core\Entity\Field\FieldInterface; use Drupal\field\Plugin\Type\Widget\WidgetBase; +use Symfony\Component\Validator\ConstraintViolationInterface; /** * Plugin implementation of the 'text_textarea' widget. @@ -89,4 +90,15 @@ public function formElement(FieldInterface $items, $delta, array $element, $lang return $element; } + /** + * {@inheritdoc} + */ + public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, array &$form_state) { + if ($violation->arrayPropertyPath == array('format') && isset($element['format']['#access']) && !$element['format']['#access']) { + // Ignore validation errors for formats if formats may not be changed, + // i.e. when existing formats become invalid. See filter_process_format(). + return FALSE; + } + return $element; + } } diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php b/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php index 50b1569..a6edca8 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php @@ -85,7 +85,8 @@ function formElement(FieldInterface $items, $delta, array $element, $langcode, a * {@inheritdoc} */ public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, array &$form_state) { - return $element[$violation->arrayPropertyPath[0]]; + $element = parent::errorElement($element, $violation, $form, $form_state); + return ($element === FALSE) ? FALSE : $element[$violation->arrayPropertyPath[0]]; } } diff --git a/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php b/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php index 50ed4a0..3a24d37 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php +++ b/core/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php @@ -11,6 +11,7 @@ use Drupal\Core\Annotation\Translation; use Drupal\Core\Entity\Field\FieldInterface; use Drupal\field\Plugin\Type\Widget\WidgetBase; +use Symfony\Component\Validator\ConstraintViolationInterface; /** * Plugin implementation of the 'text_textfield' widget. @@ -90,4 +91,16 @@ public function formElement(FieldInterface $items, $delta, array $element, $lang return $element; } + /** + * {@inheritdoc} + */ + public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, array &$form_state) { + if ($violation->arrayPropertyPath == array('format') && isset($element['format']['#access']) && !$element['format']['#access']) { + // Ignore validation errors for formats if formats may not be changed, + // i.e. when existing formats become invalid. See filter_process_format(). + return FALSE; + } + return $element; + } + } 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.