diff --git a/core/core.services.yml b/core/core.services.yml index 36376e2..7700054 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -277,7 +277,7 @@ services: arguments: ['%container.modules%', '@cache.bootstrap'] theme_handler: class: Drupal\Core\Extension\ThemeHandler - arguments: ['@config.factory', '@module_handler', '@state', '@info_parser', '@logger.channel.default', '@asset.css.collection_optimizer', '@config.installer', '@router.builder'] + arguments: ['@config.factory', '@module_handler', '@state', '@info_parser', '@logger.channel.default', '@asset.css.collection_optimizer', '@config.installer', '@config.manager', '@router.builder'] entity.manager: class: Drupal\Core\Entity\EntityManager arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@language_manager', '@string_translation', '@class_resolver', '@typed_data_manager'] diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php index ba1c257..df83a02 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -12,6 +12,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigInstallerInterface; +use Drupal\Core\Config\ConfigManagerInterface; use Drupal\Core\State\StateInterface; use Drupal\Core\Routing\RouteBuilder; use Psr\Log\LoggerInterface; @@ -109,6 +110,13 @@ class ThemeHandler implements ThemeHandlerInterface { protected $cssCollectionOptimizer; /** + * The config manager used to uninstall a theme. + * + * @var \Drupal\Core\Config\ConfigManagerInterface + */ + protected $configManager; + + /** * Constructs a new ThemeHandler. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory @@ -119,20 +127,22 @@ class ThemeHandler implements ThemeHandlerInterface { * The state store. * @param \Drupal\Core\Extension\InfoParserInterface $info_parser * The info parser to parse the theme.info.yml files. - * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $css_collection_optimizer - * The CSS asset collection optimizer service. * @param \Psr\Log\LoggerInterface $logger * A logger instance. + * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $css_collection_optimizer + * The CSS asset collection optimizer service. * @param \Drupal\Core\Config\ConfigInstallerInterface $config_installer * (optional) The config installer to install configuration. This optional * to allow the theme handler to work before Drupal is installed and has a * database. + * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager + * The config manager used to uninstall a theme. * @param \Drupal\Core\Routing\RouteBuilder $route_builder * (optional) The route builder to rebuild the routes if a theme is enabled. * @param \Drupal\Core\Extension\ExtensionDiscovery $extension_discovery * (optional) A extension discovery instance (for unit tests). */ - public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, StateInterface $state, InfoParserInterface $info_parser,LoggerInterface $logger, AssetCollectionOptimizerInterface $css_collection_optimizer = NULL, ConfigInstallerInterface $config_installer = NULL, RouteBuilder $route_builder = NULL, ExtensionDiscovery $extension_discovery = NULL) { + public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, StateInterface $state, InfoParserInterface $info_parser,LoggerInterface $logger, AssetCollectionOptimizerInterface $css_collection_optimizer = NULL, ConfigInstallerInterface $config_installer = NULL, ConfigManagerInterface $config_manager = NULL, RouteBuilder $route_builder = NULL, ExtensionDiscovery $extension_discovery = NULL) { $this->configFactory = $config_factory; $this->moduleHandler = $module_handler; $this->state = $state; @@ -140,6 +150,7 @@ public function __construct(ConfigFactoryInterface $config_factory, ModuleHandle $this->logger = $logger; $this->cssCollectionOptimizer = $css_collection_optimizer; $this->configInstaller = $config_installer; + $this->configManager = $config_manager; $this->routeBuilder = $route_builder; $this->extensionDiscovery = $extension_discovery; } @@ -368,13 +379,13 @@ public function uninstall(array $theme_list) { foreach ($theme_list as $key) { // Remove all configuration belonging to the theme. - \Drupal::service('config.manager')->uninstall('theme', $key); + $this->configManager->uninstall('theme', $key); $extension_config->clear("disabled.theme.$key"); } $extension_config->save(); - $this->moduleHandler->invokeAll('themes_uninstalled', array($theme_list)); + $this->moduleHandler->invokeAll('themes_uninstalled', [$theme_list]); } /** diff --git a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php index fc68831..353ab81 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php @@ -44,9 +44,15 @@ public function disable(array $theme_list); /** * Uninstalls a given list of disabled themes. * + * Uninstalling a theme removes all related configuration (like blocks) and + * invokes the 'themes_uninstalled' hook. + * * @param array $theme_list * The themes to uninstall. * + * @throws \InvalidArgumentException + * Thrown when you uninstall an not installed or not disabled theme. + * * @see hook_themes_uninstalled() */ public function uninstall(array $theme_list); @@ -150,6 +156,16 @@ public function getName($theme); public function getDefault(); /** + * Sets a new default theme. + * + * @param string $theme + * The new default theme. + * + * @return $this + */ + public function setDefault($theme); + + /** * Returns an array of directories for all enabled themes. * * Useful for tasks such as finding a file that exists in all theme diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php index ebd4a7c..abfd375 100644 --- a/core/modules/block/src/Tests/BlockTest.php +++ b/core/modules/block/src/Tests/BlockTest.php @@ -7,6 +7,7 @@ namespace Drupal\block\Tests; +use Drupal\block\Entity\Block; use Drupal\Core\Cache\Cache; use Drupal\simpletest\WebTestBase; use Drupal\Component\Utility\String; @@ -376,4 +377,25 @@ public function testBlockCacheTags() { $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS'); } + /** + * Tests that uninstalling a theme removes its block configuration. + */ + public function testUninstallTheme() { + /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */ + $theme_handler = \Drupal::service('theme_handler'); + + $theme_handler->enable(['seven']); + $theme_handler->setDefault('seven'); + $block = $this->drupalPlaceBlock('system_powered_by_block', ['theme' => 'seven', 'region' => 'help']); + $this->drupalGet(''); + $this->assertText('Powered by Drupal'); + + $theme_handler->setDefault('stark'); + $theme_handler->disable(['seven']); + $theme_handler->uninstall(['seven']); + + // Ensure that the block configuration does not exist anymore. + $this->assertIdentical(NULL, Block::load($block->id())); + } + } diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 59922b7..75987fa 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1397,6 +1397,42 @@ function hook_module_preuninstall($module) { } /** + * Perform necessary actions when themes are enabled. + * + * @param array $themes + * An array of theme names which are enabled. + */ +function hook_themes_enabled(array $themes) { + // Add some state entries depending on the theme. + foreach ($themes as $theme) { + \Drupal::state()->set('example.' . $theme, 'some-value'); + } +} + +/** + * Perform necessary actions when themes are disabled. + * + * @param array $themes + * An array of theme names which are enabled. + */ +function hook_themes_disabled(array $themes) { + // Update some state entries depending on the theme. + foreach ($themes as $theme) { + \Drupal::state()->set('example.' . $theme, 0); + } +} + +/** + * Perform necessary actions when themes are uninstalled. + */ +function hook_themes_uninstalled(array $themes) { + // Remove some state entries depending on the theme. + foreach ($themes as $theme) { + \Drupal::state()->delete('example.' . $theme); + } +} + +/** * Perform necessary actions after modules are uninstalled. * * This function differs from hook_uninstall() in that it gives all other