From 9b32d95bfdfefb4c5092684cbc05e1deef9898b9 Mon Sep 17 00:00:00 2001 From: Alex Pott Date: Tue, 24 Jun 2014 13:22:32 +0100 Subject: [PATCH] Issue #2277945 by Jose Reyero: Fixed Typed configuration implements TypedDataInterface improperly. --- core/config/schema/core.data_types.schema.yml | 3 +- .../lib/Drupal/Core/Config/Schema/Element.php | 9 +++ .../lib/Drupal/Core/Config/Schema/Mapping.php | 29 +++++--- .../Core/Config/Schema/SchemaCheckTrait.php | 3 +- .../Drupal/Core/Config/Schema/Sequence.php | 22 +++++- .../Drupal/Core/Config/StorableConfigBase.php | 3 +- .../Drupal/Core/Config/TypedConfigManager.php | 73 ++++++------------- .../Config/TypedConfigManagerInterface.php | 32 ++++++-- .../config/src/Tests/ConfigSchemaTest.php | 40 ++++++++-- .../src/ConfigMapperManager.php | 7 ++ .../src/Form/ConfigTranslationFormBase.php | 9 ++- .../src/FormElement/DateFormat.php | 3 +- .../src/FormElement/ElementInterface.php | 6 +- .../src/FormElement/Textarea.php | 3 +- .../src/FormElement/Textfield.php | 3 +- .../tests/src/ConfigMapperManagerTest.php | 5 +- .../locale/src/LocaleConfigManager.php | 3 +- core/modules/locale/src/LocaleTypedConfig.php | 3 +- 18 files changed, 169 insertions(+), 87 deletions(-) diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml index 7c0b05d362f..cc50e0facbc 100644 --- a/core/config/schema/core.data_types.schema.yml +++ b/core/config/schema/core.data_types.schema.yml @@ -42,9 +42,11 @@ uri: mapping: label: Mapping class: '\Drupal\Core\Config\Schema\Mapping' + definition_class: '\Drupal\Core\TypedData\MapDataDefinition' sequence: label: Sequence class: '\Drupal\Core\Config\Schema\Sequence' + definition_class: '\Drupal\Core\TypedData\ListDataDefinition' # Simple extended data types: @@ -110,7 +112,6 @@ filter: settings: type: filter_settings.[%parent.id] - # System action configuration base. action_configuration_default: type: sequence diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php index 2563ec5afb9..c492d42d73b 100644 --- a/core/lib/Drupal/Core/Config/Schema/Element.php +++ b/core/lib/Drupal/Core/Config/Schema/Element.php @@ -28,4 +28,13 @@ abstract class Element extends TypedData { return \Drupal::service('config.typed')->create($definition, $data, $key, $this); } + /** + * Build data definition object for contained elements. + * + * @return \Drupal\Core\TypedData\DataDefinitionInterface + */ + protected function buildDataDefinition($definition, $value, $key) { + return \Drupal::service('config.typed')->buildDataDefinition($definition, $value, $key, $this); + } + } diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php index 19fb596647d..57293900c2d 100644 --- a/core/lib/Drupal/Core/Config/Schema/Mapping.php +++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php @@ -19,12 +19,21 @@ use Drupal\Component\Utility\String; */ class Mapping extends ArrayElement implements ComplexDataInterface { + /** + * An array of data definitions. + * + * @var \Drupal\Core\TypedData\DataDefinitionInterface[] + */ + protected $propertyDefinitions; + /** * Overrides ArrayElement::parse() + * + * Note this only returns elements that have a data definition. */ protected function parse() { $elements = array(); - foreach ($this->definition['mapping'] as $key => $definition) { + foreach ($this->getPropertyDefinitions() as $key => $definition) { if (isset($this->value[$key]) || array_key_exists($key, $this->value)) { $elements[$key] = $this->parseElement($key, $this->value[$key], $definition); } @@ -103,13 +112,12 @@ class Mapping extends ArrayElement implements ComplexDataInterface { * @param string $name * The name of property. * - * @return array|null + * @return \Drupal\Core\TypedData\DataDefinitionInterface|null * The definition of the property or NULL if the property does not exist. */ public function getPropertyDefinition($name) { - if (isset($this->definition['mapping'][$name])) { - return $this->definition['mapping'][$name]; - } + $definitions = $this->getPropertyDefinitions(); + return isset($definitions[$name]) ? isset($definitions[$name]) : NULL; } /** @@ -120,11 +128,14 @@ class Mapping extends ArrayElement implements ComplexDataInterface { * property name. */ public function getPropertyDefinitions() { - $list = array(); - foreach ($this->getAllKeys() as $key) { - $list[$key] = $this->getPropertyDefinition($key); + if (!isset($this->propertyDefinitions)) { + $this->propertyDefinitions = array(); + foreach ($this->definition['mapping'] as $key => $definition) { + $value = isset($this->value[$key]) ? $this->value[$key] : NULL; + $this->propertyDefinitions[$key] = $this->buildDataDefinition($definition, $value, $key); + } } - return $list; + return $this->propertyDefinitions; } /** diff --git a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php index b43c6a7a494..4348316a964 100644 --- a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php +++ b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php @@ -53,7 +53,8 @@ trait SchemaCheckTrait { return FALSE; } $definition = $typed_config->getDefinition($config_name); - $this->schema = $typed_config->create($definition, $config_data); + $data_definition = $typed_config->buildDataDefinition($definition, $config_data); + $this->schema = $typed_config->create($data_definition, $config_data); $errors = array(); foreach ($config_data as $key => $value) { $errors = array_merge($errors, $this->checkValue($key, $value)); diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php index 720b8500917..e5b6a356ed3 100644 --- a/core/lib/Drupal/Core/Config/Schema/Sequence.php +++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php @@ -14,14 +14,26 @@ use Drupal\Core\TypedData\ListInterface; */ class Sequence extends ArrayElement implements ListInterface { + /** + * Data definition + * + * @var \Drupal\Core\TypedData\DataDefinitionInterface + */ + protected $itemDefinition; + /** * Overrides ArrayElement::parse() */ protected function parse() { - $definition = $this->getItemDefinition(); + // Creates a new data definition object for each item from the generic type + // definition array and actual configuration data for that item. Type + // definitions may contain variables to be replaced and those depend on + // each item's data. + $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array(); $elements = array(); foreach ($this->value as $key => $value) { - $elements[$key] = $this->parseElement($key, $value, $definition); + $data_definition = $this->buildDataDefinition($definition, $value, $key); + $elements[$key] = $this->parseElement($key, $value, $data_definition); } return $elements; } @@ -37,7 +49,11 @@ class Sequence extends ArrayElement implements ListInterface { * Implements Drupal\Core\TypedData\ListInterface::getItemDefinition(). */ public function getItemDefinition() { - return $this->definition['sequence'][0]; + if (!isset($this->itemDefinition)) { + $definition = isset($this->definition['sequence'][0]) ? $this->definition['sequence'][0] : array(); + $this->itemDefinition = $this->buildDataDefinition($definition, NULL); + } + return $this->itemDefinition; } /** diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php index 60a1d023e29..b2610f73156 100644 --- a/core/lib/Drupal/Core/Config/StorableConfigBase.php +++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php @@ -128,7 +128,8 @@ abstract class StorableConfigBase extends ConfigBase { protected function getSchemaWrapper() { if (!isset($this->schemaWrapper)) { $definition = $this->typedConfigManager->getDefinition($this->name); - $this->schemaWrapper = $this->typedConfigManager->create($definition, $this->data); + $data_definition = $this->typedConfigManager->buildDataDefinition($definition, $this->data); + $this->schemaWrapper = $this->typedConfigManager->create($data_definition, $this->data); } return $this->schemaWrapper; } diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php index 2032f4c1a6e..ba3f3530df2 100644 --- a/core/lib/Drupal/Core/Config/TypedConfigManager.php +++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php @@ -7,16 +7,14 @@ namespace Drupal\Core\Config; -use Drupal\Component\Plugin\Exception\PluginException; -use Drupal\Component\Plugin\PluginManagerBase; use Drupal\Component\Utility\NestedArray; -use Drupal\Component\Utility\String; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\TypedData\TypedDataManager; /** * Manages config type plugins. */ -class TypedConfigManager extends PluginManagerBase implements TypedConfigManagerInterface { +class TypedConfigManager extends TypedDataManager implements TypedConfigManagerInterface { /** * The cache ID for the definitions. @@ -80,19 +78,19 @@ class TypedConfigManager extends PluginManagerBase implements TypedConfigManager */ public function get($name) { $data = $this->configStorage->read($name); - $definition = $this->getDefinition($name); - return $this->create($definition, $data); + $type_definition = $this->getDefinition($name); + $data_definition = $this->buildDataDefinition($type_definition, $data); + return $this->create($data_definition, $data); } /** * {@inheritdoc} */ - public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) { - if (!isset($definition['type'])) { - // By default elements without a type are undefined. - $definition['type'] = 'undefined'; - } - elseif (strpos($definition['type'], ']')) { + public function buildDataDefinition(array $definition, $value, $name = NULL, $parent = NULL) { + // Add default values for data type and replace variables. + $definition += array('type' => 'undefined'); + + if (strpos($definition['type'], ']')) { // Replace variable names in definition. $replace = is_array($value) ? $value : array(); if (isset($parent)) { @@ -103,45 +101,18 @@ class TypedConfigManager extends PluginManagerBase implements TypedConfigManager } $definition['type'] = $this->replaceName($definition['type'], $replace); } - // Create typed config object. - $wrapper = $this->createInstance($definition['type'], array( - 'data_definition' => $definition, - 'name' => $name, - 'parent' => $parent, - )); - if (isset($value)) { - $wrapper->setValue($value, FALSE); - } - return $wrapper; - } + // Add default values from type definition. + $definition += $this->getDefinition($definition['type']); - /** - * {@inheritdoc} - */ - public function createInstance($data_type, array $configuration = array()) { - $data_definition = $configuration['data_definition']; - $type_definition = $this->getDefinition($data_type); + $data_definition = $this->createDataDefinition($definition['type']); - if (!isset($type_definition)) { - throw new \InvalidArgumentException(String::format('Invalid data type %plugin_id has been given.', array('%plugin_id' => $data_type))); + // Pass remaining values from definition array to data definition. + foreach ($definition as $key => $value) { + if (!isset($data_definition[$key])) { + $data_definition[$key] = $value; + } } - - // Allow per-data definition overrides of the used classes, i.e. take over - // classes specified in the type definition. - $data_definition += $type_definition; - - $key = empty($data_definition['list']) ? 'class' : 'list class'; - if (isset($data_definition[$key])) { - $class = $data_definition[$key]; - } - elseif (isset($type_definition[$key])) { - $class = $type_definition[$key]; - } - - if (!isset($class)) { - throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $data_type)); - } - return new $class($data_definition, $configuration['name'], $configuration['parent']); + return $data_definition; } /** @@ -169,7 +140,11 @@ class TypedConfigManager extends PluginManagerBase implements TypedConfigManager unset($definition['type']); $this->definitions[$type] = $definition; } - return $definition; + // Add type and default definition class. + return $definition + array( + 'definition_class' => '\Drupal\Core\TypedData\DataDefinition', + 'type' => $type, + ); } /** diff --git a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php index 827363fd8f5..f475c02810d 100644 --- a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php +++ b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php @@ -9,6 +9,7 @@ namespace Drupal\Core\Config; use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; use Drupal\Component\Plugin\PluginManagerInterface; +use Drupal\Core\TypedData\DataDefinitionInterface; /** * Defines an interface for typed configuration manager. @@ -36,7 +37,8 @@ Interface TypedConfigManagerInterface extends PluginManagerInterface, CachedDisc * instantiated. * @param array $configuration * The plugin configuration array, i.e. an array with the following keys: - * - data definition: The data definition array. + * - data definition: The data definition object, i.e. an instance of + * \Drupal\Core\TypedData\DataDefinitionInterface. * - name: (optional) If a property or list item is to be created, the name * of the property or the delta of the list item. * - parent: (optional) If a property or list item is to be created, the @@ -51,10 +53,10 @@ Interface TypedConfigManagerInterface extends PluginManagerInterface, CachedDisc /** * Creates a new typed configuration object instance. * - * @param array $definition - * The data definition of the typed data object + * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition + * The data definition of the typed data object. * @param mixed $value - * (optional) The data value. If set, it has to match one of the supported + * The data value. If set, it has to match one of the supported * data type format as documented for the data type classes. * @param string $name * (optional) If a property or list item is to be created, the name of the @@ -67,7 +69,27 @@ Interface TypedConfigManagerInterface extends PluginManagerInterface, CachedDisc * @return \Drupal\Core\Config\Schema\Element * The instantiated typed data object. */ - public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL); + public function create(DataDefinitionInterface $definition, $value, $name = NULL, $parent = NULL); + + /** + * Creates a new data definition object from a type definition array and + * actual configuration data. Since type definitions may contain variables + * to be replaced, we need the configuration value to create it. + * + * @param array $definition + * The base type definition array, for which a data definition should be + * created. + * @param $value + * Optional value of the configuraiton element. + * @param string $name + * Optional name of the configuration element. + * @param object $parent + * Optional parent element. + * + * @return \Drupal\Core\TypedData\DataDefinitionInterface + * A data definition for the given data type. + */ + public function buildDataDefinition(array $definition, $value, $name = NULL, $parent = NULL); /** * Checks if the configuration schema with the given config name exists. diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php index ca5eabaaa80..375280001a5 100644 --- a/core/modules/config/src/Tests/ConfigSchemaTest.php +++ b/core/modules/config/src/Tests/ConfigSchemaTest.php @@ -48,6 +48,8 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected = array(); $expected['label'] = 'Undefined'; $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; + $expected['type'] = 'undefined'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; $this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.'); // Configuration file without schema will return Undefined as well. @@ -63,21 +65,25 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; $expected['mapping']['testitem'] = array('label' => 'Test item'); $expected['mapping']['testlist'] = array('label' => 'Test list'); + $expected['type'] = 'config_schema_test.someschema'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.'); // Check type detection on elements with undefined types. $config = \Drupal::service('config.typed')->get('config_schema_test.someschema'); - $definition = $config['testitem']->getDataDefinition(); + $definition = $config['testitem']->getDataDefinition()->toArray(); $expected = array(); $expected['label'] = 'Test item'; $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; $expected['type'] = 'undefined'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; $this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.'); - $definition = $config['testlist']->getDataDefinition(); + $definition = $config['testlist']->getDataDefinition()->toArray(); $expected = array(); $expected['label'] = 'Test list'; $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; $expected['type'] = 'undefined'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; $this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.'); // Simple case, straight metadata. @@ -93,6 +99,8 @@ class ConfigSchemaTest extends DrupalUnitTestBase { 'label' => 'Default language', 'type' => 'string', ); + $expected['type'] = 'system.maintenance'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; $this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance'); // Mixed schema with ignore elements. @@ -100,6 +108,7 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected = array(); $expected['label'] = 'Ignore test'; $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; $expected['mapping']['label'] = array( 'label' => 'Label', 'type' => 'label', @@ -116,16 +125,19 @@ class ConfigSchemaTest extends DrupalUnitTestBase { 'label' => 'Weight', 'type' => 'integer', ); + $expected['type'] = 'config_schema_test.ignore'; + $this->assertEqual($definition, $expected); // The ignore elements themselves. - $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('irrelevant')->getDataDefinition(); + $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('irrelevant')->getDataDefinition()->toArray(); $expected = array(); $expected['type'] = 'ignore'; $expected['label'] = 'Irrelevant'; $expected['class'] = '\Drupal\Core\Config\Schema\Ignore'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; $this->assertEqual($definition, $expected); - $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('indescribable')->getDataDefinition(); + $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('indescribable')->getDataDefinition()->toArray(); $expected['label'] = 'Indescribable'; $this->assertEqual($definition, $expected); @@ -134,6 +146,7 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected = array(); $expected['label'] = 'Image style'; $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; $expected['mapping']['name']['type'] = 'string'; $expected['mapping']['uuid']['type'] = 'string'; $expected['mapping']['uuid']['label'] = 'UUID'; @@ -152,6 +165,7 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected['mapping']['effects']['sequence'][0]['mapping']['data']['type'] = 'image.effect.[%parent.id]'; $expected['mapping']['effects']['sequence'][0]['mapping']['weight']['type'] = 'integer'; $expected['mapping']['effects']['sequence'][0]['mapping']['uuid']['type'] = 'string'; + $expected['type'] = 'image.style.*'; $this->assertEqual($definition, $expected); @@ -161,18 +175,21 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected = array(); $expected['label'] = 'Image scale'; $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; $expected['mapping']['width']['type'] = 'integer'; $expected['mapping']['width']['label'] = 'Width'; $expected['mapping']['height']['type'] = 'integer'; $expected['mapping']['height']['label'] = 'Height'; $expected['mapping']['upscale']['type'] = 'boolean'; $expected['mapping']['upscale']['label'] = 'Upscale'; + $expected['type'] = 'image.effect.image_scale'; + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale'); // Most complex case, get metadata for actual configuration element. $effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects'); - $definition = $effects['bddf0d06-42f9-4c75-a700-a33cafa25ea0']['data']->getDataDefinition(); + $definition = $effects['bddf0d06-42f9-4c75-a700-a33cafa25ea0']['data']->getDataDefinition()->toArray(); // This should be the schema for image.effect.image_scale, reuse previous one. $expected['type'] = 'image.effect.image_scale'; @@ -188,6 +205,8 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected['mapping']['testid']['label'] = 'ID'; $expected['mapping']['testdescription']['type'] = 'text'; $expected['mapping']['testdescription']['label'] = 'Description'; + $expected['type'] = 'config_schema_test.someschema.somemodule.*.*'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection'); @@ -204,31 +223,34 @@ class ConfigSchemaTest extends DrupalUnitTestBase { // Test fetching parent one level up. $entry = $config_data->get('one_level'); - $definition = $entry['testitem']->getDataDefinition(); + $definition = $entry['testitem']->getDataDefinition()->toArray(); $expected = array( 'type' => 'config_schema_test.someschema.with_parents.key_1', 'label' => 'Test item nested one level', 'class' => '\Drupal\Core\TypedData\Plugin\DataType\String', + 'definition_class' => '\Drupal\Core\TypedData\DataDefinition', ); $this->assertEqual($definition, $expected); // Test fetching parent two levels up. $entry = $config_data->get('two_levels'); - $definition = $entry['wrapper']['testitem']->getDataDefinition(); + $definition = $entry['wrapper']['testitem']->getDataDefinition()->toArray(); $expected = array( 'type' => 'config_schema_test.someschema.with_parents.key_2', 'label' => 'Test item nested two levels', 'class' => '\Drupal\Core\TypedData\Plugin\DataType\String', + 'definition_class' => '\Drupal\Core\TypedData\DataDefinition', ); $this->assertEqual($definition, $expected); // Test fetching parent three levels up. $entry = $config_data->get('three_levels'); - $definition = $entry['wrapper_1']['wrapper_2']['testitem']->getDataDefinition(); + $definition = $entry['wrapper_1']['wrapper_2']['testitem']->getDataDefinition()->toArray(); $expected = array( 'type' => 'config_schema_test.someschema.with_parents.key_3', 'label' => 'Test item nested three levels', 'class' => '\Drupal\Core\TypedData\Plugin\DataType\String', + 'definition_class' => '\Drupal\Core\TypedData\DataDefinition', ); $this->assertEqual($definition, $expected); } @@ -359,10 +381,12 @@ class ConfigSchemaTest extends DrupalUnitTestBase { $expected = array(); $expected['label'] = 'Schema wildcard fallback test'; $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; $expected['mapping']['testid']['type'] = 'string'; $expected['mapping']['testid']['label'] = 'ID'; $expected['mapping']['testdescription']['type'] = 'text'; $expected['mapping']['testdescription']['label'] = 'Description'; + $expected['type'] = 'config_schema_test.wildcard_fallback.*'; $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something'); diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php index 2fb25682182..bb3ce658fa9 100644 --- a/core/modules/config_translation/src/ConfigMapperManager.php +++ b/core/modules/config_translation/src/ConfigMapperManager.php @@ -128,6 +128,13 @@ class ConfigMapperManager extends DefaultPluginManager implements ConfigMapperMa } } + /** + * {@inheritdoc} + */ + public function buildDataDefinition(array $definition, $value = NULL, $name = NULL, $parent = NULL) { + return $this->typedConfigManager->buildDataDefinition($definition, $value, $name, $parent); + } + /** * {@inheritdoc} */ diff --git a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php index a57b60b8979..5b6125ad9fc 100644 --- a/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php +++ b/core/modules/config_translation/src/Form/ConfigTranslationFormBase.php @@ -272,7 +272,10 @@ abstract class ConfigTranslationFormBase extends FormBase implements BaseFormIdI foreach ($schema as $key => $element) { // Make the specific element key, "$base_key.$key". $element_key = implode('.', array_filter(array($base_key, $key))); - $definition = $element->getDataDefinition() + array('label' => $this->t('N/A')); + $definition = $element->getDataDefinition(); + if (!$definition->getLabel()) { + $definition->setLabel($this->t('N/A')); + } if ($element instanceof Element) { // Build sub-structure and include it with a wrapper in the form // if there are any translatable elements there. @@ -336,7 +339,9 @@ abstract class ConfigTranslationFormBase extends FormBase implements BaseFormIdI '#type' => 'item', ); - $definition += array('form_element_class' => '\Drupal\config_translation\FormElement\Textfield'); + if (!isset($definition['form_element_class'])) { + $definition['form_element_class'] = '\Drupal\config_translation\FormElement\Textfield'; + } /** @var \Drupal\config_translation\FormElement\ElementInterface $form_element */ $form_element = new $definition['form_element_class'](); diff --git a/core/modules/config_translation/src/FormElement/DateFormat.php b/core/modules/config_translation/src/FormElement/DateFormat.php index d82d427350b..25858cf15cf 100644 --- a/core/modules/config_translation/src/FormElement/DateFormat.php +++ b/core/modules/config_translation/src/FormElement/DateFormat.php @@ -12,6 +12,7 @@ use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\TypedData\DataDefinitionInterface; /** * Defines the date format element for the configuration translation interface. @@ -22,7 +23,7 @@ class DateFormat implements ElementInterface { /** * {@inheritdoc} */ - public function getFormElement(array $definition, LanguageInterface $language, $value) { + public function getFormElement(DataDefinitionInterface $definition, LanguageInterface $language, $value) { $description = $this->t('A user-defined date format. See the PHP manual for available options.', array('@url' => 'http://php.net/manual/function.date.php')); $format = $this->t('Displayed as %date_format', array('%date_format' => \Drupal::service('date')->format(REQUEST_TIME, 'custom', $value))); return array( diff --git a/core/modules/config_translation/src/FormElement/ElementInterface.php b/core/modules/config_translation/src/FormElement/ElementInterface.php index ecc93f09155..418860f5675 100644 --- a/core/modules/config_translation/src/FormElement/ElementInterface.php +++ b/core/modules/config_translation/src/FormElement/ElementInterface.php @@ -8,6 +8,7 @@ namespace Drupal\config_translation\FormElement; use Drupal\Core\Language\LanguageInterface; +use Drupal\Core\TypedData\DataDefinitionInterface; /** * Provides an interface for configuration translation form elements. @@ -17,7 +18,7 @@ interface ElementInterface { /** * Returns the translation form element for a given configuration definition. * - * @param array $definition + * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition * Configuration schema type definition of the element. * @param \Drupal\Core\Language\LanguageInterface $language * Language object to display the translation form for. @@ -27,6 +28,7 @@ interface ElementInterface { * @return array * Form API array to represent the form element. */ - public function getFormElement(array $definition, LanguageInterface $language, $value); + public function getFormElement(DataDefinitionInterface $definition, LanguageInterface $language, $value); + } diff --git a/core/modules/config_translation/src/FormElement/Textarea.php b/core/modules/config_translation/src/FormElement/Textarea.php index 63aecf36be5..8092e4b7bce 100644 --- a/core/modules/config_translation/src/FormElement/Textarea.php +++ b/core/modules/config_translation/src/FormElement/Textarea.php @@ -9,6 +9,7 @@ namespace Drupal\config_translation\FormElement; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\TypedData\DataDefinitionInterface; /** * Defines the textarea element for the configuration translation interface. @@ -19,7 +20,7 @@ class Textarea implements ElementInterface { /** * {@inheritdoc} */ - public function getFormElement(array $definition, LanguageInterface $language, $value) { + public function getFormElement(DataDefinitionInterface $definition, LanguageInterface $language, $value) { // Estimate a comfortable size of the input textarea. $rows_words = ceil(str_word_count($value) / 5); $rows_newlines = substr_count($value, "\n" ) + 1; diff --git a/core/modules/config_translation/src/FormElement/Textfield.php b/core/modules/config_translation/src/FormElement/Textfield.php index 44105baec56..a4a820f1e06 100644 --- a/core/modules/config_translation/src/FormElement/Textfield.php +++ b/core/modules/config_translation/src/FormElement/Textfield.php @@ -9,6 +9,7 @@ namespace Drupal\config_translation\FormElement; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\TypedData\DataDefinitionInterface; /** * Defines the textfield element for the configuration translation interface. @@ -19,7 +20,7 @@ class Textfield implements ElementInterface { /** * {@inheritdoc} */ - public function getFormElement(array $definition, LanguageInterface $language, $value) { + public function getFormElement(DataDefinitionInterface $definition, LanguageInterface $language, $value) { return array( '#type' => 'textfield', '#default_value' => $value, diff --git a/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php b/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php index 4e29ecf1e2e..69be47616ab 100644 --- a/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php +++ b/core/modules/config_translation/tests/src/ConfigMapperManagerTest.php @@ -12,6 +12,8 @@ use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\Tests\UnitTestCase; +use Drupal\Core\TypedData\DataDefinition; +use Drupal\Core\TypedData\DataDefinitionInterface; /** * Tests ConfigMapperManager. @@ -162,10 +164,11 @@ class ConfigMapperManagerTest extends UnitTestCase { * The mocked schema element. */ protected function getElement(array $definition) { + $data_definition = new DataDefinition($definition); $element = $this->getMock('Drupal\Core\TypedData\TypedDataInterface'); $element->expects($this->any()) ->method('getDataDefinition') - ->will($this->returnValue($definition)); + ->will($this->returnValue($data_definition)); return $element; } diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php index 9e0f3de7cbd..5466558ea6b 100644 --- a/core/modules/locale/src/LocaleConfigManager.php +++ b/core/modules/locale/src/LocaleConfigManager.php @@ -96,9 +96,10 @@ class LocaleConfigManager extends TypedConfigManager { // We get only the data that didn't change from default. $data = $this->compareConfigData($default, $updated); $definition = $this->getDefinition($name); + $data_definition = $this->buildDataDefinition($definition, $data); // Unless the configuration has a explicit language code we assume English. $langcode = isset($default['langcode']) ? $default['langcode'] : 'en'; - $wrapper = new LocaleTypedConfig($definition, $name, $langcode, $this); + $wrapper = new LocaleTypedConfig($data_definition, $name, $langcode, $this); $wrapper->setValue($data); return $wrapper; } diff --git a/core/modules/locale/src/LocaleTypedConfig.php b/core/modules/locale/src/LocaleTypedConfig.php index dd24d9909cd..36e39a91fb8 100644 --- a/core/modules/locale/src/LocaleTypedConfig.php +++ b/core/modules/locale/src/LocaleTypedConfig.php @@ -8,6 +8,7 @@ namespace Drupal\locale; use Drupal\Core\TypedData\ContextAwareInterface; +use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\Core\Config\Schema\Element; use Drupal\Core\Config\Schema\ArrayElement; @@ -49,7 +50,7 @@ class LocaleTypedConfig extends Element { * @param \Drupal\locale\LocaleConfigManager $localeConfig; * The locale configuration manager object. */ - public function __construct($definition, $name, $langcode, LocaleConfigManager $localeConfig) { + public function __construct(DataDefinitionInterface $definition, $name, $langcode, LocaleConfigManager $localeConfig) { parent::__construct($definition, $name); $this->langcode = $langcode; $this->localeConfig = $localeConfig;