diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 51ade6c..8861481 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -203,22 +203,24 @@ public function clear($key) { /** * {@inheritdoc} */ - public function save() { + public function save($has_trusted_data = FALSE) { // 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 (!$has_trusted_data) { + 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); + } } - } - else { - foreach ($this->data as $key => $value) { - $this->validateValue($key, $value); + else { + foreach ($this->data as $key => $value) { + $this->validateValue($key, $value); + } } } @@ -302,4 +304,5 @@ public function getOriginal($key = '', $apply_overrides = TRUE) { } } } + } diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php index ffdc760..e7fa6ad 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -289,11 +289,11 @@ protected function createConfiguration($collection, array $config_to_create) { $entity = $entity_storage->createFromStorageRecord($new_config->get()); } if ($entity->isInstallable()) { - $entity->save(); + $entity->trustData()->save(); } } else { - $new_config->save(); + $new_config->save(TRUE); } } } diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index 90837f9..1675931 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -105,6 +105,8 @@ */ protected $third_party_settings = array(); + protected $trustedData = FALSE; + /** * Overrides Entity::__construct(). */ @@ -324,7 +326,7 @@ public function preSave(EntityStorageInterface $storage) { throw new ConfigDuplicateUUIDException(String::format('Attempt to save a configuration entity %id with UUID %uuid when this entity already exists with UUID %original_uuid', array('%id' => $this->id(), '%uuid' => $this->uuid(), '%original_uuid' => $original->uuid()))); } } - if (!$this->isSyncing()) { + if (!$this->isSyncing() && !$this->trustedData) { // Ensure the correct dependencies are present. If the configuration is // being written during a configuration synchronization then there is no // need to recalculate the dependencies. @@ -568,4 +570,28 @@ public function isInstallable() { return TRUE; } + /** + * {@inheritdoc} + */ + public function trustData() { + $this->trustedData = TRUE; + return $this; + } + + /** + * {@inheritdoc} + */ + public function hasTrustedData() { + return $this->trustedData; + } + + /** + * {@inheritdoc} + */ + public function save() { + $return = parent::save(); + $this->trustedData = FALSE; + return $return; + } + } diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php index 3d688a5..41997cb 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php @@ -203,4 +203,25 @@ public function getDependencies(); */ public function isInstallable(); + /** + * Sets that the data should be trusted. + * + * @param bool $trust + * TRUE if the configuration data is trusted, FALSE if not. If the data is + * trusted then dependencies will not be calculated on save and schema will + * not be used to cast the values. Generally this is only used during module + * and theme installation. + * + * @return $this + */ + public function trustData(); + + /** + * Gets whether on not the data is trusted. + * + * @return bool + * TRUE if the configuration data is trusted, FALSE if not. + */ + public function hasTrustedData(); + } diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php index 27da150..aed551a 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php @@ -256,7 +256,7 @@ protected function doSave($id, EntityInterface $entity) { // Retrieve the desired properties and set them in config. $config->setData($this->mapToStorageRecord($entity)); - $config->save(); + $config->save($entity->hasTrustedData()); return $is_new ? SAVED_NEW : SAVED_UPDATED; } diff --git a/core/lib/Drupal/Core/Config/ImmutableConfig.php b/core/lib/Drupal/Core/Config/ImmutableConfig.php index 61e7fdd..d4bdaf5 100644 --- a/core/lib/Drupal/Core/Config/ImmutableConfig.php +++ b/core/lib/Drupal/Core/Config/ImmutableConfig.php @@ -44,7 +44,7 @@ public function clear($key) { /** * {@inheritdoc} */ - public function save() { + public function save($has_trusted_data = FALSE) { throw new ImmutableConfigException(String::format('Can not save immutable configuration !name. Use \Drupal\Core\Config\ConfigFactoryInterface::getEditable() to retrieve a mutable configuration object', ['!name' => $this->getName()])); } diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php index 4af470e..9f08a8f 100644 --- a/core/lib/Drupal/Core/Config/StorableConfigBase.php +++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php @@ -66,11 +66,16 @@ /** * Saves the configuration object. * + * @param bool $has_trusted_data + * Set to TRUE is the configuration data has already been checked to ensure + * it conforms to schema. Generally this is only used during module and + * theme installation. + * * Must invalidate the cache tags associated with the configuration object. * * @return $this */ - abstract public function save(); + abstract public function save($has_trusted_data = FALSE); /** * Deletes the configuration object. diff --git a/core/modules/book/config/install/node.type.book.yml b/core/modules/book/config/install/node.type.book.yml index 1a5cc16..a5076b2 100644 --- a/core/modules/book/config/install/node.type.book.yml +++ b/core/modules/book/config/install/node.type.book.yml @@ -1,6 +1,8 @@ langcode: en status: true dependencies: + module: + - book enforced: module: - book diff --git a/core/modules/config/src/Tests/ConfigInstallTest.php b/core/modules/config/src/Tests/ConfigInstallTest.php index 9a903a1..d94fade 100644 --- a/core/modules/config/src/Tests/ConfigInstallTest.php +++ b/core/modules/config/src/Tests/ConfigInstallTest.php @@ -20,234 +20,230 @@ */ class ConfigInstallTest extends KernelTestBase { - /** - * {@inheritdoc} - */ - public static $modules = ['system']; - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - $this->installSchema('system', ['router']); - - // Ensure the global variable being asserted by this test does not exist; - // a previous test executed in this request/process might have set it. - unset($GLOBALS['hook_config_test']); - } - - /** - * Tests module installation. - */ - function testModuleInstallation() { - $default_config = 'config_test.system'; - $default_configuration_entity = 'config_test.dynamic.dotted.default'; - - // Verify that default module config does not exist before installation yet. - $config = $this->config($default_config); - $this->assertIdentical($config->isNew(), TRUE); - $config = $this->config($default_configuration_entity); - $this->assertIdentical($config->isNew(), TRUE); - - // Ensure that schema provided by modules that are not installed is not - // available. - $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install does not exist.'); - - // Install the test module. - $this->installModules(array('config_test')); - - // Verify that default module config exists. - \Drupal::configFactory()->reset($default_config); - \Drupal::configFactory()->reset($default_configuration_entity); - $config = $this->config($default_config); - $this->assertIdentical($config->isNew(), FALSE); - $config = $this->config($default_configuration_entity); - $this->assertIdentical($config->isNew(), FALSE); - - // Verify that config_test API hooks were invoked for the dynamic default - // configuration entity. - $this->assertFalse(isset($GLOBALS['hook_config_test']['load'])); - $this->assertTrue(isset($GLOBALS['hook_config_test']['presave'])); - $this->assertTrue(isset($GLOBALS['hook_config_test']['insert'])); - $this->assertFalse(isset($GLOBALS['hook_config_test']['update'])); - $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete'])); - $this->assertFalse(isset($GLOBALS['hook_config_test']['delete'])); - - // Install the schema test module. - $this->enableModules(array('config_schema_test')); - $this->installConfig(array('config_schema_test')); - - // After module installation the new schema should exist. - $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.'); - - // Ensure that data type casting is applied during config installation. - $config = $this->config('config_schema_test.schema_in_install'); - $this->assertIdentical($config->get('integer'), 1); - - // Test that uninstalling configuration removes configuration schema. - $this->config('core.extension')->set('module', array())->save(); - \Drupal::service('config.manager')->uninstall('module', 'config_test'); - $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install does not exist.'); - } - - /** - * Tests that collections are ignored if the event does not return anything. - */ - public function testCollectionInstallationNoCollections() { - // Install the test module. - $this->enableModules(array('config_collection_install_test')); - $this->installConfig(array('config_collection_install_test')); - /** @var \Drupal\Core\Config\StorageInterface $active_storage */ - $active_storage = \Drupal::service('config.storage'); - $this->assertEqual(array(), $active_storage->getAllCollectionNames()); - } - - /** - * Tests config objects in collections are installed as expected. - */ - public function testCollectionInstallationCollections() { - $collections = array( - 'another_collection', - 'collection.test1', - 'collection.test2', - ); - // Set the event listener to return three possible collections. - // @see \Drupal\config_collection_install_test\EventSubscriber - \Drupal::state()->set('config_collection_install_test.collection_names', $collections); - // Install the test module. - $this->enableModules(array('config_collection_install_test')); - $this->installConfig(array('config_collection_install_test')); - /** @var \Drupal\Core\Config\StorageInterface $active_storage */ - $active_storage = \Drupal::service('config.storage'); - $this->assertEqual($collections, $active_storage->getAllCollectionNames()); - foreach ($collections as $collection) { - $collection_storage = $active_storage->createCollection($collection); - $data = $collection_storage->read('config_collection_install_test.test'); - $this->assertEqual($collection, $data['collection']); + /** + * {@inheritdoc} + */ + public static $modules = ['system']; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $this->installSchema('system', ['router']); + + // Ensure the global variable being asserted by this test does not exist; + // a previous test executed in this request/process might have set it. + unset($GLOBALS['hook_config_test']); } - // Tests that clashing configuration in collections is detected. - try { - \Drupal::service('module_installer')->install(['config_collection_clash_install_test']); - $this->fail('Expected PreExistingConfigException not thrown.'); + /** + * Tests module installation. + */ + function testModuleInstallation() { + $default_config = 'config_test.system'; + $default_configuration_entity = 'config_test.dynamic.dotted.default'; + + // Verify that default module config does not exist before installation yet. + $config = $this->config($default_config); + $this->assertIdentical($config->isNew(), TRUE); + $config = $this->config($default_configuration_entity); + $this->assertIdentical($config->isNew(), TRUE); + + // Ensure that schema provided by modules that are not installed is not + // available. + $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.'); + + // Install the test module. + $this->installModules(array('config_test')); + + // Verify that default module config exists. + \Drupal::configFactory()->reset($default_config); + \Drupal::configFactory()->reset($default_configuration_entity); + $config = $this->config($default_config); + $this->assertIdentical($config->isNew(), FALSE); + $config = $this->config($default_configuration_entity); + $this->assertIdentical($config->isNew(), FALSE); + + // Verify that config_test API hooks were invoked for the dynamic default + // configuration entity. + $this->assertFalse(isset($GLOBALS['hook_config_test']['load'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['presave'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['insert'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['update'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['delete'])); + + // Install the schema test module. + $this->enableModules(array('config_schema_test')); + $this->installConfig(array('config_schema_test')); + + // After module installation the new schema should exist. + $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema exists.'); + + // Test that uninstalling configuration removes configuration schema. + $this->config('core.extension')->set('module', array())->save(); + \Drupal::service('config.manager')->uninstall('module', 'config_test'); + $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.'); } - catch (PreExistingConfigException $e) { - $this->assertEqual($e->getExtension(), 'config_collection_clash_install_test'); - $this->assertEqual($e->getConfigObjects(), [ - 'another_collection' => ['config_collection_install_test.test'], - 'collection.test1' => ['config_collection_install_test.test'], - 'collection.test2' => ['config_collection_install_test.test'], - ]); - $this->assertEqual($e->getMessage(), 'Configuration objects (another_collection/config_collection_install_test.test, collection/test1/config_collection_install_test.test, collection/test2/config_collection_install_test.test) provided by config_collection_clash_install_test already exist in active configuration'); + + /** + * Tests that collections are ignored if the event does not return anything. + */ + public function testCollectionInstallationNoCollections() { + // Install the test module. + $this->enableModules(array('config_collection_install_test')); + $this->installConfig(array('config_collection_install_test')); + /** @var \Drupal\Core\Config\StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + $this->assertEqual(array(), $active_storage->getAllCollectionNames()); + } + + /** + * Tests config objects in collections are installed as expected. + */ + public function testCollectionInstallationCollections() { + $collections = array( + 'another_collection', + 'collection.test1', + 'collection.test2', + ); + // Set the event listener to return three possible collections. + // @see \Drupal\config_collection_install_test\EventSubscriber + \Drupal::state()->set('config_collection_install_test.collection_names', $collections); + // Install the test module. + $this->enableModules(array('config_collection_install_test')); + $this->installConfig(array('config_collection_install_test')); + /** @var \Drupal\Core\Config\StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + foreach ($collections as $collection) { + $collection_storage = $active_storage->createCollection($collection); + $data = $collection_storage->read('config_collection_install_test.test'); + $this->assertEqual($collection, $data['collection']); + } + + // Tests that clashing configuration in collections is detected. + try { + \Drupal::service('module_installer')->install(['config_collection_clash_install_test']); + $this->fail('Expected PreExistingConfigException not thrown.'); + } + catch (PreExistingConfigException $e) { + $this->assertEqual($e->getExtension(), 'config_collection_clash_install_test'); + $this->assertEqual($e->getConfigObjects(), [ + 'another_collection' => ['config_collection_install_test.test'], + 'collection.test1' => ['config_collection_install_test.test'], + 'collection.test2' => ['config_collection_install_test.test'], + ]); + $this->assertEqual($e->getMessage(), 'Configuration objects (another_collection/config_collection_install_test.test, collection/test1/config_collection_install_test.test, collection/test2/config_collection_install_test.test) provided by config_collection_clash_install_test already exist in active configuration'); + } + + // Test that the we can use the config installer to install all the + // available default configuration in a particular collection for enabled + // extensions. + \Drupal::service('config.installer')->installCollectionDefaultConfig('entity'); + // The 'entity' collection will not exist because the 'config_test' module + // is not enabled. + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + // Enable the 'config_test' module and try again. + $this->enableModules(array('config_test')); + \Drupal::service('config.installer')->installCollectionDefaultConfig('entity'); + $collections[] = 'entity'; + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + $collection_storage = $active_storage->createCollection('entity'); + $data = $collection_storage->read('config_test.dynamic.dotted.default'); + $this->assertIdentical(array('label' => 'entity'), $data); + + // Test that the config manager uninstalls configuration from collections + // as expected. + \Drupal::service('config.manager')->uninstall('module', 'config_collection_install_test'); + $this->assertEqual(array('entity'), $active_storage->getAllCollectionNames()); + \Drupal::service('config.manager')->uninstall('module', 'config_test'); + $this->assertEqual(array(), $active_storage->getAllCollectionNames()); + } + + /** + * Tests collections which do not support config entities install correctly. + * + * Config entity detection during config installation is done by matching + * config name prefixes. If a collection provides a configuration with a + * matching name but does not support config entities it should be created + * using simple configuration. + */ + public function testCollectionInstallationCollectionConfigEntity() { + $collections = array( + 'entity', + ); + \Drupal::state()->set('config_collection_install_test.collection_names', $collections); + // Install the test module. + $this->installModules(array('config_test', 'config_collection_install_test')); + /** @var \Drupal\Core\Config\StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + $collection_storage = $active_storage->createCollection('entity'); + + // The config_test.dynamic.dotted.default configuraton object saved in the + // active store should be a configuration entity complete with UUID. Because + // the entity collection does not support configuration entities the + // configuration object stored there with the same name should only contain + // a label. + $name = 'config_test.dynamic.dotted.default'; + $data = $active_storage->read($name); + $this->assertTrue(isset($data['uuid'])); + $data = $collection_storage->read($name); + $this->assertIdentical(array('label' => 'entity'), $data); + } + + /** + * Tests the configuration with unmet dependencies is not installed. + */ + public function testDependencyChecking() { + $this->installModules(['config_test']); + try { + $this->installModules(['config_install_dependency_test']); + $this->fail('Expected UnmetDependenciesException not thrown.'); + } + catch (UnmetDependenciesException $e) { + $this->assertEqual($e->getExtension(), 'config_install_dependency_test'); + $this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']); + $this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies'); + } + $this->installModules(['config_other_module_config_test']); + $this->installModules(['config_install_dependency_test']); + $this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.'); } - // Test that the we can use the config installer to install all the - // available default configuration in a particular collection for enabled - // extensions. - \Drupal::service('config.installer')->installCollectionDefaultConfig('entity'); - // The 'entity' collection will not exist because the 'config_test' module - // is not enabled. - $this->assertEqual($collections, $active_storage->getAllCollectionNames()); - // Enable the 'config_test' module and try again. - $this->enableModules(array('config_test')); - \Drupal::service('config.installer')->installCollectionDefaultConfig('entity'); - $collections[] = 'entity'; - $this->assertEqual($collections, $active_storage->getAllCollectionNames()); - $collection_storage = $active_storage->createCollection('entity'); - $data = $collection_storage->read('config_test.dynamic.dotted.default'); - $this->assertIdentical(array('label' => 'entity'), $data); - - // Test that the config manager uninstalls configuration from collections - // as expected. - \Drupal::service('config.manager')->uninstall('module', 'config_collection_install_test'); - $this->assertEqual(array('entity'), $active_storage->getAllCollectionNames()); - \Drupal::service('config.manager')->uninstall('module', 'config_test'); - $this->assertEqual(array(), $active_storage->getAllCollectionNames()); - } - - /** - * Tests collections which do not support config entities install correctly. - * - * Config entity detection during config installation is done by matching - * config name prefixes. If a collection provides a configuration with a - * matching name but does not support config entities it should be created - * using simple configuration. - */ - public function testCollectionInstallationCollectionConfigEntity() { - $collections = array( - 'entity', - ); - \Drupal::state()->set('config_collection_install_test.collection_names', $collections); - // Install the test module. - $this->installModules(array('config_test', 'config_collection_install_test')); - /** @var \Drupal\Core\Config\StorageInterface $active_storage */ - $active_storage = \Drupal::service('config.storage'); - $this->assertEqual($collections, $active_storage->getAllCollectionNames()); - $collection_storage = $active_storage->createCollection('entity'); - - // The config_test.dynamic.dotted.default configuraton object saved in the - // active store should be a configuration entity complete with UUID. Because - // the entity collection does not support configuration entities the - // configuration object stored there with the same name should only contain - // a label. - $name = 'config_test.dynamic.dotted.default'; - $data = $active_storage->read($name); - $this->assertTrue(isset($data['uuid'])); - $data = $collection_storage->read($name); - $this->assertIdentical(array('label' => 'entity'), $data); - } - - /** - * Tests the configuration with unmet dependencies is not installed. - */ - public function testDependencyChecking() { - $this->installModules(['config_test']); - try { - $this->installModules(['config_install_dependency_test']); - $this->fail('Expected UnmetDependenciesException not thrown.'); + /** + * Tests imported configuration entities with and without language information. + */ + function testLanguage() { + $this->installModules(['config_test_language']); + // Test imported configuration with implicit language code. + $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english'); + $this->assertTrue(!isset($data['langcode'])); + $this->assertEqual( + $this->config('config_test.dynamic.dotted.english')->get('langcode'), + 'en' + ); + + // Test imported configuration with explicit language code. + $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french'); + $this->assertEqual($data['langcode'], 'fr'); + $this->assertEqual( + $this->config('config_test.dynamic.dotted.french')->get('langcode'), + 'fr' + ); } - catch (UnmetDependenciesException $e) { - $this->assertEqual($e->getExtension(), 'config_install_dependency_test'); - $this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']); - $this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies'); + + /** + * Installs a module. + * + * @param array $modules + * The module names. + */ + protected function installModules(array $modules) { + $this->container->get('module_installer')->install($modules); + $this->container = \Drupal::getContainer(); } - $this->installModules(['config_other_module_config_test']); - $this->installModules(['config_install_dependency_test']); - $this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.'); - } - - /** - * Tests imported configuration entities with and without language information. - */ - function testLanguage() { - $this->installModules(['config_test_language']); - // Test imported configuration with implicit language code. - $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english'); - $this->assertTrue(!isset($data['langcode'])); - $this->assertEqual( - $this->config('config_test.dynamic.dotted.english')->get('langcode'), - 'en' - ); - - // Test imported configuration with explicit language code. - $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french'); - $this->assertEqual($data['langcode'], 'fr'); - $this->assertEqual( - $this->config('config_test.dynamic.dotted.french')->get('langcode'), - 'fr' - ); - } - - /** - * Installs a module. - * - * @param array $modules - * The module names. - */ - protected function installModules(array $modules) { - $this->container->get('module_installer')->install($modules); - $this->container = \Drupal::getContainer(); - } } diff --git a/core/modules/config/tests/config_schema_test/config/install/config_schema_test.schema_in_install.yml b/core/modules/config/tests/config_schema_test/config/install/config_schema_test.schema_in_install.yml deleted file mode 100644 index 2ae0ace..0000000 --- a/core/modules/config/tests/config_schema_test/config/install/config_schema_test.schema_in_install.yml +++ /dev/null @@ -1 +0,0 @@ -integer: '1' diff --git a/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml index 6b4f62d..cb870d9 100644 --- a/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml +++ b/core/modules/config/tests/config_schema_test/config/schema/config_schema_test.schema.yml @@ -125,14 +125,6 @@ config_schema_test.schema_data_types: sequence: - type: boolean -config_schema_test.schema_in_install: - label: 'Schema test data with parenting' - type: mapping - mapping: - integer: - type: integer - label: 'Integer' - config_schema_test_integer: type: integer label: 'Config test integer' diff --git a/core/modules/config/tests/config_test/config/install/config_test.types.yml b/core/modules/config/tests/config_test/config/install/config_test.types.yml index 060a2b0..6c27522 100644 --- a/core/modules/config/tests/config_test/config/install/config_test.types.yml +++ b/core/modules/config/tests/config_test/config/install/config_test.types.yml @@ -2,7 +2,7 @@ array: [] boolean: true exp: 1.2e+34 float: 3.14159 -float_as_integer: 1 +float_as_integer: !!float 1 hex: 0xC int: 99 octal: 0775 diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module index 110a6e4..85c5f74 100644 --- a/core/modules/contact/contact.module +++ b/core/modules/contact/contact.module @@ -24,8 +24,10 @@ function contact_help($route_name, RouteMatchInterface $route_match) { $output .= '
' . t('Site users can be contacted with a user contact form that keeps their email address private. Users may enable or disable their personal contact forms by editing their My account page. If enabled, a Contact tab leads to a personal contact form displayed on their user profile. Site administrators are still able to use the contact form, even if has been disabled. The Contact tab is not shown when you view your own profile.') . '
'; $output .= '
' . t('Site-wide contact forms') . '
'; $output .= '
' . t('The Contact page provides a simple form for users with the Use the site-wide contact form permission to send comments, feedback, or other requests. You can create forms for directing the contact messages to a set of defined recipients. Common forms for a business site, for example, might include "Website feedback" (messages are forwarded to website administrators) and "Product information" (messages are forwarded to members of the sales department). Email addresses defined within a form are not displayed publicly.', array('@contact' => \Drupal::url('contact.site_page'))) . '

'; - $output .= '
' . t('Navigation') . '
'; - $output .= '
' . t('When the site-wide contact form is enabled, a link in the Footer menu is created, which you can modify on the Menus administration page.', array('@menu' => \Drupal::url('entity.menu.collection'))) . '
'; + if (\Drupal::moduleHandler()->moduleExists('menu_ui')) { + $output .= '
' . t('Navigation') . '
'; + $output .= '
' . t('When the site-wide contact form is enabled, a link in the Footer menu is created, which you can modify on the Menus administration page.', array('@menu' => \Drupal::url('entity.menu.collection'))) . '
'; + } $output .= '
' . t('Customization') . '
'; $output .= '
' . t('If you would like additional text to appear on the site-wide or personal contact page, use a block. You can create and edit blocks on the Blocks administration page.', array('@blocks' => \Drupal::url('block.admin_display'))) . '
'; $output .= ''; @@ -33,7 +35,9 @@ function contact_help($route_name, RouteMatchInterface $route_match) { case 'entity.contact_form.collection': $output = '

' . t('Add one or more forms on this page to set up your site-wide contact form.', array('@form' => \Drupal::url('contact.site_page'))) . '

'; - $output .= '

' . t('A Contact menu item is added to the Footer menu, which you can modify on the Menus administration page.', array('@menu-settings' => \Drupal::url('entity.menu.collection'))) . '

'; + if (\Drupal::moduleHandler()->moduleExists('menu_ui')) { + $output .= '

' . t('A Contact menu item is added to the Footer menu, which you can modify on the Menus administration page.', array('@menu-settings' => \Drupal::url('entity.menu.collection'))) . '

'; + } $output .= '

' . t('If you would like additional text to appear on the site-wide contact page, use a block. You can create and edit blocks on the Blocks administration page.', array('@blocks' => \Drupal::url('block.admin_display'))) . '

'; return $output; } diff --git a/core/modules/forum/config/install/node.type.forum.yml b/core/modules/forum/config/install/node.type.forum.yml index 8ed965d..b03afe0 100644 --- a/core/modules/forum/config/install/node.type.forum.yml +++ b/core/modules/forum/config/install/node.type.forum.yml @@ -1,6 +1,8 @@ langcode: en status: true dependencies: + module: + - forum enforced: module: - forum diff --git a/core/modules/forum/config/install/taxonomy.vocabulary.forums.yml b/core/modules/forum/config/install/taxonomy.vocabulary.forums.yml index 951e4a3..cfbcca5 100644 --- a/core/modules/forum/config/install/taxonomy.vocabulary.forums.yml +++ b/core/modules/forum/config/install/taxonomy.vocabulary.forums.yml @@ -1,6 +1,8 @@ langcode: en status: true dependencies: + module: + - forum enforced: module: - forum diff --git a/core/modules/language/src/Config/LanguageConfigOverride.php b/core/modules/language/src/Config/LanguageConfigOverride.php index 5be48ad..b673f34 100644 --- a/core/modules/language/src/Config/LanguageConfigOverride.php +++ b/core/modules/language/src/Config/LanguageConfigOverride.php @@ -50,13 +50,16 @@ public function __construct($name, StorageInterface $storage, TypedConfigManager /** * {@inheritdoc} */ - public function save() { - // @todo Use configuration schema to validate. - // https://drupal.org/node/2270399 - // Perform basic data validation. - foreach ($this->data as $key => $value) { - $this->validateValue($key, $value); + public function save($has_trusted_data = FALSE) { + if (!$has_trusted_data) { + // @todo Use configuration schema to validate. + // https://drupal.org/node/2270399 + // Perform basic data validation. + foreach ($this->data as $key => $value) { + $this->validateValue($key, $value); + } } + $this->storage->write($this->name, $this->data); // Invalidate the cache tags not only when updating, but also when creating, // because a language config override object uses the same cache tag as the diff --git a/core/modules/node/config/install/system.action.node_promote_action.yml b/core/modules/node/config/install/system.action.node_promote_action.yml index b1b4f05..4b3a9ae 100644 --- a/core/modules/node/config/install/system.action.node_promote_action.yml +++ b/core/modules/node/config/install/system.action.node_promote_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_promote_action +dependencies: + module: + - node diff --git a/core/modules/node/config/install/system.action.node_publish_action.yml b/core/modules/node/config/install/system.action.node_publish_action.yml index e882bf3..af0b82c 100644 --- a/core/modules/node/config/install/system.action.node_publish_action.yml +++ b/core/modules/node/config/install/system.action.node_publish_action.yml @@ -4,3 +4,6 @@ status: true langcode: en type: node plugin: node_publish_action +dependencies: + module: + - node diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php index 8fd6e29..9ce7cb0 100644 --- a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php +++ b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php @@ -110,4 +110,11 @@ public static function simplifyAllowedValues(array $structured_values) { return $values; } + /** + * {@inheritdoc} + */ + protected static function castAllowedValue($value) { + return (float) $value; + } + } diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php index 5992386..2ded4ca 100644 --- a/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php +++ b/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php @@ -73,4 +73,11 @@ protected static function validateAllowedValue($option) { } } + /** + * {@inheritdoc} + */ + protected static function castAllowedValue($value) { + return (int) $value; + } + } diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php index bdf063a..9b785d8 100644 --- a/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php +++ b/core/modules/options/src/Plugin/Field/FieldType/ListItemBase.php @@ -314,11 +314,24 @@ protected static function structureAllowedValues(array $values) { $label = static::structureAllowedValues($label); } $structured_values[] = array( - 'value' => $value, + 'value' => static::castAllowedValue($value), 'label' => $label, ); } return $structured_values; } + /** + * Converts a value to the correct type. + * + * @param mixed $value + * The value to cast. + * + * @return mixed + * The casted value. + */ + protected static function castAllowedValue($value) { + return $value; + } + } diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php index f3bf401..da30810 100644 --- a/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php +++ b/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php @@ -75,4 +75,11 @@ protected static function validateAllowedValue($option) { } } + /** + * {@inheritdoc} + */ + protected static function castAllowedValue($value) { + return (string) $value; + } + } diff --git a/core/modules/options/tests/options_config_install_test/config/install/field.storage.node.field_options_float.yml b/core/modules/options/tests/options_config_install_test/config/install/field.storage.node.field_options_float.yml index f355b2e..1d02c61 100644 --- a/core/modules/options/tests/options_config_install_test/config/install/field.storage.node.field_options_float.yml +++ b/core/modules/options/tests/options_config_install_test/config/install/field.storage.node.field_options_float.yml @@ -11,7 +11,7 @@ type: list_float settings: allowed_values: - - value: 0 + value: !!float 0 label: Zero - value: 0.5 diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php index 5a4bda2..2a0e7a9 100644 --- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php @@ -2294,7 +2294,7 @@ public function calculateCacheMetadata () { $cache_plugin->alterCacheMetadata($is_cacheable, $cache_contexts); } - return [$is_cacheable, $cache_contexts]; + return [(bool) $is_cacheable, $cache_contexts]; } /** diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php index 5aeac87..e01c359 100644 --- a/core/modules/views_ui/src/ViewUI.php +++ b/core/modules/views_ui/src/ViewUI.php @@ -1271,4 +1271,18 @@ public function getThirdPartyProviders() { return $this->storage->getThirdPartyProviders(); } + /** + * {@inheritdoc} + */ + public function trustData() { + return $this->storage->trustData(); + } + + /** + * {@inheritdoc} + */ + public function hasTrustedData() { + return $this->storage->hasTrustedData(); + } + }