Commit 2a0254f9 authored by catch's avatar catch

Issue #2232605 by alexpott, dawehner, martin107, Cottser, sun: Fixed Themes cannot be uninstalled.

parent f27fd1f5
module: {}
theme: {}
disabled:
theme: {}
......@@ -10,17 +10,7 @@ core.extension:
label: 'Weight'
theme:
type: sequence
label: 'Enabled themes'
label: 'Installed themes'
sequence:
- type: integer
label: 'Weight'
disabled:
type: mapping
label: 'Disabled extensions'
mapping:
theme:
type: sequence
label: 'Disabled themes'
sequence:
- type: integer
label: 'Weight'
......@@ -283,7 +283,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', '@entity.definitions.installed']
......
......@@ -1512,7 +1512,7 @@ function install_profile_modules(&$install_state) {
* Installs themes.
*
* This does not use a batch, since installing themes is faster than modules and
* because an installation profile typically enables 1-3 themes only (default
* because an installation profile typically installs 1-3 themes only (default
* theme, base theme, admin theme).
*
* @param $install_state
......@@ -1521,15 +1521,15 @@ function install_profile_modules(&$install_state) {
function install_profile_themes(&$install_state) {
$theme_handler = \Drupal::service('theme_handler');
// ThemeHandler::enable() resets the current list of themes. The theme used in
// the installer is not necessarily in the list of themes to install, so
// ThemeHandler::install() resets the current list of themes. The theme used
// in the installer is not necessarily in the list of themes to install, so
// retain the current list.
// @see _drupal_maintenance_theme()
$current_themes = $theme_handler->listInfo();
// Install the themes specified by the installation profile.
$themes = $install_state['profile_info']['themes'];
$theme_handler->enable($themes);
$theme_handler->install($themes);
foreach ($current_themes as $theme) {
$theme_handler->addTheme($theme);
......
......@@ -9,11 +9,11 @@
use Drupal\Core\Extension\ExtensionDiscovery;
/**
* Builds a list of enabled themes.
* Builds a list of installed themes.
*
* @param $type
* The type of list to return:
* - theme: All enabled themes.
* - theme: All installed themes.
*
* @return
* An associative array of themes, keyed by name.
......
......@@ -77,7 +77,7 @@
* Either the name of a theme or a full theme object.
*
* @return bool
* Boolean TRUE if the theme is enabled or is the site administration theme;
* Boolean TRUE if the theme is installed or is the site administration theme;
* FALSE otherwise.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
......@@ -765,42 +765,6 @@ function theme_settings_convert_to_config(array $theme_settings, Config $config)
return $config;
}
/**
* Enables a given list of themes.
*
* @param $theme_list
* An array of theme names.
*
* @return bool
* Whether any of the given themes have been enabled.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::service('theme_handler')->enable().
*
* @see \Drupal\Core\Extension\ThemeHandler::enable().
*/
function theme_enable($theme_list) {
return \Drupal::service('theme_handler')->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
* @{
......
......@@ -23,9 +23,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;
}
}
......
......@@ -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 install and uninstalls.
*
* @var bool
*/
......@@ -252,8 +252,8 @@ protected function getEmptyExtensionsProcessedList() {
'uninstall' => array(),
),
'theme' => array(
'enable' => array(),
'disable' => array(),
'install' => 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.
$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']));
// Work out what themes to install and to uninstall.
$theme_install = array_diff(array_keys($new_extensions['theme']), array_keys($current_extensions['theme']));
$theme_uninstall = array_diff(array_keys($current_extensions['theme']), array_keys($new_extensions['theme']));
$this->extensionChangelist = array(
'module' => array(
......@@ -405,8 +405,8 @@ protected function createExtensionChangelist() {
'install' => $install,
),
'theme' => array(
'enable' => $enable,
'disable' => $disable,
'install' => $theme_install,
'uninstall' => $theme_uninstall,
),
);
}
......@@ -441,20 +441,10 @@ protected function getExtensionChangelist($type, $op = NULL) {
*/
protected function getUnprocessedExtensions($type) {
$changelist = $this->getExtensionChangelist($type);
if ($type == 'theme') {
$unprocessed = array(
'enable' => array_diff($changelist['enable'], $this->processedExtensions[$type]['enable']),
'disable' => array_diff($changelist['disable'], $this->processedExtensions[$type]['disable']),
);
}
else {
$unprocessed = array(
'install' => array_diff($changelist['install'], $this->processedExtensions[$type]['install']),
'uninstall' => array_diff($changelist['uninstall'], $this->processedExtensions[$type]['uninstall']),
);
}
return $unprocessed;
return array(
'install' => array_diff($changelist['install'], $this->processedExtensions[$type]['install']),
'uninstall' => array_diff($changelist['uninstall'], $this->processedExtensions[$type]['uninstall']),
);
}
/**
......@@ -533,7 +523,7 @@ public function initialize() {
$this->totalExtensionsToProcess += count($modules[$op]);
}
$themes = $this->getUnprocessedExtensions('theme');
foreach (array('enable', 'disable') as $op) {
foreach (array('install', 'uninstall') as $op) {
$this->totalExtensionsToProcess += count($themes[$op]);
}
......@@ -576,7 +566,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']['install']);
$context['finished'] = $processed_count / $this->totalExtensionsToProcess;
}
else {
......@@ -650,24 +640,16 @@ protected function finish(array &$context) {
* on. If there is nothing left to do returns FALSE;
*/
protected function getNextExtensionOperation() {
foreach (array('install', 'uninstall') as $op) {
$modules = $this->getUnprocessedExtensions('module');
if (!empty($modules[$op])) {
return array(
'op' => $op,
'type' => 'module',
'name' => array_shift($modules[$op]),
);
}
}
foreach (array('enable', 'disable') as $op) {
$themes = $this->getUnprocessedExtensions('theme');
if (!empty($themes[$op])) {
return array(
'op' => $op,
'type' => 'theme',
'name' => array_shift($themes[$op]),
);
foreach (array('module', 'theme') as $type) {
foreach (array('install', 'uninstall') as $op) {
$unprocessed = $this->getUnprocessedExtensions($type);
if (!empty($unprocessed[$op])) {
return array(
'op' => $op,
'type' => $type,
'name' => array_shift($unprocessed[$op]),
);
}
}
}
return FALSE;
......@@ -794,11 +776,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;
......
......@@ -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 to store installation statuses.
*/
class ThemeHandler implements ThemeHandlerInterface {
......@@ -46,14 +47,14 @@ class ThemeHandler implements ThemeHandlerInterface {
protected $list;
/**
* The config factory to get the enabled themes.
* The config factory to get the installed themes.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The module handler to fire themes_enabled/themes_disabled hooks.
* The module handler to fire themes_installed/themes_uninstalled hooks.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
......@@ -88,7 +89,7 @@ class ThemeHandler implements ThemeHandlerInterface {
protected $logger;
/**
* The route builder to rebuild the routes if a theme is enabled.
* The route builder to rebuild the routes if a theme is installed.
*
* @var \Drupal\Core\Routing\RouteBuilder
*/
......@@ -108,31 +109,41 @@ 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.
* The config factory to get the installed themes.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to fire themes_enabled/themes_disabled hooks.
* The module handler to fire themes_installed/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.
* (optional) The route builder to rebuild the routes if a theme is
* installed.
* @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 +151,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;
}
......@@ -157,7 +169,7 @@ public function getDefault() {
public function setDefault($name) {
$list = $this->listInfo();
if (!isset($list[$name])) {
throw new \InvalidArgumentException("$name theme is not enabled.");
throw new \InvalidArgumentException("$name theme is not installed.");
}
$this->configFactory->get('system.theme')
->set('default', $name)
......@@ -168,12 +180,12 @@ public function setDefault($name) {
/**
* {@inheritdoc}
*/
public function enable(array $theme_list, $enable_dependencies = TRUE) {
public function install(array $theme_list, $install_dependencies = TRUE) {
$extension_config = $this->configFactory->get('core.extension');
$theme_data = $this->rebuildThemeData();
if ($enable_dependencies) {
if ($install_dependencies) {
$theme_list = array_combine($theme_list, $theme_list);
if ($missing = array_diff_key($theme_list, $theme_data)) {
......@@ -183,13 +195,12 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) {
)));
}
// Only process themes that are not enabled currently.
// Only process themes that are not installed currently.
$installed_themes = $extension_config->get('theme') ?: array();
if (!$theme_list = array_diff_key($theme_list, $installed_themes)) {
// Nothing to do. All themes already enabled.
// Nothing to do. All themes already installed.
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,14 +229,13 @@ 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();
$themes_installed = array();
foreach ($theme_list as $key) {
// Only process themes that are not already enabled.
$enabled = $extension_config->get("theme.$key") !== NULL;
if ($enabled) {
// Only process themes that are not already installed.
$installed = $extension_config->get("theme.$key") !== NULL;
if ($installed) {
continue;
}
......@@ -240,7 +250,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.
......@@ -263,11 +272,12 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) {
// Only install default configuration if this theme has not been installed
// already.
if (!isset($installed_themes[$key])) {
// The default config installation storage only knows about the currently
// enabled list of themes, so it has to be reset in order to pick up the
// default config of the newly installed theme. However, do not reset the
// source storage when synchronizing configuration, since that would
// needlessly trigger a reload of the whole configuration to be imported.
// The default config installation storage only knows about the
// currently installed list of themes, so it has to be reset in order to
// pick up the default config of the newly installed theme. However, do
// not reset the source storage when synchronizing configuration, since
// that would needlessly trigger a reload of the whole configuration to
// be imported.
if (!$this->configInstaller->isSyncing()) {
$this->configInstaller->resetSourceStorage();
}
......@@ -276,58 +286,57 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) {
$this->configInstaller->installDefaultConfig('theme', $key);
}
$themes_enabled[] = $key;
$themes_installed[] = $key;
// Record the fact that it was enabled.
$this->logger->info('%theme theme enabled.', array('%theme' => $key));
// Record the fact that it was installed.
$this->logger->info('%theme theme installed.', array('%theme' => $key));
}
$this->cssCollectionOptimizer->deleteAll();
$this->resetSystem();
// Invoke hook_themes_enabled() after the themes have been enabled.
$this->moduleHandler->invokeAll('themes_enabled', array($themes_enabled));
// Invoke hook_themes_installed() after the themes have been installed.
$this->moduleHandler->invokeAll('themes_installed', array($themes_installed));
return !empty($themes_enabled);
return !empty($themes_installed);
}
/**
* {@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 installed, and if
// they are not uninstalled at the same time.
// @todo https://www.drupal.org/node/474684 and
// https://www.drupal.org/node/1297856 themes should leverage the module
// dependency system.
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 +350,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]);
}
/**
......@@ -400,13 +412,13 @@ public function addTheme(Extension $theme) {
public function refreshInfo() {
$this->reset();
$extension_config = $this->configFactory->get('core.extension');
$enabled = $extension_config->get('theme');
$installed = $extension_config->get('theme');
// @todo Avoid re-scanning all themes by retaining the original (unaltered)
// theme info somewhere.
$list = $this->rebuildThemeData();
foreach ($list as $name => $theme) {
if (isset($enabled[$name])) {
if (isset($installed[$name])) {
$this->addTheme($theme);
}
}
......@@ -429,7 +441,7 @@ public function rebuildThemeData() {
$themes = $listing->scan('theme');
$engines = $listing->scan('theme_engine');
$extension_config = $this->configFactory->get('core.extension');
$enabled = $extension_config->get('theme') ?: array();
$installed = $extension_config->get('theme') ?: array();
// Set defaults for theme info.
$defaults = array(
......@@ -458,7 +470,7 @@ public function rebuildThemeData() {
// Read info files for each theme.
foreach ($themes as $key => $theme) {
// @todo Remove all code that relies on the $status property.
$theme->status = (int) isset($enabled[$key]);
$theme->status = (int) isset($installed[$key]);
$theme->info = $this->infoParser->parse($theme->getPathname()) + $defaults;
......
......@@ -8,49 +8,54 @@
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 install/uninstall them.
*/
interface ThemeHandlerInterface {
/**
* Enables a given list of themes.
* Installs a given list of themes.
*
* @param array $theme_list
* An array of theme names.
* @param bool $enable_dependencies
* @param bool $install_dependencies
* (optional) If TRUE, dependencies will automatically be installed in the
* correct order. This incurs a significant performance cost, so use FALSE
* if you know $theme_list is already complete and in the correct order.
*
* @return bool
* Whether any of the given themes have been enabled.
* Whether any of the given themes have been installed.
*
* @throws \Drupal\Core\Extension\ExtensionNameLengthException
* Thrown when the theme name is to long
*/
public function enable(array $theme_list, $enable_dependencies = TRUE);
public function install(array $theme_list, $install_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.
* Returns a list of currently installed themes.
*
* @return \Drupal\Core\Extension\Extension[]
* An associative array of the currently enabled themes. The keys are the
* An associative array of the currently installed themes. The keys are the
* themes' machine names and the values are objects having the following
* properties:
* - filename: The filepath and name of the .info.yml file.
* - name: The machine name of the theme.
* - status: 1 for enabled, 0 for disabled themes.
* - status: 1 for installed, 0 for uninstalled themes.
* - info: The contents of the .info.yml file.
* - stylesheets: A two dimensional array, using the first key for the
* media attribute (e.g. 'all'), the second for the name of the file
......@@ -83,7 +88,7 @@ public function disable(array $theme_list);
public function listInfo();
/**
* Refreshes the theme info data of currently enabled themes.
* Refreshes the theme info data of currently installed themes.
*
* Modules can alter theme info, so this is typically called after a module
* has been installed or uninstalled.
......@@ -140,7 +145,17 @@ public function getName($theme);
public function getDefault();
/**
* Returns an array of directories for all enabled themes.
* 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 installed themes.
*
* Useful for tasks such as finding a file that exists in all theme
* directories.
......@@ -150,13 +165,13 @@ public function getDefault();
public function getThemeDirectories();
/**
* Determines whether a given theme is enabled.
* Determines whether a given theme is installed.
*
* @param string $theme
* The name of the theme (without the .theme extension).
*
* @return bool
* TRUE if the theme is both installed and enabled.
* TRUE if the theme is installed.
*/
public function themeExists($theme);
......