summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2013-08-27 13:10:25 (GMT)
committerAlex Pott2013-08-27 13:10:25 (GMT)
commit52771351f9b118a4c6eb51e34173c011ee33e2d6 (patch)
treeb510e4cc96c9551d0c0777a65f59f2209f2cb307
parent94cd6717a8bd07eb1333d79fb9ab8aa61d845129 (diff)
Issue #1758622 by klausi, fago, das-peter, Pancho, Berdir, yched: Provide the options list of an entity field.
-rw-r--r--core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php98
-rw-r--r--core/lib/Drupal/Core/TypedData/TypedDataManager.php9
-rw-r--r--core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraint.php28
-rw-r--r--core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php30
-rw-r--r--core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php4
-rw-r--r--core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigEntityReferenceItemBase.php18
-rw-r--r--core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php4
-rw-r--r--core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php11
-rw-r--r--core/modules/field/lib/Drupal/field/Plugin/field/field_type/LegacyConfigFieldItem.php16
-rw-r--r--core/modules/filter/lib/Drupal/filter/Plugin/DataType/FilterFormat.php54
-rw-r--r--core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php95
-rw-r--r--core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php2
-rw-r--r--core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php2
-rw-r--r--core/modules/options/lib/Drupal/options/Plugin/field/widget/ButtonsWidget.php2
-rw-r--r--core/modules/options/lib/Drupal/options/Plugin/field/widget/OnOffWidget.php2
-rw-r--r--core/modules/options/lib/Drupal/options/Plugin/field/widget/OptionsWidgetBase.php31
-rw-r--r--core/modules/options/lib/Drupal/options/Plugin/field/widget/SelectWidget.php4
-rw-r--r--core/modules/options/lib/Drupal/options/Tests/OptionsFieldTest.php5
-rw-r--r--core/modules/options/options.api.php63
-rw-r--r--core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php2
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php2
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php2
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php13
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php2
-rw-r--r--core/modules/text/lib/Drupal/text/Plugin/field/field_type/TextItemBase.php2
-rw-r--r--core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php13
-rw-r--r--core/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php3
-rw-r--r--core/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php13
-rw-r--r--core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php2
29 files changed, 433 insertions, 99 deletions
diff --git a/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
new file mode 100644
index 0000000..bc58989
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/AllowedValuesInterface.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\AllowedValuesInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Interface for retrieving all possible and settable values.
+ *
+ * While possible values specify which values existing data might have, settable
+ * values define the values that are allowed to be set by a user.
+ *
+ * For example, in an workflow scenario, the settable values for a state field
+ * might depend on the currently set state, while possible values are all
+ * states. Thus settable values would be used in an editing context, while
+ * possible values would be used for presenting filtering options in a search.
+ *
+ * For convenience, lists of both settable and possible values are also provided
+ * as structured options arrays that can be used in an Options widget such as a
+ * select box or checkboxes.
+ *
+ * @see \Drupal\options\Plugin\field\widget\OptionsWidgetBase
+ */
+interface AllowedValuesInterface {
+
+ /**
+ * Returns an array of possible values.
+ *
+ * If the optional $account parameter is passed, then the array is filtered to
+ * values viewable by the account.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * (optional) The user account for which to filter the possible values. If
+ * omitted, all possible values are returned.
+ *
+ * @return array
+ * An array of possible values.
+ */
+ public function getPossibleValues(AccountInterface $account = NULL);
+
+ /**
+ * Returns an array of possible values with labels for display.
+ *
+ * If the optional $account parameter is passed, then the array is filtered to
+ * values viewable by the account.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * (optional) The user account for which to filter the possible options.
+ * If omitted, all possible options are returned.
+ *
+ * @return array
+ * An array of possible options for the object that may be used in an
+ * Options widget, for example when existing data should be filtered. It may
+ * either be a flat array of option labels keyed by values, or a
+ * two-dimensional array of option groups (array of flat option arrays,
+ * keyed by option group label). Note that labels should NOT be sanitized.
+ */
+ public function getPossibleOptions(AccountInterface $account = NULL);
+
+ /**
+ * Returns an array of settable values.
+ *
+ * If the optional $account parameter is passed, then the array is filtered to
+ * values settable by the account.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * (optional) The user account for which to filter the settable values. If
+ * omitted, all settable values are returned.
+ *
+ * @return array
+ * An array of settable values.
+ */
+ public function getSettableValues(AccountInterface $account = NULL);
+
+ /**
+ * Returns an array of settable values with labels for display.
+ *
+ * If the optional $account parameter is passed, then the array is filtered to
+ * values settable by the account.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * (optional) The user account for which to filter the settable options. If
+ * omitted, all settable options are returned.
+ *
+ * @return array
+ * An array of settable options for the object that may be used in an
+ * Options widget, usually when new data should be entered. It may either be
+ * a flat array of option labels keyed by values, or a two-dimensional array
+ * of option groups (array of flat option arrays, keyed by option group
+ * label). Note that labels should NOT be sanitized.
+ */
+ public function getSettableOptions(AccountInterface $account = NULL);
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index d68f4c0..6f85867 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -377,6 +377,15 @@ class TypedDataManager extends DefaultPluginManager {
if (!empty($definition['required']) && empty($definition['constraints']['NotNull'])) {
$constraints[] = $validation_manager->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 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Constraint\AllowedValuesConstraint.
+ */
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraints\Choice;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Checks for the value being allowed.
+ *
+ * @Plugin(
+ * id = "AllowedValues",
+ * label = @Translation("Allowed values", context = "Validation")
+ * )
+ *
+ * @see \Drupal\Core\TypedData\AllowedValuesInterface
+ */
+class AllowedValuesConstraint extends Choice {
+
+ public $minMessage = 'You must select at least %limit choice.|You must select at least %limit choices.';
+ public $maxMessage = 'You must select at most %limit choice.|You must select at most %limit choices.';
+}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
new file mode 100644
index 0000000..7ad2cfd
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraintValidator.
+ */
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Drupal\Core\TypedData\AllowedValuesInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\Constraints\ChoiceValidator;
+
+/**
+ * Validates the AllowedValues constraint.
+ */
+class AllowedValuesConstraintValidator extends ChoiceValidator {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($value, Constraint $constraint) {
+ if ($this->context->getMetadata()->getTypedData() instanceof AllowedValuesInterface) {
+ $account = \Drupal::currentUser();
+ $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 @@ class EditTestBase extends DrupalUnitTestBase {
$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 b3c0150..77477d9 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 @@ class ConfigEntityReferenceItemBase extends EntityReferenceItem implements Confi
}
/**
+ * 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['provider']}_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 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
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 833819a..e27484e 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 @@ interface WidgetInterface extends WidgetBaseInterface {
* @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|bool
+ * 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 26e0a10..8407cbf 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 @@ abstract class LegacyConfigFieldItem extends ConfigFieldItemBase implements Prep
}
/**
+ * 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['provider']}_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..fbbf864
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/DataType/FilterFormat.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\DataType\FilterFormat.
+ */
+
+namespace Drupal\filter\Plugin\DataType;
+
+use Drupal\Core\TypedData\Annotation\DataType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TypedData\AllowedValuesInterface;
+use Drupal\Core\TypedData\Plugin\DataType\String;
+
+/**
+ * The filter format data type.
+ *
+ * @DataType(
+ * id = "filter_format",
+ * label = @Translation("Filter format")
+ * )
+ */
+class FilterFormat extends String implements AllowedValuesInterface {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPossibleValues(AccountInterface $account = NULL) {
+ return array_keys($this->getPossibleOptions());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getPossibleOptions(AccountInterface $account = NULL) {
+ return array_map(function ($format) { return $format->label(); }, filter_formats());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSettableValues(AccountInterface $account = NULL) {
+ return array_keys($this->getSettableOptions($account));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSettableOptions(AccountInterface $account = NULL) {
+ // @todo: Avoid calling functions but move to injected dependencies.
+ return array_map(function ($format) { return $format->label(); }, filter_formats($account));
+ }
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
index f32d30b..003f82b 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 @@ class FilterAPITest extends DrupalUnitTestBase {
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 @@ class FilterAPITest extends DrupalUnitTestBase {
);
}
+ /**
+ * 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();
+ $this->container->set('current_user', $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.
+ $this->container->set('current_user', $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 0c1bcaa..13c9756 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 @@ abstract class NormalizerTestBase extends DrupalUnitTestBase {
*
* @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/node/lib/Drupal/node/Tests/NodeValidationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
index dcba3e2..e5ecd7d 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
@@ -19,7 +19,7 @@ class NodeValidationTest extends DrupalUnitTestBase {
*
* @var array
*/
- public static $modules = array('node', 'entity', 'field', 'text', 'field_sql_storage');
+ public static $modules = array('node', 'entity', 'field', 'text', 'field_sql_storage', 'filter');
public static function getInfo() {
return array(
diff --git a/core/modules/options/lib/Drupal/options/Plugin/field/widget/ButtonsWidget.php b/core/modules/options/lib/Drupal/options/Plugin/field/widget/ButtonsWidget.php
index b4557ae..bee3e3b 100644
--- a/core/modules/options/lib/Drupal/options/Plugin/field/widget/ButtonsWidget.php
+++ b/core/modules/options/lib/Drupal/options/Plugin/field/widget/ButtonsWidget.php
@@ -34,7 +34,7 @@ class ButtonsWidget extends OptionsWidgetBase {
public function formElement(FieldInterface $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
$element = parent::formElement($items, $delta, $element, $langcode, $form, $form_state);
- $options = $this->getOptions();
+ $options = $this->getOptions($items[$delta]);
$selected = $this->getSelectedOptions($items);
// If required and there is one single option, preselect it.
diff --git a/core/modules/options/lib/Drupal/options/Plugin/field/widget/OnOffWidget.php b/core/modules/options/lib/Drupal/options/Plugin/field/widget/OnOffWidget.php
index c0ef661..50cc08a 100644
--- a/core/modules/options/lib/Drupal/options/Plugin/field/widget/OnOffWidget.php
+++ b/core/modules/options/lib/Drupal/options/Plugin/field/widget/OnOffWidget.php
@@ -59,7 +59,7 @@ class OnOffWidget extends OptionsWidgetBase {
public function formElement(FieldInterface $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
$element = parent::formElement($items, $delta, $element, $langcode, $form, $form_state);
- $options = $this->getOptions();
+ $options = $this->getOptions($items[$delta]);
$selected = $this->getSelectedOptions($items);
$element += array(
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 0d55ab3..f218438 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
@@ -9,10 +9,18 @@ namespace Drupal\options\Plugin\field\widget;
use Drupal\Core\Entity\Field\FieldDefinitionInterface;
use Drupal\Core\Entity\Field\FieldInterface;
+use Drupal\Core\Entity\Field\FieldItemInterface;
use Drupal\field\Plugin\Type\Widget\WidgetBase;
/**
* 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 {
@@ -49,7 +57,6 @@ abstract class OptionsWidgetBase extends WidgetBase {
public function formElement(FieldInterface $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
// Prepare some properties for the child methods to build the actual form
// element.
- $this->entity = $element['#entity'];
$this->required = $element['#required'];
$cardinality = $this->fieldDefinition->getFieldCardinality();
$this->multiple = ($cardinality == FIELD_CARDINALITY_UNLIMITED) || ($cardinality > 1);
@@ -107,17 +114,16 @@ abstract class OptionsWidgetBase extends WidgetBase {
/**
* Returns the array of options for the widget.
*
+ * @param \Drupal\Core\Entity\Field\FieldItemInterface $item
+ * The field item.
+ *
* @return array
* The array of options for the widget.
*/
- protected function getOptions() {
+ protected function getOptions(FieldItemInterface $item) {
if (!isset($this->options)) {
- $module_handler = \Drupal::moduleHandler();
-
- // Get the list of options from the field type module, and sanitize them.
- $field_type_info = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinition($this->fieldDefinition->getFieldType());
- $module = $field_type_info['provider'];
- $options = (array) $module_handler->invoke($module, 'options_list', array($this->fieldDefinition, $this->entity));
+ // Limit the settable options for the current user account.
+ $options = $item->getSettableOptions(\Drupal::currentUser());
// Add an empty option if the widget needs one.
if ($empty_option = $this->getEmptyOption()) {
@@ -134,9 +140,10 @@ abstract class OptionsWidgetBase extends WidgetBase {
$options = array('_none' => $label) + $options;
}
+ $module_handler = \Drupal::moduleHandler();
$context = array(
'fieldDefinition' => $this->fieldDefinition,
- 'entity' => $this->entity,
+ 'entity' => $item->getParent()->getParent(),
);
$module_handler->alter('options_list', $options, $context);
@@ -158,13 +165,15 @@ abstract class OptionsWidgetBase extends WidgetBase {
*
* @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($items[$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..26fcbaf 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 @@ class SelectWidget extends OptionsWidgetBase {
$element += array(
'#type' => 'select',
- '#options' => $this->getOptions(),
- '#default_value' => $this->getSelectedOptions($items),
+ '#options' => $this->getOptions($items[$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/lib/Drupal/options/Tests/OptionsFieldTest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldTest.php
index 218e413..2a8c1a6 100644
--- a/core/modules/options/lib/Drupal/options/Tests/OptionsFieldTest.php
+++ b/core/modules/options/lib/Drupal/options/Tests/OptionsFieldTest.php
@@ -70,8 +70,11 @@ class OptionsFieldTest extends OptionsFieldUnitTestBase {
$this->assertTrue(empty($form[$this->fieldName][$langcode][3]), 'Option 3 does not exist');
// Completely new options appear.
- $this->field['settings']['allowed_values'] = array(10 => 'Update', 20 => 'Twenty');
+ $this->field->settings['allowed_values'] = array(10 => 'Update', 20 => 'Twenty');
$this->field->save();
+ // The entity holds an outdated field object with the old allowed values
+ // setting, so we need to reintialize the entity object.
+ $entity = entity_create('entity_test', array());
$form = \Drupal::entityManager()->getForm($entity);
$this->assertTrue(empty($form[$this->fieldName][$langcode][1]), 'Option 1 does not exist');
$this->assertTrue(empty($form[$this->fieldName][$langcode][2]), 'Option 2 does not exist');
diff --git a/core/modules/options/options.api.php b/core/modules/options/options.api.php
index 5e8c694..8bbf279 100644
--- a/core/modules/options/options.api.php
+++ b/core/modules/options/options.api.php
@@ -6,72 +6,13 @@
*/
/**
- * 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.
*
* @param array $options
- * The array of options for the field, as returned by hook_options_list(). An
+ * The array of options for the field, as returned by
+ * \Drupal\Core\TypedData\AllowedValuesInterface::getSettableOptions(). An
* empty option (_none) might have been added, depending on the field
* properties.
*
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 @@ abstract class NormalizerTestBase extends DrupalUnitTestBase {
*
* @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 297b00c..12de782 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -377,7 +377,7 @@ class EntityFieldTest extends EntityUnitTestBase {
$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
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 b3b25bb..29547a8 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 @@ abstract class EntityUnitTestBase extends DrupalUnitTestBase {
*
* @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 c8eb03d..eab31a8c 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 @@ class EntityValidationTest extends EntityUnitTestBase {
$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();
@@ -139,6 +138,18 @@ class EntityValidationTest extends EntityUnitTestBase {
$violations = $test_entity->validate();
$this->assertEqual($violations->count(), 1, 'Validation failed.');
$this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => 'user', '%id' => 9999)));
+
+ $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 911ee94..417d354 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 @@ abstract class TextItemBase extends ConfigFieldItemBase implements PrepareCacheI
'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..bcddb6d 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\field\Annotation\FieldWidget;
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,16 @@ class TextareaWidget extends WidgetBase {
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 @@ class TextareaWithSummaryWidget extends TextareaWidget {
* {@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\field\Annotation\FieldWidget;
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 @@ class TextfieldWidget extends WidgetBase {
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 3b19cb2..2971cb1 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.