Issue #1866610 by Jose Reyero, Gábor Hojtsy, effulgentsia, alexpott, aspilicious, YesCT, fago, sun, dawehner, heyrocker, yched, spearhead93: Introduce Kwalify-inspired schema format for configuration.

8.0.x
Dries 2013-01-31 17:09:08 -05:00
parent 2735dbb481
commit 94278ddd5a
17 changed files with 1362 additions and 0 deletions

View File

@ -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');
}

View File

@ -0,0 +1,117 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\Schema\ArrayElement.
*/
namespace Drupal\Core\Config\Schema;
use \ArrayAccess;
use \ArrayIterator;
use \Countable;
use \IteratorAggregate;
use \Traversable;
/**
* Defines a generic configuration element that contains multiple properties.
*/
abstract class ArrayElement extends Element implements IteratorAggregate, ArrayAccess, Countable {
/**
* Parsed elements.
*/
protected $elements;
/**
* Gets an array of contained elements.
*
* @return array
* Array of \Drupal\Core\Config\Schema\ArrayElement objects.
*/
protected function getElements() {
if (!isset($this->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());
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\Schema\Element.
*/
namespace Drupal\Core\Config\Schema;
use Drupal\Core\TypedData\ContextAwareTypedData;
/**
* Defines a generic configuration element.
*/
abstract class Element extends ContextAwareTypedData {
/**
* The configuration value.
*
* @var mixed
*/
protected $value;
/**
* Create typed config object.
*/
protected function parseElement($key, $data, $definition) {
return config_typed()->create($definition, $data, $key, $this);
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\Schema\Mapping.
*/
namespace Drupal\Core\Config\Schema;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\TypedData\ComplexDataInterface;
use \InvalidArgumentException;
/**
* Defines a mapping configuration element.
*
* Wraps configuration data and metadata allowing access to configuration data
* using the ComplexDataInterface API. This object may contain any number and
* type of nested properties.
*/
class Mapping extends ArrayElement implements ComplexDataInterface {
/**
* Overrides ArrayElement::parse()
*/
protected function parse() {
$elements = array();
foreach ($this->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);
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\Schema\Parser.
*/
namespace Drupal\Core\Config\Schema;
/**
* Parser.
*/
class Parser {
/**
* Parse configuration data against schema data.
*/
static function parse($data, $definition, $name = NULL, $parent = NULL) {
// Set default type depending on data and context.
if (!isset($definition['type'])) {
if (is_array($data) || !$context) {
$definition += array('type' => '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);
}
}

View File

@ -0,0 +1,22 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\Schema\Sequence.
*/
namespace Drupal\Core\Config\Schema;
/**
* Generic configuration property.
*/
class Property extends Element {
/**
* Implements TypedDataInterface::validate().
*/
public function validate() {
return isset($this->value);
}
}

View File

@ -0,0 +1,111 @@
<?php
/**
* @file
* Contains \Drupal\Config\Schema\SchemaDiscovery.
*/
namespace Drupal\Core\Config\Schema;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\Component\Utility\NestedArray;
/**
* A discovery mechanism that reads plugin definitions from schema data
* in YAML format.
*/
class SchemaDiscovery implements DiscoveryInterface {
/**
* A storage controller instance for reading configuration schema data.
*
* @var Drupal\Core\Config\StorageInterface
*/
protected $storage;
/**
* The array of plugin definitions, keyed by plugin id.
*
* @var array
*/
protected $definitions = array();
/**
* Public constructor.
*
* @param Drupal\Core\Config\StorageInterface $storage
* The storage controller object to use for reading schema data
*/
public function __construct($storage) {
$this->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;
}
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\Schema\Sequence.
*/
namespace Drupal\Core\Config\Schema;
use Drupal\Core\TypedData\ListInterface;
/**
* Defines a configuration element of type Sequence.
*/
class Sequence extends ArrayElement implements ListInterface {
/**
* Overrides ArrayElement::parse()
*/
protected function parse() {
$definition = $definition = $this->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];
}
}

View File

@ -0,0 +1,28 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\TypedConfigElementFactory.
*/
namespace Drupal\Core\Config;
use Drupal\Core\TypedData\TypedDataFactory;
/**
* A factory for typed config element objects.
*
* This factory merges the type definition into the element definition prior to
* creating the instance.
*/
class TypedConfigElementFactory extends TypedDataFactory {
/**
* Overrides Drupal\Core\TypedData\TypedDataFactory::createInstance().
*/
public function createInstance($plugin_id, array $configuration, $name = NULL, $parent = NULL) {
$type_definition = $this->discovery->getDefinition($plugin_id);
$configuration += $type_definition;
return parent::createInstance($plugin_id, $configuration, $name, $parent);
}
}

View File

@ -0,0 +1,151 @@
<?php
/**
* @file
* Contains \Drupal\Core\Config\TypedConfigManager.
*/
namespace Drupal\Core\Config;
use Drupal\Core\Config\Schema\SchemaDiscovery;
use Drupal\Core\TypedData\TypedDataManager;
/**
* Manages config type plugins.
*/
class TypedConfigManager extends TypedDataManager {
/**
* A storage controller instance for reading configuration data.
*
* @var Drupal\Core\Config\StorageInterface
*/
protected $storage;
/**
* Creates a new typed configuration manager.
*
* @param Drupal\Core\Config\StorageInterface $storage
* The storage controller object to use for reading schema data
*/
public function __construct($storage) {
$this->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];
}
}
}
}

View File

@ -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')

View File

@ -0,0 +1,146 @@
<?php
/**
* @file
* Contains \Drupal\config\Tests\ConfigSchemaTest.
*/
namespace Drupal\config\Tests;
use Drupal\Core\Config\TypedConfig;
use Drupal\simpletest\DrupalUnitTestBase;
/**
* Tests schema for configuration objects.
*/
class ConfigSchemaTest extends DrupalUnitTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('system', 'locale', 'image');
public static function getInfo() {
return array(
'name' => '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');
}
}

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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.'