Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
201
core/lib/Drupal/Core/Theme/ActiveTheme.php
Normal file
201
core/lib/Drupal/Core/Theme/ActiveTheme.php
Normal file
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ActiveTheme.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
/**
|
||||
* Defines a theme and its information needed at runtime.
|
||||
*
|
||||
* The theme manager will store the active theme object.
|
||||
*
|
||||
* @see \Drupal\Core\Theme\ThemeManager
|
||||
* @see \Drupal\Core\Theme\ThemeInitialization
|
||||
*/
|
||||
class ActiveTheme {
|
||||
|
||||
/**
|
||||
* The machine name of the active theme.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* The path to the theme.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The engine of the theme.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $engine;
|
||||
|
||||
/**
|
||||
* The path to the theme engine for root themes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $owner;
|
||||
|
||||
/**
|
||||
* An array of base theme active theme objects keyed by name.
|
||||
*
|
||||
* @var static[]
|
||||
*/
|
||||
protected $baseThemes;
|
||||
|
||||
/**
|
||||
* The extension object.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\Extension
|
||||
*/
|
||||
protected $extension;
|
||||
|
||||
/**
|
||||
* The stylesheets which are set to be removed by the theme.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $styleSheetsRemove;
|
||||
|
||||
/**
|
||||
* The libraries provided by the theme.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $libraries;
|
||||
|
||||
/**
|
||||
* The regions provided by the theme.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $regions;
|
||||
|
||||
/**
|
||||
* Constructs an ActiveTheme object.
|
||||
*
|
||||
* @param array $values
|
||||
* The properties of the object, keyed by the names.
|
||||
*/
|
||||
public function __construct(array $values) {
|
||||
$values += [
|
||||
'path' => '',
|
||||
'engine' => 'twig',
|
||||
'owner' => 'twig',
|
||||
'stylesheets_remove' => [],
|
||||
'libraries' => [],
|
||||
'extension' => 'html.twig',
|
||||
'base_themes' => [],
|
||||
'regions' => [],
|
||||
];
|
||||
|
||||
$this->name = $values['name'];
|
||||
$this->path = $values['path'];
|
||||
$this->engine = $values['engine'];
|
||||
$this->owner = $values['owner'];
|
||||
$this->styleSheetsRemove = $values['stylesheets_remove'];
|
||||
$this->libraries = $values['libraries'];
|
||||
$this->extension = $values['extension'];
|
||||
$this->baseThemes = $values['base_themes'];
|
||||
$this->regions = $values['regions'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 theme engine.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEngine() {
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the theme engine for root themes.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ThemeHandler::rebuildThemeData
|
||||
*
|
||||
* @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 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.
|
||||
*
|
||||
* The order starts with the base theme of $this and ends with the root of
|
||||
* the dependency chain.
|
||||
*
|
||||
* @return static[]
|
||||
*/
|
||||
public function getBaseThemes() {
|
||||
return $this->baseThemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* The regions used by the theme.
|
||||
*
|
||||
* @return string[]
|
||||
* The list of region machine names supported by the theme.
|
||||
*
|
||||
* @see system_region_list()
|
||||
*/
|
||||
public function getRegions() {
|
||||
return array_keys($this->regions);
|
||||
}
|
||||
|
||||
}
|
100
core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
Normal file
100
core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\AjaxBasePageNegotiator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Access\CsrfTokenGenerator;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Defines a theme negotiator that deals with the active theme on ajax requests.
|
||||
*
|
||||
* Many different pages can invoke an Ajax request to system/ajax or another
|
||||
* generic Ajax path. It is almost always desired for an Ajax response to be
|
||||
* rendered using the same theme as the base page, because most themes are built
|
||||
* with the assumption that they control the entire page, so if the CSS for two
|
||||
* themes are both loaded for a given page, they may conflict with each other.
|
||||
* For example, Bartik is Drupal's default theme, and Seven is Drupal's default
|
||||
* administration theme. Depending on whether the "Use the administration theme
|
||||
* when editing or creating content" checkbox is checked, the node edit form may
|
||||
* be displayed in either theme, but the Ajax response to the Field module's
|
||||
* "Add another item" button should be rendered using the same theme as the rest
|
||||
* of the page.
|
||||
*
|
||||
* Therefore specify '_theme: ajax_base_page' as part of the router options.
|
||||
*/
|
||||
class AjaxBasePageNegotiator implements ThemeNegotiatorInterface {
|
||||
|
||||
/**
|
||||
* The CSRF token generator.
|
||||
*
|
||||
* @var \Drupal\Core\Access\CsrfTokenGenerator
|
||||
*/
|
||||
protected $csrfGenerator;
|
||||
|
||||
/**
|
||||
* The config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* Constructs a new AjaxBasePageNegotiator.
|
||||
*
|
||||
* @param \Drupal\Core\Access\CsrfTokenGenerator $token_generator
|
||||
* The CSRF token generator.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack used to retrieve the current request.
|
||||
*/
|
||||
public function __construct(CsrfTokenGenerator $token_generator, ConfigFactoryInterface $config_factory, RequestStack $request_stack) {
|
||||
$this->csrfGenerator = $token_generator;
|
||||
$this->configFactory = $config_factory;
|
||||
$this->requestStack = $request_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies(RouteMatchInterface $route_match) {
|
||||
// Check whether the route was configured to use the base page theme.
|
||||
return ($route = $route_match->getRouteObject())
|
||||
&& $route->hasOption('_theme')
|
||||
&& $route->getOption('_theme') == 'ajax_base_page';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match) {
|
||||
if (($ajax_page_state = $this->requestStack->getCurrentRequest()->request->get('ajax_page_state')) && !empty($ajax_page_state['theme']) && !empty($ajax_page_state['theme_token'])) {
|
||||
$theme = $ajax_page_state['theme'];
|
||||
$token = $ajax_page_state['theme_token'];
|
||||
|
||||
// Prevent a request forgery from giving a person access to a theme they
|
||||
// shouldn't be otherwise allowed to see. However, since everyone is
|
||||
// allowed to see the default theme, token validation isn't required for
|
||||
// that, and bypassing it allows most use-cases to work even when accessed
|
||||
// from the page cache.
|
||||
if ($theme === $this->configFactory->get('system.theme')->get('default') || $this->csrfGenerator->validate($token, $theme)) {
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
49
core/lib/Drupal/Core/Theme/DefaultNegotiator.php
Normal file
49
core/lib/Drupal/Core/Theme/DefaultNegotiator.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\DefaultNegotiator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Determines the default theme of the site.
|
||||
*/
|
||||
class DefaultNegotiator implements ThemeNegotiatorInterface {
|
||||
|
||||
/**
|
||||
* The system theme config object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Constructs a DefaultNegotiator object.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory) {
|
||||
$this->configFactory = $config_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies(RouteMatchInterface $route_match) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match) {
|
||||
return $this->configFactory->get('system.theme')->get('default');
|
||||
}
|
||||
|
||||
}
|
603
core/lib/Drupal/Core/Theme/Registry.php
Normal file
603
core/lib/Drupal/Core/Theme/Registry.php
Normal file
|
@ -0,0 +1,603 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\Registry.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\DestructableInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
use Drupal\Core\Utility\ThemeRegistry;
|
||||
|
||||
/**
|
||||
* Defines the theme registry service.
|
||||
*
|
||||
* @todo Replace local $registry variables in methods with $this->registry.
|
||||
*/
|
||||
class Registry implements DestructableInterface {
|
||||
|
||||
/**
|
||||
* The theme object representing the active theme for this registry.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ActiveTheme
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* The lock backend that should be used.
|
||||
*
|
||||
* @var \Drupal\Core\Lock\LockBackendInterface
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
/**
|
||||
* The complete theme registry.
|
||||
*
|
||||
* @var array
|
||||
* An associative array keyed by theme hook names, whose values are
|
||||
* associative arrays containing the aggregated hook definition:
|
||||
* - type: The type of the extension the original theme hook originates
|
||||
* from; e.g., 'module' for theme hook 'node' of Node module.
|
||||
* - name: The name of the extension the original theme hook originates
|
||||
* from; e.g., 'node' for theme hook 'node' of Node module.
|
||||
* - theme path: The effective \Drupal\Core\Theme\ActiveTheme::getPath()
|
||||
* during _theme(), available as
|
||||
* 'directory' variable in templates. For functions, it should point to
|
||||
* the respective theme.For templates, it should point to the directory
|
||||
* that contains the template.
|
||||
* - includes: (optional) An array of include files to load when the theme
|
||||
* hook is executed by _theme().
|
||||
* - file: (optional) A filename to add to 'includes', either prefixed with
|
||||
* the value of 'path', or the path of the extension implementing
|
||||
* hook_theme().
|
||||
* In case of a theme base hook, one of the following:
|
||||
* - variables: An associative array whose keys are variable names and whose
|
||||
* values are default values of the variables to use for this theme hook.
|
||||
* - render element: A string denoting the name of the variable name, in
|
||||
* which the render element for this theme hook is provided.
|
||||
* In case of a theme template file:
|
||||
* - path: The path to the template file to use. Defaults to the
|
||||
* subdirectory 'templates' of the path of the extension implementing
|
||||
* hook_theme(); e.g., 'core/modules/node/templates' for Node module.
|
||||
* - template: The basename of the template file to use, without extension
|
||||
* (as the extension is specific to the theme engine). The template file
|
||||
* is in the directory defined by 'path'.
|
||||
* - template_file: A full path and file name to a template file to use.
|
||||
* Allows any extension to override the effective template file.
|
||||
* - engine: The theme engine to use for the template file.
|
||||
* In case of a theme function:
|
||||
* - function: The function name to call to generate the output.
|
||||
* For any registered theme hook, including theme hook suggestions:
|
||||
* - preprocess: An array of theme variable preprocess callbacks to invoke
|
||||
* before invoking final theme variable processors.
|
||||
* - process: An array of theme variable process callbacks to invoke
|
||||
* before invoking the actual theme function or template.
|
||||
*/
|
||||
protected $registry;
|
||||
|
||||
/**
|
||||
* The cache backend to use for the complete theme registry data.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The module handler to use to load modules.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The incomplete, runtime theme registry.
|
||||
*
|
||||
* @var \Drupal\Core\Utility\ThemeRegistry
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\Core\Theme\Registry object.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||
* The cache backend interface to use for the complete theme registry data.
|
||||
* @param \Drupal\Core\Lock\LockBackendInterface $lock
|
||||
* The lock backend.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to use to load modules.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Theme\ThemeInitializationInterface $theme_initialization
|
||||
* The theme initialization.
|
||||
* @param string $theme_name
|
||||
* (optional) The name of the theme for which to construct the registry.
|
||||
*/
|
||||
public function __construct($root, CacheBackendInterface $cache, LockBackendInterface $lock, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, ThemeInitializationInterface $theme_initialization, $theme_name = NULL) {
|
||||
$this->root = $root;
|
||||
$this->cache = $cache;
|
||||
$this->lock = $lock;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->themeName = $theme_name;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->themeInitialization = $theme_initialization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme manager.
|
||||
*
|
||||
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
|
||||
* The theme manager.
|
||||
*/
|
||||
public function setThemeManager(ThemeManagerInterface $theme_manager) {
|
||||
$this->themeManager = $theme_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a theme with a certain name.
|
||||
*
|
||||
* This function does to much magic, so it should be replaced by another
|
||||
* services which holds the current active theme information.
|
||||
*
|
||||
* @param string $theme_name
|
||||
* (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)) {
|
||||
$this->theme = $this->themeManager->getActiveTheme();
|
||||
}
|
||||
// Instead of the active theme, a specific theme was requested.
|
||||
else {
|
||||
$this->theme = $this->themeInitialization->getActiveThemeByName($theme_name);
|
||||
$this->themeInitialization->loadActiveTheme($this->theme);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete theme registry from cache or rebuilds it.
|
||||
*
|
||||
* @return array
|
||||
* The complete theme registry data array.
|
||||
*
|
||||
* @see Registry::$registry
|
||||
*/
|
||||
public function get() {
|
||||
$this->init($this->themeName);
|
||||
if (isset($this->registry)) {
|
||||
return $this->registry;
|
||||
}
|
||||
if ($cache = $this->cache->get('theme_registry:' . $this->theme->getName())) {
|
||||
$this->registry = $cache->data;
|
||||
}
|
||||
else {
|
||||
$this->registry = $this->build();
|
||||
// Only persist it if all modules are loaded to ensure it is complete.
|
||||
if ($this->moduleHandler->isLoaded()) {
|
||||
$this->setCache();
|
||||
}
|
||||
}
|
||||
return $this->registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the incomplete, runtime theme registry.
|
||||
*
|
||||
* @return \Drupal\Core\Utility\ThemeRegistry
|
||||
* A shared instance of the ThemeRegistry class, provides an ArrayObject
|
||||
* that allows it to be accessed with array syntax and isset(), and is more
|
||||
* 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'), $this->moduleHandler->isLoaded());
|
||||
}
|
||||
return $this->runtimeRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the theme registry in the cache backend.
|
||||
*/
|
||||
protected function setCache() {
|
||||
$this->cache->set('theme_registry:' . $this->theme->getName(), $this->registry, Cache::PERMANENT, array('theme_registry'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base hook for a given hook suggestion.
|
||||
*
|
||||
* @param string $hook
|
||||
* The name of a theme hook whose base hook to find.
|
||||
*
|
||||
* @return string|false
|
||||
* 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
|
||||
// not supported, so the base hook must be an original implementation that
|
||||
// points to a theme function or template.
|
||||
while ($pos = strrpos($base_hook, '__')) {
|
||||
$base_hook = substr($base_hook, 0, $pos);
|
||||
if (isset($this->registry[$base_hook]['exists'])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($pos !== FALSE && $base_hook !== $hook) {
|
||||
return $base_hook;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the theme registry cache.
|
||||
*
|
||||
* Theme hook definitions are collected in the following order:
|
||||
* - Modules
|
||||
* - Base theme engines
|
||||
* - Base themes
|
||||
* - Theme engine
|
||||
* - Theme
|
||||
*
|
||||
* All theme hook definitions are essentially just collated and merged in the
|
||||
* above order. However, various extension-specific default values and
|
||||
* customizations are required; e.g., to record the effective file path for
|
||||
* theme template. Therefore, this method first collects all extensions per
|
||||
* type, and then dispatches the processing for each extension to
|
||||
* processExtension().
|
||||
*
|
||||
* After completing the collection, modules are allowed to alter it. Lastly,
|
||||
* any derived and incomplete theme hook definitions that are hook suggestions
|
||||
* for base hooks (e.g., 'block__node' for the base hook 'block') need to be
|
||||
* determined based on the full registry and classified as 'base hook'.
|
||||
*
|
||||
* See the @link themeable Default theme implementations topic @endlink for
|
||||
* details.
|
||||
*
|
||||
* @return \Drupal\Core\Utility\ThemeRegistry
|
||||
* The build theme registry.
|
||||
*
|
||||
* @see hook_theme_registry_alter()
|
||||
*/
|
||||
protected function build() {
|
||||
$cache = array();
|
||||
// First, preprocess the theme hooks advertised by modules. This will
|
||||
// serve as the basic registry. Since the list of enabled modules is the
|
||||
// same regardless of the theme used, this is cached in its own entry to
|
||||
// save building it for every theme.
|
||||
if ($cached = $this->cache->get('theme_registry:build:modules')) {
|
||||
$cache = $cached->data;
|
||||
}
|
||||
else {
|
||||
foreach ($this->moduleHandler->getImplementations('theme') as $module) {
|
||||
$this->processExtension($cache, $module, 'module', $module, $this->getPath($module));
|
||||
}
|
||||
// Only cache this registry if all modules are loaded.
|
||||
if ($this->moduleHandler->isLoaded()) {
|
||||
$this->cache->set("theme_registry:build:modules", $cache, Cache::PERMANENT, array('theme_registry'));
|
||||
}
|
||||
}
|
||||
|
||||
// Process each base theme.
|
||||
// Ensure that we start with the root of the parents, so that both CSS files
|
||||
// and preprocess functions comes first.
|
||||
foreach (array_reverse($this->theme->getBaseThemes()) as $base) {
|
||||
// If the base theme uses a theme engine, process its hooks.
|
||||
$base_path = $base->getPath();
|
||||
if ($this->theme->getEngine()) {
|
||||
$this->processExtension($cache, $this->theme->getEngine(), 'base_theme_engine', $base->getName(), $base_path);
|
||||
}
|
||||
$this->processExtension($cache, $base->getName(), 'base_theme', $base->getName(), $base_path);
|
||||
}
|
||||
|
||||
// And then the same thing, but for the theme.
|
||||
if ($this->theme->getEngine()) {
|
||||
$this->processExtension($cache, $this->theme->getEngine(), 'theme_engine', $this->theme->getName(), $this->theme->getPath());
|
||||
}
|
||||
|
||||
// Finally, hooks provided by the theme itself.
|
||||
$this->processExtension($cache, $this->theme->getName(), 'theme', $this->theme->getName(), $this->theme->getPath());
|
||||
|
||||
// Let modules and themes alter the registry.
|
||||
$this->moduleHandler->alter('theme_registry', $cache);
|
||||
$this->themeManager->alterForTheme($this->theme, 'theme_registry', $cache);
|
||||
|
||||
// @todo Implement more reduction of the theme registry entry.
|
||||
// Optimize the registry to not have empty arrays for functions.
|
||||
foreach ($cache as $hook => $info) {
|
||||
if (empty($info['preprocess functions'])) {
|
||||
unset($cache[$hook]['preprocess functions']);
|
||||
}
|
||||
}
|
||||
$this->registry = $cache;
|
||||
|
||||
return $this->registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single implementation of hook_theme().
|
||||
*
|
||||
* @param array $cache
|
||||
* The theme registry that will eventually be cached; It is an associative
|
||||
* array keyed by theme hooks, whose values are associative arrays
|
||||
* describing the hook:
|
||||
* - 'type': The passed-in $type.
|
||||
* - 'theme path': The passed-in $path.
|
||||
* - 'function': The name of the function generating output for this theme
|
||||
* hook. Either defined explicitly in hook_theme() or, if neither
|
||||
* 'function' nor 'template' is defined, then the default theme function
|
||||
* name is used. The default theme function name is the theme hook
|
||||
* prefixed by either 'theme_' for modules or '$name_' for everything
|
||||
* else. If 'function' is defined, 'template' is not used.
|
||||
* - 'template': The filename of the template generating output for this
|
||||
* theme hook. The template is in the directory defined by the 'path' key
|
||||
* of hook_theme() or defaults to "$path/templates".
|
||||
* - 'variables': The variables for this theme hook as defined in
|
||||
* hook_theme(). If there is more than one implementation and 'variables'
|
||||
* is not specified in a later one, then the previous definition is kept.
|
||||
* - 'render element': The renderable element for this theme hook as defined
|
||||
* in hook_theme(). If there is more than one implementation and
|
||||
* 'render element' is not specified in a later one, then the previous
|
||||
* definition is kept.
|
||||
* - 'preprocess functions': See _theme() for detailed documentation.
|
||||
* @param string $name
|
||||
* The name of the module, theme engine, base theme engine, theme or base
|
||||
* theme implementing hook_theme().
|
||||
* @param string $type
|
||||
* One of 'module', 'theme_engine', 'base_theme_engine', 'theme', or
|
||||
* 'base_theme'. Unlike regular hooks that can only be implemented by
|
||||
* modules, each of these can implement hook_theme(). This function is
|
||||
* called in aforementioned order and new entries override older ones. For
|
||||
* example, if a theme hook is both defined by a module and a theme, then
|
||||
* the definition in the theme will be used.
|
||||
* @param string $theme
|
||||
* The actual name of theme, module, etc. that is being processed.
|
||||
* @param string $path
|
||||
* The directory where $name is. For example, modules/system or
|
||||
* themes/bartik.
|
||||
*
|
||||
* @see \Drupal\Core\Theme\ThemeManagerInterface::render()
|
||||
* @see hook_theme()
|
||||
* @see \Drupal\Core\Extension\ThemeHandler::listInfo()
|
||||
* @see twig_render_template()
|
||||
*
|
||||
* @throws \BadFunctionCallException
|
||||
*/
|
||||
protected function processExtension(array &$cache, $name, $type, $theme, $path) {
|
||||
$result = array();
|
||||
|
||||
$hook_defaults = array(
|
||||
'variables' => TRUE,
|
||||
'render element' => TRUE,
|
||||
'pattern' => TRUE,
|
||||
'base hook' => TRUE,
|
||||
);
|
||||
|
||||
$module_list = array_keys((array) $this->moduleHandler->getModuleList());
|
||||
|
||||
// Invoke the hook_theme() implementation, preprocess what is returned, and
|
||||
// merge it into $cache.
|
||||
$function = $name . '_theme';
|
||||
if (function_exists($function)) {
|
||||
$result = $function($cache, $type, $theme, $path);
|
||||
foreach ($result as $hook => $info) {
|
||||
// When a theme or engine overrides a module's theme function
|
||||
// $result[$hook] will only contain key/value pairs for information being
|
||||
// overridden. Pull the rest of the information from what was defined by
|
||||
// an earlier hook.
|
||||
|
||||
// Fill in the type and path of the module, theme, or engine that
|
||||
// implements this theme function.
|
||||
$result[$hook]['type'] = $type;
|
||||
$result[$hook]['theme path'] = $path;
|
||||
|
||||
if (isset($cache[$hook]['includes'])) {
|
||||
$result[$hook]['includes'] = $cache[$hook]['includes'];
|
||||
}
|
||||
|
||||
// If the theme implementation defines a file, then also use the path
|
||||
// that it defined. Otherwise use the default path. This allows
|
||||
// system.module to declare theme functions on behalf of core .include
|
||||
// files.
|
||||
if (isset($info['file'])) {
|
||||
$include_file = isset($info['path']) ? $info['path'] : $path;
|
||||
$include_file .= '/' . $info['file'];
|
||||
include_once $this->root . '/' . $include_file;
|
||||
$result[$hook]['includes'][] = $include_file;
|
||||
}
|
||||
|
||||
// A template file is the default implementation for a theme hook, but
|
||||
// if the theme hook specifies a function callback instead, check to
|
||||
// ensure the function actually exists.
|
||||
if (isset($info['function']) && !function_exists($info['function'])) {
|
||||
throw new \BadFunctionCallException(sprintf(
|
||||
'Theme hook "%s" refers to a theme function callback that does not exist: "%s"',
|
||||
$hook,
|
||||
$info['function']
|
||||
));
|
||||
}
|
||||
// Provide a default naming convention for 'template' based on the
|
||||
// hook used. If the template does not exist, the theme engine used
|
||||
// should throw an exception at runtime when attempting to include
|
||||
// the template file.
|
||||
elseif (!isset($info['template'])) {
|
||||
$info['template'] = strtr($hook, '_', '-');
|
||||
$result[$hook]['template'] = $info['template'];
|
||||
}
|
||||
|
||||
// Prepend the current theming path when none is set. This is required
|
||||
// for the default theme engine to know where the template lives.
|
||||
if (isset($result[$hook]['template']) && !isset($info['path'])) {
|
||||
$result[$hook]['path'] = $path . '/templates';
|
||||
}
|
||||
|
||||
// If the default keys are not set, use the default values registered
|
||||
// by the module.
|
||||
if (isset($cache[$hook])) {
|
||||
$result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
|
||||
}
|
||||
|
||||
// Preprocess variables for all theming hooks, whether the hook is
|
||||
// implemented as a template or as a function. Ensure they are arrays.
|
||||
if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) {
|
||||
$info['preprocess functions'] = array();
|
||||
$prefixes = array();
|
||||
if ($type == 'module') {
|
||||
// Default variable preprocessor prefix.
|
||||
$prefixes[] = 'template';
|
||||
// Add all modules so they can intervene with their own variable
|
||||
// preprocessors. This allows them to provide variable preprocessors
|
||||
// even if they are not the owner of the current hook.
|
||||
$prefixes = array_merge($prefixes, $module_list);
|
||||
}
|
||||
elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
|
||||
// Theme engines get an extra set that come before the normally
|
||||
// named variable preprocessors.
|
||||
$prefixes[] = $name . '_engine';
|
||||
// The theme engine registers on behalf of the theme using the
|
||||
// theme's name.
|
||||
$prefixes[] = $theme;
|
||||
}
|
||||
else {
|
||||
// This applies when the theme manually registers their own variable
|
||||
// preprocessors.
|
||||
$prefixes[] = $name;
|
||||
}
|
||||
foreach ($prefixes as $prefix) {
|
||||
// Only use non-hook-specific variable preprocessors for theming
|
||||
// hooks implemented as templates. See _theme().
|
||||
if (isset($info['template']) && function_exists($prefix . '_preprocess')) {
|
||||
$info['preprocess functions'][] = $prefix . '_preprocess';
|
||||
}
|
||||
if (function_exists($prefix . '_preprocess_' . $hook)) {
|
||||
$info['preprocess functions'][] = $prefix . '_preprocess_' . $hook;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check for the override flag and prevent the cached variable
|
||||
// preprocessors from being used. This allows themes or theme engines
|
||||
// to remove variable preprocessors set earlier in the registry build.
|
||||
if (!empty($info['override preprocess functions'])) {
|
||||
// Flag not needed inside the registry.
|
||||
unset($result[$hook]['override preprocess functions']);
|
||||
}
|
||||
elseif (isset($cache[$hook]['preprocess functions']) && is_array($cache[$hook]['preprocess functions'])) {
|
||||
$info['preprocess functions'] = array_merge($cache[$hook]['preprocess functions'], $info['preprocess functions']);
|
||||
}
|
||||
$result[$hook]['preprocess functions'] = $info['preprocess functions'];
|
||||
}
|
||||
|
||||
// Merge the newly created theme hooks into the existing cache.
|
||||
$cache = $result + $cache;
|
||||
}
|
||||
|
||||
// Let themes have variable preprocessors even if they didn't register a
|
||||
// template.
|
||||
if ($type == 'theme' || $type == 'base_theme') {
|
||||
foreach ($cache as $hook => $info) {
|
||||
// Check only if not registered by the theme or engine.
|
||||
if (empty($result[$hook])) {
|
||||
if (!isset($info['preprocess functions'])) {
|
||||
$cache[$hook]['preprocess functions'] = array();
|
||||
}
|
||||
// Only use non-hook-specific variable preprocessors for theme hooks
|
||||
// implemented as templates. See _theme().
|
||||
if (isset($info['template']) && function_exists($name . '_preprocess')) {
|
||||
$cache[$hook]['preprocess functions'][] = $name . '_preprocess';
|
||||
}
|
||||
if (function_exists($name . '_preprocess_' . $hook)) {
|
||||
$cache[$hook]['preprocess functions'][] = $name . '_preprocess_' . $hook;
|
||||
$cache[$hook]['theme path'] = $path;
|
||||
}
|
||||
// Ensure uniqueness.
|
||||
$cache[$hook]['preprocess functions'] = array_unique($cache[$hook]['preprocess functions']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates theme registry caches.
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
$this->runtimeRegistry = NULL;
|
||||
|
||||
$this->registry = NULL;
|
||||
Cache::invalidateTags(array('theme_registry'));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destruct() {
|
||||
if (isset($this->runtimeRegistry)) {
|
||||
$this->runtimeRegistry->destruct();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps drupal_get_path().
|
||||
*
|
||||
* @param string $module
|
||||
* The name of the item for which the path is requested.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPath($module) {
|
||||
return drupal_get_path('module', $module);
|
||||
}
|
||||
}
|
63
core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
Normal file
63
core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeAccessCheck.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Routing\Access\AccessInterface;
|
||||
|
||||
/**
|
||||
* Provides access checking for themes for routing and theme negotiation.
|
||||
*/
|
||||
class ThemeAccessCheck implements AccessInterface {
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\Core\Theme\Registry object.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
*/
|
||||
public function __construct(ThemeHandlerInterface $theme_handler) {
|
||||
$this->themeHandler = $theme_handler;
|
||||
}
|
||||
/**
|
||||
* Checks access to the theme for routing.
|
||||
*
|
||||
* @param string $theme
|
||||
* The name of a theme.
|
||||
*
|
||||
* @return \Drupal\Core\Access\AccessResultInterface
|
||||
* The access result.
|
||||
*/
|
||||
public function access($theme) {
|
||||
// Cacheable until the theme settings are modified.
|
||||
return AccessResult::allowedIf($this->checkAccess($theme))->addCacheTags(['config:' . $theme . '.settings']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the theme is accessible based on whether it is installed.
|
||||
*
|
||||
* @param string $theme
|
||||
* The name of a theme.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the theme is installed, FALSE otherwise.
|
||||
*/
|
||||
public function checkAccess($theme) {
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
return !empty($themes[$theme]->status);
|
||||
}
|
||||
|
||||
}
|
259
core/lib/Drupal/Core/Theme/ThemeInitialization.php
Normal file
259
core/lib/Drupal/Core/Theme/ThemeInitialization.php
Normal file
|
@ -0,0 +1,259 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeInitialization.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Extension\Extension;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
|
||||
/**
|
||||
* Provides the theme initialization logic.
|
||||
*/
|
||||
class ThemeInitialization implements ThemeInitializationInterface {
|
||||
|
||||
/**
|
||||
* The theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandlerInterface
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The cache backend to use for the active theme.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* The extensions that might be attaching assets.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $extensions;
|
||||
|
||||
/**
|
||||
* Constructs a new ThemeInitialization object.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||
* The cache backend.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to use to load modules.
|
||||
*/
|
||||
public function __construct($root, ThemeHandlerInterface $theme_handler, CacheBackendInterface $cache, ModuleHandlerInterface $module_handler) {
|
||||
$this->root = $root;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->cache = $cache;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function initTheme($theme_name) {
|
||||
$active_theme = $this->getActiveThemeByName($theme_name);
|
||||
$this->loadActiveTheme($active_theme);
|
||||
|
||||
return $active_theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getActiveThemeByName($theme_name) {
|
||||
if ($cached = $this->cache->get('theme.active_theme.' . $theme_name)) {
|
||||
return $cached->data;
|
||||
}
|
||||
$themes = $this->themeHandler->listInfo();
|
||||
|
||||
// If no theme could be negotiated, or if the negotiated theme is not within
|
||||
// the list of installed themes, fall back to the default theme output of
|
||||
// core and modules (like Stark, but without a theme extension at all). This
|
||||
// is possible, because loadActiveTheme() always loads the Twig theme
|
||||
// engine. This is desired, because missing or malformed theme configuration
|
||||
// should not leave the application in a broken state. By falling back to
|
||||
// default output, the user is able to reconfigure the theme through the UI.
|
||||
// Lastly, tests are expected to operate with no theme by default, so as to
|
||||
// only assert the original theme output of modules (unless a test manually
|
||||
// installs a specific theme).
|
||||
if (empty($themes) || !$theme_name || !isset($themes[$theme_name])) {
|
||||
$theme_name = 'core';
|
||||
// /core/core.info.yml does not actually exist, but is required because
|
||||
// Extension expects a pathname.
|
||||
$active_theme = $this->getActiveTheme(new Extension($this->root, 'theme', 'core/core.info.yml'));
|
||||
|
||||
// Early-return and do not set state, because the initialized $theme_name
|
||||
// differs from the original $theme_name.
|
||||
return $active_theme;
|
||||
}
|
||||
|
||||
// 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->cache->set('theme.active_theme.' . $theme_name, $active_theme);
|
||||
return $active_theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadActiveTheme(ActiveTheme $active_theme) {
|
||||
// Initialize the theme.
|
||||
if ($theme_engine = $active_theme->getEngine()) {
|
||||
// Include the engine.
|
||||
include_once $this->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 $this->root . '/' . $base->getOwner();
|
||||
}
|
||||
}
|
||||
// and our theme gets one too.
|
||||
if ($active_theme->getOwner()) {
|
||||
include_once $this->root . '/' . $active_theme->getOwner();
|
||||
}
|
||||
}
|
||||
|
||||
// Always include Twig as the default theme engine.
|
||||
include_once $this->root . '/core/themes/engines/twig/twig.engine';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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 remove CSS files
|
||||
// easily from parent.
|
||||
$values['stylesheets_remove'] = array();
|
||||
|
||||
// Grab stylesheets from base theme.
|
||||
foreach ($base_themes as $base) {
|
||||
$base_theme_path = $base->getPath();
|
||||
if (!empty($base->info['stylesheets-remove'])) {
|
||||
foreach ($base->info['stylesheets-remove'] as $css_file) {
|
||||
$css_file = $this->resolveStyleSheetPlaceholders($css_file);
|
||||
$values['stylesheets_remove'][$css_file] = $css_file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add stylesheets used by this theme.
|
||||
if (!empty($theme->info['stylesheets-remove'])) {
|
||||
foreach ($theme->info['stylesheets-remove'] as $css_file) {
|
||||
$css_file = $this->resolveStyleSheetPlaceholders($css_file);
|
||||
$values['stylesheets_remove'][$css_file] = $css_file;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (!empty($theme->info['regions'])) {
|
||||
$values['regions'] = $theme->info['regions'];
|
||||
}
|
||||
|
||||
return new ActiveTheme($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all extensions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getExtensions() {
|
||||
if (!isset($this->extensions)) {
|
||||
$this->extensions = array_merge($this->moduleHandler->getModuleList(), $this->themeHandler->listInfo());
|
||||
}
|
||||
return $this->extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets CSS file where tokens have been resolved.
|
||||
*
|
||||
* @param string $css_file
|
||||
* CSS file which may contain tokens.
|
||||
*
|
||||
* @return string
|
||||
* CSS file where placeholders are replaced.
|
||||
*/
|
||||
protected function resolveStyleSheetPlaceholders($css_file) {
|
||||
$token_candidate = explode('/', $css_file)[0];
|
||||
if (!preg_match('/@[A-z0-9_-]+/', $token_candidate)) {
|
||||
return $css_file;
|
||||
}
|
||||
|
||||
$token = substr($token_candidate, 1);
|
||||
|
||||
// Prime extensions.
|
||||
$extensions = $this->getExtensions();
|
||||
if (isset($extensions[$token])) {
|
||||
return str_replace($token_candidate, $extensions[$token]->getPath(), $css_file);
|
||||
}
|
||||
}
|
||||
}
|
65
core/lib/Drupal/Core/Theme/ThemeInitializationInterface.php
Normal file
65
core/lib/Drupal/Core/Theme/ThemeInitializationInterface.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeInitializationInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
use Drupal\Core\Extension\Extension;
|
||||
|
||||
/**
|
||||
* Defines an interface which contain theme initialization logic.
|
||||
*/
|
||||
interface ThemeInitializationInterface {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 \Drupal\Core\Theme\ActiveTheme $active_theme
|
||||
* The theme to load.
|
||||
*/
|
||||
public function loadActiveTheme(ActiveTheme $active_theme);
|
||||
|
||||
/**
|
||||
* Builds up the active theme object from extensions.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return \Drupal\Core\Theme\ActiveTheme
|
||||
* The active theme instance for the passed in $theme.
|
||||
*/
|
||||
public function getActiveTheme(Extension $theme, array $base_themes = []);
|
||||
|
||||
}
|
468
core/lib/Drupal/Core/Theme/ThemeManager.php
Normal file
468
core/lib/Drupal/Core/Theme/ThemeManager.php
Normal file
|
@ -0,0 +1,468 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Routing\StackedRouteMatchInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Provides the default implementation of a theme manager.
|
||||
*/
|
||||
class ThemeManager implements ThemeManagerInterface {
|
||||
|
||||
/**
|
||||
* The theme negotiator.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeNegotiatorInterface
|
||||
*/
|
||||
protected $themeNegotiator;
|
||||
|
||||
/**
|
||||
* The theme registry used to render an output.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\Registry
|
||||
*/
|
||||
protected $themeRegistry;
|
||||
|
||||
/**
|
||||
* Contains the current active theme.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ActiveTheme
|
||||
*/
|
||||
protected $activeTheme;
|
||||
|
||||
/**
|
||||
* The theme initialization.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeInitializationInterface
|
||||
*/
|
||||
protected $themeInitialization;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* The app root.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $root;
|
||||
|
||||
/**
|
||||
* Constructs a new ThemeManager object.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
* @param \Drupal\Core\Theme\ThemeNegotiatorInterface $theme_negotiator
|
||||
* The theme negotiator.
|
||||
* @param \Drupal\Core\Theme\ThemeInitializationInterface $theme_initialization
|
||||
* The theme initialization.
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
*/
|
||||
public function __construct($root, ThemeNegotiatorInterface $theme_negotiator, ThemeInitializationInterface $theme_initialization, RequestStack $request_stack, ModuleHandlerInterface $module_handler) {
|
||||
$this->root = $root;
|
||||
$this->themeNegotiator = $theme_negotiator;
|
||||
$this->themeInitialization = $theme_initialization;
|
||||
$this->requestStack = $request_stack;
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme registry.
|
||||
*
|
||||
* @param \Drupal\Core\Theme\Registry $theme_registry
|
||||
* The theme registry.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setThemeRegistry(Registry $theme_registry) {
|
||||
$this->themeRegistry = $theme_registry;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function render($hook, array $variables) {
|
||||
return $this->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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resetActiveTheme() {
|
||||
$this->activeTheme = NULL;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setActiveTheme(ActiveTheme $active_theme) {
|
||||
$this->activeTheme = $active_theme;
|
||||
if ($active_theme) {
|
||||
$this->themeInitialization->loadActiveTheme($active_theme);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates themed output (internal use only).
|
||||
*
|
||||
* @see \Drupal\Core\Render\RendererInterface::render();
|
||||
*/
|
||||
protected function theme($hook, $variables = array()) {
|
||||
static $default_attributes;
|
||||
|
||||
$active_theme = $this->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
|
||||
// request properly. See also \Drupal\Core\Theme\Registry::get().
|
||||
if (!$this->moduleHandler->isLoaded() && !defined('MAINTENANCE_MODE')) {
|
||||
throw new \Exception(t('_theme() may not be called until all modules are loaded.'));
|
||||
}
|
||||
|
||||
$theme_registry = $this->themeRegistry->getRuntime();
|
||||
|
||||
// If an array of hook candidates were passed, use the first one that has an
|
||||
// implementation.
|
||||
if (is_array($hook)) {
|
||||
foreach ($hook as $candidate) {
|
||||
if ($theme_registry->has($candidate)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$hook = $candidate;
|
||||
}
|
||||
// Save the original theme hook, so it can be supplied to theme variable
|
||||
// preprocess callbacks.
|
||||
$original_hook = $hook;
|
||||
|
||||
// If there's no implementation, check for more generic fallbacks.
|
||||
// If there's still no implementation, log an error and return an empty
|
||||
// string.
|
||||
if (!$theme_registry->has($hook)) {
|
||||
// Iteratively strip everything after the last '__' delimiter, until an
|
||||
// implementation is found.
|
||||
while ($pos = strrpos($hook, '__')) {
|
||||
$hook = substr($hook, 0, $pos);
|
||||
if ($theme_registry->has($hook)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$theme_registry->has($hook)) {
|
||||
// Only log a message when not trying theme suggestions ($hook being an
|
||||
// array).
|
||||
if (!isset($candidate)) {
|
||||
\Drupal::logger('theme')->warning('Theme hook %hook not found.', array('%hook' => $hook));
|
||||
}
|
||||
// There is no theme implementation for the hook passed. Return FALSE so
|
||||
// the function calling _theme() can differentiate between a hook that
|
||||
// exists and renders an empty string and a hook that is not
|
||||
// implemented.
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
$info = $theme_registry->get($hook);
|
||||
|
||||
// If a renderable array is passed as $variables, then set $variables to
|
||||
// the arguments expected by the theme function.
|
||||
if (isset($variables['#theme']) || isset($variables['#theme_wrappers'])) {
|
||||
$element = $variables;
|
||||
$variables = array();
|
||||
if (isset($info['variables'])) {
|
||||
foreach (array_keys($info['variables']) as $name) {
|
||||
if (isset($element["#$name"]) || array_key_exists("#$name", $element)) {
|
||||
$variables[$name] = $element["#$name"];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$variables[$info['render element']] = $element;
|
||||
// Give a hint to render engines to prevent infinite recursion.
|
||||
$variables[$info['render element']]['#render_children'] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge in argument defaults.
|
||||
if (!empty($info['variables'])) {
|
||||
$variables += $info['variables'];
|
||||
}
|
||||
elseif (!empty($info['render element'])) {
|
||||
$variables += array($info['render element'] => array());
|
||||
}
|
||||
// Supply original caller info.
|
||||
$variables += array(
|
||||
'theme_hook_original' => $original_hook,
|
||||
);
|
||||
|
||||
// Set base hook for later use. For example if '#theme' => 'node__article'
|
||||
// is called, we run hook_theme_suggestions_node_alter() rather than
|
||||
// hook_theme_suggestions_node__article_alter(), and also pass in the base
|
||||
// hook as the last parameter to the suggestions alter hooks.
|
||||
if (isset($info['base hook'])) {
|
||||
$base_theme_hook = $info['base hook'];
|
||||
}
|
||||
else {
|
||||
$base_theme_hook = $hook;
|
||||
}
|
||||
|
||||
// Invoke hook_theme_suggestions_HOOK().
|
||||
$suggestions = $this->moduleHandler->invokeAll('theme_suggestions_' . $base_theme_hook, array($variables));
|
||||
// If _theme() was invoked with a direct theme suggestion like
|
||||
// '#theme' => 'node__article', add it to the suggestions array before
|
||||
// invoking suggestion alter hooks.
|
||||
if (isset($info['base hook'])) {
|
||||
$suggestions[] = $hook;
|
||||
}
|
||||
|
||||
// Invoke hook_theme_suggestions_alter() and
|
||||
// hook_theme_suggestions_HOOK_alter().
|
||||
$hooks = array(
|
||||
'theme_suggestions',
|
||||
'theme_suggestions_' . $base_theme_hook,
|
||||
);
|
||||
$this->moduleHandler->alter($hooks, $suggestions, $variables, $base_theme_hook);
|
||||
$this->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
|
||||
// function may call _theme('node', ...), but a module can add
|
||||
// 'node__article' as a suggestion via hook_theme_suggestions_HOOK_alter(),
|
||||
// enabling a theme to have an alternate template file for article nodes.
|
||||
foreach (array_reverse($suggestions) as $suggestion) {
|
||||
if ($theme_registry->has($suggestion)) {
|
||||
$info = $theme_registry->get($suggestion);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Include a file if the theme function or variable preprocessor is held
|
||||
// elsewhere.
|
||||
if (!empty($info['includes'])) {
|
||||
foreach ($info['includes'] as $include_file) {
|
||||
include_once $this->root . '/' . $include_file;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke the variable preprocessors, if any.
|
||||
if (isset($info['base hook'])) {
|
||||
$base_hook = $info['base hook'];
|
||||
$base_hook_info = $theme_registry->get($base_hook);
|
||||
// Include files required by the base hook, since its variable
|
||||
// preprocessors might reside there.
|
||||
if (!empty($base_hook_info['includes'])) {
|
||||
foreach ($base_hook_info['includes'] as $include_file) {
|
||||
include_once $this->root . '/' . $include_file;
|
||||
}
|
||||
}
|
||||
// Replace the preprocess functions with those from the base hook.
|
||||
if (isset($base_hook_info['preprocess functions'])) {
|
||||
// Set a variable for the 'theme_hook_suggestion'. This is used to
|
||||
// maintain backwards compatibility with template engines.
|
||||
$theme_hook_suggestion = $hook;
|
||||
$info['preprocess functions'] = $base_hook_info['preprocess functions'];
|
||||
}
|
||||
}
|
||||
if (isset($info['preprocess functions'])) {
|
||||
foreach ($info['preprocess functions'] as $preprocessor_function) {
|
||||
if (function_exists($preprocessor_function)) {
|
||||
$preprocessor_function($variables, $hook, $info);
|
||||
}
|
||||
}
|
||||
// Allow theme preprocess functions to set $variables['#attached'] and use
|
||||
// it like the #attached property on render arrays. In Drupal 8, this is
|
||||
// the (only) officially supported method of attaching assets from
|
||||
// preprocess functions. Assets attached here should be associated with
|
||||
// the template that we're preprocessing variables for.
|
||||
if (isset($variables['#attached'])) {
|
||||
$preprocess_attached = ['#attached' => $variables['#attached']];
|
||||
drupal_render($preprocess_attached);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the output using either a function or a template.
|
||||
$output = '';
|
||||
if (isset($info['function'])) {
|
||||
if (function_exists($info['function'])) {
|
||||
$output = SafeMarkup::set($info['function']($variables));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$render_function = 'twig_render_template';
|
||||
$extension = '.html.twig';
|
||||
|
||||
// The theme engine may use a different extension and a different
|
||||
// renderer.
|
||||
$theme_engine = $active_theme->getEngine();
|
||||
if (isset($theme_engine)) {
|
||||
if ($info['type'] != 'module') {
|
||||
if (function_exists($theme_engine . '_render_template')) {
|
||||
$render_function = $theme_engine . '_render_template';
|
||||
}
|
||||
$extension_function = $theme_engine . '_extension';
|
||||
if (function_exists($extension_function)) {
|
||||
$extension = $extension_function();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In some cases, a template implementation may not have had
|
||||
// template_preprocess() run (for example, if the default implementation
|
||||
// is a function, but a template overrides that default implementation).
|
||||
// In these cases, a template should still be able to expect to have
|
||||
// access to the variables provided by template_preprocess(), so we add
|
||||
// them here if they don't already exist. We don't want the overhead of
|
||||
// running template_preprocess() twice, so we use the 'directory' variable
|
||||
// to determine if it has already run, which while not completely
|
||||
// intuitive, is reasonably safe, and allows us to save on the overhead of
|
||||
// adding some new variable to track that.
|
||||
if (!isset($variables['directory'])) {
|
||||
$default_template_variables = array();
|
||||
template_preprocess($default_template_variables, $hook, $info);
|
||||
$variables += $default_template_variables;
|
||||
}
|
||||
if (!isset($default_attributes)) {
|
||||
$default_attributes = new Attribute();
|
||||
}
|
||||
foreach (array('attributes', 'title_attributes', 'content_attributes') as $key) {
|
||||
if (isset($variables[$key]) && !($variables[$key] instanceof Attribute)) {
|
||||
if ($variables[$key]) {
|
||||
$variables[$key] = new Attribute($variables[$key]);
|
||||
}
|
||||
else {
|
||||
// Create empty attributes.
|
||||
$variables[$key] = clone $default_attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the output using the template file.
|
||||
$template_file = $info['template'] . $extension;
|
||||
if (isset($info['path'])) {
|
||||
$template_file = $info['path'] . '/' . $template_file;
|
||||
}
|
||||
// Add the theme suggestions to the variables array just before rendering
|
||||
// the template for backwards compatibility with template engines.
|
||||
$variables['theme_hook_suggestions'] = $suggestions;
|
||||
// For backwards compatibility, pass 'theme_hook_suggestion' on to the
|
||||
// template engine. This is only set when calling a direct suggestion like
|
||||
// '#theme' => 'menu__shortcut_default' when the template exists in the
|
||||
// current theme.
|
||||
if (isset($theme_hook_suggestion)) {
|
||||
$variables['theme_hook_suggestion'] = $theme_hook_suggestion;
|
||||
}
|
||||
$output = $render_function($template_file, $variables);
|
||||
}
|
||||
|
||||
return (string) $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the active theme for a given route match.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The current route match.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
if ($route_match instanceof StackedRouteMatchInterface) {
|
||||
$route_match = $route_match->getMasterRouteMatch();
|
||||
}
|
||||
$theme = $this->themeNegotiator->determineActiveTheme($route_match);
|
||||
$this->activeTheme = $this->themeInitialization->initTheme($theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Should we cache some of these information?
|
||||
*/
|
||||
public function alterForTheme(ActiveTheme $theme, $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();
|
||||
foreach ($theme->getBaseThemes() as $base) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
|
||||
$theme = $this->getActiveTheme();
|
||||
$this->alterForTheme($theme, $type, $data, $context1, $context2);
|
||||
}
|
||||
|
||||
}
|
148
core/lib/Drupal/Core/Theme/ThemeManagerInterface.php
Normal file
148
core/lib/Drupal/Core/Theme/ThemeManagerInterface.php
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeManagerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
/**
|
||||
* Provides a high level access to the active theme and methods to use it.
|
||||
*
|
||||
* Beside the active theme it provides a wrapper around _theme as well as the
|
||||
* alter functionality for themes.
|
||||
*/
|
||||
interface ThemeManagerInterface {
|
||||
|
||||
/**
|
||||
* Generates themed output.
|
||||
*
|
||||
* See the @link themeable Default theme implementations topic @endlink for
|
||||
* details.
|
||||
*
|
||||
* @param string $hook
|
||||
* The name of the theme hook to call.
|
||||
* @param array $variables
|
||||
* An associative array of theme variables.
|
||||
*
|
||||
* @return string
|
||||
* The rendered output.
|
||||
*/
|
||||
public function render($hook, array $variables);
|
||||
|
||||
/**
|
||||
* Returns the active theme object.
|
||||
*
|
||||
* @return \Drupal\Core\Theme\ActiveTheme
|
||||
*/
|
||||
public function getActiveTheme();
|
||||
|
||||
/**
|
||||
* Determines whether there is an active theme.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasActiveTheme();
|
||||
|
||||
/**
|
||||
* Resets the current active theme.
|
||||
*
|
||||
* Note: This method should not be used in common cases, just in special cases
|
||||
* like tests.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function resetActiveTheme();
|
||||
|
||||
/**
|
||||
* Sets the current active theme manually.
|
||||
*
|
||||
* Note: This method should not be used in common cases, just in special cases
|
||||
* like tests.
|
||||
*
|
||||
* @param \Drupal\Core\Theme\ActiveTheme $active_theme
|
||||
* The new active theme.
|
||||
* @return $this
|
||||
*/
|
||||
public function setActiveTheme(ActiveTheme $active_theme);
|
||||
|
||||
/**
|
||||
* Passes alterable variables to specific $theme_TYPE_alter() implementations.
|
||||
*
|
||||
* It also invokes alter hooks for all base themes.
|
||||
*
|
||||
* $theme specifies the theme name of the active theme and all its base
|
||||
* themes.
|
||||
*
|
||||
* This dispatch function hands off the passed-in variables to type-specific
|
||||
* $theme_TYPE_alter() implementations in the active theme. It ensures a
|
||||
* consistent interface for all altering operations.
|
||||
*
|
||||
* A maximum of 2 alterable arguments is supported. In case more arguments
|
||||
* need to be passed and alterable, modules provide additional variables
|
||||
* assigned by reference in the last $context argument:
|
||||
* @code
|
||||
* $context = array(
|
||||
* 'alterable' => &$alterable,
|
||||
* 'unalterable' => $unalterable,
|
||||
* 'foo' => 'bar',
|
||||
* );
|
||||
* $this->alter('mymodule_data', $alterable1, $alterable2, $context);
|
||||
* @endcode
|
||||
*
|
||||
* Note that objects are always passed by reference in PHP5. If it is
|
||||
* absolutely required that no implementation alters a passed object in
|
||||
* $context, then an object needs to be cloned:
|
||||
* @code
|
||||
* $context = array(
|
||||
* 'unalterable_object' => clone $object,
|
||||
* );
|
||||
* $this->alter('mymodule_data', $data, $context);
|
||||
* @endcode
|
||||
*
|
||||
* @param string|array $type
|
||||
* A string describing the type of the alterable $data. 'form', 'links',
|
||||
* 'node_content', and so on are several examples. Alternatively can be an
|
||||
* array, in which case $theme_TYPE_alter() is invoked for each value in the
|
||||
* array. When Form API is using $this->alter() to
|
||||
* execute both $theme_form_alter() and $theme_form_FORM_ID_alter()
|
||||
* implementations, it passes array('form', 'form_' . $form_id) for $type.
|
||||
* @param mixed $data
|
||||
* The variable that will be passed to $theme_TYPE_alter() implementations
|
||||
* to be altered. The type of this variable depends on the value of the
|
||||
* $type argument. For example, when altering a 'form', $data will be a
|
||||
* structured array. When altering a 'profile', $data will be an object.
|
||||
* @param mixed $context1
|
||||
* (optional) An additional variable that is passed by reference.
|
||||
* @param mixed $context2
|
||||
* (optional) An additional variable that is passed by reference. If more
|
||||
* context needs to be provided to implementations, then this should be an
|
||||
* associative array as described above.
|
||||
* Execute the alter hook on the current theme.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL);
|
||||
|
||||
/**
|
||||
* Provides an alter hook for a specific theme.
|
||||
*
|
||||
* Similar to ::alter, it also invokes the alter hooks for the base themes.
|
||||
*
|
||||
* @param \Drupal\Core\Theme\ActiveTheme $theme
|
||||
* A manually specified theme.
|
||||
* @param string|array $type
|
||||
* A string describing the type of the alterable $data.
|
||||
* @param mixed $data
|
||||
* The variable that will be passed to $theme_TYPE_alter() implementations
|
||||
* @param mixed $context1
|
||||
* (optional) An additional variable that is passed by reference.
|
||||
* @param mixed $context2
|
||||
* (optional) An additional variable that is passed by reference.
|
||||
*
|
||||
* @see ::alter
|
||||
*/
|
||||
public function alterForTheme(ActiveTheme $theme, $type, &$data, &$context1 = NULL, &$context2 = NULL);
|
||||
|
||||
}
|
111
core/lib/Drupal/Core/Theme/ThemeNegotiator.php
Normal file
111
core/lib/Drupal/Core/Theme/ThemeNegotiator.php
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeNegotiator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Provides a class which determines the active theme of the page.
|
||||
*
|
||||
* It therefore uses ThemeNegotiatorInterface objects which are passed in
|
||||
* using the 'theme_negotiator' tag.
|
||||
*
|
||||
* @see \Drupal\Core\Theme\ThemeNegotiatorPass
|
||||
* @see \Drupal\Core\Theme\ThemeNegotiatorInterface
|
||||
*/
|
||||
class ThemeNegotiator implements ThemeNegotiatorInterface {
|
||||
|
||||
/**
|
||||
* Holds arrays of theme negotiators, keyed by priority.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $negotiators = array();
|
||||
|
||||
/**
|
||||
* Holds the array of theme negotiators sorted by priority.
|
||||
*
|
||||
* Set to NULL if the array needs to be re-calculated.
|
||||
*
|
||||
* @var array|NULL
|
||||
*/
|
||||
protected $sortedNegotiators;
|
||||
|
||||
/**
|
||||
* The access checker for themes.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeAccessCheck
|
||||
*/
|
||||
protected $themeAccess;
|
||||
|
||||
/**
|
||||
* Constructs a new ThemeNegotiator.
|
||||
*
|
||||
* @param \Drupal\Core\Theme\ThemeAccessCheck $theme_access
|
||||
* The access checker for themes.
|
||||
*/
|
||||
public function __construct(ThemeAccessCheck $theme_access) {
|
||||
$this->themeAccess = $theme_access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a active theme negotiation service.
|
||||
*
|
||||
* @param \Drupal\Core\Theme\ThemeNegotiatorInterface $negotiator
|
||||
* The theme negotiator to add.
|
||||
* @param int $priority
|
||||
* Priority of the theme negotiator.
|
||||
*/
|
||||
public function addNegotiator(ThemeNegotiatorInterface $negotiator, $priority) {
|
||||
$this->negotiators[$priority][] = $negotiator;
|
||||
// Force the negotiators to be re-sorted.
|
||||
$this->sortedNegotiators = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sorted array of theme negotiators.
|
||||
*
|
||||
* @return array|\Drupal\Core\Theme\ThemeNegotiatorInterface[]
|
||||
* An array of theme negotiator objects.
|
||||
*/
|
||||
protected function getSortedNegotiators() {
|
||||
if (!isset($this->sortedNegotiators)) {
|
||||
// Sort the negotiators according to priority.
|
||||
krsort($this->negotiators);
|
||||
// Merge nested negotiators from $this->negotiators into
|
||||
// $this->sortedNegotiators.
|
||||
$this->sortedNegotiators = array();
|
||||
foreach ($this->negotiators as $builders) {
|
||||
$this->sortedNegotiators = array_merge($this->sortedNegotiators, $builders);
|
||||
}
|
||||
}
|
||||
return $this->sortedNegotiators;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function applies(RouteMatchInterface $route_match) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match) {
|
||||
foreach ($this->getSortedNegotiators() as $negotiator) {
|
||||
if ($negotiator->applies($route_match)) {
|
||||
$theme = $negotiator->determineActiveTheme($route_match);
|
||||
if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) {
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
53
core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php
Normal file
53
core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeNegotiatorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Defines an interface for classes which determine the active theme.
|
||||
*
|
||||
* To set the active theme, create a new service tagged with 'theme_negotiator'
|
||||
* (see user.services.yml for an example). The only method this service needs
|
||||
* to implement is determineActiveTheme. Return the name of the theme, or NULL
|
||||
* if other negotiators like the configured default one should kick in instead.
|
||||
*
|
||||
* If you are setting a theme which is closely tied to the functionality of a
|
||||
* particular page or set of pages (such that the page might not function
|
||||
* correctly if a different theme is used), make sure to set the priority on
|
||||
* the service to a high number so that it is not accidentally overridden by
|
||||
* other theme negotiators. By convention, a priority of "1000" is used in
|
||||
* these cases; see \Drupal\Core\Theme\AjaxBasePageNegotiator and
|
||||
* core.services.yml for an example.
|
||||
*/
|
||||
interface ThemeNegotiatorInterface {
|
||||
|
||||
/**
|
||||
* Whether this theme negotiator should be used to set the theme.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The current route match object.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if this negotiator should be used or FALSE to let other negotiators
|
||||
* decide.
|
||||
*/
|
||||
public function applies(RouteMatchInterface $route_match);
|
||||
|
||||
/**
|
||||
* Determine the active theme for the request.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The current route match object.
|
||||
*
|
||||
* @return string|null
|
||||
* Returns the active theme name, else return NULL.
|
||||
*/
|
||||
public function determineActiveTheme(RouteMatchInterface $route_match);
|
||||
|
||||
}
|
57
core/lib/Drupal/Core/Theme/ThemeSettings.php
Normal file
57
core/lib/Drupal/Core/Theme/ThemeSettings.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Theme\ThemeSettings.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Theme;
|
||||
|
||||
use Drupal\Core\Config\ConfigBase;
|
||||
|
||||
/**
|
||||
* Provides a configuration API wrapper for runtime merged theme settings.
|
||||
*
|
||||
* Theme settings use configuration for base values but the runtime theme
|
||||
* settings are calculated based on various site settings and are therefore
|
||||
* not persisted.
|
||||
*
|
||||
* @see theme_get_setting()
|
||||
*/
|
||||
class ThemeSettings extends ConfigBase {
|
||||
|
||||
/**
|
||||
* The theme of the theme settings object.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* Constructs a theme settings object.
|
||||
*
|
||||
* @param string $theme
|
||||
* The name of the theme settings object being constructed.
|
||||
*/
|
||||
public function __construct($theme) {
|
||||
$this->theme = $theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme of this theme settings object.
|
||||
*
|
||||
* @return string
|
||||
* The theme of this theme settings object.
|
||||
*/
|
||||
public function getTheme() {
|
||||
return $this->theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return ['rendered'];
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue