diff --git a/core/core.services.yml b/core/core.services.yml index ac24e17..d548714 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -684,6 +684,11 @@ services: class: Zend\Feed\Writer\Extension\Threading\Renderer\Entry feed.writer.wellformedwebrendererentry: class: Zend\Feed\Writer\Extension\WellFormedWeb\Renderer\Entry + theme.initialization: + class: Drupal\Core\Theme\Initialization + arguments: ['@theme_handler', '@theme.negotiator'] + calls: + - [setRequest, ['@?request=']] theme.registry: class: Drupal\Core\Theme\Registry arguments: ['@cache.cache', '@lock', '@module_handler'] diff --git a/core/includes/common.inc b/core/includes/common.inc index d18c0c1..def17de 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3206,7 +3206,7 @@ function _drupal_bootstrap_full($skip = FALSE) { // Let all modules take action before the menu system handles the request. // We do not want this while running update.php. if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); } } diff --git a/core/includes/form.inc b/core/includes/form.inc index f4a7718..a275977 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -3271,7 +3271,7 @@ function batch_set($batch_definition) { function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = NULL) { $batch =& batch_get(); - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); if (isset($batch)) { // Add process information diff --git a/core/includes/theme.inc b/core/includes/theme.inc index cfd8427..03b665f 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -88,189 +88,7 @@ function drupal_theme_access($theme) { * Initializes the theme system by loading the theme. */ function drupal_theme_initialize() { - 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(); - - // @todo Let the theme.negotiator listen to the kernel request event. - // 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. - $request = \Drupal::request(); - $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($request) ?: 'stark'; - - // 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 scripts - $final_scripts = array(); - - // Grab scripts from base theme - foreach ($base_theme as $base) { - if (!empty($base->scripts)) { - foreach ($base->scripts as $name => $script) { - $final_scripts[$name] = $script; - } - } - } - - // Add scripts used by this theme. - if (!empty($theme->scripts)) { - foreach ($theme->scripts as $name => $script) { - $final_scripts[$name] = $script; - } - } - - // Add scripts used by this theme. - $js = array(); - foreach ($final_scripts as $script) { - $js['#attached']['js'][] = array( - 'data' => $script, - 'group' => JS_THEME, - 'every_page' => TRUE, - ); - } - drupal_render($js); - - $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'; + return \Drupal::service('theme.initialization')->initThemeSystem(); } /** @@ -647,7 +465,7 @@ function path_to_theme() { global $theme_path; if (!isset($theme_path)) { - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); } return $theme_path; diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc index 429b932..c4ab914 100644 --- a/core/includes/theme.maintenance.inc +++ b/core/includes/theme.maintenance.inc @@ -96,7 +96,7 @@ 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)); + \Drupal::service('theme.initialization')->initThemeSystemWithLoadedInformation($themes[$theme], array_reverse($base_theme)); // Prime the theme registry. // @todo Remove global theme variables. Drupal::service('theme.registry'); diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php index aa7774e..fc5720b 100644 --- a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php @@ -44,7 +44,7 @@ public function onKernelRequestLegacy(GetResponseEvent $event) { */ public function onKernelRequestLegacyAfterRouting(GetResponseEvent $event) { if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); } } diff --git a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php index 22b5863..0ba9757 100644 --- a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php +++ b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php @@ -16,11 +16,11 @@ class DefaultNegotiator implements ThemeNegotiatorInterface { /** - * The system theme config object. + * The config factory * - * @var \Drupal\Core\Config\Config + * @var \Drupal\Core\Config\ConfigFactory */ - protected $config; + protected $configFactory; /** * Constructs a DefaultNegotiator object. @@ -29,7 +29,7 @@ class DefaultNegotiator implements ThemeNegotiatorInterface { * The config factory. */ public function __construct(ConfigFactoryInterface $config_factory) { - $this->config = $config_factory->get('system.theme'); + $this->configFactory = $config_factory; } /** @@ -43,7 +43,7 @@ public function applies(Request $request) { * {@inheritdoc} */ public function determineActiveTheme(Request $request) { - return $this->config->get('default'); + return $this->configFactory->get('system.theme')->get('default'); } } diff --git a/core/lib/Drupal/Core/Theme/Initialization.php b/core/lib/Drupal/Core/Theme/Initialization.php new file mode 100644 index 0000000..22ce3f9 --- /dev/null +++ b/core/lib/Drupal/Core/Theme/Initialization.php @@ -0,0 +1,293 @@ +themeHandler = $theme_handler; + $this->themeNegotiator = $theme_negotiator; + } + + /** + * Sets the current theme. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + */ + public function setRequest(Request $request) { + $this->request = $request; + } + + /** + * Initializes the theme system. + * + * This method uses the theme negotiator to figure out the active theme. + */ + public function initThemeSystem() { + global $theme, $theme_key; + + // If $theme is already set, assume the others are set, too, and do nothing + if (isset($theme)) { + return; + } + + $themes = $this->themeHandler->listInfo(); + + // @todo Let the theme.negotiator listen to the kernel request event. + // 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. + $theme = $this->themeNegotiator->determineActiveTheme($this->request) ?: 'stark'; + + // 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]; + } + $this->initThemeSystemWithLoadedInformation($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 \stdClass $theme + * An object with the following information: + * filename + * The .info.yml file for this theme. The 'path' to + * the theme will be in this file's directory. (Required) + * owner + * The path to the .theme file or the .engine file to load for + * the theme. (Required) + * stylesheet + * The primary stylesheet for the theme. (Optional) + * engine + * The name of theme engine to use. (Optional) + * @param \stdClass[] $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. + */ + public function initThemeSystemWithLoadedInformation($theme, $base_theme = array()) { + $this->addAssets($theme, $base_theme); + $this->loadIncludes($theme, $base_theme); + } + + /** + * Adds all assets onto the response. + * + * @param \stdClass $theme + * The theme object + * + * @param \stdClass[] $base_theme + * A list of theme objects of all base themes. + * + * @see \Drupal\Core\Theme\Initialization::initThemeSystemWithLoadedInformation() + */ + protected function addAssets($theme, $base_theme = array()) { + global $theme_info, $base_theme_info, $theme_path; + $theme_info = $theme; + $base_theme_info = $base_theme; + + $theme_path = dirname($theme->filename); + + // 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 = dirname($base->filename); + 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 scripts + $final_scripts = array(); + + // Grab scripts from base theme + foreach ($base_theme as $base) { + if (!empty($base->scripts)) { + foreach ($base->scripts as $name => $script) { + $final_scripts[$name] = $script; + } + } + } + + // Add scripts used by this theme. + if (!empty($theme->scripts)) { + foreach ($theme->scripts as $name => $script) { + $final_scripts[$name] = $script; + } + } + + // Add scripts used by this theme. + $js = array(); + foreach ($final_scripts as $script) { + $js['#attached']['js'][] = array( + 'data' => $script, + 'group' => JS_THEME, + 'every_page' => TRUE, + ); + } + drupal_render($js); + } + + /** + * Load the .theme file as well as the theme engine. + * + * @param \stdClass $theme + * The theme object. + * + * @param \stdClass[] $base_theme + * A list of theme objects of all base themes. + * + * @see \Drupal\Core\Theme\Initialization::initThemeSystemWithLoadedInformation() + */ + protected function loadIncludes($theme, array $base_theme = array()) { + global $theme_engine; + + $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'; + } + +} diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php index e84b33b..38e80b3 100644 --- a/core/lib/Drupal/Core/Theme/Registry.php +++ b/core/lib/Drupal/Core/Theme/Registry.php @@ -150,7 +150,7 @@ protected function init($theme_name = NULL) { if (!isset($GLOBALS['theme_info']) || !isset($GLOBALS['theme'])) { unset($this->runtimeRegistry); unset($this->registry); - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); } // #2: The testing framework only cares for the global $theme variable at // this point. Cope with that. @@ -589,7 +589,7 @@ protected function listThemes() { * Wraps drupal_theme_initialize(). */ protected function initializeTheme() { - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); } } diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 1bca18b..6f88d8b 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -115,7 +115,7 @@ function block_page_build(&$page) { global $theme; // The theme system might not yet be initialized. We need $theme. - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); // Fetch a list of regions for the current theme. $all_regions = system_region_list($theme); diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php index 4c282d8..2c7eae1 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php @@ -39,7 +39,7 @@ function setUp() { parent::setUp(); // There are dependencies in drupal_get_js() on the theme layer so we need // to initialize it. - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); // Disable preprocessing $config = \Drupal::config('system.performance'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php index b2d7eb0..62a86ee 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php @@ -34,7 +34,7 @@ function setUp() { parent::setUp(); // There are dependencies in drupal_get_js() on the theme layer so we need // to initialize it. - drupal_theme_initialize(); + \Drupal::service('theme.initialization')->initThemeSystem(); } /** 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 08e6382..3daaab1 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.module +++ b/core/modules/system/tests/modules/menu_test/menu_test.module @@ -264,7 +264,7 @@ function menu_test_custom_403_404_callback() { 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(); + \Drupal::service('theme.initialization')->initThemeSystem(); // Now we check what the theme negotiator service returns. $active_theme = \Drupal::service('theme.negotiator')->getActiveTheme('getActiveTheme'); $output = "Active theme: $active_theme. Actual theme: $theme_key.";