diff --git a/core/core.services.yml b/core/core.services.yml index 0b8be53..8271f84 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -189,7 +189,7 @@ services: arguments: ['%container.modules%', '@state', '@cache.bootstrap'] theme_handler: class: Drupal\Core\Extension\ThemeHandler - arguments: ['@config.factory', '@module_handler', '@cache.default', '@info_parser', '@config.installer', '@router.builder'] + arguments: ['@config.factory', '@module_handler', '@state', '@info_parser', '@config.installer', '@router.builder'] entity.manager: class: Drupal\Core\Entity\EntityManager arguments: ['@container.namespaces', '@service_container', '@module_handler', '@cache.default', '@language_manager', '@string_translation'] diff --git a/core/includes/module.inc b/core/includes/module.inc index c219eaf..cbbe811 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -32,13 +32,9 @@ function system_list($type) { 'theme' => array(), 'filepaths' => array(), ); - $enabled_themes = \Drupal::config('core.extension')->get('theme') ?: array(); - // system_rebuild_theme_data() allows modules to alter the info of all - // themes, so we are not able to simply consume the system.theme.data state - // record here. - $theme_data = system_rebuild_theme_data(); + // ThemeHandler maintains the 'system.theme.data' state record. + $theme_data = \Drupal::state()->get('system.theme.data', array()); foreach ($theme_data as $name => $theme) { - $theme->status = (int) isset($enabled_themes[$name]); $lists['theme'][$name] = $theme; $lists['filepaths'][] = array( 'type' => 'theme', diff --git a/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php b/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php index aa3c709..aaceae9 100644 --- a/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php +++ b/core/lib/Drupal/Core/DependencyInjection/UpdateServiceProvider.php @@ -43,7 +43,7 @@ public function register(ContainerBuilder $container) { $container->register('theme_handler', 'Drupal\Core\Extension\ThemeHandler') ->addArgument(new Reference('config.factory')) ->addArgument(new Reference('module_handler')) - ->addArgument(new Reference('cache.default')) + ->addArgument(new Reference('state')) ->addArgument(new Reference('info_parser')); } } diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 17b4239..5831024 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -650,8 +650,6 @@ public function install(array $module_list, $enable_dependencies = TRUE) { // Refresh the schema to include it. drupal_get_schema(NULL, TRUE); - // Update the theme registry to include it. - drupal_theme_rebuild(); // Allow modules to react prior to the installation of a module. $this->invokeAll('module_preinstall', array($module)); @@ -684,8 +682,18 @@ public function install(array $module_list, $enable_dependencies = TRUE) { // Record the fact that it was installed. $modules_installed[] = $module; + // Update the theme registry to include it. + drupal_theme_rebuild(); + + // Modules can alter theme info, so refresh theme data. + // @todo ThemeHandler cannot be injected into ModuleHandler, since that + // causes a circular service dependency. + // @see https://drupal.org/node/2208429 + \Drupal::service('theme_handler')->refreshInfo(); + // Allow the module to perform install tasks. $this->invoke($module, 'install'); + // Record the fact that it was installed. watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO); } @@ -792,6 +800,12 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { // Update the theme registry to remove the newly uninstalled module. drupal_theme_rebuild(); + // Modules can alter theme info, so refresh theme data. + // @todo ThemeHandler cannot be injected into ModuleHandler, since that + // causes a circular service dependency. + // @see https://drupal.org/node/2208429 + \Drupal::service('theme_handler')->refreshInfo(); + watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO); $schema_store->delete($module); diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php index 07645a6..593a0b7 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -9,9 +9,9 @@ use Drupal\Component\Utility\String; use Drupal\Core\Cache\Cache; -use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigInstallerInterface; +use Drupal\Core\KeyValueStore\StateInterface; use Drupal\Core\Routing\RouteBuilder; /** @@ -58,11 +58,11 @@ class ThemeHandler implements ThemeHandlerInterface { protected $moduleHandler; /** - * The cache backend to clear the local tasks cache. + * The state backend. * - * @var \Drupal\Core\Cache\CacheBackendInterface + * @var \Drupal\Core\KeyValueStore\StateInterface */ - protected $cacheBackend; + protected $state; /** * The config installer to install configuration. @@ -99,7 +99,7 @@ class ThemeHandler implements ThemeHandlerInterface { * 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. - * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * @param \Drupal\Core\KeyValueStore\StateInterface $state * The cache backend to clear the local tasks cache. * @param \Drupal\Core\Extension\InfoParserInterface $info_parser * The info parser to parse the theme.info.yml files. @@ -112,10 +112,10 @@ class ThemeHandler implements ThemeHandlerInterface { * @param \Drupal\Core\Extension\ExtensionDiscovery $extension_discovery * (optional) A extension discovery instance (for unit tests). */ - public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, InfoParserInterface $info_parser, 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, ConfigInstallerInterface $config_installer = NULL, RouteBuilder $route_builder = NULL, ExtensionDiscovery $extension_discovery = NULL) { $this->configFactory = $config_factory; $this->moduleHandler = $module_handler; - $this->cacheBackend = $cache_backend; + $this->state = $state; $this->infoParser = $info_parser; $this->configInstaller = $config_installer; $this->routeBuilder = $route_builder; @@ -151,7 +151,7 @@ public function setDefault($name) { public function enable(array $theme_list, $enable_dependencies = TRUE) { $extension_config = $this->configFactory->get('core.extension'); - $theme_data = system_rebuild_theme_data(); + $theme_data = $this->rebuildThemeData(); if ($enable_dependencies) { $theme_list = array_combine($theme_list, $theme_list); @@ -220,10 +220,11 @@ public function enable(array $theme_list, $enable_dependencies = TRUE) { ->save(); // Update the current theme data accordingly. - // @todo Inject State service. - $current_theme_data = \Drupal::state()->get('system.theme.data', array()); + // @todo Remove all code that relies on $status property. + $theme_data[$key]->status = 1; + $current_theme_data = $this->state->get('system.theme.data', array()); $current_theme_data[$key] = $theme_data[$key]; - \Drupal::state()->set('system.theme.data', $current_theme_data); + $this->state->set('system.theme.data', $current_theme_data); $theme_settings = &drupal_static('theme_get_setting'); unset($theme_settings[$key]); @@ -267,7 +268,7 @@ public function disable(array $theme_list) { $this->clearCssCache(); $extension_config = $this->configFactory->get('core.extension'); - $current_theme_data = \Drupal::state()->get('system.theme.data', array()); + $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 @@ -280,7 +281,7 @@ public function disable(array $theme_list) { unset($theme_settings[$key]); } $extension_config->save(); - \Drupal::state()->set('system.theme.data', $current_theme_data); + $this->state->set('system.theme.data', $current_theme_data); $this->reset(); $this->resetSystem(); @@ -328,6 +329,25 @@ public function addTheme(Extension $theme) { /** * {@inheritdoc} */ + public function refreshInfo() { + $this->reset(); + $extension_config = $this->configFactory->get('core.extension'); + $enabled = $extension_config->get('theme') ?: array(); + + // @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])) { + $this->list[$name] = $theme; + } + } + $this->state->set('system.theme.data', $this->list); + } + + /** + * {@inheritdoc} + */ public function reset() { $this->systemListReset(); $this->list = NULL; @@ -340,6 +360,8 @@ public function rebuildThemeData() { $listing = $this->getExtensionDiscovery(); $themes = $listing->scan('theme'); $engines = $listing->scan('theme_engine'); + $extension_config = $this->configFactory->get('core.extension'); + $enabled = $extension_config->get('theme') ?: array(); // Set defaults for theme info. $defaults = array( @@ -364,8 +386,12 @@ public function rebuildThemeData() { ); $sub_themes = array(); + $files = array(); // 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->info = $this->infoParser->parse($theme->getPathname()) + $defaults; // Add the info file modification time, so it becomes available for @@ -397,7 +423,17 @@ public function rebuildThemeData() { if (!empty($theme->info['screenshot'])) { $theme->info['screenshot'] = $path . '/' . $theme->info['screenshot']; } + + $files[$key] = $theme->getPathname(); } + // Build dependencies. + // @todo Move into a generic ExtensionHandler base class. + // @see https://drupal.org/node/2208429 + $themes = $this->moduleHandler->buildModuleDependencies($themes); + + // Store filenames to allow system_list() and drupal_get_filename() to + // retrieve them without having to scan the filesystem. + $this->state->set('system.theme.files', $files); // After establishing the full list of available themes, fill in data for // sub-themes. diff --git a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php index 92ab92f..fbf57fd 100644 --- a/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php @@ -77,6 +77,14 @@ public function disable(array $theme_list); public function listInfo(); /** + * Refreshes the theme info data of currently enabled themes. + * + * Modules can alter theme info, so this is typically called after a module + * has been installed or uninstalled. + */ + public function refreshInfo(); + + /** * Resets the internal state of the theme handler. */ public function reset(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Extension/ThemeHandlerTest.php b/core/modules/system/lib/Drupal/system/Tests/Extension/ThemeHandlerTest.php new file mode 100644 index 0000000..0268f7d --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Extension/ThemeHandlerTest.php @@ -0,0 +1,298 @@ + 'Theme handler', + 'description' => 'Tests installing/enabling, disabling, and uninstalling of themes.', + 'group' => 'Extension', + ); + } + + public function containerBuild(ContainerBuilder $container) { + parent::containerBuild($container); + // Some test methods involve ModuleHandler operations, which attempt to + // rebuild and dump routes. + $container + ->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper'); + } + + function setUp() { + parent::setUp(); + $this->installConfig(array('system')); + } + + /** + * Verifies that no themes are installed/enabled/disabled 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'))); + + // Rebuilding available themes should always yield results though. + $this->assertTrue($this->themeHandler()->rebuildThemeData()['stark'], 'ThemeHandler::rebuildThemeData() yields all available themes.'); + $this->assertTrue(system_rebuild_theme_data()['stark'], 'system_rebuild_theme_data() yields all available themes.'); + + // theme_get_setting() should return global default theme settings. + $this->assertIdentical(theme_get_setting('features.favicon'), TRUE); + } + + /** + * Tests enabling a theme. + */ + function testEnable() { + $name = 'test_basetheme'; + + $themes = $this->themeHandler()->listInfo(); + $this->assertFalse(isset($themes[$name])); + + $this->themeHandler()->enable(array($name)); + + $this->assertIdentical($this->extensionConfig()->get("theme.$name"), 0); + $this->assertFalse($this->extensionConfig()->get("disabled.theme.$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)); + + // Verify that test_basetheme.settings is active. + $this->assertIdentical(theme_get_setting('features.favicon', $name), FALSE); + $this->assertEqual(theme_get_setting('base', $name), 'only'); + $this->assertEqual(theme_get_setting('override', $name), 'base'); + } + + /** + * Tests enabling a sub-theme. + */ + function testEnableSubTheme() { + $name = 'test_subtheme'; + $base_name = 'test_basetheme'; + + $themes = $this->themeHandler()->listInfo(); + $this->assertFalse(array_keys($themes)); + + $this->themeHandler()->enable(array($name)); + + $themes = $this->themeHandler()->listInfo(); + $this->assertTrue(isset($themes[$name])); + $this->assertTrue(isset($themes[$base_name])); + + $this->themeHandler()->disable(array($name)); + + $themes = $this->themeHandler()->listInfo(); + $this->assertFalse(isset($themes[$name])); + $this->assertTrue(isset($themes[$base_name])); + } + + /** + * Tests disabling a 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. + */ + function testDisableEnable() { + $name = 'test_basetheme'; + $this->testDisable(); + $this->config("$name.settings")->set('keep', 'me')->save(); + + $this->testEnable(); + $this->assertIdentical($this->config("$name.settings")->get('keep'), 'me'); + } + + /** + * Tests disabling the default theme. + */ + function testDisableDefault() { + $name = 'stark'; + $other_name = 'bartik'; + $this->themeHandler()->enable(array($name, $other_name)); + $this->themeHandler()->setDefault($name); + + $themes = $this->themeHandler()->listInfo(); + $this->assertTrue(isset($themes[$name])); + $this->assertTrue(isset($themes[$other_name])); + + try { + $message = 'ThemeHandler::disable() throws InvalidArgumentException upon disabling default theme.'; + $this->themeHandler()->disable(array($name)); + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + + $themes = $this->themeHandler()->listInfo(); + $this->assertTrue(isset($themes[$name])); + $this->assertTrue(isset($themes[$other_name])); + } + + /** + * Tests disabling the admin theme. + */ + function testDisableAdmin() { + $name = 'stark'; + $other_name = 'bartik'; + $this->themeHandler()->enable(array($name, $other_name)); + $this->config('system.theme')->set('admin', $name)->save(); + + $themes = $this->themeHandler()->listInfo(); + $this->assertTrue(isset($themes[$name])); + $this->assertTrue(isset($themes[$other_name])); + + try { + $message = 'ThemeHandler::disable() throws InvalidArgumentException upon disabling admin theme.'; + $this->themeHandler()->disable(array($name)); + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + + $themes = $this->themeHandler()->listInfo(); + $this->assertTrue(isset($themes[$name])); + $this->assertTrue(isset($themes[$other_name])); + } + + /** + * Tests disabling a sub-theme. + */ + function testDisableSubTheme() { + $name = 'test_subtheme'; + $base_name = 'test_basetheme'; + + $this->themeHandler()->enable(array($name)); + $this->themeHandler()->disable(array($name)); + + $themes = $this->themeHandler()->listInfo(); + $this->assertFalse(isset($themes[$name])); + $this->assertTrue(isset($themes[$base_name])); + } + + /** + * Tests that theme info can be altered by a module. + * + * @see module_test_system_info_alter() + */ + function testThemeInfoAlter() { + $name = 'seven'; + $this->container->get('state')->set('module_test.hook_system_info_alter', TRUE); + $module_handler = $this->container->get('module_handler'); + + $this->themeHandler()->enable(array($name)); + + $themes = $this->themeHandler()->listInfo(); + $this->assertFalse(isset($themes[$name]->info['regions']['test_region'])); + + $module_handler->install(array('module_test'), FALSE); + $this->assertTrue($module_handler->moduleExists('module_test')); + + $themes = $this->themeHandler()->listInfo(); + $this->assertTrue(isset($themes[$name]->info['regions']['test_region'])); + + // Legacy assertions. + // @todo Remove once theme initialization/info has been modernized. + // @see https://drupal.org/node/2228093 + $info = system_get_info('theme', $name); + $this->assertTrue(isset($info['regions']['test_region'])); + $regions = system_region_list($name); + $this->assertTrue(isset($regions['test_region'])); + $system_list = system_list('theme'); + $this->assertTrue(isset($system_list[$name]->info['regions']['test_region'])); + + $module_handler->uninstall(array('module_test')); + $this->assertFalse($module_handler->moduleExists('module_test')); + + $themes = $this->themeHandler()->listInfo(); + $this->assertFalse(isset($themes[$name]->info['regions']['test_region'])); + + // Legacy assertions. + // @todo Remove once theme initialization/info has been modernized. + // @see https://drupal.org/node/2228093 + $info = system_get_info('theme', $name); + $this->assertFalse(isset($info['regions']['test_region'])); + $regions = system_region_list($name); + $this->assertFalse(isset($regions['test_region'])); + $system_list = system_list('theme'); + $this->assertFalse(isset($system_list[$name]->info['regions']['test_region'])); + } + + /** + * Returns the theme handler service. + * + * @return \Drupal\Core\Extension\ThemeHandlerInterface + */ + protected function themeHandler() { + return $this->container->get('theme_handler'); + } + + /** + * Returns the system.theme config object. + * + * @return \Drupal\Core\Config\Config + */ + protected function extensionConfig() { + return $this->config('core.extension'); + } + + /** + * Returns a given config object. + * + * @return \Drupal\Core\Config\Config + */ + protected function config($name) { + return $this->container->get('config.factory')->get($name); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Extension/ThemeInstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Extension/ThemeInstallTest.php deleted file mode 100644 index 4bb5418..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Extension/ThemeInstallTest.php +++ /dev/null @@ -1,145 +0,0 @@ - 'Theme installation', - 'description' => 'Tests installing/enabling, disabling, and uninstalling of themes.', - 'group' => 'Extension', - ); - } - - function setUp() { - parent::setUp(); - $this->installConfig(array('system')); - } - - /** - * Verifies that no themes are installed/enabled/disabled by default. - */ - function testEmpty() { - $this->assertFalse($this->extensionConfig()->get('theme')); - $this->assertFalse($this->extensionConfig()->get('disabled.theme')); - - $this->assertFalse($this->themeHandler()->listInfo()); - $this->assertFalse(system_list('theme')); - - // Rebuilding available themes should always yield results though. - $this->assertTrue($this->themeHandler()->rebuildThemeData()['stark'], 'ThemeHandler::rebuildThemeData() yields all available themes.'); - $this->assertTrue(system_rebuild_theme_data()['stark'], 'system_rebuild_theme_data() yields all available themes.'); - - // theme_get_setting() should return global default theme settings. - $this->assertIdentical(theme_get_setting('features.favicon'), TRUE); - } - - /** - * Tests enabling a theme. - */ - function testEnable() { - $name = 'test_basetheme'; - $this->themeHandler()->enable(array($name)); - - $this->assertIdentical($this->extensionConfig()->get("theme.$name"), 0); - $this->assertFalse($this->extensionConfig()->get("disabled.theme.$name")); - - $list = $this->themeHandler()->listInfo(); - $this->assertTrue(isset($list[$name])); - $this->assertEqual($list[$name]->getName(), $name); - - $this->assertEqual(array_keys(system_list('theme')), array_keys($list)); - - // Verify that test_basetheme.settings is active. - $this->assertIdentical(theme_get_setting('features.favicon', $name), FALSE); - $this->assertEqual(theme_get_setting('base', $name), 'only'); - $this->assertEqual(theme_get_setting('override', $name), 'base'); - } - - /** - * Tests disabling a 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($this->themeHandler()->listInfo()); - $this->assertFalse(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. - */ - function testDisableEnable() { - $name = 'test_basetheme'; - $this->testDisable(); - $this->config("$name.settings")->set('keep', 'me')->save(); - - $this->testEnable(); - $this->assertIdentical($this->config("$name.settings")->get('keep'), 'me'); - } - - /** - * Returns the theme handler service. - * - * @return \Drupal\Core\Extension\ThemeHandlerInterface - */ - protected function themeHandler() { - return $this->container->get('theme_handler'); - } - - /** - * Returns the system.theme config object. - * - * @return \Drupal\Core\Config\Config - */ - protected function extensionConfig() { - return $this->config('core.extension'); - } - - /** - * Returns a given config object. - * - * @return \Drupal\Core\Config\Config - */ - protected function config($name) { - return $this->container->get('config.factory')->get($name); - } - -} diff --git a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php index b57d44e..31387d0 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php @@ -36,25 +36,10 @@ function testSystemInfoAlter() { $info = system_rebuild_module_data(); $this->assertFalse(isset($info['node']->info['required']), 'Before the module_test is installed the node module is not required.'); - // Enable seven and the test module. - theme_enable(array('seven')); + // Enable the test module. \Drupal::moduleHandler()->install(array('module_test'), FALSE); $this->assertTrue(\Drupal::moduleHandler()->moduleExists('module_test'), 'Test module is enabled.'); - // Verify that the rebuilt and altered theme info is returned. - $info = system_get_info('theme', 'seven'); - $this->assertTrue(isset($info['regions']['test_region']), 'Altered theme info was returned by system_get_info().'); - - $seven_regions = system_region_list('seven'); - $this->assertTrue(isset($seven_regions['test_region']), 'Altered theme info was returned by system_region_list().'); - - $system_list_themes = system_list('theme'); - $info = $system_list_themes['seven']->info; - $this->assertTrue(isset($info['regions']['test_region']), 'Altered theme info was returned by system_list().'); - - $list_themes = list_themes(); - $this->assertTrue(isset($list_themes['seven']->info['regions']['test_region']), 'Altered theme info was returned by list_themes().'); - $info = system_rebuild_module_data(); $this->assertTrue($info['node']->info['required'], 'After the module_test is installed the node module is required.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php index 1fab23b..9d34007 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php @@ -280,39 +280,4 @@ function testPreprocessHtml() { $this->assertText('theme test page bottom markup', 'Modules are able to set the page bottom region.'); } - /** - * Test that themes can be disabled programmatically but admin theme and default theme can not. - */ - function testDisableTheme() { - // Enable Bartik, Seven and Stark. - \Drupal::service('theme_handler')->enable(array('bartik', 'seven', 'stark')); - - // Set Bartik as the default theme and Seven as the admin theme. - \Drupal::config('system.theme') - ->set('default', 'bartik') - ->set('admin', 'seven') - ->save(); - - $theme_list = \Drupal::service('theme_handler')->listInfo(); - $this->assertTrue(isset($theme_list['bartik']), 'Default theme is enabled.'); - $this->assertTrue(isset($theme_list['seven']), 'Admin theme is enabled.'); - $this->assertTrue(isset($theme_list['stark']), 'Stark is enabled.'); - - // Attempt to disable all themes. theme_disable() ensures that the default - // theme and the admin theme cannot be disabled. - try { - \Drupal::service('theme_handler')->disable(array('bartik', 'seven', 'stark')); - $this->fail('ThemeHandler::disable() throws InvalidArgumentException when trying to disable the default or admin theme.'); - } - catch (\InvalidArgumentException $e) { - $this->pass('ThemeHandler::disable() throws InvalidArgumentException when trying to disable the default or admin theme.'); - } - - $theme_list = \Drupal::service('theme_handler')->listInfo(); - - // Assert that all themes are still enabled. - $this->assertTrue(isset($theme_list['bartik']), 'Default theme is enabled.'); - $this->assertTrue(isset($theme_list['seven']), 'Admin theme is enabled.'); - $this->assertTrue(isset($theme_list['stark']), 'Stark is enabled.'); - } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigSettingsTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigSettingsTest.php index 5689b3d..c920f2a 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigSettingsTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigSettingsTest.php @@ -77,24 +77,19 @@ function testTwigDebugOverride() { */ function testTwigCacheOverride() { $extension = twig_extension(); - theme_enable(array('test_theme')); - \Drupal::config('system.theme') - ->set('default', 'test_theme') - ->save(); - - // Unset the global variables, so \Drupal\Core\Theme\Registry::init() fires - // drupal_theme_initialize, which fills up the global variables properly - // and chosen the current active theme. - unset($GLOBALS['theme_info']); - unset($GLOBALS['theme']); - // Reset the theme registry, so that the new theme is used. - $this->container->set('theme.registry', NULL); + $theme_handler = \Drupal::service('theme_handler'); + $theme_handler->enable(array('test_theme')); + $theme_handler->setDefault('test_theme'); - // Load array of Twig templates. - $registry = $this->container->get('theme.registry'); - $registry->reset(); + // The registry still works on theme globals, so set them here. + $GLOBALS['theme'] = 'test_theme'; + $GLOBALS['theme_info'] = $theme_handler->listInfo()['test_theme']; - $templates = $registry->getRuntime(); + // Null out the container's theme registry to clear its properties. + $this->container->set('theme.registry', NULL); + // Load Twig templates in a new theme registry. reset() is necessary to + // invalidate the caches tagged with 'theme_registry'. + $templates = $this->container->get('theme.registry')->reset()->getRuntime(); // Get the template filename and the cache filename for // theme_test.template_test.html.twig. diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 5e2b12a..f47ce55 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1309,33 +1309,12 @@ function system_rebuild_module_data() { * * @return \Drupal\Core\Extension\Extension[] * Array of all available themes and their data. + * + * @deprecated 8.x + * Use \Drupal::service('theme_handler')->rebuildThemeData(). */ function system_rebuild_theme_data() { - $themes = \Drupal::service('theme_handler')->rebuildThemeData(); - ksort($themes); - - // Add human-readable name and status. - $enabled_themes = \Drupal::config('core.extension')->get('theme') ?: array(); - $files = array(); - foreach ($themes as $name => $theme) { - $files[$name] = $theme->getPathname(); - $theme->status = (int) isset($enabled_themes[$name]); - } - // Build dependencies. - if ($themes) { - $themes = \Drupal::moduleHandler()->buildModuleDependencies($themes); - } - - // Replace last known theme data state. - \Drupal::state()->set('system.theme.data', array_filter($themes, function (Extension $theme) { - return !empty($theme->status); - })); - - // Store filenames to allow system_list() and drupal_get_filename() to - // retrieve them without having to rebuild or scan the filesystem. - \Drupal::state()->set('system.theme.files', $files); - - return $themes; + return \Drupal::service('theme_handler')->rebuildThemeData(); } /** diff --git a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php index 04ea77e..a464ad2 100644 --- a/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php +++ b/core/tests/Drupal/Tests/Core/Extension/ThemeHandlerTest.php @@ -11,6 +11,8 @@ use Drupal\Core\Extension\InfoParser; use Drupal\Core\Extension\ThemeHandler; use Drupal\Core\Config\ConfigInstaller; +use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; +use Drupal\Core\KeyValueStore\State; use Drupal\Tests\UnitTestCase; /** @@ -38,11 +40,11 @@ class ThemeHandlerTest extends UnitTestCase { protected $infoParser; /** - * The mocked cache backend. + * The mocked state backend. * - * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\KeyValueStore\StateInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $cacheBackend; + protected $state; /** * The mocked config factory. @@ -104,7 +106,7 @@ protected function setUp() { ), )); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); - $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->state = new State(new KeyValueMemoryFactory()); $this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface'); $this->configInstaller = $this->getMock('Drupal\Core\Config\ConfigInstallerInterface'); $this->routeBuilder = $this->getMockBuilder('Drupal\Core\Routing\RouteBuilder') @@ -113,15 +115,16 @@ protected function setUp() { $this->extensionDiscovery = $this->getMockBuilder('Drupal\Core\Extension\ExtensionDiscovery') ->disableOriginalConstructor() ->getMock(); - $this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->cacheBackend, $this->infoParser, $this->configInstaller, $this->routeBuilder, $this->extensionDiscovery); + $this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->state, $this->infoParser, $this->configInstaller, $this->routeBuilder, $this->extensionDiscovery); - $this->getContainerWithCacheBins($this->cacheBackend); + $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $this->getContainerWithCacheBins($cache_backend); } /** * Tests enabling a theme with a name longer than 50 chars. * - * @requires function system_rebuild_theme_data + * @requires function system__too_much_mocking * * @expectedException \Drupal\Core\Extension\ExtensionNameLengthException * @expectedExceptionMessage Theme name thisNameIsFarTooLong0000000000000000000000000000051 is over the maximum allowed length of 50 characters. @@ -133,7 +136,7 @@ public function testThemeEnableWithTooLongName() { /** * Tests enabling a single theme. * - * @requires function system_rebuild_theme_data + * @requires function system__too_much_mocking * * @see \Drupal\Core\Extension\ThemeHandler::enable() */ @@ -181,7 +184,7 @@ public function testEnableSingleTheme() { /** * Ensures that enabling a theme does clear the theme info listing. * - * @requires function system_rebuild_theme_data + * @requires function system__too_much_mocking * * @see \Drupal\Core\Extension\ThemeHandler::listInfo() */