diff -u b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php --- b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php +++ b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php @@ -102,15 +102,23 @@ /** * Shows diff of specificed configuration file. * - * @param string $config_file + * @param string $source_name * The name of the configuration file. + * @param string $target_name + * (optional) The name of the target configuration file if different from + * the $source_name. + * @param string $collection + * (optional) The configuration collection name. Defaults to the default + * collection. * * @return string * Table showing a two-way diff between the active and staged configuration. */ - public function diff($source_name, $target_name = NULL) { - - $diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $source_name, $target_name); + public function diff($source_name, $target_name = NULL, $collection = NULL) { + if (!isset($collection)) { + $collection = StorageInterface::DEFAULT_COLLECTION; + } + $diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $source_name, $target_name, $collection); $formatter = new \DrupalDiffFormatter(); $formatter->show_header = FALSE; diff -u b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php --- b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php +++ b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php @@ -226,14 +226,20 @@ ); foreach ($config_names as $config_name) { - // @todo Add collection to diffing if ($config_change_type == 'rename') { $names = $storage_comparer->extractRenameNames($config_name); - $href = $this->urlGenerator->getPathFromRoute('config.diff', array('source_name' => $names['old_name'], 'target_name' => $names['new_name'])); + $route_options = array('source_name' => $names['old_name'], 'target_name' => $names['new_name']); $config_name = $this->t('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name'])); } else { - $href = $this->urlGenerator->getPathFromRoute('config.diff', array('source_name' => $config_name)); + $route_options = array('source_name' => $config_name); + } + if ($collection != StorageInterface::DEFAULT_COLLECTION) { + $route_options['collection'] = $collection; + $href = $this->urlGenerator->getPathFromRoute('config.diff_collection', $route_options); + } + else { + $href = $this->urlGenerator->getPathFromRoute('config.diff', $route_options); } $links['view_diff'] = array( 'title' => $this->t('View differences'), diff -u b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php --- b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigExportImportUITest.php @@ -176,6 +176,24 @@ $this->assertFalse(in_array('collection/test2/config_test.another_delete.yml', $files), 'Config export does not contain collection/test2/config_test.another_delete.yml.'); $this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload'); + // Verify that there are configuration differences to import. + $this->drupalGet('admin/config/development/configuration'); + $this->assertNoText(t('There are no configuration changes.')); + $this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test1'))); + $this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test2'))); + $this->assertText('config_test.create'); + $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.create'); + $this->assertText('config_test.update'); + $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.update'); + $this->assertText('config_test.delete'); + $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.delete'); + $this->assertText('config_test.another_create'); + $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_create'); + $this->assertText('config_test.another_update'); + $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_update'); + $this->assertText('config_test.another_delete'); + $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_delete'); + $this->drupalPostForm(NULL, array(), 'Import all'); $this->assertText(t('There are no configuration changes.')); only in patch2: unchanged: --- a/core/lib/Drupal/Core/Config/ConfigManager.php +++ b/core/lib/Drupal/Core/Config/ConfigManager.php @@ -100,7 +100,11 @@ public function getConfigFactory() { /** * {@inheritdoc} */ - public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL) { + public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL, $collection = StorageInterface::DEFAULT_COLLECTION) { + if ($collection != StorageInterface::DEFAULT_COLLECTION) { + $source_storage = $source_storage->createCollection($collection); + $target_storage = $target_storage->createCollection($collection); + } if (!isset($target_name)) { $target_name = $source_name; } only in patch2: unchanged: --- a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php @@ -51,13 +51,16 @@ public function getConfigFactory(); * @param string $target_name * (optional) The name of the configuration object in the target storage. * If omitted, the source name is used. + * @param string $collection + * (optional) The configuration collection name. Defaults to the default + * collection. * * @return core/lib/Drupal/Component/Diff * A formatted string showing the difference between the two storages. * * @todo Make renderer injectable */ - public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL); + public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL, $collection = StorageInterface::DEFAULT_COLLECTION); /** * Creates a configuration snapshot following a successful import. only in patch2: unchanged: --- a/core/modules/config/config.routing.yml +++ b/core/modules/config/config.routing.yml @@ -14,6 +14,14 @@ config.diff: requirements: _permission: 'synchronize configuration' +config.diff_collection: + path: '/admin/config/development/configuration/sync/diff_collection/{collection}/{source_name}/{target_name}' + defaults: + _content: '\Drupal\config\Controller\ConfigController::diff' + target_name: NULL + requirements: + _permission: 'synchronize configuration' + config.export_download: path: '/admin/config/development/configuration/full/export-download' defaults: only in patch2: unchanged: --- a/core/modules/config/lib/Drupal/config/Tests/ConfigDiffTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigDiffTest.php @@ -113,4 +113,36 @@ function testDiff() { $this->assertEqual(count($diff->edits), 2, 'There are two items in the diff.'); } + /** + * Tests calculating the difference between two sets of config collections. + */ + function testCollectionDiff() { + /** @var \Drupal\Core\Config\StorageInterface $active */ + $active = $this->container->get('config.storage'); + /** @var \Drupal\Core\Config\StorageInterface $staging */ + $staging = $this->container->get('config.storage.staging'); + $active_test_collection = $active->createCollection('test'); + $staging_test_collection = $staging->createCollection('test'); + + $config_name = 'config_test.test'; + $data = array('foo' => 'bar'); + + $active->write($config_name, $data); + $staging->write($config_name, $data); + $active_test_collection->write($config_name, $data); + $staging_test_collection->write($config_name, array('foo' => 'baz')); + + // Test the fields match in the default collection diff. + $diff = \Drupal::service('config.manager')->diff($active, $staging, $config_name); + $this->assertEqual($diff->edits[0]->type, 'copy', 'The first item in the diff is a copy.'); + $this->assertEqual(count($diff->edits), 1, 'There is one item in the diff'); + + // Test that the differences are detected when diffing the collection. + $diff = \Drupal::service('config.manager')->diff($active, $staging, $config_name, NULL, 'test'); + $this->assertEqual($diff->edits[0]->type, 'change', 'The second item in the diff is a copy.'); + $this->assertEqual($diff->edits[0]->orig, array('foo: bar')); + $this->assertEqual($diff->edits[0]->closing, array('foo: baz')); + $this->assertEqual($diff->edits[1]->type, 'copy', 'The second item in the diff is a copy.'); + } + }