Issue #2659940 by almaudoh, alexpott, phenaproxima, Jo Fitzgerald, markcarver, oriol_e9g, dawehner, dsnopek, jibran, larowlan: Extension System, Part III: ThemeExtensionList and ThemeEngineExtensionList
parent
bab4f6d3f3
commit
10722cd872
|
|
@ -517,6 +517,12 @@ services:
|
||||||
extension.list.profile:
|
extension.list.profile:
|
||||||
class: Drupal\Core\Extension\ProfileExtensionList
|
class: Drupal\Core\Extension\ProfileExtensionList
|
||||||
arguments: ['@app.root', 'profile', '@cache.default', '@info_parser', '@module_handler', '@state', '%install_profile%']
|
arguments: ['@app.root', 'profile', '@cache.default', '@info_parser', '@module_handler', '@state', '%install_profile%']
|
||||||
|
extension.list.theme:
|
||||||
|
class: Drupal\Core\Extension\ThemeExtensionList
|
||||||
|
arguments: ['@app.root', 'theme', '@cache.default', '@info_parser', '@module_handler', '@state', '@config.factory', '@extension.list.theme_engine', '%install_profile%']
|
||||||
|
extension.list.theme_engine:
|
||||||
|
class: Drupal\Core\Extension\ThemeEngineExtensionList
|
||||||
|
arguments: ['@app.root', 'theme_engine', '@cache.default', '@info_parser', '@module_handler', '@state', '%install_profile%']
|
||||||
content_uninstall_validator:
|
content_uninstall_validator:
|
||||||
class: Drupal\Core\Entity\ContentUninstallValidator
|
class: Drupal\Core\Entity\ContentUninstallValidator
|
||||||
tags:
|
tags:
|
||||||
|
|
@ -531,7 +537,7 @@ services:
|
||||||
lazy: true
|
lazy: true
|
||||||
theme_handler:
|
theme_handler:
|
||||||
class: Drupal\Core\Extension\ThemeHandler
|
class: Drupal\Core\Extension\ThemeHandler
|
||||||
arguments: ['@app.root', '@config.factory', '@module_handler', '@state', '@info_parser']
|
arguments: ['@app.root', '@config.factory', '@extension.list.theme']
|
||||||
theme_installer:
|
theme_installer:
|
||||||
class: Drupal\Core\Extension\ThemeInstaller
|
class: Drupal\Core\Extension\ThemeInstaller
|
||||||
arguments: ['@theme_handler', '@config.factory', '@config.installer', '@module_handler', '@config.manager', '@asset.css.collection_optimizer', '@router.builder', '@logger.channel.default', '@state']
|
arguments: ['@theme_handler', '@config.factory', '@config.installer', '@module_handler', '@config.manager', '@asset.css.collection_optimizer', '@router.builder', '@logger.channel.default', '@state']
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ use Drupal\Component\Utility\Html;
|
||||||
use Drupal\Component\Render\FormattableMarkup;
|
use Drupal\Component\Render\FormattableMarkup;
|
||||||
use Drupal\Component\Utility\Unicode;
|
use Drupal\Component\Utility\Unicode;
|
||||||
use Drupal\Core\Config\BootstrapConfigStorageFactory;
|
use Drupal\Core\Config\BootstrapConfigStorageFactory;
|
||||||
use Drupal\Core\Extension\Exception\UnknownExtensionException;
|
|
||||||
use Drupal\Core\Logger\RfcLogLevel;
|
use Drupal\Core\Logger\RfcLogLevel;
|
||||||
use Drupal\Core\Test\TestDatabase;
|
use Drupal\Core\Test\TestDatabase;
|
||||||
use Drupal\Core\Session\AccountInterface;
|
use Drupal\Core\Session\AccountInterface;
|
||||||
use Drupal\Core\Utility\Error;
|
use Drupal\Core\Utility\Error;
|
||||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||||
|
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum supported version of PHP.
|
* Minimum supported version of PHP.
|
||||||
|
|
@ -223,10 +223,6 @@ function config_get_config_directory($type) {
|
||||||
* The filename of the requested item or NULL if the item is not found.
|
* The filename of the requested item or NULL if the item is not found.
|
||||||
*/
|
*/
|
||||||
function drupal_get_filename($type, $name, $filename = NULL) {
|
function drupal_get_filename($type, $name, $filename = NULL) {
|
||||||
// The location of files will not change during the request, so do not use
|
|
||||||
// drupal_static().
|
|
||||||
static $files = [];
|
|
||||||
|
|
||||||
// Type 'core' only exists to simplify application-level logic; it always maps
|
// Type 'core' only exists to simplify application-level logic; it always maps
|
||||||
// to the /core directory, whereas $name is ignored. It is only requested via
|
// to the /core directory, whereas $name is ignored. It is only requested via
|
||||||
// drupal_get_path(). /core/core.info.yml does not exist, but is required
|
// drupal_get_path(). /core/core.info.yml does not exist, but is required
|
||||||
|
|
@ -235,45 +231,31 @@ function drupal_get_filename($type, $name, $filename = NULL) {
|
||||||
return 'core/core.info.yml';
|
return 'core/core.info.yml';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type === 'module' || $type === 'profile') {
|
try {
|
||||||
$service_id = 'extension.list.' . $type;
|
|
||||||
/** @var \Drupal\Core\Extension\ExtensionList $extension_list */
|
/** @var \Drupal\Core\Extension\ExtensionList $extension_list */
|
||||||
$extension_list = \Drupal::service($service_id);
|
$extension_list = \Drupal::service("extension.list.$type");
|
||||||
if (isset($filename)) {
|
if (isset($filename)) {
|
||||||
// Manually add the info file path of an extension.
|
// Manually add the info file path of an extension.
|
||||||
$extension_list->setPathname($name, $filename);
|
$extension_list->setPathname($name, $filename);
|
||||||
}
|
}
|
||||||
try {
|
return $extension_list->getPathname($name);
|
||||||
return $extension_list->getPathname($name);
|
|
||||||
}
|
|
||||||
catch (UnknownExtensionException $e) {
|
|
||||||
// Catch the exception. This will result in triggering an error.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
catch (ServiceNotFoundException $e) {
|
||||||
|
// Catch the exception. This will result in triggering an error.
|
||||||
if (!isset($files[$type])) {
|
// If the service is unknown, create a user-level error message.
|
||||||
$files[$type] = [];
|
trigger_error(
|
||||||
}
|
sprintf('Unknown type specified: "%s". Must be one of: "core", "profile", "module", "theme", or "theme_engine".', $type),
|
||||||
|
E_USER_WARNING
|
||||||
if (isset($filename)) {
|
);
|
||||||
$files[$type][$name] = $filename;
|
}
|
||||||
}
|
catch (\InvalidArgumentException $e) {
|
||||||
elseif (!isset($files[$type][$name])) {
|
// Catch the exception. This will result in triggering an error.
|
||||||
// If still unknown, retrieve the file list prepared in state by
|
// If the filename is still unknown, create a user-level error message.
|
||||||
// \Drupal\Core\Extension\ExtensionList() and
|
trigger_error(
|
||||||
// \Drupal\Core\Extension\ThemeHandlerInterface::rebuildThemeData().
|
sprintf('The following %s is missing from the file system: %s', $type, $name),
|
||||||
if (!isset($files[$type][$name]) && \Drupal::hasService('state')) {
|
E_USER_WARNING
|
||||||
$files[$type] += \Drupal::state()->get('system.' . $type . '.files', []);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($files[$type][$name])) {
|
|
||||||
return $files[$type][$name];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the filename is still unknown, create a user-level error message.
|
|
||||||
trigger_error(new FormattableMarkup('The following @type is missing from the file system: @name', ['@type' => $type, '@name' => $name]), E_USER_WARNING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -14,41 +14,31 @@ use Drupal\Core\Extension\ExtensionDiscovery;
|
||||||
* The type of list to return:
|
* The type of list to return:
|
||||||
* - theme: All installed themes.
|
* - theme: All installed themes.
|
||||||
*
|
*
|
||||||
* @return
|
* @return array
|
||||||
* An associative array of themes, keyed by name.
|
* An associative array of themes, keyed by name.
|
||||||
* For $type 'theme', the array values are objects representing the
|
* For $type 'theme', the array values are objects representing the
|
||||||
* respective database row, with the 'info' property already unserialized.
|
* respective database row, with the 'info' property already unserialized.
|
||||||
*
|
*
|
||||||
|
* @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use
|
||||||
|
* \Drupal::service('theme_handler')->listInfo() instead.
|
||||||
|
*
|
||||||
|
* @see https://www.drupal.org/node/2709919
|
||||||
* @see \Drupal\Core\Extension\ThemeHandler::listInfo()
|
* @see \Drupal\Core\Extension\ThemeHandler::listInfo()
|
||||||
*/
|
*/
|
||||||
function system_list($type) {
|
function system_list($type) {
|
||||||
$lists = &drupal_static(__FUNCTION__);
|
@trigger_error('system_list() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal::service(\'theme_handler\')->listInfo() instead. See https://www.drupal.org/node/2709919', E_USER_DEPRECATED);
|
||||||
if ($cached = \Drupal::cache('bootstrap')->get('system_list')) {
|
|
||||||
$lists = $cached->data;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$lists = [
|
|
||||||
'theme' => [],
|
|
||||||
'filepaths' => [],
|
|
||||||
];
|
|
||||||
// ThemeHandler maintains the 'system.theme.data' state record.
|
|
||||||
$theme_data = \Drupal::state()->get('system.theme.data', []);
|
|
||||||
foreach ($theme_data as $name => $theme) {
|
|
||||||
$lists['theme'][$name] = $theme;
|
|
||||||
$lists['filepaths'][] = [
|
|
||||||
'type' => 'theme',
|
|
||||||
'name' => $name,
|
|
||||||
'filepath' => $theme->getPathname(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
\Drupal::cache('bootstrap')->set('system_list', $lists);
|
|
||||||
}
|
|
||||||
// To avoid a separate database lookup for the filepath, prime the
|
|
||||||
// drupal_get_filename() static cache with all enabled themes.
|
|
||||||
foreach ($lists['filepaths'] as $item) {
|
|
||||||
system_register($item['type'], $item['name'], $item['filepath']);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$lists = [
|
||||||
|
'theme' => \Drupal::service('theme_handler')->listInfo(),
|
||||||
|
'filepaths' => [],
|
||||||
|
];
|
||||||
|
foreach ($lists['theme'] as $name => $theme) {
|
||||||
|
$lists['filepaths'][] = [
|
||||||
|
'type' => 'theme',
|
||||||
|
'name' => $name,
|
||||||
|
'filepath' => $theme->getPathname(),
|
||||||
|
];
|
||||||
|
}
|
||||||
return $lists[$type];
|
return $lists[$type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,9 +46,10 @@ function system_list($type) {
|
||||||
* Resets all system_list() caches.
|
* Resets all system_list() caches.
|
||||||
*/
|
*/
|
||||||
function system_list_reset() {
|
function system_list_reset() {
|
||||||
drupal_static_reset('system_list');
|
\Drupal::service('extension.list.profile')->reset();
|
||||||
\Drupal::service('extension.list.module')->reset();
|
\Drupal::service('extension.list.module')->reset();
|
||||||
\Drupal::cache('bootstrap')->delete('system_list');
|
\Drupal::service('extension.list.theme_engine')->reset();
|
||||||
|
\Drupal::service('extension.list.theme')->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ function theme_get_registry($complete = TRUE) {
|
||||||
/**
|
/**
|
||||||
* Returns an array of default theme features.
|
* Returns an array of default theme features.
|
||||||
*
|
*
|
||||||
* @see \Drupal\Core\Extension\ThemeHandler::$defaultFeatures
|
* @see \Drupal\Core\Extension\ThemeExtensionList::$defaults
|
||||||
*/
|
*/
|
||||||
function _system_default_theme_features() {
|
function _system_default_theme_features() {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ function _drupal_maintenance_theme() {
|
||||||
$theme_init = \Drupal::service('theme.initialization');
|
$theme_init = \Drupal::service('theme.initialization');
|
||||||
$theme_handler = \Drupal::service('theme_handler');
|
$theme_handler = \Drupal::service('theme_handler');
|
||||||
if (empty($themes) || !isset($themes[$custom_theme])) {
|
if (empty($themes) || !isset($themes[$custom_theme])) {
|
||||||
$themes = $theme_handler->rebuildThemeData();
|
$themes = \Drupal::service('extension.list.theme')->getList();
|
||||||
$theme_handler->addTheme($themes[$custom_theme]);
|
$theme_handler->addTheme($themes[$custom_theme]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -169,8 +169,8 @@ class Extension implements \Serializable {
|
||||||
'filename' => $this->filename,
|
'filename' => $this->filename,
|
||||||
];
|
];
|
||||||
|
|
||||||
// @todo ThemeHandler::listInfo(), ThemeHandler::rebuildThemeData(), and
|
// @todo \Drupal\Core\Extension\ThemeExtensionList is adding custom
|
||||||
// system_list() are adding custom properties to the Extension object.
|
// properties to the Extension object.
|
||||||
$info = new \ReflectionObject($this);
|
$info = new \ReflectionObject($this);
|
||||||
foreach ($info->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
|
foreach ($info->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
|
||||||
$data[$property->getName()] = $property->getValue($this);
|
$data[$property->getName()] = $property->getValue($this);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ use Drupal\Core\State\StateInterface;
|
||||||
* Provides available extensions.
|
* Provides available extensions.
|
||||||
*
|
*
|
||||||
* The extension list is per extension type, like module, theme and profile.
|
* The extension list is per extension type, like module, theme and profile.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* This class is not yet stable and therefore there are no guarantees that the
|
||||||
|
* internal implementations including constructor signature and protected
|
||||||
|
* properties / methods will not change over time. This will be reviewed after
|
||||||
|
* https://www.drupal.org/project/drupal/issues/2940481
|
||||||
*/
|
*/
|
||||||
abstract class ExtensionList {
|
abstract class ExtensionList {
|
||||||
|
|
||||||
|
|
@ -305,15 +311,7 @@ abstract class ExtensionList {
|
||||||
|
|
||||||
// Read info files for each extension.
|
// Read info files for each extension.
|
||||||
foreach ($extensions as $extension_name => $extension) {
|
foreach ($extensions as $extension_name => $extension) {
|
||||||
// Look for the info file.
|
$extension->info = $this->createExtensionInfo($extension);
|
||||||
$extension->info = $this->infoParser->parse($extension->getPathname());
|
|
||||||
|
|
||||||
// Add the info file modification time, so it becomes available for
|
|
||||||
// contributed extensions to use for ordering extension lists.
|
|
||||||
$extension->info['mtime'] = $extension->getMTime();
|
|
||||||
|
|
||||||
// Merge extension type-specific defaults.
|
|
||||||
$extension->info += $this->defaults;
|
|
||||||
|
|
||||||
// Invoke hook_system_info_alter() to give installed modules a chance to
|
// Invoke hook_system_info_alter() to give installed modules a chance to
|
||||||
// modify the data in the .info.yml files if necessary.
|
// modify the data in the .info.yml files if necessary.
|
||||||
|
|
@ -541,4 +539,26 @@ abstract class ExtensionList {
|
||||||
return dirname($this->getPathname($extension_name));
|
return dirname($this->getPathname($extension_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the info value for an extension object.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Extension\Extension $extension
|
||||||
|
* The extension whose info is to be altered.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* The extension info array.
|
||||||
|
*/
|
||||||
|
protected function createExtensionInfo(Extension $extension) {
|
||||||
|
$info = $this->infoParser->parse($extension->getPathname());
|
||||||
|
|
||||||
|
// Add the info file modification time, so it becomes available for
|
||||||
|
// contributed extensions to use for ordering extension lists.
|
||||||
|
$info['mtime'] = $extension->getMTime();
|
||||||
|
|
||||||
|
// Merge extension type-specific defaults.
|
||||||
|
$info += $this->defaults;
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,12 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of available modules.
|
* Provides a list of available modules.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* This class is not yet stable and therefore there are no guarantees that the
|
||||||
|
* internal implementations including constructor signature and protected
|
||||||
|
* properties / methods will not change over time. This will be reviewed after
|
||||||
|
* https://www.drupal.org/project/drupal/issues/2940481
|
||||||
*/
|
*/
|
||||||
class ModuleExtensionList extends ExtensionList {
|
class ModuleExtensionList extends ExtensionList {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,12 @@ namespace Drupal\Core\Extension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a list of installation profiles.
|
* Provides a list of installation profiles.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* This class is not yet stable and therefore there are no guarantees that the
|
||||||
|
* internal implementations including constructor signature and protected
|
||||||
|
* properties / methods will not change over time. This will be reviewed after
|
||||||
|
* https://www.drupal.org/project/drupal/issues/2940481
|
||||||
*/
|
*/
|
||||||
class ProfileExtensionList extends ExtensionList {
|
class ProfileExtensionList extends ExtensionList {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Core\Extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a list of available theme engines.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* This class is not yet stable and therefore there are no guarantees that the
|
||||||
|
* internal implementations including constructor signature and protected
|
||||||
|
* properties / methods will not change over time. This will be reviewed after
|
||||||
|
* https://www.drupal.org/project/drupal/issues/2940481
|
||||||
|
*/
|
||||||
|
class ThemeEngineExtensionList extends ExtensionList {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $defaults = [
|
||||||
|
'dependencies' => [],
|
||||||
|
'description' => '',
|
||||||
|
'package' => 'Other',
|
||||||
|
'version' => NULL,
|
||||||
|
'php' => DRUPAL_MINIMUM_PHP,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getInstalledExtensionNames() {
|
||||||
|
// Theme engines do not have an 'install' state, so return names of all
|
||||||
|
// discovered theme engines.
|
||||||
|
return array_keys($this->extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,289 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Core\Extension;
|
||||||
|
|
||||||
|
use Drupal\Core\Cache\CacheBackendInterface;
|
||||||
|
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||||
|
use Drupal\Core\State\StateInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a list of available themes.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
* This class is not yet stable and therefore there are no guarantees that the
|
||||||
|
* internal implementations including constructor signature and protected
|
||||||
|
* properties / methods will not change over time. This will be reviewed after
|
||||||
|
* https://www.drupal.org/project/drupal/issues/2940481
|
||||||
|
*/
|
||||||
|
class ThemeExtensionList extends ExtensionList {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected $defaults = [
|
||||||
|
'engine' => 'twig',
|
||||||
|
'base theme' => 'stable',
|
||||||
|
'regions' => [
|
||||||
|
'sidebar_first' => 'Left sidebar',
|
||||||
|
'sidebar_second' => 'Right sidebar',
|
||||||
|
'content' => 'Content',
|
||||||
|
'header' => 'Header',
|
||||||
|
'primary_menu' => 'Primary menu',
|
||||||
|
'secondary_menu' => 'Secondary menu',
|
||||||
|
'footer' => 'Footer',
|
||||||
|
'highlighted' => 'Highlighted',
|
||||||
|
'help' => 'Help',
|
||||||
|
'page_top' => 'Page top',
|
||||||
|
'page_bottom' => 'Page bottom',
|
||||||
|
'breadcrumb' => 'Breadcrumb',
|
||||||
|
],
|
||||||
|
'description' => '',
|
||||||
|
// The following array should be kept inline with
|
||||||
|
// _system_default_theme_features().
|
||||||
|
'features' => [
|
||||||
|
'favicon',
|
||||||
|
'logo',
|
||||||
|
'node_user_picture',
|
||||||
|
'comment_user_picture',
|
||||||
|
'comment_user_verification',
|
||||||
|
],
|
||||||
|
'screenshot' => 'screenshot.png',
|
||||||
|
'php' => DRUPAL_MINIMUM_PHP,
|
||||||
|
'libraries' => [],
|
||||||
|
'libraries_extend' => [],
|
||||||
|
'libraries_override' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The config factory.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||||
|
*/
|
||||||
|
protected $configFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The theme engine list needed by this theme list.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Extension\ThemeEngineExtensionList
|
||||||
|
*/
|
||||||
|
protected $engineList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of installed themes.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $installedThemes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ThemeExtensionList instance.
|
||||||
|
*
|
||||||
|
* @param string $root
|
||||||
|
* The app root.
|
||||||
|
* @param string $type
|
||||||
|
* The extension type.
|
||||||
|
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||||
|
* The cache.
|
||||||
|
* @param \Drupal\Core\Extension\InfoParserInterface $info_parser
|
||||||
|
* The info parser.
|
||||||
|
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||||
|
* The module handler.
|
||||||
|
* @param \Drupal\Core\State\StateInterface $state
|
||||||
|
* The state service.
|
||||||
|
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||||
|
* The config factory.
|
||||||
|
* @param \Drupal\Core\Extension\ThemeEngineExtensionList $engine_list
|
||||||
|
* The theme engine extension listing.
|
||||||
|
* @param string $install_profile
|
||||||
|
* The install profile used by the site.
|
||||||
|
*/
|
||||||
|
public function __construct($root, $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, StateInterface $state, ConfigFactoryInterface $config_factory, ThemeEngineExtensionList $engine_list, $install_profile) {
|
||||||
|
parent::__construct($root, $type, $cache, $info_parser, $module_handler, $state, $install_profile);
|
||||||
|
|
||||||
|
$this->configFactory = $config_factory;
|
||||||
|
$this->engineList = $engine_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doList() {
|
||||||
|
// Find themes.
|
||||||
|
$themes = parent::doList();
|
||||||
|
|
||||||
|
$engines = $this->engineList->getList();
|
||||||
|
// Always get the freshest list of themes (rather than the already cached
|
||||||
|
// list in $this->installedThemes) when building the theme listing because a
|
||||||
|
// theme could have just been installed or uninstalled.
|
||||||
|
$this->installedThemes = $this->configFactory->get('core.extension')->get('theme') ?: [];
|
||||||
|
|
||||||
|
$sub_themes = [];
|
||||||
|
// Read info files for each theme.
|
||||||
|
foreach ($themes as $name => $theme) {
|
||||||
|
// Defaults to 'twig' (see self::defaults above).
|
||||||
|
$engine = $theme->info['engine'];
|
||||||
|
if (isset($engines[$engine])) {
|
||||||
|
$theme->owner = $engines[$engine]->getExtensionPathname();
|
||||||
|
$theme->prefix = $engines[$engine]->getName();
|
||||||
|
}
|
||||||
|
// Add this theme as a sub-theme if it has a base theme.
|
||||||
|
if (!empty($theme->info['base theme'])) {
|
||||||
|
$sub_themes[] = $name;
|
||||||
|
}
|
||||||
|
// Add weight and status.
|
||||||
|
$theme->status = (int) isset($this->installedThemes[$name]);
|
||||||
|
$theme->weight = isset($this->installedThemes[$name]) ? $this->installedThemes[$name] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build dependencies.
|
||||||
|
$themes = $this->moduleHandler->buildModuleDependencies($themes);
|
||||||
|
|
||||||
|
// After establishing the full list of available themes, fill in data for
|
||||||
|
// sub-themes.
|
||||||
|
$this->fillInSubThemeData($themes, $sub_themes);
|
||||||
|
|
||||||
|
return $themes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills in data for themes that are also sub-themes.
|
||||||
|
*
|
||||||
|
* @param array $themes
|
||||||
|
* The array of partly processed theme information.
|
||||||
|
* @param array $sub_themes
|
||||||
|
* A list of themes from the $theme array that are also sub-themes.
|
||||||
|
*/
|
||||||
|
protected function fillInSubThemeData(array &$themes, array $sub_themes) {
|
||||||
|
foreach ($sub_themes as $name) {
|
||||||
|
$sub_theme = $themes[$name];
|
||||||
|
// The $base_themes property is optional; only set for sub themes.
|
||||||
|
// @see ThemeHandlerInterface::listInfo()
|
||||||
|
$sub_theme->base_themes = $this->doGetBaseThemes($themes, $name);
|
||||||
|
// empty() cannot be used here, since static::doGetBaseThemes() adds
|
||||||
|
// the key of a base theme with a value of NULL in case it is not found,
|
||||||
|
// in order to prevent needless iterations.
|
||||||
|
if (!current($sub_theme->base_themes)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Determine the root base theme.
|
||||||
|
$root_key = key($sub_theme->base_themes);
|
||||||
|
// Build the list of sub-themes for each of the theme's base themes.
|
||||||
|
foreach (array_keys($sub_theme->base_themes) as $base_theme) {
|
||||||
|
$themes[$base_theme]->sub_themes[$name] = $sub_theme->info['name'];
|
||||||
|
}
|
||||||
|
// Add the theme engine info from the root base theme.
|
||||||
|
if (isset($themes[$root_key]->owner)) {
|
||||||
|
$sub_theme->info['engine'] = $themes[$root_key]->info['engine'];
|
||||||
|
$sub_theme->owner = $themes[$root_key]->owner;
|
||||||
|
$sub_theme->prefix = $themes[$root_key]->prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all the base themes for the specified theme.
|
||||||
|
*
|
||||||
|
* Themes can inherit templates and function implementations from earlier
|
||||||
|
* themes.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Extension\Extension[] $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) {
|
||||||
|
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, array $used_themes = []) {
|
||||||
|
if (!isset($themes[$theme]->info['base theme'])) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$base_key = $themes[$theme]->info['base theme'];
|
||||||
|
// Does the base theme exist?
|
||||||
|
if (!isset($themes[$base_key])) {
|
||||||
|
return [$base_key => NULL];
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_base_theme = [$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 [$base_key => NULL];
|
||||||
|
}
|
||||||
|
$used_themes[$base_key] = TRUE;
|
||||||
|
return $this->doGetBaseThemes($themes, $base_key, $used_themes) + $current_base_theme;
|
||||||
|
}
|
||||||
|
// If we get here, then this is our parent theme.
|
||||||
|
return $current_base_theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function createExtensionInfo(Extension $extension) {
|
||||||
|
$info = parent::createExtensionInfo($extension);
|
||||||
|
// Remove the default Stable base theme when 'base theme: false' is set in
|
||||||
|
// a theme .info.yml file.
|
||||||
|
if ($info['base theme'] === FALSE) {
|
||||||
|
unset($info['base theme']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($info['base theme'])) {
|
||||||
|
// Add the base theme as a proper dependency.
|
||||||
|
$info['dependencies'][] = $info['base theme'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix screenshot with theme path.
|
||||||
|
if (!empty($info['screenshot'])) {
|
||||||
|
$info['screenshot'] = $extension->getPath() . '/' . $info['screenshot'];
|
||||||
|
}
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function getInstalledExtensionNames() {
|
||||||
|
// Cache the installed themes to avoid multiple calls to the config system.
|
||||||
|
if (!isset($this->installedThemes)) {
|
||||||
|
$this->installedThemes = $this->configFactory->get('core.extension')->get('theme') ?: [];
|
||||||
|
}
|
||||||
|
return array_keys($this->installedThemes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function reset() {
|
||||||
|
parent::reset();
|
||||||
|
$this->installedThemes = NULL;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,28 +5,12 @@ namespace Drupal\Core\Extension;
|
||||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||||
use Drupal\Core\Extension\Exception\UninstalledExtensionException;
|
use Drupal\Core\Extension\Exception\UninstalledExtensionException;
|
||||||
use Drupal\Core\Extension\Exception\UnknownExtensionException;
|
use Drupal\Core\Extension\Exception\UnknownExtensionException;
|
||||||
use Drupal\Core\State\StateInterface;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default theme handler using the config system to store installation statuses.
|
* Default theme handler using the config system to store installation statuses.
|
||||||
*/
|
*/
|
||||||
class ThemeHandler implements ThemeHandlerInterface {
|
class ThemeHandler implements ThemeHandlerInterface {
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains the features enabled for themes by default.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*
|
|
||||||
* @see _system_default_theme_features()
|
|
||||||
*/
|
|
||||||
protected $defaultFeatures = [
|
|
||||||
'favicon',
|
|
||||||
'logo',
|
|
||||||
'node_user_picture',
|
|
||||||
'comment_user_picture',
|
|
||||||
'comment_user_verification',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of all currently available themes.
|
* A list of all currently available themes.
|
||||||
*
|
*
|
||||||
|
|
@ -41,68 +25,12 @@ class ThemeHandler implements ThemeHandlerInterface {
|
||||||
*/
|
*/
|
||||||
protected $configFactory;
|
protected $configFactory;
|
||||||
|
|
||||||
/**
|
|
||||||
* The module handler to fire themes_installed/themes_uninstalled hooks.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
|
||||||
*/
|
|
||||||
protected $moduleHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The state backend.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\State\StateInterface
|
|
||||||
*/
|
|
||||||
protected $state;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The config installer to install configuration.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Config\ConfigInstallerInterface
|
|
||||||
*/
|
|
||||||
protected $configInstaller;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The info parser to parse the theme.info.yml files.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Extension\InfoParserInterface
|
|
||||||
*/
|
|
||||||
protected $infoParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A logger instance.
|
|
||||||
*
|
|
||||||
* @var \Psr\Log\LoggerInterface
|
|
||||||
*/
|
|
||||||
protected $logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The route builder to rebuild the routes if a theme is installed.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Routing\RouteBuilderInterface
|
|
||||||
*/
|
|
||||||
protected $routeBuilder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extension discovery instance.
|
* An extension discovery instance.
|
||||||
*
|
*
|
||||||
* @var \Drupal\Core\Extension\ExtensionDiscovery
|
* @var \Drupal\Core\Extension\ThemeExtensionList
|
||||||
*/
|
*/
|
||||||
protected $extensionDiscovery;
|
protected $themeList;
|
||||||
|
|
||||||
/**
|
|
||||||
* The CSS asset collection optimizer service.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
|
|
||||||
*/
|
|
||||||
protected $cssCollectionOptimizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The config manager used to uninstall a theme.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Config\ConfigManagerInterface
|
|
||||||
*/
|
|
||||||
protected $configManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The app root.
|
* The app root.
|
||||||
|
|
@ -118,22 +46,13 @@ class ThemeHandler implements ThemeHandlerInterface {
|
||||||
* The app root.
|
* The app root.
|
||||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||||
* The config factory to get the installed themes.
|
* The config factory to get the installed themes.
|
||||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
* @param \Drupal\Core\Extension\ThemeExtensionList $theme_list
|
||||||
* The module handler to fire themes_installed/themes_uninstalled hooks.
|
* A extension discovery instance.
|
||||||
* @param \Drupal\Core\State\StateInterface $state
|
|
||||||
* The state store.
|
|
||||||
* @param \Drupal\Core\Extension\InfoParserInterface $info_parser
|
|
||||||
* The info parser to parse the theme.info.yml files.
|
|
||||||
* @param \Drupal\Core\Extension\ExtensionDiscovery $extension_discovery
|
|
||||||
* (optional) A extension discovery instance (for unit tests).
|
|
||||||
*/
|
*/
|
||||||
public function __construct($root, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, StateInterface $state, InfoParserInterface $info_parser, ExtensionDiscovery $extension_discovery = NULL) {
|
public function __construct($root, ConfigFactoryInterface $config_factory, ThemeExtensionList $theme_list) {
|
||||||
$this->root = $root;
|
$this->root = $root;
|
||||||
$this->configFactory = $config_factory;
|
$this->configFactory = $config_factory;
|
||||||
$this->moduleHandler = $module_handler;
|
$this->themeList = $theme_list;
|
||||||
$this->state = $state;
|
|
||||||
$this->infoParser = $info_parser;
|
|
||||||
$this->extensionDiscovery = $extension_discovery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -181,16 +100,10 @@ class ThemeHandler implements ThemeHandlerInterface {
|
||||||
public function listInfo() {
|
public function listInfo() {
|
||||||
if (!isset($this->list)) {
|
if (!isset($this->list)) {
|
||||||
$this->list = [];
|
$this->list = [];
|
||||||
$themes = $this->systemThemeList();
|
$installed_themes = $this->configFactory->get('core.extension')->get('theme');
|
||||||
// @todo Ensure that systemThemeList() does not contain an empty list
|
if (!empty($installed_themes)) {
|
||||||
// during the batch installer, see https://www.drupal.org/node/2322619.
|
$installed_themes = array_intersect_key($this->themeList->getList(), $installed_themes);
|
||||||
if (empty($themes)) {
|
array_map([$this, 'addTheme'], $installed_themes);
|
||||||
$this->refreshInfo();
|
|
||||||
$this->list = $this->list ?: [];
|
|
||||||
$themes = \Drupal::state()->get('system.theme.data', []);
|
|
||||||
}
|
|
||||||
foreach ($themes as $theme) {
|
|
||||||
$this->addTheme($theme);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $this->list;
|
return $this->list;
|
||||||
|
|
@ -200,6 +113,11 @@ class ThemeHandler implements ThemeHandlerInterface {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function addTheme(Extension $theme) {
|
public function addTheme(Extension $theme) {
|
||||||
|
// Register the namespaces of installed themes.
|
||||||
|
// @todo Implement proper theme registration
|
||||||
|
// https://www.drupal.org/project/drupal/issues/2941757
|
||||||
|
\Drupal::service('class_loader')->addPsr4('Drupal\\' . $theme->getName() . '\\', $this->root . '/' . $theme->getPath() . '/src');
|
||||||
|
|
||||||
if (!empty($theme->info['libraries'])) {
|
if (!empty($theme->info['libraries'])) {
|
||||||
foreach ($theme->info['libraries'] as $library => $name) {
|
foreach ($theme->info['libraries'] as $library => $name) {
|
||||||
$theme->libraries[$library] = $name;
|
$theme->libraries[$library] = $name;
|
||||||
|
|
@ -218,32 +136,21 @@ class ThemeHandler implements ThemeHandlerInterface {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function refreshInfo() {
|
public function refreshInfo() {
|
||||||
$extension_config = $this->configFactory->get('core.extension');
|
$installed = $this->configFactory->get('core.extension')->get('theme');
|
||||||
$installed = $extension_config->get('theme');
|
|
||||||
// Only refresh the info if a theme has been installed. Modules are
|
// Only refresh the info if a theme has been installed. Modules are
|
||||||
// installed before themes by the installer and this method is called during
|
// installed before themes by the installer and this method is called during
|
||||||
// module installation.
|
// module installation.
|
||||||
if (empty($installed) && empty($this->list)) {
|
if (empty($installed) && empty($this->list)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->reset();
|
$this->reset();
|
||||||
// @todo Avoid re-scanning all themes by retaining the original (unaltered)
|
|
||||||
// theme info somewhere.
|
|
||||||
$list = $this->rebuildThemeData();
|
|
||||||
foreach ($list as $name => $theme) {
|
|
||||||
if (isset($installed[$name])) {
|
|
||||||
$this->addTheme($theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->state->set('system.theme.data', $this->list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function reset() {
|
public function reset() {
|
||||||
$this->systemListReset();
|
$this->themeList->reset();
|
||||||
$this->list = NULL;
|
$this->list = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -251,214 +158,21 @@ class ThemeHandler implements ThemeHandlerInterface {
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function rebuildThemeData() {
|
public function rebuildThemeData() {
|
||||||
$listing = $this->getExtensionDiscovery();
|
return $this->themeList->reset()->getList();
|
||||||
$themes = $listing->scan('theme');
|
|
||||||
$engines = $listing->scan('theme_engine');
|
|
||||||
$extension_config = $this->configFactory->get('core.extension');
|
|
||||||
$installed = $extension_config->get('theme') ?: [];
|
|
||||||
|
|
||||||
// Set defaults for theme info.
|
|
||||||
$defaults = [
|
|
||||||
'engine' => 'twig',
|
|
||||||
'base theme' => 'stable',
|
|
||||||
'regions' => [
|
|
||||||
'sidebar_first' => 'Left sidebar',
|
|
||||||
'sidebar_second' => 'Right sidebar',
|
|
||||||
'content' => 'Content',
|
|
||||||
'header' => 'Header',
|
|
||||||
'primary_menu' => 'Primary menu',
|
|
||||||
'secondary_menu' => 'Secondary menu',
|
|
||||||
'footer' => 'Footer',
|
|
||||||
'highlighted' => 'Highlighted',
|
|
||||||
'help' => 'Help',
|
|
||||||
'page_top' => 'Page top',
|
|
||||||
'page_bottom' => 'Page bottom',
|
|
||||||
'breadcrumb' => 'Breadcrumb',
|
|
||||||
],
|
|
||||||
'description' => '',
|
|
||||||
'features' => $this->defaultFeatures,
|
|
||||||
'screenshot' => 'screenshot.png',
|
|
||||||
'php' => DRUPAL_MINIMUM_PHP,
|
|
||||||
'libraries' => [],
|
|
||||||
];
|
|
||||||
|
|
||||||
$sub_themes = [];
|
|
||||||
$files_theme = [];
|
|
||||||
$files_theme_engine = [];
|
|
||||||
// Read info files for each theme.
|
|
||||||
foreach ($themes as $key => $theme) {
|
|
||||||
// @todo Remove all code that relies on the $status property.
|
|
||||||
$theme->status = (int) isset($installed[$key]);
|
|
||||||
|
|
||||||
$theme->info = $this->infoParser->parse($theme->getPathname()) + $defaults;
|
|
||||||
// Remove the default Stable base theme when 'base theme: false' is set in
|
|
||||||
// a theme .info.yml file.
|
|
||||||
if ($theme->info['base theme'] === FALSE) {
|
|
||||||
unset($theme->info['base theme']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the info file modification time, so it becomes available for
|
|
||||||
// contributed modules to use for ordering theme lists.
|
|
||||||
$theme->info['mtime'] = $theme->getMTime();
|
|
||||||
|
|
||||||
// Invoke hook_system_info_alter() to give installed modules a chance to
|
|
||||||
// modify the data in the .info.yml files if necessary.
|
|
||||||
// @todo Remove $type argument, obsolete with $theme->getType().
|
|
||||||
$type = 'theme';
|
|
||||||
$this->moduleHandler->alter('system_info', $theme->info, $theme, $type);
|
|
||||||
|
|
||||||
if (!empty($theme->info['base theme'])) {
|
|
||||||
$sub_themes[] = $key;
|
|
||||||
// Add the base theme as a proper dependency.
|
|
||||||
$themes[$key]->info['dependencies'][] = $themes[$key]->info['base theme'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defaults to 'twig' (see $defaults above).
|
|
||||||
$engine = $theme->info['engine'];
|
|
||||||
if (isset($engines[$engine])) {
|
|
||||||
$theme->owner = $engines[$engine]->getExtensionPathname();
|
|
||||||
$theme->prefix = $engines[$engine]->getName();
|
|
||||||
$files_theme_engine[$engine] = $engines[$engine]->getPathname();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefix screenshot with theme path.
|
|
||||||
if (!empty($theme->info['screenshot'])) {
|
|
||||||
$theme->info['screenshot'] = $theme->getPath() . '/' . $theme->info['screenshot'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$files_theme[$key] = $theme->getPathname();
|
|
||||||
}
|
|
||||||
// Build dependencies.
|
|
||||||
// @todo Move into a generic ExtensionHandler base class.
|
|
||||||
// @see https://www.drupal.org/node/2208429
|
|
||||||
$themes = $this->moduleHandler->buildModuleDependencies($themes);
|
|
||||||
|
|
||||||
// Store filenames to allow system_list() and drupal_get_filename() to
|
|
||||||
// retrieve them for themes and theme engines without having to scan the
|
|
||||||
// filesystem.
|
|
||||||
$this->state->set('system.theme.files', $files_theme);
|
|
||||||
$this->state->set('system.theme_engine.files', $files_theme_engine);
|
|
||||||
|
|
||||||
// After establishing the full list of available themes, fill in data for
|
|
||||||
// sub-themes.
|
|
||||||
foreach ($sub_themes as $key) {
|
|
||||||
$sub_theme = $themes[$key];
|
|
||||||
// The $base_themes property is optional; only set for sub themes.
|
|
||||||
// @see ThemeHandlerInterface::listInfo()
|
|
||||||
$sub_theme->base_themes = $this->getBaseThemes($themes, $key);
|
|
||||||
// empty() cannot be used here, since ThemeHandler::doGetBaseThemes() adds
|
|
||||||
// the key of a base theme with a value of NULL in case it is not found,
|
|
||||||
// in order to prevent needless iterations.
|
|
||||||
if (!current($sub_theme->base_themes)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Determine the root base theme.
|
|
||||||
$root_key = key($sub_theme->base_themes);
|
|
||||||
// Build the list of sub-themes for each of the theme's base themes.
|
|
||||||
foreach (array_keys($sub_theme->base_themes) as $base_theme) {
|
|
||||||
$themes[$base_theme]->sub_themes[$key] = $sub_theme->info['name'];
|
|
||||||
}
|
|
||||||
// Add the theme engine info from the root base theme.
|
|
||||||
if (isset($themes[$root_key]->owner)) {
|
|
||||||
$sub_theme->info['engine'] = $themes[$root_key]->info['engine'];
|
|
||||||
$sub_theme->owner = $themes[$root_key]->owner;
|
|
||||||
$sub_theme->prefix = $themes[$root_key]->prefix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $themes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getBaseThemes(array $themes, $theme) {
|
public function getBaseThemes(array $themes, $theme) {
|
||||||
return $this->doGetBaseThemes($themes, $theme);
|
return $this->themeList->getBaseThemes($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 = []) {
|
|
||||||
if (!isset($themes[$theme]->info['base theme'])) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$base_key = $themes[$theme]->info['base theme'];
|
|
||||||
// Does the base theme exist?
|
|
||||||
if (!isset($themes[$base_key])) {
|
|
||||||
return [$base_key => NULL];
|
|
||||||
}
|
|
||||||
|
|
||||||
$current_base_theme = [$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 [$base_key => NULL];
|
|
||||||
}
|
|
||||||
$used_themes[$base_key] = TRUE;
|
|
||||||
return $this->doGetBaseThemes($themes, $base_key, $used_themes) + $current_base_theme;
|
|
||||||
}
|
|
||||||
// If we get here, then this is our parent theme.
|
|
||||||
return $current_base_theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an extension discovery object.
|
|
||||||
*
|
|
||||||
* @return \Drupal\Core\Extension\ExtensionDiscovery
|
|
||||||
* The extension discovery object.
|
|
||||||
*/
|
|
||||||
protected function getExtensionDiscovery() {
|
|
||||||
if (!isset($this->extensionDiscovery)) {
|
|
||||||
$this->extensionDiscovery = new ExtensionDiscovery($this->root);
|
|
||||||
}
|
|
||||||
return $this->extensionDiscovery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getName($theme) {
|
public function getName($theme) {
|
||||||
$themes = $this->listInfo();
|
return $this->themeList->getName($theme);
|
||||||
if (!isset($themes[$theme])) {
|
|
||||||
throw new UnknownExtensionException("Requested the name of a non-existing theme $theme");
|
|
||||||
}
|
|
||||||
return $themes[$theme]->info['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps system_list_reset().
|
|
||||||
*/
|
|
||||||
protected function systemListReset() {
|
|
||||||
system_list_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps system_list().
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* A list of themes keyed by name.
|
|
||||||
*/
|
|
||||||
protected function systemThemeList() {
|
|
||||||
return system_list('theme');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ interface ThemeHandlerInterface {
|
||||||
*
|
*
|
||||||
* @return \Drupal\Core\Extension\Extension[]
|
* @return \Drupal\Core\Extension\Extension[]
|
||||||
* An associative array of the currently installed themes. The keys are the
|
* An associative array of the currently installed themes. The keys are the
|
||||||
* themes' machine names and the values are objects having the following
|
* themes' machine names and the values are Extension objects having the
|
||||||
* properties:
|
* following properties:
|
||||||
* - filename: The filepath and name of the .info.yml file.
|
* - filename: The filepath and name of the .info.yml file.
|
||||||
* - name: The machine name of the theme.
|
* - name: The machine name of the theme.
|
||||||
* - status: 1 for installed, 0 for uninstalled themes.
|
* - status: 1 for installed, 0 for uninstalled themes.
|
||||||
|
|
|
||||||
|
|
@ -174,22 +174,12 @@ class ThemeInstaller implements ThemeInstallerInterface {
|
||||||
->set("theme.$key", 0)
|
->set("theme.$key", 0)
|
||||||
->save(TRUE);
|
->save(TRUE);
|
||||||
|
|
||||||
// Add the theme to the current list.
|
|
||||||
// @todo Remove all code that relies on $status property.
|
|
||||||
$theme_data[$key]->status = 1;
|
|
||||||
$this->themeHandler->addTheme($theme_data[$key]);
|
|
||||||
|
|
||||||
// Update the current theme data accordingly.
|
|
||||||
$current_theme_data = $this->state->get('system.theme.data', []);
|
|
||||||
$current_theme_data[$key] = $theme_data[$key];
|
|
||||||
$this->state->set('system.theme.data', $current_theme_data);
|
|
||||||
|
|
||||||
// Reset theme settings.
|
// Reset theme settings.
|
||||||
$theme_settings = &drupal_static('theme_get_setting');
|
$theme_settings = &drupal_static('theme_get_setting');
|
||||||
unset($theme_settings[$key]);
|
unset($theme_settings[$key]);
|
||||||
|
|
||||||
// @todo Remove system_list().
|
// Reset theme listing.
|
||||||
$this->systemListReset();
|
$this->themeHandler->reset();
|
||||||
|
|
||||||
// Only install default configuration if this theme has not been installed
|
// Only install default configuration if this theme has not been installed
|
||||||
// already.
|
// already.
|
||||||
|
|
@ -245,14 +235,10 @@ class ThemeInstaller implements ThemeInstallerInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cssCollectionOptimizer->deleteAll();
|
$this->cssCollectionOptimizer->deleteAll();
|
||||||
$current_theme_data = $this->state->get('system.theme.data', []);
|
|
||||||
foreach ($theme_list as $key) {
|
foreach ($theme_list as $key) {
|
||||||
// The value is not used; the weight is ignored for themes currently.
|
// The value is not used; the weight is ignored for themes currently.
|
||||||
$extension_config->clear("theme.$key");
|
$extension_config->clear("theme.$key");
|
||||||
|
|
||||||
// Update the current theme data accordingly.
|
|
||||||
unset($current_theme_data[$key]);
|
|
||||||
|
|
||||||
// Reset theme settings.
|
// Reset theme settings.
|
||||||
$theme_settings = &drupal_static('theme_get_setting');
|
$theme_settings = &drupal_static('theme_get_setting');
|
||||||
unset($theme_settings[$key]);
|
unset($theme_settings[$key]);
|
||||||
|
|
@ -264,11 +250,10 @@ class ThemeInstaller implements ThemeInstallerInterface {
|
||||||
// Don't check schema when uninstalling a theme since we are only clearing
|
// Don't check schema when uninstalling a theme since we are only clearing
|
||||||
// keys.
|
// keys.
|
||||||
$extension_config->save(TRUE);
|
$extension_config->save(TRUE);
|
||||||
$this->state->set('system.theme.data', $current_theme_data);
|
|
||||||
|
|
||||||
// @todo Remove system_list().
|
// Refresh theme info.
|
||||||
$this->themeHandler->refreshInfo();
|
|
||||||
$this->resetSystem();
|
$this->resetSystem();
|
||||||
|
$this->themeHandler->reset();
|
||||||
|
|
||||||
$this->moduleHandler->invokeAll('themes_uninstalled', [$theme_list]);
|
$this->moduleHandler->invokeAll('themes_uninstalled', [$theme_list]);
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +265,6 @@ class ThemeInstaller implements ThemeInstallerInterface {
|
||||||
if ($this->routeBuilder) {
|
if ($this->routeBuilder) {
|
||||||
$this->routeBuilder->setRebuildNeeded();
|
$this->routeBuilder->setRebuildNeeded();
|
||||||
}
|
}
|
||||||
$this->systemListReset();
|
|
||||||
|
|
||||||
// @todo It feels wrong to have the requirement to clear the local tasks
|
// @todo It feels wrong to have the requirement to clear the local tasks
|
||||||
// cache here.
|
// cache here.
|
||||||
|
|
@ -295,11 +279,4 @@ class ThemeInstaller implements ThemeInstallerInterface {
|
||||||
drupal_theme_rebuild();
|
drupal_theme_rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps system_list_reset().
|
|
||||||
*/
|
|
||||||
protected function systemListReset() {
|
|
||||||
system_list_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,9 @@ function hook_module_implements_alter(&$implementations, $hook) {
|
||||||
/**
|
/**
|
||||||
* Alter the information parsed from module and theme .info.yml files.
|
* Alter the information parsed from module and theme .info.yml files.
|
||||||
*
|
*
|
||||||
* This hook is invoked in _system_rebuild_module_data() and in
|
* This hook is invoked in \Drupal\Core\Extension\ExtensionList::doList(). A
|
||||||
* \Drupal\Core\Extension\ThemeHandlerInterface::rebuildThemeData(). A module
|
* module may implement this hook in order to add to or alter the data generated
|
||||||
* may implement this hook in order to add to or alter the data generated by
|
* by reading the .info.yml file with \Drupal\Core\Extension\InfoParser.
|
||||||
* reading the .info.yml file with \Drupal\Core\Extension\InfoParser.
|
|
||||||
*
|
*
|
||||||
* Using implementations of this hook to make modules required by setting the
|
* Using implementations of this hook to make modules required by setting the
|
||||||
* $info['required'] key is discouraged. Doing so will slow down the module
|
* $info['required'] key is discouraged. Doing so will slow down the module
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Core\Installer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides common functionality for the extension list classes.
|
||||||
|
*/
|
||||||
|
trait ExtensionListTrait {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static version of the added file names during the installer.
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected static $staticAddedPathNames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see \Drupal\Core\Extension\ExtensionList::setPathname()
|
||||||
|
*/
|
||||||
|
public function setPathname($extension_name, $pathname) {
|
||||||
|
parent::setPathname($extension_name, $pathname);
|
||||||
|
|
||||||
|
// In the early installer the container is rebuilt multiple times. Therefore
|
||||||
|
// we have to keep the added filenames across those rebuilds. This is not a
|
||||||
|
// final design, but rather just a workaround resolved at some point,
|
||||||
|
// hopefully.
|
||||||
|
// @todo Remove as part of https://drupal.org/project/drupal/issues/2934063
|
||||||
|
static::$staticAddedPathNames[$extension_name] = $pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see \Drupal\Core\Extension\ExtensionList::getPathname()
|
||||||
|
*/
|
||||||
|
public function getPathname($extension_name) {
|
||||||
|
if (isset($this->addedPathNames[$extension_name])) {
|
||||||
|
return $this->addedPathNames[$extension_name];
|
||||||
|
}
|
||||||
|
elseif (isset($this->pathNames[$extension_name])) {
|
||||||
|
return $this->pathNames[$extension_name];
|
||||||
|
}
|
||||||
|
elseif (isset(static::$staticAddedPathNames[$extension_name])) {
|
||||||
|
return static::$staticAddedPathNames[$extension_name];
|
||||||
|
}
|
||||||
|
elseif (($path_names = $this->getPathnames()) && isset($path_names[$extension_name])) {
|
||||||
|
// Ensure we don't have to do path scanning more than really needed.
|
||||||
|
foreach ($path_names as $extension => $path_name) {
|
||||||
|
static::$staticAddedPathNames[$extension] = $path_name;
|
||||||
|
}
|
||||||
|
return $path_names[$extension_name];
|
||||||
|
}
|
||||||
|
throw new \InvalidArgumentException("The {$this->type} $extension_name does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -8,51 +8,6 @@ use Drupal\Core\Extension\ModuleExtensionList;
|
||||||
* Overrides the module extension list to have a static cache.
|
* Overrides the module extension list to have a static cache.
|
||||||
*/
|
*/
|
||||||
class InstallerModuleExtensionList extends ModuleExtensionList {
|
class InstallerModuleExtensionList extends ModuleExtensionList {
|
||||||
|
use ExtensionListTrait;
|
||||||
/**
|
|
||||||
* Static version of the added file names during the installer.
|
|
||||||
*
|
|
||||||
* @var string[]
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
protected static $staticAddedPathNames;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function setPathname($extension_name, $pathname) {
|
|
||||||
parent::setPathname($extension_name, $pathname);
|
|
||||||
|
|
||||||
// In the early installer the container is rebuilt multiple times. Therefore
|
|
||||||
// we have to keep the added filenames across those rebuilds. This is not a
|
|
||||||
// final design, but rather just a workaround resolved at some point,
|
|
||||||
// hopefully.
|
|
||||||
// @todo Remove as part of https://drupal.org/project/drupal/issues/2934063
|
|
||||||
static::$staticAddedPathNames[$extension_name] = $pathname;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getPathname($extension_name) {
|
|
||||||
if (isset($this->addedPathNames[$extension_name])) {
|
|
||||||
return $this->addedPathNames[$extension_name];
|
|
||||||
}
|
|
||||||
elseif (isset($this->pathNames[$extension_name])) {
|
|
||||||
return $this->pathNames[$extension_name];
|
|
||||||
}
|
|
||||||
elseif (isset(static::$staticAddedPathNames[$extension_name])) {
|
|
||||||
return static::$staticAddedPathNames[$extension_name];
|
|
||||||
}
|
|
||||||
elseif (($path_names = $this->getPathnames()) && isset($path_names[$extension_name])) {
|
|
||||||
// Ensure we don't have to do path scanning more than really needed.
|
|
||||||
foreach ($path_names as $extension => $path_name) {
|
|
||||||
static::$staticAddedPathNames[$extension] = $path_name;
|
|
||||||
}
|
|
||||||
return $path_names[$extension_name];
|
|
||||||
}
|
|
||||||
throw new \InvalidArgumentException("The {$this->type} $extension_name does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Core\Installer;
|
||||||
|
|
||||||
|
use Drupal\Core\Extension\ThemeEngineExtensionList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the theme engine extension list to have a static cache.
|
||||||
|
*/
|
||||||
|
class InstallerThemeEngineExtensionList extends ThemeEngineExtensionList {
|
||||||
|
use ExtensionListTrait;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Core\Installer;
|
||||||
|
|
||||||
|
use Drupal\Core\Extension\ThemeExtensionList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the theme extension list to have a static cache.
|
||||||
|
*/
|
||||||
|
class InstallerThemeExtensionList extends ThemeExtensionList {
|
||||||
|
use ExtensionListTrait;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,8 @@ class NormalInstallerServiceProvider implements ServiceProviderInterface {
|
||||||
public function register(ContainerBuilder $container) {
|
public function register(ContainerBuilder $container) {
|
||||||
// Use a performance optimised module extension list.
|
// Use a performance optimised module extension list.
|
||||||
$container->getDefinition('extension.list.module')->setClass('Drupal\Core\Installer\InstallerModuleExtensionList');
|
$container->getDefinition('extension.list.module')->setClass('Drupal\Core\Installer\InstallerModuleExtensionList');
|
||||||
|
$container->getDefinition('extension.list.theme')->setClass('Drupal\Core\Installer\InstallerThemeExtensionList');
|
||||||
|
$container->getDefinition('extension.list.theme_engine')->setClass('Drupal\Core\Installer\InstallerThemeEngineExtensionList');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ class ActiveTheme {
|
||||||
/**
|
/**
|
||||||
* Returns the path to the theme engine for root themes.
|
* Returns the path to the theme engine for root themes.
|
||||||
*
|
*
|
||||||
* @see \Drupal\Core\Extension\ThemeHandler::rebuildThemeData
|
* @see \Drupal\Core\Extension\ThemeExtensionList::doList()
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1829,7 +1829,8 @@ function system_update_8014() {
|
||||||
$theme_handler->refreshInfo();
|
$theme_handler->refreshInfo();
|
||||||
foreach ($theme_handler->listInfo() as $theme) {
|
foreach ($theme_handler->listInfo() as $theme) {
|
||||||
// We first check that a base theme is set because if it's set to false then
|
// We first check that a base theme is set because if it's set to false then
|
||||||
// it's unset in \Drupal\Core\Extension\ThemeHandler::rebuildThemeData().
|
// it's unset in
|
||||||
|
// \Drupal\Core\Extension\ThemeExtensionList::createExtensionInfo().
|
||||||
if (isset($theme->info['base theme']) && $theme->info['base theme'] == 'stable') {
|
if (isset($theme->info['base theme']) && $theme->info['base theme'] == 'stable') {
|
||||||
$theme_handler->install(['stable']);
|
$theme_handler->install(['stable']);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use Drupal\Component\Render\PlainTextOutput;
|
||||||
use Drupal\Component\Utility\UrlHelper;
|
use Drupal\Component\Utility\UrlHelper;
|
||||||
use Drupal\Core\Asset\AttachedAssetsInterface;
|
use Drupal\Core\Asset\AttachedAssetsInterface;
|
||||||
use Drupal\Core\Cache\Cache;
|
use Drupal\Core\Cache\Cache;
|
||||||
use Drupal\Core\Extension\Exception\UnknownExtensionException;
|
|
||||||
use Drupal\Core\Queue\QueueGarbageCollectionInterface;
|
use Drupal\Core\Queue\QueueGarbageCollectionInterface;
|
||||||
use Drupal\Core\Database\Query\AlterableInterface;
|
use Drupal\Core\Database\Query\AlterableInterface;
|
||||||
use Drupal\Core\Extension\Extension;
|
use Drupal\Core\Extension\Extension;
|
||||||
|
|
@ -963,38 +962,20 @@ function system_check_directory($form_element, FormStateInterface $form_state) {
|
||||||
* array is returned.
|
* array is returned.
|
||||||
*
|
*
|
||||||
* @see system_rebuild_module_data()
|
* @see system_rebuild_module_data()
|
||||||
* @see \Drupal\Core\Extension\ThemeHandlerInterface::rebuildThemeData()
|
* @see \Drupal\Core\Extension\ThemeExtensionList
|
||||||
*/
|
*/
|
||||||
function system_get_info($type, $name = NULL) {
|
function system_get_info($type, $name = NULL) {
|
||||||
if ($type == 'module') {
|
/** @var \Drupal\Core\Extension\ExtensionList $extension_list */
|
||||||
/** @var \Drupal\Core\Extension\ModuleExtensionList $module_list */
|
$extension_list = \Drupal::service('extension.list.' . $type);
|
||||||
$module_list = \Drupal::service('extension.list.module');
|
if (isset($name)) {
|
||||||
if (isset($name)) {
|
try {
|
||||||
try {
|
return $extension_list->getExtensionInfo($name);
|
||||||
return $module_list->getExtensionInfo($name);
|
|
||||||
}
|
|
||||||
catch (UnknownExtensionException $e) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
catch (\InvalidArgumentException $e) {
|
||||||
return $module_list->getAllInstalledInfo();
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
return $extension_list->getAllInstalledInfo();
|
||||||
// @todo move into ThemeExtensionList https://www.drupal.org/node/2659940
|
|
||||||
$info = [];
|
|
||||||
$list = system_list($type);
|
|
||||||
foreach ($list as $shortname => $item) {
|
|
||||||
if (!empty($item->status)) {
|
|
||||||
$info[$shortname] = $item->info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($name)) {
|
|
||||||
return isset($info[$name]) ? $info[$name] : [];
|
|
||||||
}
|
|
||||||
return $info;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -158,4 +158,14 @@ class ThemeTestController extends ControllerBase {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for testing a namespaced class in a theme.
|
||||||
|
*/
|
||||||
|
public function testThemeClass() {
|
||||||
|
return [
|
||||||
|
'#theme' => 'theme_test_theme_class',
|
||||||
|
'#title' => 'Testing loading a class from a .theme file',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<p>{{ message }}</p>
|
||||||
|
|
@ -70,6 +70,11 @@ function theme_test_theme($existing, $type, $theme, $path) {
|
||||||
'render element' => 'content',
|
'render element' => 'content',
|
||||||
'base hook' => 'container',
|
'base hook' => 'container',
|
||||||
];
|
];
|
||||||
|
$items['theme_test_theme_class'] = [
|
||||||
|
'variables' => [
|
||||||
|
'message' => '',
|
||||||
|
],
|
||||||
|
];
|
||||||
return $items;
|
return $items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,10 @@ theme_test.preprocess_suggestions:
|
||||||
_controller: '\Drupal\theme_test\ThemeTestController::preprocessSuggestions'
|
_controller: '\Drupal\theme_test\ThemeTestController::preprocessSuggestions'
|
||||||
requirements:
|
requirements:
|
||||||
_access: 'TRUE'
|
_access: 'TRUE'
|
||||||
|
|
||||||
|
theme_test.test_theme_class:
|
||||||
|
path: '/theme-test/test-theme-class'
|
||||||
|
defaults:
|
||||||
|
_controller: '\Drupal\theme_test\ThemeTestController::testThemeClass'
|
||||||
|
requirements:
|
||||||
|
_access: 'TRUE'
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,20 @@ class ThemeTest extends BrowserTestBase {
|
||||||
$this->assertTrue(in_array('page__front', $suggestions), 'Front page template was suggested.');
|
$this->assertTrue(in_array('page__front', $suggestions), 'Front page template was suggested.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests theme can provide classes.
|
||||||
|
*/
|
||||||
|
public function testClassLoading() {
|
||||||
|
// Install test theme and set it as default.
|
||||||
|
$this->config('system.theme')
|
||||||
|
->set('default', 'test_theme')
|
||||||
|
->save();
|
||||||
|
$this->resetAll();
|
||||||
|
// Visit page controller and confirm that the theme class is loaded.
|
||||||
|
$this->drupalGet('/theme-test/test-theme-class');
|
||||||
|
$this->assertText('Loading ThemeClass was successful.');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
|
* Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ namespace Drupal\Tests\system\Kernel\Theme;
|
||||||
|
|
||||||
use Drupal\KernelTests\KernelTestBase;
|
use Drupal\KernelTests\KernelTestBase;
|
||||||
use Drupal\Component\Render\MarkupInterface;
|
use Drupal\Component\Render\MarkupInterface;
|
||||||
use Drupal\test_theme\ThemeClass;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests low-level theme functions.
|
* Tests low-level theme functions.
|
||||||
|
|
@ -146,13 +145,6 @@ class ThemeTest extends KernelTestBase {
|
||||||
$this->assertThemeOutput('theme_test_render_element_children', $element, 'Foo', 'drupal_render() avoids #theme_wrappers recursion loop when rendering a render element.');
|
$this->assertThemeOutput('theme_test_render_element_children', $element, 'Foo', 'drupal_render() avoids #theme_wrappers recursion loop when rendering a render element.');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests theme can provide classes.
|
|
||||||
*/
|
|
||||||
public function testClassLoading() {
|
|
||||||
new ThemeClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests drupal_find_theme_templates().
|
* Tests drupal_find_theme_templates().
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -154,3 +154,15 @@ function test_theme_preprocess_theme_test_preprocess_suggestions__kitten__flamin
|
||||||
function test_theme_preprocess_theme_test_preprocess_suggestions__kitten__meerkat__tarsier__moose(&$variables) {
|
function test_theme_preprocess_theme_test_preprocess_suggestions__kitten__meerkat__tarsier__moose(&$variables) {
|
||||||
$variables['bar'] = 'Moose';
|
$variables['bar'] = 'Moose';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that a class can be loaded within a .theme file.
|
||||||
|
*/
|
||||||
|
function test_theme_preprocess_theme_test_theme_class(&$variables) {
|
||||||
|
if (class_exists('\Drupal\test_theme\ThemeClass')) {
|
||||||
|
$variables['message'] = 'Loading ThemeClass was successful.';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$variables['message'] = 'Loading ThemeClass failed.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\KernelTests\Core\Extension;
|
||||||
|
|
||||||
|
use Drupal\Core\Site\Settings;
|
||||||
|
use Drupal\KernelTests\KernelTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @coversDefaultClass \Drupal\Core\Extension\ThemeEngineExtensionList
|
||||||
|
* @group Extension
|
||||||
|
*/
|
||||||
|
class ThemeEngineExtensionListTest extends KernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getList
|
||||||
|
*/
|
||||||
|
public function testGetlist() {
|
||||||
|
$settings = Settings::getAll();
|
||||||
|
$settings['install_profile'] = 'testing';
|
||||||
|
new Settings($settings);
|
||||||
|
|
||||||
|
// Confirm that all theme engines are available.
|
||||||
|
$theme_engines = \Drupal::service('extension.list.theme_engine')->getList();
|
||||||
|
$this->assertArrayHasKey('twig', $theme_engines);
|
||||||
|
$this->assertArrayHasKey('nyan_cat', $theme_engines);
|
||||||
|
$this->assertCount(2, $theme_engines);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\KernelTests\Core\Extension;
|
||||||
|
|
||||||
|
use Drupal\Core\Site\Settings;
|
||||||
|
use Drupal\KernelTests\KernelTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @coversDefaultClass \Drupal\Core\Extension\ThemeExtensionList
|
||||||
|
* @group Extension
|
||||||
|
*/
|
||||||
|
class ThemeExtensionListTest extends KernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers ::getList
|
||||||
|
*/
|
||||||
|
public function testGetlist() {
|
||||||
|
$settings = Settings::getAll();
|
||||||
|
$settings['install_profile'] = 'testing';
|
||||||
|
new Settings($settings);
|
||||||
|
|
||||||
|
\Drupal::configFactory()->getEditable('core.extension')
|
||||||
|
->set('module.testing', 1000)
|
||||||
|
->set('theme.test_theme', 0)
|
||||||
|
->save();
|
||||||
|
|
||||||
|
// The installation profile is provided by a container parameter.
|
||||||
|
// Saving the configuration doesn't automatically trigger invalidation
|
||||||
|
$this->container->get('kernel')->rebuildContainer();
|
||||||
|
|
||||||
|
/** @var \Drupal\Core\Extension\ThemeExtensionList $theme_extension_list */
|
||||||
|
$theme_extension_list = \Drupal::service('extension.list.theme');
|
||||||
|
$extensions = $theme_extension_list->getList();
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('test_theme', $extensions);
|
||||||
|
$this->assertEquals(0, $extensions['test_theme']->weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\KernelTests\Core\Theme;
|
||||||
|
|
||||||
|
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||||
|
use Drupal\Core\Routing\NullMatcherDumper;
|
||||||
|
use Drupal\KernelTests\KernelTestBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests system_list() function.
|
||||||
|
*
|
||||||
|
* In Drupal 8 the system_list() function only lists themes.
|
||||||
|
*
|
||||||
|
* @group Extension
|
||||||
|
* @group legacy
|
||||||
|
*/
|
||||||
|
class SystemListTest extends KernelTestBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $modules = ['system'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function register(ContainerBuilder $container) {
|
||||||
|
parent::register($container);
|
||||||
|
// Some test methods involve ModuleHandler operations, which attempt to
|
||||||
|
// rebuild and dump routes.
|
||||||
|
$container->register('router.dumper', NullMatcherDumper::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function setUp() {
|
||||||
|
parent::setUp();
|
||||||
|
$this->installConfig(['system']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests installing a theme.
|
||||||
|
*
|
||||||
|
* @expectedDeprecation system_list() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal::service('theme_handler')->listInfo() instead. See https://www.drupal.org/node/2709919
|
||||||
|
*/
|
||||||
|
public function testSystemList() {
|
||||||
|
// Verifies that no themes are listed by default.
|
||||||
|
$this->assertEmpty(system_list('theme'));
|
||||||
|
$this->assertEmpty(system_list('filepaths'));
|
||||||
|
|
||||||
|
$this->container->get('theme_installer')->install(['test_basetheme']);
|
||||||
|
|
||||||
|
$themes = system_list('theme');
|
||||||
|
$this->assertTrue(isset($themes['test_basetheme']));
|
||||||
|
$this->assertEqual($themes['test_basetheme']->getName(), 'test_basetheme');
|
||||||
|
$filepaths = system_list('filepaths');
|
||||||
|
$this->assertEquals('test_basetheme', $filepaths[0]['name']);
|
||||||
|
$this->assertEquals('core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml', $filepaths[0]['filepath']);
|
||||||
|
$this->assertCount(1, $filepaths);
|
||||||
|
|
||||||
|
$this->container->get('theme_installer')->uninstall(['test_basetheme']);
|
||||||
|
|
||||||
|
$this->assertEmpty(system_list('theme'));
|
||||||
|
$this->assertEmpty(system_list('filepaths'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ class ThemeInstallerTest extends KernelTestBase {
|
||||||
$this->assertFalse($this->extensionConfig()->get('theme'));
|
$this->assertFalse($this->extensionConfig()->get('theme'));
|
||||||
|
|
||||||
$this->assertFalse(array_keys($this->themeHandler()->listInfo()));
|
$this->assertFalse(array_keys($this->themeHandler()->listInfo()));
|
||||||
$this->assertFalse(array_keys(system_list('theme')));
|
$this->assertFalse(array_keys(\Drupal::service('theme_handler')->listInfo()));
|
||||||
|
|
||||||
// Rebuilding available themes should always yield results though.
|
// Rebuilding available themes should always yield results though.
|
||||||
$this->assertTrue($this->themeHandler()->rebuildThemeData()['stark'], 'ThemeHandler::rebuildThemeData() yields all available themes.');
|
$this->assertTrue($this->themeHandler()->rebuildThemeData()['stark'], 'ThemeHandler::rebuildThemeData() yields all available themes.');
|
||||||
|
|
@ -70,8 +70,6 @@ class ThemeInstallerTest extends KernelTestBase {
|
||||||
$this->assertTrue(isset($themes[$name]));
|
$this->assertTrue(isset($themes[$name]));
|
||||||
$this->assertEqual($themes[$name]->getName(), $name);
|
$this->assertEqual($themes[$name]->getName(), $name);
|
||||||
|
|
||||||
$this->assertEqual(array_keys(system_list('theme')), array_keys($themes));
|
|
||||||
|
|
||||||
// Verify that test_basetheme.settings is active.
|
// Verify that test_basetheme.settings is active.
|
||||||
$this->assertIdentical(theme_get_setting('features.favicon', $name), FALSE);
|
$this->assertIdentical(theme_get_setting('features.favicon', $name), FALSE);
|
||||||
$this->assertEqual(theme_get_setting('base', $name), 'only');
|
$this->assertEqual(theme_get_setting('base', $name), 'only');
|
||||||
|
|
@ -272,7 +270,6 @@ class ThemeInstallerTest extends KernelTestBase {
|
||||||
$this->themeInstaller()->uninstall([$name]);
|
$this->themeInstaller()->uninstall([$name]);
|
||||||
|
|
||||||
$this->assertFalse(array_keys($this->themeHandler()->listInfo()));
|
$this->assertFalse(array_keys($this->themeHandler()->listInfo()));
|
||||||
$this->assertFalse(array_keys(system_list('theme')));
|
|
||||||
|
|
||||||
$this->assertFalse($this->config("$name.settings")->get());
|
$this->assertFalse($this->config("$name.settings")->get());
|
||||||
|
|
||||||
|
|
@ -281,7 +278,6 @@ class ThemeInstallerTest extends KernelTestBase {
|
||||||
$themes = $this->themeHandler()->listInfo();
|
$themes = $this->themeHandler()->listInfo();
|
||||||
$this->assertTrue(isset($themes[$name]));
|
$this->assertTrue(isset($themes[$name]));
|
||||||
$this->assertEqual($themes[$name]->getName(), $name);
|
$this->assertEqual($themes[$name]->getName(), $name);
|
||||||
$this->assertEqual(array_keys(system_list('theme')), array_keys($themes));
|
|
||||||
$this->assertTrue($this->config("$name.settings")->get());
|
$this->assertTrue($this->config("$name.settings")->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,8 +327,8 @@ class ThemeInstallerTest extends KernelTestBase {
|
||||||
$this->assertTrue(isset($info['regions']['test_region']));
|
$this->assertTrue(isset($info['regions']['test_region']));
|
||||||
$regions = system_region_list($name);
|
$regions = system_region_list($name);
|
||||||
$this->assertTrue(isset($regions['test_region']));
|
$this->assertTrue(isset($regions['test_region']));
|
||||||
$system_list = system_list('theme');
|
$theme_list = \Drupal::service('theme_handler')->listInfo();
|
||||||
$this->assertTrue(isset($system_list[$name]->info['regions']['test_region']));
|
$this->assertTrue(isset($theme_list[$name]->info['regions']['test_region']));
|
||||||
|
|
||||||
$this->moduleInstaller()->uninstall(['module_test']);
|
$this->moduleInstaller()->uninstall(['module_test']);
|
||||||
$this->assertFalse($this->moduleHandler()->moduleExists('module_test'));
|
$this->assertFalse($this->moduleHandler()->moduleExists('module_test'));
|
||||||
|
|
@ -347,8 +343,8 @@ class ThemeInstallerTest extends KernelTestBase {
|
||||||
$this->assertFalse(isset($info['regions']['test_region']));
|
$this->assertFalse(isset($info['regions']['test_region']));
|
||||||
$regions = system_region_list($name);
|
$regions = system_region_list($name);
|
||||||
$this->assertFalse(isset($regions['test_region']));
|
$this->assertFalse(isset($regions['test_region']));
|
||||||
$system_list = system_list('theme');
|
$theme_list = \Drupal::service('theme_handler')->listInfo();
|
||||||
$this->assertFalse(isset($system_list[$name]->info['regions']['test_region']));
|
$this->assertFalse(isset($theme_list[$name]->info['regions']['test_region']));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\Tests\Core\Extension;
|
||||||
|
|
||||||
|
use Drupal\Core\Cache\MemoryBackend;
|
||||||
|
use Drupal\Core\Cache\NullBackend;
|
||||||
|
use Drupal\Core\Extension\Extension;
|
||||||
|
use Drupal\Core\Extension\ExtensionDiscovery;
|
||||||
|
use Drupal\Core\Extension\InfoParser;
|
||||||
|
use Drupal\Core\Extension\InfoParserInterface;
|
||||||
|
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||||
|
use Drupal\Core\Extension\ThemeEngineExtensionList;
|
||||||
|
use Drupal\Core\Extension\ThemeExtensionList;
|
||||||
|
use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
|
||||||
|
use Drupal\Core\Lock\NullLockBackend;
|
||||||
|
use Drupal\Core\State\State;
|
||||||
|
use Drupal\Tests\UnitTestCase;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @coversDefaultClass \Drupal\Core\Extension\ThemeExtensionList
|
||||||
|
* @group Extension
|
||||||
|
*/
|
||||||
|
class ThemeExtensionListTest extends UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests rebuild the theme data with theme parents.
|
||||||
|
*/
|
||||||
|
public function testRebuildThemeDataWithThemeParents() {
|
||||||
|
$extension_discovery = $this->prophesize(ExtensionDiscovery::class);
|
||||||
|
$extension_discovery
|
||||||
|
->scan('theme')
|
||||||
|
->willReturn([
|
||||||
|
'test_subtheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml', 'test_subtheme.info.yml'),
|
||||||
|
'test_basetheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml', 'test_basetheme.info.yml'),
|
||||||
|
]);
|
||||||
|
$extension_discovery
|
||||||
|
->scan('theme_engine')
|
||||||
|
->willReturn([
|
||||||
|
'twig' => new Extension($this->root, 'theme_engine', $this->root . '/core/themes/engines/twig/twig.info.yml', 'twig.engine'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Verify that info parser is called with the specified paths.
|
||||||
|
$argument_condition = function ($path) {
|
||||||
|
return in_array($path, [
|
||||||
|
$this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml',
|
||||||
|
$this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml',
|
||||||
|
$this->root . '/core/themes/engines/twig/twig.info.yml',
|
||||||
|
], TRUE);
|
||||||
|
};
|
||||||
|
$info_parser = $this->prophesize(InfoParserInterface::class);
|
||||||
|
$info_parser->parse(Argument::that($argument_condition))
|
||||||
|
->shouldBeCalled()
|
||||||
|
->will(function ($file) {
|
||||||
|
$info_parser = new InfoParser();
|
||||||
|
return $info_parser->parse($file[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||||
|
$module_handler
|
||||||
|
->buildModuleDependencies(Argument::type('array'))
|
||||||
|
->willReturnArgument(0);
|
||||||
|
$module_handler
|
||||||
|
->alter('system_info', Argument::type('array'), Argument::type(Extension::class), Argument::any())
|
||||||
|
->shouldBeCalled();
|
||||||
|
|
||||||
|
$state = new State(new KeyValueMemoryFactory(), new MemoryBackend(), new NullLockBackend());
|
||||||
|
|
||||||
|
$config_factory = $this->getConfigFactoryStub([
|
||||||
|
'core.extension' => [
|
||||||
|
'module' => [],
|
||||||
|
'theme' => [],
|
||||||
|
'disabled' => [
|
||||||
|
'theme' => [],
|
||||||
|
],
|
||||||
|
'theme_engine' => '',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$theme_engine_list = new TestThemeEngineExtensionList($this->root, 'theme_engine', new NullBackend('test'), $info_parser->reveal(), $module_handler->reveal(), $state, $config_factory, 'testing');
|
||||||
|
$theme_engine_list->setExtensionDiscovery($extension_discovery->reveal());
|
||||||
|
|
||||||
|
$theme_list = new TestThemeExtensionList($this->root, 'theme', new NullBackend('test'), $info_parser->reveal(), $module_handler->reveal(), $state, $config_factory, $theme_engine_list, 'testing');
|
||||||
|
$theme_list->setExtensionDiscovery($extension_discovery->reveal());
|
||||||
|
|
||||||
|
$theme_data = $theme_list->reset()->getList();
|
||||||
|
$this->assertCount(2, $theme_data);
|
||||||
|
|
||||||
|
$info_basetheme = $theme_data['test_basetheme'];
|
||||||
|
$info_subtheme = $theme_data['test_subtheme'];
|
||||||
|
|
||||||
|
// Ensure some basic properties.
|
||||||
|
$this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_basetheme);
|
||||||
|
$this->assertEquals('test_basetheme', $info_basetheme->getName());
|
||||||
|
$this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_subtheme);
|
||||||
|
$this->assertEquals('test_subtheme', $info_subtheme->getName());
|
||||||
|
|
||||||
|
// Test the parent/child-theme properties.
|
||||||
|
$info_subtheme->info['base theme'] = 'test_basetheme';
|
||||||
|
$info_basetheme->sub_themes = ['test_subtheme'];
|
||||||
|
|
||||||
|
$this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_basetheme->owner);
|
||||||
|
$this->assertEquals('twig', $info_basetheme->prefix);
|
||||||
|
$this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_subtheme->owner);
|
||||||
|
$this->assertEquals('twig', $info_subtheme->prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
// Mocks and stubs.
|
||||||
|
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||||
|
$state = new State(new KeyValueMemoryFactory(), new MemoryBackend(), new NullLockBackend());
|
||||||
|
$config_factory = $this->getConfigFactoryStub([]);
|
||||||
|
$theme_engine_list = $this->prophesize(ThemeEngineExtensionList::class);
|
||||||
|
$theme_listing = new ThemeExtensionList($this->root, 'theme', new NullBackend('test'), new InfoParser(), $module_handler->reveal(), $state, $config_factory, $theme_engine_list->reveal(), 'test');
|
||||||
|
|
||||||
|
$base_themes = $theme_listing->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 = [];
|
||||||
|
|
||||||
|
// Tests a theme without any base theme.
|
||||||
|
$themes = [];
|
||||||
|
$themes['test_1'] = (object) [
|
||||||
|
'name' => 'test_1',
|
||||||
|
'info' => [
|
||||||
|
'name' => 'test_1',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$data[] = [$themes, 'test_1', []];
|
||||||
|
|
||||||
|
// Tests a theme with a non existing base theme.
|
||||||
|
$themes = [];
|
||||||
|
$themes['test_1'] = (object) [
|
||||||
|
'name' => 'test_1',
|
||||||
|
'info' => [
|
||||||
|
'name' => 'test_1',
|
||||||
|
'base theme' => 'test_2',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$data[] = [$themes, 'test_1', ['test_2' => NULL]];
|
||||||
|
|
||||||
|
// Tests a theme with a single existing base theme.
|
||||||
|
$themes = [];
|
||||||
|
$themes['test_1'] = (object) [
|
||||||
|
'name' => 'test_1',
|
||||||
|
'info' => [
|
||||||
|
'name' => 'test_1',
|
||||||
|
'base theme' => 'test_2',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$themes['test_2'] = (object) [
|
||||||
|
'name' => 'test_2',
|
||||||
|
'info' => [
|
||||||
|
'name' => 'test_2',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$data[] = [$themes, 'test_1', ['test_2' => 'test_2']];
|
||||||
|
|
||||||
|
// Tests a theme with multiple base themes.
|
||||||
|
$themes = [];
|
||||||
|
$themes['test_1'] = (object) [
|
||||||
|
'name' => 'test_1',
|
||||||
|
'info' => [
|
||||||
|
'name' => 'test_1',
|
||||||
|
'base theme' => 'test_2',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$themes['test_2'] = (object) [
|
||||||
|
'name' => 'test_2',
|
||||||
|
'info' => [
|
||||||
|
'name' => 'test_2',
|
||||||
|
'base theme' => 'test_3',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$themes['test_3'] = (object) [
|
||||||
|
'name' => 'test_3',
|
||||||
|
'info' => [
|
||||||
|
'name' => 'test_3',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$data[] = [
|
||||||
|
$themes,
|
||||||
|
'test_1',
|
||||||
|
['test_2' => 'test_2', 'test_3' => 'test_3'],
|
||||||
|
];
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait that allows extension discovery to be set.
|
||||||
|
*/
|
||||||
|
trait SettableDiscoveryExtensionListTrait {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The extension discovery for this extension list.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Extension\ExtensionDiscovery
|
||||||
|
*/
|
||||||
|
protected $extensionDiscovery;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the extension discovery.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Extension\ExtensionDiscovery $discovery
|
||||||
|
* The extension discovery.
|
||||||
|
*/
|
||||||
|
public function setExtensionDiscovery(ExtensionDiscovery $discovery) {
|
||||||
|
$this->extensionDiscovery = $discovery;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getExtensionDiscovery() {
|
||||||
|
return $this->extensionDiscovery;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test theme extension list class.
|
||||||
|
*/
|
||||||
|
class TestThemeExtensionList extends ThemeExtensionList {
|
||||||
|
|
||||||
|
use SettableDiscoveryExtensionListTrait;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test theme engine extension list class.
|
||||||
|
*/
|
||||||
|
class TestThemeEngineExtensionList extends ThemeEngineExtensionList {
|
||||||
|
|
||||||
|
use SettableDiscoveryExtensionListTrait;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('DRUPAL_MINIMUM_PHP')) {
|
||||||
|
define('DRUPAL_MINIMUM_PHP', '5.5.9');
|
||||||
|
}
|
||||||
|
|
@ -7,11 +7,10 @@
|
||||||
|
|
||||||
namespace Drupal\Tests\Core\Extension;
|
namespace Drupal\Tests\Core\Extension;
|
||||||
|
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
use Drupal\Core\Extension\Extension;
|
use Drupal\Core\Extension\Extension;
|
||||||
use Drupal\Core\Extension\InfoParser;
|
use Drupal\Core\Extension\ThemeExtensionList;
|
||||||
use Drupal\Core\Extension\ThemeHandler;
|
use Drupal\Core\Extension\ThemeHandler;
|
||||||
use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
|
|
||||||
use Drupal\Core\State\State;
|
|
||||||
use Drupal\Tests\UnitTestCase;
|
use Drupal\Tests\UnitTestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -20,20 +19,6 @@ use Drupal\Tests\UnitTestCase;
|
||||||
*/
|
*/
|
||||||
class ThemeHandlerTest extends UnitTestCase {
|
class ThemeHandlerTest extends UnitTestCase {
|
||||||
|
|
||||||
/**
|
|
||||||
* The mocked info parser.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Extension\InfoParserInterface|\PHPUnit_Framework_MockObject_MockObject
|
|
||||||
*/
|
|
||||||
protected $infoParser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The mocked state backend.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\State\StateInterface|\PHPUnit_Framework_MockObject_MockObject
|
|
||||||
*/
|
|
||||||
protected $state;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mocked config factory.
|
* The mocked config factory.
|
||||||
*
|
*
|
||||||
|
|
@ -42,18 +27,11 @@ class ThemeHandlerTest extends UnitTestCase {
|
||||||
protected $configFactory;
|
protected $configFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mocked module handler.
|
* The theme listing service.
|
||||||
*
|
*
|
||||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
|
* @var \Drupal\Core\Extension\ThemeExtensionList|\PHPUnit_Framework_MockObject_MockObject
|
||||||
*/
|
*/
|
||||||
protected $moduleHandler;
|
protected $themeList;
|
||||||
|
|
||||||
/**
|
|
||||||
* The extension discovery.
|
|
||||||
*
|
|
||||||
* @var \Drupal\Core\Extension\ExtensionDiscovery|\PHPUnit_Framework_MockObject_MockObject
|
|
||||||
*/
|
|
||||||
protected $extensionDiscovery;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tested theme handler.
|
* The tested theme handler.
|
||||||
|
|
@ -77,16 +55,17 @@ class ThemeHandlerTest extends UnitTestCase {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
$this->themeList = $this->getMockBuilder(ThemeExtensionList::class)
|
||||||
$this->state = new State(new KeyValueMemoryFactory());
|
|
||||||
$this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface');
|
|
||||||
$this->extensionDiscovery = $this->getMockBuilder('Drupal\Core\Extension\ExtensionDiscovery')
|
|
||||||
->disableOriginalConstructor()
|
->disableOriginalConstructor()
|
||||||
->getMock();
|
->getMock();
|
||||||
$this->themeHandler = new StubThemeHandler($this->root, $this->configFactory, $this->moduleHandler, $this->state, $this->infoParser, $this->extensionDiscovery);
|
$this->themeHandler = new StubThemeHandler($this->root, $this->configFactory, $this->themeList);
|
||||||
|
|
||||||
$cache_tags_invalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
|
$container = $this->createMock('Symfony\Component\DependencyInjection\ContainerInterface');
|
||||||
$this->getContainerWithCacheTagsInvalidator($cache_tags_invalidator);
|
$container->expects($this->any())
|
||||||
|
->method('get')
|
||||||
|
->with('class_loader')
|
||||||
|
->will($this->returnValue($this->createMock(ClassLoader::class)));
|
||||||
|
\Drupal::setContainer($container);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -95,31 +74,14 @@ class ThemeHandlerTest extends UnitTestCase {
|
||||||
* @see \Drupal\Core\Extension\ThemeHandler::rebuildThemeData()
|
* @see \Drupal\Core\Extension\ThemeHandler::rebuildThemeData()
|
||||||
*/
|
*/
|
||||||
public function testRebuildThemeData() {
|
public function testRebuildThemeData() {
|
||||||
$this->extensionDiscovery->expects($this->at(0))
|
$this->themeList->expects($this->at(0))
|
||||||
->method('scan')
|
->method('reset')
|
||||||
->with('theme')
|
->willReturnSelf();
|
||||||
|
$this->themeList->expects($this->at(1))
|
||||||
|
->method('getList')
|
||||||
->will($this->returnValue([
|
->will($this->returnValue([
|
||||||
'seven' => new Extension($this->root, 'theme', $this->root . '/core/themes/seven/seven.info.yml', 'seven.theme'),
|
'seven' => new Extension($this->root, 'theme', $this->root . '/core/themes/seven/seven.info.yml', 'seven.theme'),
|
||||||
]));
|
]));
|
||||||
$this->extensionDiscovery->expects($this->at(1))
|
|
||||||
->method('scan')
|
|
||||||
->with('theme_engine')
|
|
||||||
->will($this->returnValue([
|
|
||||||
'twig' => new Extension($this->root, 'theme_engine', $this->root . '/core/themes/engines/twig/twig.info.yml', 'twig.engine'),
|
|
||||||
]));
|
|
||||||
$this->infoParser->expects($this->once())
|
|
||||||
->method('parse')
|
|
||||||
->with($this->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('buildModuleDependencies')
|
|
||||||
->will($this->returnArgument(0));
|
|
||||||
|
|
||||||
$this->moduleHandler->expects($this->once())
|
|
||||||
->method('alter');
|
|
||||||
|
|
||||||
$theme_data = $this->themeHandler->rebuildThemeData();
|
$theme_data = $this->themeHandler->rebuildThemeData();
|
||||||
$this->assertCount(1, $theme_data);
|
$this->assertCount(1, $theme_data);
|
||||||
|
|
@ -130,11 +92,7 @@ class ThemeHandlerTest extends UnitTestCase {
|
||||||
$this->assertEquals('seven', $info->getName());
|
$this->assertEquals('seven', $info->getName());
|
||||||
$this->assertEquals($this->root . '/core/themes/seven/seven.info.yml', $info->getPathname());
|
$this->assertEquals($this->root . '/core/themes/seven/seven.info.yml', $info->getPathname());
|
||||||
$this->assertEquals($this->root . '/core/themes/seven/seven.theme', $info->getExtensionPathname());
|
$this->assertEquals($this->root . '/core/themes/seven/seven.theme', $info->getExtensionPathname());
|
||||||
$this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info->owner);
|
|
||||||
$this->assertEquals('twig', $info->prefix);
|
|
||||||
|
|
||||||
$this->assertEquals('twig', $info->info['engine']);
|
|
||||||
$this->assertEquals(['seven/global-styling'], $info->info['libraries']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -151,158 +109,6 @@ class ThemeHandlerTest extends UnitTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests rebuild the theme data with theme parents.
|
|
||||||
*/
|
|
||||||
public function testRebuildThemeDataWithThemeParents() {
|
|
||||||
$this->extensionDiscovery->expects($this->at(0))
|
|
||||||
->method('scan')
|
|
||||||
->with('theme')
|
|
||||||
->will($this->returnValue([
|
|
||||||
'test_subtheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml', 'test_subtheme.info.yml'),
|
|
||||||
'test_basetheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml', 'test_basetheme.info.yml'),
|
|
||||||
]));
|
|
||||||
$this->extensionDiscovery->expects($this->at(1))
|
|
||||||
->method('scan')
|
|
||||||
->with('theme_engine')
|
|
||||||
->will($this->returnValue([
|
|
||||||
'twig' => new Extension($this->root, 'theme_engine', $this->root . '/core/themes/engines/twig/twig.info.yml', 'twig.engine'),
|
|
||||||
]));
|
|
||||||
$this->infoParser->expects($this->at(0))
|
|
||||||
->method('parse')
|
|
||||||
->with($this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml')
|
|
||||||
->will($this->returnCallback(function ($file) {
|
|
||||||
$info_parser = new InfoParser();
|
|
||||||
return $info_parser->parse($file);
|
|
||||||
}));
|
|
||||||
$this->infoParser->expects($this->at(1))
|
|
||||||
->method('parse')
|
|
||||||
->with($this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml')
|
|
||||||
->will($this->returnCallback(function ($file) {
|
|
||||||
$info_parser = new InfoParser();
|
|
||||||
return $info_parser->parse($file);
|
|
||||||
}));
|
|
||||||
$this->moduleHandler->expects($this->once())
|
|
||||||
->method('buildModuleDependencies')
|
|
||||||
->will($this->returnArgument(0));
|
|
||||||
|
|
||||||
$theme_data = $this->themeHandler->rebuildThemeData();
|
|
||||||
$this->assertCount(2, $theme_data);
|
|
||||||
|
|
||||||
$info_basetheme = $theme_data['test_basetheme'];
|
|
||||||
$info_subtheme = $theme_data['test_subtheme'];
|
|
||||||
|
|
||||||
// Ensure some basic properties.
|
|
||||||
$this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_basetheme);
|
|
||||||
$this->assertEquals('test_basetheme', $info_basetheme->getName());
|
|
||||||
$this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_subtheme);
|
|
||||||
$this->assertEquals('test_subtheme', $info_subtheme->getName());
|
|
||||||
|
|
||||||
// Test the parent/child-theme properties.
|
|
||||||
$info_subtheme->info['base theme'] = 'test_basetheme';
|
|
||||||
$info_basetheme->sub_themes = ['test_subtheme'];
|
|
||||||
|
|
||||||
$this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_basetheme->owner);
|
|
||||||
$this->assertEquals('twig', $info_basetheme->prefix);
|
|
||||||
$this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_subtheme->owner);
|
|
||||||
$this->assertEquals('twig', $info_subtheme->prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 = [];
|
|
||||||
|
|
||||||
// Tests a theme without any base theme.
|
|
||||||
$themes = [];
|
|
||||||
$themes['test_1'] = (object) [
|
|
||||||
'name' => 'test_1',
|
|
||||||
'info' => [
|
|
||||||
'name' => 'test_1',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$data[] = [$themes, 'test_1', []];
|
|
||||||
|
|
||||||
// Tests a theme with a non existing base theme.
|
|
||||||
$themes = [];
|
|
||||||
$themes['test_1'] = (object) [
|
|
||||||
'name' => 'test_1',
|
|
||||||
'info' => [
|
|
||||||
'name' => 'test_1',
|
|
||||||
'base theme' => 'test_2',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$data[] = [$themes, 'test_1', ['test_2' => NULL]];
|
|
||||||
|
|
||||||
// Tests a theme with a single existing base theme.
|
|
||||||
$themes = [];
|
|
||||||
$themes['test_1'] = (object) [
|
|
||||||
'name' => 'test_1',
|
|
||||||
'info' => [
|
|
||||||
'name' => 'test_1',
|
|
||||||
'base theme' => 'test_2',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$themes['test_2'] = (object) [
|
|
||||||
'name' => 'test_2',
|
|
||||||
'info' => [
|
|
||||||
'name' => 'test_2',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$data[] = [$themes, 'test_1', ['test_2' => 'test_2']];
|
|
||||||
|
|
||||||
// Tests a theme with multiple base themes.
|
|
||||||
$themes = [];
|
|
||||||
$themes['test_1'] = (object) [
|
|
||||||
'name' => 'test_1',
|
|
||||||
'info' => [
|
|
||||||
'name' => 'test_1',
|
|
||||||
'base theme' => 'test_2',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$themes['test_2'] = (object) [
|
|
||||||
'name' => 'test_2',
|
|
||||||
'info' => [
|
|
||||||
'name' => 'test_2',
|
|
||||||
'base theme' => 'test_3',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$themes['test_3'] = (object) [
|
|
||||||
'name' => 'test_3',
|
|
||||||
'info' => [
|
|
||||||
'name' => 'test_3',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$data[] = [
|
|
||||||
$themes,
|
|
||||||
'test_1',
|
|
||||||
['test_2' => 'test_2', 'test_3' => 'test_3'],
|
|
||||||
];
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -324,13 +130,6 @@ class StubThemeHandler extends ThemeHandler {
|
||||||
*/
|
*/
|
||||||
protected $registryRebuild;
|
protected $registryRebuild;
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of themes keyed by name.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $systemList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -345,27 +144,8 @@ class StubThemeHandler extends ThemeHandler {
|
||||||
$this->registryRebuild = TRUE;
|
$this->registryRebuild = 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_MINIMUM_PHP')) {
|
if (!defined('DRUPAL_MINIMUM_PHP')) {
|
||||||
define('DRUPAL_MINIMUM_PHP', '5.3.10');
|
define('DRUPAL_MINIMUM_PHP', '5.5.9');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue