diff --git a/core/lib/Drupal/Core/Config/Metadata/ElementBase.php b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php index 3f8e68d..b5bc6af 100644 --- a/core/lib/Drupal/Core/Config/Metadata/ElementBase.php +++ b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php @@ -142,24 +142,10 @@ public function getMetadata() { */ public function setMetadata($metadata) { // Process '.include' directive if present. - if (isset($metadata['.include'])) { - $replace = array(); - // If this element has a key (is not the parent), add it to the variables - // for replacement under '[%key]' - if (isset($this->key)) { - $replace['[%key]'] = $this->key; - } - // Get other variable replacements from the data, only scalar values. - if (is_array($this->value)) { - foreach (array_filter($this->value, 'is_scalar') as $key => $value) { - $replace['[' . $key . ']'] = $value; - } - } - $metadata = $this->processInclude($metadata, $replace); - } - $this->metadata = $metadata; + $this->metadata = $this->processInclude($metadata); } + /** * Implements Drupal\Core\TypedData\TypedDataInterface::validate(). */ @@ -304,21 +290,103 @@ public static function buildDefinition($data, $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. + * * This method is called recursively in case the included metadata has also * an '.include' keyword. * * @param array $metadata * Metadata array for the element. - * @param array $replace - * Variable replacements for the include value. */ - protected function processInclude($metadata, $replace) { - if (isset($metadata['.include']) && $include = config_metadata(strtr($metadata['.include'], $replace))) { - // There may be nested '.include' directives, we need to process them too. - $include = $this->processInclude($include, $replace); - $metadata = NestedArray::mergeDeep($include, $metadata); + protected function processInclude($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'] + ); + if ($include = config_metadata($name)) { + // There may be nested '.include' directives, we need to process them too. + $include = $this->processInclude($include); + $metadata = NestedArray::mergeDeep($include, $metadata); + } } return $metadata; } + /** + * Replaces variable values in included names with configuration data. + * + * Variable values are nested configuration keys that will be replaced by + * their value or some of these special strings: + * - '%key', will be replaced by the element's key. + * - '%parent', to reference the parent element. + * + * There may be nested configuration keys separated by dots or more complex + * patterns like '%parent.name' which references the 'name' value of the + * parent element. + * + * Example patterns: + * - '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. + * + * @return string + * The replaced value if a replacement found or the original value if not. + */ + public function replaceVariable($value) { + $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 no return value found, return the original value. + return $value; + } } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php index 1807505..6f358b7 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php @@ -47,52 +47,28 @@ function testBasicMetadata() { ); $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for system.maintenance'); // More complex case, fallback to parent name. - $metadata = config_metadata('image.style.large'); + $metadata = config_metadata('image.effect.image_scale'); $expected = array(); - $expected['name']['.definition'] = array( - 'label' => 'Machine name', - ); - $expected['label']['.definition'] = array( - 'label' => 'Label', - 'type' => 'text' - ); - $expected['effects']['.definition'] = array( - 'label' => 'Style effects', - ); - $expected['effects']['.list']['.include'] = 'image.effect.[name]'; - $expected['effects']['.list']['weight']['.definition'] = array( - 'label' => 'Weight', - 'type' => 'integer' - ); - $expected['effects']['.list']['ieid']['.definition'] = array( - 'label' => 'IEID' - ); - $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for image.style.large'); - - // Most complex case, metadata from actual configuration element using includes. - $metadata = config_wrapper('image.style.medium')->get('effects.bddf0d06-42f9-4c75-a700-a33cafa25ea0')->getMetadata(); - $expected = array(); - $expected['.include'] = 'image.effect.[name]'; - $expected['.definition']['label'] = 'Image scale'; - $expected['data']['width']['.definition'] = array( + $expected['.include'] = 'image.effect.image_resize'; + $expected['width']['.definition'] = array( 'label' => 'Width', 'type' => 'integer', ); - $expected['data']['height']['.definition'] = array( + $expected['height']['.definition'] = array( 'label' => 'Height', 'type' => 'integer', ); - $expected['data']['upscale']['.definition'] = array( + $expected['upscale']['.definition'] = array( 'label' => 'Upscale', 'type' => 'boolean', ); - $expected['weight']['.definition'] = array( - 'label' => 'Weight', - 'type' => 'integer' - ); - $expected['ieid']['.definition'] = array( - 'label' => 'IEID' - ); + $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for image.style.large'); + + // 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'); } @@ -149,3 +125,4 @@ function testConfigMetadata() { } } + diff --git a/core/modules/image/meta/image.effect.%.yml b/core/modules/image/meta/image.effect.%.yml deleted file mode 100644 index cc6bddc..0000000 --- a/core/modules/image/meta/image.effect.%.yml +++ /dev/null @@ -1,8 +0,0 @@ -.definition: - label: 'Image effect' -name: - .definition: - label: 'Machine name' -data: - .definition: - label: 'Data' diff --git a/core/modules/image/meta/image.effect.image_crop.yml b/core/modules/image/meta/image.effect.image_crop.yml new file mode 100644 index 0000000..f03df09 --- /dev/null +++ b/core/modules/image/meta/image.effect.image_crop.yml @@ -0,0 +1,4 @@ +.include: image.effect.image_resize +anchor: + .definition: + label: 'Anchor' diff --git a/core/modules/image/meta/image.effect.image_resize.yml b/core/modules/image/meta/image.effect.image_resize.yml new file mode 100644 index 0000000..ad647b4 --- /dev/null +++ b/core/modules/image/meta/image.effect.image_resize.yml @@ -0,0 +1,8 @@ +width: + .definition: + label: 'Width' + type: integer +height: + .definition: + label: 'Height' + type: integer diff --git a/core/modules/image/meta/image.effect.image_rotate.yml b/core/modules/image/meta/image.effect.image_rotate.yml new file mode 100644 index 0000000..040f38c --- /dev/null +++ b/core/modules/image/meta/image.effect.image_rotate.yml @@ -0,0 +1,10 @@ +degrees: + .definition: + label: 'Rotation angle' +bgcolor: + .definition: + label: 'Background color' +random: + .definition: + label: 'Randomize' + type: boolean diff --git a/core/modules/image/meta/image.effect.image_scale.yml b/core/modules/image/meta/image.effect.image_scale.yml index c17c55d..e75b418 100644 --- a/core/modules/image/meta/image.effect.image_scale.yml +++ b/core/modules/image/meta/image.effect.image_scale.yml @@ -1,16 +1,5 @@ -.include: image.effect.%.yml -.definition: - label: 'Image scale' -data: - width: - .definition: - label: 'Width' - type: integer - height: - .definition: - label: 'Height' - type: integer - upscale: - .definition: - label: 'Upscale' - type: boolean +.include: 'image.effect.image_resize' +upscale: + .definition: + label: 'Upscale' + type: boolean 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 new file mode 100644 index 0000000..fd39bee --- /dev/null +++ b/core/modules/image/meta/image.effect.image_scale_and_crop.yml @@ -0,0 +1 @@ +.include: image.effect.image_resize \ No newline at end of file diff --git a/core/modules/image/meta/image.style.%.yml b/core/modules/image/meta/image.style.%.yml index 31852b8..3dcd4d7 100644 --- a/core/modules/image/meta/image.style.%.yml +++ b/core/modules/image/meta/image.style.%.yml @@ -9,11 +9,20 @@ effects: .definition: label: 'Style effects' .list: - .include: 'image.effect.[name]' + .definition: + label: 'Image effect' weight: .definition: label: 'Weight' type: integer ieid: .definition: - label: 'IEID' \ No newline at end of file + label: 'IEID' + name: + .definition: + label: 'Machine name' + data: + .include: 'image.effect.[%parent.name]' + .definition: + label: 'Data' + \ No newline at end of file