diff -u b/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php --- b/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Config\Entity\ConfigDependencyManager; use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Site\Settings; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class ConfigInstaller implements ConfigInstallerInterface { @@ -161,13 +162,19 @@ */ public function installOptionalConfig(StorageInterface $storage = NULL, $prefix = '') { if (!$storage) { - // Optional configuration in the profile can never be used. - $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE); + // Search the install profile's optional configuration too. + $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE); + // The extension install storage ensures that overrides are used. + $profile_storage = NULL; + } + else { + // Creates a profile storage to search for overrides. + $profile_install_path = $this->drupalGetPath('module', $this->drupalGetProfile()) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY; + $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION); } + $collection_info = $this->configManager->getConfigCollectionInfo(); $enabled_extensions = $this->getEnabledExtensions(); - // Gets a profile storage to search for overrides if necessary. - $profile_storage = $this->getProfileStorage(); foreach ($collection_info->getCollectionNames() as $collection) { if (!$this->configManager->supportsConfigurationEntities($collection)) { @@ -593,7 +600,7 @@ } /** - * Wrapper for drupal_get_profile(). + * Gets the install profile from settings. * * @return string|null $profile * The name of the installation profile or NULL if no installation profile @@ -601,7 +608,9 @@ * of the installer or during unit tests. */ protected function drupalGetProfile() { - return drupal_get_profile(); + // Settings is safe to use because settings.php is written before any module + // is installed. + return Settings::get('install_profile'); } /** diff -u b/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php b/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php --- b/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php +++ b/core/modules/config/src/Tests/ConfigInstallProfileOverrideTest.php @@ -78,13 +78,43 @@ $tour = Tour::load('language-add'); $this->assertEqual(count($tour->getTips()), 3, 'Optional configuration that is not overridden is not affected.'); - // Ensure that optional configuration is not installed from a profile. - $id = 'testing_config_overrides'; - $this->assertNull(Tour::load($id), 'The tour contained in the profile\'s optional directory does not exist.'); - // Make that we don't get false positives from the assertion above. + // Ensure that optional configuration from a profile is created if + // dependencies are met. + $this->assertEqual(Tour::load('testing_config_overrides')->label(), 'Config override test'); + + // Ensure that optional configuration from a profile is not created if + // dependencies are not met. Cannot use the entity system since the entity + // type does not exist. $optional_dir = drupal_get_path('module', 'testing_config_overrides') . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY; - $storage = new FileStorage($optional_dir); - $this->assertTrue($storage->exists('tour.tour.' . $id), 'The tour does exist in the profile\'s optional directory.'); + $optional_storage = new FileStorage($optional_dir); + foreach (['config_test.dynamic.dotted.default', 'config_test.dynamic.override','config_test.dynamic.override_unmet'] as $id) { + $this->assertTrue(\Drupal::config($id)->isNew(), "The config_test entity $id contained in the profile's optional directory does not exist."); + // Make that we don't get false positives from the assertion above. + $this->assertTrue($optional_storage->exists($id), "The config_test entity $id does exist in the profile's optional directory."); + } + + // Install the config_test module and ensure that the override from the + // install profile is not used. Optional configuration can not override + // configuration in a modules config/install directory. + $this->container->get('module_installer')->install(['config_test']); + $this->rebuildContainer(); + $config_test_storage = \Drupal::entityManager()->getStorage('config_test'); + $this->assertEqual($config_test_storage->load('dotted.default')->label(), 'Default', 'The config_test entity is not overridden by the profile optional configuration.'); + // Test that override of optional configuration does work. + $this->assertEqual($config_test_storage->load('override')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.'); + // Test that override of optional configuration which introduces an unmet + // dependency does not get created. + $this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.'); + + $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.'); + + } } only in patch2: unchanged: --- a/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php +++ b/core/lib/Drupal/Core/Config/ExtensionInstallStorage.php @@ -45,7 +45,7 @@ class ExtensionInstallStorage extends InstallStorage { * default collection. * @param bool $include_profile * (optional) Whether to include the install profile in extensions to - * search. + * search and to get overrides from. */ public function __construct(StorageInterface $config_storage, $directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION, $include_profile = TRUE) { $this->configStorage = $config_storage; @@ -82,27 +82,24 @@ protected function getAllFolders() { $this->folders = array(); $this->folders += $this->getComponentNames('core', array('core')); + $install_profile = Settings::get('install_profile'); $extensions = $this->configStorage->read('core.extension'); if (!empty($extensions['module'])) { $modules = $extensions['module']; - if (!$this->includeProfile) { - if ($install_profile = Settings::get('install_profile')) { - unset($modules[$install_profile]); - } - } + // Remove the install profile as this is handled later. + unset($modules[$install_profile]); $this->folders += $this->getComponentNames('module', array_keys($modules)); } if (!empty($extensions['theme'])) { $this->folders += $this->getComponentNames('theme', array_keys($extensions['theme'])); } - // The install profile can override module default configuration. We do - // this by replacing the config file path from the module/theme with the - // install profile version if there are any duplicates. - $profile_folders = $this->getComponentNames('profile', array(drupal_get_profile())); - $folders_to_replace = array_intersect_key($profile_folders, $this->folders); - if (!empty($folders_to_replace)) { - $this->folders = array_merge($this->folders, $folders_to_replace); + if ($this->includeProfile) { + // The install profile can override module default configuration. We do + // this by replacing the config file path from the module/theme with the + // install profile version if there are any duplicates. + $profile_folders = $this->getComponentNames('profile', array(drupal_get_profile())); + $this->folders = $profile_folders + $this->folders; } } return $this->folders; only in patch2: unchanged: --- /dev/null +++ b/core/modules/config/tests/config_test/config/optional/config_test.dynamic.override.yml @@ -0,0 +1,6 @@ +# See \Drupal\config\Tests\ConfigInstallProfileOverrideTest +id: override +label: Default +weight: 0 +protected_property: Default +status: 1 only in patch2: unchanged: --- /dev/null +++ b/core/modules/config/tests/config_test/config/optional/config_test.dynamic.override_unmet.yml @@ -0,0 +1,10 @@ +# See \Drupal\config\Tests\ConfigInstallProfileOverrideTest +# This configuration entity has dependencies which would be met. +id: override_unmet +label: Default +weight: 0 +protected_property: Default +status: 1 +dependencies: + module: + - tour \ No newline at end of file only in patch2: unchanged: --- /dev/null +++ b/core/profiles/testing_config_overrides/config/optional/config_test.dynamic.dotted.default.yml @@ -0,0 +1,6 @@ +id: dotted.default +label: 'Default install profile override' +weight: 0 +protected_property: Default +# Intentionally commented out to verify default status behavior. +# status: 1 only in patch2: unchanged: --- /dev/null +++ b/core/profiles/testing_config_overrides/config/optional/config_test.dynamic.override.yml @@ -0,0 +1,8 @@ +id: override +label: Override +weight: 0 +protected_property: Default +status: 1 +dependencies: + module: + - tour \ No newline at end of file only in patch2: unchanged: --- /dev/null +++ b/core/profiles/testing_config_overrides/config/optional/config_test.dynamic.override_unmet.yml @@ -0,0 +1,8 @@ +id: override_unmet +label: Override +weight: 0 +protected_property: Default +status: 1 +dependencies: + module: + - dblog \ No newline at end of file