diff --git a/core/core.services.yml b/core/core.services.yml index 6134359..10cd1e5 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -662,6 +662,11 @@ services: class: Zend\Feed\Writer\Extension\Threading\Renderer\Entry feed.writer.wellformedwebrendererentry: class: Zend\Feed\Writer\Extension\WellFormedWeb\Renderer\Entry + theme.initialisation: + 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/install.core.inc b/core/includes/install.core.inc index 6dfc270..c3aea66 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -417,6 +417,17 @@ function install_begin_request(&$install_state) { ->addArgument(new Reference('module_handler')) ->addTag('needs_destruction'); + $container + ->register('theme.initialisation', 'Drupal\Core\Theme\Initialisation') + ->addArgument(new Reference('theme_handler')) + ->addArgument(new Reference('theme.negotiator')); + $container + ->register('theme.negotiator', 'Drupal\Core\Theme\ThemeNegotiator') + ->addArgument(new Reference('access_check.theme')) + ->setMethodCalls(array(array('setRequest', array(new Reference('request'))))); + $container + ->register('access_check.theme', 'Drupal\Core\Theme\ThemeAccessCheck'); + // Register a module handler for managing enabled modules. $container ->register('module_handler', 'Drupal\Core\Extension\ModuleHandler'); diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 46113c6..8d1999d 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -84,199 +84,7 @@ function drupal_theme_access($theme) { * Initializes the theme system by loading the theme. */ function drupal_theme_initialize() { - global $theme, $user, $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 $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 $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 = 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); - - $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'; + \Drupal::service('theme.initialisation')->initThemeSystem(); } /** diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc index 96dcd2f..e5826e3 100644 --- a/core/includes/theme.maintenance.inc +++ b/core/includes/theme.maintenance.inc @@ -90,8 +90,8 @@ 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), '_theme_load_offline_registry'); - _drupal_theme_initialize($themes[$theme], array_reverse($base_theme)); + \Drupal::service('theme.initialisation')->addAssets($themes[$theme], array_reverse($base_theme)); + \Drupal::service('theme.initialisation')->loadIncludes($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/Theme/DefaultNegotiator.php b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php index b08882d..94dc4e6 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(ConfigFactory $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'; + } + +}