Commit d64047e3 authored by catch's avatar catch

Issue #2448843 by dawehner: [regression] Themes unable to implement hook_element_info_alter()

parent 95fe74d5
......@@ -979,7 +979,7 @@ services:
parent: default_plugin_manager
plugin.manager.element_info:
class: Drupal\Core\Render\ElementInfoManager
parent: default_plugin_manager
arguments: ['@container.namespaces', '@cache.discovery', '@cache_tags.invalidator', '@module_handler', '@theme.manager']
stream_wrapper_manager:
class: Drupal\Core\StreamWrapper\StreamWrapperManager
calls:
......
......@@ -9,9 +9,11 @@
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Render\Element\FormElementInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
/**
* Provides a plugin manager for element plugins.
......@@ -33,6 +35,20 @@ class ElementInfoManager extends DefaultPluginManager implements ElementInfoMana
*/
protected $elementInfo;
/**
* The theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* The cache tag invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
*/
protected $cacheTagInvalidator;
/**
* Constructs a ElementInfoManager object.
*
......@@ -41,11 +57,17 @@ class ElementInfoManager extends DefaultPluginManager implements ElementInfoMana
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tag_invalidator
* The cache tag invalidator.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, CacheTagsInvalidatorInterface $cache_tag_invalidator, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager) {
$this->setCacheBackend($cache_backend, 'element_info');
$this->themeManager = $theme_manager;
$this->cacheTagInvalidator = $cache_tag_invalidator;
parent::__construct('Element', $namespaces, $module_handler, 'Drupal\Core\Render\Element\ElementInterface', 'Drupal\Core\Render\Annotation\RenderElement');
}
......@@ -54,20 +76,27 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
* {@inheritdoc}
*/
public function getInfo($type) {
if (!isset($this->elementInfo)) {
$this->elementInfo = $this->buildInfo();
$theme_name = $this->themeManager->getActiveTheme()->getName();
if (!isset($this->elementInfo[$theme_name])) {
$this->elementInfo[$theme_name] = $this->buildInfo($theme_name);
}
$info = isset($this->elementInfo[$type]) ? $this->elementInfo[$type] : array();
$info = isset($this->elementInfo[$theme_name][$type]) ? $this->elementInfo[$theme_name][$type] : array();
$info['#defaults_loaded'] = TRUE;
return $info;
}
/**
* Builds up all element information.
*
* @param string $theme_name
* The theme name.
*
* @return array
*/
protected function buildInfo() {
protected function buildInfo($theme_name) {
// Get cached definitions.
if ($cache = $this->cacheBackend->get('element_info_build')) {
$cid = $this->getCid($theme_name);
if ($cache = $this->cacheBackend->get($cid)) {
return $cache->data;
}
......@@ -94,8 +123,9 @@ protected function buildInfo() {
}
// Allow modules to alter the element type defaults.
$this->moduleHandler->alter('element_info', $info);
$this->themeManager->alter('element_info', $info);
$this->cacheBackend->set('element_info_build', $info, Cache::PERMANENT);
$this->cacheBackend->set($cid, $info, Cache::PERMANENT, ['element_info_build']);
return $info;
}
......@@ -114,10 +144,21 @@ public function createInstance($plugin_id, array $configuration = array()) {
*/
public function clearCachedDefinitions() {
$this->elementInfo = NULL;
$this->cacheBackend->delete('element_info_build');
$this->cacheTagInvalidator->invalidateTags(['element_info_build']);
parent::clearCachedDefinitions();
}
/**
* Returns the CID used to cache the element info.
*
* @param string $theme_name
* The theme name.
*
* @return string
*/
protected function getCid($theme_name) {
return 'element_info_build:' . $theme_name;
}
}
......@@ -87,6 +87,16 @@ class ActiveTheme {
* The properties of the object, keyed by the names.
*/
public function __construct(array $values) {
$values += [
'path' => '',
'engine' => 'twig',
'owner' => 'twig',
'stylesheets_remove' => [],
'stylesheets_override' => [],
'libraries' => [],
'extension' => 'html.twig',
'base_themes' => [],
];
$this->name = $values['name'];
$this->path = $values['path'];
$this->engine = $values['engine'];
......
<?php
/**
* @file
* Contains \Drupal\system\Tests\Render\ElementInfoIntegrationTest.
*/
namespace Drupal\system\Tests\Render;
use Drupal\simpletest\KernelTestBase;
/**
* Tests the element info.
*
* @group Render
*/
class ElementInfoIntegrationTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container->get('theme_handler')->install(['test_theme', 'classy']);
}
/**
* Ensures that the element info can be altered by themes.
*/
public function testElementInfoByTheme() {
/** @var \Drupal\Core\Theme\ThemeInitializationInterface $theme_initializer */
$theme_initializer = $this->container->get('theme.initialization');
/** @var \Drupal\Core\Theme\ThemeManagerInterface $theme_manager */
$theme_manager = $this->container->get('theme.manager');
/** @var \Drupal\Core\Render\ElementInfoManagerInterface $element_info */
$element_info = $this->container->get('plugin.manager.element_info');
$theme_manager->setActiveTheme($theme_initializer->getActiveThemeByName('classy'));
$this->assertEqual(60, $element_info->getInfo('textfield')['#size']);
$theme_manager->setActiveTheme($theme_initializer->getActiveThemeByName('test_theme'));
$this->assertEqual(40, $element_info->getInfo('textfield')['#size']);
}
}
......@@ -19,6 +19,16 @@ function test_theme_theme_test__suggestion($variables) {
return 'Theme hook implementor=test_theme_theme_test__suggestion(). Foo=' . $variables['foo'];
}
/**
* Implements hook_element_info_alter().
*/
function test_theme_element_info_alter(&$info) {
// Decrease the default size of textfields.
if (isset($info['textfield']['#size'])) {
$info['textfield']['#size'] = 40;
}
}
/**
* Tests a theme implementing an alter hook.
*
......
......@@ -8,6 +8,7 @@
namespace Drupal\Tests\Core\Render;
use Drupal\Core\Render\ElementInfoManager;
use Drupal\Core\Theme\ActiveTheme;
use Drupal\Tests\UnitTestCase;
/**
......@@ -37,6 +38,20 @@ class ElementInfoManagerTest extends UnitTestCase {
*/
protected $moduleHandler;
/**
* The mocked theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $themeManager;
/**
* The cache tags invalidator.
*
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $cacheTagsInvalidator;
/**
* {@inheritdoc}
*
......@@ -44,9 +59,11 @@ class ElementInfoManagerTest extends UnitTestCase {
*/
protected function setUp() {
$this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
$this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
$this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
$this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
$this->elementInfo = new ElementInfoManager(new \ArrayObject(), $this->cache, $this->moduleHandler);
$this->elementInfo = new ElementInfoManager(new \ArrayObject(), $this->cache, $this->cacheTagsInvalidator, $this->moduleHandler, $this->themeManager);
}
/**
......@@ -68,10 +85,19 @@ public function testGetInfo($type, $expected_info, $element_info, callable $alte
->will($this->returnCallback($alter_callback ?: function($info) {
return $info;
}));
$this->themeManager->expects($this->once())
->method('getActiveTheme')
->willReturn(new ActiveTheme(['name' => 'test']));
$this->themeManager->expects($this->once())
->method('alter')
->with('element_info', $this->anything())
->will($this->returnCallback($alter_callback ?: function($info) {
return $info;
}));
$this->cache->expects($this->at(0))
->method('get')
->with('element_info_build')
->with('element_info_build:test')
->will($this->returnValue(FALSE));
$this->cache->expects($this->at(1))
->method('get')
......@@ -82,7 +108,7 @@ public function testGetInfo($type, $expected_info, $element_info, callable $alte
->with('element_info');
$this->cache->expects($this->at(3))
->method('set')
->with('element_info_build');
->with('element_info_build:test');
$this->assertEquals($expected_info, $this->elementInfo->getInfo($type));
}
......@@ -167,9 +193,14 @@ public function testGetInfoElementPlugin($plugin_class, $expected_info) {
));
$element_info = $this->getMockBuilder('Drupal\Core\Render\ElementInfoManager')
->setConstructorArgs(array(new \ArrayObject(), $this->cache, $this->moduleHandler))
->setConstructorArgs(array(new \ArrayObject(), $this->cache, $this->cacheTagsInvalidator, $this->moduleHandler, $this->themeManager))
->setMethods(array('getDefinitions', 'createInstance'))
->getMock();
$this->themeManager->expects($this->any())
->method('getActiveTheme')
->willReturn(new ActiveTheme(['name' => 'test']));
$element_info->expects($this->once())
->method('createInstance')
->with('page')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment