diff --git a/core/includes/config.inc b/core/includes/config.inc index b4a0666..d73a800 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -35,15 +35,15 @@ function config_install_default_config($type, $name) { $source_storage = new FileStorage($config_dir); $target_storage = drupal_container()->get('config.storage'); - $config_changes = array( - 'delete' => array(), - 'create' => array(), - 'change' => array(), - ); - $config_changes['create'] = $source_storage->listAll(); + // Ignore manifest files. + $config_changes = config_sync_get_changes($source_storage, $target_storage, FALSE); if (empty($config_changes['create'])) { return; } + + // Do not overwrite or delete pre-existing configuration. + $config_changes['change'] = array(); + $config_changes['delete'] = array(); $remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage); config_sync_changes($remaining_changes, $source_storage, $target_storage); } @@ -106,44 +106,51 @@ function config($name) { * The storage to synchronize configuration from. * @param Drupal\Core\Config\StorageInterface $target_storage * The storage to synchronize configuration to. + * @param bool $use_manifest + * (optional) Whether to determine changes based on manifest files. Defaults + * to TRUE. * * @return array|bool - * An assocative array containing the differences between source and target + * An associative array containing the differences between source and target * storage, or FALSE if there are no differences. */ -function config_sync_get_changes(StorageInterface $source_storage, StorageInterface $target_storage) { +function config_sync_get_changes(StorageInterface $source_storage, StorageInterface $target_storage, $use_manifest = TRUE) { + $config_changes = array( + 'create' => array(), + 'change' => array(), + 'delete' => array(), + ); + $all_source_names = $source_storage->listAll(); + $all_target_names = $target_storage->listAll(); + // Config entities maintain 'manifest' files that list the objects they // are currently handling. Each file is a simple indexed array of config // object names. In order to generate a list of objects that have been // created or deleted we need to open these files in both the source and // target storage, generate an array of the objects, and compare them. - $source_config_data = array(); - $target_config_data = array(); - foreach ($source_storage->listAll('manifest') as $name) { - if ($source_manifest_data = $source_storage->read($name)) { - $source_config_data = array_merge($source_config_data, $source_manifest_data); + if ($use_manifest) { + $source_config_data = array(); + $target_config_data = array(); + foreach ($source_storage->listAll('manifest') as $name) { + if ($source_manifest_data = $source_storage->read($name)) { + $source_config_data = array_merge($source_config_data, $source_manifest_data); + } + if ($target_manifest_data = $target_storage->read($name)) { + $target_config_data = array_merge($target_config_data, $target_manifest_data); + } } - - if ($target_manifest_data = $target_storage->read($name)) { - $target_config_data = array_merge($target_config_data, $target_manifest_data); + foreach (array_diff_key($target_config_data, $source_config_data) as $name => $value) { + $config_changes['delete'][] = $value['name']; + } + foreach (array_diff_key($source_config_data, $target_config_data) as $name => $value) { + $config_changes['create'][] = $value['name']; } } - - $config_changes = array( - 'create' => array(), - 'change' => array(), - 'delete' => array(), - ); - - foreach (array_diff_key($target_config_data, $source_config_data) as $name => $value) { - $config_changes['delete'][] = $value['name']; - } - - foreach (array_diff_key($source_config_data, $target_config_data) as $name => $value) { - $config_changes['create'][] = $value['name']; + else { + $config_changes['delete'] = array_diff($all_target_names, $all_source_names); + $config_changes['create'] = array_diff($all_source_names, $all_target_names); } - - foreach (array_intersect($source_storage->listAll(), $target_storage->listAll()) as $name) { + foreach (array_intersect($all_source_names, $all_target_names) as $name) { // Ignore manifest files if (substr($name, 0, 9) != 'manifest.') { $source_config_data = $source_storage->read($name); diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallWebTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallWebTest.php new file mode 100644 index 0000000..06950f9 --- /dev/null +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallWebTest.php @@ -0,0 +1,108 @@ + 'Installation functionality', + 'description' => 'Tests installation of configuration objects in installation functionality.', + 'group' => 'Configuration', + ); + } + + function setUp() { + parent::setUp(); + + // 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 re-installation. + */ + function testIntegrationModuleReinstallation() { + $default_config = 'config_integration_test.settings'; + $default_configuration_entity = 'config_test.dynamic.config_integration_test'; + + // Install the config_test module we're integrating with. + module_enable(array('config_test')); + + // Verify the configuration does not exist prior to installation. + $config_static = config($default_config); + $this->assertIdentical($config_static->isNew(), TRUE); + $config_entity = config($default_configuration_entity); + $this->assertIdentical($config_entity->isNew(), TRUE); + + // Install the integration module. + module_enable(array('config_integration_test')); + + // Verify that default module config exists. + $config_static = config($default_config); + $this->assertIdentical($config_static->isNew(), FALSE); + $this->assertIdentical($config_static->get('foo'), 'default setting'); + $config_entity = config($default_configuration_entity); + $this->assertIdentical($config_entity->isNew(), FALSE); + $this->assertIdentical($config_entity->get('label'), 'Default integration config label'); + + // Customize both configuration objects. + $config_static->set('foo', 'customized setting')->save(); + $config_entity->set('label', 'Customized integration config label')->save(); + + // @todo FIXME: Setting config keys WITHOUT SAVING retains the changed config + // object in memory. Every new call to config() MUST revert in-memory changes + // that haven't been saved! + // In other words: This test passes even without this reset, but it shouldn't. + $this->container->get('config.factory')->reset(); + + // Disable and enable the integration module. + module_disable(array('config_integration_test')); + module_enable(array('config_integration_test')); + + // Verify that customized config exists. + $config_static = config($default_config); + $this->assertIdentical($config_static->isNew(), FALSE); + $this->assertIdentical($config_static->get('foo'), 'customized setting'); + $config_entity = config($default_configuration_entity); + $this->assertIdentical($config_entity->isNew(), FALSE); + $this->assertIdentical($config_entity->get('label'), 'Customized integration config label'); + + // Disable and uninstall the integration module. + module_disable(array('config_integration_test')); + module_uninstall(array('config_integration_test')); + + // Verify the integration module's config was uninstalled. + $config_static = config($default_config); + $this->assertIdentical($config_static->isNew(), TRUE); + + // Verify the integration config still exists. + $config_entity = config($default_configuration_entity); + $this->assertIdentical($config_entity->isNew(), FALSE); + $this->assertIdentical($config_entity->get('label'), 'Customized integration config label'); + + // Reinstall the integration module. + module_enable(array('config_integration_test')); + + // Verify the integration module's config was re-installed. + $config_static = config($default_config); + $this->assertIdentical($config_static->isNew(), FALSE); + $this->assertIdentical($config_static->get('foo'), 'default setting'); + + // Verify the customized integration config still exists. + $config_entity = config($default_configuration_entity); + $this->assertIdentical($config_entity->isNew(), FALSE); + $this->assertIdentical($config_entity->get('label'), 'Customized integration config label'); + } + +} diff --git a/core/modules/config/tests/config_integration_test/config/config_integration_test.settings.yml b/core/modules/config/tests/config_integration_test/config/config_integration_test.settings.yml new file mode 100644 index 0000000..1b33ac0 --- /dev/null +++ b/core/modules/config/tests/config_integration_test/config/config_integration_test.settings.yml @@ -0,0 +1 @@ +foo: 'default setting' diff --git a/core/modules/config/tests/config_integration_test/config/config_test.dynamic.config_integration_test.yml b/core/modules/config/tests/config_integration_test/config/config_test.dynamic.config_integration_test.yml new file mode 100644 index 0000000..f87b942 --- /dev/null +++ b/core/modules/config/tests/config_integration_test/config/config_test.dynamic.config_integration_test.yml @@ -0,0 +1,2 @@ +id: config_integration_test +label: 'Default integration config label' diff --git a/core/modules/config/tests/config_integration_test/config_integration_test.info b/core/modules/config/tests/config_integration_test/config_integration_test.info new file mode 100644 index 0000000..8a9db4b --- /dev/null +++ b/core/modules/config/tests/config_integration_test/config_integration_test.info @@ -0,0 +1,6 @@ +name = ConfigTest integration +package = Testing +version = VERSION +core = 8.x +hidden = TRUE +dependencies[] = config_test diff --git a/core/modules/config/tests/config_integration_test/config_integration_test.module b/core/modules/config/tests/config_integration_test/config_integration_test.module new file mode 100644 index 0000000..9c92696 --- /dev/null +++ b/core/modules/config/tests/config_integration_test/config_integration_test.module @@ -0,0 +1,6 @@ +