diff --git a/core/config/install/core.extension.yml b/core/config/install/core.extension.yml
index 1514a9e..eae39ef 100644
--- a/core/config/install/core.extension.yml
+++ b/core/config/install/core.extension.yml
@@ -1,4 +1,5 @@
module: {}
-theme: {}
+theme:
+ stark: 0
disabled:
theme: {}
diff --git a/core/core.services.yml b/core/core.services.yml
index a58f779..14beb67 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -145,7 +145,7 @@ services:
arguments: [default]
form_builder:
class: Drupal\Core\Form\FormBuilder
- arguments: ['@form_validator', '@form_submitter', '@module_handler', '@keyvalue.expirable', '@event_dispatcher', '@request_stack', '@class_resolver', '@?csrf_token', '@?http_kernel']
+ arguments: ['@form_validator', '@form_submitter', '@module_handler', '@keyvalue.expirable', '@event_dispatcher', '@request_stack', '@class_resolver', '@theme', '@?csrf_token', '@?http_kernel']
form_validator:
class: Drupal\Core\Form\FormValidator
arguments: ['@request_stack', '@string_translation', '@csrf_token']
@@ -223,10 +223,6 @@ services:
arguments: ['@access_check.theme']
tags:
- { name: service_collector, tag: theme_negotiator, call: addNegotiator }
- theme.negotiator.request_subscriber:
- class: Drupal\Core\EventSubscriber\ThemeNegotiatorRequestSubscriber
- tags:
- - { name: event_subscriber }
theme.negotiator.default:
class: Drupal\Core\Theme\DefaultNegotiator
arguments: ['@config.factory']
@@ -829,6 +825,12 @@ services:
class: Zend\Feed\Writer\Extension\Threading\Renderer\Entry
feed.writer.wellformedwebrendererentry:
class: Zend\Feed\Writer\Extension\WellFormedWeb\Renderer\Entry
+ theme:
+ class: Drupal\Core\Theme\ThemeManager
+ arguments: ['@theme_handler', '@theme.registry', '@theme.negotiator', '@theme.initialization', '@request_stack']
+ theme.initialization:
+ class: Drupal\Core\Theme\Initialization
+ arguments: ['@theme_handler', '@state']
theme.registry:
class: Drupal\Core\Theme\Registry
arguments: ['@cache.default', '@lock', '@module_handler']
diff --git a/core/includes/common.inc b/core/includes/common.inc
index df5677f..73e2c4c 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1187,8 +1187,21 @@ function _drupal_add_css($data = NULL, $options = NULL) {
*
* @see _drupal_add_css()
*/
-function drupal_get_css($css = NULL, $skip_alter = FALSE) {
- global $theme_info;
+function drupal_get_css($css = NULL, $skip_alter = FALSE, $theme_add_css = TRUE) {
+ $theme_info = \Drupal::theme()->getActiveTheme();
+
+ // @todo There are probably better place to add the CSS from themes.
+ if ($theme_add_css) {
+ foreach ($theme_info->getStyleSheets() as $media => $stylesheets) {
+ foreach ($stylesheets as $stylesheet) {
+ _drupal_add_css($stylesheet, array(
+ 'group' => CSS_AGGREGATE_THEME,
+ 'every_page' => TRUE,
+ 'media' => $media
+ ));
+ }
+ }
+ }
if (!isset($css)) {
$css = _drupal_add_css();
@@ -1197,24 +1210,25 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
// Allow modules and themes to alter the CSS items.
if (!$skip_alter) {
\Drupal::moduleHandler()->alter('css', $css);
+ \Drupal::theme()->alter('css', $css);
}
// Sort CSS items, so that they appear in the correct order.
uasort($css, 'drupal_sort_css_js');
// Allow themes to remove CSS files by basename.
- if (!empty($theme_info->stylesheets_remove)) {
+ if ($stylesheet_remove = $theme_info->getStyleSheetsRemove()) {
foreach ($css as $key => $options) {
- if (isset($options['basename']) && isset($theme_info->stylesheets_remove[$options['basename']])) {
+ if (isset($options['basename']) && isset($stylesheet_remove[$options['basename']])) {
unset($css[$key]);
}
}
}
// Allow themes to conditionally override CSS files by basename.
- if (!empty($theme_info->stylesheets_override)) {
+ if ($stylesheet_override = $theme_info->getStyleSheetsOverride()) {
foreach ($css as $key => $options) {
- if (isset($options['basename']) && isset($theme_info->stylesheets_override[$options['basename']])) {
- $css[$key]['data'] = $theme_info->stylesheets_override[$options['basename']];
+ if (isset($options['basename']) && isset($stylesheet_override[$options['basename']])) {
+ $css[$key]['data'] = $stylesheet_override[$options['basename']];
}
}
}
@@ -1847,7 +1861,15 @@ function drupal_js_defaults($data = NULL) {
* @see locale_js_alter()
* @see drupal_js_defaults()
*/
-function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALSE, $is_ajax = FALSE) {
+function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALSE, $is_ajax = FALSE, $theme_add_js = TRUE) {
+ $active_theme = \Drupal::theme()->getActiveTheme();
+ if ($theme_add_js) {
+ // Add libraries used by this theme.
+ foreach ($active_theme->getLibraries() as $library) {
+ _drupal_add_library($library);
+ }
+ }
+
if (!isset($javascript)) {
$javascript = _drupal_add_js();
}
@@ -1858,6 +1880,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
// Allow modules to alter the JavaScript.
if (!$skip_alter) {
\Drupal::moduleHandler()->alter('js', $javascript);
+ \Drupal::theme()->alter('js', $javascript);
}
// Filter out elements of the given scope.
@@ -1874,7 +1897,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
// Don't add settings if there is no other JavaScript on the page, unless
// this is an AJAX request.
if (!empty($items['settings']) || $is_ajax) {
- global $theme_key;
+ $theme_key = $active_theme->getName();
// Provide the page with information about the theme that's used, so that
// a later AJAX request can be rendered using the same theme.
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator
@@ -2921,6 +2944,7 @@ function drupal_prepare_page($page) {
// Modules alter the $page as needed. Blocks are populated into regions like
// 'sidebar_first', 'footer', etc.
\Drupal::moduleHandler()->alter('page', $page);
+ \Drupal::theme()->alter('page', $page);
// The "main" and "secondary" menus are never part of the page-level render
// array and therefore their cache tags will never bubble up into the page
@@ -4034,7 +4058,7 @@ function drupal_flush_all_caches() {
// use it. Unlike regular usages of this function, the installer and update
// scripts need to flush all caches during GET requests/page building.
if (function_exists('_drupal_maintenance_theme')) {
- unset($GLOBALS['theme']);
+ \Drupal::theme()->resetActiveTheme();
drupal_maintenance_theme();
}
}
diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 43fe7e7..51add03 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -119,17 +119,18 @@ function error_displayable($error = NULL) {
function _drupal_log_error($error, $fatal = FALSE) {
$is_installer = drupal_installation_attempted();
// Initialize a maintenance theme if the bootstrap was not complete.
- // Do it early because drupal_set_message() triggers a drupal_theme_initialize().
+ // Do it early because drupal_set_message() triggers a
+ // \Drupal\Core\Theme\ThemeManager::initTheme().
if ($fatal && drupal_get_bootstrap_phase() < DRUPAL_BOOTSTRAP_CODE) {
// The installer initializes a maintenance theme at the earliest possible
// point in time already. Do not unset that.
if (!$is_installer) {
- unset($GLOBALS['theme']);
+ \Drupal::theme()->setActiveTheme(NULL);
}
if (!defined('MAINTENANCE_MODE')) {
define('MAINTENANCE_MODE', 'error');
}
- // No-op if $GLOBALS['theme'] is set already.
+ // No-op if the active theme is set already.
drupal_maintenance_theme();
}
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 7131a7b..dfe945b 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -3056,9 +3056,8 @@ function batch_set($batch_definition) {
function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = NULL) {
$batch =& batch_get();
- drupal_theme_initialize();
-
if (isset($batch)) {
+ $a = 123;
// Add process information
$process_info = array(
'current_set' => 0,
@@ -3067,7 +3066,7 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = NU
'url_options' => array(),
'source_url' => current_path(),
'redirect' => $redirect,
- 'theme' => $GLOBALS['theme_key'],
+ 'theme' => drupal_installation_attempted() ? 'seven' : \Drupal::theme()->getActiveTheme()->getName(),
'redirect_callback' => $redirect_callback,
);
$batch += $process_info;
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index f23a864..6dc2e8b 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -91,210 +91,6 @@ function drupal_theme_access($theme) {
}
/**
- * Initializes the theme system by loading the theme.
- *
- * @param RouteMatch $route_match
- * The route match to use for theme initialization.
-// @todo Force calling methods to provide as RouteMatch.
- */
-function drupal_theme_initialize(RouteMatch $route_match = NULL) {
- global $theme, $theme_key;
-
- // If $theme is already set, assume the others are set, too, and do nothing
- if (isset($theme)) {
- return;
- }
-
- $themes = list_themes();
-
- // Determine the active theme for the theme negotiator service. This includes
- // the default theme as well as really specific ones like the ajax base theme.
- if (!$route_match) {
- $route_match = \Drupal::routeMatch();
- }
- $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($route_match);
-
- // If no theme could be negotiated, or if the negotiated theme is not within
- // the list of enabled themes, fall back to the default theme output of core
- // and modules (similar to Stark, but without a theme extension at all). This
- // is possible, because _drupal_theme_initialize() always loads the Twig theme
- // engine.
- if (!$theme || !isset($themes[$theme])) {
- $theme = 'core';
- $theme_key = $theme;
- // /core/core.info.yml does not actually exist, but is required because
- // Extension expects a pathname.
- _drupal_theme_initialize(new Extension('theme', 'core/core.info.yml'));
- return;
- }
-
- // Store the identifier for retrieving theme settings with.
- $theme_key = $theme;
-
- // Find all our ancestor themes and put them in an array.
- $base_theme = array();
- $ancestor = $theme;
- while ($ancestor && isset($themes[$ancestor]->base_theme)) {
- $ancestor = $themes[$ancestor]->base_theme;
- $base_theme[] = $themes[$ancestor];
- }
- _drupal_theme_initialize($themes[$theme], array_reverse($base_theme));
-}
-
-/**
- * Initializes the theme system given already loaded information.
- *
- * This function is useful to initialize a theme when no database is present.
- *
- * @param \Drupal\Core\Extension\Extension $theme
- * The theme extension object.
- * @param \Drupal\Core\Extension\Extension[] $base_theme
- * An optional array of objects that represent the 'base theme' if the
- * theme is meant to be derivative of another theme. It requires
- * the same information as the $theme object. It should be in
- * 'oldest first' order, meaning the top level of the chain will
- * be first.
- */
-function _drupal_theme_initialize($theme, $base_theme = array()) {
- global $theme_info, $base_theme_info, $theme_engine, $theme_path;
- $theme_info = $theme;
- $base_theme_info = $base_theme;
-
- $theme_path = $theme->getPath();
-
- // Prepare stylesheets from this theme as well as all ancestor themes.
- // We work it this way so that we can have child themes override parent
- // theme stylesheets easily.
- $final_stylesheets = array();
- // CSS file basenames to override, pointing to the final, overridden filepath.
- $theme->stylesheets_override = array();
- // CSS file basenames to remove.
- $theme->stylesheets_remove = array();
-
- // Grab stylesheets from base theme
- foreach ($base_theme as $base) {
- if (!empty($base->stylesheets)) {
- foreach ($base->stylesheets as $media => $stylesheets) {
- foreach ($stylesheets as $name => $stylesheet) {
- $final_stylesheets[$media][$name] = $stylesheet;
- }
- }
- }
- $base_theme_path = $base->getPath();
- if (!empty($base->info['stylesheets-remove'])) {
- foreach ($base->info['stylesheets-remove'] as $basename) {
- $theme->stylesheets_remove[$basename] = $base_theme_path . '/' . $basename;
- }
- }
- if (!empty($base->info['stylesheets-override'])) {
- foreach ($base->info['stylesheets-override'] as $name) {
- $basename = drupal_basename($name);
- $theme->stylesheets_override[$basename] = $base_theme_path . '/' . $name;
- }
- }
- }
-
- // Add stylesheets used by this theme.
- if (!empty($theme->stylesheets)) {
- foreach ($theme->stylesheets as $media => $stylesheets) {
- foreach ($stylesheets as $name => $stylesheet) {
- $final_stylesheets[$media][$name] = $stylesheet;
- }
- }
- }
- if (!empty($theme->info['stylesheets-remove'])) {
- foreach ($theme->info['stylesheets-remove'] as $basename) {
- $theme->stylesheets_remove[$basename] = $theme_path . '/' . $basename;
-
- if (isset($theme->stylesheets_override[$basename])) {
- unset($theme->stylesheets_override[$basename]);
- }
- }
- }
- if (!empty($theme->info['stylesheets-override'])) {
- foreach ($theme->info['stylesheets-override'] as $name) {
- $basename = drupal_basename($name);
- $theme->stylesheets_override[$basename] = $theme_path . '/' . $name;
-
- if (isset($theme->stylesheets_remove[$basename])) {
- unset($theme->stylesheets_remove[$basename]);
- }
- }
- }
-
- // And now add the stylesheets properly.
- $css = array();
- foreach ($final_stylesheets as $media => $stylesheets) {
- foreach ($stylesheets as $stylesheet) {
- $css['#attached']['css'][$stylesheet] = array(
- 'group' => CSS_AGGREGATE_THEME,
- 'every_page' => TRUE,
- 'media' => $media
- );
- }
- }
- drupal_render($css);
-
- // Do basically the same as the above for libraries
- $final_libraries = array();
-
- // Grab libraries from base theme
- foreach ($base_theme as $base) {
- if (!empty($base->libraries)) {
- foreach ($base->libraries as $library) {
- $final_libraries[] = $library;
- }
- }
- }
-
- // Add libraries used by this theme.
- if (!empty($theme->libraries)) {
- foreach ($theme->libraries as $library) {
- $final_libraries[] = $library;
- }
- }
-
- // Add libraries used by this theme.
- $libraries = array();
- foreach ($final_libraries as $library) {
- $libraries['#attached']['library'][] = $library;
- }
- drupal_render($libraries);
-
- $theme_engine = NULL;
-
- // Initialize the theme.
- if (isset($theme->engine)) {
- // Include the engine.
- include_once DRUPAL_ROOT . '/' . $theme->owner;
-
- $theme_engine = $theme->engine;
- if (function_exists($theme_engine . '_init')) {
- foreach ($base_theme as $base) {
- call_user_func($theme_engine . '_init', $base);
- }
- call_user_func($theme_engine . '_init', $theme);
- }
- }
- else {
- // include non-engine theme files
- foreach ($base_theme as $base) {
- // Include the theme file or the engine.
- if (!empty($base->owner)) {
- include_once DRUPAL_ROOT . '/' . $base->owner;
- }
- }
- // and our theme gets one too.
- if (!empty($theme->owner)) {
- include_once DRUPAL_ROOT . '/' . $theme->owner;
- }
- }
-
- // Always include Twig as the default theme engine.
- include_once DRUPAL_ROOT . '/core/themes/engines/twig/twig.engine';
-}
-
-/**
* Gets the theme registry.
*
* @param bool $complete
@@ -417,6 +213,7 @@ function _theme($hook, $variables = array()) {
static $default_attributes;
$module_handler = \Drupal::moduleHandler();
+ $active_theme = \Drupal::theme()->getActiveTheme();
// If called before all modules are loaded, we do not necessarily have a full
// theme registry to work with, and therefore cannot process the theme
@@ -424,8 +221,6 @@ function _theme($hook, $variables = array()) {
if (!$module_handler->isLoaded() && !defined('MAINTENANCE_MODE')) {
throw new Exception(t('_theme() may not be called until all modules are loaded.'));
}
- // Ensure the theme is initialized.
- drupal_theme_initialize();
/** @var \Drupal\Core\Utility\ThemeRegistry $theme_registry */
$theme_registry = \Drupal::service('theme.registry')->getRuntime();
@@ -469,11 +264,6 @@ function _theme($hook, $variables = array()) {
}
$info = $theme_registry->get($hook);
- global $theme_path;
- $temp = $theme_path;
- // point path_to_theme() to the currently used theme path:
- $theme_path = $info['theme path'];
-
// If a renderable array is passed as $variables, then set $variables to
// the arguments expected by the theme function.
@@ -533,6 +323,7 @@ function _theme($hook, $variables = array()) {
'theme_suggestions_' . $base_theme_hook,
);
$module_handler->alter($hooks, $suggestions, $variables, $base_theme_hook);
+ \Drupal::theme()->alter($hooks, $suggestions, $variables, $base_theme_hook);
// Check if each suggestion exists in the theme registry, and if so,
// use it instead of the hook that _theme() was called with. For example, a
@@ -593,7 +384,7 @@ function _theme($hook, $variables = array()) {
$extension = '.html.twig';
// The theme engine may use a different extension and a different renderer.
- global $theme_engine;
+ $theme_engine = $active_theme->getEngine();
if (isset($theme_engine)) {
if ($info['type'] != 'module') {
if (function_exists($theme_engine . '_render_template')) {
@@ -654,32 +445,10 @@ function _theme($hook, $variables = array()) {
$output = $render_function($template_file, $variables);
}
- // restore path_to_theme()
- $theme_path = $temp;
return (string) $output;
}
/**
- * Returns the path to the current themed element.
- *
- * It can point to the active theme or the module handling a themed
- * implementation. For example, when invoked within the scope of a theming call
- * it will depend on where the theming function is handled. If implemented from
- * a module, it will point to the module. If implemented from the active theme,
- * it will point to the active theme. When called outside the scope of a
- * theming call, it will always point to the active theme.
- */
-function path_to_theme() {
- global $theme_path;
-
- if (!isset($theme_path)) {
- drupal_theme_initialize();
- }
-
- return $theme_path;
-}
-
-/**
* Allows themes and/or theme engines to discover overridden theme functions.
*
* @param $cache
@@ -765,7 +534,7 @@ function drupal_find_theme_templates($cache, $extension, $path) {
}
}
}
- global $theme;
+ $theme = \Drupal::theme()->getActiveTheme()->getName();
$subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array();
// Escape the periods in the extension.
@@ -867,7 +636,7 @@ function theme_get_setting($setting_name, $theme = NULL) {
// If no key is given, use the current theme if we can determine it.
if (!isset($theme)) {
- $theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : '';
+ $theme = \Drupal::theme()->getActiveTheme()->getName();
}
if (empty($cache[$theme])) {
@@ -1881,7 +1650,7 @@ function template_preprocess_container(&$variables) {
*/
function template_preprocess(&$variables, $hook, $info) {
// Tell all templates where they are located.
- $variables['directory'] = path_to_theme();
+ $variables['directory'] = \Drupal::theme()->getActiveTheme()->getPath();
// Merge in variables that don't depend on hook and don't change during a
// single page request.
@@ -1971,8 +1740,7 @@ function drupal_pre_render_html(array $element) {
* @see system_element_info()
*/
function drupal_pre_render_page(array $element) {
- global $theme;
- $element['#cache']['tags']['theme'] = $theme;
+ $element['#cache']['tags']['theme'] = \Drupal::theme()->getActiveTheme()->getName();
$element['#cache']['tags']['theme_global_settings'] = TRUE;
return $element;
}
@@ -2093,7 +1861,7 @@ function template_preprocess_page(&$variables) {
$variables['show_messages'] = $variables['page']['#show_messages'];
$variables['title'] = $variables['page']['#title'];
- foreach (system_region_list($GLOBALS['theme']) as $region_key => $region_name) {
+ foreach (system_region_list(\Drupal::theme()->getActiveTheme()->getName()) as $region_key => $region_name) {
if (!isset($variables['page'][$region_key])) {
$variables['page'][$region_key] = array();
}
diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc
index 8eb0775..2adb930 100644
--- a/core/includes/theme.maintenance.inc
+++ b/core/includes/theme.maintenance.inc
@@ -7,6 +7,7 @@
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Site\Settings;
+use Drupal\Core\Theme\ActiveTheme;
/**
* Sets up the theming system for maintenance page.
@@ -18,10 +19,8 @@
* setting a "maintenance_theme" key in the $settings variable in settings.php.
*/
function _drupal_maintenance_theme() {
- global $theme, $theme_key;
-
- // If $theme is already set, assume the others are set too, and do nothing.
- if (isset($theme)) {
+ // If the theme is already set, assume the others are set too, and do nothing.
+ if (\Drupal::theme()->hasActiveTheme()) {
return;
}
@@ -76,10 +75,13 @@ function _drupal_maintenance_theme() {
// If no themes are installed yet, or if the requested custom theme is not
// installed, retrieve all available themes.
+ /** @var \Drupal\Core\Theme\Initialization $theme_init */
+ $theme_init = \Drupal::service('theme.initialization');
if (empty($themes) || !isset($themes[$custom_theme])) {
$theme_handler = \Drupal::service('theme_handler');
$themes = $theme_handler->rebuildThemeData();
$theme_handler->addTheme($themes[$custom_theme]);
+ \Drupal::theme()->setActiveTheme($theme_init->getActiveTheme($themes[$custom_theme], array()));
}
// list_themes() triggers a \Drupal\Core\Extension\ModuleHandler::alter() in
@@ -88,9 +90,6 @@ function _drupal_maintenance_theme() {
// list_themes() builds its cache.
$theme = $custom_theme;
- // Store the identifier for retrieving theme settings with.
- $theme_key = $theme;
-
// Find all our ancestor themes and put them in an array.
$base_theme = array();
$ancestor = $theme;
@@ -98,7 +97,9 @@ function _drupal_maintenance_theme() {
$base_theme[] = $new_base_theme = $themes[$themes[$ancestor]->base_theme];
$ancestor = $themes[$ancestor]->base_theme;
}
- _drupal_theme_initialize($themes[$theme], array_reverse($base_theme));
+ // @todo This is just a workaround. Find a better way how to handle themes
+ // on maintenance pages.
+ \Drupal::theme()->setActiveTheme($theme_init->getActiveTheme($themes[$custom_theme], array_reverse($base_theme)));
// Prime the theme registry.
// @todo Remove global theme variables.
Drupal::service('theme.registry');
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index b5e27de..c555d39 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -624,6 +624,15 @@ public static function formBuilder() {
}
/**
+ * Gets the theme service.
+ *
+ * @return \Drupal\Core\Theme\ThemeManagerInterface
+ */
+ public static function theme() {
+ return static::$container->get('theme');
+ }
+
+ /**
* Gets the syncing state.
*
* @return bool
diff --git a/core/lib/Drupal/Core/EventSubscriber/ThemeNegotiatorRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ThemeNegotiatorRequestSubscriber.php
deleted file mode 100644
index 322981d..0000000
--- a/core/lib/Drupal/Core/EventSubscriber/ThemeNegotiatorRequestSubscriber.php
+++ /dev/null
@@ -1,48 +0,0 @@
-getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
- if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
- // @todo Refactor drupal_theme_initialize() into a request subscriber.
- // @see https://drupal.org/node/2228093
- drupal_theme_initialize(RouteMatch::createFromRequest($event->getRequest()));
- }
- }
- }
-
- /**
- * Registers the methods in this class that should be listeners.
- *
- * @return array
- * An array of event listener definitions.
- */
- public static function getSubscribedEvents() {
- $events[KernelEvents::REQUEST][] = array('onKernelRequestThemeNegotiator', 29);
- return $events;
- }
-
-}
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index 6270c8e..12292e8 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -482,30 +482,6 @@ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
}
}
}
- // Allow the theme to alter variables after the theme system has been
- // initialized.
- global $theme, $base_theme_info;
- if (isset($theme)) {
- $theme_keys = array();
- foreach ($base_theme_info as $base) {
- $theme_keys[] = $base->getName();
- }
- $theme_keys[] = $theme;
- foreach ($theme_keys as $theme_key) {
- $function = $theme_key . '_' . $hook;
- if (function_exists($function)) {
- $this->alterFunctions[$cid][] = $function;
- }
- if (isset($extra_types)) {
- foreach ($extra_types as $extra_type) {
- $function = $theme_key . '_' . $extra_type . '_alter';
- if (function_exists($function)) {
- $this->alterFunctions[$cid][] = $function;
- }
- }
- }
- }
- }
}
foreach ($this->alterFunctions[$cid] as $function) {
diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php
index dbe2c78..cdcc492 100644
--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -336,6 +336,12 @@ public function listInfo() {
if (!isset($this->list)) {
$this->list = array();
$themes = $this->systemThemeList();
+ // @FIXME: this is just a workaround
+ if (empty($themes)) {
+ $this->refreshInfo();
+ $this->list = $this->list ?: array();
+ $themes = \Drupal::state()->get('system.theme.data', array());
+ }
foreach ($themes as $theme) {
$this->addTheme($theme);
}
@@ -371,7 +377,7 @@ public function addTheme(Extension $theme) {
public function refreshInfo() {
$this->reset();
$extension_config = $this->configFactory->get('core.extension');
- $enabled = $extension_config->get('theme') ?: array();
+ $enabled = $extension_config->get('theme');
// @todo Avoid re-scanning all themes by retaining the original (unaltered)
// theme info somewhere.
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index c92f2d6..649e234 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -18,6 +18,7 @@
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Site\Settings;
+use Drupal\Core\Theme\ThemeManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
@@ -90,6 +91,13 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
protected $currentUser;
/**
+ * The theme manager.
+ *
+ * @var \Drupal\Core\Theme\ThemeManagerInterface
+ */
+ protected $themeManager;
+
+ /**
* @var \Drupal\Core\Form\FormValidatorInterface
*/
protected $formValidator;
@@ -121,7 +129,7 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
* @param \Drupal\Core\HttpKernel $http_kernel
* The HTTP kernel.
*/
- public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, ModuleHandlerInterface $module_handler, KeyValueExpirableFactoryInterface $key_value_expirable_factory, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, CsrfTokenGenerator $csrf_token = NULL, HttpKernel $http_kernel = NULL) {
+ public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, ModuleHandlerInterface $module_handler, KeyValueExpirableFactoryInterface $key_value_expirable_factory, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL, HttpKernel $http_kernel = NULL) {
$this->formValidator = $form_validator;
$this->formSubmitter = $form_submitter;
$this->moduleHandler = $module_handler;
@@ -131,6 +139,7 @@ public function __construct(FormValidatorInterface $form_validator, FormSubmitte
$this->classResolver = $class_resolver;
$this->csrfToken = $csrf_token;
$this->httpKernel = $http_kernel;
+ $this->themeManager = $theme_manager;
}
/**
@@ -648,6 +657,7 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
}
$hooks[] = 'form_' . $form_id;
$this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
+ $this->themeManager->alter($hooks, $form, $form_state, $form_id);
}
/**
diff --git a/core/lib/Drupal/Core/Theme/ActiveTheme.php b/core/lib/Drupal/Core/Theme/ActiveTheme.php
new file mode 100644
index 0000000..5958c7f
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ActiveTheme.php
@@ -0,0 +1,180 @@
+name = $values['name'];
+ $this->path = $values['path'];
+ if (isset($values['parent'])) {
+ $this->parent = $values['parent'];
+ }
+ $this->engine = $values['engine'];
+ $this->owner = $values['owner'];
+ $this->styleSheets = $values['stylesheets'];
+ $this->styleSheetsRemove = $values['stylesheets_remove'];
+ $this->styleSheetsOverride = $values['stylesheets_override'];
+ $this->libraries = $values['libraries'];
+ $this->extension = $values['extension'];
+ $this->baseThemes = $values['base_themes'];
+ }
+
+ /**
+ * Returns the machine name of the theme.
+ *
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Returns the path to the theme directory.
+ *
+ * @return string
+ */
+ public function getPath() {
+ return $this->path;
+ }
+
+ /**
+ * Returns the parent active theme object or NULL if this is the root element.
+ *
+ * @return static|NULL
+ */
+ public function getParent() {
+ return $this->parent;
+ }
+
+ /**
+ * Returns the theme engine.
+ *
+ * @return string
+ */
+ public function getEngine() {
+ return $this->engine;
+ }
+
+ /**
+ * Returns the path theme engine file.
+ *
+ * @return mixed
+ */
+ public function getOwner() {
+ return $this->owner;
+ }
+
+ /**
+ * Returns the extension object.
+ *
+ * @return \Drupal\Core\Extension\Extension
+ */
+ public function getExtension() {
+ return $this->extension;
+ }
+
+ /**
+ * Returns the libraries provided by the theme.
+ *
+ * @return mixed
+ */
+ public function getLibraries() {
+ return $this->libraries;
+ }
+
+ /**
+ * Returns the stylesheets provided by the theme.
+ *
+ * @return mixed
+ */
+ public function getStyleSheets() {
+ return $this->styleSheets;
+ }
+
+ /**
+ * Returns the overridden stylesheets by the theme.
+ *
+ * @return mixed
+ */
+ public function getStyleSheetsOverride() {
+ return $this->styleSheetsOverride;
+ }
+
+ /**
+ * Returns the removed stylesheets by the theme.
+ *
+ * @return mixed
+ */
+ public function getStyleSheetsRemove() {
+ return $this->styleSheetsRemove;
+ }
+
+ /**
+ * Returns an array of base theme active theme objects keyed by name.
+ *
+ * @return static[]
+ */
+ public function getBaseThemes() {
+ return $this->baseThemes;
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
index 2f0e6f7..6000315 100644
--- a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
+++ b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php
@@ -43,7 +43,7 @@ public function applies(RouteMatchInterface $route_match) {
* {@inheritdoc}
*/
public function determineActiveTheme(RouteMatchInterface $route_match) {
- return $this->config->get('default');
+ return $this->config->get('default') ?: 'stark';
}
}
diff --git a/core/lib/Drupal/Core/Theme/Initialization.php b/core/lib/Drupal/Core/Theme/Initialization.php
new file mode 100644
index 0000000..1efa00e
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/Initialization.php
@@ -0,0 +1,256 @@
+themeHandler = $theme_handler;
+ $this->state = $state;
+ }
+
+ /**
+ * Initializes a given theme.
+ *
+ * This loads the active theme, for example include its engine file.
+ *
+ * @param string $theme_name
+ * The machine name of the theme.
+ *
+ * @return \Drupal\Core\Theme\ActiveTheme
+ * An active theme object instance for the given theme.
+ */
+ public function initTheme($theme_name) {
+ $active_theme = $this->getActiveThemeByName($theme_name);
+ $this->loadActiveTheme($active_theme);
+
+ return $active_theme;
+ }
+
+ /**
+ * Builds an active theme object.
+ *
+ * @param string $theme_name
+ * The machine name of the theme.
+ *
+ * @return \Drupal\Core\Theme\ActiveTheme
+ * An active theme object instance for the given theme.
+ */
+ public function getActiveThemeByName($theme_name) {
+ if ($active_theme = $this->state->get('theme.active_theme.' . $theme_name)) {
+ return $active_theme;
+ }
+ else {
+ $themes = $this->themeHandler->listInfo();
+
+ if (empty($themes)) {
+ throw new \RuntimeException('No theme is enabled.');
+ }
+ if (!isset($themes[$theme_name])) {
+ throw new \InvalidArgumentException(String::format('Theme %theme is not enabled/does not exist.', array('theme' => $theme_name)));
+ }
+
+ // Find all our ancestor themes and put them in an array.
+ $base_themes = array();
+ $ancestor = $theme_name;
+ while ($ancestor && isset($themes[$ancestor]->base_theme)) {
+ $ancestor = $themes[$ancestor]->base_theme;
+ $base_themes[] = $themes[$ancestor];
+ }
+
+ $active_theme = $this->getActiveTheme($themes[$theme_name], $base_themes);
+
+ $this->state->set('theme.active_theme.' . $theme_name, $active_theme);
+ return $active_theme;
+ }
+ }
+
+ /**
+ * Build active theme objects for all themes.
+ */
+ protected function buildActiveThemes() {
+ }
+
+ /**
+ * Loads a theme, so it is ready to be used.
+ *
+ * Loading a theme includes loading and initializing the engine,
+ * each base theme and its engines.
+ *
+ * @param ActiveTheme $active_theme
+ */
+ public function loadActiveTheme(ActiveTheme $active_theme) {
+ // Initialize the theme.
+ if ($theme_engine = $active_theme->getEngine()) {
+ // Include the engine.
+ include_once DRUPAL_ROOT . '/' . $active_theme->getOwner();
+
+ if (function_exists($theme_engine . '_init')) {
+ foreach ($active_theme->getBaseThemes() as $base) {
+ call_user_func($theme_engine . '_init', $base->getExtension());
+ }
+ call_user_func($theme_engine . '_init', $active_theme->getExtension());
+ }
+ }
+ else {
+ // include non-engine theme files
+ foreach ($active_theme->getBaseThemes() as $base) {
+ // Include the theme file or the engine.
+ if ($base->getOwner()) {
+ include_once DRUPAL_ROOT . '/' . $base->getOwner();
+ }
+ }
+ // and our theme gets one too.
+ if ($active_theme->getOwner()) {
+ include_once DRUPAL_ROOT . '/' . $active_theme->getOwner();
+ }
+ }
+
+ // Always include Twig as the default theme engine.
+ include_once DRUPAL_ROOT . '/core/themes/engines/twig/twig.engine';
+ }
+
+ /**
+ * Builds up the active theme object.
+ *
+ * @param \Drupal\Core\Extension\Extension $theme
+ * The theme extension object.
+ * @param \Drupal\Core\Extension\Extension[] $base_themes
+ * An array of extension objects of base theme and its bases. It is ordered
+ * by 'oldest first', meaning the top level of the chain will be first.
+ *
+ * @throws \Exception
+ * todo
+ * @return \Drupal\Core\Theme\ActiveTheme
+ * The active theme instance for the passed in $theme.
+ */
+ public function getActiveTheme(Extension $theme, array $base_themes) {
+ $theme_path = $theme->getPath();
+
+ $values['path'] = $theme_path;
+ $values['name'] = $theme->getName();
+
+ // Prepare stylesheets from this theme as well as all ancestor themes.
+ // We work it this way so that we can have child themes override parent
+ // theme stylesheets easily.
+ $values['stylesheets'] = array();
+ // CSS file basenames to override, pointing to the final, overridden filepath.
+ $values['stylesheets_override'] = array();
+ // CSS file basenames to remove.
+ $values['stylesheets_remove'] = array();
+
+ // Grab stylesheets from base theme.
+ $final_stylesheets = array();
+ foreach ($base_themes as $base) {
+ if (!empty($base->stylesheets)) {
+ foreach ($base->stylesheets as $media => $stylesheets) {
+ foreach ($stylesheets as $name => $stylesheet) {
+ $final_stylesheets[$media][$name] = $stylesheet;
+ }
+ }
+ }
+ $base_theme_path = $base->getPath();
+ if (!empty($base->info['stylesheets-remove'])) {
+ foreach ($base->info['stylesheets-remove'] as $basename) {
+ $values['stylesheets_remove'][$basename] = $base_theme_path . '/' . $basename;
+ }
+ }
+ if (!empty($base->info['stylesheets-override'])) {
+ foreach ($base->info['stylesheets-override'] as $name) {
+ $basename = drupal_basename($name);
+ $values['stylesheets_override'][$basename] = $base_theme_path . '/' . $name;
+ }
+ }
+ }
+
+ // Add stylesheets used by this theme.
+ if (!empty($theme->stylesheets)) {
+ foreach ($theme->stylesheets as $media => $stylesheets) {
+ foreach ($stylesheets as $name => $stylesheet) {
+ $final_stylesheets[$media][$name] = $stylesheet;
+ }
+ }
+ }
+ if (!empty($theme->info['stylesheets-remove'])) {
+ foreach ($theme->info['stylesheets-remove'] as $basename) {
+ $values['stylesheets_remove'][$basename] = $theme_path . '/' . $basename;
+
+ if (isset($values['stylesheets_override'][$basename])) {
+ unset($values['stylesheets_override'][$basename]);
+ }
+ }
+ }
+ if (!empty($theme->info['stylesheets-override'])) {
+ foreach ($theme->info['stylesheets-override'] as $name) {
+ $basename = drupal_basename($name);
+ $values['stylesheets_override'][$basename] = $theme_path . '/' . $name;
+
+ if (isset($values['stylesheets_remove'][$basename])) {
+ unset($values['stylesheets_remove'][$basename]);
+ }
+ }
+ }
+
+ // And now add the stylesheets properly.
+ $values['stylesheets'] = $final_stylesheets;
+
+ // Do basically the same as the above for libraries
+ $values['libraries'] = array();
+
+ // Grab libraries from base theme
+ foreach ($base_themes as $base) {
+ if (!empty($base->libraries)) {
+ foreach ($base->libraries as $library) {
+ $values['libraries'][] = $library;
+ }
+ }
+ }
+
+ // Add libraries used by this theme.
+ if (!empty($theme->libraries)) {
+ foreach ($theme->libraries as $library) {
+ $values['libraries'][] = $library;
+ }
+ }
+
+ $values['engine'] = isset($theme->engine) ? $theme->engine : NULL;
+ $values['owner'] = isset($theme->owner) ? $theme->owner : NULL;
+ $values['extension'] = $theme;
+
+ $base_active_themes = array();
+ foreach ($base_themes as $base_theme) {
+ $base_active_themes[$base_theme->getName()] = $this->getActiveTheme($base_theme, array_slice($base_themes, 1));
+ }
+
+ $values['base_themes'] = $base_active_themes;
+
+ return new ActiveTheme($values);
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php
index 402c9f7..a0be4c1 100644
--- a/core/lib/Drupal/Core/Theme/Registry.php
+++ b/core/lib/Drupal/Core/Theme/Registry.php
@@ -34,7 +34,7 @@ class Registry implements DestructableInterface {
*
* @var array
*/
- protected $baseThemes;
+ protected $baseThemes = array();
/**
* The name of the theme engine of $theme.
@@ -116,6 +116,20 @@ class Registry implements DestructableInterface {
protected $runtimeRegistry;
/**
+ * Stores whether the registry was already initialized.
+ *
+ * @var bool
+ */
+ protected $initialized = FALSE;
+
+ /**
+ * The name of the theme for which to construct the registry, if given.
+ *
+ * @var string|null
+ */
+ protected $themeName;
+
+ /**
* Constructs a \Drupal\Core\\Theme\Registry object.
*
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
@@ -131,7 +145,7 @@ public function __construct(CacheBackendInterface $cache, LockBackendInterface $
$this->cache = $cache;
$this->lock = $lock;
$this->moduleHandler = $module_handler;
- $this->init($theme_name);
+ $this->themeName = $theme_name;
}
/**
@@ -144,19 +158,18 @@ public function __construct(CacheBackendInterface $cache, LockBackendInterface $
* (optional) The name of the theme for which to construct the registry.+
*/
protected function init($theme_name = NULL) {
+ if ($this->initialized) {
+ return;
+ }
// Unless instantiated for a specific theme, use globals.
if (!isset($theme_name)) {
- if (isset($GLOBALS['theme']) && isset($GLOBALS['theme_info'])) {
- $this->theme = $GLOBALS['theme_info'];
- $this->baseThemes = $GLOBALS['base_theme_info'];
- $this->engine = $GLOBALS['theme_engine'];
- }
- else {
- // @see drupal_theme_initialize()
- $this->theme = new Extension('theme', 'core/core.info.yml');
- $this->baseThemes = array();
- $this->engine = 'twig';
+ $active_theme = \Drupal::theme()->getActiveTheme();
+ $this->theme = $active_theme;
+ // @todo use $active_theme->getBaseThemes();
+ if ($parent = $active_theme->getParent()) {
+ $this->baseThemes = array($parent);
}
+ $this->engine = $active_theme->getEngine();
}
// Instead of the global theme, a specific theme was requested.
else {
@@ -176,7 +189,6 @@ protected function init($theme_name = NULL) {
}
$this->baseThemes = array_reverse($this->baseThemes);
- // @see _drupal_theme_initialize()
if (isset($this->theme->engine)) {
$this->engine = $this->theme->engine;
include_once DRUPAL_ROOT . '/' . $this->theme->owner;
@@ -199,6 +211,7 @@ protected function init($theme_name = NULL) {
* @see Registry::$registry
*/
public function get() {
+ $this->init($this->themeName);
if (isset($this->registry)) {
return $this->registry;
}
@@ -224,6 +237,7 @@ public function get() {
* lightweight than the full registry.
*/
public function getRuntime() {
+ $this->init($this->themeName);
if (!isset($this->runtimeRegistry)) {
$this->runtimeRegistry = new ThemeRegistry('theme_registry:runtime:' . $this->theme->getName(), $this->cache, $this->lock, array('theme_registry' => TRUE), $this->moduleHandler->isLoaded());
}
@@ -247,6 +261,7 @@ protected function setCache() {
* The name of the base hook or FALSE.
*/
public function getBaseHook($hook) {
+ $this->init($this->themeName);
$base_hook = $hook;
// Iteratively strip everything after the last '__' delimiter, until a
// base hook definition is found. Recursive base hooks of base hooks are
@@ -331,6 +346,7 @@ protected function build() {
// Let modules alter the registry.
$this->moduleHandler->alter('theme_registry', $cache);
+ // @todo Do we want to allow themes to take part?
// @todo Implement more reduction of the theme registry entry.
// Optimize the registry to not have empty arrays for functions.
@@ -538,7 +554,6 @@ protected function processExtension(&$cache, $name, $type, $theme, $path) {
* To be called when the list of enabled extensions is changed.
*/
public function reset() {
-
// Reset the runtime registry.
if (isset($this->runtimeRegistry) && $this->runtimeRegistry instanceof ThemeRegistry) {
$this->runtimeRegistry->clear();
@@ -580,11 +595,4 @@ protected function listThemes() {
return list_themes();
}
- /**
- * Wraps drupal_theme_initialize().
- */
- protected function initializeTheme() {
- drupal_theme_initialize();
- }
-
}
diff --git a/core/lib/Drupal/Core/Theme/ThemeManager.php b/core/lib/Drupal/Core/Theme/ThemeManager.php
new file mode 100644
index 0000000..de41361
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeManager.php
@@ -0,0 +1,159 @@
+themeHandler = $theme_handler;
+ $this->themeNegotiator = $theme_negotiator;
+ $this->themeRegistry = $theme_registry;
+ $this->themeInitialization = $theme_initialization;
+ $this->requestStack = $request_stack;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function render($hook, $variables) {
+ return _theme($hook, $variables);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getActiveTheme(RouteMatchInterface $route_match = NULL) {
+ if (!isset($this->activeTheme)) {
+ $this->initTheme($route_match);
+ }
+ return $this->activeTheme;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasActiveTheme() {
+ return isset($this->activeTheme);
+ }
+
+ /**
+ * @TODO
+ */
+ public function resetActiveTheme() {
+ $this->activeTheme = NULL;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setActiveTheme(ActiveTheme $active_theme) {
+ $this->activeTheme = $active_theme;
+ if ($active_theme) {
+ $this->themeInitialization->loadActiveTheme($active_theme);
+ }
+ }
+
+ protected function initTheme(RouteMatchInterface $route_match = NULL) {
+ // Determine the active theme for the theme negotiator service. This includes
+ // the default theme as well as really specific ones like the ajax base theme.
+ if (!$route_match) {
+ $route_match = \Drupal::routeMatch();
+ }
+ $theme = $this->themeNegotiator->determineActiveTheme($route_match) ?: 'stark';
+ $this->activeTheme = $this->themeInitialization->initTheme($theme);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @todo Should we cache some of these information?
+ */
+ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
+ // Most of the time, $type is passed as a string, so for performance,
+ // normalize it to that. When passed as an array, usually the first item in
+ // the array is a generic type, and additional items in the array are more
+ // specific variants of it, as in the case of array('form', 'form_FORM_ID').
+ if (is_array($type)) {
+ $extra_types = $type;
+ $type = array_shift($extra_types);
+ // Allow if statements in this function to use the faster isset() rather
+ // than !empty() both when $type is passed as a string, or as an array with
+ // one item.
+ if (empty($extra_types)) {
+ unset($extra_types);
+ }
+ }
+
+ $theme_keys = array();
+ $theme = $this->getActiveTheme();
+ while ($base = $theme->getParent()) {
+ $theme_keys[] = $base->getName();
+ }
+
+ $theme_keys[] = $theme->getName();
+ $functions = array();
+ foreach ($theme_keys as $theme_key) {
+ $function = $theme_key . '_' . $type . '_alter';
+ if (function_exists($function)) {
+ $functions[] = $function;
+ }
+ if (isset($extra_types)) {
+ foreach ($extra_types as $extra_type) {
+ $function = $theme_key . '_' . $extra_type . '_alter';
+ if (function_exists($function)) {
+ $functions[] = $function;
+ }
+ }
+ }
+ }
+
+ foreach ($functions as $function) {
+ $function($data, $context1, $context2);
+ }
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Theme/ThemeManagerInterface.php b/core/lib/Drupal/Core/Theme/ThemeManagerInterface.php
new file mode 100644
index 0000000..0d11a39
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeManagerInterface.php
@@ -0,0 +1,53 @@
+getActiveTheme()->getName();
// Fetch a list of regions for the current theme.
$all_regions = system_region_list($theme);
diff --git a/core/modules/block/src/BlockListBuilder.php b/core/modules/block/src/BlockListBuilder.php
index 1b26b9f..dd171f9 100644
--- a/core/modules/block/src/BlockListBuilder.php
+++ b/core/modules/block/src/BlockListBuilder.php
@@ -87,7 +87,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
public function load() {
// If no theme was specified, use the current theme.
if (!$this->theme) {
- $this->theme = $GLOBALS['theme'];
+ $this->theme = \Drupal::theme()->getActiveTheme()->getName();
}
// Store the region list.
@@ -117,7 +117,7 @@ public function load() {
public function render($theme = NULL, Request $request = NULL) {
$this->request = $request;
// If no theme was specified, use the current theme.
- $this->theme = $theme ?: $GLOBALS['theme_key'];
+ $this->theme = $theme ?: \Drupal::theme()->getActiveTheme()->getName();
return \Drupal::formBuilder()->getForm($this);
}
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 1f03e18..663a92c 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -67,7 +67,7 @@ function color_form_system_theme_settings_alter(&$form, FormStateInterface $form
* Replaces style sheets with color-altered style sheets.
*/
function color_css_alter(&$css) {
- global $theme_key;
+ $theme_key = \Drupal::theme()->getActiveTheme()->getName();
$themes = list_themes();
// Override stylesheets.
@@ -97,7 +97,7 @@ function color_css_alter(&$css) {
* Replace the logo with the colored version if available.
*/
function color_preprocess_page(&$variables) {
- global $theme_key;
+ $theme_key = \Drupal::theme()->getActiveTheme()->getName();
// Override logo.
$logo = \Drupal::config('color.theme.' . $theme_key)->get('logo');
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index 616d064..afea590 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -176,7 +176,7 @@ protected function setUp() {
// \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() to work.
// Write directly to active storage to avoid early instantiation of
// the event dispatcher which can prevent modules from registering events.
- \Drupal::service('config.storage')->write('core.extension', array('module' => array(), 'theme' => array()));
+ \Drupal::service('config.storage')->write('core.extension', array('module' => array(), 'theme' => array('seven' => 1, 'stark' => 1)));
// Collect and set a fixed module list.
$class = get_class($this);
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index afe1feb..4e89fd6 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -1080,12 +1080,6 @@ private function prepareEnvironment() {
unset($GLOBALS['config_directories']);
unset($GLOBALS['config']);
unset($GLOBALS['conf']);
- unset($GLOBALS['theme_key']);
- unset($GLOBALS['theme']);
- unset($GLOBALS['theme_info']);
- unset($GLOBALS['base_theme_info']);
- unset($GLOBALS['theme_engine']);
- unset($GLOBALS['theme_path']);
// Log fatal errors.
ini_set('log_errors', 1);
@@ -1186,14 +1180,6 @@ private function restoreEnvironment() {
// this second reset is guaranteed to reset everything to nothing.
drupal_static_reset();
- // Reset global theme variables.
- unset($GLOBALS['theme_key']);
- unset($GLOBALS['theme']);
- unset($GLOBALS['theme_info']);
- unset($GLOBALS['base_theme_info']);
- unset($GLOBALS['theme_engine']);
- unset($GLOBALS['theme_path']);
-
// Restore original in-memory configuration.
$GLOBALS['config'] = $this->originalConfig;
$GLOBALS['conf'] = $this->originalConf;
diff --git a/core/modules/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php
index 9d398d9..ceaf612 100644
--- a/core/modules/system/src/Form/ThemeSettingsForm.php
+++ b/core/modules/system/src/Form/ThemeSettingsForm.php
@@ -248,7 +248,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $theme =
$local_file = drupal_get_path('theme', $theme) . '/' . $default;
}
else {
- $local_file = path_to_theme() . '/' . $default;
+ $local_file = \Drupal::theme()->getActiveTheme()->getPath() . '/' . $default;
}
$element['#description'] = t('Examples: @implicit-public-file
(for a file in the public filesystem), @explicit-file
, or @local-file
.', array(
@@ -284,8 +284,11 @@ public function buildForm(array $form, FormStateInterface $form_state, $theme =
// Save the name of the current theme (if any), so that we can temporarily
// override the current theme and allow theme_get_setting() to work
// without having to pass the theme name to it.
- $default_theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : NULL;
- $GLOBALS['theme_key'] = $theme;
+ $default_active_theme = \Drupal::theme()->getActiveTheme();
+ $default_theme = $default_active_theme->getName();
+ /** @var \Drupal\Core\Theme\Initialization $theme_initialization */
+ $theme_initialization = \Drupal::service('theme.initialization');
+ \Drupal::theme()->setActiveTheme($theme_initialization->getActiveThemeByName($theme));
// Process the theme and all its base themes.
foreach ($theme_keys as $theme) {
@@ -304,10 +307,10 @@ public function buildForm(array $form, FormStateInterface $form_state, $theme =
// Restore the original current theme.
if (isset($default_theme)) {
- $GLOBALS['theme_key'] = $default_theme;
+ \Drupal::theme()->setActiveTheme($default_theme);
}
else {
- unset($GLOBALS['theme_key']);
+ \Drupal::theme()->setActiveTheme(NULL);
}
}
diff --git a/core/modules/system/src/Tests/Common/AlterTest.php b/core/modules/system/src/Tests/Common/AlterTest.php
index d291275..40b3193 100644
--- a/core/modules/system/src/Tests/Common/AlterTest.php
+++ b/core/modules/system/src/Tests/Common/AlterTest.php
@@ -29,9 +29,8 @@ class AlterTest extends WebTestBase {
function testDrupalAlter() {
// This test depends on Bartik, so make sure that it is always the current
// active theme.
- global $theme, $base_theme_info;
- $theme = 'bartik';
- $base_theme_info = array();
+ \Drupal::service('theme_handler')->enable(array('bartik'));
+ \Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->initTheme('bartik'));
$array = array('foo' => 'bar');
$entity = new \stdClass();
@@ -41,12 +40,14 @@ function testDrupalAlter() {
$array_copy = $array;
$array_expected = array('foo' => 'Drupal theme');
\Drupal::moduleHandler()->alter('drupal_alter', $array_copy);
+ \Drupal::theme()->alter('drupal_alter', $array_copy);
$this->assertEqual($array_copy, $array_expected, 'Single array was altered.');
$entity_copy = clone $entity;
$entity_expected = clone $entity;
$entity_expected->foo = 'Drupal theme';
\Drupal::moduleHandler()->alter('drupal_alter', $entity_copy);
+ \Drupal::theme()->alter('drupal_alter', $entity_copy);
$this->assertEqual($entity_copy, $entity_expected, 'Single object was altered.');
// Verify alteration of multiple arguments.
@@ -58,6 +59,7 @@ function testDrupalAlter() {
$array2_copy = $array;
$array2_expected = array('foo' => 'Drupal theme');
\Drupal::moduleHandler()->alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
+ \Drupal::theme()->alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
$this->assertEqual($array_copy, $array_expected, 'First argument to \Drupal::moduleHandler->alter() was altered.');
$this->assertEqual($entity_copy, $entity_expected, 'Second argument to \Drupal::moduleHandler->alter() was altered.');
$this->assertEqual($array2_copy, $array2_expected, 'Third argument to \Drupal::moduleHandler->alter() was altered.');
@@ -68,6 +70,7 @@ function testDrupalAlter() {
$array_copy = $array;
$array_expected = array('foo' => 'Drupal block theme');
\Drupal::moduleHandler()->alter(array('drupal_alter', 'drupal_alter_foo'), $array_copy);
+ \Drupal::theme()->alter(array('drupal_alter', 'drupal_alter_foo'), $array_copy);
$this->assertEqual($array_copy, $array_expected, 'hook_TYPE_alter() implementations ran in correct order.');
}
}
diff --git a/core/modules/system/src/Tests/Common/CascadingStylesheetsTest.php b/core/modules/system/src/Tests/Common/CascadingStylesheetsTest.php
index d2b3614..4fc770d 100644
--- a/core/modules/system/src/Tests/Common/CascadingStylesheetsTest.php
+++ b/core/modules/system/src/Tests/Common/CascadingStylesheetsTest.php
@@ -69,7 +69,7 @@ function testReset() {
function testRenderFile() {
$css = drupal_get_path('module', 'simpletest') . '/css/simpletest.module.css';
_drupal_add_css($css);
- $styles = drupal_get_css();
+ $styles = drupal_get_css(NULL, FALSE, FALSE);
$this->assertTrue(strpos($styles, $css) > 0, 'Rendered CSS includes the added stylesheet.');
// Verify that newlines are properly added inside style tags.
$query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
@@ -100,7 +100,7 @@ function testRenderInlinePreprocess() {
$css = 'body { padding: 0px; }';
$css_preprocessed = '';
_drupal_add_css($css, array('type' => 'inline'));
- $styles = drupal_get_css();
+ $styles = drupal_get_css(NULL, NULL, FALSE);
$this->assertEqual(trim($styles), $css_preprocessed, 'Rendering preprocessed inline CSS adds it to the page.');
}
@@ -132,7 +132,7 @@ function testRenderOrder() {
drupal_get_path('module', 'simpletest') . '/css/simpletest.module.css',
);
- $styles = drupal_get_css();
+ $styles = drupal_get_css(NULL, NULL, FALSE);
// Stylesheet URL may be the href of a LINK tag or in an @import statement
// of a STYLE tag.
if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
diff --git a/core/modules/system/src/Tests/Common/JavaScriptTest.php b/core/modules/system/src/Tests/Common/JavaScriptTest.php
index 2f5dcae..58fd249 100644
--- a/core/modules/system/src/Tests/Common/JavaScriptTest.php
+++ b/core/modules/system/src/Tests/Common/JavaScriptTest.php
@@ -32,9 +32,6 @@ class JavaScriptTest extends DrupalUnitTestBase {
function setUp() {
parent::setUp();
- // There are dependencies in drupal_get_js() on the theme layer so we need
- // to initialize it.
- drupal_theme_initialize();
// Disable preprocessing
$config = \Drupal::config('system.performance');
diff --git a/core/modules/system/src/Tests/Common/RegionContentTest.php b/core/modules/system/src/Tests/Common/RegionContentTest.php
index 6b2d3a4..51ea973 100644
--- a/core/modules/system/src/Tests/Common/RegionContentTest.php
+++ b/core/modules/system/src/Tests/Common/RegionContentTest.php
@@ -19,7 +19,7 @@ class RegionContentTest extends WebTestBase {
* Tests setting and retrieving content for theme regions.
*/
function testRegions() {
- global $theme_key;
+ $theme_key = \Drupal::theme()->getActiveTheme()->getName();
$block_regions = array_keys(system_region_list($theme_key));
$delimiter = $this->randomName(32);
diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php
index 8ca6acc..6d24b5d 100644
--- a/core/modules/system/src/Tests/Common/RenderTest.php
+++ b/core/modules/system/src/Tests/Common/RenderTest.php
@@ -26,13 +26,6 @@ class RenderTest extends DrupalUnitTestBase {
*/
public static $modules = array('system', 'common_test');
- function setUp() {
- parent::setUp();
- // There are dependencies in drupal_get_js() on the theme layer so we need
- // to initialize it.
- drupal_theme_initialize();
- }
-
/**
* Tests the output drupal_render() for some elementary input values.
*/
diff --git a/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php b/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php
index 03a94cf..23e8cc1 100644
--- a/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php
+++ b/core/modules/system/src/Tests/Extension/ThemeHandlerTest.php
@@ -36,6 +36,10 @@ public function containerBuild(ContainerBuilder $container) {
function setUp() {
parent::setUp();
$this->installConfig(array('system'));
+
+ // Reset the available themes to test the case of no themes.
+ $this->extensionConfig()->set('theme', array());
+ $this->extensionConfig()->set('disabled.theme', array());
}
/**
@@ -51,8 +55,17 @@ function testEmpty() {
// Rebuilding available themes should always yield results though.
$this->assertTrue($this->themeHandler()->rebuildThemeData()['stark'], 'ThemeHandler::rebuildThemeData() yields all available themes.');
- // theme_get_setting() should return global default theme settings.
- $this->assertIdentical(theme_get_setting('features.favicon'), TRUE);
+ try {
+ // theme_get_setting() should fail, as no theme is available.
+ $this->assertIdentical(theme_get_setting('features.favicon'), TRUE);
+ $this->fail('No theme should lead to an exception');
+ }
+ catch (\RuntimeException $e) {
+ $this->assertEqual($e->getMessage(), 'No theme is enabled.');
+ }
+ catch (\Exception $e) {
+ $this->fail('The runtime exception should be thrown.');
+ }
}
/**
diff --git a/core/modules/system/src/Tests/Theme/ThemeTest.php b/core/modules/system/src/Tests/Theme/ThemeTest.php
index d41ec8d..685e7c4 100644
--- a/core/modules/system/src/Tests/Theme/ThemeTest.php
+++ b/core/modules/system/src/Tests/Theme/ThemeTest.php
@@ -139,14 +139,6 @@ function testFrontPageThemeSuggestion() {
}
/**
- * Ensures theme hook_*_alter() implementations can run before anything is rendered.
- */
- function testAlter() {
- $this->drupalGet('theme-test/alter');
- $this->assertText('The altered data is test_theme_theme_test_alter_alter was invoked.', 'The theme was able to implement an alter hook during page building before anything was rendered.');
- }
-
- /**
* Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
*
* @see test_theme.info.yml
@@ -220,7 +212,7 @@ function testListThemes() {
*/
function testThemeGetSetting() {
$this->container->get('theme_handler')->enable(array('test_subtheme'));
- $GLOBALS['theme_key'] = 'test_theme';
+ \Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->initTheme('test_theme'));
$this->assertIdentical(theme_get_setting('theme_test_setting'), 'default value', 'theme_get_setting() uses the default theme automatically.');
$this->assertNotEqual(theme_get_setting('subtheme_override', 'test_basetheme'), theme_get_setting('subtheme_override', 'test_subtheme'), 'Base theme\'s default settings values can be overridden by subtheme.');
$this->assertIdentical(theme_get_setting('basetheme_only', 'test_subtheme'), 'base theme value', 'Base theme\'s default settings values are inherited by subtheme.');
diff --git a/core/modules/system/src/Tests/Theme/TwigSettingsTest.php b/core/modules/system/src/Tests/Theme/TwigSettingsTest.php
index 630fc14..277eef5 100644
--- a/core/modules/system/src/Tests/Theme/TwigSettingsTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigSettingsTest.php
@@ -76,8 +76,7 @@ function testTwigCacheOverride() {
$theme_handler->setDefault('test_theme');
// The registry still works on theme globals, so set them here.
- $GLOBALS['theme'] = 'test_theme';
- $GLOBALS['theme_info'] = $theme_handler->listInfo()['test_theme'];
+ \Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->getActiveThemeByName('test_theme'));
// Reset the theme registry, so that the new theme is used.
$this->container->set('theme.registry', NULL);
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index f421e6b..5d3b98f 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1657,7 +1657,7 @@ function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $repl
function system_page_alter(&$page) {
// Find all non-empty page regions, and add a theme wrapper function that
// allows them to be consistently themed.
- $regions = system_region_list($GLOBALS['theme']);
+ $regions = system_region_list(\Drupal::theme()->getActiveTheme()->getName());
foreach (array_keys($regions) as $region) {
if (!empty($page[$region])) {
$page[$region]['#theme_wrappers'][] = 'region';
diff --git a/core/modules/system/tests/modules/batch_test/batch_test.module b/core/modules/system/tests/modules/batch_test/batch_test.module
index 9290ef8..bc22b92 100644
--- a/core/modules/system/tests/modules/batch_test/batch_test.module
+++ b/core/modules/system/tests/modules/batch_test/batch_test.module
@@ -162,7 +162,7 @@ function _batch_test_theme_callback() {
// theme is being used on the batch processing page by viewing that page
// directly. Instead, we save the theme being used in a variable here, so
// that it can be loaded and inspected in the thread running the test.
- global $theme;
+ $theme = \Drupal::theme()->getActiveTheme()->getName();
batch_test_stack($theme);
}
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index b4f76a2..a5ce74e 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -110,9 +110,7 @@ function menu_test_callback() {
* @deprecated Use \Drupal\menu_test\Controller\MenuTestController::themePage()
*/
function menu_test_theme_page_callback($inherited = FALSE) {
- global $theme_key;
- // Initialize the theme system so that $theme_key will be populated.
- drupal_theme_initialize();
+ $theme_key = \Drupal::theme()->getActiveTheme()->getName();
// Now we check what the theme negotiator service returns.
$active_theme = \Drupal::service('theme.negotiator')->determineActiveTheme(\Drupal::routeMatch());
$output = "Active theme: $active_theme. Actual theme: $theme_key.";
diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
index 08c07b7..ff02186 100644
--- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -328,7 +328,7 @@ public function generateOutputKey() {
'result' => $this->view->result,
'roles' => $user->getRoles(),
'super-user' => $user->id() == 1, // special caching for super user.
- 'theme' => $GLOBALS['theme'],
+ 'theme' => \Drupal::theme()->getActiveTheme()->getName(),
'langcode' => \Drupal::languageManager()->getCurrentLanguage()->id,
'base_url' => $GLOBALS['base_url'],
);
diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php
index c7e810b..e244c1c 100644
--- a/core/modules/views/src/ViewExecutable.php
+++ b/core/modules/views/src/ViewExecutable.php
@@ -1299,6 +1299,12 @@ public function render($display_id = NULL) {
$module_handler = \Drupal::moduleHandler();
+ // @TODO on the longrun it would be great to execute a view without
+ // the theme system at all.
+ $active_theme = \Drupal::theme()->getActiveTheme();
+ $themes = array_keys($active_theme->getBaseThemes());
+ $themes[] = $active_theme->getName();
+
// Check for already-cached output.
if (!empty($this->live_preview)) {
$cache = FALSE;
@@ -1355,12 +1361,11 @@ public function render($display_id = NULL) {
$module_handler->invokeAll('views_pre_render', array($this));
// Let the themes play too, because pre render is a very themey thing.
- if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) {
- foreach ($GLOBALS['base_theme_info'] as $base) {
- $module_handler->invoke($base->getName(), 'views_pre_render', array($this));
+ foreach ($themes as $theme_name) {
+ $function = $theme_name . '_views_pre_render';
+ if (function_exists($function)) {
+ $function($this);
}
-
- $module_handler->invoke($GLOBALS['theme'], 'views_pre_render', array($this));
}
$this->display_handler->output = $this->display_handler->render();
@@ -1379,12 +1384,11 @@ public function render($display_id = NULL) {
$module_handler->invokeAll('views_post_render', array($this, &$this->display_handler->output, $cache));
// Let the themes play too, because post render is a very themey thing.
- if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) {
- foreach ($GLOBALS['base_theme_info'] as $base) {
- $module_handler->invoke($base->getName(), 'views_post_render', array($this));
+ foreach ($themes as $theme_name) {
+ $function = $theme_name . '_views_post_render';
+ if (function_exists($function)) {
+ $function($this);
}
-
- $module_handler->invoke($GLOBALS['theme'], 'views_post_render', array($this));
}
return $this->display_handler->output;
diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
index 08eaa31..80e5a6d 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
@@ -134,6 +134,13 @@
*/
protected $httpKernel;
+ /**
+ * The mocked theme manager.
+ *
+ * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $themeManager;
+
public function setUp() {
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
@@ -158,6 +165,7 @@ public function setUp() {
->disableOriginalConstructor()
->getMock();
$this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
+ $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
$this->request = new Request();
$this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
$this->requestStack = new RequestStack();
@@ -171,7 +179,7 @@ public function setUp() {
->setMethods(array('batchGet', 'drupalInstallationAttempted'))
->getMock();
- $this->formBuilder = new TestFormBuilder($this->formValidator, $this->formSubmitter, $this->moduleHandler, $this->keyValueExpirableFactory, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->csrfToken, $this->httpKernel);
+ $this->formBuilder = new TestFormBuilder($this->formValidator, $this->formSubmitter, $this->moduleHandler, $this->keyValueExpirableFactory, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->themeManager, $this->csrfToken, $this->httpKernel);
$this->formBuilder->setCurrentUser($this->account);
}