diff --git a/core/includes/config.inc b/core/includes/config.inc index 5e51f65..68aca58 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -186,6 +186,10 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf * elements when there's no other metadata for them. This will also imply * that the type definition's 'list' property for the parent element will be * set to TRUE. + * - '.type', the element definition's type. This is a shorthand for setting + * the 'type' key in the '.definition' array. + * - '.label', the element definition's label. This is a shorthand for setting + * the 'label' key in the '.definition' array. * * For example, for the config file core/modules/system/config/system.site.yml: * @code diff --git a/core/lib/Drupal/Core/Config/Metadata/ElementBase.php b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php index b5bc6af..12e0900 100644 --- a/core/lib/Drupal/Core/Config/Metadata/ElementBase.php +++ b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php @@ -81,14 +81,21 @@ protected function getElement($key) { protected function getElementMetadata($key) { // Metadata for nested elements must be built before. if (isset($this->metadata[$key])) { - return $this->metadata[$key]; + $metadata = $this->metadata[$key]; } elseif (isset($this->metadata['.list'])) { - return $this->metadata['.list']; + $metadata = $this->metadata['.list']; } else { - return array(); + $metadata = array(); } + if ($metadata) { + $data = isset($this->value[$key]) && is_array($this->value[$key]) ? $this->value[$key] : array(); + $data['%parent'] = $this->value; + $data['%key'] = $key; + $metadata = $this->buildMetadata($data, $metadata); + } + return $metadata; } /** @@ -141,11 +148,9 @@ public function getMetadata() { * Implements Drupal\Core\Config\Metadata\ElementInterface::setMetadata(). */ public function setMetadata($metadata) { - // Process '.include' directive if present. - $this->metadata = $this->processInclude($metadata); + $this->metadata = $metadata; } - /** * Implements Drupal\Core\TypedData\TypedDataInterface::validate(). */ @@ -262,7 +267,6 @@ public static function buildElement($value, $metadata, $key, $parent = NULL) { * * @param mixed $data * The configuration data for the element. - * * @param array $metadata * The raw metadata array for the element. * @@ -288,11 +292,33 @@ public static function buildDefinition($data, $metadata) { } /** + * Prepares metadata using actual configuration data. + * + * @param mixed $data + * The configuration data for the element. + * @param array $metadata + * The raw metadata array for the element. + * + * @return array + * The preprocessed metadata array. + */ + public static function buildMetadata($data, $metadata) { + $metadata = self::processInclude($data, $metadata); + // Process shorthand '.type', '.label' + foreach (array('type', 'label') as $key) { + if (isset($metadata['.' . $key])) { + $metadata['.definition'][$key] = $metadata['.' . $key]; + unset($metadata['.' . $key]); + } + } + return $metadata; + } + + /** * Processes '.include' keyword applying variable replacements. * - * The include name may contain one or more variables to be replaced. These - * are enclosed in square brackets like '[name]' and follow the replacement - * rules defined by the replaceVariable() method. + * The include name may contain variables referencing configuration values. + * They will be replaced as defined in the replaceName() method. * * This method is called recursively in case the included metadata has also * an '.include' keyword. @@ -300,25 +326,48 @@ public static function buildDefinition($data, $metadata) { * @param array $metadata * Metadata array for the element. */ - protected function processInclude($metadata) { + protected static function processInclude($data, $metadata) { if (isset($metadata['.include'])) { // Parse the include string for variable names in squae brackets. - $self = $this; - $name = preg_replace_callback( - "/\[(.*)\]/", - function($matches) use ($self) {return $self->replaceVariable($matches[1]);}, - $metadata['.include'] - ); + $name = self::replaceName($metadata['.include'], $data); if ($include = config_metadata($name)) { // There may be nested '.include' directives, we need to process them too. - $include = $this->processInclude($include); + $include = self::processInclude($data, $include); $metadata = NestedArray::mergeDeep($include, $metadata); } + unset($metadata['.include']); } return $metadata; } /** + * Replaces variables in configuration name. + * + * The configuration name may contain one or more variables to be replaced, + * enclosed in square brackets like '[name]' and will follow the replacement + * rules defined by the replaceVariable() method. + * + * @param string $name + * Configuration name with variables in square brackets. + * @param mixed $data + * Configuration data for the element. + * @return string + * Configuration name with variables replaced. + */ + protected static function replaceName($name, $data) { + if (preg_match_all("/\[(.*)\]/U", $name, $matches)) { + // Build our list of '[value]' => replacement. + foreach (array_combine($matches[0], $matches[1]) as $key => $value) { + $replace[$key] = self::replaceVariable($value, $data); + } + return strtr($name, $replace); + } + else { + return $name; + } + } + + /** * Replaces variable values in included names with configuration data. * * Variable values are nested configuration keys that will be replaced by @@ -334,7 +383,6 @@ function($matches) use ($self) {return $self->replaceVariable($matches[1]);}, * - 'name.subkey', indicates a nested value of the current element. * - '%parent.name', will be replaced by the 'name' value of the parent. * - '%parent.%key', will be replaced by the parent element's key. - * - '%parent.%parent.name', will be replaced by the parent's parent 'name'. * * @param string $value * Variable value to be replaced. @@ -342,51 +390,23 @@ function($matches) use ($self) {return $self->replaceVariable($matches[1]);}, * @return string * The replaced value if a replacement found or the original value if not. */ - public function replaceVariable($value) { + protected static function replaceVariable($value, $data) { $parts = explode('.', $value); - $element = $this; - $data = $this->getValue(); // Process each value part, one at a time. while ($name = array_shift($parts)) { - switch ($name) { - case '%key': - // Element's key, it should be a final property. - if (!$parts) { - return $element->getName(); - } - else { - // If '%key' is not a final property, return original value. - return $value; - } - break; - case '%parent': - // Property from the parent element. - $element = $element->getParent(); - if ($element && $element instanceof ElementIterface) { - $data = $element->getValue(); - } - else { - // No parent or not the right type, return original value. - return $value; - } - break; - default: - // This should be a configuration key. - if (is_array($data) && isset($data[$name])) { - $data = $data[$name]; - // If no more parts left, this is the final property. - if (!$parts) { - return (string)$data; - } - } - else { - // Non existent key, return original value - return $value; - } - break; + if (!is_array($data) || !isset($data[$name])) { + // Key not found, return original value + return $value; + } + elseif (!$parts) { + // If no more parts left, this is the final property. + return (string)$data[$name]; + } + else { + // Get nested value and continue processing. + $data = $data[$name]; } } - // If no return value found, return the original value. - return $value; } + } diff --git a/core/lib/Drupal/Core/Config/Metadata/TypedConfig.php b/core/lib/Drupal/Core/Config/Metadata/TypedConfig.php index 1964ee1..42288cf 100644 --- a/core/lib/Drupal/Core/Config/Metadata/TypedConfig.php +++ b/core/lib/Drupal/Core/Config/Metadata/TypedConfig.php @@ -125,7 +125,8 @@ public function getLanguage() { */ protected function getBase() { if (!isset($this->base)) { - $this->base = ElementBase::buildElement($this->data, config_metadata($this->name), ''); + $metadata = ElementBase::buildMetadata($this->data, config_metadata($this->name)); + $this->base = ElementBase::buildElement($this->data, $metadata, ''); } return $this->base; } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php index 6f358b7..683fe32 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php @@ -49,7 +49,6 @@ function testBasicMetadata() { // More complex case, fallback to parent name. $metadata = config_metadata('image.effect.image_scale'); $expected = array(); - $expected['.include'] = 'image.effect.image_resize'; $expected['width']['.definition'] = array( 'label' => 'Width', 'type' => 'integer', @@ -67,7 +66,6 @@ function testBasicMetadata() { // Most complex case, metadata from actual configuration element using includes. $metadata = config_wrapper('image.style.medium')->get('effects.bddf0d06-42f9-4c75-a700-a33cafa25ea0.data')->getMetadata(); // Build with the previous case expected metadata - $expected['.include'] = 'image.effect.[%parent.name]'; $expected['.definition']['label'] = 'Data'; $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for the first effect of image.style.medium'); } diff --git a/core/modules/image/meta/image.effect.image_crop.yml b/core/modules/image/meta/image.effect.image_crop.yml index f03df09..62cdb6b 100644 --- a/core/modules/image/meta/image.effect.image_crop.yml +++ b/core/modules/image/meta/image.effect.image_crop.yml @@ -1,4 +1,11 @@ -.include: image.effect.image_resize +width: + .definition: + label: 'Width' + type: integer +height: + .definition: + label: 'Height' + type: integer anchor: .definition: label: 'Anchor' diff --git a/core/modules/image/meta/image.effect.image_scale.yml b/core/modules/image/meta/image.effect.image_scale.yml index e75b418..2e126a7 100644 --- a/core/modules/image/meta/image.effect.image_scale.yml +++ b/core/modules/image/meta/image.effect.image_scale.yml @@ -1,4 +1,11 @@ -.include: 'image.effect.image_resize' +width: + .definition: + label: 'Width' + type: integer +height: + .definition: + label: 'Height' + type: integer upscale: .definition: label: 'Upscale' diff --git a/core/modules/image/meta/image.effect.image_scale_and_crop.yml b/core/modules/image/meta/image.effect.image_scale_and_crop.yml index fd39bee..9d68822 100644 --- a/core/modules/image/meta/image.effect.image_scale_and_crop.yml +++ b/core/modules/image/meta/image.effect.image_scale_and_crop.yml @@ -1 +1,8 @@ -.include: image.effect.image_resize \ No newline at end of file +width: + .definition: + label: 'Width' + type: integer +height: + .definition: + label: 'Height' + type: integer \ No newline at end of file diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php index 77f138d..5ba78b8 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php @@ -89,7 +89,7 @@ function testConfigTranslation() { // Get non strict translation and check we've got all properties. $translation = $wrapper->getTranslation($langcode, FALSE); $properties = $translation->get()->getProperties(); - $this->assertTrue(count($properties) == 4 && count($wrapper->get('page')) == 3, 'Got the right number of properties with non strict translation'); + $this->assertTrue(count($properties) == 5 && count($wrapper->get('page')) == 3, 'Got the right number of properties with non strict translation'); $this->assertEqual($properties['name']->getValue(), $site_name, 'Got the right translation for site name with non strict translation'); // Check the translated site name is displayed.