diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index eac2f97..00d8bc0 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -335,6 +335,13 @@ const CONFIG_STAGING_DIRECTORY = 'staging'; /** + * $config_directories key for snapshot directory. + * + * @see config_get_config_directory + */ +const CONFIG_SNAPSHOT_DIRECTORY = 'snapshot'; + +/** * Starts the timer with the specified name. * * If you start and stop the same timer multiple times, the measured intervals @@ -2480,6 +2487,11 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { // Register the EntityManager. $container->register('plugin.manager.entity', 'Drupal\Core\Entity\EntityManager'); + + // Register import snapshot configuration storage. + $container + ->register('config.storage.snapshot', 'Drupal\Core\Config\FileStorage') + ->addArgument(config_get_config_directory(CONFIG_SNAPSHOT_DIRECTORY)); } return $container; } diff --git a/core/includes/config.inc b/core/includes/config.inc index ef43774..2390109 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -137,6 +137,7 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto function config_import() { // Retrieve a list of differences between staging and the active configuration. $source_storage = drupal_container()->get('config.storage.staging'); + $snapshot_storage = drupal_container()->get('config.storage.snapshot'); $target_storage = drupal_container()->get('config.storage'); $config_changes = config_sync_get_changes($source_storage, $target_storage); @@ -157,6 +158,7 @@ function config_import() { try { $remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage); config_sync_changes($remaining_changes, $source_storage, $target_storage); + config_import_create_snapshot($target_storage, $snapshot_storage); } catch (ConfigException $e) { watchdog_exception('config_import', $e); @@ -167,6 +169,53 @@ function config_import() { } /** + * Creates a configuration snapshot following a successful import. + * + * @param Drupal\Core\Config\StorageInterface $source_storage + * The storage to synchronize configuration from. + * @param Drupal\Core\Config\StorageInterface $target_storage + * The storage to synchronize configuration to. + */ +function config_import_create_snapshot(StorageInterface $source_storage, StorageInterface $snapshot_storage) { + foreach ($snapshot_storage->listAll() as $name) { + $snapshot_storage->delete($name); + } + foreach ($source_storage->listAll() as $name) { + $snapshot_storage->write($name, $source_storage->read($name)); + } +} + +/** + * Resets configuration to the state of the last import. + */ +function config_restore_from_snapshot() { + $source_storage = drupal_container()->get('config.storage.snapshot'); + $target_storage = drupal_container()->get('config.storage'); + $config_changes = config_sync_get_changes($source_storage, $target_storage); + if (empty($config_changes)) { + return; + } + + if (!lock_acquire('config_import')) { + return FALSE; + } + + $success = TRUE; + try { + $remaining_changes = config_import_invoke_owner($config_changes, $source_storage, $target_storage); + config_sync_changes($remaining_changes, $source_storage, $target_storage); + // Flush all caches and reset static variables after a successful import. + drupal_flush_all_caches(); + } + catch (ConfigException $e) { + watchdog_exception('config_restore_from_snapshot', $e); + $success = FALSE; + } + lock_release(__FUNCTION__); + return $success; +} + +/** * Invokes MODULE_config_import() callbacks for configuration changes. * * @param array $config_changes diff --git a/core/includes/install.inc b/core/includes/install.inc index 023e204..8a200a3 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -263,6 +263,9 @@ function drupal_install_config_directories() { CONFIG_STAGING_DIRECTORY => array( 'path' => 'config_' . $config_directories_hash . '/staging', ), + CONFIG_SNAPSHOT_DIRECTORY => array( + 'path' => 'config_' . $config_directories_hash . '/snapshot', + ), ), 'required' => TRUE, ); @@ -271,7 +274,7 @@ function drupal_install_config_directories() { } // Ensure the config directories exist or can be created, and are writable. - foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $config_type) { + foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY, CONFIG_SNAPSHOT_DIRECTORY) as $config_type) { // This should never fail, since if the config directory was specified in // settings.php it will have already been created and verified earlier, and // if it wasn't specified in settings.php, it is created here inside the diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index fdb819a..8f19851 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -858,7 +858,7 @@ protected function prepareEnvironment() { $GLOBALS['config_directories'] = array(); $this->configDirectories = array(); include_once DRUPAL_ROOT . '/core/includes/install.inc'; - foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) { + foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY, CONFIG_SNAPSHOT_DIRECTORY) as $type) { // Assign the relative path to the global variable. $path = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type; $GLOBALS['config_directories'][$type]['path'] = $path;