diff --git a/core/core.services.yml b/core/core.services.yml index bffe0aa..49181d7 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -93,7 +93,7 @@ services: class: Drupal\Core\Config\ConfigFactory tags: - { name: persist } - arguments: ['@config.storage', '@config.context'] + arguments: ['@config.storage', '@config.context', '@config.typed'] config.storage.staging: class: Drupal\Core\Config\FileStorage factory_class: Drupal\Core\Config\FileStorageFactory diff --git a/core/includes/config.inc b/core/includes/config.inc index 8e9ce5e..d0f1d7b 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -67,6 +67,7 @@ function ($value) use ($name) { $config_factory = Drupal::service('config.factory'); $context = new FreeConfigContext(Drupal::service('event_dispatcher'), Drupal::service('uuid')); $target_storage = Drupal::service('config.storage'); + $typed_config = Drupal::service('config.typed'); $config_factory->enterContext($context); foreach ($config_to_install as $name) { // Only import new config. @@ -74,7 +75,7 @@ function ($value) use ($name) { continue; } - $new_config = new Config($name, $target_storage, $context); + $new_config = new Config($name, $target_storage, $context, $typed_config); $data = $source_storage->read($name); if ($data !== FALSE) { $new_config->setData($data); @@ -214,7 +215,7 @@ function config_get_entity_type_by_name($name) { * * @see \Drupal\Core\TypedData\TypedDataManager::create() * - * @return \Drupal\Core\TypedData\TypedConfigManager + * @return \Drupal\Core\Config\TypedConfigManager */ function config_typed() { return drupal_container()->get('config.typed'); diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 3cbbd82..52a0700 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -386,9 +386,16 @@ function install_begin_request(&$install_state) { ->setFactoryService(new Reference('config.context.factory')) ->setFactoryMethod('get'); + $container->register('config.storage.schema', 'Drupal\Core\Config\Schema\SchemaStorage'); + + $container->register('config.typed', 'Drupal\Core\Config\TypedConfigManager') + ->addArgument(new Reference('config.storage')) + ->addArgument(new Reference('config.storage.schema')); + $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') ->addArgument(new Reference('config.storage')) - ->addArgument(new Reference('config.context')); + ->addArgument(new Reference('config.context')) + ->addArgument(new Reference('config.typed')); // Register the 'language_manager' service. $container diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 807d930..a60ec4d 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -8,8 +8,10 @@ namespace Drupal\Core\Config; use Drupal\Component\Utility\NestedArray; +use Drupal\Component\Utility\String; use Drupal\Core\Config\ConfigNameException; use Drupal\Core\Config\Context\ContextInterface; +use Drupal\Core\Config\TypedConfigManager; /** * Defines the default configuration object. @@ -78,6 +80,20 @@ class Config { protected $isLoaded = FALSE; /** + * The config schema wrapper object for this configuration object. + * + * @var \Drupal\Core\Config\Schema\Element + */ + protected $schemaWrapper; + + /** + * The typed config manager. + * + * @var \Drupal\Core\Config\TypedConfigManager + */ + protected $typedConfigManager; + + /** * Constructs a configuration object. * * @param string $name @@ -87,11 +103,14 @@ class Config { * configuration data. * @param \Drupal\Core\Config\Context\ContextInterface $context * The configuration context used for this configuration object. + * @param \Drupal\Core\Config\TypedConfigManager $typed_config + * The typed configuration manager service. */ - public function __construct($name, StorageInterface $storage, ContextInterface $context) { + public function __construct($name, StorageInterface $storage, ContextInterface $context, TypedConfigManager $typed_config) { $this->name = $name; $this->storage = $storage; $this->context = $context; + $this->typedConfigManager = $typed_config; } /** @@ -404,6 +423,17 @@ public function load() { public function save() { // Validate the configuration object name before saving. static::validateName($this->name); + + // If there is a schema for this configuration object, cast all values to + // conform to the schema. + if ($this->typedConfigManager->hasConfigSchema($this->name)) { + // Ensure that the schema wrapper has the latest data. + $this->schemaWrapper = NULL; + foreach ($this->data as $key => $value) { + $this->data[$key] = $this->castValue($key, $value); + } + } + if (!$this->isLoaded) { $this->load(); } @@ -466,4 +496,108 @@ public function merge(array $data_to_merge) { $this->replaceData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE)); return $this; } + + /** + * Gets the schema wrapper for the whole configuration object. + * + * The schema wrapper is dependent on the configuration name and the whole + * data structure, so if the name or the data changes in any way, the wrapper + * should be reset. + * + * @return \Drupal\Core\Config\Schema\Element + */ + protected function getSchemaWrapper() { + if (!isset($this->schemaWrapper)) { + $definition = $this->typedConfigManager->getDefinition($this->name); + $this->schemaWrapper = $this->typedConfigManager->create($definition, $this->data); + } + return $this->schemaWrapper; + } + + /** + * Gets the definition for the configuration key. + * + * @param string $key + * A string that maps to a key within the configuration data. + * + * @return \Drupal\Core\Config\Schema\Element + */ + protected function getSchemaForKey($key) { + $parts = explode('.', $key); + $schema_wrapper = $this->getSchemaWrapper(); + if (count($parts) == 1) { + $schema = $schema_wrapper->get($key); + } + else { + $schema = clone $schema_wrapper; + foreach ($parts as $nested_key) { + if (!is_object($schema) || !method_exists($schema, 'get')) { + throw new ConfigException(String::format("Incomplete schema for !key key in configuration object !name.", array('!name' => $this->name, '!key' => $key))); + } + else { + $schema = $schema->get($nested_key); + } + } + } + return $schema; + } + + /** + * Casts the value to correct data type using the configuration schema. + * + * @param string $key + * A string that maps to a key within the configuration data. + * @param string $value + * Value to associate with the key. + * + * @return mixed + * The value cast to the type indicated in the schema. + */ + protected function castValue($key, $value) { + if ($value === NULL) { + return $value; + } + if (is_scalar($value)) { + try { + $class = get_class($this->getSchemaForKey($key)); + } + catch (\Exception $e) { + // @todo throw an exception due to an incomplete schema. Only possible + // once https://drupal.org/node/1910624 is complete. + $class = FALSE; + } + switch ($class) { + case 'Drupal\Core\TypedData\Plugin\DataType\String': + case 'Drupal\Core\TypedData\Plugin\DataType\Uri': + case 'Drupal\Core\TypedData\Plugin\DataType\Email': + case 'Drupal\Core\Config\Schema\Property': + $value = (string) $value; + break; + + case 'Drupal\Core\TypedData\Plugin\DataType\Integer': + $value = ($value !== '' ? (int) $value : NULL); + break; + + case 'Drupal\Core\TypedData\Plugin\DataType\Boolean': + $value = ($value !== '' ? (bool) $value : NULL); + break; + + case 'Drupal\Core\TypedData\Plugin\DataType\Float': + $value = ($value !== '' ? (float) $value : NULL); + break; + } + } + else { + // Any non-scalar value must be an array. + if (!is_array($value)) { + $value = (array) $value; + } + // Recurse into any nested keys. + foreach ($value as $nested_value_key => $nested_value) { + $value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value); + } + } + return $value; + } + } diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php index 60b3d0d..7b1e97c 100644 --- a/core/lib/Drupal/Core/Config/ConfigFactory.php +++ b/core/lib/Drupal/Core/Config/ConfigFactory.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Config; use Drupal\Core\Config\Context\ContextInterface; +use Drupal\Core\Config\TypedConfigManager; /** * Defines the configuration object factory. @@ -52,15 +53,25 @@ class ConfigFactory { protected $cache = array(); /** + * The typed config manager. + * + * @var \Drupal\Core\Config\TypedConfigManager + */ + protected $typedConfigManager; + + /** * Constructs the Config factory. * - * @param \Drupal\Core\Config\StorageInterface + * @param \Drupal\Core\Config\StorageInterface $storage * The configuration storage engine. - * @param \Drupal\Core\Config\Context\ContextInterface + * @param \Drupal\Core\Config\Context\ContextInterface $context * Configuration context object. + * @param \Drupal\Core\Config\TypedConfigManager $typed_config + * The typed configuration manager. */ - public function __construct(StorageInterface $storage, ContextInterface $context) { + public function __construct(StorageInterface $storage, ContextInterface $context, TypedConfigManager $typed_config) { $this->storage = $storage; + $this->typedConfigManager = $typed_config; $this->enterContext($context); } @@ -80,7 +91,7 @@ public function get($name) { return $this->cache[$cache_key]; } - $this->cache[$cache_key] = new Config($name, $this->storage, $context); + $this->cache[$cache_key] = new Config($name, $this->storage, $context, $this->typedConfigManager); return $this->cache[$cache_key]->init(); } @@ -115,7 +126,7 @@ public function loadMultiple(array $names) { $storage_data = $this->storage->readMultiple($names); foreach ($storage_data as $name => $data) { $cache_key = $this->getCacheKey($name, $context); - $this->cache[$cache_key] = new Config($name, $this->storage, $context); + $this->cache[$cache_key] = new Config($name, $this->storage, $context, $this->typedConfigManager); $this->cache[$cache_key]->initWithData($data); $list[$name] = $this->cache[$cache_key]; } @@ -172,7 +183,7 @@ public function rename($old_name, $new_name) { } else { // Create the config object if it's not yet loaded into the static cache. - $config = new Config($old_name, $this->storage, $context); + $config = new Config($old_name, $this->storage, $context, $this->typedConfigManager); } $this->cache[$new_cache_key] = $config; diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index f96d965..66f51e4 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Config; use Drupal\Core\Config\Context\FreeConfigContext; +use Drupal\Core\Config\TypedConfigManager; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Lock\LockBackendInterface; use Drupal\Component\Uuid\UuidInterface; @@ -103,6 +104,13 @@ class ConfigImporter { protected $uuidService; /** + * The typed config manager. + * + * @var \Drupal\Core\Config\TypedConfigManager + */ + protected $typedConfigManager; + + /** * Constructs a configuration import object. * * @param \Drupal\Core\Config\StorageComparerInterface $storage_comparer @@ -118,14 +126,17 @@ class ConfigImporter { * The lock backend to ensure multiple imports do not occur at the same time. * @param \Drupal\Component\Uuid\UuidInterface $uuid_service * The UUID service. + * @param \Drupal\Core\Config\TypedConfigManager $typed_config + * The typed configuration manager. */ - public function __construct(StorageComparerInterface $storage_comparer, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, LockBackendInterface $lock, UuidInterface $uuid_service) { + public function __construct(StorageComparerInterface $storage_comparer, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, LockBackendInterface $lock, UuidInterface $uuid_service, TypedConfigManager $typed_config) { $this->storageComparer = $storage_comparer; $this->eventDispatcher = $event_dispatcher; $this->configFactory = $config_factory; $this->entityManager = $entity_manager; $this->lock = $lock; $this->uuidService = $uuid_service; + $this->typedConfigManager = $typed_config; $this->processed = $this->storageComparer->getEmptyChangelist(); // Use an override free context for importing so that overrides to do not // pollute the imported data. The context is hard coded to ensure this is @@ -264,7 +275,7 @@ public function validate() { protected function importConfig() { foreach (array('delete', 'create', 'update') as $op) { foreach ($this->getUnprocessed($op) as $name) { - $config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context); + $config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context, $this->typedConfigManager); if ($op == 'delete') { $config->delete(); } @@ -297,11 +308,11 @@ protected function importInvokeOwner() { // Validate the configuration object name before importing it. // Config::validateName($name); if ($entity_type = config_get_entity_type_by_name($name)) { - $old_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context); + $old_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context, $this->typedConfigManager); $old_config->load(); $data = $this->storageComparer->getSourceStorage()->read($name); - $new_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context); + $new_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context, $this->typedConfigManager); if ($data !== FALSE) { $new_config->setData($data); } diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php index 9701df0..92e9a90 100644 --- a/core/lib/Drupal/Core/Config/Schema/Mapping.php +++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php @@ -27,7 +27,7 @@ class Mapping extends ArrayElement implements ComplexDataInterface { protected function parse() { $elements = array(); foreach ($this->definition['mapping'] as $key => $definition) { - if (isset($this->value[$key])) { + if (isset($this->value[$key]) || array_key_exists($key, $this->value)) { $elements[$key] = $this->parseElement($key, $this->value[$key], $definition); } } diff --git a/core/lib/Drupal/Core/Config/Schema/Sequence.php b/core/lib/Drupal/Core/Config/Schema/Sequence.php index a399583..44dbe1d 100644 --- a/core/lib/Drupal/Core/Config/Schema/Sequence.php +++ b/core/lib/Drupal/Core/Config/Schema/Sequence.php @@ -49,4 +49,19 @@ public function onChange($delta) { $this->parent->onChange($this->name); } } + + /** + * Gets a typed configuration element from the sequence. + * + * @param string $key + * The key of the sequence to get. + * + * @return \Drupal\Core\Config\Schema\Element + * Typed configuration element. + */ + public function get($key) { + $elements = $this->parse(); + return $elements[$key]; + } + } diff --git a/core/modules/block/config/schema/block.schema.yml b/core/modules/block/config/schema/block.schema.yml index 61d66e5..b6ba2ae 100644 --- a/core/modules/block/config/schema/block.schema.yml +++ b/core/modules/block/config/schema/block.schema.yml @@ -87,6 +87,9 @@ block.block.*: view_mode: type: string label: 'View mode' + module: + type: string + label: 'Module' langcode: type: string label: 'Default language' diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php index 82827ab..99977cb 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockStorageUnitTest.php @@ -96,7 +96,7 @@ protected function createTests() { 'status' => TRUE, 'langcode' => language_default()->id, 'theme' => 'stark', - 'region' => -1, + 'region' => '-1', 'plugin' => 'test_html_id', 'settings' => array( 'cache' => 1, @@ -106,7 +106,7 @@ protected function createTests() { ), 'visibility' => NULL, ); - $this->assertIdentical($actual_properties, $expected_properties, 'The block properties are exported correctly.'); + $this->assertIdentical($actual_properties, $expected_properties); $this->assertTrue($entity->getPlugin() instanceof TestHtmlIdBlock, 'The entity has an instance of the correct block plugin.'); } diff --git a/core/modules/breakpoint/config/schema/breakpoint.schema.yml b/core/modules/breakpoint/config/schema/breakpoint.schema.yml index eee2445..6281da7 100644 --- a/core/modules/breakpoint/config/schema/breakpoint.schema.yml +++ b/core/modules/breakpoint/config/schema/breakpoint.schema.yml @@ -36,6 +36,9 @@ breakpoint.breakpoint.*.*.*: langcode: type: string label: 'Default language' + status: + type: boolean + label: 'Enabled' breakpoint.breakpoint_group.*.*.*: type: mapping @@ -65,3 +68,9 @@ breakpoint.breakpoint_group.*.*.*: sourceType: type: string label: 'Group source type' + langcode: + type: string + label: 'Default language' + status: + type: boolean + label: 'Enabled' diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php index 7e95109..12ce8e4 100644 --- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php +++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/CKEditorPlugin/DrupalImage.php @@ -84,7 +84,6 @@ public function settingsForm(array $form, array &$form_state, Editor $editor) { * * @see \Drupal\editor\Form\EditorImageDialog * @see editor_image_upload_settings_form() - * @see editor_image_upload_settings_validate() */ function validateImageUploadSettings(array $element, array &$form_state) { $settings = &$form_state['values']['editor']['settings']['plugins']['drupalimage']['image_upload']; diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php index 5654d45..4287d86 100644 --- a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php +++ b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php @@ -16,6 +16,7 @@ use Drupal\Core\Config\ConfigImporter; use Drupal\Core\Config\ConfigException; use Drupal\Core\Config\ConfigFactory; +use Drupal\Core\Config\TypedConfigManager; use Drupal\Core\Routing\UrlGeneratorInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -73,6 +74,13 @@ class ConfigSync extends FormBase { protected $uuidService; /** + * The typed config manager. + * + * @var \Drupal\Core\Config\TypedConfigManager + */ + protected $typedConfigManager; + + /** * Constructs the object. * * @param \Drupal\Core\Config\StorageInterface $sourceStorage @@ -90,9 +98,11 @@ class ConfigSync extends FormBase { * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator * The url generator service. * @param \Drupal\Component\Uuid\UuidInterface $uuid_service - * The UUID Service. + * The UUID Service. + * @param \Drupal\Core\Config\TypedConfigManager $typed_config + * The typed configuration manager. */ - public function __construct(StorageInterface $sourceStorage, StorageInterface $targetStorage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, UrlGeneratorInterface $url_generator, UuidInterface $uuid_service) { + public function __construct(StorageInterface $sourceStorage, StorageInterface $targetStorage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, EntityManagerInterface $entity_manager, UrlGeneratorInterface $url_generator, UuidInterface $uuid_service, TypedConfigManager $typed_config) { $this->sourceStorage = $sourceStorage; $this->targetStorage = $targetStorage; $this->lock = $lock; @@ -101,6 +111,7 @@ public function __construct(StorageInterface $sourceStorage, StorageInterface $t $this->entity_manager = $entity_manager; $this->urlGenerator = $url_generator; $this->uuidService = $uuid_service; + $this->typedConfigManager = $typed_config; } /** @@ -115,7 +126,8 @@ public static function create(ContainerInterface $container) { $container->get('config.factory'), $container->get('entity.manager'), $container->get('url_generator'), - $container->get('uuid') + $container->get('uuid'), + $container->get('config.typed') ); } @@ -222,7 +234,8 @@ public function submitForm(array &$form, array &$form_state) { $this->configFactory, $this->entity_manager, $this->lock, - $this->uuidService + $this->uuidService, + $this->typedConfigManager ); if ($config_importer->alreadyImporting()) { drupal_set_message($this->t('Another request may be synchronizing configuration already.')); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php index 9acca96..d3d9818 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityTest.php @@ -146,7 +146,7 @@ function testCRUD() { // Verify that the entity was overwritten. $same_id = entity_load('config_test', $config_test->id()); $this->assertIdentical($same_id->id(), $config_test->id()); - $this->assertIdentical($same_id->label(), NULL); + $this->assertIdentical($same_id->label(), ''); $this->assertNotEqual($same_id->uuid(), $config_test->uuid()); // Delete the overridden entity first. diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php index cb4aeaa..ef9a9ee 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php @@ -61,7 +61,8 @@ function setUp() { $this->container->get('config.factory'), $this->container->get('entity.manager'), $this->container->get('lock'), - $this->container->get('uuid') + $this->container->get('uuid'), + $this->container->get('config.typed') ); $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging')); } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php index 3b406b1..c9bf579 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php @@ -102,6 +102,8 @@ function testSchemaMapping() { $expected['label'] = 'Image style'; $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; $expected['mapping']['name']['type'] = 'string'; + $expected['mapping']['uuid']['label'] = 'UUID'; + $expected['mapping']['uuid']['type'] = 'string'; $expected['mapping']['label']['type'] = 'label'; $expected['mapping']['effects']['type'] = 'sequence'; $expected['mapping']['effects']['sequence'][0]['type'] = 'mapping'; @@ -111,6 +113,8 @@ function testSchemaMapping() { $expected['mapping']['effects']['sequence'][0]['mapping']['uuid']['type'] = 'string'; $expected['mapping']['langcode']['label'] = 'Default language'; $expected['mapping']['langcode']['type'] = 'string'; + $expected['mapping']['status']['label'] = 'Enabled'; + $expected['mapping']['status']['type'] = 'boolean'; $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.style.large'); @@ -242,4 +246,55 @@ function testSchemaData() { $this->assertEqual($site_slogan->getValue(), $new_slogan, 'Successfully updated the contained configuration data'); } + /** + * Test configuration value data type enforcement using schemas. + */ + public function testConfigSaveWithSchema() { + $untyped_values = array( + 'string' => 1, + 'null_string' => '', + 'integer' => '100', + 'null_integer' => '', + 'boolean' => 1, + // In the config schema this doesn't have a type so should cast to string. + 'no_type' => 1, + 'array' => array( + 'string' => 1 + ), + 'float' => '3.14', + 'null_float' => '', + // Not in schema and therefore should be left untouched. + 'not_present_in_schema' => TRUE, + ); + $untyped_to_typed = $untyped_values; + $untyped_to_typed['null_string'] = NULL; + + $typed_values = array( + 'string' => '1', + 'null_string' => NULL, + 'integer' => 100, + 'null_integer' => NULL, + 'boolean' => TRUE, + 'no_type' => '1', + 'array' => array( + 'string' => '1' + ), + 'float' => 3.14, + 'null_float' => NULL, + 'not_present_in_schema' => TRUE, + ); + + // Save config which has a schema that enforces types. + \Drupal::config('config_test.schema_data_types') + ->setData($untyped_to_typed) + ->save(); + $this->assertIdentical(\Drupal::config('config_test.schema_data_types')->get(), $typed_values); + + // Save config which does not have a schema that enforces types. + \Drupal::config('config_test.no_schema_data_types') + ->setData($untyped_values) + ->save(); + $this->assertIdentical(\Drupal::config('config_test.no_schema_data_types')->get(), $untyped_values); + } + } diff --git a/core/modules/config/tests/config_test/config/schema/config_test.schema.yml b/core/modules/config/tests/config_test/config/schema/config_test.schema.yml index 04329ab..e0377bf 100644 --- a/core/modules/config/tests/config_test/config/schema/config_test.schema.yml +++ b/core/modules/config/tests/config_test/config/schema/config_test.schema.yml @@ -91,3 +91,29 @@ config_test.dynamic.*: protected_property: type: string label: 'Protected property' + +config_test.schema_data_types: + type: mapping + label: 'Config test schema' + mapping: + integer: + type: integer + null_integer: + type: integer + float: + type: float + null_float: + type: float + string: + type: string + null_string: + type: string + boolean: + type: boolean + no_type: + label: 'No label' + array: + type: mapping + mapping: + string: + type: string diff --git a/core/modules/editor/editor.admin.inc b/core/modules/editor/editor.admin.inc index eed9761..a29924b 100644 --- a/core/modules/editor/editor.admin.inc +++ b/core/modules/editor/editor.admin.inc @@ -128,30 +128,5 @@ function editor_image_upload_settings_form(Editor $editor) { '#states' => $show_if_image_uploads_enabled, ); - $form['#element_validate'] = array( - 'editor_image_upload_settings_validate', - ); - return $form; } - -/** - * #element_validate handler for editor_image_upload_settings_validate(). - * - * Ensures each form item's value is cast to the proper type. - * - * @see \Drupal\editor\Form\EditorImageDialog - * @ingroup forms - */ -function editor_image_upload_settings_validate(array $element, array &$form_state) { - $cast_value = function($type, $element) use (&$form_state) { - $section = $element['#parents']; - $value = NestedArray::getValue($form_state['values'], $section); - settype($value, $type); - NestedArray::setValue($form_state['values'], $section, $value); - }; - - $cast_value('bool', $element['status']); - $cast_value('int', $element['max_dimensions']['width']); - $cast_value('int', $element['max_dimensions']['height']); -} diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module index 837544f..e5fa4cd 100644 --- a/core/modules/editor/editor.module +++ b/core/modules/editor/editor.module @@ -568,7 +568,7 @@ function _editor_get_processed_text_fields(ContentEntityInterface $entity) { $settings = Field::fieldInfo() ->getInstance($entity->entityType(), $entity->bundle(), $field) ->getSettings(); - return isset($settings['text_processing']) && $settings['text_processing'] === '1'; + return isset($settings['text_processing']) && $settings['text_processing'] === TRUE; }); } diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml index 7c09cce..33b8d3e 100644 --- a/core/modules/field/config/schema/field.schema.yml +++ b/core/modules/field/config/schema/field.schema.yml @@ -24,6 +24,9 @@ field.field.*.*: langcode: type: string label: 'Default language' + name: + type: string + label: 'Name' entity_type: type: string label: 'Entity type' diff --git a/core/modules/filter/config/schema/filter.schema.yml b/core/modules/filter/config/schema/filter.schema.yml index 391b968..3f32071 100644 --- a/core/modules/filter/config/schema/filter.schema.yml +++ b/core/modules/filter/config/schema/filter.schema.yml @@ -21,6 +21,9 @@ filter.format.*: name: type: label label: 'Name' + uuid: + type: string + label: 'UUID' status: type: boolean label: 'Enabled' diff --git a/core/modules/image/config/schema/image.schema.yml b/core/modules/image/config/schema/image.schema.yml index 87a8f1e..b0dfd56 100644 --- a/core/modules/image/config/schema/image.schema.yml +++ b/core/modules/image/config/schema/image.schema.yml @@ -6,6 +6,9 @@ image.style.*: mapping: name: type: string + uuid: + type: string + label: 'UUID' label: type: label effects: @@ -24,6 +27,9 @@ image.style.*: langcode: type: string label: 'Default language' + status: + type: boolean + label: 'Enabled' image.effect.image_crop: type: image_size diff --git a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php index c09c390..a878076 100644 --- a/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php +++ b/core/modules/migrate_drupal/lib/Drupal/migrate_drupal/Tests/MigrateSystemConfigsTest.php @@ -85,8 +85,8 @@ public function testSystemPerformance() { $executable = new MigrateExecutable($migration, new MigrateMessage()); $executable->import(); $config = \Drupal::config('system.performance'); - $this->assertIdentical($config->get('css.preprocess'), 0); - $this->assertIdentical($config->get('js.preprocess'), 0); + $this->assertIdentical($config->get('css.preprocess'), FALSE); + $this->assertIdentical($config->get('js.preprocess'), FALSE); $this->assertIdentical($config->get('cache.page.max_age'), 0); } } diff --git a/core/modules/number/config/schema/number.schema.yml b/core/modules/number/config/schema/number.schema.yml index 087ef8f..f597c02 100644 --- a/core/modules/number/config/schema/number.schema.yml +++ b/core/modules/number/config/schema/number.schema.yml @@ -51,10 +51,10 @@ field.number_decimal.instance_settings: label: 'Decimal' mapping: min: - type: integer + type: float label: 'Minimum' max: - type: integer + type: float label: 'Maximum' prefix: type: string @@ -71,7 +71,7 @@ field.number_decimal.value: label: 'Default value' mapping: value: - type: integer + type: float label: 'Value' field.number_float.settings: @@ -86,10 +86,10 @@ field.number_float.instance_settings: label: 'Float' mapping: min: - type: integer + type: float label: 'Minimum' max: - type: integer + type: float label: 'Maximum' prefix: type: string @@ -106,5 +106,5 @@ field.number_float.value: label: 'Default value' mapping: value: - type: integer + type: float label: 'Value' diff --git a/core/modules/options/config/schema/options.schema.yml b/core/modules/options/config/schema/options.schema.yml index 8d1481a..367e4b5 100644 --- a/core/modules/options/config/schema/options.schema.yml +++ b/core/modules/options/config/schema/options.schema.yml @@ -15,11 +15,9 @@ field.list_integer.settings: label: 'Allowed values function' field.list_integer.instance_settings: - type: mapping label: 'List (integer)' - sequence: - - type: string - label: 'setting' + type: mapping + mapping: { } field.list_integer.value: type: sequence @@ -47,11 +45,9 @@ field.list_float.settings: label: 'Allowed values function' field.list_float.instance_settings: - type: mapping label: 'List (float)' - sequence: - - type: string - label: 'setting' + type: mapping + mapping: { } field.list_float.value: type: sequence @@ -79,11 +75,9 @@ field.list_text.settings: label: 'Allowed values function' field.list_text.instance_settings: - type: mapping label: 'List (float)' - sequence: - - type: string - label: 'setting' + type: mapping + mapping: { } field.list_text.value: type: sequence @@ -111,11 +105,9 @@ field.list_boolean.settings: label: 'Allowed values function' field.list_boolean.instance_settings: - type: mapping label: 'List (boolean)' - sequence: - - type: string - label: 'setting' + type: mapping + mapping: { } field.list_boolean.value: type: sequence diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 882542e..1e29384 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -1400,7 +1400,8 @@ public function configImporter() { $this->container->get('config.factory'), $this->container->get('entity.manager'), $this->container->get('lock'), - $this->container->get('uuid') + $this->container->get('uuid'), + $this->container->get('config.typed') ); } // Always recalculate the changelist when called. diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml index 4f3625b..0dabf5e 100644 --- a/core/modules/system/config/schema/system.schema.yml +++ b/core/modules/system/config/schema/system.schema.yml @@ -97,7 +97,7 @@ system.date: default: type: string label: 'Default time zone' - user: + user: type: mapping label: 'User' mapping: @@ -220,7 +220,7 @@ system.performance: type: mapping label: 'Page cache' mapping: - enabled: + use_internal: type: boolean label: 'Cache pages for anonymous users' max_age: @@ -236,6 +236,22 @@ system.performance: gzip: type: boolean label: 'Compress CSS files' + fast_404: + type: mapping + label: 'Fast 404 settings' + mapping: + enabled: + type: boolean + label: 'Fast 404 enabled' + paths: + type: string + label: 'Regular expression to match' + exclude_paths: + type: string + label: 'Regular expression to not match' + html: + type: string + label: 'Fast 404 page html' js: type: mapping label: 'JavaScript performance settings' @@ -295,6 +311,9 @@ system.theme: sequence: - type: string label: 'Theme' + default: + type: string + label: 'Default theme' system.menu.*: type: mapping @@ -303,6 +322,9 @@ system.menu.*: id: type: string label: 'Menu identifier' + uuid: + type: string + label: 'UUID' label: type: label label: 'Menu label' @@ -312,3 +334,9 @@ system.menu.*: langcode: type: string label: 'Default language' + locked: + type: boolean + label: '' + status: + type: boolean + label: '' diff --git a/core/modules/system/config/system.maintenance.yml b/core/modules/system/config/system.maintenance.yml index 5ea379d..40cfeb2 100644 --- a/core/modules/system/config/system.maintenance.yml +++ b/core/modules/system/config/system.maintenance.yml @@ -1,3 +1,2 @@ -enabled: '0' message: '@site is currently under maintenance. We should be back shortly. Thank you for your patience.' langcode: en diff --git a/core/modules/taxonomy/config/schema/taxonomy.schema.yml b/core/modules/taxonomy/config/schema/taxonomy.schema.yml index 21d1322..9170280 100644 --- a/core/modules/taxonomy/config/schema/taxonomy.schema.yml +++ b/core/modules/taxonomy/config/schema/taxonomy.schema.yml @@ -21,6 +21,9 @@ taxonomy.vocabulary.*: vid: type: string label: 'Machine name' + uuid: + type: string + label: 'UUID' name: type: label label: 'Name' @@ -28,7 +31,7 @@ taxonomy.vocabulary.*: type: label label: 'Description' hierarchy: - type: boolean + type: integer label: 'Hierarchy' weight: type: integer diff --git a/core/modules/tour/config/schema/tour.schema.yml b/core/modules/tour/config/schema/tour.schema.yml index ca8d781..b1a0c28 100644 --- a/core/modules/tour/config/schema/tour.schema.yml +++ b/core/modules/tour/config/schema/tour.schema.yml @@ -7,9 +7,18 @@ tour.tour.*: id: type: string label: 'ID' + uuid: + type: string + label: 'UUID' + module: + type: string + label: 'Providing module' label: type: label label: 'Label' + status: + type: boolean + label: 'Enabled' langcode: type: string label: 'Default language' @@ -42,6 +51,9 @@ tour.tip: weight: type: integer label: 'Weight' + location: + type: string + label: 'Location' attributes: type: sequence label: 'Attributes' diff --git a/core/modules/user/config/schema/user.schema.yml b/core/modules/user/config/schema/user.schema.yml index 05155ff..5aec5a2 100644 --- a/core/modules/user/config/schema/user.schema.yml +++ b/core/modules/user/config/schema/user.schema.yml @@ -137,6 +137,9 @@ user.role.*: sequence: - type: string label: 'Permission' + status: + type: boolean + label: 'Status' langcode: type: string label: 'Default language'