annotate core/modules/block/src/Plugin/DisplayVariant/BlockPageVariant.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\block\Plugin\DisplayVariant;
Chris@0 4
Chris@0 5 use Drupal\block\BlockRepositoryInterface;
Chris@0 6 use Drupal\Core\Block\MainContentBlockPluginInterface;
Chris@0 7 use Drupal\Core\Block\TitleBlockPluginInterface;
Chris@0 8 use Drupal\Core\Block\MessagesBlockPluginInterface;
Chris@0 9 use Drupal\Core\Cache\CacheableMetadata;
Chris@0 10 use Drupal\Core\Display\PageVariantInterface;
Chris@0 11 use Drupal\Core\Entity\EntityViewBuilderInterface;
Chris@0 12 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
Chris@0 13 use Drupal\Core\Display\VariantBase;
Chris@0 14 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@0 15
Chris@0 16 /**
Chris@0 17 * Provides a page display variant that decorates the main content with blocks.
Chris@0 18 *
Chris@0 19 * To ensure essential information is displayed, each essential part of a page
Chris@0 20 * has a corresponding block plugin interface, so that BlockPageVariant can
Chris@0 21 * automatically provide a fallback in case no block for each of these
Chris@0 22 * interfaces is placed.
Chris@0 23 *
Chris@0 24 * @see \Drupal\Core\Block\MainContentBlockPluginInterface
Chris@0 25 * @see \Drupal\Core\Block\MessagesBlockPluginInterface
Chris@0 26 *
Chris@0 27 * @PageDisplayVariant(
Chris@0 28 * id = "block_page",
Chris@0 29 * admin_label = @Translation("Page with blocks")
Chris@0 30 * )
Chris@0 31 */
Chris@0 32 class BlockPageVariant extends VariantBase implements PageVariantInterface, ContainerFactoryPluginInterface {
Chris@0 33
Chris@0 34 /**
Chris@0 35 * The block repository.
Chris@0 36 *
Chris@0 37 * @var \Drupal\block\BlockRepositoryInterface
Chris@0 38 */
Chris@0 39 protected $blockRepository;
Chris@0 40
Chris@0 41 /**
Chris@0 42 * The block view builder.
Chris@0 43 *
Chris@0 44 * @var \Drupal\Core\Entity\EntityViewBuilderInterface
Chris@0 45 */
Chris@0 46 protected $blockViewBuilder;
Chris@0 47
Chris@0 48 /**
Chris@0 49 * The Block entity type list cache tags.
Chris@0 50 *
Chris@0 51 * @var string[]
Chris@0 52 */
Chris@0 53 protected $blockListCacheTags;
Chris@0 54
Chris@0 55 /**
Chris@0 56 * The render array representing the main page content.
Chris@0 57 *
Chris@0 58 * @var array
Chris@0 59 */
Chris@0 60 protected $mainContent = [];
Chris@0 61
Chris@0 62 /**
Chris@0 63 * The page title: a string (plain title) or a render array (formatted title).
Chris@0 64 *
Chris@0 65 * @var string|array
Chris@0 66 */
Chris@0 67 protected $title = '';
Chris@0 68
Chris@0 69 /**
Chris@0 70 * Constructs a new BlockPageVariant.
Chris@0 71 *
Chris@0 72 * @param array $configuration
Chris@0 73 * A configuration array containing information about the plugin instance.
Chris@0 74 * @param string $plugin_id
Chris@0 75 * The plugin ID for the plugin instance.
Chris@0 76 * @param mixed $plugin_definition
Chris@0 77 * The plugin implementation definition.
Chris@0 78 * @param \Drupal\block\BlockRepositoryInterface $block_repository
Chris@0 79 * The block repository.
Chris@0 80 * @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
Chris@0 81 * The block view builder.
Chris@0 82 * @param string[] $block_list_cache_tags
Chris@0 83 * The Block entity type list cache tags.
Chris@0 84 */
Chris@0 85 public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, array $block_list_cache_tags) {
Chris@0 86 parent::__construct($configuration, $plugin_id, $plugin_definition);
Chris@0 87 $this->blockRepository = $block_repository;
Chris@0 88 $this->blockViewBuilder = $block_view_builder;
Chris@0 89 $this->blockListCacheTags = $block_list_cache_tags;
Chris@0 90 }
Chris@0 91
Chris@0 92 /**
Chris@0 93 * {@inheritdoc}
Chris@0 94 */
Chris@0 95 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
Chris@0 96 return new static(
Chris@0 97 $configuration,
Chris@0 98 $plugin_id,
Chris@0 99 $plugin_definition,
Chris@0 100 $container->get('block.repository'),
Chris@0 101 $container->get('entity.manager')->getViewBuilder('block'),
Chris@0 102 $container->get('entity.manager')->getDefinition('block')->getListCacheTags()
Chris@0 103 );
Chris@0 104 }
Chris@0 105
Chris@0 106 /**
Chris@0 107 * {@inheritdoc}
Chris@0 108 */
Chris@0 109 public function setMainContent(array $main_content) {
Chris@0 110 $this->mainContent = $main_content;
Chris@0 111 return $this;
Chris@0 112 }
Chris@0 113
Chris@0 114 /**
Chris@0 115 * {@inheritdoc}
Chris@0 116 */
Chris@0 117 public function setTitle($title) {
Chris@0 118 $this->title = $title;
Chris@0 119 return $this;
Chris@0 120 }
Chris@0 121
Chris@0 122 /**
Chris@0 123 * {@inheritdoc}
Chris@0 124 */
Chris@0 125 public function build() {
Chris@0 126 // Track whether blocks showing the main content and messages are displayed.
Chris@0 127 $main_content_block_displayed = FALSE;
Chris@0 128 $messages_block_displayed = FALSE;
Chris@0 129
Chris@0 130 $build = [
Chris@0 131 '#cache' => [
Chris@0 132 'tags' => $this->blockListCacheTags,
Chris@0 133 ],
Chris@0 134 ];
Chris@0 135 // Load all region content assigned via blocks.
Chris@0 136 $cacheable_metadata_list = [];
Chris@0 137 foreach ($this->blockRepository->getVisibleBlocksPerRegion($cacheable_metadata_list) as $region => $blocks) {
Chris@0 138 /** @var $blocks \Drupal\block\BlockInterface[] */
Chris@0 139 foreach ($blocks as $key => $block) {
Chris@0 140 $block_plugin = $block->getPlugin();
Chris@0 141 if ($block_plugin instanceof MainContentBlockPluginInterface) {
Chris@0 142 $block_plugin->setMainContent($this->mainContent);
Chris@0 143 $main_content_block_displayed = TRUE;
Chris@0 144 }
Chris@0 145 elseif ($block_plugin instanceof TitleBlockPluginInterface) {
Chris@0 146 $block_plugin->setTitle($this->title);
Chris@0 147 }
Chris@0 148 elseif ($block_plugin instanceof MessagesBlockPluginInterface) {
Chris@0 149 $messages_block_displayed = TRUE;
Chris@0 150 }
Chris@0 151 $build[$region][$key] = $this->blockViewBuilder->view($block);
Chris@0 152
Chris@0 153 // The main content block cannot be cached: it is a placeholder for the
Chris@0 154 // render array returned by the controller. It should be rendered as-is,
Chris@0 155 // with other placed blocks "decorating" it. Analogous reasoning for the
Chris@0 156 // title block.
Chris@0 157 if ($block_plugin instanceof MainContentBlockPluginInterface || $block_plugin instanceof TitleBlockPluginInterface) {
Chris@0 158 unset($build[$region][$key]['#cache']['keys']);
Chris@0 159 }
Chris@0 160 }
Chris@0 161 if (!empty($build[$region])) {
Chris@0 162 // \Drupal\block\BlockRepositoryInterface::getVisibleBlocksPerRegion()
Chris@0 163 // returns the blocks in sorted order.
Chris@0 164 $build[$region]['#sorted'] = TRUE;
Chris@0 165 }
Chris@0 166 }
Chris@0 167
Chris@0 168 // If no block that shows the main content is displayed, still show the main
Chris@0 169 // content. Otherwise the end user will see all displayed blocks, but not
Chris@0 170 // the main content they came for.
Chris@0 171 if (!$main_content_block_displayed) {
Chris@0 172 $build['content']['system_main'] = $this->mainContent;
Chris@0 173 }
Chris@0 174
Chris@0 175 // If no block displays status messages, still render them.
Chris@0 176 if (!$messages_block_displayed) {
Chris@0 177 $build['content']['messages'] = [
Chris@0 178 '#weight' => -1000,
Chris@0 179 '#type' => 'status_messages',
Chris@18 180 '#include_fallback' => TRUE,
Chris@0 181 ];
Chris@0 182 }
Chris@0 183
Chris@0 184 // If any render arrays are manually placed, render arrays and blocks must
Chris@0 185 // be sorted.
Chris@0 186 if (!$main_content_block_displayed || !$messages_block_displayed) {
Chris@0 187 unset($build['content']['#sorted']);
Chris@0 188 }
Chris@0 189
Chris@0 190 // The access results' cacheability is currently added to the top level of the
Chris@0 191 // render array. This is done to prevent issues with empty regions being
Chris@0 192 // displayed.
Chris@0 193 // This would need to be changed to allow caching of block regions, as each
Chris@0 194 // region must then have the relevant cacheable metadata.
Chris@0 195 $merged_cacheable_metadata = CacheableMetadata::createFromRenderArray($build);
Chris@0 196 foreach ($cacheable_metadata_list as $cacheable_metadata) {
Chris@0 197 $merged_cacheable_metadata = $merged_cacheable_metadata->merge($cacheable_metadata);
Chris@0 198 }
Chris@0 199 $merged_cacheable_metadata->applyTo($build);
Chris@0 200
Chris@0 201 return $build;
Chris@0 202 }
Chris@0 203
Chris@0 204 }