diff --git a/core/config/install/core.extension.yml b/core/config/install/core.extension.yml index 1514a9e..ce74bae 100644 --- a/core/config/install/core.extension.yml +++ b/core/config/install/core.extension.yml @@ -1,4 +1,2 @@ module: {} theme: {} -disabled: - theme: {} diff --git a/core/config/schema/core.extension.schema.yml b/core/config/schema/core.extension.schema.yml index 3b93d92..7d452a3 100644 --- a/core/config/schema/core.extension.schema.yml +++ b/core/config/schema/core.extension.schema.yml @@ -14,13 +14,3 @@ core.extension: sequence: - type: integer label: 'Weight' - disabled: - type: mapping - label: 'Disabled extensions' - mapping: - theme: - type: sequence - label: 'Disabled themes' - sequence: - - type: integer - label: 'Weight' diff --git a/core/core.services.yml b/core/core.services.yml index f8c428f..80bca4a 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -278,7 +278,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/includes/theme.inc b/core/includes/theme.inc index b349918..511edef 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -784,24 +784,6 @@ function theme_enable($theme_list) { } /** - * Disables a given list of themes. - * - * @param $theme_list - * An array of theme names. - * - * @return bool - * Whether any of the given themes have been disabled. - * - * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. - * Use \Drupal::service('theme_handler')->disable(). - * - * @see \Drupal\Core\Extension\ThemeHandler::disable(). - */ -function theme_disable($theme_list) { - return \Drupal::service('theme_handler')->disable($theme_list); -} - -/** * @addtogroup themeable * @{ */ diff --git a/core/includes/update.inc b/core/includes/update.inc index f9a88db..46e5428 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -29,9 +29,6 @@ function update_fix_compatibility() { foreach ($extension_config->get($type) as $name => $weight) { if (update_check_incompatibility($name, $type)) { $extension_config->clear("$type.$name"); - if ($type === 'theme') { - $extension_config->set("disabled.theme.$name", 0); - } $save = TRUE; } } diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index e060cdf..5c7e1ae 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -124,7 +124,7 @@ class ConfigImporter { protected $themeHandler; /** - * Flag set to import system.theme during processing theme enable and disables. + * Flag set to import system.theme during processing theme enable and uninstalls. * * @var bool */ @@ -253,7 +253,7 @@ protected function getEmptyExtensionsProcessedList() { ), 'theme' => array( 'enable' => array(), - 'disable' => array(), + 'uninstall' => array(), ), ); } @@ -395,9 +395,9 @@ protected function createExtensionChangelist() { $module_list = array_reverse($module_list); $install = array_intersect(array_keys($module_list), $install); - // Work out what themes to enable and to disable. + // Work out what themes to enable and to uninstall. $enable = array_diff(array_keys($new_extensions['theme']), array_keys($current_extensions['theme'])); - $disable = array_diff(array_keys($current_extensions['theme']), array_keys($new_extensions['theme'])); + $theme_uninstall = array_diff(array_keys($current_extensions['theme']), array_keys($new_extensions['theme'])); $this->extensionChangelist = array( 'module' => array( @@ -406,7 +406,7 @@ protected function createExtensionChangelist() { ), 'theme' => array( 'enable' => $enable, - 'disable' => $disable, + 'uninstall' => $theme_uninstall, ), ); } @@ -445,7 +445,7 @@ protected function getUnprocessedExtensions($type) { if ($type == 'theme') { $unprocessed = array( 'enable' => array_diff($changelist['enable'], $this->processedExtensions[$type]['enable']), - 'disable' => array_diff($changelist['disable'], $this->processedExtensions[$type]['disable']), + 'uninstall' => array_diff($changelist['uninstall'], $this->processedExtensions[$type]['uninstall']), ); } else { @@ -533,7 +533,7 @@ public function initialize() { $this->totalExtensionsToProcess += count($modules[$op]); } $themes = $this->getUnprocessedExtensions('theme'); - foreach (array('enable', 'disable') as $op) { + foreach (array('enable', 'uninstall') as $op) { $this->totalExtensionsToProcess += count($themes[$op]); } @@ -576,7 +576,7 @@ protected function processExtensions(array &$context) { $this->processExtension($operation['type'], $operation['op'], $operation['name']); $context['message'] = t('Synchronising extensions: @op @name.', array('@op' => $operation['op'], '@name' => $operation['name'])); $processed_count = count($this->processedExtensions['module']['install']) + count($this->processedExtensions['module']['uninstall']); - $processed_count += count($this->processedExtensions['theme']['disable']) + count($this->processedExtensions['theme']['enable']); + $processed_count += count($this->processedExtensions['theme']['uninstall']) + count($this->processedExtensions['theme']['enable']); $context['finished'] = $processed_count / $this->totalExtensionsToProcess; } else { @@ -660,7 +660,7 @@ protected function getNextExtensionOperation() { ); } } - foreach (array('enable', 'disable') as $op) { + foreach (array('enable', 'uninstall') as $op) { $themes = $this->getUnprocessedExtensions('theme'); if (!empty($themes[$op])) { return array( @@ -794,11 +794,11 @@ protected function processExtension($type, $op, $name) { $this->moduleHandler->loadAll(); } if ($type == 'theme') { - // Theme disables possible remove default or admin themes therefore we - // need to import this before doing any. If there are no disables and - // the default or admin theme is change this will be picked up whilst + // Theme uninstalls possible remove default or admin themes therefore we + // need to import this before doing any. If there are no uninstalls and + // the default or admin theme is changing this will be picked up whilst // processing configuration. - if ($op == 'disable' && $this->processedSystemTheme === FALSE) { + if ($op == 'uninstall' && $this->processedSystemTheme === FALSE) { $this->importConfig(StorageInterface::DEFAULT_COLLECTION, 'update', 'system.theme'); $this->configManager->getConfigFactory()->reset('system.theme'); $this->processedSystemTheme = TRUE; diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php index a4a086a..56e399c 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -12,12 +12,13 @@ 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; /** - * Default theme handler using the config system for enabled/disabled themes. + * Default theme handler using the config system for enabled/unsintalled themes. */ class ThemeHandler implements ThemeHandlerInterface { @@ -53,7 +54,7 @@ class ThemeHandler implements ThemeHandlerInterface { protected $configFactory; /** - * The module handler to fire themes_enabled/themes_disabled hooks. + * The module handler to fire themes_enabled/themes_uninstalled hooks. * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ @@ -109,30 +110,39 @@ 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 * The config factory to get the enabled themes. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler to fire themes_enabled/themes_disabled hooks. + * The module handler to fire themes_enabled/themes_uninstalled hooks. * @param \Drupal\Core\State\StateInterface $state * 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; } @@ -189,7 +200,6 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) { // Nothing to do. All themes already enabled. return TRUE; } - $installed_themes += $extension_config->get('disabled.theme') ?: array(); while (list($theme) = each($theme_list)) { // Add dependencies to the list. The new themes will be processed as @@ -218,7 +228,6 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) { } else { $installed_themes = $extension_config->get('theme') ?: array(); - $installed_themes += $extension_config->get('disabled.theme') ?: array(); } $themes_enabled = array(); @@ -240,7 +249,6 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) { // The value is not used; the weight is ignored for themes currently. $extension_config ->set("theme.$key", 0) - ->clear("disabled.theme.$key") ->save(); // Add the theme to the current list. @@ -294,40 +302,36 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) { /** * {@inheritdoc} */ - public function disable(array $theme_list) { - $list = $this->listInfo(); + public function uninstall(array $theme_list) { + $extension_config = $this->configFactory->get('core.extension'); $theme_config = $this->configFactory->get('system.theme'); - + $list = $this->listInfo(); foreach ($theme_list as $key) { if (!isset($list[$key])) { throw new \InvalidArgumentException("Unknown theme: $key."); } if ($key === $theme_config->get('default')) { - throw new \InvalidArgumentException("The current default theme $key cannot be disabled."); + throw new \InvalidArgumentException("The current default theme $key cannot be uninstalled."); } if ($key === $theme_config->get('admin')) { - throw new \InvalidArgumentException("The current admin theme $key cannot be disabled."); + throw new \InvalidArgumentException("The current admin theme $key cannot be uninstalled."); } - // Base themes cannot be disabled if sub themes are enabled, and if they - // are not disabled at the same time. + // Base themes cannot be uninstalled if sub themes are enabled, and if they + // are not uninstalled at the same time. if (!empty($list[$key]->sub_themes)) { foreach ($list[$key]->sub_themes as $sub_key => $sub_label) { if (isset($list[$sub_key]) && !in_array($sub_key, $theme_list, TRUE)) { - throw new \InvalidArgumentException("The base theme $key cannot be disabled, because theme $sub_key depends on it."); + throw new \InvalidArgumentException("The base theme $key cannot be uninstalled, because theme $sub_key depends on it."); } } } } $this->cssCollectionOptimizer->deleteAll(); - - $extension_config = $this->configFactory->get('core.extension'); $current_theme_data = $this->state->get('system.theme.data', array()); foreach ($theme_list as $key) { // The value is not used; the weight is ignored for themes currently. - $extension_config - ->clear("theme.$key") - ->set("disabled.theme.$key", 0); + $extension_config->clear("theme.$key"); // Remove the theme from the current list. unset($this->list[$key]); @@ -341,14 +345,17 @@ public function disable(array $theme_list) { // @todo Remove system_list(). $this->systemListReset(); + + // Remove all configuration belonging to the theme. + $this->configManager->uninstall('theme', $key); + } $extension_config->save(); $this->state->set('system.theme.data', $current_theme_data); $this->resetSystem(); - // Invoke hook_themes_disabled after the themes have been disabled. - $this->moduleHandler->invokeAll('themes_disabled', 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 74f9ad2..a17a851 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Extension; /** - * Manages the list of available themes as well as enable/disable them. + * Manages the list of available themes as well as enable/unsintall them. */ interface ThemeHandlerInterface { @@ -31,15 +31,20 @@ public function enable(array $theme_list, $enable_dependencies = TRUE); /** - * Disables a given list of themes. + * Uninstalls a given list of themes. + * + * Uninstalling a theme removes all related configuration (like blocks) and + * invokes the 'themes_uninstalled' hook. * * @param array $theme_list - * An array of theme names. + * The themes to uninstall. * - * @return bool - * Whether any of the given themes have been disabled. + * @throws \InvalidArgumentException + * Thrown when you uninstall an not installed theme. + * + * @see hook_themes_uninstalled() */ - public function disable(array $theme_list); + public function uninstall(array $theme_list); /** * Returns a list of currently enabled themes. @@ -140,6 +145,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/lib/Drupal/Core/Utility/ProjectInfo.php b/core/lib/Drupal/Core/Utility/ProjectInfo.php index 2a49c8c..9382cba 100644 --- a/core/lib/Drupal/Core/Utility/ProjectInfo.php +++ b/core/lib/Drupal/Core/Utility/ProjectInfo.php @@ -56,6 +56,7 @@ function processInfoList(array &$projects, array $list, $project_type, $status, $file->enabled_sub_themes[$key] = $name; } } + // @todo the following looks incorrect // If the theme is disabled and there are no enabled subthemes, we // should ignore this base theme for the enabled case. If the site is // trying to display disabled themes, we'll catch it then. diff --git a/core/modules/block/src/Tests/BlockAdminThemeTest.php b/core/modules/block/src/Tests/BlockAdminThemeTest.php index 20b892c..939eb9d 100644 --- a/core/modules/block/src/Tests/BlockAdminThemeTest.php +++ b/core/modules/block/src/Tests/BlockAdminThemeTest.php @@ -31,7 +31,8 @@ function testAdminTheme() { $admin_user = $this->drupalCreateUser(array('administer blocks', 'administer themes')); $this->drupalLogin($admin_user); - // Ensure that access to block admin page is denied when theme is disabled. + // Ensure that access to block admin page is denied when theme is not + // installed. $this->drupalGet('admin/structure/block/list/bartik'); $this->assertResponse(403); diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php index 6240fcd..90b97ce 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; @@ -377,4 +378,24 @@ 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->uninstall(['seven']); + + // Ensure that the block configuration does not exist anymore. + $this->assertIdentical(NULL, Block::load($block->id())); + } + } diff --git a/core/modules/breakpoint/breakpoint.module b/core/modules/breakpoint/breakpoint.module index 35187b1..5abf8a9 100644 --- a/core/modules/breakpoint/breakpoint.module +++ b/core/modules/breakpoint/breakpoint.module @@ -44,8 +44,8 @@ function breakpoint_themes_enabled($theme_list) { } /** - * Implements hook_themes_disabled() + * Implements hook_themes_uninstalled() */ -function breakpoint_themes_disabled($theme_list) { +function breakpoint_themes_uninstalled($theme_list) { \Drupal::service('breakpoint.manager')->clearCachedDefinitions(); } diff --git a/core/modules/config/src/Tests/ConfigImportUITest.php b/core/modules/config/src/Tests/ConfigImportUITest.php index f5e4947..ad97616 100644 --- a/core/modules/config/src/Tests/ConfigImportUITest.php +++ b/core/modules/config/src/Tests/ConfigImportUITest.php @@ -164,7 +164,6 @@ function testImport() { unset($core_extension['module']['options']); unset($core_extension['module']['text']); unset($core_extension['theme']['bartik']); - $core_extension['disabled']['theme']['bartik'] = 0; $staging->write('core.extension', $core_extension); $staging->delete('action.settings'); $staging->delete('text.settings'); @@ -205,7 +204,7 @@ function testImport() { $this->assertTrue(empty($installed), 'No modules installed during import'); $theme_info = \Drupal::service('theme_handler')->listInfo(); - $this->assertFalse(isset($theme_info['bartik']), 'Bartik theme disabled during import.'); + $this->assertFalse(isset($theme_info['bartik']), 'Bartik theme uninstalled during import.'); // Verify that the action.settings configuration object was only deleted // once during the import process. diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module index 43aa754..57b9cb0 100644 --- a/core/modules/config_translation/config_translation.module +++ b/core/modules/config_translation/config_translation.module @@ -72,9 +72,9 @@ function config_translation_themes_enabled() { } /** - * Implements hook_themes_disabled(). + * Implements hook_themes_uninstalled(). */ -function config_translation_themes_disabled() { +function config_translation_themes_uninstalled() { // Themes can provide *.config_translation.yml declarations. // @todo Make ThemeHandler trigger an event instead and make // ConfigMapperManager plugin manager subscribe to it. diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 3122f59..b770cc3 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -366,9 +366,9 @@ function locale_themes_enabled($themes) { } /** - * Implements hook_themes_disabled(). + * Implements hook_themes_uninstalled(). */ -function locale_themes_disabled($themes) { +function locale_themes_uninstalled($themes) { $components['theme'] = $themes; locale_system_remove($components); } diff --git a/core/modules/system/css/system.admin.css b/core/modules/system/css/system.admin.css index 0031327..beec8fc 100644 --- a/core/modules/system/css/system.admin.css +++ b/core/modules/system/css/system.admin.css @@ -219,7 +219,7 @@ table.system-status-report .status-title { .system-themes-list { margin-bottom: 20px; } -.system-themes-list-disabled { +.system-themes-list-uninstalled { border-top: 1px solid #cdcdcd; padding-top: 20px; } @@ -241,8 +241,8 @@ table.system-status-report .status-title { .theme-default .screenshot { border: 1px solid #aaa; } -.system-themes-list-disabled .screenshot, -.system-themes-list-disabled .no-screenshot { +.system-themes-list-uninstalled .screenshot, +.system-themes-list-uninstalled .no-screenshot { max-width: 194px; height: auto; } @@ -271,17 +271,17 @@ table.system-status-report .status-title { body:not(.toolbar-vertical) .system-themes-list-enabled h3 { margin-top: 0; } - body:not(.toolbar-vertical) .system-themes-list-disabled .theme-selector { + body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-selector { box-sizing: border-box; width: 31.25%; float: left; /* LTR */ padding: 20px 20px 20px 0; /* LTR */ } - [dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-disabled .theme-selector { + [dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-selector { float: right; padding: 20px 0 20px 20px; } - body:not(.toolbar-vertical) .system-themes-list-disabled .theme-info { + body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-info { min-height: 170px; } } @@ -304,17 +304,17 @@ table.system-status-report .status-title { .toolbar-vertical .system-themes-list-enabled h3 { margin-top: 0; } - .toolbar-vertical .system-themes-list-disabled .theme-selector { + .toolbar-vertical .system-themes-list-uninstalled .theme-selector { box-sizing: border-box; width: 31.25%; float: left; /* LTR */ padding: 20px 20px 20px 0; /* LTR */ } - [dir="rtl"] .toolbar-vertical .system-themes-list-disabled .theme-selector { + [dir="rtl"] .toolbar-vertical .system-themes-list-uninstalled .theme-selector { float: right; padding: 20px 0 20px 20px; } - .toolbar-vertical .system-themes-list-disabled .theme-info { + .toolbar-vertical .system-themes-list-uninstalled .theme-info { min-height: 170px; } } diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php index 605b0a9..cf83c40 100644 --- a/core/modules/system/src/Controller/SystemController.php +++ b/core/modules/system/src/Controller/SystemController.php @@ -191,7 +191,7 @@ public function themesPage() { uasort($themes, 'system_sort_modules_by_info_name'); $theme_default = $config->get('default'); - $theme_groups = array('enabled' => array(), 'disabled' => array()); + $theme_groups = array('enabled' => array(), 'uninstalled' => array()); $admin_theme = $config->get('admin'); $admin_theme_options = array(); @@ -251,10 +251,10 @@ public function themesPage() { if (!$theme->is_default) { if ($theme->getName() != $admin_theme) { $theme->operations[] = array( - 'title' => $this->t('Disable'), - 'route_name' => 'system.theme_disable', + 'title' => $this->t('Uninstall'), + 'route_name' => 'system.theme_uninstall', 'query' => $query, - 'attributes' => array('title' => $this->t('Disable !theme theme', array('!theme' => $theme->info['name']))), + 'attributes' => array('title' => $this->t('Uninstall !theme theme', array('!theme' => $theme->info['name']))), ); } $theme->operations[] = array( @@ -294,16 +294,16 @@ public function themesPage() { $theme->notes[] = $this->t('admin theme'); } - // Sort enabled and disabled themes into their own groups. - $theme_groups[$theme->status ? 'enabled' : 'disabled'][] = $theme; + // Sort enabled and uninstalled themes into their own groups. + $theme_groups[$theme->status ? 'enabled' : 'uninstalled'][] = $theme; } // There are two possible theme groups. $theme_group_titles = array( 'enabled' => $this->formatPlural(count($theme_groups['enabled']), 'Enabled theme', 'Enabled themes'), ); - if (!empty($theme_groups['disabled'])) { - $theme_group_titles['disabled'] = $this->formatPlural(count($theme_groups['disabled']), 'Disabled theme', 'Disabled themes'); + if (!empty($theme_groups['uninstalled'])) { + $theme_group_titles['uninstalled'] = $this->formatPlural(count($theme_groups['uninstalled']), 'Uninstalled theme', 'Uninstalled themes'); } uasort($theme_groups['enabled'], 'system_sort_themes'); diff --git a/core/modules/system/src/Controller/ThemeController.php b/core/modules/system/src/Controller/ThemeController.php index 51d91f9..2e58c5c 100644 --- a/core/modules/system/src/Controller/ThemeController.php +++ b/core/modules/system/src/Controller/ThemeController.php @@ -57,7 +57,7 @@ public static function create(ContainerInterface $container) { } /** - * Disables a theme. + * Uninstalls a theme. * * @param \Symfony\Component\HttpFoundation\Request $request * A request object containing a theme name and a valid token. @@ -69,7 +69,7 @@ public static function create(ContainerInterface $container) { * Throws access denied when no theme or token is set in the request or when * the token is invalid. */ - public function disable(Request $request) { + public function uninstall(Request $request) { $theme = $request->get('theme'); $config = $this->config('system.theme'); @@ -79,13 +79,13 @@ public function disable(Request $request) { // Check if the specified theme is one recognized by the system. if (!empty($themes[$theme])) { - // Do not disable the default or admin theme. + // Do not uninstall the default or admin theme. if ($theme === $config->get('default') || $theme === $config->get('admin')) { - drupal_set_message($this->t('%theme is the default theme and cannot be disabled.', array('%theme' => $themes[$theme]->info['name'])), 'error'); + drupal_set_message($this->t('%theme is the default theme and cannot be uninstalled.', array('%theme' => $themes[$theme]->info['name'])), 'error'); } else { - $this->themeHandler->disable(array($theme)); - drupal_set_message($this->t('The %theme theme has been disabled.', array('%theme' => $themes[$theme]->info['name']))); + $this->themeHandler->uninstall(array($theme)); + drupal_set_message($this->t('The %theme theme has been uninstalled.', array('%theme' => $themes[$theme]->info['name']))); } } else { diff --git a/core/modules/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php index 85a58a5..304b8e1 100644 --- a/core/modules/system/src/Form/ThemeSettingsForm.php +++ b/core/modules/system/src/Form/ThemeSettingsForm.php @@ -71,7 +71,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $theme = $themes = list_themes(); - // Deny access if the theme is disabled or not found. + // Deny access if the theme is not enabled or not found. if (!empty($theme) && (empty($themes[$theme]) || !$themes[$theme]->status)) { throw new NotFoundHttpException(); } diff --git a/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php b/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php index f652bfd..1b941ab 100644 --- a/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php +++ b/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php @@ -39,11 +39,10 @@ protected function setUp() { } /** - * Verifies that no themes are installed/enabled/disabled by default. + * Verifies that no themes are installed/enabled by default. */ function testEmpty() { $this->assertFalse($this->extensionConfig()->get('theme')); - $this->assertFalse($this->extensionConfig()->get('disabled.theme')); $this->assertFalse(array_keys($this->themeHandler()->listInfo())); $this->assertFalse(array_keys(system_list('theme'))); @@ -67,7 +66,6 @@ function testEnable() { $this->themeHandler()->enable(array($name)); $this->assertIdentical($this->extensionConfig()->get("theme.$name"), 0); - $this->assertNull($this->extensionConfig()->get("disabled.theme.$name")); $themes = $this->themeHandler()->listInfo(); $this->assertTrue(isset($themes[$name])); @@ -97,7 +95,7 @@ function testEnableSubTheme() { $this->assertTrue(isset($themes[$name])); $this->assertTrue(isset($themes[$base_name])); - $this->themeHandler()->disable(array($name)); + $this->themeHandler()->uninstall(array($name)); $themes = $this->themeHandler()->listInfo(); $this->assertFalse(isset($themes[$name])); @@ -143,76 +141,9 @@ function testEnableNameTooLong() { } /** - * Tests disabling a theme. + * Tests uninstalling the default theme. */ - function testDisable() { - $name = 'test_basetheme'; - $this->themeHandler()->enable(array($name)); - - // Prime the relevant drupal_static()s. - $this->assertEqual(array_keys(system_list('theme')), array($name)); - $this->assertIdentical(theme_get_setting('features.favicon', $name), FALSE); - - $this->themeHandler()->disable(array($name)); - - $this->assertIdentical($this->extensionConfig()->get('theme'), array()); - $this->assertIdentical($this->extensionConfig()->get("disabled.theme.$name"), 0); - - $this->assertFalse(array_keys($this->themeHandler()->listInfo())); - $this->assertFalse(array_keys(system_list('theme'))); - - // Verify that test_basetheme.settings no longer applies, even though the - // configuration still exists. - $this->assertIdentical(theme_get_setting('features.favicon', $name), TRUE); - $this->assertNull(theme_get_setting('base', $name)); - $this->assertNull(theme_get_setting('override', $name)); - - // The theme is not uninstalled, so its configuration must still exist. - $this->assertTrue($this->config("$name.settings")->get()); - } - - /** - * Tests disabling and enabling a theme. - * - * Verifies that - * - themes can be re-enabled - * - default configuration is not re-imported upon re-enabling an already - * installed theme. - */ - function testDisableEnable() { - $name = 'test_basetheme'; - - $this->themeHandler()->enable(array($name)); - $this->themeHandler()->disable(array($name)); - - $this->assertIdentical($this->config("$name.settings")->get('base'), 'only'); - $this->assertIdentical($this->config('core.date_format.fancy')->get('label'), 'Fancy date'); - - // Default configuration never overwrites custom configuration, so just - // changing values in existing configuration will cause ConfigInstaller to - // simply skip those files. To ensure that no default configuration is - // re-imported, the custom configuration has to be deleted. - $this->configStorage()->delete("$name.settings"); - $this->configStorage()->delete('core.date_format.fancy'); - // Reflect direct storage operations in ConfigFactory. - $this->container->get('config.factory')->reset(); - - $this->themeHandler()->enable(array($name)); - - $themes = $this->themeHandler()->listInfo(); - $this->assertTrue(isset($themes[$name])); - $this->assertEqual($themes[$name]->getName(), $name); - - $this->assertEqual(array_keys(system_list('theme')), array_keys($themes)); - - $this->assertFalse($this->config("$name.settings")->get()); - $this->assertNull($this->config('core.date_format.fancy')->get('label')); - } - - /** - * Tests disabling the default theme. - */ - function testDisableDefault() { + function testUnisntallDefault() { $name = 'stark'; $other_name = 'bartik'; $this->themeHandler()->enable(array($name, $other_name)); @@ -223,8 +154,8 @@ function testDisableDefault() { $this->assertTrue(isset($themes[$other_name])); try { - $message = 'ThemeHandler::disable() throws InvalidArgumentException upon disabling default theme.'; - $this->themeHandler()->disable(array($name)); + $message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon disabling default theme.'; + $this->themeHandler()->uninstall(array($name)); $this->fail($message); } catch (\InvalidArgumentException $e) { @@ -237,9 +168,9 @@ function testDisableDefault() { } /** - * Tests disabling the admin theme. + * Tests uninstalling the admin theme. */ - function testDisableAdmin() { + function testUninstallAdmin() { $name = 'stark'; $other_name = 'bartik'; $this->themeHandler()->enable(array($name, $other_name)); @@ -250,8 +181,8 @@ function testDisableAdmin() { $this->assertTrue(isset($themes[$other_name])); try { - $message = 'ThemeHandler::disable() throws InvalidArgumentException upon disabling admin theme.'; - $this->themeHandler()->disable(array($name)); + $message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon disabling admin theme.'; + $this->themeHandler()->uninstall(array($name)); $this->fail($message); } catch (\InvalidArgumentException $e) { @@ -264,14 +195,14 @@ function testDisableAdmin() { } /** - * Tests disabling a sub-theme. + * Tests uninstalling a sub-theme. */ - function testDisableSubTheme() { + function testUninstallSubTheme() { $name = 'test_subtheme'; $base_name = 'test_basetheme'; $this->themeHandler()->enable(array($name)); - $this->themeHandler()->disable(array($name)); + $this->themeHandler()->uninstall(array($name)); $themes = $this->themeHandler()->listInfo(); $this->assertFalse(isset($themes[$name])); @@ -279,17 +210,17 @@ function testDisableSubTheme() { } /** - * Tests disabling a base theme before its sub-theme. + * Tests uninstalling a base theme before its sub-theme. */ - function testDisableBaseBeforeSubTheme() { + function testUninstallBaseBeforeSubTheme() { $name = 'test_basetheme'; $sub_name = 'test_subtheme'; $this->themeHandler()->enable(array($sub_name)); try { - $message = 'ThemeHandler::disable() throws InvalidArgumentException upon disabling base theme before sub theme.'; - $this->themeHandler()->disable(array($name)); + $message = 'ThemeHandler::install() throws InvalidArgumentException upon uninstalling base theme before sub theme.'; + $this->themeHandler()->uninstall(array($name)); $this->fail($message); } catch (\InvalidArgumentException $e) { @@ -300,8 +231,8 @@ function testDisableBaseBeforeSubTheme() { $this->assertTrue(isset($themes[$name])); $this->assertTrue(isset($themes[$sub_name])); - // Verify that disabling both at the same time works. - $this->themeHandler()->disable(array($name, $sub_name)); + // Verify that uninstalling both at the same time works. + $this->themeHandler()->uninstall(array($name, $sub_name)); $themes = $this->themeHandler()->listInfo(); $this->assertFalse(isset($themes[$name])); @@ -309,7 +240,7 @@ function testDisableBaseBeforeSubTheme() { } /** - * Tests disabling a non-existing theme. + * Tests uninstalling a non-existing theme. */ function testDisableNonExisting() { $name = 'non_existing_theme'; @@ -318,8 +249,8 @@ function testDisableNonExisting() { $this->assertFalse(array_keys($themes)); try { - $message = 'ThemeHandler::disable() throws InvalidArgumentException upon disabling a non-existing theme.'; - $this->themeHandler()->disable(array($name)); + $message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon uninstalling a non-existing theme.'; + $this->themeHandler()->uninstall(array($name)); $this->fail($message); } catch (\InvalidArgumentException $e) { @@ -331,6 +262,37 @@ function testDisableNonExisting() { } /** + * Tests uninstalling a theme. + */ + function testUninstall() { + $name = 'test_basetheme'; + + $this->themeHandler()->enable(array($name)); + $this->themeHandler()->uninstall(array($name)); + + $this->assertFalse(array_keys($this->themeHandler()->listInfo())); + $this->assertFalse(array_keys(system_list('theme'))); + + $this->assertFalse($this->config("$name.settings")->get()); + } + + /** + * Tests uninstalling a theme that is not installed. + */ + function testUninstallNotInstalled() { + $name = 'test_basetheme'; + + try { + $message = 'ThemeHandler::uninstall() throws InvalidArgumentException upon uninstalling a theme that is not installed.'; + $this->themeHandler()->uninstall(array($name)); + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** * Tests that theme info can be altered by a module. * * @see module_test_system_info_alter() diff --git a/core/modules/system/src/Tests/Menu/MenuRouterTest.php b/core/modules/system/src/Tests/Menu/MenuRouterTest.php index c49e3fd..8013480 100644 --- a/core/modules/system/src/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/src/Tests/Menu/MenuRouterTest.php @@ -247,7 +247,7 @@ public function testThemeIntegration() { ->set('default', $this->default_theme) ->set('admin', $this->admin_theme) ->save(); - $theme_handler->disable(array('stark')); + $theme_handler->uninstall(array('stark')); $this->doTestThemeCallbackMaintenanceMode(); @@ -307,7 +307,7 @@ protected function doTestThemeCallbackOptionalTheme() { $this->assertText('Active theme: stark. Actual theme: stark.', 'The theme negotiation system uses an optional theme once it has been enabled.'); $this->assertRaw('stark/css/layout.css', "The optional theme's CSS appears on the page."); - $theme_handler->disable(array('stark')); + $theme_handler->uninstall(array('stark')); } /** diff --git a/core/modules/system/src/Tests/System/ThemeTest.php b/core/modules/system/src/Tests/System/ThemeTest.php index 6912afb..e6799db 100644 --- a/core/modules/system/src/Tests/System/ThemeTest.php +++ b/core/modules/system/src/Tests/System/ThemeTest.php @@ -41,7 +41,7 @@ protected function setUp() { function testThemeSettings() { // Ensure invalid theme settings form URLs return a proper 404. $this->drupalGet('admin/appearance/settings/bartik'); - $this->assertResponse(404, 'The theme settings form URL for a disabled theme could not be found.'); + $this->assertResponse(404, 'The theme settings form URL for a uninstalled theme could not be found.'); $this->drupalGet('admin/appearance/settings/' . $this->randomMachineName()); $this->assertResponse(404, 'The theme settings form URL for a non-existent theme could not be found.'); diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index c2f590f..c0f7258 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1396,6 +1396,29 @@ 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 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 diff --git a/core/modules/system/system.module b/core/modules/system/system.module index f3b65a2..a648792 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -80,7 +80,7 @@ function system_help($route_name, RouteMatchInterface $route_match) { $output .= '
' . t('Managing modules') . '
'; $output .= '
' . t('The System module allows users with the appropriate permissions to enable and disable modules on the Modules administration page. Drupal comes with a number of core modules, and each module provides a discrete set of features and may be enabled or disabled depending on the needs of the site. Many additional modules contributed by members of the Drupal community are available for download at the Drupal.org module page.', array('@modules' => url('admin/modules'), '@drupal-modules' => 'http://drupal.org/project/modules')) . '
'; $output .= '
' . t('Managing themes') . '
'; - $output .= '
' . t('The System module allows users with the appropriate permissions to enable and disable themes on the Appearance administration page. Themes determine the design and presentation of your site. Drupal comes packaged with several core themes, and additional contributed themes are available at the Drupal.org theme page.', array('@themes' => url('admin/appearance'), '@drupal-themes' => 'http://drupal.org/project/themes')) . '
'; + $output .= '
' . t('The System module allows users with the appropriate permissions to enable and uninstall themes on the Appearance administration page. Themes determine the design and presentation of your site. Drupal comes packaged with several core themes, and additional contributed themes are available at the Drupal.org theme page.', array('@themes' => url('admin/appearance'), '@drupal-themes' => 'http://drupal.org/project/themes')) . '
'; $output .= '
' . t('Managing caching') . '
'; $output .= '
' . t("The System module allows users with the appropriate permissions to manage caching on the Performance settings page. Drupal has a robust caching system that allows the efficient re-use of previously-constructed web pages and web page components. Pages requested by anonymous users are stored in a compressed format; depending on your site configuration and the amount of your web traffic tied to anonymous visitors, the caching system may significantly increase the speed of your site.", array('@cache-settings' => url('admin/config/development/performance'))) . '
'; $output .= '
' . t('Performing system maintenance') . '
'; diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 8034863..4f51b4c 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -253,10 +253,10 @@ system.modules_list_confirm: requirements: _permission: 'administer modules' -system.theme_disable: +system.theme_uninstall: path: '/admin/appearance/disable' defaults: - _controller: 'Drupal\system\Controller\ThemeController::disable' + _controller: 'Drupal\system\Controller\ThemeController::uninstall' requirements: _permission: 'administer themes' _csrf_token: 'TRUE' diff --git a/core/modules/system/templates/system-themes-page.html.twig b/core/modules/system/templates/system-themes-page.html.twig index e40545b..34f5656 100644 --- a/core/modules/system/templates/system-themes-page.html.twig +++ b/core/modules/system/templates/system-themes-page.html.twig @@ -8,7 +8,7 @@ * - theme_groups: A list of theme groups. Each theme group contains: * - attributes: HTML attributes specific to this theme group. * - title: Title for the theme group. - * - state: State of the theme group, e.g. enabled or disabled. + * - state: State of the theme group, e.g. enabled or uninstalled. * - themes: A list of themes within the theme group. Each theme contains: * - attributes: HTML attributes specific to this theme. * - screenshot: A screenshot representing the theme. diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php index 089334f..d6d5bf1 100644 --- a/core/modules/system/theme.api.php +++ b/core/modules/system/theme.api.php @@ -482,14 +482,16 @@ function hook_themes_enabled($theme_list) { } /** - * Respond to themes being disabled. + * Respond to themes being uninstalled. * * @param array $theme_list - * Array containing the names of the themes being disabled. + * Array containing the names of the themes being uninstalled. * - * @see theme_disable() + * @see @todo */ -function hook_themes_disabled($theme_list) { - // Clear all update module caches. - update_storage_clear(); +function hook_themes_uninstalled(array $themes) { + // Remove some state entries depending on the theme. + foreach ($themes as $theme) { + \Drupal::state()->delete('example.' . $theme); + } } diff --git a/core/modules/update/src/Tests/UpdateContribTest.php b/core/modules/update/src/Tests/UpdateContribTest.php index 0e547ef..a93dd43 100644 --- a/core/modules/update/src/Tests/UpdateContribTest.php +++ b/core/modules/update/src/Tests/UpdateContribTest.php @@ -197,6 +197,8 @@ function testUpdateBaseThemeSecurityUpdate() { /** * Tests that disabled themes are only shown when desired. + * + * @todo fix */ function testUpdateShowDisabledThemes() { $update_settings = \Drupal::config('update.settings'); diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 6529a48..b1c08c4 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -241,11 +241,11 @@ function update_themes_enabled($themes) { } /** - * Implements hook_themes_disabled(). + * Implements hook_themes_uninstalled(). * - * If themes are disabled, we invalidate the information of available updates. + * If themes are uninstalled, we invalidate the information of available updates. */ -function update_themes_disabled($themes) { +function update_themes_uninstalled($themes) { // Clear all update module data. update_storage_clear(); } diff --git a/core/modules/user/css/user.module.css b/core/modules/user/css/user.module.css index 658988d..8a03bdb 100644 --- a/core/modules/user/css/user.module.css +++ b/core/modules/user/css/user.module.css @@ -26,7 +26,8 @@ } .password-strength__meter { background-color: #ebeae4; - height: 0.5em; + height: 0.75em; + margin-top: 0.5em; } .password-strength__indicator { height: 100%; diff --git a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php index 56bab8f..3d89abe 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php @@ -64,6 +64,13 @@ class ThemeHandlerTest extends UnitTestCase { protected $configInstaller; /** + * The mocked config manager. + * + * @var \Drupal\Core\Config\ConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $configManager; + + /** * The extension discovery. * * @var \Drupal\Core\Extension\ExtensionDiscovery|\PHPUnit_Framework_MockObject_MockObject @@ -101,6 +108,7 @@ protected function setUp() { $this->state = new State(new KeyValueMemoryFactory()); $this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface'); $this->configInstaller = $this->getMock('Drupal\Core\Config\ConfigInstallerInterface'); + $this->configManager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface'); $this->routeBuilder = $this->getMockBuilder('Drupal\Core\Routing\RouteBuilder') ->disableOriginalConstructor() ->getMock(); @@ -111,7 +119,7 @@ protected function setUp() { ->disableOriginalConstructor() ->getMock(); $logger = $this->getMock('Psr\Log\LoggerInterface'); - $this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->state, $this->infoParser, $logger, $this->cssCollectionOptimizer, $this->configInstaller, $this->routeBuilder, $this->extensionDiscovery); + $this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->state, $this->infoParser, $logger, $this->cssCollectionOptimizer, $this->configInstaller, $this->configManager, $this->routeBuilder, $this->extensionDiscovery); $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); $this->getContainerWithCacheBins($cache_backend);