Commit ec7b0562 authored by alexpott's avatar alexpott
Browse files

Issue #2367579 by tim.plunkett, chx: Fixed Move retrieval of visible blocks to...

Issue #2367579 by tim.plunkett, chx: Fixed Move retrieval of visible blocks to a standalone service.
parent 957078ec
......@@ -18,3 +18,6 @@ services:
arguments: ['@current_route_match']
tags:
- { name: 'event_subscriber' }
block.repository:
class: Drupal\block\BlockRepository
arguments: ['@entity.manager', '@theme.manager']
<?php
/**
* @file
* Contains \Drupal\block\BlockRepository.
*/
namespace Drupal\block;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
/**
* Provides a repository for Block config entities.
*/
class BlockRepository implements BlockRepositoryInterface {
/**
* The block storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $blockStorage;
/**
* The theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* Constructs a new BlockRepository.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager.
*/
public function __construct(EntityManagerInterface $entity_manager, ThemeManagerInterface $theme_manager) {
$this->blockStorage = $entity_manager->getStorage('block');
$this->themeManager = $theme_manager;
}
/**
* Returns the human-readable list of regions keyed by machine name.
*
* @return array
* An array of human-readable region names keyed by machine name.
*/
protected function getRegionNames() {
return system_region_list($this->getTheme());
}
/**
* Gets the current theme for this page.
*
* @return string
* The current theme.
*/
protected function getTheme() {
return $this->themeManager->getActiveTheme()->getName();
}
/**
* {@inheritdoc}
*/
public function getVisibleBlocksPerRegion() {
// Build an array of the region names in the right order.
$empty = array_fill_keys(array_keys($this->getRegionNames()), array());
$full = array();
foreach ($this->blockStorage->loadByProperties(array('theme' => $this->getTheme())) as $block_id => $block) {
if ($block->access('view')) {
$full[$block->get('region')][$block_id] = $block;
}
}
// Merge it with the actual values to maintain the region ordering.
$assignments = array_intersect_key(array_merge($empty, $full), $empty);
foreach ($assignments as &$assignment) {
// Suppress errors because PHPUnit will indirectly modify the contents,
// triggering https://bugs.php.net/bug.php?id=50688.
@uasort($assignment, 'Drupal\block\Entity\Block::sort');
}
return $assignments;
}
}
<?php
/**
* @file
* Contains \Drupal\block\BlockRepositoryInterface.
*/
namespace Drupal\block;
interface BlockRepositoryInterface {
/**
* Returns an array of regions and their block entities.
*
* @return array
* The array is first keyed by region machine name, with the values
* containing an array keyed by block ID, with block entities as the values.
*/
public function getVisibleBlocksPerRegion();
}
......@@ -7,14 +7,12 @@
namespace Drupal\block\Plugin\DisplayVariant;
use Drupal\block\BlockRepositoryInterface;
use Drupal\Core\Block\MainContentBlockPluginInterface;
use Drupal\Core\Display\PageVariantInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityViewBuilderInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Display\VariantBase;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -28,11 +26,11 @@
class FullPageVariant extends VariantBase implements PageVariantInterface, ContainerFactoryPluginInterface {
/**
* The block storage.
* The block repository.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
* @var \Drupal\block\BlockRepositoryInterface
*/
protected $blockStorage;
protected $blockRepository;
/**
* The block view builder.
......@@ -48,20 +46,6 @@ class FullPageVariant extends VariantBase implements PageVariantInterface, Conta
*/
protected $theme;
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* The theme negotiator.
*
* @var \Drupal\Core\Theme\ThemeNegotiatorInterface
*/
protected $themeNegotiator;
/**
* The render array representing the main page content.
*
......@@ -78,21 +62,15 @@ class FullPageVariant extends VariantBase implements PageVariantInterface, Conta
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $block_storage
* The block entity storage.
* @param \Drupal\block\BlockRepositoryInterface $block_repository
* The block repository.
* @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
* The block view builder.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param \Drupal\Core\Theme\ThemeNegotiatorInterface $theme_negotiator
* The theme negotiator.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $block_storage, EntityViewBuilderInterface $block_view_builder, RouteMatchInterface $route_match, ThemeNegotiatorInterface $theme_negotiator) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->blockStorage = $block_storage;
$this->blockRepository = $block_repository;
$this->blockViewBuilder = $block_view_builder;
$this->routeMatch = $route_match;
$this->themeNegotiator = $theme_negotiator;
}
/**
......@@ -103,10 +81,8 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')->getStorage('block'),
$container->get('entity.manager')->getViewBuilder('block'),
$container->get('current_route_match'),
$container->get('theme.negotiator')
$container->get('block.repository'),
$container->get('entity.manager')->getViewBuilder('block')
);
}
......@@ -118,16 +94,6 @@ public function setMainContent(array $main_content) {
return $this;
}
/**
* Gets the current theme for this page.
*
* @return string
* The current theme.
*/
protected function getTheme() {
return $this->themeNegotiator->determineActiveTheme($this->routeMatch);
}
/**
* {@inheritdoc}
*/
......@@ -137,20 +103,19 @@ public function build() {
$build = array();
// Load all region content assigned via blocks.
foreach ($this->getRegionAssignments() as $region => $blocks) {
foreach ($this->blockRepository->getVisibleBlocksPerRegion() as $region => $blocks) {
/** @var $blocks \Drupal\block\BlockInterface[] */
foreach ($blocks as $key => $block) {
if ($block->access('view')) {
$block_plugin = $block->getPlugin();
if ($block_plugin instanceof MainContentBlockPluginInterface) {
$block_plugin->setMainContent($this->mainContent);
$main_content_block_displayed = TRUE;
}
$build[$region][$key] = $this->blockViewBuilder->view($block);
$block_plugin = $block->getPlugin();
if ($block_plugin instanceof MainContentBlockPluginInterface) {
$block_plugin->setMainContent($this->mainContent);
$main_content_block_displayed = TRUE;
}
$build[$region][$key] = $this->blockViewBuilder->view($block);
}
if (!empty($build[$region])) {
// self::getRegionAssignments() returns the blocks in sorted order.
// \Drupal\block\BlockRepositoryInterface::getVisibleBlocksPerRegion()
// returns the blocks in sorted order.
$build[$region]['#sorted'] = TRUE;
}
}
......@@ -165,40 +130,4 @@ public function build() {
return $build;
}
/**
* Returns the human-readable list of regions keyed by machine name.
*
* @return array
* An array of human-readable region names keyed by machine name.
*/
protected function getRegionNames() {
return system_region_list($this->getTheme());
}
/**
* Returns an array of regions and their block entities.
*
* @return array
* The array is first keyed by region machine name, with the values
* containing an array keyed by block ID, with block entities as the values.
*/
protected function getRegionAssignments() {
// Build an array of the region names in the right order.
$empty = array_fill_keys(array_keys($this->getRegionNames()), array());
$full = array();
foreach ($this->blockStorage->loadByProperties(array('theme' => $this->getTheme())) as $block_id => $block) {
$full[$block->get('region')][$block_id] = $block;
}
// Merge it with the actual values to maintain the region ordering.
$assignments = array_intersect_key(array_merge($empty, $full), $empty);
foreach ($assignments as &$assignment) {
// Suppress errors because PHPUnit will indirectly modify the contents,
// triggering https://bugs.php.net/bug.php?id=50688.
@uasort($assignment, 'Drupal\block\Entity\Block::sort');
}
return $assignments;
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\block\Unit\BlockRepositoryTest.
*/
namespace Drupal\Tests\block\Unit;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\block\BlockRepository
* @group block
*/
class BlockRepositoryTest extends UnitTestCase {
/**
* Tests the retrieval of block entities.
*
* @covers ::getVisibleBlocksPerRegion
*
* @dataProvider providerBlocksConfig
*/
function testGetVisibleBlocksPerRegion(array $blocks_config, array $expected_blocks) {
$theme = $this->randomMachineName();
$active_theme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme')
->disableOriginalConstructor()
->getMock();
$active_theme->expects($this->atLeastOnce())
->method('getName')
->willReturn($theme);
$theme_manager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
$theme_manager->expects($this->once())
->method('getActiveTheme')
->will($this->returnValue($active_theme));
$block_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$entity_manager->expects($this->any())
->method('getStorage')
->willReturn($block_storage);
$block_repository = $this->getMockBuilder('Drupal\block\BlockRepository')
->setConstructorArgs([$entity_manager, $theme_manager])
->setMethods(['getRegionNames'])
->getMock();
$block_repository->expects($this->once())
->method('getRegionNames')
->willReturn([
'top' => 'Top',
'center' => 'Center',
'bottom' => 'Bottom',
]);
$blocks = [];
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
$block->expects($this->once())
->method('access')
->will($this->returnValue($block_config[0]));
$block->expects($block_config[0] ? $this->atLeastOnce() : $this->never())
->method('get')
->will($this->returnValueMap(array(
array('region', $block_config[1]),
array('weight', $block_config[2]),
array('status', TRUE),
)));
$blocks[$block_id] = $block;
}
$block_storage->expects($this->once())
->method('loadByProperties')
->with(['theme' => $theme])
->willReturn($blocks);
$result = [];
foreach ($block_repository->getVisibleBlocksPerRegion() as $region => $resulting_blocks) {
$result[$region] = [];
foreach ($resulting_blocks as $plugin_id => $block) {
$result[$region][] = $plugin_id;
}
}
$this->assertSame($result, $expected_blocks);
}
public function providerBlocksConfig() {
$blocks_config = array(
'block1' => array(
TRUE, 'top', 0
),
// Test a block without access.
'block2' => array(
FALSE, 'bottom', 0
),
// Test two blocks in the same region with specific weight.
'block3' => array(
TRUE, 'bottom', 5
),
'block4' => array(
TRUE, 'bottom', -5
),
);
$test_cases = [];
$test_cases[] = [$blocks_config,
[
'top' => ['block1'],
'center' => [],
'bottom' => ['block4', 'block3'],
]
];
return $test_cases;
}
}
......@@ -16,11 +16,11 @@
class FullPageVariantTest extends UnitTestCase {
/**
* The block storage.
* The block repository.
*
* @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
* @var \Drupal\block\BlockRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $blockStorage;
protected $blockRepository;
/**
* The block view builder.
......@@ -55,12 +55,12 @@ class FullPageVariantTest extends UnitTestCase {
* A mocked display variant plugin.
*/
public function setUpDisplayVariant($configuration = array(), $definition = array()) {
$this->blockStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
$this->blockRepository = $this->getMock('Drupal\block\BlockRepositoryInterface');
$this->blockViewBuilder = $this->getMock('Drupal\Core\Entity\EntityViewBuilderInterface');
$this->routeMatch = $this->getMock('Drupal\Core\Routing\RouteMatchInterface');
$this->themeNegotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface');
return $this->getMockBuilder('Drupal\block\Plugin\DisplayVariant\FullPageVariant')
->setConstructorArgs(array($configuration, 'test', $definition, $this->blockStorage, $this->blockViewBuilder, $this->routeMatch, $this->themeNegotiator))
->setConstructorArgs(array($configuration, 'test', $definition, $this->blockRepository, $this->blockViewBuilder, $this->routeMatch, $this->themeNegotiator))
->setMethods(array('getRegionNames'))
->getMock();
}
......@@ -68,22 +68,18 @@ public function setUpDisplayVariant($configuration = array(), $definition = arra
public function providerBuild() {
$blocks_config = array(
'block1' => array(
TRUE, 'top', 0, FALSE,
'top', FALSE,
),
// Test a block without access.
// Test multiple blocks in the same region.
'block2' => array(
FALSE, 'bottom', 0, FALSE,
'bottom', FALSE,
),
// Test two blocks in the same region with specific weight.
'block3' => array(
TRUE, 'bottom', 5, FALSE,
),
'block4' => array(
TRUE, 'bottom', -5, FALSE,
'bottom', FALSE,
),
// Test a block implementing MainContentBlockPluginInterface.
'block5' => array(
TRUE, 'center', 0, TRUE,
'block4' => array(
'center', TRUE,
),
);
......@@ -96,17 +92,17 @@ public function providerBuild() {
],
// The main content was rendered via a block.
'center' => [
'block5' => [],
'block4' => [],
'#sorted' => TRUE,
],
'bottom' => [
'block4' => [],
'block2' => [],
'block3' => [],
'#sorted' => TRUE,
],
],
];
unset($blocks_config['block5']);
unset($blocks_config['block4']);
$test_cases[] = [$blocks_config, 3,
[
'top' => [
......@@ -114,7 +110,7 @@ public function providerBuild() {
'#sorted' => TRUE,
],
'bottom' => [
'block4' => [],
'block2' => [],
'block3' => [],
'#sorted' => TRUE,
],
......@@ -132,53 +128,29 @@ public function providerBuild() {
* Tests the building of a full page variant.
*
* @covers ::build
* @covers ::getRegionAssignments
*
* @dataProvider providerBuild
*/
public function testBuild(array $blocks_config, $visible_block_count, array $expected_render_array) {
$theme = $this->randomMachineName();
$display_variant = $this->setUpDisplayVariant();
$this->themeNegotiator->expects($this->any())
->method('determineActiveTheme')
->with($this->routeMatch)
->will($this->returnValue($theme));
$display_variant->expects($this->once())
->method('getRegionNames')
->will($this->returnValue(array(
'top' => 'Top',
'center' => 'Center',
'bottom' => 'Bottom',
)));
$display_variant->setMainContent(['#markup' => 'Hello kittens!']);
$blocks = array();
$blocks = ['top' => [], 'center' => [], 'bottom' => []];
$block_plugin = $this->getMock('Drupal\Core\Block\BlockPluginInterface');
$main_content_block_plugin = $this->getMock('Drupal\Core\Block\MainContentBlockPluginInterface');
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
$block->expects($this->once())
->method('access')
->will($this->returnValue($block_config[0]));
$block->expects($this->any())
->method('get')
->will($this->returnValueMap(array(
array('region', $block_config[1]),
array('weight', $block_config[2]),
array('status', TRUE),
)));
$block->expects($this->any())
$block->expects($this->atLeastOnce())
->method('getPlugin')
->willReturn($block_config[3] ? $main_content_block_plugin : $block_plugin);
$blocks[$block_id] = $block;
->willReturn($block_config[1] ? $main_content_block_plugin : $block_plugin);
$blocks[$block_config[0]][$block_id] = $block;
}
$this->blockViewBuilder->expects($this->exactly($visible_block_count))
->method('view')
->will($this->returnValue(array()));
$this->blockStorage->expects($this->once())
->method('loadByProperties')
->with(array('theme' => $theme))
$this->blockRepository->expects($this->once())
->method('getVisibleBlocksPerRegion')
->will($this->returnValue($blocks));
$this->assertSame($expected_render_array, $display_variant->build());
......
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