diff --git a/core/modules/config/config.routing.yml b/core/modules/config/config.routing.yml index f2a944a..36e6f89 100644 --- a/core/modules/config/config.routing.yml +++ b/core/modules/config/config.routing.yml @@ -54,3 +54,10 @@ config.export_single: config_name: NULL requirements: _permission: 'export configuration' + +config.snapshot_diff: + path: '/admin/config/development/configuration/sync/snapshot-diff/{storage}' + defaults: + _content: '\Drupal\config\Controller\ConfigController::snapshotDiff' + requirements: + _permission: 'import configuration' diff --git a/core/modules/config/lib/Drupal/config/Controller/ConfigController.php b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php index dd7a0c3..c27a41b 100644 --- a/core/modules/config/lib/Drupal/config/Controller/ConfigController.php +++ b/core/modules/config/lib/Drupal/config/Controller/ConfigController.php @@ -10,16 +10,17 @@ use Drupal\Component\Archiver\ArchiveTar; use Drupal\Core\Config\ConfigManagerInterface; use Drupal\Core\Config\StorageInterface; -use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\system\FileDownloadController; use Symfony\Component\Yaml\Dumper; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\Config\StorageComparer; +use Drupal\Core\Controller\ControllerBase; /** * Returns responses for config module routes. */ -class ConfigController implements ContainerInjectionInterface { +class ConfigController extends ControllerBase { /** * The target storage. @@ -36,6 +37,13 @@ class ConfigController implements ContainerInjectionInterface { protected $sourceStorage; /** + * The snapshot. + * + * @var \Drupal\Core\Config\StorageInterface + */ + protected $snapshotStorage; + + /** * The configuration manager. * * @var \Drupal\Core\Config\ConfigManagerInterface @@ -56,6 +64,7 @@ public static function create(ContainerInterface $container) { return new static( $container->get('config.storage'), $container->get('config.storage.staging'), + $container->get('config.storage.snapshot'), $container->get('config.manager'), new FileDownloadController() ); @@ -71,9 +80,10 @@ public static function create(ContainerInterface $container) { * @param \Drupal\system\FileDownloadController $file_download_controller * The file download controller. */ - public function __construct(StorageInterface $target_storage, StorageInterface $source_storage, ConfigManagerInterface $config_manager, FileDownloadController $file_download_controller) { + public function __construct(StorageInterface $target_storage, StorageInterface $source_storage, StorageInterface $snapshot_storage, ConfigManagerInterface $config_manager, FileDownloadController $file_download_controller) { $this->targetStorage = $target_storage; $this->sourceStorage = $source_storage; + $this->snapshotStorage = $snapshot_storage; $this->configManager = $config_manager; $this->fileDownloadController = $file_download_controller; } @@ -134,9 +144,66 @@ public function diff($source_name, $target_name = NULL) { ), ), '#title' => "Back to 'Synchronize configuration' page.", - '#href' => 'admin/config/development/configuration', + '#route_name' => 'config.sync', ); return $build; } + + /** + * Show diff of snapshot and active/staging storage. + * + * @return string + * Table showing a two-way diff between the snapshot and + * active/staged configuration. + */ + public function snapshotDiff($storage = 'active') { + $build = array(); + + // Add the CSS for the inline diff. + $build['#attached']['css'][] = drupal_get_path('module', 'system') . '/css/system.diff.css'; + + if ($storage == 'active') { + $build['#title'] = t('View changes between snapshot and active configuration.'); + $used_storage = $this->targetStorage; + $compare_button = $this->l($this->t('Compare snapshot with staging storage'), 'config.snapshot_diff', array('storage' => 'staging'), array('attributes' => array('class' => array('button')))); + } + else { + $build['#title'] = t('View changes between snapshot and staging configuration.'); + $used_storage = $this->sourceStorage; + $compare_button = $this->l($this->t('Compare snapshot with active storage'), 'config.snapshot_diff', array('storage' => 'active'), array('attributes' => array('class' => array('button')))); + } + + $build['compare']['#markup'] = $compare_button; + + $storage_snapshot_comparer = new StorageComparer($used_storage, $this->snapshotStorage); + + // Collect changes. + $storage_snapshot_comparer->createChangelist(); + $changelist = $storage_snapshot_comparer->getChangelist(); + + // Verify that we have an initial snapshot that matches the active/staging + // configuration. + if ($storage_snapshot_comparer->createChangelist()->hasChanges()) { + // Show changes for each configuration object. + foreach ($changelist as $op) { + foreach ($op as $name) { + $diff = $this->configManager->diff($this->snapshotStorage, $used_storage, $name); + $formatter = new \DrupalDiffFormatter(); + $formatter->show_header = FALSE; + $build[$name] = array( + '#prefix' => '
' . $this->t('Use the upload button below.') . '
', ); diff --git a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php index 19f417f..42d1c95 100644 --- a/core/modules/config/lib/Drupal/config/Form/ConfigSync.php +++ b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php @@ -92,6 +92,13 @@ class ConfigSync extends FormBase { protected $themeHandler; /** + * The snapshot configuration object. + * + * @var \Drupal\Core\Config\StorageInterface + */ + protected $snapshotStorage; + + /** * Constructs the object. * * @param \Drupal\Core\Config\StorageInterface $sourceStorage @@ -123,6 +130,7 @@ public function __construct(StorageInterface $sourceStorage, StorageInterface $t $this->typedConfigManager = $typed_config; $this->moduleHandler = $module_handler; $this->themeHandler = $theme_handler; + $this->snapshotStorage = $snapshotStorage; } /** @@ -138,7 +146,8 @@ public static function create(ContainerInterface $container) { $container->get('url_generator'), $container->get('config.typed'), $container->get('module_handler'), - $container->get('theme_handler') + $container->get('theme_handler'), + $container->get('config.storage.snapshot') ); } @@ -153,6 +162,16 @@ public function getFormId() { * {@inheritdoc} */ public function buildForm(array $form, array &$form_state) { + $active_snapshot_comparer = new StorageComparer($this->targetStorage, $this->snapshotStorage); + + // Verify that we have an initial snapshot that matches the active + // configuration. + if ($active_snapshot_comparer->createChangelist()->hasChanges()) { + drupal_set_message($this->t('Changes have been made to your active configuration, which might be lost on the next import attempt. You can find and review the differences between snapshot and active/staging storage here: Compare snapshot and active/staging storage.', array( + '@link' => $this->url('config.snapshot_diff', array('storage' => 'active')))), 'warning'); + + } + $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit',