Issue #2109287 by dawehner, Cottser, tim.plunkett, kim.pepper: Replace list_themes() with a service.
parent
1ceeda436a
commit
67e93b23ac
|
@ -173,6 +173,9 @@ services:
|
|||
default_plugin_manager:
|
||||
abstract: true
|
||||
arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
|
||||
theme_handler:
|
||||
class: Drupal\Core\Extension\ThemeHandler
|
||||
arguments: ['@config.factory', '@module_handler', '@cache.cache', '@info_parser', '@router.builder']
|
||||
entity.manager:
|
||||
class: Drupal\Core\Entity\EntityManager
|
||||
arguments: ['@container.namespaces', '@service_container', '@module_handler', '@cache.cache', '@language_manager', '@string_translation']
|
||||
|
|
|
@ -484,7 +484,14 @@ function install_begin_request(&$install_state) {
|
|||
|
||||
// Register the info parser.
|
||||
$container->register('info_parser', 'Drupal\Core\Extension\InfoParser');
|
||||
|
||||
$container->register('theme_handler', 'Drupal\Core\Extension\ThemeHandler')
|
||||
->addArgument(new Reference('config.factory'))
|
||||
->addArgument(new Reference('module_handler'))
|
||||
->addArgument(new Reference('cache.cache'))
|
||||
->addArgument(new Reference('info_parser'));
|
||||
}
|
||||
|
||||
// Set the request in the kernel to the new created Request above
|
||||
// so it is available to the rest of the installation process.
|
||||
$container->set('request', $request);
|
||||
|
|
|
@ -355,52 +355,19 @@ function drupal_theme_rebuild() {
|
|||
* the themes' machine names, and the values are the themes' human-readable
|
||||
* names. This element is not set if there are no themes on the system that
|
||||
* declare this theme as their base theme.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use \Drupal::service('theme_handler')->listInfo().
|
||||
*/
|
||||
function list_themes($refresh = FALSE) {
|
||||
$list = &drupal_static(__FUNCTION__, array());
|
||||
/** @var \Drupal\Core\Extension\ThemeHandler $theme_handler */
|
||||
$theme_handler = \Drupal::service('theme_handler');
|
||||
|
||||
if ($refresh) {
|
||||
$list = array();
|
||||
$theme_handler->reset();
|
||||
system_list_reset();
|
||||
}
|
||||
|
||||
if (empty($list)) {
|
||||
$list = array();
|
||||
// Extract from the database only when it is available.
|
||||
// Also check that the site is not in the middle of an install or update.
|
||||
try {
|
||||
$themes = system_list('theme');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// If the database is not available, rebuild the theme data.
|
||||
$themes = _system_rebuild_theme_data();
|
||||
}
|
||||
|
||||
foreach ($themes as $theme) {
|
||||
foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
|
||||
foreach ($stylesheets as $stylesheet => $path) {
|
||||
$theme->stylesheets[$media][$stylesheet] = $path;
|
||||
}
|
||||
}
|
||||
foreach ($theme->info['scripts'] as $script => $path) {
|
||||
$theme->scripts[$script] = $path;
|
||||
}
|
||||
if (isset($theme->info['engine'])) {
|
||||
$theme->engine = $theme->info['engine'];
|
||||
}
|
||||
if (isset($theme->info['base theme'])) {
|
||||
$theme->base_theme = $theme->info['base theme'];
|
||||
}
|
||||
// Status is normally retrieved from the database. Add zero values when
|
||||
// read from the installation directory to prevent notices.
|
||||
if (!isset($theme->status)) {
|
||||
$theme->status = 0;
|
||||
}
|
||||
$list[$theme->name] = $theme;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
return $theme_handler->listInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -413,38 +380,15 @@ function list_themes($refresh = FALSE) {
|
|||
* An array of available themes.
|
||||
* @param $key
|
||||
* The name of the theme whose base we are looking for.
|
||||
* @param $used_keys
|
||||
* (optional) A recursion parameter preventing endless loops. Defaults to
|
||||
* NULL.
|
||||
*
|
||||
* @return
|
||||
* Returns an array of all of the theme's ancestors; the first element's value
|
||||
* will be NULL if an error occurred.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use \Drupal::service('theme_handler')->getBaseThemes().
|
||||
*/
|
||||
function drupal_find_base_themes($themes, $key, $used_keys = array()) {
|
||||
$base_key = $themes[$key]->info['base theme'];
|
||||
// Does the base theme exist?
|
||||
if (!isset($themes[$base_key])) {
|
||||
return array($base_key => NULL);
|
||||
}
|
||||
|
||||
$current_base_theme = array($base_key => $themes[$base_key]->info['name']);
|
||||
|
||||
// Is the base theme itself a child of another theme?
|
||||
if (isset($themes[$base_key]->info['base theme'])) {
|
||||
// Do we already know the base themes of this theme?
|
||||
if (isset($themes[$base_key]->base_themes)) {
|
||||
return $themes[$base_key]->base_themes + $current_base_theme;
|
||||
}
|
||||
// Prevent loops.
|
||||
if (!empty($used_keys[$base_key])) {
|
||||
return array($base_key => NULL);
|
||||
}
|
||||
$used_keys[$base_key] = TRUE;
|
||||
return drupal_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
|
||||
}
|
||||
// If we get here, then this is our parent theme.
|
||||
return $current_base_theme;
|
||||
function drupal_find_base_themes($themes, $key) {
|
||||
return \Drupal::service('theme_handler')->getBaseThemes($themes, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1133,38 +1077,11 @@ function theme_settings_convert_to_config(array $theme_settings, Config $config)
|
|||
*
|
||||
* @param $theme_list
|
||||
* An array of theme names.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use \Drupal::service('theme_handler')->enable().
|
||||
*/
|
||||
function theme_enable($theme_list) {
|
||||
drupal_clear_css_cache();
|
||||
$theme_config = \Drupal::config('system.theme');
|
||||
$disabled_themes = \Drupal::config('system.theme.disabled');
|
||||
foreach ($theme_list as $key) {
|
||||
// Throw an exception if the theme name is too long.
|
||||
if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) {
|
||||
throw new ExtensionNameLengthException(format_string('Theme name %name is over the maximum allowed length of @max characters.', array(
|
||||
'%name' => $key,
|
||||
'@max' => DRUPAL_EXTENSION_NAME_MAX_LENGTH,
|
||||
)));
|
||||
}
|
||||
|
||||
// The value is not used; the weight is ignored for themes currently.
|
||||
$theme_config->set("enabled.$key", 0)->save();
|
||||
$disabled_themes->clear($key)->save();
|
||||
|
||||
// Refresh the theme list as config_install_default_config() needs an
|
||||
// updated list to work.
|
||||
list_themes(TRUE);
|
||||
// Install default configuration of the theme.
|
||||
config_install_default_config('theme', $key);
|
||||
}
|
||||
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
menu_router_rebuild();
|
||||
\Drupal::cache('cache')->deleteTags(array('local_task' => 1));
|
||||
drupal_theme_rebuild();
|
||||
|
||||
// Invoke hook_themes_enabled() after the themes have been enabled.
|
||||
\Drupal::moduleHandler()->invokeAll('themes_enabled', array($theme_list));
|
||||
\Drupal::service('theme_handler')->enable($theme_list);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1172,36 +1089,11 @@ function theme_enable($theme_list) {
|
|||
*
|
||||
* @param $theme_list
|
||||
* An array of theme names.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use \Drupal::service('theme_handler')->disable().
|
||||
*/
|
||||
function theme_disable($theme_list) {
|
||||
// Don't disable the default theme.
|
||||
if ($pos = array_search(\Drupal::config('system.theme')->get('default'), $theme_list) !== FALSE) {
|
||||
unset($theme_list[$pos]);
|
||||
if (empty($theme_list)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
drupal_clear_css_cache();
|
||||
|
||||
$theme_config = \Drupal::config('system.theme');
|
||||
$disabled_themes = \Drupal::config('system.theme.disabled');
|
||||
foreach ($theme_list as $key) {
|
||||
// The value is not used; the weight is ignored for themes currently.
|
||||
$theme_config->clear("enabled.$key");
|
||||
$disabled_themes->set($key, 0);
|
||||
}
|
||||
$theme_config->save();
|
||||
$disabled_themes->save();
|
||||
|
||||
list_themes(TRUE);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
menu_router_rebuild();
|
||||
\Drupal::cache('cache')->deleteTags(array('local_task' => 1));
|
||||
drupal_theme_rebuild();
|
||||
|
||||
// Invoke hook_themes_disabled after the themes have been disabled.
|
||||
\Drupal::moduleHandler()->invokeAll('themes_disabled', array($theme_list));
|
||||
\Drupal::service('theme_handler')->disable($theme_list);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\Core\DependencyInjection;
|
|||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* ServiceProvider class for update.php service overrides.
|
||||
|
@ -38,6 +39,12 @@ class UpdateServiceProvider implements ServiceProviderInterface {
|
|||
->register("cache_factory", 'Drupal\Core\Cache\MemoryBackendFactory');
|
||||
$container
|
||||
->register('router.builder', 'Drupal\Core\Routing\RouteBuilderStatic');
|
||||
|
||||
$container->register('theme_handler', 'Drupal\Core\Extension\ThemeHandler')
|
||||
->addArgument(new Reference('config.factory'))
|
||||
->addArgument(new Reference('module_handler'))
|
||||
->addArgument(new Reference('cache.cache'))
|
||||
->addArgument(new Reference('info_parser'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,500 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Extension\ThemeHandler.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Extension;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Config\ConfigFactory;
|
||||
use Drupal\Core\Routing\RouteBuilder;
|
||||
use Drupal\Core\SystemListingInfo;
|
||||
|
||||
/**
|
||||
* Default theme handler using the config system for enabled/disabled themes.
|
||||
*/
|
||||
class ThemeHandler implements ThemeHandlerInterface {
|
||||
|
||||
/**
|
||||
* Contains the features enabled for themes by default.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultFeatures = array(
|
||||
'logo',
|
||||
'favicon',
|
||||
'name',
|
||||
'slogan',
|
||||
'node_user_picture',
|
||||
'comment_user_picture',
|
||||
'comment_user_verification',
|
||||
'main_menu',
|
||||
'secondary_menu',
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of all currently available themes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $list = array();
|
||||
|
||||
/**
|
||||
* The config factory to get the enabled themes.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactory
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The module handler to fire themes_enabled/themes_disabled hooks.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The cache backend to clear the local tasks cache.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* The info parser to parse the theme.info.yml files.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\InfoParserInterface
|
||||
*/
|
||||
protected $infoParser;
|
||||
|
||||
/**
|
||||
* The route builder to rebuild the routes if a theme is enabled.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteBuilder
|
||||
*/
|
||||
protected $routerBuilder;
|
||||
|
||||
/**
|
||||
* The system listing info
|
||||
*
|
||||
* @var \Drupal\Core\SystemListingInfo
|
||||
*/
|
||||
protected $systemListingInfo;
|
||||
|
||||
/**
|
||||
* Constructs a new ThemeHandler.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactory $config_factory
|
||||
* The config factory to get the enabled themes.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler to fire themes_enabled/themes_disabled hooks.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
||||
* The cache backend to clear the local tasks cache.
|
||||
* @param \Drupal\Core\Extension\InfoParserInterface $info_parser
|
||||
* The info parser to parse the theme.info.yml files.
|
||||
* @param \Drupal\Core\Routing\RouteBuilder $route_builder
|
||||
* (optional) The route builder to rebuild the routes if a theme is enabled.
|
||||
* @param \Drupal\Core\SystemListingInfo $system_list_info
|
||||
* (optional) The system listing info.
|
||||
*/
|
||||
public function __construct(ConfigFactory $config_factory, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, InfoParserInterface $info_parser, RouteBuilder $route_builder = NULL, SystemListingInfo $system_list_info = NULL) {
|
||||
$this->configFactory = $config_factory;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->cacheBackend = $cache_backend;
|
||||
$this->infoParser = $info_parser;
|
||||
$this->routeBuilder = $route_builder;
|
||||
$this->systemListingInfo = $system_list_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function enable(array $theme_list) {
|
||||
$this->clearCssCache();
|
||||
$theme_config = $this->configFactory->get('system.theme');
|
||||
$disabled_themes = $this->configFactory->get('system.theme.disabled');
|
||||
foreach ($theme_list as $key) {
|
||||
// Throw an exception if the theme name is too long.
|
||||
if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) {
|
||||
throw new ExtensionNameLengthException(String::format('Theme name %name is over the maximum allowed length of @max characters.', array(
|
||||
'%name' => $key,
|
||||
'@max' => DRUPAL_EXTENSION_NAME_MAX_LENGTH,
|
||||
)));
|
||||
}
|
||||
|
||||
// The value is not used; the weight is ignored for themes currently.
|
||||
$theme_config->set("enabled.$key", 0)->save();
|
||||
$disabled_themes->clear($key)->save();
|
||||
|
||||
// Refresh the theme list as config_install_default_config() needs an
|
||||
// updated list to work.
|
||||
$this->reset();
|
||||
// Install default configuration of the theme.
|
||||
$this->configInstallDefaultConfig($key);
|
||||
}
|
||||
|
||||
$this->resetSystem();
|
||||
|
||||
// Invoke hook_themes_enabled() after the themes have been enabled.
|
||||
$this->moduleHandler->invokeAll('themes_enabled', array($theme_list));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function disable(array $theme_list) {
|
||||
// Don't disable the default theme.
|
||||
if ($pos = array_search($this->configFactory->get('system.theme')->get('default'), $theme_list) !== FALSE) {
|
||||
unset($theme_list[$pos]);
|
||||
if (empty($theme_list)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->clearCssCache();
|
||||
|
||||
$theme_config = $this->configFactory->get('system.theme');
|
||||
$disabled_themes = $this->configFactory->get('system.theme.disabled');
|
||||
foreach ($theme_list as $key) {
|
||||
// The value is not used; the weight is ignored for themes currently.
|
||||
$theme_config->clear("enabled.$key");
|
||||
$disabled_themes->set($key, 0);
|
||||
}
|
||||
$theme_config->save();
|
||||
$disabled_themes->save();
|
||||
|
||||
$this->reset();
|
||||
$this->resetSystem();
|
||||
|
||||
// Invoke hook_themes_disabled after the themes have been disabled.
|
||||
$this->moduleHandler->invokeAll('themes_disabled', array($theme_list));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function listInfo() {
|
||||
if (empty($this->list)) {
|
||||
$this->list = array();
|
||||
try {
|
||||
$themes = $this->systemThemeList();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If the database is not available, rebuild the theme data.
|
||||
$themes = $this->rebuildThemeData();
|
||||
}
|
||||
|
||||
foreach ($themes as $theme) {
|
||||
foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
|
||||
foreach ($stylesheets as $stylesheet => $path) {
|
||||
$theme->stylesheets[$media][$stylesheet] = $path;
|
||||
}
|
||||
}
|
||||
foreach ($theme->info['scripts'] as $script => $path) {
|
||||
$theme->scripts[$script] = $path;
|
||||
}
|
||||
if (isset($theme->info['engine'])) {
|
||||
$theme->engine = $theme->info['engine'];
|
||||
}
|
||||
if (isset($theme->info['base theme'])) {
|
||||
$theme->base_theme = $theme->info['base theme'];
|
||||
}
|
||||
// Status is normally retrieved from the database. Add zero values when
|
||||
// read from the installation directory to prevent notices.
|
||||
if (!isset($theme->status)) {
|
||||
$theme->status = 0;
|
||||
}
|
||||
$this->list[$theme->name] = $theme;
|
||||
}
|
||||
}
|
||||
return $this->list;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset() {
|
||||
// listInfo() calls system_info() which has a lot of side effects that have
|
||||
// to be triggered like the classloading of theme classes.
|
||||
$this->list = array();
|
||||
$this->systemListReset();
|
||||
$this->listInfo();
|
||||
$this->list = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function rebuildThemeData() {
|
||||
// Find themes.
|
||||
$listing = $this->getSystemListingInfo();
|
||||
$themes = $listing->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes', 'name', 1);
|
||||
// Allow modules to add further themes.
|
||||
if ($module_themes = $this->moduleHandler->invokeAll('system_theme_info')) {
|
||||
foreach ($module_themes as $name => $uri) {
|
||||
// @see file_scan_directory()
|
||||
$themes[$name] = (object) array(
|
||||
'uri' => $uri,
|
||||
'filename' => pathinfo($uri, PATHINFO_FILENAME),
|
||||
'name' => $name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Find theme engines.
|
||||
$listing = $this->getSystemListingInfo();
|
||||
$engines = $listing->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines', 'name', 1);
|
||||
|
||||
// Set defaults for theme info.
|
||||
$defaults = array(
|
||||
'engine' => 'twig',
|
||||
'regions' => array(
|
||||
'sidebar_first' => 'Left sidebar',
|
||||
'sidebar_second' => 'Right sidebar',
|
||||
'content' => 'Content',
|
||||
'header' => 'Header',
|
||||
'footer' => 'Footer',
|
||||
'highlighted' => 'Highlighted',
|
||||
'help' => 'Help',
|
||||
'page_top' => 'Page top',
|
||||
'page_bottom' => 'Page bottom',
|
||||
),
|
||||
'description' => '',
|
||||
'features' => $this->defaultFeatures,
|
||||
'screenshot' => 'screenshot.png',
|
||||
'php' => DRUPAL_MINIMUM_PHP,
|
||||
'stylesheets' => array(),
|
||||
'scripts' => array(),
|
||||
);
|
||||
|
||||
$sub_themes = array();
|
||||
// Read info files for each theme.
|
||||
foreach ($themes as $key => $theme) {
|
||||
$themes[$key]->filename = $theme->uri;
|
||||
$themes[$key]->info = $this->infoParser->parse($theme->uri) + $defaults;
|
||||
|
||||
// Skip this extension if its type is not theme.
|
||||
if (!isset($themes[$key]->info['type']) || $themes[$key]->info['type'] != 'theme') {
|
||||
unset($themes[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the info file modification time, so it becomes available for
|
||||
// contributed modules to use for ordering theme lists.
|
||||
$themes[$key]->info['mtime'] = filemtime($theme->uri);
|
||||
|
||||
// Invoke hook_system_info_alter() to give installed modules a chance to
|
||||
// modify the data in the .info.yml files if necessary.
|
||||
$type = 'theme';
|
||||
$this->moduleHandler->alter('system_info', $themes[$key]->info, $themes[$key], $type);
|
||||
|
||||
if (!empty($themes[$key]->info['base theme'])) {
|
||||
$sub_themes[] = $key;
|
||||
}
|
||||
|
||||
$engine = $themes[$key]->info['engine'];
|
||||
if (isset($engines[$engine])) {
|
||||
$themes[$key]->owner = $engines[$engine]->uri;
|
||||
$themes[$key]->prefix = $engines[$engine]->name;
|
||||
$themes[$key]->template = TRUE;
|
||||
}
|
||||
|
||||
// Prefix stylesheets and scripts with module path.
|
||||
$path = dirname($theme->uri);
|
||||
$theme->info['stylesheets'] = $this->themeInfoPrefixPath($theme->info['stylesheets'], $path);
|
||||
$theme->info['scripts'] = $this->themeInfoPrefixPath($theme->info['scripts'], $path);
|
||||
|
||||
// Give the screenshot proper path information.
|
||||
if (!empty($themes[$key]->info['screenshot'])) {
|
||||
$themes[$key]->info['screenshot'] = $path . '/' . $themes[$key]->info['screenshot'];
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've established all our master themes, go back and fill in
|
||||
// data for sub-themes.
|
||||
foreach ($sub_themes as $key) {
|
||||
$themes[$key]->base_themes = $this->doGetBaseThemes($themes, $key);
|
||||
// Don't proceed if there was a problem with the root base theme.
|
||||
if (!current($themes[$key]->base_themes)) {
|
||||
continue;
|
||||
}
|
||||
$base_key = key($themes[$key]->base_themes);
|
||||
foreach (array_keys($themes[$key]->base_themes) as $base_theme) {
|
||||
$themes[$base_theme]->sub_themes[$key] = $themes[$key]->info['name'];
|
||||
}
|
||||
// Copy the 'owner' and 'engine' over if the top level theme uses a theme
|
||||
// engine.
|
||||
if (isset($themes[$base_key]->owner)) {
|
||||
if (isset($themes[$base_key]->info['engine'])) {
|
||||
$themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
|
||||
$themes[$key]->owner = $themes[$base_key]->owner;
|
||||
$themes[$key]->prefix = $themes[$base_key]->prefix;
|
||||
}
|
||||
else {
|
||||
$themes[$key]->prefix = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefixes all values in an .info.yml file array with a given path.
|
||||
*
|
||||
* This helper function is mainly used to prefix all array values of an
|
||||
* .info.yml file property with a single given path (to the module or theme);
|
||||
* e.g., to prefix all values of the 'stylesheets' or 'scripts' properties
|
||||
* with the file path to the defining module/theme.
|
||||
*
|
||||
* @param array $info
|
||||
* A nested array of data of an .info.yml file to be processed.
|
||||
* @param string $path
|
||||
* A file path to prepend to each value in $info.
|
||||
*
|
||||
* @return array
|
||||
* The $info array with prefixed values.
|
||||
*
|
||||
* @see _system_rebuild_module_data()
|
||||
* @see self::rebuildThemeData()
|
||||
*/
|
||||
protected function themeInfoPrefixPath(array $info, $path) {
|
||||
foreach ($info as $key => $value) {
|
||||
// Recurse into nested values until we reach the deepest level.
|
||||
if (is_array($value)) {
|
||||
$info[$key] = $this->themeInfoPrefixPath($info[$key], $path);
|
||||
}
|
||||
// Unset the original value's key and set the new value with prefix, using
|
||||
// the original value as key, so original values can still be looked up.
|
||||
else {
|
||||
unset($info[$key]);
|
||||
$info[$value] = $path . '/' . $value;
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseThemes(array $themes, $theme) {
|
||||
return $this->doGetBaseThemes($themes, $theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the base themes for the specific theme.
|
||||
*
|
||||
* @param array $themes
|
||||
* An array of available themes.
|
||||
* @param string $theme
|
||||
* The name of the theme whose base we are looking for.
|
||||
* @param array $used_themes
|
||||
* (optional) A recursion parameter preventing endless loops. Defaults to
|
||||
* an empty array.
|
||||
*
|
||||
* @return array
|
||||
* An array of base themes.
|
||||
*/
|
||||
protected function doGetBaseThemes(array $themes, $theme, $used_themes = array()) {
|
||||
if (!isset($themes[$theme]->info['base theme'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$base_key = $themes[$theme]->info['base theme'];
|
||||
// Does the base theme exist?
|
||||
if (!isset($themes[$base_key])) {
|
||||
return array($base_key => NULL);
|
||||
}
|
||||
|
||||
$current_base_theme = array($base_key => $themes[$base_key]->info['name']);
|
||||
|
||||
// Is the base theme itself a child of another theme?
|
||||
if (isset($themes[$base_key]->info['base theme'])) {
|
||||
// Do we already know the base themes of this theme?
|
||||
if (isset($themes[$base_key]->base_themes)) {
|
||||
return $themes[$base_key]->base_themes + $current_base_theme;
|
||||
}
|
||||
// Prevent loops.
|
||||
if (!empty($used_themes[$base_key])) {
|
||||
return array($base_key => NULL);
|
||||
}
|
||||
$used_themes[$base_key] = TRUE;
|
||||
return $this->getBaseThemes($themes, $base_key, $used_themes) + $current_base_theme;
|
||||
}
|
||||
// If we get here, then this is our parent theme.
|
||||
return $current_base_theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a system listing info object.
|
||||
*
|
||||
* @return \Drupal\Core\SystemListingInfo
|
||||
* The system listing object.
|
||||
*/
|
||||
protected function getSystemListingInfo() {
|
||||
if (!isset($this->systemListingInfo)) {
|
||||
$this->systemListingInfo = new SystemListingInfo();
|
||||
}
|
||||
return $this->systemListingInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the default theme config.
|
||||
*
|
||||
* @param string $theme
|
||||
* The theme to install config for.
|
||||
*/
|
||||
protected function configInstallDefaultConfig($theme) {
|
||||
config_install_default_config('theme', $theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets some other systems like rebuilding the route information or caches.
|
||||
*/
|
||||
protected function resetSystem() {
|
||||
if ($this->routeBuilder) {
|
||||
$this->routeBuilder->rebuild();
|
||||
}
|
||||
$this->systemListReset();
|
||||
|
||||
// @todo It feels wrong to have the requirement to clear the local tasks
|
||||
// cache here.
|
||||
$this->cacheBackend->deleteTags(array('local_task' => 1));
|
||||
$this->themeRegistryRebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps system_list_reset().
|
||||
*/
|
||||
protected function systemListReset() {
|
||||
system_list_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps drupal_clear_css_cache().
|
||||
*/
|
||||
protected function clearCssCache() {
|
||||
drupal_clear_css_cache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps drupal_theme_rebuild().
|
||||
*/
|
||||
protected function themeRegistryRebuild() {
|
||||
drupal_theme_rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps system_list().
|
||||
*
|
||||
* @return array
|
||||
* A list of themes keyed by name.
|
||||
*/
|
||||
protected function systemThemeList() {
|
||||
return system_list('theme');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Extension\ThemeHandlerInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Extension;
|
||||
|
||||
/**
|
||||
* Manages the list of available themes as well as enable/disable them.
|
||||
*/
|
||||
interface ThemeHandlerInterface {
|
||||
|
||||
/**
|
||||
* Enables a given list of themes.
|
||||
*
|
||||
* @param array $theme_list
|
||||
* An array of theme names.
|
||||
*
|
||||
* @throws \Drupal\Core\Extension\ExtensionNameLengthException
|
||||
* Thrown when the theme name is to long
|
||||
*/
|
||||
public function enable(array $theme_list);
|
||||
|
||||
/**
|
||||
* Disables a given list of themes.
|
||||
*
|
||||
* @param array $theme_list
|
||||
* An array of theme names.
|
||||
*/
|
||||
public function disable(array $theme_list);
|
||||
|
||||
/**
|
||||
* Returns a list of all currently available themes.
|
||||
*
|
||||
* Retrieved from the database, if available and the site is not in
|
||||
* maintenance mode; otherwise compiled freshly from the filesystem.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of the currently available themes. The keys are the
|
||||
* themes' machine names and the values are objects having the following
|
||||
* properties:
|
||||
* - filename: The filepath and name of the .info.yml file.
|
||||
* - name: The machine name of the theme.
|
||||
* - status: 1 for enabled, 0 for disabled themes.
|
||||
* - info: The contents of the .info.yml file.
|
||||
* - stylesheets: A two dimensional array, using the first key for the
|
||||
* media attribute (e.g. 'all'), the second for the name of the file
|
||||
* (e.g. style.css). The value is a complete filepath (e.g.
|
||||
* themes/bartik/style.css). Not set if no stylesheets are defined in the
|
||||
* .info.yml file.
|
||||
* - scripts: An associative array of JavaScripts, using the filename as key
|
||||
* and the complete filepath as value. Not set if no scripts are defined
|
||||
* in the .info.yml file.
|
||||
* - prefix: The base theme engine prefix.
|
||||
* - engine: The machine name of the theme engine.
|
||||
* - base_theme: If this is a sub-theme, the machine name of the base theme
|
||||
* defined in the .info.yml file. Otherwise, the element is not set.
|
||||
* - base_themes: If this is a sub-theme, an associative array of the
|
||||
* base-theme ancestors of this theme, starting with this theme's base
|
||||
* theme, then the base theme's own base theme, etc. Each entry has an
|
||||
* array key equal to the theme's machine name, and a value equal to the
|
||||
* human-readable theme name; if a theme with matching machine name does
|
||||
* not exist in the system, the value will instead be NULL (and since the
|
||||
* system would not know whether that theme itself has a base theme, that
|
||||
* will end the array of base themes). This is not set if the theme is not
|
||||
* a sub-theme.
|
||||
* - sub_themes: An associative array of themes on the system that are
|
||||
* either direct sub-themes (that is, they declare this theme to be
|
||||
* their base theme), direct sub-themes of sub-themes, etc. The keys are
|
||||
* the themes' machine names, and the values are the themes'
|
||||
* human-readable names. This element is not set if there are no themes on
|
||||
* the system that declare this theme as their base theme.
|
||||
*/
|
||||
public function listInfo();
|
||||
|
||||
/**
|
||||
* Resets the internal state of the theme handler.
|
||||
*/
|
||||
public function reset();
|
||||
|
||||
/**
|
||||
* Helper function to scan and collect theme .info.yml data and their engines.
|
||||
*
|
||||
* @return array
|
||||
* An associative array of themes information.
|
||||
*/
|
||||
public function rebuildThemeData();
|
||||
|
||||
/**
|
||||
* Finds all the base themes for the specified theme.
|
||||
*
|
||||
* Themes can inherit templates and function implementations from earlier
|
||||
* themes.
|
||||
*
|
||||
* @param array $themes
|
||||
* An array of available themes.
|
||||
* @param string $theme
|
||||
* The name of the theme whose base we are looking for.
|
||||
*
|
||||
* @return array
|
||||
* Returns an array of all of the theme's ancestors; the first element's
|
||||
* value will be NULL if an error occurred.
|
||||
*/
|
||||
public function getBaseThemes(array $themes, $theme);
|
||||
|
||||
}
|
|
@ -2461,117 +2461,11 @@ function system_rebuild_module_data() {
|
|||
*
|
||||
* @return
|
||||
* An associative array of themes information.
|
||||
*
|
||||
* @deprecated as of Drupal 8.0. Use \Drupal::service('theme_handler')->rebuildThemeData().
|
||||
*/
|
||||
function _system_rebuild_theme_data() {
|
||||
// Find themes
|
||||
$themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes');
|
||||
// Allow modules to add further themes.
|
||||
if ($module_themes = \Drupal::moduleHandler()->invokeAll('system_theme_info')) {
|
||||
foreach ($module_themes as $name => $uri) {
|
||||
// @see file_scan_directory()
|
||||
$themes[$name] = (object) array(
|
||||
'uri' => $uri,
|
||||
'filename' => pathinfo($uri, PATHINFO_FILENAME),
|
||||
'name' => $name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Find theme engines
|
||||
$engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines');
|
||||
|
||||
// Set defaults for theme info.
|
||||
$defaults = array(
|
||||
'engine' => 'twig',
|
||||
'regions' => array(
|
||||
'sidebar_first' => 'Left sidebar',
|
||||
'sidebar_second' => 'Right sidebar',
|
||||
'content' => 'Content',
|
||||
'header' => 'Header',
|
||||
'footer' => 'Footer',
|
||||
'highlighted' => 'Highlighted',
|
||||
'help' => 'Help',
|
||||
'page_top' => 'Page top',
|
||||
'page_bottom' => 'Page bottom',
|
||||
),
|
||||
'description' => '',
|
||||
'features' => _system_default_theme_features(),
|
||||
'screenshot' => 'screenshot.png',
|
||||
'php' => DRUPAL_MINIMUM_PHP,
|
||||
'stylesheets' => array(),
|
||||
'scripts' => array(),
|
||||
);
|
||||
|
||||
$sub_themes = array();
|
||||
// Read info files for each theme
|
||||
foreach ($themes as $key => $theme) {
|
||||
$themes[$key]->filename = $theme->uri;
|
||||
$themes[$key]->info = \Drupal::service('info_parser')->parse($theme->uri) + $defaults;
|
||||
|
||||
// Skip this extension if its type is not theme.
|
||||
if (!isset($themes[$key]->info['type']) || $themes[$key]->info['type'] != 'theme') {
|
||||
unset($themes[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the info file modification time, so it becomes available for
|
||||
// contributed modules to use for ordering theme lists.
|
||||
$themes[$key]->info['mtime'] = filemtime($theme->uri);
|
||||
|
||||
// Invoke hook_system_info_alter() to give installed modules a chance to
|
||||
// modify the data in the .info.yml files if necessary.
|
||||
$type = 'theme';
|
||||
drupal_alter('system_info', $themes[$key]->info, $themes[$key], $type);
|
||||
|
||||
if (!empty($themes[$key]->info['base theme'])) {
|
||||
$sub_themes[] = $key;
|
||||
}
|
||||
|
||||
$engine = $themes[$key]->info['engine'];
|
||||
if (isset($engines[$engine])) {
|
||||
$themes[$key]->owner = $engines[$engine]->uri;
|
||||
$themes[$key]->prefix = $engines[$engine]->name;
|
||||
$themes[$key]->template = TRUE;
|
||||
}
|
||||
|
||||
// Prefix stylesheets and scripts with module path.
|
||||
$path = dirname($theme->uri);
|
||||
$theme->info['stylesheets'] = _system_info_add_path($theme->info['stylesheets'], $path);
|
||||
$theme->info['scripts'] = _system_info_add_path($theme->info['scripts'], $path);
|
||||
|
||||
// Give the screenshot proper path information.
|
||||
if (!empty($themes[$key]->info['screenshot'])) {
|
||||
$themes[$key]->info['screenshot'] = $path . '/' . $themes[$key]->info['screenshot'];
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've established all our master themes, go back and fill in data
|
||||
// for subthemes.
|
||||
foreach ($sub_themes as $key) {
|
||||
$themes[$key]->base_themes = drupal_find_base_themes($themes, $key);
|
||||
// Don't proceed if there was a problem with the root base theme.
|
||||
if (!current($themes[$key]->base_themes)) {
|
||||
continue;
|
||||
}
|
||||
$base_key = key($themes[$key]->base_themes);
|
||||
foreach (array_keys($themes[$key]->base_themes) as $base_theme) {
|
||||
$themes[$base_theme]->sub_themes[$key] = $themes[$key]->info['name'];
|
||||
}
|
||||
// Copy the 'owner' and 'engine' over if the top level theme uses a theme
|
||||
// engine.
|
||||
if (isset($themes[$base_key]->owner)) {
|
||||
if (isset($themes[$base_key]->info['engine'])) {
|
||||
$themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
|
||||
$themes[$key]->owner = $themes[$base_key]->owner;
|
||||
$themes[$key]->prefix = $themes[$base_key]->prefix;
|
||||
}
|
||||
else {
|
||||
$themes[$key]->prefix = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $themes;
|
||||
return \Drupal::service('theme_handler')->rebuildThemeData();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2607,41 +2501,6 @@ function system_rebuild_theme_data() {
|
|||
return $themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefixes all values in an .info.yml file array with a given path.
|
||||
*
|
||||
* This helper function is mainly used to prefix all array values of an
|
||||
* .info.yml file property with a single given path (to the module or theme);
|
||||
* e.g., to prefix all values of the 'stylesheets' or 'scripts' properties with
|
||||
* the file path to the defining module/theme.
|
||||
*
|
||||
* @param $info
|
||||
* A nested array of data of an .info.yml file to be processed.
|
||||
* @param $path
|
||||
* A file path to prepend to each value in $info.
|
||||
*
|
||||
* @return
|
||||
* The $info array with prefixed values.
|
||||
*
|
||||
* @see _system_rebuild_module_data()
|
||||
* @see _system_rebuild_theme_data()
|
||||
*/
|
||||
function _system_info_add_path($info, $path) {
|
||||
foreach ($info as $key => $value) {
|
||||
// Recurse into nested values until we reach the deepest level.
|
||||
if (is_array($value)) {
|
||||
$info[$key] = _system_info_add_path($info[$key], $path);
|
||||
}
|
||||
// Unset the original value's key and set the new value with prefix, using
|
||||
// the original value as key, so original values can still be looked up.
|
||||
else {
|
||||
unset($info[$key]);
|
||||
$info[$value] = $path . '/' . $value;
|
||||
}
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of default theme features.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,428 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Core\Extension\ThemeHandlerTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Core\Extension;
|
||||
|
||||
use Drupal\Core\Extension\InfoParser;
|
||||
use Drupal\Core\Extension\ThemeHandler;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the theme handler.
|
||||
*
|
||||
* @group Drupal
|
||||
* @group Theme
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ThemeHandler
|
||||
*/
|
||||
class ThemeHandlerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The mocked route builder.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteBuilder|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $routeBuilder;
|
||||
|
||||
/**
|
||||
* The mocked info parser.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\InfoParserInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $infoParser;
|
||||
|
||||
/**
|
||||
* The mocked cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $cacheBackend;
|
||||
|
||||
/**
|
||||
* The mocked config factory.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactory|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* The mocked module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The system listing info.
|
||||
*
|
||||
* @var \Drupal\Core\SystemListingInfo|\PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
protected $systemListingInfo;
|
||||
|
||||
/**
|
||||
* The tested theme handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandler|\Drupal\Tests\Core\Extension\TestThemeHandler
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Theme handler',
|
||||
'description' => 'Tests the theme handler.',
|
||||
'group' => 'Theme',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$this->configFactory = $this->getConfigFactoryStub(array('system.theme' => array(), 'system.theme.disabled' => array()));
|
||||
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
$this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
|
||||
$this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface');
|
||||
$this->routeBuilder = $this->getMockBuilder('Drupal\Core\Routing\RouteBuilder')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->systemListingInfo = $this->getMockBuilder('Drupal\Core\SystemListingInfo')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->themeHandler = new TestThemeHandler($this->configFactory, $this->moduleHandler, $this->cacheBackend, $this->infoParser, $this->routeBuilder, $this->systemListingInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests enabling a theme with a name longer than 50 chars.
|
||||
*
|
||||
* @expectedException \Drupal\Core\Extension\ExtensionNameLengthException
|
||||
* @expectedExceptionMessage Theme name <em class="placeholder">thisNameIsFarTooLong0000000000000000000000000000051</em> is over the maximum allowed length of 50 characters.
|
||||
*/
|
||||
public function testThemeEnableWithTooLongName() {
|
||||
$this->themeHandler->enable(array('thisNameIsFarTooLong0000000000000000000000000000051'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests enabling a single theme.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ThemeHandler::enable()
|
||||
*/
|
||||
public function testEnableSingleTheme() {
|
||||
$theme_list = array('theme_test');
|
||||
|
||||
$this->configFactory->get('system.theme')
|
||||
->expects($this->once())
|
||||
->method('set')
|
||||
->with('enabled.theme_test', 0)
|
||||
->will($this->returnSelf());
|
||||
$this->configFactory->get('system.theme')
|
||||
->expects($this->once())
|
||||
->method('save');
|
||||
|
||||
$this->configFactory->get('system.theme.disabled')
|
||||
->expects($this->once())
|
||||
->method('clear')
|
||||
->with('theme_test')
|
||||
->will($this->returnSelf());
|
||||
$this->configFactory->get('system.theme.disabled')
|
||||
->expects($this->once())
|
||||
->method('save');
|
||||
|
||||
$this->systemListingInfo->expects($this->any())
|
||||
->method('scan')
|
||||
->will($this->returnValue(array()));
|
||||
|
||||
// Ensure that the themes_enabled hook is fired.
|
||||
$this->moduleHandler->expects($this->at(0))
|
||||
->method('invokeAll')
|
||||
->with('system_theme_info')
|
||||
->will($this->returnValue(array()));
|
||||
|
||||
$this->moduleHandler->expects($this->at(1))
|
||||
->method('invokeAll')
|
||||
->with('themes_enabled', array($theme_list));
|
||||
|
||||
$this->themeHandler->enable($theme_list);
|
||||
|
||||
$this->assertTrue($this->themeHandler->clearedCssCache);
|
||||
$this->assertTrue($this->themeHandler->registryRebuild);
|
||||
$this->assertTrue($this->themeHandler->installedDefaultConfig['theme_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that enabling a theme does clear the theme info listing.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ThemeHandler::listInfo()
|
||||
*/
|
||||
public function testEnableAndListInfo() {
|
||||
$this->configFactory->get('system.theme')
|
||||
->expects($this->exactly(2))
|
||||
->method('set')
|
||||
->will($this->returnSelf());
|
||||
|
||||
$this->configFactory->get('system.theme.disabled')
|
||||
->expects($this->exactly(2))
|
||||
->method('clear')
|
||||
->will($this->returnSelf());
|
||||
|
||||
$this->systemListingInfo->expects($this->any())
|
||||
->method('scan')
|
||||
->will($this->returnValue(array()));
|
||||
|
||||
$this->themeHandler->enable(array('bartik'));
|
||||
$this->themeHandler->systemList['bartik'] = (object) array(
|
||||
'name' => 'bartik',
|
||||
'info' => array(
|
||||
'stylesheets' => array(
|
||||
'all' => array(
|
||||
'css/layout.css',
|
||||
'css/style.css',
|
||||
'css/colors.css',
|
||||
),
|
||||
),
|
||||
'scripts' => array(
|
||||
'example' => 'theme.js',
|
||||
),
|
||||
'engine' => 'twig',
|
||||
'base theme' => 'stark',
|
||||
),
|
||||
);
|
||||
|
||||
$list_info = $this->themeHandler->listInfo();
|
||||
$this->assertCount(1, $list_info);
|
||||
|
||||
$this->assertEquals($this->themeHandler->systemList['bartik']->info['stylesheets'], $list_info['bartik']->stylesheets);
|
||||
$this->assertEquals($this->themeHandler->systemList['bartik']->scripts, $list_info['bartik']->scripts);
|
||||
$this->assertEquals('twig', $list_info['bartik']->engine);
|
||||
$this->assertEquals('stark', $list_info['bartik']->base_theme);
|
||||
$this->assertEquals(0, $list_info['bartik']->status);
|
||||
|
||||
$this->themeHandler->systemList['seven'] = (object) array(
|
||||
'name' => 'seven',
|
||||
'info' => array(
|
||||
'stylesheets' => array(
|
||||
'screen' => array(
|
||||
'style.css',
|
||||
),
|
||||
),
|
||||
'scripts' => array(),
|
||||
),
|
||||
'status' => 1,
|
||||
);
|
||||
|
||||
$this->themeHandler->enable(array('seven'));
|
||||
|
||||
$list_info = $this->themeHandler->listInfo();
|
||||
$this->assertCount(2, $list_info);
|
||||
|
||||
$this->assertEquals($this->themeHandler->systemList['seven']->info['stylesheets'], $list_info['seven']->stylesheets);
|
||||
$this->assertEquals(1, $list_info['seven']->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rebuilding the theme data.
|
||||
*
|
||||
* @see \Drupal\Core\Extension\ThemeHandler::rebuildThemeData()
|
||||
*/
|
||||
public function testRebuildThemeData() {
|
||||
$this->systemListingInfo->expects($this->at(0))
|
||||
->method('scan')
|
||||
->with($this->anything(), 'themes', 'name', 1)
|
||||
->will($this->returnValue(array(
|
||||
'seven' => (object) array(
|
||||
'name' => 'seven',
|
||||
'uri' => DRUPAL_ROOT . '/core/themes/seven/seven.info.yml',
|
||||
),
|
||||
)));
|
||||
$this->infoParser->expects($this->once())
|
||||
->method('parse')
|
||||
->with(DRUPAL_ROOT . '/core/themes/seven/seven.info.yml')
|
||||
->will($this->returnCallback(function ($file) {
|
||||
$info_parser = new InfoParser();
|
||||
return $info_parser->parse($file);
|
||||
}));
|
||||
|
||||
$this->moduleHandler->expects($this->once())
|
||||
->method('alter');
|
||||
|
||||
$theme_data = $this->themeHandler->rebuildThemeData();
|
||||
$this->assertCount(1, $theme_data);
|
||||
$info = $theme_data['seven'];
|
||||
|
||||
// Ensure some basic properties.
|
||||
$this->assertInstanceOf('stdClass', $info);
|
||||
$this->assertEquals('seven', $info->name);
|
||||
$this->assertEquals(DRUPAL_ROOT . '/core/themes/seven/seven.info.yml', $info->uri);
|
||||
$this->assertEquals(DRUPAL_ROOT . '/core/themes/seven/seven.info.yml', $info->filename);
|
||||
|
||||
$this->assertEquals('twig', $info->info['engine']);
|
||||
$this->assertEquals(array(), $info->info['scripts']);
|
||||
|
||||
// Ensure that the css paths are set with the proper prefix.
|
||||
$this->assertEquals(array(
|
||||
'screen' => array(
|
||||
'style.css' => DRUPAL_ROOT . '/core/themes/seven/style.css',
|
||||
'css/components/buttons.css' => DRUPAL_ROOT . '/core/themes/seven/css/components/buttons.css',
|
||||
'css/components/buttons.theme.css' => DRUPAL_ROOT . '/core/themes/seven/css/components/buttons.theme.css',
|
||||
),
|
||||
), $info->info['stylesheets']);
|
||||
$this->assertEquals(DRUPAL_ROOT . '/core/themes/seven/screenshot.png', $info->info['screenshot']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getting the base themes for a set a defines themes.
|
||||
*
|
||||
* @param array $themes
|
||||
* An array of available themes, keyed by the theme name.
|
||||
* @param string $theme
|
||||
* The theme name to find all its base themes.
|
||||
* @param array $expected
|
||||
* The expected base themes.
|
||||
*
|
||||
* @dataProvider providerTestGetBaseThemes
|
||||
*/
|
||||
public function testGetBaseThemes(array $themes, $theme, array $expected) {
|
||||
$base_themes = $this->themeHandler->getBaseThemes($themes, $theme);
|
||||
$this->assertEquals($expected, $base_themes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for testGetBaseThemes.
|
||||
*
|
||||
* @return array
|
||||
* An array of theme test data.
|
||||
*/
|
||||
public function providerTestGetBaseThemes() {
|
||||
$data = array();
|
||||
|
||||
// Tests a theme without any base theme.
|
||||
$themes = array();
|
||||
$themes['test_1'] = (object) array(
|
||||
'name' => 'test_1',
|
||||
'info' => array(
|
||||
'name' => 'test_1',
|
||||
),
|
||||
);
|
||||
$data[] = array($themes, 'test_1', array());
|
||||
|
||||
// Tests a theme with a non existing base theme.
|
||||
$themes = array();
|
||||
$themes['test_1'] = (object) array(
|
||||
'name' => 'test_1',
|
||||
'info' => array(
|
||||
'name' => 'test_1',
|
||||
'base theme' => 'test_2',
|
||||
),
|
||||
);
|
||||
$data[] = array($themes, 'test_1', array('test_2' => NULL));
|
||||
|
||||
// Tests a theme with a single existing base theme.
|
||||
$themes = array();
|
||||
$themes['test_1'] = (object) array(
|
||||
'name' => 'test_1',
|
||||
'info' => array(
|
||||
'name' => 'test_1',
|
||||
'base theme' => 'test_2',
|
||||
),
|
||||
);
|
||||
$themes['test_2'] = (object) array(
|
||||
'name' => 'test_2',
|
||||
'info' => array(
|
||||
'name' => 'test_2',
|
||||
),
|
||||
);
|
||||
$data[] = array($themes, 'test_1', array('test_2' => 'test_2'));
|
||||
|
||||
// Tests a theme with multiple base themes.
|
||||
$themes = array();
|
||||
$themes['test_1'] = (object) array(
|
||||
'name' => 'test_1',
|
||||
'info' => array(
|
||||
'name' => 'test_1',
|
||||
'base theme' => 'test_2',
|
||||
),
|
||||
);
|
||||
$themes['test_2'] = (object) array(
|
||||
'name' => 'test_2',
|
||||
'info' => array(
|
||||
'name' => 'test_2',
|
||||
'base theme' => 'test_3',
|
||||
),
|
||||
);
|
||||
$themes['test_3'] = (object) array(
|
||||
'name' => 'test_3',
|
||||
'info' => array(
|
||||
'name' => 'test_3',
|
||||
),
|
||||
);
|
||||
$data[] = array(
|
||||
$themes,
|
||||
'test_1',
|
||||
array('test_2' => 'test_2', 'test_3' => 'test_3'),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the default theme handler to mock some drupal_ methods.
|
||||
*/
|
||||
class TestThemeHandler extends ThemeHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function clearCssCache() {
|
||||
$this->clearedCssCache = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function themeRegistryRebuild() {
|
||||
$this->registryRebuild = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configInstallDefaultConfig($theme) {
|
||||
$this->installedDefaultConfig[$theme] = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function systemThemeList() {
|
||||
return $this->systemList;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function systemListReset() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!defined('DRUPAL_EXTENSION_NAME_MAX_LENGTH')) {
|
||||
define('DRUPAL_EXTENSION_NAME_MAX_LENGTH', 50);
|
||||
}
|
||||
if (!defined('DRUPAL_PHP_FUNCTION_PATTERN')) {
|
||||
define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
|
||||
}
|
||||
if (!defined('DRUPAL_ROOT')) {
|
||||
define('DRUPAL_ROOT', dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__)))));
|
||||
}
|
||||
if (!defined('DRUPAL_MINIMUM_PHP')) {
|
||||
define('DRUPAL_MINIMUM_PHP', '5.3.10');
|
||||
}
|
Loading…
Reference in New Issue