Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
CRAP | |
65.12% |
28 / 43 |
| BlockPageVariant | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
31.75 | |
65.12% |
28 / 43 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
| create | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
| setMainContent | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| setTitle | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| build | |
0.00% |
0 / 1 |
14.06 | |
93.33% |
28 / 30 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\block\Plugin\DisplayVariant\BlockPageVariant. | |
| */ | |
| namespace Drupal\block\Plugin\DisplayVariant; | |
| use Drupal\block\BlockRepositoryInterface; | |
| use Drupal\Core\Block\MainContentBlockPluginInterface; | |
| use Drupal\Core\Block\TitleBlockPluginInterface; | |
| use Drupal\Core\Block\MessagesBlockPluginInterface; | |
| use Drupal\Core\Cache\CacheableMetadata; | |
| use Drupal\Core\Display\PageVariantInterface; | |
| use Drupal\Core\Entity\EntityViewBuilderInterface; | |
| use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
| use Drupal\Core\Display\VariantBase; | |
| use Symfony\Component\DependencyInjection\ContainerInterface; | |
| /** | |
| * Provides a page display variant that decorates the main content with blocks. | |
| * | |
| * To ensure essential information is displayed, each essential part of a page | |
| * has a corresponding block plugin interface, so that BlockPageVariant can | |
| * automatically provide a fallback in case no block for each of these | |
| * interfaces is placed. | |
| * | |
| * @see \Drupal\Core\Block\MainContentBlockPluginInterface | |
| * @see \Drupal\Core\Block\MessagesBlockPluginInterface | |
| * | |
| * @PageDisplayVariant( | |
| * id = "block_page", | |
| * admin_label = @Translation("Page with blocks") | |
| * ) | |
| */ | |
| class BlockPageVariant extends VariantBase implements PageVariantInterface, ContainerFactoryPluginInterface { | |
| /** | |
| * The block repository. | |
| * | |
| * @var \Drupal\block\BlockRepositoryInterface | |
| */ | |
| protected $blockRepository; | |
| /** | |
| * The block view builder. | |
| * | |
| * @var \Drupal\Core\Entity\EntityViewBuilderInterface | |
| */ | |
| protected $blockViewBuilder; | |
| /** | |
| * The Block entity type list cache tags. | |
| * | |
| * @var string[] | |
| */ | |
| protected $blockListCacheTags; | |
| /** | |
| * The render array representing the main page content. | |
| * | |
| * @var array | |
| */ | |
| protected $mainContent = []; | |
| /** | |
| * The page title: a string (plain title) or a render array (formatted title). | |
| * | |
| * @var string|array | |
| */ | |
| protected $title = ''; | |
| /** | |
| * Constructs a new BlockPageVariant. | |
| * | |
| * @param array $configuration | |
| * A configuration array containing information about the plugin instance. | |
| * @param string $plugin_id | |
| * The plugin ID for the plugin instance. | |
| * @param mixed $plugin_definition | |
| * The plugin implementation definition. | |
| * @param \Drupal\block\BlockRepositoryInterface $block_repository | |
| * The block repository. | |
| * @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder | |
| * The block view builder. | |
| * @param string[] $block_list_cache_tags | |
| * The Block entity type list cache tags. | |
| */ | |
| public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, array $block_list_cache_tags) { | |
| parent::__construct($configuration, $plugin_id, $plugin_definition); | |
| $this->blockRepository = $block_repository; | |
| $this->blockViewBuilder = $block_view_builder; | |
| $this->blockListCacheTags = $block_list_cache_tags; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | |
| return new static( | |
| $configuration, | |
| $plugin_id, | |
| $plugin_definition, | |
| $container->get('block.repository'), | |
| $container->get('entity.manager')->getViewBuilder('block'), | |
| $container->get('entity.manager')->getDefinition('block')->getListCacheTags() | |
| ); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setMainContent(array $main_content) { | |
| $this->mainContent = $main_content; | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setTitle($title) { | |
| $this->title = $title; | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function build() { | |
| // Track whether blocks showing the main content and messages are displayed. | |
| $main_content_block_displayed = FALSE; | |
| $messages_block_displayed = FALSE; | |
| $build = [ | |
| '#cache' => [ | |
| 'tags' => $this->blockListCacheTags, | |
| ], | |
| ]; | |
| // Load all region content assigned via blocks. | |
| $cacheable_metadata_list = []; | |
| foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata_list) as $region => $blocks) { | |
| /** @var $blocks \Drupal\block\BlockInterface[] */ | |
| foreach ($blocks as $key => $block) { | |
| $block_plugin = $block->getPlugin(); | |
| if ($block_plugin instanceof MainContentBlockPluginInterface) { | |
| $block_plugin->setMainContent($this->mainContent); | |
| $main_content_block_displayed = TRUE; | |
| } | |
| elseif ($block_plugin instanceof TitleBlockPluginInterface) { | |
| $block_plugin->setTitle($this->title); | |
| } | |
| elseif ($block_plugin instanceof MessagesBlockPluginInterface) { | |
| $messages_block_displayed = TRUE; | |
| } | |
| $build[$region][$key] = $this->blockViewBuilder->view($block); | |
| // The main content block cannot be cached: it is a placeholder for the | |
| // render array returned by the controller. It should be rendered as-is, | |
| // with other placed blocks "decorating" it. Analogous reasoning for the | |
| // title block. | |
| if ($block_plugin instanceof MainContentBlockPluginInterface || $block_plugin instanceof TitleBlockPluginInterface) { | |
| unset($build[$region][$key]['#cache']['keys']); | |
| } | |
| } | |
| if (!empty($build[$region])) { | |
| // \Drupal\block\BlockRepositoryInterface::getVisibleBlocksPerRegion() | |
| // returns the blocks in sorted order. | |
| $build[$region]['#sorted'] = TRUE; | |
| } | |
| } | |
| // If no block that shows the main content is displayed, still show the main | |
| // content. Otherwise the end user will see all displayed blocks, but not | |
| // the main content they came for. | |
| if (!$main_content_block_displayed) { | |
| $build['content']['system_main'] = $this->mainContent; | |
| } | |
| // If no block displays status messages, still render them. | |
| if (!$messages_block_displayed) { | |
| $build['content']['messages'] = [ | |
| '#weight' => -1000, | |
| '#type' => 'status_messages', | |
| ]; | |
| } | |
| // If any render arrays are manually placed, render arrays and blocks must | |
| // be sorted. | |
| if (!$main_content_block_displayed || !$messages_block_displayed) { | |
| unset($build['content']['#sorted']); | |
| } | |
| // The access results' cacheability is currently added to the top level of the | |
| // render array. This is done to prevent issues with empty regions being | |
| // displayed. | |
| // This would need to be changed to allow caching of block regions, as each | |
| // region must then have the relevant cacheable metadata. | |
| $merged_cacheable_metadata = CacheableMetadata::createFromRenderArray($build); | |
| foreach ($cacheable_metadata_list as $cacheable_metadata) { | |
| $merged_cacheable_metadata = $merged_cacheable_metadata->merge($cacheable_metadata); | |
| } | |
| $merged_cacheable_metadata->applyTo($build); | |
| return $build; | |
| } | |
| } |