diff --git a/core/modules/config/config.module b/core/modules/config/config.module index 05f4b6d..30456b6 100644 --- a/core/modules/config/config.module +++ b/core/modules/config/config.module @@ -15,7 +15,7 @@ function config_help($route_name, RouteMatchInterface $route_match) { case 'help.page.config': $output = ''; $output .= '

' . t('About') . '

'; - $output .= '

' . t('The Configuration manager module provides a user interface for importing and exporting configuration changes; i.e., for staging configuration data between multiple instances of this web site. For more information, see the online handbook entry for Configuration manager module', array( + $output .= '

' . t('The Configuration Manager module provides a user interface for importing and exporting configuration changes; i.e., for staging configuration data between multiple instances of this web site. For more information, see the online handbook entry for Configuration manager module', array( '!url' => 'https://www.drupal.org/documentation/administer/config', )) . '

'; return $output; diff --git a/core/modules/system/src/Tests/Module/InstallUninstallTest.php b/core/modules/system/src/Tests/Module/InstallUninstallTest.php index 125b754..10ced5b 100644 --- a/core/modules/system/src/Tests/Module/InstallUninstallTest.php +++ b/core/modules/system/src/Tests/Module/InstallUninstallTest.php @@ -37,32 +37,62 @@ public function testInstallUninstall() { // Try to install and uninstall book, toolbar modules and its dependencies. $all_modules = system_rebuild_module_data(); + // Test help on required modules, but do not test uninstalling. + $required_modules = array_filter($all_modules, function ($module) { + if (!empty($module->info['required']) || $module->status == TRUE) { + if ($module->info['package'] != 'Testing' && empty($module->info['hidden'])) { + return TRUE; + } + } + return FALSE; + }); + + $required_modules['help'] = $all_modules['help']; + + // Test uninstalling without hidden, required, and already enabled modules. $all_modules = array_filter($all_modules, function ($module) { - // Filter hidden, required and already enabled modules. if (!empty($module->info['hidden']) || !empty($module->info['required']) || $module->status == TRUE || $module->info['package'] == 'Testing') { return FALSE; } return TRUE; }); - // Go through each module in the list and try to install it (unless it was - // already installed automatically due to a dependency). - $automatically_installed = array(); + // There are a couple of modules with special needs before they can be + // uninstalled. Make special methods for them. + $pre_uninstall = array( + 'forum' => 'preUninstallForum', + ); + + // Install the Help module, and verify it installed successfully. + unset($all_modules['help']); + $this->assertModules(array('help'), FALSE); + $this->assertModuleTablesDoNotExist('help'); + $edit = array(); + $package = $required_modules['help']->info['package']; + $edit["modules[$package][help][enable]"] = TRUE; + $this->drupalPostForm('admin/modules', $edit, t('Save configuration')); + $this->assertText(t('The configuration options have been saved.'), 'Modules status has been updated.'); + $this->assertText(t('hook_modules_installed fired for help')); + $this->assertModules(array('help'), TRUE); + $this->assertModuleTablesExist('help'); + $this->assertModuleConfig('help'); + + // Test help for the required modules. + foreach ($required_modules as $name => $module) { + $this->assertHelp($name, $module->info['name']); + } + + // Go through each module in the list and try to install and uninstall + // it with its dependencies. while (list($name, $module) = each($all_modules)) { - // Skip modules that have been automatically installed. - if (in_array($name, $automatically_installed)) { - continue; - } + $this->rebuildContainer(); + $was_installed_list = \Drupal::moduleHandler()->getModuleList(); // Start a list of modules that we expect to be installed this time. $modules_to_install = array($name); foreach (array_keys($module->requires) as $dependency) { - if (isset($all_modules[$dependency]) && !in_array($dependency, $automatically_installed)) { + if (isset($all_modules[$dependency])) { $modules_to_install[] = $dependency; - - // Add any potential dependency of this module to the list of modules we - // expect to be automatically installed. - $automatically_installed[] = $dependency; } } @@ -98,36 +128,49 @@ public function testInstallUninstall() { $this->assertLogMessage('system', "%module module installed.", array('%module' => $module_to_install), RfcLogLevel::INFO); } - // Uninstall the original module, and check appropriate - // hooks, tables, and log messages. (Later, we'll go back and do the - // same thing for modules that were enabled automatically.) - $this->assertSuccessfullUninstall($name, $package); - } + // Verify the help page. + $this->assertHelp($name, $module->info['name']); - // Go through all modules that were automatically installed, and try to - // uninstall them one by one. - while ($automatically_installed) { - $initial_count = count($automatically_installed); - foreach ($automatically_installed as $name) { - $package = $all_modules[$name]->info['package']; - // If the module can't be uninstalled due to dependencies, skip it and - // try again the next time. Otherwise, try to uninstall it. - $this->drupalGet('admin/modules/uninstall'); - $disabled_checkbox = $this->xpath('//input[@type="checkbox" and @disabled="disabled" and @name="uninstall[' . $name . ']"]'); - if (empty($disabled_checkbox)) { - $automatically_installed = array_diff($automatically_installed, array($name)); - $this->assertSuccessfullUninstall($name, $package); - } + // Uninstall the original module, plus everything else that was installed + // with it. + $this->rebuildContainer(); + if (isset($pre_uninstall[$name])) { + call_user_func(array($this, $pre_uninstall[$name])); } - $final_count = count($automatically_installed); - // If all checkboxes were disabled, something is really wrong with the - // test. Throw a failure and avoid an infinite loop. - if ($initial_count == $final_count) { - $this->fail('Remaining modules could not be disabled.'); - break; + $now_installed_list = \Drupal::moduleHandler()->getModuleList(); + $added_modules = array_diff(array_keys($now_installed_list), array_keys($was_installed_list)); + while ($added_modules) { + $initial_count = count($added_modules); + foreach ($added_modules as $to_uninstall) { + // See if we can currently uninstall this module (if its dependencies + // have been uninstalled), and do so if we can. + $this->drupalGet('admin/modules/uninstall'); + $field_name = "uninstall[$to_uninstall]"; + $has_checkbox = $this->xpath('//input[@type="checkbox" and @name="' . $field_name . '"]'); + $disabled = $this->xpath('//input[@type="checkbox" and @name="' . $field_name . '" and @disabled="disabled"]'); + + if (!empty($has_checkbox) && empty($disabled)) { + // This one is eligible for being uninstalled. + $package = $all_modules[$to_uninstall]->info['package']; + $this->assertSuccessfullUninstall($to_uninstall, $package); + $added_modules = array_diff($added_modules, array($to_uninstall)); + } + } + + // If we were not able to find a module to uninstall, fail and exit the + // loop. + $final_count = count($added_modules); + if ($initial_count == $final_count) { + $this->fail('Remaining modules could not be disabled for ' . $name); + break; + } } } + // Uninstall the help module and put it back into the list of modules. + $all_modules['help'] = $required_modules['help']; + $this->assertSuccessfullUninstall('help', $required_modules['help']->info['package']); + // Now that all modules have been tested, go back and try to enable them // all again at once. This tests two things: // - That each module can be successfully enabled again after being @@ -153,15 +196,6 @@ public function testInstallUninstall() { */ protected function assertSuccessfullUninstall($module, $package = 'Core') { $edit = array(); - if ($module == 'forum') { - // Forum cannot be uninstalled until all of the content entities related - // to it have been deleted. - $vid = $this->config('forum.settings')->get('vocabulary'); - $terms = entity_load_multiple_by_properties('taxonomy_term', ['vid' => $vid]); - foreach ($terms as $term) { - $term->delete(); - } - } $edit['uninstall[' . $module . ']'] = TRUE; $this->drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall')); $this->drupalPostForm(NULL, NULL, t('Uninstall')); @@ -181,4 +215,35 @@ protected function assertSuccessfullUninstall($module, $package = 'Core') { $this->assertNoModuleConfig($module); } + /** + * Verifies a module's help. + * + * Verifies that the module help page from hook_help() exists and can be + * displayed, and that it contains the phrase "Foo Bar module", where "Foo + * Bar" is the name of the module from the .info.yml file. + * + * @param string $module + * Machine name of the module to verify. + * @param array $info + * Human-readable name of the module. + */ + protected function assertHelp($module, $name) { + $this->drupalGet('admin/help/' . $module); + $this->assertResponse(200, "Help for $module displayed successfully"); + $this->assertText($name . ' module', "'$name module' is on the help page for $module"); + } + + /** + * Deletes forum taxonomy terms, so Forum can be uninstalled. + */ + protected function preUninstallForum() { + // There only should be a 'General discussion' term in the 'forums' + // vocabulary, but just delete any terms there in case the name changes. + $query = \Drupal::entityQuery('taxonomy_term'); + $query->condition('vid', 'forums'); + $ids = $query->execute(); + $storage = \Drupal::entityManager()->getStorage('taxonomy_term'); + $terms = $storage->loadMultiple($ids); + $storage->delete($terms); + } }