diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index fdcb01b..75294b6 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 @@ -2468,6 +2475,10 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { // Register the KeyValueStore factory. $container ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory'); + // 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 5e07c54..6e832fe 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); // Flush all caches and reset static variables after a successful import. drupal_flush_all_caches(); } @@ -169,6 +171,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 e74e06b..c392be0 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -262,6 +262,9 @@ function drupal_install_config_directories() { CONFIG_STAGING_DIRECTORY => array( 'path' => 'config/staging_' . drupal_hash_base64(drupal_random_bytes(55)), ), + CONFIG_SNAPSHOT_DIRECTORY => array( + 'path' => 'config/snapshot_' . drupal_hash_base64(drupal_random_bytes(55)), + ), ), 'required' => TRUE, ); @@ -270,7 +273,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 dc6dae4..dc35917 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -856,7 +856,7 @@ protected function prepareEnvironment() { // a test-prefix-specific directory within the public files directory. // @see config_get_config_directory() $GLOBALS['config_directories'] = array(); - foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) { + foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY, CONFIG_SNAPSHOT_DIRECTORY) as $type) { $GLOBALS['config_directories'][$type]['path'] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type; }