diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php index c8defe7..8ece7ec 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -85,9 +85,17 @@ public function __construct(ConfigFactoryInterface $config_factory, StorageInter } /** - * {@inheritdoc} + * Gather default configuration to install. + * + * @param string $type + * Type of extension to install. + * @param string $name + * Name of extension to install. + * + * @return + * Array with list of configuration keys to install. */ - public function installDefaultConfig($type, $name) { + public function gatherDefaultConfig($type, $name) { $extension_path = drupal_get_path($type, $name); // If the extension provides configuration schema clear the definitions. if (is_dir($extension_path . '/' . InstallStorage::CONFIG_SCHEMA_DIRECTORY)) { @@ -110,9 +118,27 @@ public function installDefaultConfig($type, $name) { foreach ($collection_info->getCollectionNames(TRUE) as $collection) { $config_to_install = $this->listDefaultConfigCollection($collection, $type, $name, $enabled_extensions); - if (!empty($config_to_install)) { - $this->createConfiguration($collection, $config_to_install); + // Profiles can provide default configuration on behalf of other modules if + // the configuration already exists remove it as it was used when the + // extension that owns the configuration was installed. + if ($type == 'profile') { + $config_to_install = array_filter($config_to_install, function ($config_name) { + return !$this->activeStorage->exists($config_name); + }); } + return $config_to_install; + } + } + + /** + * {@inheritdoc} + */ + public function installDefaultConfig($type, $name) { + $source_storage = new ExtensionInstallStorage($this->activeStorage); + $config_to_install = $this->gatherDefaultConfig($type, $name); + + if (!empty($config_to_install)) { + $this->createConfiguration($collection, $config_to_install); } $this->configFactory->setOverrideState($old_state); // Reset all the static caches and list caches. @@ -322,4 +348,29 @@ public function setSyncing($status) { public function isSyncing() { return $this->isSyncing; } + + /** + * Validate default configuration before installation. + * + * @param string $type + * Type of extension to install. + * @param string $name + * Name of extension to install. + * + * @return bool + * Whether all the default configuration of this module can be installed. + * If any of the default configuration already exists in the system, returns + * FALSE. + */ + public function validatePreInstall($type, $name) { + $config_to_install = $this->gatherDefaultConfig($type, $name); + foreach ($config_to_install as $config_name) { + if ($this->activeStorage->exists($config_name)) { + drupal_set_message(t('%extension cannot be installed because default configuration named %config_name already exists in active configuration.', array('%config_name' => $config_name, '%extension' => $name)), 'error'); + return FALSE; + } + } + return TRUE; + } + } diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 62cdd06..1ea4fc0 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -635,50 +635,48 @@ public static function parseDependency($dependency) { */ public function install(array $module_list, $enable_dependencies = TRUE) { $extension_config = \Drupal::config('core.extension'); - if ($enable_dependencies) { - // Get all module data so we can find dependencies and sort. - $module_data = system_rebuild_module_data(); - $module_list = $module_list ? array_combine($module_list, $module_list) : array(); - if (array_diff_key($module_list, $module_data)) { - // One or more of the given modules doesn't exist. - return FALSE; - } + // Get all module data so we can find dependencies and sort. + $module_data = system_rebuild_module_data(); + $module_list = $module_list ? array_combine($module_list, $module_list) : array(); + if (array_diff_key($module_list, $module_data)) { + // One or more of the given modules doesn't exist. + return FALSE; + } - // Only process currently uninstalled modules. - $installed_modules = $extension_config->get('module') ?: array(); - if (!$module_list = array_diff_key($module_list, $installed_modules)) { - // Nothing to do. All modules already installed. - return TRUE; - } + // Only process currently uninstalled modules. + $installed_modules = $extension_config->get('module') ?: array(); + if (!$module_list = array_diff_key($module_list, $installed_modules)) { + // Nothing to do. All modules already installed. + return TRUE; + } - // Conditionally add the dependencies to the list of modules. - if ($enable_dependencies) { - // Add dependencies to the list. The new modules will be processed as - // the while loop continues. - while (list($module) = each($module_list)) { - foreach (array_keys($module_data[$module]->requires) as $dependency) { - if (!isset($module_data[$dependency])) { - // The dependency does not exist. - return FALSE; - } + // Conditionally add the dependencies to the list of modules. + if ($enable_dependencies) { + // Add dependencies to the list. The new modules will be processed as + // the while loop continues. + while (list($module) = each($module_list)) { + foreach (array_keys($module_data[$module]->requires) as $dependency) { + if (!isset($module_data[$dependency])) { + // The dependency does not exist. + return FALSE; + } - // Skip already installed modules. - if (!isset($module_list[$dependency]) && !isset($installed_modules[$dependency])) { - $module_list[$dependency] = $dependency; - } + // Skip already installed modules. + if (!isset($module_list[$dependency]) && !isset($installed_modules[$dependency])) { + $module_list[$dependency] = $dependency; } } } + } - // Set the actual module weights. - $module_list = array_map(function ($module) use ($module_data) { - return $module_data[$module]->sort; - }, $module_list); + // Set the actual module weights. + $module_list = array_map(function ($module) use ($module_data) { + return $module_data[$module]->sort; + }, $module_list); - // Sort the module list by their weights (reverse). - arsort($module_list); - $module_list = array_keys($module_list); - } + // Sort the module list by their weights (reverse). + arsort($module_list); + $module_list = array_keys($module_list); // Required for module installation checks. include_once DRUPAL_ROOT . '/core/includes/install.inc'; @@ -700,7 +698,12 @@ public function install(array $module_list, $enable_dependencies = TRUE) { '@max' => DRUPAL_EXTENSION_NAME_MAX_LENGTH, ))); } - + // Validate default configuration of this module. Bail if unable to + // install. Should not continue installing more modules because those + // may depend on this one. + if (!\Drupal::service('config.installer')->validatePreInstall($module_data[$module]->getType(), $module)) { + break; + } $extension_config ->set("module.$module", 0) ->set('module', module_config_sort($extension_config->get('module'))) @@ -731,7 +734,6 @@ public function install(array $module_list, $enable_dependencies = TRUE) { $module_filenames[$name] = new Extension('module', $pathname, $filename); } } - // Update the module handler in order to load the module's code. // This allows the module to participate in hooks and its existence to // be discovered by other modules. @@ -789,7 +791,7 @@ public function install(array $module_list, $enable_dependencies = TRUE) { // configuration. $config_installer->resetSourceStorage(); } - \Drupal::service('config.installer')->installDefaultConfig('module', $module); + \Drupal::service('config.installer')->installDefaultConfig($module_data[$module]->getType(), $module); // If the module has no current updates, but has some that were // previously removed, set the version to the value of diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallWebTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallWebTest.php index 776daa4..39212a3 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallWebTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallWebTest.php @@ -96,6 +96,13 @@ function testIntegrationModuleReinstallation() { $config_entity = \Drupal::config($default_configuration_entity); $this->assertIdentical($config_entity->isNew(), FALSE); $this->assertIdentical($config_entity->get('label'), 'Customized integration config label'); + + // Verify that installing a module with default configuration that already + // exists in active configuration produces a warning. + \Drupal::moduleHandler()->install(array('config_install_fail')); + $messages = drupal_get_messages('error'); + $expected_message = t('%extension cannot be installed because default configuration named %config_name already exists in active configuration.', array('%config_name' => 'config_test.dynamic.dotted.default', '%extension_name' => 'config_install_fail')); + $this->assertTrue(in_array($expected_message, $messages['error']), 'Error message about existing configuration exists.'); } /** diff --git a/core/modules/config/tests/config_install_fail/config/config_test.dynamic.dotted.default.yml b/core/modules/config/tests/config_install_fail/config/config_test.dynamic.dotted.default.yml new file mode 100644 index 0000000..6e2af21 --- /dev/null +++ b/core/modules/config/tests/config_install_fail/config/config_test.dynamic.dotted.default.yml @@ -0,0 +1,6 @@ +id: dotted.default +label: 'Config install fail' +weight: 0 +protected_property: Default +# Intentionally commented out to verify default status behavior. +# status: 1 diff --git a/core/modules/config/tests/config_install_fail/config_install_fail.info.yml b/core/modules/config/tests/config_install_fail/config_install_fail.info.yml new file mode 100644 index 0000000..ebe72ad --- /dev/null +++ b/core/modules/config/tests/config_install_fail/config_install_fail.info.yml @@ -0,0 +1,8 @@ +name: 'Configuration test install fial' +type: module +package: Testing +version: VERSION +core: 8.x +hidden: true +dependencies: + - config_test diff --git a/core/modules/config/tests/config_install_fail/config_install_fail.module b/core/modules/config/tests/config_install_fail/config_install_fail.module new file mode 100644 index 0000000..e77c7ce --- /dev/null +++ b/core/modules/config/tests/config_install_fail/config_install_fail.module @@ -0,0 +1,6 @@ +installConfig(array('field_test_config')); $active = $this->container->get('config.storage'); $staging = $this->container->get('config.storage.staging'); $this->copyConfig($active, $staging); diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php index b0485c4..c3142a3 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php @@ -17,6 +17,10 @@ class FieldImportDeleteTest extends FieldUnitTestBase { /** * Modules to enable. * + * The default configuration provided by field_test_config is imported by + * \Drupal\field\Tests\FieldUnitTestBase::setUp() when it installs field + * configuration. + * * @var array */ public static $modules = array('field_test_config'); @@ -57,9 +61,6 @@ public function testImportDelete() { // Create a second bundle for the 'Entity test' entity type. entity_test_create_bundle('test_bundle'); - // Import default config. - $this->installConfig(array('field_test_config')); - // Get the uuid's for the fields. $field_uuid = entity_load('field_config', $field_id)->uuid(); $field_uuid_2 = entity_load('field_config', $field_id_2)->uuid();