diff --git a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php
index b5fddb1..b44c561 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php
@@ -72,14 +72,33 @@ public function onConfigImporterValidate(ConfigImporterEvent $event) {
}
}
$config_importer = $event->getConfigImporter();
- if ($config_importer->getStorageComparer()->getSourceStorage()->exists('core.extension')) {
+ if ($this->validateCoreExtension($config_importer)) {
$this->validateModules($config_importer);
$this->validateThemes($config_importer);
$this->validateDependencies($config_importer);
}
- else {
+ }
+
+ /**
+ * Validates the core.extension configuration file.
+ *
+ * @param \Drupal\Core\Config\ConfigImporter $config_importer
+ * The configuration importer.
+ *
+ * @return bool
+ * TRUE if the core.extension is valid, FALSE if not.
+ */
+ protected function validateCoreExtension(ConfigImporter $config_importer) {
+ $data = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension');
+ if ($data === FALSE) {
$config_importer->logError($this->t('The core.extension configuration does not exist.'));
+ return FALSE;
+ }
+ elseif (empty($data['versions'])) {
+ $config_importer->logError($this->t('The core.extension file is out-of-date. Update the codebase of the source site, run update.php and export the configuration again.'));
+ return FALSE;
}
+ return TRUE;
}
/**
@@ -97,6 +116,23 @@ protected function validateModules(ConfigImporter $config_importer) {
$config_importer->logError($this->t('Unable to install the %module module since it does not exist.', array('%module' => $module)));
}
+ $existing_modules = array_keys(array_intersect_key($core_extension['module'], $config_importer->getStorageComparer()->getTargetStorage()->read('core.extension')['module']));
+ foreach ($existing_modules as $module) {
+ if ($core_extension['versions'][$module]['current'] !== $module_data[$module]->info['version']) {
+ $config_importer->logError($this->t(
+ 'Unable to import configuration because the version of %module module on the source site (@source_version) does not match this site (@target_version).',
+ ['%module' => $module, '@source_version' => $core_extension['versions'][$module]['current'], '@target_version' => $module_data[$module]->info['version']]
+ ));
+ }
+ elseif ($core_extension['versions'][$module]['schema'] !== (string) drupal_get_installed_schema_version($module)) {
+ $config_importer->logError($this->t(
+ 'Unable to import configuration because the schema version of %module module on the source site (@source_schema) does not match this site (@target_schema).',
+ ['%module' => $module, '@source_schema' => $core_extension['versions'][$module]['schema'], '@target_schema' => drupal_get_installed_schema_version($module)]
+ ));
+
+ }
+ }
+
// Ensure that all modules being installed have their dependencies met.
$installs = $config_importer->getExtensionChangelist('module', 'install');
foreach ($installs as $module) {
@@ -166,6 +202,16 @@ protected function validateThemes(ConfigImporter $config_importer) {
}
}
+ $existing_themes = array_keys(array_intersect_key($core_extension['theme'], $config_importer->getStorageComparer()->getTargetStorage()->read('core.extension')['theme']));
+ foreach ($existing_themes as $theme) {
+ if ($core_extension['versions'][$theme]['current'] !== $theme_data[$theme]->info['version']) {
+ $config_importer->logError($this->t(
+ 'Unable to import configuration because the version of %theme theme on the source site (@source_version) does not match this site (@target_version).',
+ ['%theme' => $theme, '@source_version' => $core_extension['versions'][$theme]['current'], '@target_version' => $theme_data[$theme]->info['version']]
+ ));
+ }
+ }
+
// Ensure that all themes being installed have their dependencies met.
foreach ($installs as $theme) {
foreach (array_keys($theme_data[$theme]->requires) as $required_theme) {
diff --git a/core/modules/config/src/Tests/ConfigImportUITest.php b/core/modules/config/src/Tests/ConfigImportUITest.php
index c98915c..85529c1 100644
--- a/core/modules/config/src/Tests/ConfigImportUITest.php
+++ b/core/modules/config/src/Tests/ConfigImportUITest.php
@@ -523,6 +523,9 @@ public function testExtensionValidation() {
// This theme does exist but the version is wrong.
$core['theme']['seven'] = 0;
$core['versions']['seven'] = ['current' => '8.0.0-rc3', 'install' => '8.0.0-rc3'];
+ $core['versions']['bartik']['current'] = '8.0.0-rc3';
+ $core['versions']['text']['current'] = '8.0.0-rc3';
+ $core['versions']['config_test']['schema'] = '8999';
$sync->write('core.extension', $core);
@@ -534,6 +537,8 @@ public function testExtensionValidation() {
$this->assertText('Unable to install the does_not_exist theme since it does not exist.');
$this->assertText('Unable to install the ban module since the installed version (8.0.0-rc3) does not match the code base (' . \Drupal::VERSION . ').');
$this->assertText('Unable to install the seven theme since the installed version (8.0.0-rc3) does not match the code base (' . \Drupal::VERSION . ').');
+ $this->assertText('Unable to import configuration because the schema version of config_test module on the source site (8999) does not match this site (' . drupal_get_installed_schema_version('config_test') . ').');
+ $this->assertText('Unable to import configuration because the version of bartik theme on the source site (8.0.0-rc3) does not match this site (' . \Drupal::VERSION . ').');
}
}
diff --git a/core/modules/config/src/Tests/ConfigImporterTest.php b/core/modules/config/src/Tests/ConfigImporterTest.php
index e62a777..1f5724d 100644
--- a/core/modules/config/src/Tests/ConfigImporterTest.php
+++ b/core/modules/config/src/Tests/ConfigImporterTest.php
@@ -651,6 +651,58 @@ public function testMissingCoreExtension() {
}
/**
+ * Tests missing core.extension:versions during configuration import.
+ *
+ * Configuration exported before the versions information was added should
+ * fail.
+ *
+ * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
+ */
+ public function testMissingVersionInformation() {
+ $sync = $this->container->get('config.storage.sync');
+ $data = $sync->read('core.extension');
+ unset($data['versions']);
+ $sync->write('core.extension', $data);
+ try {
+ $this->configImporter->reset()->import();
+ $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing version information.');
+ }
+ catch (ConfigImporterException $e) {
+ $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
+ $error_log = $this->configImporter->getErrors();
+ $this->assertEqual(['The core.extension file is out-of-date. Update the codebase of the source site, run update.php and export the configuration again.'], $error_log);
+ }
+ }
+
+ /**
+ * Tests missing core.extension:versions during configuration import.
+ *
+ * Configuration exported before the versions information was added should
+ * fail.
+ *
+ * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
+ */
+ public function testVersionMismatch() {
+ $sync = $this->container->get('config.storage.sync');
+ $data = $sync->read('core.extension');
+ $data['versions']['system']['current'] = '8.0.0-beta1';
+ $data['versions']['config_test']['schema'] = '8999';
+ $sync->write('core.extension', $data);
+ try {
+ $this->configImporter->reset()->import();
+ $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing version information.');
+ }
+ catch (ConfigImporterException $e) {
+ $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
+ $error_log = $this->configImporter->getErrors();
+ $this->assertEqual([
+ 'Unable to import configuration because the schema version of config_test module on the source site (8999) does not match this site (-1).',
+ 'Unable to import configuration because the version of system module on the source site (8.0.0-beta1) does not match this site (8.0.0-dev).'
+ ], $error_log);
+ }
+ }
+
+ /**
* Tests install profile validation during configuration import.
*
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index a3d17c6..3f686e7 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -522,7 +522,7 @@ protected function enableModules(array $modules) {
'current' => $version,
'install' => $version,
// @todo How to get schema - or should installSchema do this?
- 'schema' => NULL,
+ 'schema' => SCHEMA_UNINSTALLED,
];
}
$active_storage->write('core.extension', $extensions);
diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php
index fa05e4d..0149613 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -835,7 +835,7 @@ protected function enableModules(array $modules) {
'current' => $version,
'install' => $version,
// @todo How to get schema - or should installSchema do this?
- 'schema' => NULL,
+ 'schema' => SCHEMA_UNINSTALLED,
];
}
$active_storage->write('core.extension', $extension_config);