diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php index f22fcfa..8231668 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -359,8 +359,14 @@ public function rebuildThemeData() { } } - // Store the list of module dependencies separately from the full requires list. foreach ($themes as $key => $theme) { + // buildModuleDependencies() adds a theme->requires array that contains + // both module and base theme dependencies, if they are specified. Ensure + // that every theme stores the list of module dependencies separately + // from the full requires list. + if (!isset($theme->requires)) { + $theme->requires = []; + } $themes[$key]->module_dependencies = isset($theme->base_themes) ? array_diff_key($theme->requires, $theme->base_themes) : $theme->requires; } diff --git a/core/lib/Drupal/Core/Extension/ThemeInstaller.php b/core/lib/Drupal/Core/Extension/ThemeInstaller.php index fbfe1e9..e12ff0b 100644 --- a/core/lib/Drupal/Core/Extension/ThemeInstaller.php +++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php @@ -7,6 +7,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigInstallerInterface; use Drupal\Core\Config\ConfigManagerInterface; +use Drupal\Core\Extension\ModuleInstallerInterface; use Drupal\Core\Routing\RouteBuilderInterface; use Drupal\Core\State\StateInterface; use Psr\Log\LoggerInterface; @@ -86,6 +87,8 @@ class ThemeInstaller implements ThemeInstallerInterface { * A logger instance. * @param \Drupal\Core\State\StateInterface $state * The state store. + * @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer + * The module installer. */ public function __construct(ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory, ConfigInstallerInterface $config_installer, ModuleHandlerInterface $module_handler, ConfigManagerInterface $config_manager, AssetCollectionOptimizerInterface $css_collection_optimizer, RouteBuilderInterface $route_builder, LoggerInterface $logger, StateInterface $state, ModuleInstallerInterface $module_installer = NULL) { $this->themeHandler = $theme_handler; @@ -136,10 +139,12 @@ public function install(array $theme_list, $install_dependencies = TRUE) { while (list($theme) = each($theme_list)) { $module_dependencies = $theme_data[$theme]->module_dependencies; $theme_dependencies = array_diff_key($theme_data[$theme]->requires, $module_dependencies); + + // Install the module dependencies. $this->getModuleInstaller()->install(array_keys($module_dependencies)); - // Add dependencies to the list. The new themes will be processed as - // the while loop continues. + // Add dependencies to the list of themes to install. The new themes + // will be processed as the while loop continues. foreach (array_keys($theme_dependencies) as $dependency) { if (!isset($theme_data[$dependency])) { // The dependency does not exist. diff --git a/core/modules/system/src/Controller/ThemeController.php b/core/modules/system/src/Controller/ThemeController.php index e135356..62e0b5e 100644 --- a/core/modules/system/src/Controller/ThemeController.php +++ b/core/modules/system/src/Controller/ThemeController.php @@ -107,8 +107,22 @@ public function install(Request $request) { if (isset($theme)) { try { if ($this->themeHandler->install(array($theme))) { - $themes = $this->themeHandler->listInfo(); - drupal_set_message($this->t('The %theme theme has been installed.', array('%theme' => $themes[$theme]->info['name']))); + $theme_data = $this->themeHandler->listInfo(); + if ($theme_data[$theme]->module_dependencies) { + $module_data = system_rebuild_module_data(); + $module_names = []; + foreach(array_keys($theme_data[$theme]->module_dependencies) as $key) { + $module_names[] = $module_data[$key]->info['name']; + } + drupal_set_message($this->formatPlural(count($module_names), 'The %theme theme and its module dependency, %name, have been installed.', 'The %theme theme and its @count module dependencies have been installed: %names', array( + '%theme' => $theme_data[$theme]->info['name'], + '%name' => $module_names[0], + '%names' => implode(', ', $module_names), + ))); + } + else { + drupal_set_message($this->t('The %theme theme has been installed.', array('%theme' => $theme_data[$theme]->info['name']))); + } } else { drupal_set_message($this->t('The %theme theme was not found.', array('%theme' => $theme)), 'error'); diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php index 256b94c..30b5fd4 100644 --- a/core/modules/system/src/Form/ModulesListForm.php +++ b/core/modules/system/src/Form/ModulesListForm.php @@ -187,22 +187,25 @@ public function buildForm(array $form, FormStateInterface $form_state) { } /** + * Checks a single module dependency of a module or theme that has the given + * module version requirements. + * * @param array $modules * The list of existing modules. - * @param $dependency + * @param string $dependency * The dependency to check. - * @param $version - * Version information from the module whose dependency we are checking. + * @param array $version + * Version requirement data from the module or theme declaring the + * dependency we are checking. * - * @return + * @return string|null * NULL if compatible, otherwise a string describing the incompatibility. */ public static function checkDependency(array $modules, $dependency, $version) { if (!isset($modules[$dependency])) { return t('@module (missing)', array('@module' => Unicode::ucfirst($dependency))); } - // Only display visible modules. - elseif (empty($modules[$dependency]->hidden)) { + else { $name = $modules[$dependency]->info['name']; // Check if it is incompatible with the dependency's version. if ($incompatible_version = drupal_check_incompatibility($version, str_replace(\Drupal::CORE_COMPATIBILITY . '-', '', $modules[$dependency]->info['version']))) { @@ -332,18 +335,20 @@ protected function buildRow(array $modules, Extension $module, $distribution) { // If this module requires other modules, add them to the array. foreach ($module->requires as $dependency => $version) { - if ($incompatible = $this->checkDependency($modules, $dependency, $version)) { - $row['#requires'][$dependency] = $incompatible; - $row['enable']['#disabled'] = TRUE; - } - // Only display visible modules. - elseif (empty($modules[$dependency]->hidden)) { - $name = $modules[$dependency]->info['name']; - if ($modules[$dependency]->status) { - $row['#requires'][$dependency] = $this->t('@module', array('@module' => $name)); + // Only display missing or visible modules. + if (empty($modules[$dependency]->hidden)) { + if ($incompatible = $this->checkDependency($modules, $dependency, $version)) { + $row['#requires'][$dependency] = $incompatible; + $row['enable']['#disabled'] = TRUE; } else { - $row['#requires'][$dependency] = $this->t('@module (disabled)', array('@module' => $name)); + $name = $modules[$dependency]->info['name']; + if ($modules[$dependency]->status) { + $row['#requires'][$dependency] = $this->t('@module', array('@module' => $name)); + } + else { + $row['#requires'][$dependency] = $this->t('@module (disabled)', array('@module' => $name)); + } } } } diff --git a/core/modules/system/tests/themes/test_theme_depending_on_module/test_module_required_by_theme/src/Service.php b/core/modules/system/tests/themes/test_theme_depending_on_module/test_module_required_by_theme/src/Service.php new file mode 100644 index 0000000..3e7d7ee --- /dev/null +++ b/core/modules/system/tests/themes/test_theme_depending_on_module/test_module_required_by_theme/src/Service.php @@ -0,0 +1,7 @@ +assertFalse($this->moduleHandler()->moduleExists('test_theme_dependency')); - $this->themeInstaller()->install(['test_theme_module_dependency']); - $this->assertTrue($this->moduleHandler()->moduleExists('test_theme_dependency')); + $this->assertFalse($this->moduleHandler()->moduleExists('test_module_required_by_theme')); + $this->themeInstaller()->install(['test_theme_depending_on_module']); + $this->assertTrue($this->moduleHandler()->moduleExists('test_module_required_by_theme')); - $service = \Drupal::service('test_theme_dependency.service'); + $service = \Drupal::service('test_module_required_by_theme.service'); $this->assertInstanceOf(Service::class, $service); }