diff --git a/core/includes/config.inc b/core/includes/config.inc index 06e0e67e910..ce6f0b795fd 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -317,3 +317,16 @@ function config_get_entity_type_by_name($name) { }); return key($entities); } + +/* + * Returns the typed config manager service. + * + * Use the typed data manager service for creating typed configuration objects. + * + * @see Drupal\Core\TypedData\TypedDataManager::create() + * + * @return Drupal\Core\TypedData\TypedConfigManager + */ +function config_typed() { + return drupal_container()->get('config.typed'); +} diff --git a/core/lib/Drupal/Core/Config/Schema/ArrayElement.php b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php new file mode 100644 index 00000000000..f801c28724b --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/ArrayElement.php @@ -0,0 +1,117 @@ +elements)) { + $this->elements = $this->parse(); + } + return $this->elements; + } + + /** + * 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(); + } + + /** + * Builds an array of contained elements. + * + * @return array + * Array of \Drupal\Core\Config\Schema\ArrayElement objects. + */ + protected abstract function parse(); + + /** + * Implements TypedDataInterface::validate(). + */ + public function validate() { + foreach ($this->getElements() as $element) { + if (!$element->validate()) { + return FALSE; + } + } + return TRUE; + } + + /** + * Implements ArrayAccess::offsetExists(). + */ + public function offsetExists($offset) { + return array_key_exists($offset, $this->getElements()); + } + + /** + * Implements ArrayAccess::offsetGet(). + */ + public function offsetGet($offset) { + $elements = $this->getElements(); + return $elements[$offset]; + } + + /** + * Implements ArrayAccess::offsetSet(). + */ + public function offsetSet($offset, $value) { + if ($value instanceof TypedDataInterface) { + $value = $value->getValue(); + } + $this->value[$offset] = $value; + unset($this->elements); + } + + /** + * Implements ArrayAccess::offsetUnset(). + */ + public function offsetUnset($offset) { + unset($this->value[$offset]); + unset($this->elements); + } + + /** + * Implements Countable::count(). + */ + public function count() { + return count($this->getElements()); + } + + /** + * Implements IteratorAggregate::getIterator(); + */ + public function getIterator() { + return new ArrayIterator($this->getElements()); + } + +} diff --git a/core/lib/Drupal/Core/Config/Schema/Element.php b/core/lib/Drupal/Core/Config/Schema/Element.php new file mode 100644 index 00000000000..efe7147c869 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/Element.php @@ -0,0 +1,31 @@ +create($definition, $data, $key, $this); + } + +} diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php new file mode 100644 index 00000000000..c09d9ad2873 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php @@ -0,0 +1,125 @@ +definition['mapping'] as $key => $definition) { + if (isset($this->value[$key])) { + $elements[$key] = $this->parseElement($key, $this->value[$key], $definition); + } + } + return $elements; + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::get(). + */ + public function get($property_name) { + $elements = $this->getElements(); + if (isset($elements[$property_name])) { + return $elements[$property_name]; + } + 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->getElements(); + } + + /** + * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues(). + */ + public function getPropertyValues() { + return $this->getValue(); + } + + /** + * 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) { + if (isset($this->definition['mapping'][$name])) { + return $this->definition['mapping'][$name]; + } + else { + return 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/Schema/Parser.php b/core/lib/Drupal/Core/Config/Schema/Parser.php new file mode 100644 index 00000000000..9569244a179 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/Parser.php @@ -0,0 +1,43 @@ + 'any'); + } + else { + $definition += array('type' => 'str'); + } + } + // Create typed data object. + config_typed()->create($definition, $data, $name, $parent); + } + + /** + * Validate configuration data against schema data. + */ + static function validate($config_data, $schema_data) { + return self::parse($config_data, $schema_data)->validate(); + } + + static function getDefinition($type, $data) { + return config_definition($type); + } +} + diff --git a/core/lib/Drupal/Core/Config/Schema/Property.php b/core/lib/Drupal/Core/Config/Schema/Property.php new file mode 100644 index 00000000000..d7e4b6064be --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/Property.php @@ -0,0 +1,22 @@ +value); + } + +} diff --git a/core/lib/Drupal/Core/Config/Schema/SchemaDiscovery.php b/core/lib/Drupal/Core/Config/Schema/SchemaDiscovery.php new file mode 100644 index 00000000000..9a08992d627 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/SchemaDiscovery.php @@ -0,0 +1,111 @@ +storage = $storage; + // Load definitions for all enabled modules. + foreach (module_list() as $module) { + $this->loadSchema($module); + } + // @todo Load definitions for all enabled themes. + + } + + /** + * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition(). + */ + public function getDefinition($base_plugin_id) { + if (isset($this->definitions[$base_plugin_id])) { + $type = $base_plugin_id; + } + elseif (strpos($base_plugin_id, '.') && ($name = $this->getFallbackName($base_plugin_id)) && isset($this->definitions[$name])) { + // Found a generic name, replacing the last element by '*'. + $type = $name; + } + else { + // If we don't have definition, return the 'default' element. + // This should map to 'undefined' type by default, unless overridden. + $type = 'default'; + } + $definition = $this->definitions[$type]; + // Check whether this type is an extension of another one and compile it. + if (isset($definition['type'])) { + $merge = $this->getDefinition($definition['type']); + $definition = NestedArray::mergeDeep($merge, $definition); + // Unset type so we try the merge only once per type. + unset($definition['type']); + $this->definitions[$type] = $definition; + } + return $definition; + } + + /** + * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions(). + */ + public function getDefinitions() { + return $this->definitions; + } + + /** + * Load schema for module / theme. + */ + protected function loadSchema($component) { + if ($schema = $this->storage->read($component . '.schema')) { + foreach ($schema as $type => $definition) { + $this->definitions[$type] = $definition; + + } + } + } + + /** + * Gets fallback metadata name. + * + * @param string $name + * Configuration name or key. + * + * @return string + * Same name with the last part replaced by the filesystem marker. + */ + protected static function getFallbackName($name) { + $replaced = preg_replace('/\.[^.]+$/', '.' . '*', $name); + if ($replaced != $name) { + return $replaced; + } + } +} diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php new file mode 100644 index 00000000000..0301b0c0770 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php @@ -0,0 +1,43 @@ +getItemDefinition(); + $elements = array(); + foreach ($this->value as $key => $value) { + $elements[$key] = $this->parseElement($key, $value, $definition); + } + return $elements; + } + + /** + * Implements Drupal\Core\TypedData\ListInterface::isEmpty(). + */ + public function isEmpty() { + return empty($this->value); + } + + /** + * Implements Drupal\Core\TypedData\ListInterface::getItemDefinition(). + */ + public function getItemDefinition() { + return $this->definition['sequence'][0]; + } + +} diff --git a/core/lib/Drupal/Core/Config/TypedConfigElementFactory.php b/core/lib/Drupal/Core/Config/TypedConfigElementFactory.php new file mode 100644 index 00000000000..7ff51298579 --- /dev/null +++ b/core/lib/Drupal/Core/Config/TypedConfigElementFactory.php @@ -0,0 +1,28 @@ +discovery->getDefinition($plugin_id); + $configuration += $type_definition; + return parent::createInstance($plugin_id, $configuration, $name, $parent); + } +} diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php new file mode 100644 index 00000000000..c06ec511dc3 --- /dev/null +++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php @@ -0,0 +1,151 @@ +storage = $storage; + $this->discovery = new SchemaDiscovery($storage); + $this->factory = new TypedConfigElementFactory($this->discovery); + } + + /** + * Gets typed configuration data. + * + * @param string $name + * Configuration object name. + * + * @return Drupal\Core\Config\Schema\Element + * Typed configuration element. + */ + public function get($name) { + $data = $this->storage->read($name); + $definition = $this->getDefinition($name); + return $this->create($definition, $data); + } + + /** + * Overrides Drupal\Core\TypedData\TypedDataManager::create() + * + * Fills in default type and does variable replacement. + */ + public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) { + if (!isset($definition['type'])) { + // Set default type 'str' if possible. If not it will be 'any'. + if (is_string($value)) { + $definition['type'] = 'str'; + } + else { + $definition['type'] = 'any'; + } + } + elseif (strpos($definition['type'], ']')) { + // Replace variable names in definition. + $replace = is_array($value) ? $value : array(); + if (isset($parent)) { + $replace['%parent'] = $parent->getValue(); + } + if (isset($name)) { + $replace['%key'] = $name; + } + $definition['type'] = $this->replaceName($definition['type'], $replace); + } + // Create typed config object. + return parent::create($definition, $value, $name, $parent); + } + + /** + * Replaces variables in configuration name. + * + * The configuration name may contain one or more variables to be replaced, + * enclosed in square brackets like '[name]' and will follow the replacement + * rules defined by the replaceVariable() method. + * + * @param string $name + * Configuration name with variables in square brackets. + * @param mixed $data + * Configuration data for the element. + * @return string + * Configuration name with variables replaced. + */ + protected static function replaceName($name, $data) { + if (preg_match_all("/\[(.*)\]/U", $name, $matches)) { + // Build our list of '[value]' => replacement. + foreach (array_combine($matches[0], $matches[1]) as $key => $value) { + $replace[$key] = self::replaceVariable($value, $data); + } + return strtr($name, $replace); + } + else { + return $name; + } + } + + /** + * Replaces variable values in included names with configuration data. + * + * Variable values are nested configuration keys that will be replaced by + * their value or some of these special strings: + * - '%key', will be replaced by the element's key. + * - '%parent', to reference the parent element. + * + * There may be nested configuration keys separated by dots or more complex + * patterns like '%parent.name' which references the 'name' value of the + * parent element. + * + * Example patterns: + * - 'name.subkey', indicates a nested value of the current element. + * - '%parent.name', will be replaced by the 'name' value of the parent. + * - '%parent.%key', will be replaced by the parent element's key. + * + * @param string $value + * Variable value to be replaced. + * + * @return string + * The replaced value if a replacement found or the original value if not. + */ + protected static function replaceVariable($value, $data) { + $parts = explode('.', $value); + // Process each value part, one at a time. + while ($name = array_shift($parts)) { + if (!is_array($data) || !isset($data[$name])) { + // Key not found, return original value + return $value; + } + elseif (!$parts) { + // If no more parts left, this is the final property. + return (string)$data[$name]; + } + else { + // Get nested value and continue processing. + $data = $data[$name]; + } + } + } + +} diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php index a15279e0871..4684695132e 100644 --- a/core/lib/Drupal/Core/CoreBundle.php +++ b/core/lib/Drupal/Core/CoreBundle.php @@ -60,6 +60,10 @@ class CoreBundle extends Bundle { ->register('config.storage.staging', 'Drupal\Core\Config\FileStorage') ->addArgument(config_get_config_directory(CONFIG_STAGING_DIRECTORY)); + // Register the typed configuration data manager. + $container->register('config.typed', 'Drupal\Core\Config\TypedConfigManager') + ->addArgument(new Reference('config.storage')); + // Register the service for the default database connection. $container->register('database', 'Drupal\Core\Database\Connection') ->setFactoryClass('Drupal\Core\Database\Database') diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php new file mode 100644 index 00000000000..93a7b559e9d --- /dev/null +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php @@ -0,0 +1,146 @@ + 'Configuration schema', + 'description' => 'Tests Metadata for configuration objects.', + 'group' => 'Configuration', + ); + } + + public function setUp() { + parent::setUp(); + config_install_default_config('module', 'system'); + config_install_default_config('module', 'image'); + } + + /** + * Tests the basic metadata retrieval layer. + */ + function testSchemaMapping() { + // Simple case, straight metadata. + $definition = config_typed()->getDefinition('system.maintenance'); + $expected = array(); + $expected['label'] = 'Maintenance mode'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['mapping']['enabled'] = array( + 'label' => 'Put site into maintenance mode', + 'type' => 'boolean' + ); + $expected['mapping']['message'] = array( + 'label' => 'Message to display when in maintenance mode', + 'type' => 'text', + ); + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance'); + + // More complex case, generic type. Metadata for image style. + $definition = config_typed()->getDefinition('image.style.large'); + $expected = array(); + $expected['label'] = 'Image style'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['mapping']['name']['type'] = 'string'; + $expected['mapping']['label']['type'] = 'label'; + $expected['mapping']['effects']['type'] = 'sequence'; + $expected['mapping']['effects']['sequence'][0]['type'] = 'mapping'; + $expected['mapping']['effects']['sequence'][0]['mapping']['name']['type'] = 'string'; + $expected['mapping']['effects']['sequence'][0]['mapping']['data']['type'] = 'image.effect.[%parent.name]'; + $expected['mapping']['effects']['sequence'][0]['mapping']['weight']['type'] = 'integer'; + $expected['mapping']['effects']['sequence'][0]['mapping']['ieid']['type'] = 'string'; + + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.style.large'); + + // More complex, type based on a complex one. + $definition = config_typed()->getDefinition('image.effect.image_scale'); + // This should be the schema for image.effect.image_scale. + $expected = array(); + $expected['label'] = 'Image scale'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['mapping']['width']['type'] = 'integer'; + $expected['mapping']['width']['label'] = 'Width'; + $expected['mapping']['height']['type'] = 'integer'; + $expected['mapping']['height']['label'] = 'Height'; + $expected['mapping']['upscale']['type'] = 'boolean'; + $expected['mapping']['upscale']['label'] = 'Upscale'; + + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale'); + + // Most complex case, get metadata for actual configuration element. + $effects = config_typed()->get('image.style.medium')->get('effects'); + $definition = $effects['bddf0d06-42f9-4c75-a700-a33cafa25ea0']['data']->getDefinition(); + // This should be the schema for image.effect.image_scale, reuse previous one. + $expected['type'] = 'image.effect.image_scale'; + + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium'); + } + + /** + * Tests metadata applied to configuration objects. + */ + function testSchemaData() { + // Try some simple properties. + $meta = config_typed()->get('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(), 'label', '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')->get('front'); + $this->assertTrue(is_a($property, 'Drupal\Core\TypedData\Type\String'), 'Got the right wrapper fo the page.front property.'); + $this->assertEqual($property->getType(), 'path', 'Got the right type for page.front data (undefined).'); + $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.'); + + // 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_typed()->get('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->getValue()); + $effect = $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.'); + $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_typed()->get('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/contact/config/contact.schema.yml b/core/modules/contact/config/contact.schema.yml new file mode 100644 index 00000000000..f0b70bd3697 --- /dev/null +++ b/core/modules/contact/config/contact.schema.yml @@ -0,0 +1,35 @@ +# Configuration schema: contact.schema.yml + +# Schema for contact category (multiple) +contact.category.*: + type: mapping + mapping: + "id": + type: string + "label": + type: string + "recipients": + type: sequence + sequence: + - type: email + "reply": + type: string + "weight": + type: integer + +# Module settings +contact.settings: + type: mapping + mapping: + "default_category": + type: string + required: yes + "flood": + type: mapping + mapping: + "limit": + type: integer + "interval": + type: integer + "user_default_enabled": + type: boolean diff --git a/core/modules/image/config/image.schema.yml b/core/modules/image/config/image.schema.yml new file mode 100644 index 00000000000..f4c8fd0b340 --- /dev/null +++ b/core/modules/image/config/image.schema.yml @@ -0,0 +1,82 @@ +# Image module schema: image.schema.yml + +# Data types for image module. +image.size: + type: mapping + mapping: + "width": + type: integer + label: "Width" + "height": + type: integer + label: "Height" + +# Image styles (multiple). +# Plugin \Drupal\image\Plugin\Core\Entity\ImageStyle +image.style.*: + type: mapping + label: "Image style" + mapping: + "name": + type: string + "label": + type: label + "effects": + type: sequence + sequence: + - type: mapping + mapping: + "name": + type: string + "data": + type: image.effect.[%parent.name] + "weight": + type: integer + "ieid": + type: string + +# Image effects plugins: image.effect.% +# These are used in image styles. +image.effect.image_crop: + type: image.size + label: "Image crop" + mapping: + "anchor": + label: "Anchor" + +image.effect.image_resize: + type: image.size + label: "Image resize" + +image.effect.image_rotate: + type: mapping + label: "Image rotate" + mapping: + degrees: + type: integer + label: 'Rotation angle' + bgcolor: + label: 'Background color' + random: + type: boolean + label: 'Randomize' + +image.effect.image_scale: + type: image.size + label: "Image scale" + mapping: + "upscale": + type: boolean + label: "Upscale" + +image.effect.image_scale_and_crop: + type: image.size + label: "Image scale and crop" + +# Schema for configuration files of image module. +image.settings: + type: mapping + mapping: + "preview_image": + type: string + label: "Preview image" diff --git a/core/modules/system/config/system.schema.yml b/core/modules/system/config/system.schema.yml new file mode 100644 index 00000000000..351426019be --- /dev/null +++ b/core/modules/system/config/system.schema.yml @@ -0,0 +1,104 @@ +# Basic scalar data types from typed data. +boolean: + label: 'Boolean' + class: '\Drupal\Core\TypedData\Type\Boolean' +email: + label: 'Email' + class: '\Drupal\Core\TypedData\Type\Email' +integer: + label: 'Integer' + class: '\Drupal\Core\TypedData\Type\Integer' +string: + label: 'String' + class: '\Drupal\Core\TypedData\Type\String' +uri: + label: 'Uri' + class: '\Drupal\Core\TypedData\Type\Uri' + +# Basic data types for configuration. +undefined: + label: 'Undefined' + class: '\Drupal\Core\Config\Schema\Property' +mapping: + label: Mapping + class: '\Drupal\Core\Config\Schema\Mapping' +sequence: + label: Sequence + class: '\Drupal\Core\Config\Schema\Sequence' + +# Default mapping for unknown types or types not found. +default: + type: undefined + label: 'Unknown' + +# Simple extended data types: + +# Human readable string that must be plain text and editable with a text field. +label: + type: string + label: 'Label' + +# Internal Drupal path +path: + type: string + label: 'Path' + +# Human readable string that can contain multiple lines of text or HTML. +text: + type: string + label: 'Text' + +# Complex extended data types: + +# Mail text with subject and body parts. +mail: + type: mapping + label: "Mail" + mapping: + "subject": + type: text + label: "Subject" + "body": + type: text + label: "Body" + +# Schema for configuration files of system module: + +system.site: + type: mapping + label: 'Site information' + mapping: + "name": + label: "Site name" + type: label + "mail": + label: "Site mail" + type: email + "slogan": + label: "Site slogan" + type: text + "page": + type: mapping + mapping: + "403": + type: path + "404": + type: path + "front": + type: path + label: "Front page path" + "admin_compact_mode": + type: boolean + "weight_select_max": + type: integer + +system.maintenance: + type: mapping + label: 'Maintenance mode' + mapping: + "enabled": + type: boolean + label: "Put site into maintenance mode" + "message": + type: text + label: "Message to display when in maintenance mode" diff --git a/core/modules/user/config/user.schema.yml b/core/modules/user/config/user.schema.yml new file mode 100644 index 00000000000..6af74712609 --- /dev/null +++ b/core/modules/user/config/user.schema.yml @@ -0,0 +1,75 @@ +# User module schema: user.schema.yml + +# User mails. +user.mail: + type: mapping + mapping: + "cancel_confirm": + type: mail + label: "Account cancellation confirmation" + "password_reset": + type: mail + label: "Password reset email" + "register_admin_created": + type: mail + label: "Account created by administrator" + "register_no_approval_required": + type: mail + label: "Registration confirmation (No approval required)" + "register_pending_approval": + type: mail + label: "Registration confirmation (Pending approval)" + "status_activated": + type: mail + label: "Account activation" + "status_blocked": + type: mail + label: "Account blocked" + "status_canceled": + type: mail + label: "Account cancelled" + +# User settings. +user.settings: + type: mapping + mapping: + "admin_role": + type: string + label: "Administrator role" + "anonymous": + type: label + label: "Anonymous name" + "verify_mail": + type: boolean + label: "Verify mail" + "notify": + type: mapping + label: "Notify user" + mapping: + "cancel_confirm": + type: boolean + "password_reset": + type: boolean + "status_activated": + type: boolean + "status_blocked": + type: boolean + "status_cancelled": + type: boolean + "register_admin_created": + type: boolean + "register_no_approval_required": + type: boolean + "register_pending_approval": + type: boolean + "register": + type: string + enum: [visitors, admin_only, visitors_admin_approval] + "signatures": + type: boolean + label: "User signatures" + "cancel_method": + type: string + label: "User cancel method" + "password_reset_timeout": + type: integer diff --git a/core/modules/views/config/views.schema.yml b/core/modules/views/config/views.schema.yml new file mode 100644 index 00000000000..59c90a5866a --- /dev/null +++ b/core/modules/views/config/views.schema.yml @@ -0,0 +1,232 @@ +# View definition (multiple) +views.view.*: + type: mapping + label: 'View' + mapping: + disabled: + type: boolean + label: 'Disabled' + api_version: + label: 'API version' + module: + label: 'Module' + name: + label: 'Machine name' + description: + type: text + label: 'Administrative description' + tag: + label: 'Tag' + base_table: + label: 'Base table' + base_field: + label: 'Base field' + human_name: + type: label + label: 'Human readable name' + core: + label: 'Drupal version' + uuid: + label: 'UUID' + display: + type: sequence + label: 'Displays' + sequence: + - type: mapping + label: 'Display settings' + mapping: + id: + label: 'Machine name' + display_title: + type: text + label: 'Title' + display_plugin: + label: 'Display plugin' + position: + type: integer + label: 'Position' + display_options: + type: 'views.display.[%parent.display_plugin]' + +# Views display: common +# Options for Drupal\views\Plugin\views\display\DisplayPluginBase +views.display.*: + type: mapping + label: 'Display options' + mapping: + title: + type: text + label: 'Display title' + format: + label: 'Format' + fields: + type: sequence + label: 'Fields' + sequence: + - type: 'views.field.[table]-[field]' + pager: + type: mapping + label: 'Pager' + mapping: + type: + label: 'Pager type' + options: + type: mapping + label: 'Options' + mapping: + offset: + type: integer + label: 'Offset' + + exposed_form: + type: mapping + label: 'Exposed form' + mapping: + type: + label: 'Exposed form type' + access: + type: mapping + label: 'Access' + mapping: + type: + label: 'Access type' + other: + label: 'Other' + cache: + type: mapping + label: 'Cache' + mapping: + type: + label: 'Cache type' + sorts: + type: sequence + label: 'Sorts' + sequence: + - type: 'views.sort.[table]-[field]' + arguments: + type: sequence + label: 'Arguments' + sequence: + - type: 'views.argument.[table]-[field]' + filters: + type: sequence + label: 'Filters' + sequence: + - type: 'views.filter.[table]-[field]' + style: + type: mapping + label: 'Style' + mapping: + type: + label: 'Type' + row: + type: mapping + label: 'Row' + mapping: + type: + label: 'Row type' + options: + include: 'views.row.[%parent.type]' + query: + type: mapping + label: 'Query' + mapping: + type: + label: 'Query type' + options: + type: mapping + label: 'Query options' + mapping: + query_comment: + type: boolean + label: 'Query comment' + defaults: + type: mapping + label: 'Defaults' + mapping: + style_plugin: + label: 'Style plugin' + style_options: + type: 'views.style.[%parent.style_plugin]' + row_plugin: + label: 'Row plugin' + row_options: + type: 'views.style.[%parent.row_plugin]' + relationships: + type: sequence + label: 'Relationships' + sequence: + - type: 'views.relationship.[table]-[field]' + +# Options for Drupal\views\Plugin\views\display\PathPluginBase +views.display.PathPluginBase: + include: 'views.display.%' + type: mapping + mapping: + path: + type: string + label: 'Page path' + +# Views display plugin: Drupal\views\Plugin\views\display\Page +views.display.page: + type: 'views.display.PathPluginBase' + label: 'Page display options' + mapping: + menu: + type: mapping + label: 'Menu' + mapping: + type: + label: 'Type' + title: + type: text + label: 'Title' + description: + type: text + label: 'Description' + weight: + type: integer + label: 'Weight' + name: + label: 'Menu name' + context: + label: 'Context' + tab_options: + type: mapping + label: 'Tab options' + mapping: + type: + label: 'Type' + title: + type: text + label: 'Title' + description: + type: text + label: 'Description' + weight: + type: integer + label: 'Weight' + name: + label: 'Menu name' + +# Views display plugin: Drupal\views\Plugin\views\display\Block +views.display.block: + type: 'views.display.%' + label: 'Block display options' + mapping: + block_description: + type: text + label: 'Block name' + block_caching: + label: 'Block caching' + +# Views display plugin: Drupal\views\Plugin\views\display\Feed +views.display.feed: + type: 'views.display.PathPluginBase' + label: 'Feed display options' + mapping: + sitename_title: + type: boolean + label: 'Use the site name for the title' + displays: + label: 'The feed icon will be available only to the selected displays.'