--- /home/shaal/Downloads/allow-themes-to-declare-dependencies-1-474684-194.patch 2019-10-30 16:03:44.030482589 -0400 +++ /home/shaal/Downloads/allow-themes-to-declare-dependencies-474684-203.patch 2019-10-30 17:01:26.134133162 -0400 @@ -1,8 +1,8 @@ diff --git a/core/core.services.yml b/core/core.services.yml -index 08b09d9953..e612ba0453 100644 +index 582f0bd907..0694089def 100644 --- a/core/core.services.yml +++ b/core/core.services.yml -@@ -540,7 +540,7 @@ services: +@@ -553,7 +553,7 @@ services: arguments: ['@app.root', '@config.factory', '@extension.list.theme'] theme_installer: class: Drupal\Core\Extension\ThemeInstaller @@ -12,10 +12,10 @@ # entity* services instead. entity.manager: diff --git a/core/lib/Drupal/Core/Extension/ThemeExtensionList.php b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php -index 07a2f3f8f6..453ee04089 100644 +index 5722d1c2fe..2c35f0535b 100644 --- a/core/lib/Drupal/Core/Extension/ThemeExtensionList.php +++ b/core/lib/Drupal/Core/Extension/ThemeExtensionList.php -@@ -52,6 +52,7 @@ class ThemeExtensionList extends ExtensionList { +@@ -51,6 +51,7 @@ class ThemeExtensionList extends ExtensionList { 'libraries' => [], 'libraries_extend' => [], 'libraries_override' => [], @@ -23,7 +23,7 @@ ]; /** -@@ -141,6 +142,17 @@ protected function doList() { +@@ -140,6 +141,17 @@ protected function doList() { // sub-themes. $this->fillInSubThemeData($themes, $sub_themes); @@ -183,7 +183,7 @@ + } diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php -index 106f4e2915..ac6b856de7 100644 +index f27cc01eb1..4d29f333f5 100644 --- a/core/modules/system/src/Controller/SystemController.php +++ b/core/modules/system/src/Controller/SystemController.php @@ -10,6 +10,7 @@ @@ -202,7 +202,7 @@ foreach ($themes as &$theme) { if (!empty($theme->info['hidden'])) { -@@ -232,9 +234,35 @@ public function themesPage() { +@@ -231,9 +233,35 @@ public function themesPage() { $theme->incompatible_base = (isset($theme->info['base theme']) && !($theme->base_themes === array_filter($theme->base_themes))); // Confirm that the theme engine is available. $theme->incompatible_engine = isset($theme->info['engine']) && !isset($theme->owner); @@ -210,50 +210,50 @@ + $theme->incompatible_module = FALSE; } + -+ // Check module dependencies. -+ if ($theme->module_dependencies) { -+ if (empty($modules)) { -+ $modules = system_rebuild_module_data(); -+ } -+ foreach ($theme->module_dependencies as $dependency => $version) { -+ if ($incompatible = ModulesListForm::checkDependencyMessage($modules, $dependency, $version)) { -+ $theme->module_dependencies[$dependency] = $incompatible; -+ $theme->incompatible_module = TRUE; -+ continue; ++ // Check module dependencies. ++ if ($theme->module_dependencies) { ++ if (empty($modules)) { ++ $modules = system_rebuild_module_data(); + } ++ foreach ($theme->module_dependencies as $dependency => $version) { ++ if ($incompatible = ModulesListForm::checkDependencyMessage($modules, $dependency, $version)) { ++ $theme->module_dependencies[$dependency] = $incompatible; ++ $theme->incompatible_module = TRUE; ++ continue; ++ } + -+ // Only display visible modules. -+ if (!empty($modules[$dependency]->hidden)) { -+ unset($theme->module_dependencies[$dependency]); -+ continue; -+ } ++ // Only display visible modules. ++ if (!empty($modules[$dependency]->hidden)) { ++ unset($theme->module_dependencies[$dependency]); ++ continue; ++ } + -+ $name = $modules[$dependency]->info['name']; -+ $theme->module_dependencies[$dependency] = $modules[$dependency]->status ? $this->t('@module', ['@module' => $name]) : $this->t('@module (disabled)', ['@module' => $name]); ++ $name = $modules[$dependency]->info['name']; ++ $theme->module_dependencies[$dependency] = $modules[$dependency]->status ? $this->t('@module', ['@module' => $name]) : $this->t('@module (disabled)', ['@module' => $name]); ++ } + } -+ } + $theme->operations = []; -- if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php && !$theme->incompatible_base && !$theme->incompatible_engine) { -+ if (!empty($theme->status) || !$theme->incompatible_core && !$theme->incompatible_php && !$theme->incompatible_base && !$theme->incompatible_engine && !$theme->incompatible_module) { +- if (!empty($theme->status) || !$theme->info['core_incompatible'] && !$theme->incompatible_php && !$theme->incompatible_base && !$theme->incompatible_engine) { ++ if (!empty($theme->status) || !$theme->info['core_incompatible'] && !$theme->incompatible_core && !$theme->incompatible_php && !$theme->incompatible_base && !$theme->incompatible_engine && !$theme->incompatible_module) { // Create the operations links. $query['theme'] = $theme->getName(); if ($this->themeAccess->checkAccess($theme->getName())) { diff --git a/core/modules/system/src/Controller/ThemeController.php b/core/modules/system/src/Controller/ThemeController.php -index 31be59ed1a..f1ae735c61 100644 +index 0a8ad31cf8..8cfbd49d3f 100644 --- a/core/modules/system/src/Controller/ThemeController.php +++ b/core/modules/system/src/Controller/ThemeController.php -@@ -6,6 +6,7 @@ - use Drupal\Core\Config\PreExistingConfigException; +@@ -7,6 +7,7 @@ use Drupal\Core\Config\UnmetDependenciesException; use Drupal\Core\Controller\ControllerBase; + use Drupal\Core\Extension\ThemeExtensionList; +use Drupal\Core\DrupalKernelInterface; use Drupal\Core\Extension\ThemeHandlerInterface; - use Symfony\Component\DependencyInjection\ContainerInterface; - use Symfony\Component\HttpFoundation\Request; -@@ -23,6 +24,13 @@ class ThemeController extends ControllerBase { + use Drupal\Core\Extension\ThemeInstallerInterface; + use Drupal\system\Form\ThemeExperimentalConfirmForm; +@@ -40,6 +41,13 @@ class ThemeController extends ControllerBase { */ - protected $themeHandler; + protected $themeInstaller; + /** + * The Drupal kernel. @@ -265,52 +265,54 @@ /** * Constructs a new ThemeController. * -@@ -30,10 +38,13 @@ class ThemeController extends ControllerBase { - * The theme handler. - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory +@@ -51,12 +59,15 @@ class ThemeController extends ControllerBase { * The config factory. + * @param \Drupal\Core\Extension\ThemeInstallerInterface $theme_installer + * The theme installer. + * @param \Drupal\Core\DrupalKernelInterface $kernel + * The Drupal kernel. */ -- public function __construct(ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory) { -+ public function __construct(ThemeHandlerInterface $theme_handler, ConfigFactoryInterface $config_factory, DrupalKernelInterface $kernel) { +- public function __construct(ThemeHandlerInterface $theme_handler, ThemeExtensionList $theme_list, ConfigFactoryInterface $config_factory, ThemeInstallerInterface $theme_installer) { ++ public function __construct(ThemeHandlerInterface $theme_handler, ThemeExtensionList $theme_list, ConfigFactoryInterface $config_factory, ThemeInstallerInterface $theme_installer, DrupalKernelInterface $kernel) { $this->themeHandler = $theme_handler; + $this->themeList = $theme_list; $this->configFactory = $config_factory; + $this->themeInstaller = $theme_installer; + $this->kernel = $kernel; } /** -@@ -42,7 +53,8 @@ public function __construct(ThemeHandlerInterface $theme_handler, ConfigFactoryI - public static function create(ContainerInterface $container) { - return new static( +@@ -67,7 +78,8 @@ public static function create(ContainerInterface $container) { $container->get('theme_handler'), -- $container->get('config.factory') -+ $container->get('config.factory'), + $container->get('extension.list.theme'), + $container->get('config.factory'), +- $container->get('theme_installer') ++ $container->get('theme_installer'), + $container->get('kernel') ); } -@@ -75,6 +87,7 @@ public function uninstall(Request $request) { +@@ -99,6 +111,7 @@ public function uninstall(Request $request) { + $this->messenger()->addError($this->t('%theme is the default theme and cannot be uninstalled.', ['%theme' => $themes[$theme]->info['name']])); } else { - $this->themeHandler->uninstall([$theme]); + $this->updateDependencies(\Drupal::getContainer()); + $this->themeInstaller->uninstall([$theme]); $this->messenger()->addStatus($this->t('The %theme theme has been uninstalled.', ['%theme' => $themes[$theme]->info['name']])); } +@@ -137,9 +150,30 @@ public function install(Request $request) { } -@@ -106,9 +119,28 @@ public function install(Request $request) { - if (isset($theme)) { try { + // Get an up-to-date list of modules in the system. + $previously_installed_modules = $this->moduleHandler()->getModuleList(); - if ($this->themeHandler->install([$theme])) { -- $themes = $this->themeHandler->listInfo(); -- $this->messenger()->addStatus($this->t('The %theme theme has been installed.', ['%theme' => $themes[$theme]->info['name']])); + if ($this->themeInstaller->install([$theme])) { + $themes = $this->themeHandler->listInfo(); + $this->messenger()->addStatus($this->t('The %theme theme has been installed.', ['%theme' => $themes[$theme]->info['name']])); + $this->updateDependencies(\Drupal::getContainer()); + $theme_data = $this->themeHandler->listInfo(); + if ($theme_data[$theme]->module_dependencies) { -+ $module_data = system_rebuild_module_data(); ++ $module_data = \Drupal::service('extension.list.module')->getList(); + $newly_installed_modules = array_diff_key($theme_data[$theme]->module_dependencies, $previously_installed_modules); + $newly_installed_modules_names = array_map(function ($module_name) use ($module_data) { + return $module_data[$module_name]->info['name']; @@ -320,7 +322,7 @@ + $this->messenger()->addStatus($this->formatPlural(count($newly_installed_modules_names), 'The %theme theme and its module dependency, %name, have been installed.', 'The %theme theme and its @count module dependencies have been installed: %names.', [ + '%theme' => $theme_data[$theme]->info['name'], + '%name' => $newly_installed_modules_names[0], -+ '%names' => implode(', ', $newly_installed_modules_names), ++ '%names' => implode(', ', $newly_installed_modules_names), + ])); + } + else { @@ -329,7 +331,7 @@ } else { $this->messenger()->addError($this->t('The %theme theme was not found.', ['%theme' => $theme])); -@@ -190,4 +222,34 @@ public function setDefaultTheme(Request $request) { +@@ -249,4 +283,34 @@ public function setDefaultTheme(Request $request) { throw new AccessDeniedHttpException(); } @@ -348,65 +350,65 @@ + * add the trait that supplies this method from that issue. + */ + protected function updateDependencies(ContainerInterface $container) { -+ $vars = get_object_vars($this); -+ foreach ($vars as $key => $value) { -+ if (is_object($value) && isset($value->_serviceId)) { -+ $this->$key = $container->get($value->_serviceId); -+ continue; -+ } -+ -+ // Special case the container, which might not have a service ID. -+ if ($value instanceof ContainerInterface) { -+ $this->$key = $container; ++ $vars = get_object_vars($this); ++ foreach ($vars as $key => $value) { ++ if (is_object($value) && isset($value->_serviceId)) { ++ $this->$key = $container->get($value->_serviceId); + continue; -+ } -+ } ++ } ++ ++ // Special case the container, which might not have a service ID. ++ if ($value instanceof ContainerInterface) { ++ $this->$key = $container; ++ continue; ++ } ++ } + } + } diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php -index c2ade11b7a..e83121b3ba 100644 +index f32dad0909..7cfd417041 100644 --- a/core/modules/system/src/Form/ModulesListForm.php +++ b/core/modules/system/src/Form/ModulesListForm.php -@@ -197,11 +197,47 @@ public function buildForm(array $form, FormStateInterface $form_state) { +@@ -210,11 +210,47 @@ public function buildForm(array $form, FormStateInterface $form_state) { return $form; } -+ /** -+ * Determines a message for missing/invalid theme/module dependencies. -+ * -+ * @param array $modules -+ * The list of existing modules. -+ * @param string $dependency -+ * The module dependency to check. -+ * @param array $version -+ * Version requirement data from the module or theme declaring the -+ * dependency we are checking. -+ * -+ * @return string|null -+ * NULL if compatible, otherwise a string describing the incompatibility. ++ /** ++ * Determines a message for missing/invalid theme/module dependencies. ++ * ++ * @param array $modules ++ * The list of existing modules. ++ * @param string $dependency ++ * The module dependency to check. ++ * @param array $version ++ * Version requirement data from the module or theme declaring the ++ * dependency we are checking. ++ * ++ * @return string|null ++ * NULL if compatible, otherwise a string describing the incompatibility. + */ -+ public static function checkDependencyMessage(array $modules, $dependency, $version) { -+ if (!isset($modules[$dependency])) { -+ return t('@module (missing)', ['@module' => Unicode::ucfirst($dependency)]); -+ } -+ 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']))) { -+ return t('@module (incompatible with version @version)', [ -+ '@module' => $name . $incompatible_version, -+ '@version' => $modules[$dependency]->info['version'], -+ ]); -+ } -+ // Ensure that incompatible modules cannot be installed. -+ if ($modules[$dependency]->info['core'] != \Drupal::CORE_COMPATIBILITY) { -+ return t('@module (incompatible with this version of Drupal core)', [ -+ '@module' => $name, -+ ]); -+ } -+ } -+ } ++ public static function checkDependencyMessage(array $modules, $dependency, $version) { ++ if (!isset($modules[$dependency])) { ++ return t('@module (missing)', ['@module' => Unicode::ucfirst($dependency)]); ++ } ++ else { ++ $name = $modules[$dependency]->info['name']; ++ // Check if it is incompatible with the dependency's version. ++ if ($incompatible_version = \Dependency::isCompatible($version, str_replace(\Drupal::CORE_COMPATIBILITY . '-', '', $modules[$dependency]->info['version']))) { ++ return t('@module (incompatible with version @version)', [ ++ '@module' => $name . $incompatible_version, ++ '@version' => $modules[$dependency]->info['version'], ++ ]); ++ } ++ // Ensure that incompatible modules cannot be installed. ++ if ($modules[$dependency]->info['core'] != \Drupal::CORE_COMPATIBILITY) { ++ return t('@module (incompatible with this version of Drupal core)', [ ++ '@module' => $name, ++ ]); ++ } ++ } ++ } + /** * Builds a table row for the system modules page. @@ -417,61 +419,19 @@ * @param \Drupal\Core\Extension\Extension $module * The module for which to build the form row. * @param $distribution -@@ -309,38 +345,19 @@ protected function buildRow(array $modules, Extension $module, $distribution) { - // If this module requires other modules, add them to the array. - /** @var \Drupal\Core\Extension\Dependency $dependency_object */ - foreach ($module->requires as $dependency => $dependency_object) { -- if (!isset($modules[$dependency])) { -- $row['#requires'][$dependency] = $this->t('@module (missing)', ['@module' => Unicode::ucfirst($dependency)]); -- $row['enable']['#disabled'] = TRUE; -+ // Only display missing or visible modules. -+ if (!empty($modules[$dependency]->hidden)) { -+ continue; - } -- // Only display visible modules. -- elseif (empty($modules[$dependency]->hidden)) { -- $name = $modules[$dependency]->info['name']; -- // Disable the module's checkbox if it is incompatible with the -- // dependency's version. -- if (!$dependency_object->isCompatible(str_replace(\Drupal::CORE_COMPATIBILITY . '-', '', $modules[$dependency]->info['version']))) { -- $row['#requires'][$dependency] = $this->t('@module (@constraint) (incompatible with version @version)', [ -- '@module' => $name, -- '@constraint' => $dependency_object->getConstraintString(), -- '@version' => $modules[$dependency]->info['version'], -- ]); -- $row['enable']['#disabled'] = TRUE; -- } -- // Disable the checkbox if the dependency is incompatible with this -- // version of Drupal core. -- elseif ($modules[$dependency]->info['core'] != \Drupal::CORE_COMPATIBILITY) { -- $row['#requires'][$dependency] = $this->t('@module (incompatible with this version of Drupal core)', [ -- '@module' => $name, -- ]); -- $row['enable']['#disabled'] = TRUE; -- } -- elseif ($modules[$dependency]->status) { -- $row['#requires'][$dependency] = $this->t('@module', ['@module' => $name]); -- } -- else { -- $row['#requires'][$dependency] = $this->t('@module (disabled)', ['@module' => $name]); -- } -+ -+ if ($incompatible = static::checkDependencyMessage($modules, $dependency, $version)) { -+ $row['#requires'][$dependency] = $incompatible; -+ $row['enable']['#disabled'] = TRUE; +@@ -329,6 +365,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) { + if (!isset($modules[$dependency])) { + $row['#requires'][$dependency] = $this->t('@module (missing)', ['@module' => $dependency]); + $row['enable']['#disabled'] = TRUE; + continue; } -+ -+ $name = $modules[$dependency]->info['name']; -+ $row['#requires'][$dependency] = $modules[$dependency]->status ? $this->t('@module', ['@module' => $name]) : $this->t('@module (disabled)', ['@module' => $name]); - } - - // If this module is required by other modules, list those, and then make it + // Only display visible modules. + elseif (empty($modules[$dependency]->hidden)) { diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc -index 799869bbc3..9431642e78 100644 +index 661677b51a..4edb955909 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc -@@ -165,6 +165,8 @@ function template_preprocess_system_modules_details(&$variables) { +@@ -166,6 +166,8 @@ function template_preprocess_system_modules_details(&$variables) { ]; $module['requires'] = $renderer->render($requires); } @@ -480,7 +440,7 @@ if (!empty($module['#required_by'])) { $required_by = [ '#theme' => 'item_list', -@@ -290,6 +292,12 @@ function template_preprocess_system_themes_page(&$variables) { +@@ -291,6 +293,12 @@ function template_preprocess_system_themes_page(&$variables) { $current_theme['is_default'] = $theme->is_default; $current_theme['is_admin'] = $theme->is_admin; @@ -492,8 +452,8 @@ + // Make sure to provide feedback on compatibility. $current_theme['incompatible'] = ''; - if (!empty($theme->incompatible_core)) { -@@ -310,6 +318,9 @@ function template_preprocess_system_themes_page(&$variables) { + if (!empty($theme->info['core_incompatible'])) { +@@ -311,6 +319,9 @@ function template_preprocess_system_themes_page(&$variables) { elseif (!empty($theme->incompatible_engine)) { $current_theme['incompatible'] = t('This theme requires the theme engine @theme_engine to operate correctly.', ['@theme_engine' => $theme->info['engine']]); } @@ -543,7 +503,7 @@ * The library discovery service. * diff --git a/core/tests/Drupal/KernelTests/Core/Render/ElementInfoIntegrationTest.php b/core/tests/Drupal/KernelTests/Core/Render/ElementInfoIntegrationTest.php -index cd33fcb6dc..512a3bd40a 100644 +index f5b58c6445..e79c843684 100644 --- a/core/tests/Drupal/KernelTests/Core/Render/ElementInfoIntegrationTest.php +++ b/core/tests/Drupal/KernelTests/Core/Render/ElementInfoIntegrationTest.php @@ -11,6 +11,11 @@ @@ -559,7 +519,7 @@ * {@inheritdoc} */ diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php -index 5c650d4f46..85fe4f2c7c 100644 +index d625c2d9f6..32046d79e8 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php @@ -6,6 +6,7 @@ @@ -570,7 +530,7 @@ /** * Tests installing and uninstalling of themes. -@@ -347,6 +348,22 @@ public function testThemeInfoAlter() { +@@ -345,6 +346,22 @@ public function testThemeInfoAlter() { $this->assertFalse(isset($theme_list[$name]->info['regions']['test_region'])); }