diff --git a/core/lib/Drupal/Core/Extension/ModuleInstaller.php b/core/lib/Drupal/Core/Extension/ModuleInstaller.php index a8124d5..c97de42 100644 --- a/core/lib/Drupal/Core/Extension/ModuleInstaller.php +++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php @@ -13,6 +13,7 @@ use Drupal\Core\DrupalKernelInterface; use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Entity\FieldableEntityInterface; +use Drupal\Core\Update\UpdateRegistry; /** * Default implementation of the module installer. @@ -260,6 +261,12 @@ public function install(array $module_list, $enable_dependencies = TRUE) { } drupal_set_installed_schema_version($module, $version); + // Ensure that all post_update functions are registered already. + $post_update_key_value = \Drupal::keyValue('post_update'); + $post_update_registry = new UpdateRegistry($this->root, \Drupal::service('site.path'), array_keys($this->moduleHandler->getModuleList()), $post_update_key_value); + $module_post_update_functions = $post_update_registry->getModuleUpdateFunctions($module); + $post_update_key_value->set('existing_updates', array_merge($post_update_key_value->get('existing_updates', []), $module_post_update_functions)); + // Record the fact that it was installed. $modules_installed[] = $module; @@ -445,6 +452,11 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { $schema_store = \Drupal::keyValue('system.schema'); $schema_store->delete($module); + + $post_update_key_value = \Drupal::keyValue('post_update'); + $post_update_registry = new UpdateRegistry($this->root, \Drupal::service('site.path'), array_keys($this->moduleHandler->getModuleList()), $post_update_key_value); + $module_post_update_functions = $post_update_registry->getModuleUpdateFunctions($module); + $post_update_key_value->set('existing_updates', array_diff($post_update_key_value->get('existing_updates', []), $module_post_update_functions)); } \Drupal::service('router.builder')->setRebuildNeeded(); drupal_get_installed_schema_version(NULL, TRUE); diff --git a/core/lib/Drupal/Core/Update/UpdateRegistry.php b/core/lib/Drupal/Core/Update/UpdateRegistry.php index 8d7be81..df662f2 100644 --- a/core/lib/Drupal/Core/Update/UpdateRegistry.php +++ b/core/lib/Drupal/Core/Update/UpdateRegistry.php @@ -93,14 +93,7 @@ public function getPendingUpdateFunctions() { // bootstrap factory) and b) the path to the modules, we use the extension // discovery for that. - // Scan the module list. - $extension_discovery = new ExtensionDiscovery($this->root, FALSE, [], $this->sitePath); - $module_extensions = $extension_discovery->scan('module'); - - $profile_extensions = $extension_discovery->scan('profile'); - $extensions = array_merge($module_extensions, $profile_extensions); - - $this->loadUpdateFiles($extensions); + $this->scanExtensionsAndLoadUpdateFiles(); // First figure out which hook_{$this->updateName}_NAME got executed already. $existing_update_functions = $this->keyValue->get('existing_updates', []); @@ -172,4 +165,36 @@ public function getPendingUpdateInformation() { return $ret; } + /** + * Returns all available updates for a given module. + * + * @param string $module_name + * The module name. + * + * @return callable[] + */ + public function getModuleUpdateFunctions($module_name) { + $this->scanExtensionsAndLoadUpdateFiles(); + $all_functions = $this->getAvailableUpdateFunctions(); + + return array_filter($all_functions, function($function_name) use ($module_name) { + list($function_module_name, ) = explode("_{$this->updateName}_", $function_name); + return $function_module_name === $module_name; + }); + } + + /** + * Scans all module + profile extensions and load the update files. + */ + protected function scanExtensionsAndLoadUpdateFiles() { + // Scan the module list. + $extension_discovery = new ExtensionDiscovery($this->root, FALSE, [], $this->sitePath); + $module_extensions = $extension_discovery->scan('module'); + + $profile_extensions = $extension_discovery->scan('profile'); + $extensions = array_merge($module_extensions, $profile_extensions); + + $this->loadUpdateFiles($extensions); + } + } diff --git a/core/modules/block/block.post_update.php b/core/modules/block/block.post_update.php index e4079eb..4a16ca1 100644 --- a/core/modules/block/block.post_update.php +++ b/core/modules/block/block.post_update.php @@ -19,8 +19,8 @@ function block_post_update_disable_blocks_with_missing_contexts() { $block_ids = array_keys($block_update_8001); $block_storage = \Drupal::entityManager()->getStorage('block'); $blocks = $block_storage->loadMultiple($block_ids); + /** @var $blocks \Drupal\block\BlockInterface[] */ foreach ($blocks as $block) { - /** @var $block \Drupal\block\BlockInterface */ // This block will have an invalid context mapping service and must be // disabled in order to prevent information disclosure. @@ -46,11 +46,12 @@ function block_post_update_disable_blocks_with_missing_contexts() { $message .= ''; + debug($message); return $message; } diff --git a/core/modules/system/src/Tests/Module/InstallTest.php b/core/modules/system/src/Tests/Module/InstallTest.php index ad81180..a1d6927 100644 --- a/core/modules/system/src/Tests/Module/InstallTest.php +++ b/core/modules/system/src/Tests/Module/InstallTest.php @@ -53,6 +53,19 @@ public function testRequiredModuleSchemaVersions() { $this->assertTrue($version > 0, 'System module version is > 0.'); $version = drupal_get_installed_schema_version('user', TRUE); $this->assertTrue($version > 0, 'User module version is > 0.'); + + $post_update_key_value = \Drupal::keyValue('post_update'); + $existing_updates = $post_update_key_value->get('existing_updates', []); + $this->assertTrue(in_array('module_test_post_update_test', $existing_updates)); + } + + /** + * Ensures that post update functions are removed on uninstall. + */ + public function testUninstallPostUpdateFunctions() { + $post_update_key_value = \Drupal::keyValue('post_update'); + $existing_updates = $post_update_key_value->get('existing_updates', []); + $this->assertTrue(in_array('module_test_post_update_test', $existing_updates)); } /** diff --git a/core/modules/system/src/Tests/Update/UpdatePostUpdateTest.php b/core/modules/system/src/Tests/Update/UpdatePostUpdateTest.php index 2dd1fbd..e9392b3 100644 --- a/core/modules/system/src/Tests/Update/UpdatePostUpdateTest.php +++ b/core/modules/system/src/Tests/Update/UpdatePostUpdateTest.php @@ -39,6 +39,7 @@ public function testPostUpdate() { $this->assertIdentical($updates, \Drupal::state()->get('post_update_test_execution', [])); $key_value = \Drupal::keyValue('update__post_update'); + $updates[] = 'block_post_update_disable_blocks_with_missing_contexts'; $this->assertEqual($updates, $key_value->get('existing_updates')); } diff --git a/core/modules/system/tests/modules/module_test/module_test.post_update.php b/core/modules/system/tests/modules/module_test/module_test.post_update.php new file mode 100644 index 0000000..2226578 --- /dev/null +++ b/core/modules/system/tests/modules/module_test/module_test.post_update.php @@ -0,0 +1,7 @@ +assertEquals($expected, $update_registry->getPendingUpdateInformation()); } + /** + * @covers ::getModuleUpdateFunctions + */ + public function testGetModuleUpdateFunctions() { + $this->setupBasicModules(); + $key_value = $this->prophesize(KeyValueStoreInterface::class)->reveal(); + + $update_registry = new UpdateRegistry('vfs://drupal', 'sites/default', [ + 'module_a', + 'module_b' + ], $key_value, FALSE); + + $this->assertEquals(['module_a_post_update_a', 'module_a_post_update_b'], array_values($update_registry->getModuleUpdateFunctions('module_a'))); + $this->assertEquals(['module_b_post_update_a'], array_values($update_registry->getModuleUpdateFunctions('module_b'))); + } + }