diff --git a/core/includes/common.inc b/core/includes/common.inc index aea8529..36de48e 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -7064,6 +7064,84 @@ function drupal_get_updaters() { } /** + * Gets information about all data types. + * + * @see drupal_wrap_data() + * @see hook_data_type_info() + */ +function drupal_get_data_type_info() { + $items = &drupal_static(__FUNCTION__); + if (!isset($items)) { + $items = module_invoke_all('data_type_info'); + drupal_alter('data_type_info', $items); + } + return $items; +} + +/** + * Wraps a data value into a typed data wrapper. + * + * @param array $definition + * The data definition array with the following array keys and values: + * - type: The data type of the data to wrap. Required. + * - label: A human readable label. + * - description: A human readable description. + * - list: Whether the wrapped data is multi-valued, i.e. a list of data + * items. Defaults to FALSE. + * - computed: A boolean specifying whether the data value is computed by the + * wrapper, e.g. depending on some other values. + * - read-only: A boolean specifying whether the wrapped data is read-only. + * Defaults to TRUE for computed properties, to FALSE otherwise. + * - class: If set and 'list' is FALSE, the class to use for creating the + * typed data wrapper. Else the default class as specified for the data type + * will be used. + * - list class: If set and 'list' is TRUE, the class to use for creating the + * typed data wrapper. Else the default list class as specified for the data + * type will be used. + * - settings: An array of settings, as required by the used class. See the + * documentation of the used class for supported or required settings. + * - constraints: An array of type specific value constraints, e.g. for data + * of type 'entity' the 'entity type' and 'bundle' may be specified. See the + * documentation of the data type class for supported constraints. + * - required: A boolean specifying whether a non-NULL value is mandatory. + * Further keys may be supported in certain usages, e.g. for further keys + * supported for entity property definitions see + * Drupal\Core\Entity\StorageControllerInterface::getPropertyDefinitions(). + * @param mixed $value + * (optional) The data value. If set, it has to match the data type format as + * documented for the data type classes. + * @param array $context + * (optional) An array describing the context of the data. It should be + * passed if a data value is wrapped as part of a data structure. The + * following keys are supported: + * - name: The name of the data being wrapped. + * - parent: The parent object containing the data. Must be an instance of + * Drupal\Core\TypedData\StructureInterface or + * Drupal\Core\TypedData\ListInterface. + * + * @return Drupal\Core\TypedData\WrapperInterface + * + * @see drupal_get_data_type_info() + * @see Drupal\Core\TypedData\Type\Integer + * @see Drupal\Core\TypedData\Type\Decimal + * @see Drupal\Core\TypedData\Type\String + * @see Drupal\Core\TypedData\Type\Boolean + * @see Drupal\Core\TypedData\Type\Duration + * @see Drupal\Core\TypedData\Type\Date + * @see Drupal\Core\TypedData\Type\Uri + * @see Drupal\Core\TypedData\Type\Binary + * @see Drupal\Core\Entity\Property\EntityWrapper + */ +function drupal_wrap_data(array $definition, $value = NULL, array $context = array()) { + $type_info = drupal_get_data_type_info(); + + // Allow per-data definition overrides of the used classes. + $full_definition = $definition + $type_info[$definition['type']]; + $class = empty($full_definition['list']) ? $full_definition['class'] : $full_definition['list class']; + return new $class($full_definition, $value, $context); +} + +/** * Assembles the Drupal FileTransfer registry. * * @return diff --git a/core/includes/config.inc b/core/includes/config.inc index 39094f8..8b43120 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -3,6 +3,8 @@ use Drupal\Core\Config\Config; use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\NullStorage; +use Drupal\Core\Config\Metadata\ConfigWrapper; +use Drupal\Core\Config\Metadata\MetadataLookup; use Drupal\Core\Config\StorageInterface; /** @@ -104,6 +106,36 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf } /** + * Retrieves a metadata object for a configuration object or key. + * + * @param $name + * The name of the configuration object for which we want to build a metadata object. + * + * @return Drupal\Core\Config\ConfigMetadata + * An metadata object that contains the full metadata for the reguested name. + */ +function config_metadata($name) { + $metadata = &drupal_static(__FUNCTION__); + if (!$metadata) { + $metadata = new MetadataLookup(); + } + return $metadata[$name]; +} + +/** + * Retrieves a configuration wrapper object to access data as typed properties. + * + * @param $name + * The name of the configuration object to retrieve. + * + * @return Drupal\Core\Config\Metadata\Wrapper + * A configuration wrapper object. + */ +function config_wrapper($name) { + return new ConfigWrapper(config($name)); +} + +/** * Writes an array of config file changes from a source storage to a target storage. * * @param array $config_changes diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigStructure.php b/core/lib/Drupal/Core/Config/Metadata/ConfigStructure.php new file mode 100644 index 0000000..0f3202d --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/ConfigStructure.php @@ -0,0 +1,399 @@ +key = $key; + $this->parentStructure = $parentStructure; + $this->setMetadata($metadata); + } + + /** + * Builds properties using configuration data and metadata. + * + * @param string $key + * Configuration key. + * @param $data + * Configuration data for this element. + * + * @result Drupal\Core\Config\Metadata\ConfigStructure | Drupal\Core\TypedData\WrapperInterface + * While end properties will be of type WrapperInterface, nested arrays will + * be mapped into ConfigStructure objects. + */ + protected function buildElement($key, $data) { + // Get metadata and split into parent and children's properties. + $metadata = $this->getMetadata($key); + $children_meta = self::getChildrenMetadata($metadata); + $parent_meta = self::getElementMetadata($metadata); + + // The default type will be undefined if not children elements or config_structure. + $parent_meta += array('type' => $children_meta ? 'config_structure' : 'undefined'); + + // Build element depending on type. + switch ($parent_meta['type']) { + case 'config_nested': + // This is an array of nested configuration elements. + return new NestedStructure($key, $metadata, $this); + + case 'config_structure': + // Array with multiple properties. + return new ConfigStructure($key, $children_meta, $this); + + default: + // This is an end property. The metadata will be the property definition. + return $this->buildProperty($key, $data, $parent_meta); + } + } + +/** + * Get children (subkey) names from a piece of metadata. + * + * Children elements will be the ones that have an array as value. + * + * Example: + * + * page: + * config.type: subkeys + * 403: + * label: 'Default 403 (access denied) page' + * type: 'path' + * 404: + * label: 'Default 404 (not found) page' + * type: 'path' + * front: + * label: 'Default front page' + * type: 'path' + * + * The 'children' of the 'page' element in this example will be '403', '404', 'front' + */ + protected static function getChildrenMetadata($metadata) { + foreach (array_keys($metadata) as $key) { + if (!is_array($metadata[$key])) { + unset($metadata[$key]); + } + } + return $metadata; + } + + /** + * Element's metadata. + * + * These are the properties that are not arrays, thus they're this element's metadata. + */ + protected static function getElementMetadata($metadata) { + foreach (array_keys($metadata) as $key) { + if (is_array($metadata[$key])) { + unset($metadata[$key]); + } + } + return $metadata; + } + + /** + * Builds property as typed data. + * + * @see drupal_wrap_data(); + * + * @return Drupal\Core\TypedData\WrapperInterface + * Property as typed data. + */ + protected function buildProperty($name, $value = NULL, array $definition = array()) { + $type_info = drupal_get_data_type_info(); + $context = array( + 'name' => $name, + 'parent' => $this, + ); + // Allow per-data definition overrides of the used classes. + if (isset($definition['type']) && isset($type_info[$definition['type']])) { + $full_definition = $definition + $type_info[$definition['type']]; + } + else { + // We use undefined data type for properties that don't have a known type. + $full_definition = $definition + array('type' => 'undefined', 'class' => 'Drupal\Core\TypedData\Type\Undefined'); + } + $class = empty($full_definition['list']) ? $full_definition['class'] : $full_definition['list class']; + return new $class($full_definition, $value, $context); + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::getKey(). + */ + public function getKey($key = '') { + return $this->key . ($key ? '.' . $key : ''); + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::setMetadata(). + */ + public function setMetadata($metadata) { + $this->metadata = $metadata; + return $this; + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::getMetadata(). + */ + public function getMetadata($key = '') { + if ($key) { + return isset($this->metadata[$key]) ? $this->metadata[$key] : array(); + } + else { + return $this->metadata; + } + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::getConfigData(). + */ + public function getConfigData($key = '') { + return $this->parentStructure->getConfigData($this->getKey($key)); + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::toList(). + */ + public function toList() { + $list = array(); + foreach ($this->getProperties() as $key => $property) { + if ($property instanceof ConfigStructure) { + foreach ($property->toList() as $index => $value) { + $list[$key . '.' . $index] = $value; + } + } + else { + $list[$key] = $property; + } + } + return $list; + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::get(). + */ + public function get($property_name) { + $parts = explode('.', $property_name); + $first = array_shift($parts); + $data = $this->getConfigData($first); + if (isset($data)) { + // We have data and metadata, built typed data. + $element = $this->buildElement($first, $data); + $key = implode('.', $parts); + if (!$key) { + return $element; + } + elseif ($element instanceof ConfigStructure) { + return $element->get($key); + } + } + else { + return NULL; + } + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::set(). + */ + public function set($property_name, $value) { + // Set the value in the parent object that holds the Config. + $this->parentStructure->set($this->getKey($property_name), $value); + return $this; + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::getProperties(). + */ + public function getProperties($include_computed = FALSE) { + $array = array(); + foreach ($this->getConfigData() as $key => $data) { + $array[$key] = $this->buildElement($key, $data); + } + return $array; + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::setProperties(). + */ + public function setProperties($properties) { + foreach ($properties as $name => $property) { + if ($property instanceof WrapperInterface) { + $value = $property->getString(); + } + else { + $value = $property; + } + $this->set($name, $value); + } + return $this; + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::getPropertyDefinition(). + */ + public function getPropertyDefinition($name) { + if ($meta = $this->getMetadata($name)) { + return $this->getElementMetadata($meta); + } + else { + return FALSE; + } + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::getPropertyDefinitions(). + */ + public function getPropertyDefinitions() { + return $this->getChildrenMetadata($this->getMetadata()); + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::toArray(). + */ + public function toArray() { + return $this->getConfigData(); + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::isEmpty(). + */ + public function isEmpty() { + return !(bool)$this->getConfigData(); + } + + /** + * Implements IteratorAggregate::getIterator(). + */ + public function getIterator() { + return new ArrayIterator($this->getProperties()); + } + + /** + * Implements ArrayAccess::offsetExists(). + */ + public function offsetExists($offset) { + $data = $this->getConfigData($offset); + return isset($data); + } + + /** + * Implements ArrayAccess::offsetGet(). + */ + public function offsetGet($offset) { + return $this->get($offset); + } + + /** + * Implements ArrayAccess::offsetSet(). + */ + public function offsetSet($offset, $value) { + $this->set($offset, $value); + } + + /** + * Implements ArrayAccess::offsetUnset(). + */ + public function offsetUnset($offset) { + $this->set($offset, NULL); + } + + /** + * Implements Countable::count(). + */ + public function count() { + return count($this->getConfigData()); + } +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigStructureInterface.php b/core/lib/Drupal/Core/Config/Metadata/ConfigStructureInterface.php new file mode 100644 index 0000000..2db2994 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/ConfigStructureInterface.php @@ -0,0 +1,73 @@ +config = $config; + parent::__construct(''); + } + + /** + * Gets the configuration object for this data. + * + * @return Drupal\Core\Config\Config + */ + public function getConfig() { + return $this->config; + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::getKey(). + */ + public function getKey($key = '') { + // As this is the parent structure, there is no base key. + return $key; + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::getMetadata(). + */ + public function getMetadata($key = '') { + if (!isset($this->metadata)) { + $this->metadata = config_metadata($this->getConfig()->getName()); + } + return parent::getMetadata($key); + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::getConfigData(). + */ + public function getConfigData($key = '') { + // As this is the parent structure, we get it right from the config object. + return $this->config->get($key); + } + + /** + * Implements Drupal\Core\TypedData\StructureInterface::set(). + */ + public function set($property_name, $value) { + if (isset($value)) { + $this->config->set($property_name, $value); + } + else { + $this->config->clear($property_name); + } + return $this; + } +} diff --git a/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php b/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php new file mode 100644 index 0000000..f7f0639 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php @@ -0,0 +1,102 @@ +getBaseName($offset)) { + $metadata = $this->offsetGet($basename); + } + // Merge current metadata on top of it. + if ($meta = $this->readMetadata($offset)) { + $metadata = NestedArray::mergeDeep($metadata, $meta); + } + $this->storage[$offset] = $metadata; + return $metadata; + } + + /** + * Get parent metadata name. + */ + protected static function getBaseName($name) { + $parts = explode('.', $name); + $last = array_pop($parts); + // If this is not the generic metadata (where the last component of the + // name is a '+'), try to load that first. + if ($last != self::BASE_MARK) { + return implode('.', array_merge($parts, array(self::BASE_MARK))); + } + } + + /** + * Read metadata from file system. + * + * @return array + * Metadata array if found or empty array if not. + */ + protected function readMetadata($name) { + if (!isset($this->metadataStorage)) { + $this->metadataStorage = new MetadataStorage(); + } + try { + return $this->metadataStorage->read($name); + } + catch (Symfony\Component\Yaml\Exception\ParseException $e) { + // @todo Just log an error, but return empty array. + } + return array(); + } +} diff --git a/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php new file mode 100644 index 0000000..1806a01 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php @@ -0,0 +1,50 @@ +directory = drupal_get_path('module', $module) . '/meta'; + return parent::listAll($prefix); + } + } +} \ No newline at end of file diff --git a/core/lib/Drupal/Core/Config/Metadata/NestedStructure.php b/core/lib/Drupal/Core/Config/Metadata/NestedStructure.php new file mode 100644 index 0000000..1602036 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Metadata/NestedStructure.php @@ -0,0 +1,132 @@ +metadata[$key])) { + $this->metadata[$key] = NestedArray::mergeDeep($this->getBaseMetadata(), $this->getNestedMetadata($key)); + } + return parent::getMetadata($key); + } + + /** + * Implements Drupal\Core\Config\Metadata\ConfigStructureInterface::setMetadata(). + */ + public function setMetadata($metadata) { + if (isset($metadata['nested.base'])) { + $this->baseConfigName = $metadata['nested.base']; + } + if (isset($metadata['nested.name'])) { + $this->configNestedKey = $metadata['nested.name']; + } + parent::setMetadata($metadata); + return $this; + } + + /** + * Get metadata for nested element. + * + * This will depend on the actual data and the property we are using to map + * it to the configuration metadata name. + * + */ + protected function getNestedMetadata($key) { + // This depends on the actual configuration data. + if (isset($this->configNestedKey) && ($data = $this->getConfigData($key)) && isset($data[$this->configNestedKey])) { + return config_metadata($data[$this->configNestedKey]); + } + else { + return array(); + } + } + + /** + * Gets base metadata for all nested keys. + */ + protected function getBaseMetadata() { + if (isset($this->baseConfigName)) { + return config_metadata($this->baseConfigName); + } + else { + return array(); + } + } +} diff --git a/core/lib/Drupal/Core/TypedData/AccessibleInterface.php b/core/lib/Drupal/Core/TypedData/AccessibleInterface.php new file mode 100644 index 0000000..3c42f7a --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/AccessibleInterface.php @@ -0,0 +1,27 @@ +handle) && isset($this->uri)) { + $this->handle = fopen($this->uri, 'rb'); + } + return $this->handle; + } + + /** + * Implements WrapperInterface::setValue(). + */ + public function setValue($value) { + if (!isset($value)) { + $this->handle = NULL; + $this->uri = NULL; + } + elseif (is_resource($value)) { + $this->handle = $value; + } + elseif (is_string($value)) { + $this->uri = $value; + } + else { + throw new InvalidArgumentException("Invalid value for binary data given."); + } + } + + /** + * Implements WrapperInterface::getString(). + */ + public function getString() { + $contents = ''; + while (!feof($this->getValue())) { + $contents .= fread($this->handle, 8192); + } + return $contents; + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/Boolean.php b/core/lib/Drupal/Core/TypedData/Type/Boolean.php new file mode 100644 index 0000000..de67e06 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/Boolean.php @@ -0,0 +1,39 @@ +value = isset($value) ? (bool) $value : $value; + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/Date.php b/core/lib/Drupal/Core/TypedData/Type/Date.php new file mode 100644 index 0000000..a60c20d --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/Date.php @@ -0,0 +1,68 @@ +value; + } + + /** + * Implements WrapperInterface::setValue(). + */ + public function setValue($value) { + if ($value instanceof DateTime || !isset($value)) { + $this->value = $value; + } + elseif (is_numeric($value)) { + // Value is a timestamp. + $this->value = new DateTime('@' . $value); + } + elseif (is_string($value)) { + $this->value = new DateTime($value); + } + else { + throw new InvalidArgumentException("Invalid date format given."); + } + } + + /** + * Implements WrapperInterface::getString(). + */ + public function getString() { + return (string) $this->getValue()->format(DateTime::ISO8601); + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/Decimal.php b/core/lib/Drupal/Core/TypedData/Type/Decimal.php new file mode 100644 index 0000000..3bede7e --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/Decimal.php @@ -0,0 +1,39 @@ +value = isset($value) ? (float) $value : $value; + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/Duration.php b/core/lib/Drupal/Core/TypedData/Type/Duration.php new file mode 100644 index 0000000..5dc8fc9 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/Duration.php @@ -0,0 +1,66 @@ +value = $value; + } + elseif (is_numeric($value)) { + // Value is a time span in seconds. + $this->value = new DateInterval('PT' . $value . 'S'); + } + elseif (is_string($value)) { + // @todo: Add support for negative intervals on top of the DateInterval + // constructor. + $this->value = new DateInterval($value); + } + else { + throw new InvalidArgumentException("Invalid duration format given."); + } + } + + /** + * Implements WrapperInterface::getString(). + */ + public function getString() { + // Generate an ISO 8601 formatted string as supported by + // DateInterval::__construct() and setValue(). + return (string) $this->getValue()->format('%rP%yY%mM%dDT%hH%mM%sS'); + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/Integer.php b/core/lib/Drupal/Core/TypedData/Type/Integer.php new file mode 100644 index 0000000..1d6bf3f --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/Integer.php @@ -0,0 +1,39 @@ +value = isset($value) ? (int) $value : $value; + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/Language.php b/core/lib/Drupal/Core/TypedData/Type/Language.php new file mode 100644 index 0000000..068a32e --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/Language.php @@ -0,0 +1,93 @@ +definition = $definition; + + if (isset($context['parent']) && !empty($this->definition['settings']['langcode source'])) { + $this->langcode = $context['parent']->get($this->definition['settings']['langcode source']); + } + else { + // No context given, so just initialize an langcode property for storing + // the code. + $this->langcode = drupal_wrap_data(array('type' => 'string')); + } + + if (isset($value)) { + $this->setValue($value); + } + } + + /** + * Implements WrapperInterface::getValue(). + */ + public function getValue() { + $langcode = $this->langcode->getValue(); + return $langcode ? language_load($langcode) : NULL; + } + + /** + * Implements WrapperInterface::setValue(). + * + * Both the langcode and the language object may be passed as value. + */ + public function setValue($value) { + if (!isset($value)) { + $this->langcode->setValue(NULL); + } + elseif (is_scalar($value)) { + $this->langcode->setValue($value); + } + elseif (is_object($value)) { + $this->langcode->setValue($value->langcode); + } + else { + throw new InvalidArgumentException('Value is no valid langcode or language object.'); + } + } + + /** + * Implements WrapperInterface::getString(). + */ + public function getString() { + $language = $this->getValue(); + return $language ? $language->name : ''; + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/String.php b/core/lib/Drupal/Core/TypedData/Type/String.php new file mode 100644 index 0000000..f87e111 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/String.php @@ -0,0 +1,32 @@ +value = $value; + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/Uri.php b/core/lib/Drupal/Core/TypedData/Type/Uri.php new file mode 100644 index 0000000..9b933ea --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/Uri.php @@ -0,0 +1,38 @@ +value = isset($value) ? (string) $value : $value; + } + + /** + * Implements WrapperInterface::validate(). + */ + public function validate() { + // TODO: Implement validate() method. + } +} diff --git a/core/lib/Drupal/Core/TypedData/Type/WrapperBase.php b/core/lib/Drupal/Core/TypedData/Type/WrapperBase.php new file mode 100644 index 0000000..6411cb6 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/Type/WrapperBase.php @@ -0,0 +1,86 @@ +definition = $definition; + if (isset($value)) { + $this->setValue($value); + } + } + + /** + * Implements WrapperInterface::getType(). + * + * @return string + */ + public function getType() { + return $this->definition['type']; + } + + /** + * Implements WrapperInterface::getDefinition(). + * + * @return array + */ + public function getDefinition() { + return $this->definition; + } + + /** + * Implements WrapperInterface::getValue(). + * + * @return mixed + */ + public function getValue() { + return $this->value; + } + + /** + * Implements WrapperInterface::setValue(). + * + * @param mixed $value + */ + public function setValue($value) { + $this->value = $value; + } + + /** + * Implements WrapperInterface::getString(). + * + * @return string + */ + public function getString() { + return (string) $this->getValue(); + } +} diff --git a/core/lib/Drupal/Core/TypedData/WrapperInterface.php b/core/lib/Drupal/Core/TypedData/WrapperInterface.php new file mode 100644 index 0000000..75acaf5 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/WrapperInterface.php @@ -0,0 +1,83 @@ + 'Configuration metadata', + 'description' => 'Tests Metadata for configuration objects.', + 'group' => 'Configuration', + ); + } + + /** + * Tests the basic metadata retrieval layer. + */ + function testBasicMetadata() { + // Simple case, straight metadata. + $metadata = config_metadata('system.maintenance'); + $expected = array( + 'enabled' => array( + 'label' => 'Put site into maintenance mode', + 'type' => 'boolean' + ), + 'message' => array( + 'label' => 'Message to display when in maintenance mode', + 'type' => 'string' + ), + ); + $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for system.maintenance'); + // More complex case, fallback to parent name. + $metadata = config_metadata('image.style.large'); + $expected = array( + 'name' => array( + 'label' => 'Machine name', + 'type' => 'machine_name' + ), + 'label' => array( + 'label' => 'Label', + 'type' => 'string' + ), + 'effects' => array( + 'label' => 'Style effects', + 'type' => 'config_nested', + 'nested.base' => 'image.style.effects.*', + 'nested.name' => 'name' + ) + ); + $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for image.style.large'); + + // Most complex case, fallback to parent name with merging + $metadata = config_metadata('image.style.effects.scale'); + $expected = array( + 'name' => array( + 'label' => 'Style name' + ), + 'data' => array( + 'label' => 'Data', + 'width' => array( + 'label' => 'Width', + 'type' => 'integer' + ), + 'height' => array( + 'label' => 'Height', + 'type' => 'integer', + ), + 'upscale' => array( + 'label' => 'Upscale', + 'type' => 'boolean', + ) + ), + 'weight' => array( + 'label' => 'Weight', + 'type' => 'integer' + ), + 'ieid' => array( + 'label' => 'IEID', + 'type' => 'ieid', + ), + + ); + $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for image.style.effects.scale'); + + } + + /** + * Tests metadata applied to configuration objects. + */ + function testConfigMetadata() { + // Try some simple properties. + $meta = config_wrapper('system.site'); + $property = $meta->get('name'); + $this->assertTrue(is_a($property, 'Drupal\Core\TypedData\Type\String'), 'Got the right wrapper fo the site name property.'); + $this->assertEqual($property->getType(), 'string', 'Got the right string type for site name data.'); + $this->assertEqual($property->getValue(), 'Drupal', 'Got the right string value for site name data.'); + + $property = $meta->get('page.front'); + $this->assertTrue(is_a($property, 'Drupal\Core\TypedData\Type\Undefined'), 'Got the right wrapper fo the page.front property.'); + $this->assertEqual($property->getType(), 'path', 'Got the right string type for page.front data.'); + $this->assertEqual($property->getValue(), 'user', 'Got the right value for page.front data.'); + + // Check nested array of properties. + $list = $meta->get('page'); + $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.'); + + // 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->getProperties()); + $effect = $wrapper->get('effects.' . $ieid); + + $this->assertTrue(count($effect['data']) && $effect['name']->getValue() == 'image.style.effects.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.'); + $this->assertEqual($effect['data']['width']->getValue(), 480, 'Got the right value for the scale effect width.' ); + + // Finally update some object using a configuration wrapper. + $new_slogan = 'Site slogan for testing configuration metadata'; + $wrapper = config_wrapper('system.site'); + $wrapper->set('slogan', $new_slogan); + $site_slogan = $wrapper->get('slogan'); + $this->assertEqual($site_slogan->getValue(), $new_slogan, 'Successfully updated the contained configuration data'); + } + +} diff --git a/core/modules/image/config/image.style.large.yml b/core/modules/image/config/image.style.large.yml index 012af17..99e1056 100644 --- a/core/modules/image/config/image.style.large.yml +++ b/core/modules/image/config/image.style.large.yml @@ -2,7 +2,7 @@ name: large label: Large (480x480) effects: ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d: - name: image_scale + name: image.style.effects.scale data: width: '480' height: '480' diff --git a/core/modules/image/config/image.style.medium.yml b/core/modules/image/config/image.style.medium.yml index 941ab85..1b5ad6e 100644 --- a/core/modules/image/config/image.style.medium.yml +++ b/core/modules/image/config/image.style.medium.yml @@ -2,7 +2,7 @@ name: medium label: Medium (220x220) effects: bddf0d06-42f9-4c75-a700-a33cafa25ea0: - name: image_scale + name: image.style.effects.scale data: width: '220' height: '220' diff --git a/core/modules/image/config/image.style.thumbnail.yml b/core/modules/image/config/image.style.thumbnail.yml index a454200..19f8652 100644 --- a/core/modules/image/config/image.style.thumbnail.yml +++ b/core/modules/image/config/image.style.thumbnail.yml @@ -2,7 +2,7 @@ name: thumbnail label: Thumbnail (100x100) effects: 1cfec298-8620-4749-b100-ccb6c4500779: - name: image_scale + name: image.style.effects.scale data: width: '100' height: '100' diff --git a/core/modules/image/image.effects.inc b/core/modules/image/image.effects.inc index 35a6a74..d491796 100644 --- a/core/modules/image/image.effects.inc +++ b/core/modules/image/image.effects.inc @@ -10,7 +10,7 @@ */ function image_image_effect_info() { $effects = array( - 'image_resize' => array( + 'image.style.effects.resize' => array( 'label' => t('Resize'), 'help' => t('Resizing will make images an exact set of dimensions. This may cause images to be stretched or shrunk disproportionately.'), 'effect callback' => 'image_resize_effect', @@ -18,7 +18,7 @@ function image_image_effect_info() { 'form callback' => 'image_resize_form', 'summary theme' => 'image_resize_summary', ), - 'image_scale' => array( + 'image.style.effects.scale' => array( 'label' => t('Scale'), 'help' => t('Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.'), 'effect callback' => 'image_scale_effect', @@ -26,7 +26,7 @@ function image_image_effect_info() { 'form callback' => 'image_scale_form', 'summary theme' => 'image_scale_summary', ), - 'image_scale_and_crop' => array( + 'image.style.effects.scale_and_crop' => array( 'label' => t('Scale and crop'), 'help' => t('Scale and crop will maintain the aspect-ratio of the original image, then crop the larger dimension. This is most useful for creating perfectly square thumbnails without stretching the image.'), 'effect callback' => 'image_scale_and_crop_effect', @@ -34,7 +34,7 @@ function image_image_effect_info() { 'form callback' => 'image_resize_form', 'summary theme' => 'image_resize_summary', ), - 'image_crop' => array( + 'image.style.effects.crop' => array( 'label' => t('Crop'), 'help' => t('Cropping will remove portions of an image to make it the specified dimensions.'), 'effect callback' => 'image_crop_effect', @@ -42,13 +42,13 @@ function image_image_effect_info() { 'form callback' => 'image_crop_form', 'summary theme' => 'image_crop_summary', ), - 'image_desaturate' => array( + 'image.style.effects.desaturate' => array( 'label' => t('Desaturate'), 'help' => t('Desaturate converts an image to grayscale.'), 'effect callback' => 'image_desaturate_effect', 'dimensions passthrough' => TRUE, ), - 'image_rotate' => array( + 'image.style.effects.rotate' => array( 'label' => t('Rotate'), 'help' => t('Rotating an image may cause the dimensions of an image to increase to fit the diagonal.'), 'effect callback' => 'image_rotate_effect', diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php index b52c4ac..3c17c0f 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php @@ -70,28 +70,28 @@ class ImageAdminStylesTest extends ImageFieldTestBase { $style_label = $this->randomString(); $style_path = 'admin/config/media/image-styles/edit/' . $style_name; $effect_edits = array( - 'image_resize' => array( + 'image.style.effects.resize' => array( 'data[width]' => 100, 'data[height]' => 101, ), - 'image_scale' => array( + 'image.style.effects.scale' => array( 'data[width]' => 110, 'data[height]' => 111, 'data[upscale]' => 1, ), - 'image_scale_and_crop' => array( + 'image.style.effects.scale_and_crop' => array( 'data[width]' => 120, 'data[height]' => 121, ), - 'image_crop' => array( + 'image.style.effects.crop' => array( 'data[width]' => 130, 'data[height]' => 131, 'data[anchor]' => 'center-center', ), - 'image_desaturate' => array( + 'image.style.effects.desaturate' => array( // No options for desaturate. ), - 'image_rotate' => array( + 'image.style.effects.rotate' => array( 'data[degrees]' => 5, 'data[random]' => 1, 'data[bgcolor]' => '#FFFF00', @@ -275,7 +275,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase { // Add a scale effect. $this->drupalGet('admin/config/media/image-styles/add'); $this->drupalPost(NULL, array('label' => 'Test style effect edit', 'name' => 'test_style_effect_edit'), t('Create new style')); - $this->drupalPost(NULL, array('new' => 'image_scale_and_crop'), t('Add')); + $this->drupalPost(NULL, array('new' => 'image.style.effects.scale_and_crop'), t('Add')); $this->drupalPost(NULL, array('data[width]' => '300', 'data[height]' => '200'), t('Add effect')); $this->assertText(t('Scale and crop 300x200')); @@ -290,13 +290,13 @@ class ImageAdminStylesTest extends ImageFieldTestBase { // Add another scale effect. $this->drupalGet('admin/config/media/image-styles/add'); $this->drupalPost(NULL, array('label' => 'Test style scale edit scale', 'name' => 'test_style_scale_edit_scale'), t('Create new style')); - $this->drupalPost(NULL, array('new' => 'image_scale'), t('Add')); + $this->drupalPost(NULL, array('new' => 'image.style.effects.scale'), t('Add')); $this->drupalPost(NULL, array('data[width]' => '12', 'data[height]' => '19'), t('Add effect')); // Edit the scale effect that was just added. $this->clickLink(t('edit')); $this->drupalPost(NULL, array('data[width]' => '24', 'data[height]' => '19'), t('Update effect')); - $this->drupalPost(NULL, array('new' => 'image_scale'), t('Add')); + $this->drupalPost(NULL, array('new' => 'image.style.effects.scale'), t('Add')); // Add another scale effect and make sure both exist. $this->drupalPost(NULL, array('data[width]' => '12', 'data[height]' => '19'), t('Add effect')); diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php index a3fff6b..ec8c618 100644 --- a/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php +++ b/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php @@ -58,7 +58,7 @@ class ImageDimensionsTest extends WebTestBase { // Scale an image that is wider than it is high. $effect = array( - 'name' => 'image_scale', + 'name' => 'image.style.effects.scale', 'data' => array( 'width' => 120, 'height' => 90, @@ -80,7 +80,7 @@ class ImageDimensionsTest extends WebTestBase { // Rotate 90 degrees anticlockwise. $effect = array( - 'name' => 'image_rotate', + 'name' => 'image.style.effects.rotate', 'data' => array( 'degrees' => -90, 'random' => FALSE, @@ -101,7 +101,7 @@ class ImageDimensionsTest extends WebTestBase { // Scale an image that is higher than it is wide (rotated by previous effect). $effect = array( - 'name' => 'image_scale', + 'name' => 'image.style.effects.scale', 'data' => array( 'width' => 120, 'height' => 90, @@ -123,7 +123,7 @@ class ImageDimensionsTest extends WebTestBase { // Test upscale disabled. $effect = array( - 'name' => 'image_scale', + 'name' => 'image.style.effects.scale', 'data' => array( 'width' => 400, 'height' => 200, @@ -145,7 +145,7 @@ class ImageDimensionsTest extends WebTestBase { // Add a desaturate effect. $effect = array( - 'name' => 'image_desaturate', + 'name' => 'image.style.effects.desaturate', 'data' => array(), 'weight' => 4, ); @@ -163,7 +163,7 @@ class ImageDimensionsTest extends WebTestBase { // Add a random rotate effect. $effect = array( - 'name' => 'image_rotate', + 'name' => 'image.style.effects.rotate', 'data' => array( 'degrees' => 180, 'random' => TRUE, @@ -182,7 +182,7 @@ class ImageDimensionsTest extends WebTestBase { // Add a crop effect. $effect = array( - 'name' => 'image_crop', + 'name' => 'image.style.effects.crop', 'data' => array( 'width' => 30, 'height' => 30, @@ -204,7 +204,7 @@ class ImageDimensionsTest extends WebTestBase { // Rotate to a non-multiple of 90 degrees. $effect = array( - 'name' => 'image_rotate', + 'name' => 'image.style.effects.rotate', 'data' => array( 'degrees' => 57, 'random' => FALSE, diff --git a/core/modules/image/meta/image.style.%.yml b/core/modules/image/meta/image.style.%.yml new file mode 100644 index 0000000..82b09b7 --- /dev/null +++ b/core/modules/image/meta/image.style.%.yml @@ -0,0 +1,12 @@ +name: + label: 'Machine name' + type: 'machine_name' +label: + label: 'Label' + type: 'string' +effects: + label: 'Style effects' + type: 'config_nested' + nested.base: 'image.style.effects.*' + nested.name: 'name' + \ No newline at end of file diff --git a/core/modules/image/meta/image.style.effects.%.yml b/core/modules/image/meta/image.style.effects.%.yml new file mode 100644 index 0000000..d809908 --- /dev/null +++ b/core/modules/image/meta/image.style.effects.%.yml @@ -0,0 +1,10 @@ +name: + label: 'Style name' +data: + label: 'Data' +weight: + label: 'Weight' + type: 'integer' +ieid: + label: 'IEID' + type: 'ieid' diff --git a/core/modules/image/meta/image.style.effects.scale.yml b/core/modules/image/meta/image.style.effects.scale.yml new file mode 100644 index 0000000..3fda284 --- /dev/null +++ b/core/modules/image/meta/image.style.effects.scale.yml @@ -0,0 +1,10 @@ +data: + width: + label: 'Width' + type: 'integer' + height: + label: 'Height' + type: 'integer' + upscale: + label: 'Upscale' + type: 'boolean' diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 20cc70b..45d9ded 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -2033,6 +2033,26 @@ abstract class WebTestBase extends TestBase { } /** + * Wraps a data value into a typed data wrapper and executes some basic + * assertions. + * + * @see drupal_wrap_data(). + */ + protected function drupalWrapData($definition, $value = NULL, $context = array()) { + // Save the type that was passed in so we can compare with it later. + $type = $definition['type']; + // Construct the wrapper. + $wrapper = drupal_wrap_data($definition, $value, $context); + // Assert the definition of the wrapper. + $this->assertTrue($wrapper instanceof \Drupal\Core\TypedData\WrapperInterface, 'Wrapper is an instance of the wrapper interface.'); + $definition = $wrapper->getDefinition(); + $this->assertTrue(!empty($definition['label']), $definition['label'] . ' wrapper definition was returned.'); + // Assert that the correct type was constructed. + $this->assertEqual($wrapper->getType(), $type, $definition['label'] . ' wrapper returned type.'); + return $wrapper; + } + + /** * Pass if the internal browser's URL matches the given path. * * @param $path diff --git a/core/modules/system/lib/Drupal/system/Tests/TypedData/DataWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/DataWrapperTest.php new file mode 100644 index 0000000..23dd238 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/DataWrapperTest.php @@ -0,0 +1,130 @@ + 'Test data wrappers', + 'description' => 'Tests the functionality of all core data wrappers.', + 'group' => 'Typed Data API', + ); + } + + /** + * Tests the basics around constructing and working with data wrappers. + */ + public function testGetAndSet() { + // Boolean type. + $wrapper = $this->drupalWrapData(array('type' => 'boolean'), TRUE); + $this->assertTrue($wrapper->getValue() === TRUE, 'Boolean value was fetched.'); + $wrapper->setValue(FALSE); + $this->assertTrue($wrapper->getValue() === FALSE, 'Boolean value was changed.'); + $this->assertTrue(is_string($wrapper->getString()), 'Boolean value was converted to string'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'Boolean wrapper is null-able.'); + + // String type. + $value = $this->randomString(); + $wrapper = $this->drupalWrapData(array('type' => 'string'), $value); + $this->assertTrue($wrapper->getValue() === $value, 'String value was fetched.'); + $new_value = $this->randomString(); + $wrapper->setValue($new_value); + $this->assertTrue($wrapper->getValue() === $new_value, 'String value was changed.'); + // Funky test. + $this->assertTrue(is_string($wrapper->getString()), 'String value was converted to string'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'String wrapper is null-able.'); + + // Integer type. + $value = rand(); + $wrapper = $this->drupalWrapData(array('type' => 'integer'), $value); + $this->assertTrue($wrapper->getValue() === $value, 'Integer value was fetched.'); + $new_value = rand(); + $wrapper->setValue($new_value); + $this->assertTrue($wrapper->getValue() === $new_value, 'Integer value was changed.'); + $this->assertTrue(is_string($wrapper->getString()), 'Integer value was converted to string'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'Integer wrapper is null-able.'); + + // Decimal type. + $value = 123.45; + $wrapper = $this->drupalWrapData(array('type' => 'decimal'), $value); + $this->assertTrue($wrapper->getValue() === $value, 'Decimal value was fetched.'); + $new_value = 678.90; + $wrapper->setValue($new_value); + $this->assertTrue($wrapper->getValue() === $new_value, 'Decimal value was changed.'); + $this->assertTrue(is_string($wrapper->getString()), 'Decimal value was converted to string'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'Decimal wrapper is null-able.'); + + // Date type. + $value = new DateTime('@' . REQUEST_TIME); + $wrapper = $this->drupalWrapData(array('type' => 'date'), $value); + $this->assertTrue($wrapper->getValue() === $value, 'Date value was fetched.'); + $new_value = REQUEST_TIME + 1; + $wrapper->setValue($new_value); + $this->assertTrue($wrapper->getValue()->getTimestamp() === $new_value, 'Date value was changed and set by timestamp.'); + $wrapper->setValue('2000-01-01'); + $this->assertTrue($wrapper->getValue()->format('Y-m-d') == '2000-01-01', 'Date value was changed and set by date string.'); + $this->assertTrue(is_string($wrapper->getString()), 'Date value was converted to string'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'Date wrapper is null-able.'); + + // Duration type. + $value = new DateInterval('PT20S'); + $wrapper = $this->drupalWrapData(array('type' => 'duration'), $value); + $this->assertTrue($wrapper->getValue() === $value, 'Duration value was fetched.'); + $wrapper->setValue(10); + $this->assertTrue($wrapper->getValue()->s == 10, 'Duration value was changed and set by time span in seconds.'); + $wrapper->setValue('P40D'); + $this->assertTrue($wrapper->getValue()->d == 40, 'Duration value was changed and set by duration string.'); + $this->assertTrue(is_string($wrapper->getString()), 'Duration value was converted to string'); + // Test getting the string and passing it back as value. + $duration = $wrapper->getString(); + $wrapper->setValue($duration); + $this->assertEqual($wrapper->getString(), $duration, 'Duration formatted as string can be used to set the duration value.'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'Duration wrapper is null-able.'); + + // Generate some files that will be used to test the URI and the binary + // data types. + $files = $this->drupalGetTestFiles('image'); + + // URI type. + $wrapper = $this->drupalWrapData(array('type' => 'uri'), $files[0]->uri); + $this->assertTrue($wrapper->getValue() === $files[0]->uri, 'URI value was fetched.'); + $wrapper->setValue($files[1]->uri); + $this->assertTrue($wrapper->getValue() === $files[1]->uri, 'URI value was changed.'); + $this->assertTrue(is_string($wrapper->getString()), 'URI value was converted to string'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'URI wrapper is null-able.'); + + // Binary type. + $wrapper = $this->drupalWrapData(array('type' => 'binary'), $files[0]->uri); + $this->assertTrue(is_resource($wrapper->getValue()), 'Binary value was fetched.'); + // Try setting by URI. + $wrapper->setValue($files[1]->uri); + $this->assertEqual(is_resource($wrapper->getValue()), fopen($files[1]->uri, 'r'), 'Binary value was changed.'); + $this->assertTrue(is_string($wrapper->getString()), 'Binary value was converted to string'); + // Try setting by resource. + $wrapper->setValue(fopen($files[2]->uri, 'r')); + $this->assertEqual(is_resource($wrapper->getValue()), fopen($files[2]->uri, 'r'), 'Binary value was changed.'); + $this->assertTrue(is_string($wrapper->getString()), 'Binary value was converted to string'); + $wrapper->setValue(NULL); + $this->assertNull($wrapper->getValue(), 'Binary wrapper is null-able.'); + } +} diff --git a/core/modules/system/meta/system.maintenance.yml b/core/modules/system/meta/system.maintenance.yml new file mode 100644 index 0000000..0f4cd6d --- /dev/null +++ b/core/modules/system/meta/system.maintenance.yml @@ -0,0 +1,6 @@ +enabled: + label: 'Put site into maintenance mode' + type: 'boolean' +message: + label: 'Message to display when in maintenance mode' + type: 'string' diff --git a/core/modules/system/meta/system.site.yml b/core/modules/system/meta/system.site.yml new file mode 100644 index 0000000..7085ed3 --- /dev/null +++ b/core/modules/system/meta/system.site.yml @@ -0,0 +1,20 @@ +name: + label: 'Site name' + type: 'string' +mail: + label: 'Site mail' + type: 'email' +slogan: + label: 'Site slogan' + type: 'string' +page: + type: config_structure + 403: + label: 'Default 403 (access denied) page' + type: 'path' + 404: + label: 'Default 404 (not found) page' + type: 'path' + front: + label: 'Default front page' + type: 'path' \ No newline at end of file diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 4f87ff6..c2873ed 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -147,6 +147,63 @@ function hook_cron() { } /** + * Defines available data types for typed data wrappers. + * + * Typed data wrappers allow modules to support any kind of data based upon + * pre-defined primitive types and interfaces for data structures and lists. + * + * Defined data types may map to one of the pre-defined primitive types in + * \Drupal\Core\TypedData\Primitive or may be data structures, containing one or + * more data properties. Wrapper classes of data structures have to implement + * the \Drupal\Core\TypedData\StructureInterface. Further interfaces + * that may be implemented are: + * - \Drupal\Core\TypedData\AccessibleInterface + * - \Drupal\Core\TypedData\StructureTranslatableInterface + * + * Furthermore, lists of data items are represented with wrappers implementing + * the \Drupal\Core\TypedData\ListInterface, for which the class may be + * specified using the 'list class' key. + * + * @return array + * An associative array where the key is the data type name and the value is + * again an associative array. Supported keys are: + * - label: The human readable label of the data type. + * - class: The associated typed data wrapper class. Must implement the + * \Drupal\Core\TypedData\WrapperInterface. + * - list class: (optional) A typed data wrapper class used to wrap multiple + * data items of the type. Must implement the + * \Drupal\Core\TypedData\ListInterface. + * - primitive type: (optional) Maps the data type to one of the pre-defined + * primitive types in \Drupal\Core\TypedData\Primitive. If set, it must be + * a constant defined by \Drupal\Core\TypedData\Primitive such as + * \Drupal\Core\TypedData\Primitive::String. + * + * @see drupal_wrap_data() + * @see drupal_get_data_type_info() + */ +function hook_data_type_info() { + return array( + 'email' => array( + 'label' => t('Email'), + 'class' => '\Drupal\email\Type\Email', + 'primitive type' => \Drupal\Core\TypedData\Primitive::String, + ), + ); +} + +/** + * Alter available data types for typed data wrappers. + * + * @param array $data_types + * An array of data type information. + * + * @see hook_data_type_info() + */ +function hook_data_type_info_alter(&$data_types) { + $data_types['email']['class'] = '\Drupal\mymodule\Type\Email'; +} + +/** * Declare queues holding items that need to be run periodically. * * While there can be only one hook_cron() process running at the same time, diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 6fcfa84..f09f40f 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -6,6 +6,7 @@ */ use Drupal\Core\Utility\ModuleInfo; +use Drupal\Core\TypedData\Primitive; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; @@ -1942,6 +1943,118 @@ function system_stream_wrappers() { } /** + * Implements hook_data_type_info(). + */ +function system_data_type_info() { + return array( + 'boolean' => array( + 'label' => t('Boolean'), + 'class' => '\Drupal\Core\TypedData\Type\Boolean', + 'primitive type' => Primitive::BOOLEAN, + ), + 'string' => array( + 'label' => t('String'), + 'class' => '\Drupal\Core\TypedData\Type\String', + 'primitive type' => Primitive::STRING, + ), + 'integer' => array( + 'label' => t('Integer'), + 'class' => '\Drupal\Core\TypedData\Type\Integer', + 'primitive type' => Primitive::INTEGER, + ), + 'decimal' => array( + 'label' => t('Decimal'), + 'class' => '\Drupal\Core\TypedData\Type\Decimal', + 'primitive type' => Primitive::DECIMAL, + ), + 'date' => array( + 'label' => t('Date'), + 'class' => '\Drupal\Core\TypedData\Type\Date', + 'primitive type' => Primitive::DATE, + ), + 'duration' => array( + 'label' => t('Duration'), + 'class' => '\Drupal\Core\TypedData\Type\Duration', + 'primitive type' => Primitive::DURATION, + ), + 'uri' => array( + 'label' => t('URI'), + 'class' => '\Drupal\Core\TypedData\Type\Uri', + 'primitive type' => Primitive::URI, + ), + 'binary' => array( + 'label' => t('Binary'), + 'class' => '\Drupal\Core\TypedData\Type\Binary', + 'primitive type' => Primitive::BINARY, + ), + 'language' => array( + 'label' => t('Language'), + 'description' => t('A language object.'), + 'class' => '\Drupal\Core\TypedData\Type\Language', + ), + 'entity' => array( + 'label' => t('Entity'), + 'description' => t('All kind of entities, e.g. nodes, comments or users.'), + 'class' => '\Drupal\Core\Entity\Property\EntityWrapper', + ), + 'entity_translation' => array( + 'label' => t('Entity translation'), + 'description' => t('A translation of an entity'), + 'class' => '\Drupal\Core\Entity\EntityTranslation', + ), + 'boolean_item' => array( + 'label' => t('Boolean item'), + 'description' => t('An entity property containing a boolean value.'), + 'class' => '\Drupal\Core\Entity\Property\BooleanItem', + 'list class' => '\Drupal\Core\Entity\Property\ItemList', + ), + 'string_item' => array( + 'label' => t('String item'), + 'description' => t('An entity property containing a string value.'), + 'class' => '\Drupal\Core\Entity\Property\StringItem', + 'list class' => '\Drupal\Core\Entity\Property\ItemList', + ), + 'integer_item' => array( + 'label' => t('Integer item'), + 'description' => t('An entity property containing an integer value.'), + 'class' => '\Drupal\Core\Entity\Property\IntegerItem', + 'list class' => '\Drupal\Core\Entity\Property\ItemList', + ), + 'date_item' => array( + 'label' => t('Date item'), + 'description' => t('An entity property containing a date value.'), + 'class' => '\Drupal\Core\Entity\Property\DateItem', + 'list class' => '\Drupal\Core\Entity\Property\ItemList', + ), + 'language_item' => array( + 'label' => t('Language item'), + 'description' => t('An entity property referencing a language.'), + 'class' => '\Drupal\Core\Entity\Property\LanguageItem', + 'list class' => '\Drupal\Core\Entity\Property\ItemList', + ), + 'entityreference_item' => array( + 'label' => t('Entity reference item'), + 'description' => t('An entity property containing an entity reference.'), + 'class' => '\Drupal\Core\Entity\Property\EntityReferenceItem', + 'list class' => '\Drupal\Core\Entity\Property\ItemList', + ), + ); +} + +/** + * Retrieve a blocked IP address from the database. + * + * @param $iid integer + * The ID of the blocked IP address to retrieve. + * + * @return + * The blocked IP address from the database as an array. + */ +function blocked_ip_load($iid) { + return db_query("SELECT * FROM {blocked_ips} WHERE iid = :iid", array(':iid' => $iid))->fetchAssoc(); +} + +/** * Menu item access callback - only enabled themes can be accessed. */ function _system_themes_access($theme) {