diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigElement.php b/core/lib/Drupal/Core/Config/Metadata/ConfigElement.php new file mode 100644 index 0000000..4f49415 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/ConfigElement.php @@ -0,0 +1,108 @@ +getElement($property_name)) { + return $property; + } + else { + throw new InvalidArgumentException(format_string("The configuration property @key doesn't exist.", array( + '@key' => $property_name, + ))); + } + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::set(). + */ + public function set($property_name, $value) { + // Set the data into the configuration array but behave according to the + // interface specification when we've got a null value. + if (isset($value)) { + $this->value[$property_name] = $value; + return $this->get($property_name); + } + else { + // In these objects, when clearing the value, the property is gone. + // As this needs to return a property, we get it before we delete it. + $property = $this->get($property_name); + unset($this->value[$property_name]); + $property->setValue($value); + return $property; + } + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::getProperties(). + */ + public function getProperties($include_computed = FALSE) { + return $this->toArray(); + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues(). + */ + public function getPropertyValues() { + return $this->value; + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues(). + */ + public function setPropertyValues($values) { + foreach ($values as $name => $value) { + $this->value[$name] = $value; + } + return $this; + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition(). + */ + public function getPropertyDefinition($name) { + $value = isset($this->value[$name]) ? $this->value[$name] : NULL; + $metadata = $this->getElementMetadata($key); + $definition = Metadata::buildDefinition($value, $metadata); + return $definition ? $definition : FALSE; + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions(). + */ + public function getPropertyDefinitions() { + $list = array(); + foreach ($this->getAllKeys() as $key) { + $list[$key] = $this->getPropertyDefinition($key); + } + return $list; + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::isEmpty(). + */ + public function isEmpty() { + return empty($this->value); + } + +} diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigWrapper.php b/core/lib/Drupal/Core/Config/Metadata/ConfigWrapper.php index 21e9cf2..6d19458 100644 --- a/core/lib/Drupal/Core/Config/Metadata/ConfigWrapper.php +++ b/core/lib/Drupal/Core/Config/Metadata/ConfigWrapper.php @@ -7,9 +7,10 @@ namespace Drupal\Core\Config\Metadata; +use Drupal\Core\Config\Config; use Drupal\Core\Language\Language; use Drupal\Core\TypedData\ComplexDataInterface; -use InvalidArgumentException; +use Drupal\Core\TypedData\ListInterface; /** * Defines the configuration wrapper object. @@ -17,14 +18,14 @@ * This object wraps the full configuration object's data and is the outermost * level for configuration wrappers. All of the others are nested in this one. */ -class ConfigWrapper extends WrapperBase implements ComplexDataInterface { +class ConfigWrapper extends Config { /** - * The name of the configuration object. + * The base typed data element for this configuration data. * - * @var string + * @var Drupal\Core\TypedData\TypedDataInterface */ - protected $name; + protected $base; /** * Constructs a configuration wrapper object. @@ -40,118 +41,101 @@ public function __construct($name, $data) { } /** - * Gets the configuration language. + * Gets data from this config object. * - * @return Drupal\Core\Language\Language - * The language object. - */ - public function getLanguage() { - $meta = $this->getMetadata(); - // The default language will be English as this is hardcoded information. - $langcode = isset($meta['language']) ? $meta['language'] : 'en'; - return new Language(array('langcode' => $langcode)); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getKey(). + * @return mixed + * The data that was requested. */ - public function getKey() { - // This is the parent wrapper, so there is no key. - return ''; + public function getData() { + return $this->data; } /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getMetadata(). + * Overrides Drupal\Core\Config\Config::setData(). */ - public function getMetadata() { - return config_metadata($this->name); + public function setData(array $data) { + $this->data = $data; + $this->setBase(); + return $this; } /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getBase(). + * Gets data from this config object as typed data. + * + * @param string $key + * A string that maps to a key within the configuration data. + * + * @return Drupal\Core\TypedData\TypedDataInterface + * The data that was requested. */ - public function getBase() { - return $this->buildDefinition($this->data, $this->getMetadata()); - } + public function get($key = '') { + $element = $this->getBase(); + if (!empty($key)) { + // Get nested element recursively. + $parts = explode('.', $key); + while ($element && $name = array_shift($parts)) { + if ($element instanceof ListInterface) { + $element = $element[$name]; + } + elseif ($element instanceof ComplexDataInterface) { + $element = $element->get($name); + } + else { + $element = NULL; + } + } - /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::get(). - */ - public function get($property_name) { - if ($property = $this->getElement($property_name)) { - return $property; - } - else { - throw new InvalidArgumentException(format_string("The configuration property @key doesn't exist.", array( - '@key' => $property_name, - ))); } + return $element; } /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::set(). + * Overrides Drupal\Core\Config\Config::set(). */ - public function set($property_name, $value) { - // Set the data into the configuration array but behave according to the - // interface specification when we've got a null value. - if (isset($value)) { - $this->setElementData($property_name, $value); - return $this->get($property_name); - } - else { - // In these objects, when clearing the value, the property is gone. - // As this needs to return a property, we get it before we delete it. - $property = $this->get($property_name); - $this->clearElementData($property_name); - $property->setValue($value); - return $property; - } - } - - /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::getProperties(). - */ - public function getProperties($include_computed = FALSE) { - return $this->toArray(); - } - - /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues(). - */ - public function getPropertyValues() { - return $this->getData(); + public function set($key, $value) { + parent::set($key, $value); + $this->setBase(); + return $this; } /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues(). + * Overrides Drupal\Core\Config\Config::clear(). */ - public function setPropertyValues($values) { - foreach ($values as $name => $value) { - $this->set($name, $value); - } + public function clear($key) { + parent::clear($key); + $this->setBase(); return $this; } /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition(). + * Gets the configuration language. + * + * @return Drupal\Core\Language\Language + * The language object. */ - public function getPropertyDefinition($name) { - $definition = $this->getElementDefinition($name); - return $definition ? $definition : FALSE; + public function getLanguage() { + // The default language will be English as this is hardcoded information. + $langcode = isset($this->data['language']) ? $this->data['language'] : 'en'; + return new Language(array('langcode' => $langcode)); } /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions(). + * Gets the base configuration element. */ - public function getPropertyDefinitions() { - return $this->getChildrenMetadata($this->getMetadata()); + protected function getBase() { + if (!isset($this->base)) { + $this->base = Metadata::buildElement($this->data, config_metadata($this->name), ''); + } + return $this->base; } /** - * Implements Drupal\Core\TypedData\ComplexDataInterface::isEmpty(). + * Resets the base element's data. */ - public function isEmpty() { - return empty($this->data); + protected function setBase() { + if (isset($this->base)) { + $this->base->setValue($this->data); + } } } diff --git a/core/lib/Drupal/Core/Config/Metadata/ElementBase.php b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php new file mode 100644 index 0000000..9fcdd7c --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/ElementBase.php @@ -0,0 +1,259 @@ +value[$key])) { + return Metadata::buildElement($this->value[$key], $this->getElementMetadata($key), $key, $this); + } + else { + return NULL; + } + } + + /** + * Gets the built metadata array for a child element. + * + * @param $key + * Key from the configuration data array. + * + * @return array + * The metadata for the element, defaults to an empty array. + */ + protected function getElementMetadata($key) { + // Metadata for nested elements must be built before. + if (!isset($this->metadata[$key])) { + $this->metadata[$key] = $this->buildElementMetadata($key, $this->value[$key]); + } + return $this->metadata[$key]; + } + + /** + * Builds metadata for a child element. + * + * @param string $key + * Element's key. + * @param mixed $data + * Configuration data for the element. + * + * @return array + * Configuration metadata for the element. + */ + protected function buildElementMetadata($key, $data) { + $metadata = $this->getMetadata(); + return isset($metadata[$key]) ? $metadata[$key] : array(); + } + + /** + * Gets valid configuration data keys. + * + * @return array + * Array of valid configuration data keys. + */ + protected function getAllKeys() { + return is_array($this->value) ? array_keys($this->value) : array(); + } + + /** + * Gets typed data properties as array. + * + * @return array + * List of TypedDataInterface objects indexed by key. + */ + protected function toArray() { + $array = array(); + foreach ($this->getAllKeys() as $key) { + $array[$key] = $this->getElement($key); + } + return array_filter($array); + } + + /** + * Implements Drupal\Core\Config\Metadata\ElementInterface::getKey(). + */ + public function getKey() { + $parent = isset($this->parent) ? $this->parent->getKey() : NULL; + return $parent || is_numeric($parent) ? $parent . '.' . $this->key : $this->key; + } + + /** + * Implements Drupal\Core\Config\Metadata\ElementInterface::getLabel(). + */ + public function getLabel() { + return isset($this->definition['label']) ? $this->definition['label'] : $this->getKey(); + } + + /** + * Implements Drupal\Core\Config\Metadata\ElementInterface::toList(). + */ + public function toList() { + $list = array(); + foreach ($this->toArray() as $key => $element) { + if ($element instanceof ElementInterface) { + foreach ($element->toList() as $subkey => $element) { + $list[$key . '.' . $subkey] = $element; + } + } + else { + $list[$key] = $element; + } + } + return $list; + } + + /** + * Implements Drupal\Core\Config\Metadata\ElementInterface::getMetadata(). + */ + public function getMetadata() { + return $this->metadata; + } + + /** + * Implements Drupal\Core\Config\Metadata\ElementInterface::setMetadata(). + */ + public function setMetadata($metadata) { + $this->metadata = $metadata; + } + + /** + * Implements Drupal\Core\TypedData\TypedDataInterface::validate(). + */ + public function validate() { + // This will be ok if we have any config data at all. + return isset($this->value); + } + + /** + * Implements Drupal\Core\TypedData\ContextAwareInterface::getName(). + */ + public function getName() { + return $this->key; + } + + /** + * Implements Drupal\Core\TypedData\ContextAwareInterface::setName(). + */ + public function setName($name) { + $this->key = $name; + } + + /** + * Implements Drupal\Core\TypedData\ContextAwareInterface::getParent(). + */ + public function getParent() { + return $this->parent; + } + + /** + * Implements Drupal\Core\TypedData\ContextAwareInterface::setParent(). + */ + public function setParent($parent) { + $this->parent = $parent; + } + + /** + * Implements ArrayAccess::offsetExists(). + */ + public function offsetExists($offset) { + return isset($this->value[$offset]); + } + + /** + * Implements ArrayAccess::offsetGet(). + */ + public function offsetGet($offset) { + return $this->getElement($offset); + } + + /** + * Implements ArrayAccess::offsetSet(). + */ + public function offsetSet($offset, $value) { + if ($value instanceof TypedDataInterface) { + $value = $value->getValue(); + } + $this->value[$offset] = $value; + } + + /** + * Implements ArrayAccess::offsetUnset(). + */ + public function offsetUnset($offset) { + unset($this->value[$offset]); + } + + /** + * Implements Countable::count(). + */ + public function count() { + return count($this->getValue()); + } + + /** + * Implements IteratorAggregate::getIterator(). + */ + public function getIterator() { + return new ArrayIterator($this->toArray()); + } +} diff --git a/core/lib/Drupal/Core/Config/Metadata/ElementInterface.php b/core/lib/Drupal/Core/Config/Metadata/ElementInterface.php new file mode 100644 index 0000000..22eda08 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/ElementInterface.php @@ -0,0 +1,94 @@ +getElementData($offset); - return isset($data); - } - - /** - * Implements ArrayAccess::offsetGet(). - */ - public function offsetGet($offset) { - return $this->buildElement($offset); - } +class ListElement extends ElementBase implements ListInterface { /** - * Implements ArrayAccess::offsetSet(). + * Overrides Drupal\Core\Config\Metadata\ElementBase::buildElementMetadata(). */ - public function offsetSet($offset, $value) { - if ($value instanceof TypedDataInterface) { - $value = $value->getValue(); + protected function buildElementMetadata($key, $data) { + $meta = parent::buildElementMetadata($key, $data); + // When the parent element is a list, elements may have special settings. + if (isset($this->definition['list settings'])) { + $settings = $this->definition['list settings']; + if (isset($settings['elements_base']) && $base = config_metadata($settings['elements_base'])) { + // Load common base metadata for all elements. + $meta = Metadata::mergeDeep($base, $meta); + } + if (isset($settings['elements_name'])) { + // Load specific metadata for each element. + $name = $settings['elements_name']; + $element_key = $key; + if (isset($settings['elements_key'])) { + // The search key for this one is in the data itself. + $element_key = isset($data[$settings['elements_key']]) ? $data[$settings['elements_key']] : NULL; + } + if ($element_key) { + $name = str_replace('%', $element_key, $name); + } + if ($merge = config_metadata($name)) { + $meta = Metadata::mergeDeep($meta, $merge); + } + } + if (isset($settings['elements_label']) && !isset($meta['.label'])) { + // Extract element's label from configuration data. + if (!empty($data[$settings['elements_label']])) { + $meta['.label'] = $data[$settings['elements_label']]; + } + } } - $this->setElementData($offset, $value); - } - - /** - * Implements ArrayAccess::offsetUnset(). - */ - public function offsetUnset($offset) { - $this->clearElementData($offset); - } - - /** - * Implements Countable::count(). - */ - public function count() { - return count($this->getData()); + return $meta; } /** diff --git a/core/lib/Drupal/Core/Config/Metadata/Metadata.php b/core/lib/Drupal/Core/Config/Metadata/Metadata.php new file mode 100644 index 0000000..ce773a4 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/Metadata.php @@ -0,0 +1,85 @@ + $key, 'parent' => $parent); + $element = typed_data()->create($definition, $value, $context); + if ($element instanceof ElementInterface) { + // If this is a nested ConfigElement we need to set the metadata for it. + $element->setMetadata($metadata); + } + return $element; + } + + /** + * Builds element definition from metadata. + * + * @param mixed $data + * The configuration data for the element. + * + * @param array $metadata + * The raw metadata array for the element. + * + * @return array + * The elemement's data definition. + */ + public static function buildDefinition($data, $metadata) { + $element = $children = array(); + foreach ($metadata as $name => $value) { + if (strpos($name, '.') === 0) { + // Metadata properties will be prefixed by a dot. + $element[substr($name, 1)] = $value; + } + else { + // Children elements will be arrays of metadata themselves. + $children[$name] = $value; + } + } + // The default type will depend on whether we've got children or not. + if (!isset($element['type'])) { + if (!empty($children) || is_array($data) || !empty($element['list']) || !empty($element['list settings'])) { + $element['type'] = 'config_element'; + } + else { + $element['type'] = 'string'; + } + } + if (isset($element['settings'])) { + // The element label may be contained in the data. + if (isset($element['settings']['label']) && isset($data[$element['settings']['label']])) { + $element['label'] = $data[$element['settings']['label']]; + } + } + return $element; + } + +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Config/Metadata/NestedElement.php b/core/lib/Drupal/Core/Config/Metadata/NestedElement.php deleted file mode 100644 index f800536..0000000 --- a/core/lib/Drupal/Core/Config/Metadata/NestedElement.php +++ /dev/null @@ -1,174 +0,0 @@ -definition = $definition; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::setData(). - */ - public function setData($value) { - $this->data = $value; - $this->getParent()->setElementData($this->key, $this->data); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::setElementData() - */ - public function setElementData($key, $value) { - parent::setElementData($key, $value); - $this->getParent()->setElementData($this->key, $this->data); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getKey(). - */ - public function getKey() { - $parent = $this->getParent()->getKey(); - return ($parent || is_numeric($parent) ? $parent . '.' : '') . $this->key; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getMetadata(). - */ - public function getMetadata() { - return $this->getParent()->getElementMetadata($this->key); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getBase(). - */ - public function getBase() { - return $this->getDefinition(); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::clearElementData() - */ - public function clearElementData($key) { - parent::clearElementData($key); - $this->getParent()->setElementData($this->key, $data); - } - - /** - * Implements Drupal\Core\TypedData\TypedDataInterface::getType(). - */ - public function getType() { - return $this->definition['type']; - } - - /** - * Implements Drupal\Core\TypedData\TypedDataInterface::getDefinition(). - */ - public function getDefinition() { - return $this->definition; - } - - /** - * Implements Drupal\Core\TypedData\TypedDataInterface::getValue(). - */ - public function getValue() { - return $this->getData(); - } - - /** - * Implements Drupal\Core\TypedData\TypedDataInterface::setValue(). - */ - public function setValue($value) { - $this->data = $value; - // Due to how the constructor works, this function may get called before - // the parent is set. - // @see Drupal\Core\TypedData\TypedDataManager::createInstance() - if ($this->getParent()) { - $this->getParent()->set($this->key, $value); - } - } - - /** - * Implements Drupal\Core\TypedData\TypedDataInterface::getString(). - */ - public function getString() { - return (string) $this->getValue(); - } - - /** - * Implements Drupal\Core\TypedData\TypedDataInterface::validate(). - */ - public function validate() { - // This will be ok if we have any config data at all. - return isset($this->data); - } - - /** - * Implements Drupal\Core\TypedData\ContextAwareInterface::getName(). - */ - public function getName() { - return $this->key; - } - - /** - * Implements Drupal\Core\TypedData\ContextAwareInterface::setName(). - */ - public function setName($name) { - $this->key = $name; - } - - /** - * Implements Drupal\Core\TypedData\ContextAwareInterface::getParent(). - */ - public function getParent() { - return $this->parent; - } - - /** - * Implements Drupal\Core\TypedData\ContextAwareInterface::setParent(). - */ - public function setParent($parent) { - $this->parent = $parent; - } - -} diff --git a/core/lib/Drupal/Core/Config/Metadata/WrapperBase.php b/core/lib/Drupal/Core/Config/Metadata/WrapperBase.php deleted file mode 100644 index 737a620..0000000 --- a/core/lib/Drupal/Core/Config/Metadata/WrapperBase.php +++ /dev/null @@ -1,299 +0,0 @@ -getElementDefinition($key); - $value = $this->getElementData($key); - // If this is an end property, the value will be the configuration data. - // If it is a NestedElement, it won't be set, but retrieved from the parent - // element. See NestedElement::setValue() - $context = array('name' => $key, 'parent' => $this); - return typed_data()->create($definition, $value, $context); - } - - /** - * Builds metadata for a child element. - * - * @param $key - * Element's key. - * - * @return array - * Configuration metadata for the element. - */ - protected function buildElementMetadata($key) { - $metadata = $this->getMetadata(); - $meta = isset($metadata[$key]) ? $metadata[$key] : array(); - $definition = $this->getBase(); - $constraints = isset($definition['constraints']) ? $definition['constraints'] : array(); - if (isset($constraints['elements_base']) && $base = config_metadata($constraints['elements_base'])) { - // Load common base metadata for all elements. - $meta = NestedArray::mergeDeep($base, $meta); - } - if (isset($constraints['elements_name'])) { - // Load specific metadata for each element. - $name = $constraints['elements_name']; - $element_key = $key; - if (isset($constraints['elements_key'])) { - // The search key for this one is in the data itself. - $key_name = str_replace('%', $key, $constraints['elements_key']); - - $element_key = $this->getElementData($key_name); - } - if ($element_key) { - $name = str_replace('%', $element_key, $name); - } - if ($merge = config_metadata($name)) { - $meta = NestedArray::mergeDeep($meta, $merge); - } - } - if (isset($constraints['elements_label']) && !isset($meta['.label'])) { - // Extract element's label from configuration data. - $key_name = str_replace('%', $key, $constraints['elements_label']); - if ($label = $this->getElementData($key_name)) { - $meta['.label'] = $label; - } - } - return $meta; - } - - /** - * Builds element definition from metadata. - * - * @param mixed $data - * The configuration data for the element. - * - * @param array $metadata - * The raw metadata array for the element. - * - * @return array - * The elemement's data definition. - */ - protected function buildDefinition($data, $metadata) { - $element = $children = array(); - foreach ($metadata as $name => $value) { - if (strpos($name, '.') === 0) { - // Metadata properties will be prefixed by a dot. - $element[substr($name, 1)] = $value; - } - else { - // Children elements will be arrays of metadata themselves. - $children[$name] = $value; - } - } - // The default type will depend on whether we've got children or not. - if (!isset($element['type'])) { - if (!empty($children) || is_array($data) || !empty($element['list'])) { - $element['type'] = 'config_element'; - $element['list'] = TRUE; - } - else { - $element['type'] = 'string'; - } - } - return $element; - } - - /** - * Gets valid configuration data keys. - * - * @return array - * Array of valid configuration data keys. - */ - protected function getAllKeys() { - return is_array($this->data) ? array_keys($this->data) : array(); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getData(). - */ - public function getData() { - return $this->data; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::setData(). - */ - public function setData($value) { - $this->data = $value; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getLabel(). - */ - public function getLabel() { - $definition = $this->getBase(); - return isset($definition['label']) ? $definition['label'] : $this->getKey(); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getElement(). - */ - public function getElement($key) { - $parts = explode('.', $key); - $first = array_shift($parts); - $data = $this->getElementData($first); - // Only attempt to build the element if it actually has configuration data. - if (isset($data) && $element = $this->buildElement($first)) { - $subkey = implode('.', $parts); - // If not a nested key, return the element whatever it is. But if we need - // to find a nested key, this is only possible if it's a WrapperInterface. - if (!$subkey) { - return $element; - } - elseif ($element instanceof WrapperInterface) { - return $element->getElement($subkey); - } - } - return NULL; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::setElementData(). - */ - public function setElementData($key, $value) { - $parts = explode('.', $key); - if (count($parts) == 1) { - $this->data[$key] = $value; - } - else { - NestedArray::setValue($this->data, $parts, $value); - } - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getElementData(). - */ - public function getElementData($key) { - $parts = explode('.', $key); - if (count($parts) == 1) { - return isset($this->data[$key]) ? $this->data[$key] : NULL; - } - else { - $value = NestedArray::getValue($this->data, $parts, $key_exists); - return $key_exists ? $value : NULL; - } - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getElementDefinition(). - */ - public function getElementDefinition($key) { - return $this->buildDefinition($this->getElementData($key), $this->getElementMetadata($key)); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getElementMetadata(). - */ - public function getElementMetadata($key) { - // Metadata for nested elements must be built before. - if (!isset($this->metadata[$key])) { - $this->metadata[$key] = $this->buildElementMetadata($key); - } - return $this->metadata[$key]; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getElementLabel(). - */ - public function getElementLabel($key) { - $definition = $this->getElementDefinition($key); - return isset($definition['label']) ? $definition['label'] : $this->getElementKey($key); - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::getElementKey(). - */ - public function getElementKey($key) { - $parent = $this->getKey(); - return $parent ? $parent . '.' . $key : $key; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::clearElementData(). - */ - public function clearElementData($key) { - $parts = explode('.', $key); - if (count($parts) == 1) { - unset($this->data[$key]); - } - else { - NestedArray::unsetValue($this->data, $parts); - } - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::toArray(). - */ - public function toArray() { - $array = array(); - foreach ($this->getAllKeys() as $key) { - $array[$key] = $this->buildElement($key); - } - return $array; - } - - /** - * Implements Drupal\Core\Config\Metadata\WrapperInterface::toList(). - */ - public function toList($base_name = '') { - $list = array(); - foreach ($this->toArray() as $key => $element) { - $name = $base_name ? $base_name . '.' . $key : $key; - if ($element instanceof WrapperInterface) { - $list += $element->toList($name); - } - else { - $list[$name] = $element; - } - } - return $list; - } - - /** - * Implements IteratorAggregate::getIterator(). - */ - public function getIterator() { - return new ArrayIterator($this->toArray()); - } - -} diff --git a/core/lib/Drupal/Core/Config/Metadata/WrapperInterface.php b/core/lib/Drupal/Core/Config/Metadata/WrapperInterface.php deleted file mode 100644 index fd4bf6f..0000000 --- a/core/lib/Drupal/Core/Config/Metadata/WrapperInterface.php +++ /dev/null @@ -1,212 +0,0 @@ - array( '.label' => 'Style effects', - '.list' => 'true', - '.constraints' => array( + '.list' => '1', + '.list settings' => array( 'elements_name' => 'image.style.effects.%', - 'elements_key' => '%.name' + 'elements_key' => 'name' ), ) ); @@ -78,7 +78,6 @@ function testBasicMetadata() { ), 'data' => array( '.label' => 'Data', - '.list' => 'true', 'width' => array( '.label' => 'Width', '.type' => 'integer' @@ -126,13 +125,18 @@ function testConfigMetadata() { $this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data'); $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.'); $this->assertEqual($list['front']->getValue(), 'user', 'Got the right value for page.front data from the list.'); + // And test some ComplexDataInterface methods. + $properties = $list->getProperties(); + $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.'); + $values = $list->getPropertyValues(); + $this->assertTrue(count($values) == 3 && $values['front'] == 'user', 'Got the right property values for site page.'); // Now let's try something more complex, with nested objects. $wrapper = config_wrapper('image.style.large'); $effects = $wrapper->get('effects'); // The function is_array() doesn't work with ArrayAccess, so we use count(). $this->assertTrue(count($effects) == 1, 'Got an array with effects for image.style.large data'); - $ieid = key($effects->toArray()); + $ieid = key($effects->getValue()); $effect = $wrapper->get('effects.' . $ieid); $this->assertTrue(count($effect['data']) && $effect['name']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.'); $this->assertEqual($effect['data']['width']->getType(), 'integer', 'Got the right type for the scale effect width.'); @@ -142,7 +146,7 @@ function testConfigMetadata() { $this->assertEqual($effect['data']->getKey(), $key, 'Got the right configuration key for a nested image styple effect'); $object = $wrapper->get($key); $this->assertEqual(get_class($effect['data']), get_class($object), 'Got the same object class using different keys with dot notation.'); - $this->assertEqual($effect['data']->getData(), $object->getData(), 'Got the same object values using different keys with dot notation.'); + $this->assertEqual($effect['data']->getValue(), $object->getValue(), 'Got the same object values using different keys with dot notation.'); // Finally update some object using a configuration wrapper. $new_slogan = 'Site slogan for testing configuration metadata'; diff --git a/core/modules/contact/meta/contact.category.%.yml b/core/modules/contact/meta/contact.category.%.yml index eb6226a..a0095d0 100644 --- a/core/modules/contact/meta/contact.category.%.yml +++ b/core/modules/contact/meta/contact.category.%.yml @@ -1,3 +1,5 @@ +.settings: + label: label id: .type: string .label: 'Id' diff --git a/core/modules/image/meta/image.style.%.yml b/core/modules/image/meta/image.style.%.yml index b5fcab1..5399fa4 100644 --- a/core/modules/image/meta/image.style.%.yml +++ b/core/modules/image/meta/image.style.%.yml @@ -7,6 +7,6 @@ label: effects: .label: 'Style effects' .list: '1' - .constraints: + .list settings: elements_name: 'image.style.effects.%' - elements_key: '%.name' + elements_key: 'name' diff --git a/core/modules/image/meta/image.style.effects.%.yml b/core/modules/image/meta/image.style.effects.%.yml index 42ef18b..2f73d81 100644 --- a/core/modules/image/meta/image.style.effects.%.yml +++ b/core/modules/image/meta/image.style.effects.%.yml @@ -3,7 +3,6 @@ name: .type: string data: .label: 'Data' - .list: '1' weight: .label: 'Weight' .type: integer diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigWrapper.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigWrapper.php index 5a90d57..f16d913 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleConfigWrapper.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigWrapper.php @@ -9,7 +9,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\Config\Metadata\ConfigWrapper; -use Drupal\Core\Config\Metadata\WrapperInterface; +use Drupal\Core\Config\Metadata\ElementInterface; use Drupal\Core\TypedData\TranslatableInterface; use InvalidArgumentException; @@ -125,7 +125,7 @@ public function getTranslation($langcode, $strict = TRUE) { 'strict' => $strict, ); //$data = $source->getTranslatedData($this, $options); - $data = $this->getTranslatedData($this->toArray(), $options); + $data = $this->getTranslatedData($this->get(), $options); $translation = new LocaleConfigWrapper($this->name, $data); $translation->setTranslation($langcode, $strict, $this); return $translation; @@ -147,8 +147,8 @@ public function language() { /** * Get translated configuration data. * - * @param array $elements - * Array of configuration elements. + * @param Traversable $elements + * Configuration elements. * @param array $options * Array with options that will depend on the translator used. * @@ -160,8 +160,8 @@ protected function getTranslatedData($elements, $options) { $translation = array(); foreach ($elements as $key => $element) { $value = NULL; - if ($element instanceof WrapperInterface) { - $value = $this->getTranslatedData($element->toArray(), $options); + if ($element instanceof ElementInterface) { + $value = $this->getTranslatedData($element, $options); } elseif ($this->translateElement($element, $options)) { $value = $element->getValue(); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php index 40c27fc..765c314 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php @@ -7,6 +7,7 @@ namespace Drupal\locale\Tests; +use Drupal\Core\Language\Language; use Drupal\simpletest\WebTestBase; use Drupal\locale\LocaleConfigWrapper; @@ -41,7 +42,7 @@ function setUp() { function testConfigTranslation() { // Add custom language $langcode = 'xx'; - $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages')); + $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface')); $this->drupalLogin($admin_user); $name = $this->randomName(16); $edit = array( @@ -51,28 +52,47 @@ function testConfigTranslation() { 'direction' => '0', ); $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language')); + $language = new Language(array('langcode' => $langcode)); + // Set path prefix. + $edit = array( "prefix[$langcode]" => $langcode ); + $this->drupalPost('admin/config/regional/language/detection/url', $edit, t('Save configuration')); // Check site name string exists and create translation for it. - $site_name = $this->randomName(20); $string = $this->storage->findString(array('source' => 'Drupal', 'context' => '', 'type' => 'configuration')); $this->assertTrue($string, 'Configuration strings have been created upon installation.'); - $this->storage - ->createTranslation(array('lid' => $string->lid, 'language' => $langcode, 'translation' => $site_name)) - ->save(); + + // Translate using the UI so configuration is refreshed. + $site_name = $this->randomName(20); + $search = array( + 'string' => $string->source, + 'langcode' => $langcode, + 'translation' => 'all', + ); + $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter')); + $textarea = current($this->xpath('//textarea')); + $lid = (string) $textarea[0]['name']; + $edit = array( + $lid => $site_name, + ); + $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); $data = config('system.site')->get(); $wrapper = new LocaleConfigWrapper('system.site', $data, $this->storage); // Get strict translation and check we've got only the site name. $translation = $wrapper->getTranslation($langcode, TRUE); - $properties = $translation->toList(); + $properties = $translation->get()->toList(); $this->assertEqual(count($properties), 1, 'Got the right number of properties with strict translation'); $this->assertEqual($properties['name']->getValue(), $site_name, 'Got the right translation for site name with strict translation'); // Get non strict translation and check we've got all properties. $translation = $wrapper->getTranslation($langcode, FALSE); - $properties = $translation->toList(); + $properties = $translation->get()->toList(); $this->assertEqual(count($properties), 6, '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. + $this->drupalGet($langcode); + $this->assertText($site_name, 'The translated site name is displayed after translations refreshed.'); } } diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 8e01943..e69e138 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -14,7 +14,6 @@ use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\StorageException; - /** * Form constructor for the translation import screen. * diff --git a/core/modules/system/meta/system.site.yml b/core/modules/system/meta/system.site.yml index f95f546..8476bdf 100644 --- a/core/modules/system/meta/system.site.yml +++ b/core/modules/system/meta/system.site.yml @@ -7,7 +7,6 @@ slogan: .label: 'Site slogan' .type: text page: - .list: '1' .label: 'Default pages' 403: .label: 'Default 403 (access denied) page' diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 72894c8..ac458d5 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2113,9 +2113,9 @@ function system_data_type_info() { 'list class' => '\Drupal\Core\Entity\Field\Type\Field', ), 'config_element' => array( - 'label' => t('Nested configuration'), - 'description' => t('Nested configuration data'), - 'class' => 'Drupal\Core\Config\Metadata\NestedElement', + 'label' => t('Configuration element'), + 'description' => t('Configuration data'), + 'class' => 'Drupal\Core\Config\Metadata\ConfigElement', 'list class' => 'Drupal\Core\Config\Metadata\ListElement', ), // These are UI string types. diff --git a/core/modules/user/meta/user.mail.yml b/core/modules/user/meta/user.mail.yml index d25633c..5a3e9bb 100644 --- a/core/modules/user/meta/user.mail.yml +++ b/core/modules/user/meta/user.mail.yml @@ -1,4 +1,4 @@ .label: 'User mails' .list: '1' -.constraints: +.list settings: elements_name: 'user.mail.%'