diff --git a/core/config/install/core.extension.yml b/core/config/install/core.extension.yml index ce74bae..ff9a4ba 100644 --- a/core/config/install/core.extension.yml +++ b/core/config/install/core.extension.yml @@ -1,2 +1,3 @@ module: {} theme: {} +versions: {} diff --git a/core/config/schema/core.extension.schema.yml b/core/config/schema/core.extension.schema.yml index 954df35..9565d8a 100644 --- a/core/config/schema/core.extension.schema.yml +++ b/core/config/schema/core.extension.schema.yml @@ -14,19 +14,19 @@ core.extension: sequence: type: integer label: 'Weight' - installed: - type: mapping - label: 'Version at install time' - mapping: - module: - type: sequence - label: 'Modules' - sequence: + versions: + type: sequence + label: 'Version information' + sequence: + type: mapping + label: 'Extensions' + mapping: + current: + type: string + label: 'Currently installed version' + install: type: string - label: 'Version' - theme: - type: sequence - label: 'Themes' - sequence: + label: 'Version at install time' + schema: type: string - label: 'Version' + label: 'Currently installed schema version' diff --git a/core/includes/schema.inc b/core/includes/schema.inc index 4712d3b..0242006 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -104,6 +104,9 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F */ function drupal_set_installed_schema_version($module, $version) { \Drupal::keyValue('system.schema')->set($module, $version); + // Store the information in config so that we can ensure that database updates + // are the same between the source and the target site instances. + \Drupal::configFactory()->getEditable('core.extension')->set("versions.$module.schema", $version); // Reset the static cache of module schema versions. drupal_get_installed_schema_version(NULL, TRUE); } diff --git a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php index 69b5241..b5fddb1 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ConfigImportSubscriber.php @@ -115,10 +115,10 @@ protected function validateModules(ConfigImporter $config_importer) { ); $config_importer->logError($message); } - if ($core_extension['installed']['module'][$module] !== $module_data[$module]->info['version']) { + if ($core_extension['versions'][$module]['install'] !== $module_data[$module]->info['version']) { $config_importer->logError($this->t( 'Unable to install the %module module since the installed version (@config_version) does not match the code base (@code_version).', - ['%module' => $module, '@config_version' => $core_extension['installed']['module'][$module], '@code_version' => $module_data[$module]->info['version']] + ['%module' => $module, '@config_version' => $core_extension['versions'][$module]['install'], '@code_version' => $module_data[$module]->info['version']] )); } } @@ -175,10 +175,10 @@ protected function validateThemes(ConfigImporter $config_importer) { $config_importer->logError($this->t('Unable to install the %theme theme since it requires the %required_theme theme.', array('%theme' => $theme_name, '%required_theme' => $required_theme_name))); } } - if ($core_extension['installed']['theme'][$theme] !== $theme_data[$theme]->info['version']) { + if ($core_extension['versions'][$theme]['install'] !== $theme_data[$theme]->info['version']) { $config_importer->logError($this->t( 'Unable to install the %theme theme since the installed version (@config_version) does not match the code base (@code_version).', - ['%theme' => $theme, '@config_version' => $core_extension['installed']['theme'][$theme], '@code_version' => $theme_data[$theme]->info['version']] + ['%theme' => $theme, '@config_version' => $core_extension['versions'][$theme]['install'], '@code_version' => $theme_data[$theme]->info['version']] )); } } diff --git a/core/lib/Drupal/Core/Extension/InfoParserDynamic.php b/core/lib/Drupal/Core/Extension/InfoParserDynamic.php index 64e4941..0ea95f0 100644 --- a/core/lib/Drupal/Core/Extension/InfoParserDynamic.php +++ b/core/lib/Drupal/Core/Extension/InfoParserDynamic.php @@ -36,6 +36,9 @@ public function parse($filename) { if (isset($parsed_info['version']) && $parsed_info['version'] === 'VERSION') { $parsed_info['version'] = \Drupal::VERSION; } + if (!isset($parsed_info['version'])) { + $parsed_info['version'] = 'None'; + } } return $parsed_info; } diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php index 060edba..9df8b9e 100644 --- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php +++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php @@ -294,16 +294,21 @@ public function install(array $module_list, $enable_dependencies = TRUE) { // If any modules were newly installed, invoke hook_modules_installed(). if (!empty($modules_installed)) { - // Update installed version list. + // There will be a new config factory. + $extension_config = \Drupal::configFactory()->getEditable('core.extension'); + // Update version information in configuration. $list = $this->moduleHandler->getModuleList(); - $installed_versions = $extension_config->get('installed.module'); + $versions = $extension_config->get('versions'); foreach ($modules_installed as $module_name) { $info = \Drupal::service('info_parser')->parse($list[$module_name]->getPathname()); - $installed_versions[$module_name] = $info['version']; + $versions[$module_name] = [ + 'current' => $info['version'], + 'install' => $info['version'], + 'schema' => drupal_get_installed_schema_version($module_name), + ]; } - ksort($installed_versions); - $extension_config->set('installed.module', $installed_versions); - $extension_config->save(); + ksort($versions); + $extension_config->set('versions', $versions)->save(); \Drupal::service('router.builder')->setRebuildNeeded(); $this->moduleHandler->invokeAll('modules_installed', array($modules_installed)); } @@ -427,7 +432,7 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { \Drupal::configFactory() ->getEditable('core.extension') ->clear("module.$module") - ->clear("installed.module.$module") + ->clear("versions.$module") ->save(TRUE); // Update the module handler to remove the module. diff --git a/core/lib/Drupal/Core/Extension/ThemeInstaller.php b/core/lib/Drupal/Core/Extension/ThemeInstaller.php index 03f027a..006eb94 100644 --- a/core/lib/Drupal/Core/Extension/ThemeInstaller.php +++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php @@ -170,12 +170,15 @@ public function install(array $theme_list, $install_dependencies = TRUE) { // The value is not used; the weight is ignored for themes currently. Do // not check schema when saving the configuration. - $installed_versions = $extension_config->get('installed.theme'); - $installed_versions[$key] = $theme_data[$key]->info['version']; - ksort($installed_versions); + $versions = $extension_config->get('versions'); + $versions[$key] = [ + 'current' => $theme_data[$key]->info['version'], + 'install' => $theme_data[$key]->info['version'], + ]; + ksort($versions); $extension_config ->set("theme.$key", 0) - ->set('installed.theme', $installed_versions) + ->set('versions', $versions) ->save(TRUE); // Add the theme to the current list. @@ -254,7 +257,7 @@ public function uninstall(array $theme_list) { // The value is not used; the weight is ignored for themes currently. $extension_config ->clear("theme.$key") - ->clear("installed.theme.$key"); + ->clear("versions.$key"); // Update the current theme data accordingly. unset($current_theme_data[$key]); diff --git a/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php index e15d3ac..19ee917 100644 --- a/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php +++ b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php @@ -67,8 +67,13 @@ public function testInstallProfileValidation() { $core['module']['testing_config_import'] = 0; unset($core['module']['syslog']); unset($core['theme']['stark']); + unset($core['versions']['syslog']); + unset($core['versions']['stark']); $core['theme']['stable'] = 0; $core['theme']['classy'] = 0; + $core['versions']['stable'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION]; + $core['versions']['classy'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION]; + ksort($core['versions']); $sync->write('core.extension', $core); $sync->deleteAll('syslog.'); $theme = $sync->read('system.theme'); diff --git a/core/modules/config/src/Tests/ConfigImportUITest.php b/core/modules/config/src/Tests/ConfigImportUITest.php index 7842d12..c98915c 100644 --- a/core/modules/config/src/Tests/ConfigImportUITest.php +++ b/core/modules/config/src/Tests/ConfigImportUITest.php @@ -84,15 +84,14 @@ function testImport() { $core_extension['module']['action'] = 0; $core_extension['module']['ban'] = 0; $core_extension['module'] = module_config_sort($core_extension['module']); - $core_extension['installed']['module']['action'] = \Drupal::VERSION; - $core_extension['installed']['module']['ban'] = \Drupal::VERSION; - ksort($core_extension['installed']['module']); + $core_extension['versions']['action'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION, 'schema' => '8000']; + $core_extension['versions']['ban'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION, 'schema' => '8000']; // Bartik is a subtheme of classy so classy must be enabled. $core_extension['theme']['classy'] = 0; $core_extension['theme']['bartik'] = 0; - $core_extension['installed']['theme']['classy'] = \Drupal::VERSION; - $core_extension['installed']['theme']['bartik'] = \Drupal::VERSION; - ksort($core_extension['installed']['theme']); + $core_extension['versions']['classy'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION]; + $core_extension['versions']['bartik'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION]; + ksort($core_extension['versions']); $sync->write('core.extension', $core_extension); // Use the install storage so that we can read configuration from modules @@ -181,11 +180,11 @@ function testImport() { unset($core_extension['module']['options']); unset($core_extension['module']['text']); unset($core_extension['theme']['bartik']); - unset($core_extension['installed']['module']['action']); - unset($core_extension['installed']['module']['ban']); - unset($core_extension['installed']['module']['options']); - unset($core_extension['installed']['module']['text']); - unset($core_extension['installed']['theme']['bartik']); + unset($core_extension['versions']['action']); + unset($core_extension['versions']['ban']); + unset($core_extension['versions']['options']); + unset($core_extension['versions']['text']); + unset($core_extension['versions']['bartik']); $sync->write('core.extension', $core_extension); $sync->delete('action.settings'); $sync->delete('text.settings'); @@ -520,10 +519,10 @@ public function testExtensionValidation() { $core['theme']['does_not_exist'] = 0; // This module does exist but the version is wrong. $core['module']['ban'] = 0; - $core['installed']['module']['ban'] = '8.0.0-rc3'; + $core['versions']['ban'] = ['current' => '8.0.0-rc3', 'install' => '8.0.0-rc3', 'schema' => '8000']; // This theme does exist but the version is wrong. $core['theme']['seven'] = 0; - $core['installed']['theme']['seven'] = '8.0.0-rc3'; + $core['versions']['seven'] = ['current' => '8.0.0-rc3', 'install' => '8.0.0-rc3']; $sync->write('core.extension', $core); diff --git a/core/modules/config/src/Tests/ConfigImporterTest.php b/core/modules/config/src/Tests/ConfigImporterTest.php index 3fca8c0..e62a777 100644 --- a/core/modules/config/src/Tests/ConfigImporterTest.php +++ b/core/modules/config/src/Tests/ConfigImporterTest.php @@ -578,6 +578,8 @@ public function testUnmetDependency() { // Add a module and a theme that depend on uninstalled extensions. $extensions['module']['book'] = 0; $extensions['theme']['bartik'] = 0; + $extensions['versions']['book'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION]; + $extensions['versions']['bartik'] = ['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION]; $sync->write('core.extension', $extensions); try { diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 56c800e..a3d17c6 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -236,7 +236,13 @@ protected function setUp() { // \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work. // Write directly to active storage to avoid early instantiation of // the event dispatcher which can prevent modules from registering events. - \Drupal::service('config.storage')->write('core.extension', array('module' => array(), 'theme' => array())); + \Drupal::service('config.storage')->write('core.extension', + [ + 'module' => [], + 'theme' => [], + 'versions' => [], + ] + ); // Collect and set a fixed module list. $class = get_class($this); @@ -511,6 +517,13 @@ protected function enableModules(array $modules) { $module_handler->addModule($module, $module_list[$module]->getPath()); // Maintain the list of enabled modules in configuration. $extensions['module'][$module] = 0; + $version = \Drupal::service('info_parser')->parse($module_list[$module]->getPathname())['version']; + $extensions['versions'][$module] = [ + 'current' => $version, + 'install' => $version, + // @todo How to get schema - or should installSchema do this? + 'schema' => NULL, + ]; } $active_storage->write('core.extension', $extensions); diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php index ae3742e..83135f6 100644 --- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php +++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php @@ -334,6 +334,18 @@ public function testDrupalGetProfile() { } /** + * @covers ::enableModules + */ + public function testEnableModules() { + $this->assertFalse(\Drupal::moduleHandler()->moduleExists('system')); + $this->enableModules(['system', 'user']); + $core = \Drupal::config('core.extension'); + $this->assertEqual(['entity_test' => 0, 'user' => 0, 'system' => 0], $core->get('module')); + $this->assertEqual(['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION, 'schema' => NULL], $core->get('versions.user')); + $this->assertTrue(\Drupal::moduleHandler()->moduleExists('system')); + } + + /** * {@inheritdoc} */ public function run(array $methods = array()) { diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php index 0239a41..c865675 100644 --- a/core/modules/system/src/Controller/DbUpdateController.php +++ b/core/modules/system/src/Controller/DbUpdateController.php @@ -653,6 +653,17 @@ public static function batchFinished($success, $results, $operations) { // No updates to run, so caches won't get flushed later. Clear them now. drupal_flush_all_caches(); + // Update core.extensions to have the correct version information. + $core = \Drupal::configFactory()->getEditable('core.extension'); + foreach (\Drupal::moduleHandler()->getModuleList() as $module => $extension) { + $version = \Drupal::service('info_parser')->parse($extension->getPathname())['version']; + $core->set("versions.$module.current", $version); + } + foreach (\Drupal::service('theme_handler')->listInfo() as $theme => $extension) { + $version = \Drupal::service('info_parser')->parse($extension->getPathname())['version']; + $core->set("versions.$theme.current", $version); + } + $_SESSION['update_results'] = $results; $_SESSION['update_success'] = $success; $_SESSION['updates_remaining'] = $operations; diff --git a/core/modules/system/system.install b/core/modules/system/system.install index afa5892..a8c07c1 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1871,3 +1871,29 @@ function system_update_8014() { /** * @} End of "addtogroup updates-8.0.0-rc". */ + +/** + * Add the version information to core.extension + */ +function system_update_8015() { + // Update core.extensions to have the correct version information. + $core = \Drupal::configFactory()->getEditable('core.extension'); + $versions = $core->get('versions'); + foreach (\Drupal::moduleHandler()->getModuleList() as $module => $extension) { + $version = \Drupal::service('info_parser')->parse($extension->getPathname())['version']; + $versions[$module] = [ + 'current' => $version, + 'install' => $version, + 'schema' => drupal_get_installed_schema_version($module), + ]; + } + foreach (\Drupal::service('theme_handler')->listInfo() as $theme => $extension) { + $version = \Drupal::service('info_parser')->parse($extension->getPathname())['version']; + $versions[$theme] = [ + 'current' => $version, + 'install' => $version, + ]; + } + ksort($versions); + $core->set('versions', $versions)->save(); +} diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index fd7b864..fa05e4d 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -830,6 +830,13 @@ protected function enableModules(array $modules) { $module_handler->addModule($module, $module_list[$module]->getPath()); // Maintain the list of enabled modules in configuration. $extension_config['module'][$module] = 0; + $version = \Drupal::service('info_parser')->parse($module_list[$module]->getPathname())['version']; + $extension_config['versions'][$module] = [ + 'current' => $version, + 'install' => $version, + // @todo How to get schema - or should installSchema do this? + 'schema' => NULL, + ]; } $active_storage->write('core.extension', $extension_config); diff --git a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php index 188d111..d3e30db 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBaseTest.php +++ b/core/tests/Drupal/KernelTests/KernelTestBaseTest.php @@ -211,6 +211,18 @@ public function testRenderWithTheme() { } /** + * @covers ::enableModules + */ + public function testEnableModules() { + $this->assertFalse(\Drupal::moduleHandler()->moduleExists('system')); + $this->enableModules(['system', 'user']); + $core = \Drupal::config('core.extension'); + $this->assertEquals(['user' => 0, 'system' => 0], $core->get('module')); + $this->assertEquals(['current' => \Drupal::VERSION, 'install' => \Drupal::VERSION, 'schema' => NULL], $core->get('versions.user')); + $this->assertTrue(\Drupal::moduleHandler()->moduleExists('system')); + } + + /** * {@inheritdoc} */ protected function tearDown() { diff --git a/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php b/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php index e65c651..62377c2 100644 --- a/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/DefaultConfigTest.php @@ -32,6 +32,7 @@ public function testConfigIsEmpty() { $expected = array( 'module' => array(), 'theme' => array(), + 'versions' => array(), ); $this->assertEquals($expected, $config); }