diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php index ffdc760..c109ea7 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\Entity\ConfigDependencyManager; +use Drupal\Core\Config\Entity\ConfigEntityDependency; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Site\Settings; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -141,16 +142,11 @@ public function installDefaultConfig($type, $name) { $storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION); $this->installOptionalConfig($storage, ''); } - // Install any optional configuration entities whose type this extension - // provides. This searches all the installed modules config/optional + // Install any optional configuration entities whose dependencies can now + // be met. This searches all the installed modules config/optional // directories. - $provides_config_entity_type = array_reduce($this->configManager->getEntityManager()->getDefinitions(), function ($return, EntityTypeInterface $entity_type) use ($name) { - return $return ?: $entity_type->getProvider() && $entity_type->getConfigPrefix(); - }, FALSE); - if ($provides_config_entity_type) { - $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE); - $this->installOptionalConfig($storage, $name . '.'); - } + $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE); + $this->installOptionalConfig($storage, [$type => $name]); } // Reset all the static caches and list caches. @@ -160,7 +156,7 @@ public function installDefaultConfig($type, $name) { /** * {@inheritdoc} */ - public function installOptionalConfig(StorageInterface $storage = NULL, $prefix = '') { + public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []) { if (!$storage) { // Search the install profile's optional configuration too. $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE); @@ -180,19 +176,38 @@ public function installOptionalConfig(StorageInterface $storage = NULL, $prefix if (!$this->configManager->supportsConfigurationEntities($collection)) { continue; } - $existing_config = $this->getActiveStorages($collection)->listAll($prefix); - $config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storage); - $all_config = array_merge($existing_config, array_keys($config_to_create)); + $existing_config = $this->getActiveStorages($collection)->listAll(); + + $list = array_filter($storage->listAll(), function($config_name) use ($existing_config) { + // Only list configuration that: + // - does not already exist + // - is a configuration entity (this also excludes config that has an + // implicit dependency on modules that are not yet installed) + return !in_array($config_name, $existing_config) && $this->configManager->getEntityTypeIdByName($config_name); + }); + + $all_config = array_merge($existing_config, $list); + $config_to_create = $storage->readMultiple($list); + // Check to see if the corresponding override storage has any overrides. + if ($profile_storage) { + if ($profile_storage->getCollectionName() != $collection) { + $profile_storage = $profile_storage->createCollection($collection); + } + $config_to_create = $profile_storage->readMultiple($list) + $config_to_create; + } foreach ($config_to_create as $config_name => $data) { - // Exclude configuration that: - // - already exists - // - is a not configuration entity - // - or its dependencies cannot be met. - if (in_array($config_name, $existing_config) || - !$this->configManager->getEntityTypeIdByName($config_name) || - !$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config)) { + // Exclude configuration where its dependencies cannot be met. + if (!$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config)) { unset($config_to_create[$config_name]); } + // Exclude configuration that does not have a matching dependency. + elseif (!empty($dependency)) { + // Create a light weight dependency object to check dependencies. + $config_entity = new ConfigEntityDependency($config_name, $data); + if (!$config_entity->hasDependency(key($dependency), reset($dependency))) { + unset($config_to_create[$config_name]); + } + } } if (!empty($config_to_create)) { $this->createConfiguration($collection, $config_to_create, TRUE); diff --git a/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php b/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php index 6d8e489..3e99bce 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigInstallerInterface.php @@ -49,11 +49,13 @@ public function installDefaultConfig($type, $name); * (optional) The configuration storage to search for optional * configuration. If not provided, all enabled extension's optional * configuration directories will be searched. - * @param string $prefix - * (optional) If set, limits the installed configuration to only - * configuration beginning with the provided value. + * @param array $dependency + * (optional) If set, ensures that the configuration being installed has + * this dependency. The format is dependency type as the key ('module', + * 'theme', or 'config') and the dependency name as the value + * ('book', 'bartik', 'views.view.frontpage'). */ - public function installOptionalConfig(StorageInterface $storage = NULL, $prefix = ''); + public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []); /** * Installs all default configuration in the specified collection. diff --git a/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php b/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php index 3e2c7bf..08e7bec 100644 --- a/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php +++ b/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php @@ -106,15 +106,10 @@ function testInstallProfileConfigOverwrite() { // dependency does not get created. $this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.'); + // Installing db_log creates the optional configuration. $this->container->get('module_installer')->install(['dblog']); $this->rebuildContainer(); - // Just installing db_log does not create the optional configuration. - $this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.'); - // Install all available optional configuration. - $this->container->get('config.installer')->installOptionalConfig(); - $this->assertEqual($config_test_storage->load('override_unmet')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.'); - - + $this->assertEqual($config_test_storage->load('override_unmet')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration and is installed when its dependencies are met.'); } } diff --git a/core/modules/config/src/Tests/ConfigOtherModuleTest.php b/core/modules/config/src/Tests/ConfigOtherModuleTest.php index ee03317..aaa90a8 100644 --- a/core/modules/config/src/Tests/ConfigOtherModuleTest.php +++ b/core/modules/config/src/Tests/ConfigOtherModuleTest.php @@ -61,6 +61,12 @@ public function testInstallOtherModuleFirst() { // not throw an error. $this->installModule('config_other_module_config_test'); $this->assertTrue(\Drupal::moduleHandler()->moduleExists('config_other_module_config_test'), 'The config_other_module_config_test module is installed.'); + + // Ensure that optional configuration with unmet dependencies is only + // installed once all the dependencies are met. + $this->assertNull(entity_load('config_test', 'other_module_test_unmet', TRUE), 'The optional configuration whose dependencies are met is not created.'); + $this->installModule('config_install_dependency_test'); + $this->assertTrue(entity_load('config_test', 'other_module_test_unmet', TRUE), 'The optional configuration whose dependencies are met is now created.'); } /** diff --git a/core/modules/config/tests/config_other_module_config_test/config/optional/config_test.dynamic.other_module_test_unmet.yml b/core/modules/config/tests/config_other_module_config_test/config/optional/config_test.dynamic.other_module_test_unmet.yml new file mode 100644 index 0000000..572a79f --- /dev/null +++ b/core/modules/config/tests/config_other_module_config_test/config/optional/config_test.dynamic.other_module_test_unmet.yml @@ -0,0 +1,13 @@ +id: other_module_test_unmet +label: 'Other module test to test optional config installation' +weight: 0 +style: '' +status: true +langcode: en +protected_property: Default +dependencies: + module: + - config_install_dependency_test + enforced: + module: + - config_install_dependency_test