Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / block / src / BlockViewBuilder.php
1 <?php
2
3 namespace Drupal\block;
4
5 use Drupal\Core\Block\MainContentBlockPluginInterface;
6 use Drupal\Core\Block\TitleBlockPluginInterface;
7 use Drupal\Core\Cache\Cache;
8 use Drupal\Core\Cache\CacheableMetadata;
9 use Drupal\Core\Entity\EntityManagerInterface;
10 use Drupal\Core\Entity\EntityTypeInterface;
11 use Drupal\Core\Entity\EntityViewBuilder;
12 use Drupal\Core\Entity\EntityInterface;
13 use Drupal\Core\Extension\ModuleHandlerInterface;
14 use Drupal\Core\Language\LanguageManagerInterface;
15 use Drupal\Core\Plugin\ContextAwarePluginInterface;
16 use Drupal\Core\Render\Element;
17 use Drupal\block\Entity\Block;
18 use Symfony\Component\DependencyInjection\ContainerInterface;
19
20 /**
21  * Provides a Block view builder.
22  */
23 class BlockViewBuilder extends EntityViewBuilder {
24
25   /**
26    * The module handler.
27    *
28    * @var \Drupal\Core\Extension\ModuleHandlerInterface
29    */
30   protected $moduleHandler;
31
32   /**
33    * Constructs a new BlockViewBuilder.
34    *
35    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
36    *   The entity type definition.
37    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
38    *   The entity manager service.
39    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
40    *   The language manager.
41    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
42    *   The module handler.
43    */
44   public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, ModuleHandlerInterface $module_handler) {
45     parent::__construct($entity_type, $entity_manager, $language_manager);
46     $this->moduleHandler = $module_handler;
47   }
48
49   /**
50    * {@inheritdoc}
51    */
52   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
53     return new static(
54       $entity_type,
55       $container->get('entity.manager'),
56       $container->get('language_manager'),
57       $container->get('module_handler')
58     );
59   }
60
61   /**
62    * {@inheritdoc}
63    */
64   public function buildComponents(array &$build, array $entities, array $displays, $view_mode) {
65   }
66
67   /**
68    * {@inheritdoc}
69    */
70   public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
71     $build = $this->viewMultiple([$entity], $view_mode, $langcode);
72     return reset($build);
73   }
74
75   /**
76    * {@inheritdoc}
77    */
78   public function viewMultiple(array $entities = [], $view_mode = 'full', $langcode = NULL) {
79     /** @var \Drupal\block\BlockInterface[] $entities */
80     $build = [];
81     foreach ($entities as $entity) {
82       $entity_id = $entity->id();
83       $plugin = $entity->getPlugin();
84
85       $cache_tags = Cache::mergeTags($this->getCacheTags(), $entity->getCacheTags());
86       $cache_tags = Cache::mergeTags($cache_tags, $plugin->getCacheTags());
87
88       // Create the render array for the block as a whole.
89       // @see template_preprocess_block().
90       $build[$entity_id] = [
91         '#cache' => [
92           'keys' => ['entity_view', 'block', $entity->id()],
93           'contexts' => Cache::mergeContexts(
94             $entity->getCacheContexts(),
95             $plugin->getCacheContexts()
96           ),
97           'tags' => $cache_tags,
98           'max-age' => $plugin->getCacheMaxAge(),
99         ],
100         '#weight' => $entity->getWeight(),
101       ];
102
103       // Allow altering of cacheability metadata or setting #create_placeholder.
104       $this->moduleHandler->alter(['block_build', "block_build_" . $plugin->getBaseId()], $build[$entity_id], $plugin);
105
106       if ($plugin instanceof MainContentBlockPluginInterface || $plugin instanceof TitleBlockPluginInterface) {
107         // Immediately build a #pre_render-able block, since this block cannot
108         // be built lazily.
109         $build[$entity_id] += static::buildPreRenderableBlock($entity, $this->moduleHandler());
110       }
111       else {
112         // Assign a #lazy_builder callback, which will generate a #pre_render-
113         // able block lazily (when necessary).
114         $build[$entity_id] += [
115           '#lazy_builder' => [static::class . '::lazyBuilder', [$entity_id, $view_mode, $langcode]],
116         ];
117       }
118     }
119
120     return $build;
121   }
122
123   /**
124    * Builds a #pre_render-able block render array.
125    *
126    * @param \Drupal\block\BlockInterface $entity
127    *   A block config entity.
128    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
129    *   The module handler service.
130    *
131    * @return array
132    *   A render array with a #pre_render callback to render the block.
133    */
134   protected static function buildPreRenderableBlock($entity, ModuleHandlerInterface $module_handler) {
135     $plugin = $entity->getPlugin();
136     $plugin_id = $plugin->getPluginId();
137     $base_id = $plugin->getBaseId();
138     $derivative_id = $plugin->getDerivativeId();
139     $configuration = $plugin->getConfiguration();
140
141     // Inject runtime contexts.
142     if ($plugin instanceof ContextAwarePluginInterface) {
143       $contexts = \Drupal::service('context.repository')->getRuntimeContexts($plugin->getContextMapping());
144       \Drupal::service('context.handler')->applyContextMapping($plugin, $contexts);
145     }
146
147     // Create the render array for the block as a whole.
148     // @see template_preprocess_block().
149     $build = [
150       '#theme' => 'block',
151       '#attributes' => [],
152       // All blocks get a "Configure block" contextual link.
153       '#contextual_links' => [
154         'block' => [
155           'route_parameters' => ['block' => $entity->id()],
156         ],
157       ],
158       '#weight' => $entity->getWeight(),
159       '#configuration' => $configuration,
160       '#plugin_id' => $plugin_id,
161       '#base_plugin_id' => $base_id,
162       '#derivative_plugin_id' => $derivative_id,
163       '#id' => $entity->id(),
164       '#pre_render' => [
165         static::class . '::preRender',
166       ],
167       // Add the entity so that it can be used in the #pre_render method.
168       '#block' => $entity,
169     ];
170
171     // If an alter hook wants to modify the block contents, it can append
172     // another #pre_render hook.
173     $module_handler->alter(['block_view', "block_view_$base_id"], $build, $plugin);
174
175     return $build;
176   }
177
178   /**
179    * #lazy_builder callback; builds a #pre_render-able block.
180    *
181    * @param $entity_id
182    *   A block config entity ID.
183    * @param $view_mode
184    *   The view mode the block is being viewed in.
185    *
186    * @return array
187    *   A render array with a #pre_render callback to render the block.
188    */
189   public static function lazyBuilder($entity_id, $view_mode) {
190     return static::buildPreRenderableBlock(Block::load($entity_id), \Drupal::service('module_handler'));
191   }
192
193   /**
194    * #pre_render callback for building a block.
195    *
196    * Renders the content using the provided block plugin, and then:
197    * - if there is no content, aborts rendering, and makes sure the block won't
198    *   be rendered.
199    * - if there is content, moves the contextual links from the block content to
200    *   the block itself.
201    */
202   public static function preRender($build) {
203     $content = $build['#block']->getPlugin()->build();
204     // Remove the block entity from the render array, to ensure that blocks
205     // can be rendered without the block config entity.
206     unset($build['#block']);
207     if ($content !== NULL && !Element::isEmpty($content)) {
208       // Place the $content returned by the block plugin into a 'content' child
209       // element, as a way to allow the plugin to have complete control of its
210       // properties and rendering (for instance, its own #theme) without
211       // conflicting with the properties used above, or alternate ones used by
212       // alternate block rendering approaches in contrib (for instance, Panels).
213       // However, the use of a child element is an implementation detail of this
214       // particular block rendering approach. Semantically, the content returned
215       // by the plugin "is the" block, and in particular, #attributes and
216       // #contextual_links is information about the *entire* block. Therefore,
217       // we must move these properties from $content and merge them into the
218       // top-level element.
219       foreach (['#attributes', '#contextual_links'] as $property) {
220         if (isset($content[$property])) {
221           $build[$property] += $content[$property];
222           unset($content[$property]);
223         }
224       }
225       $build['content'] = $content;
226     }
227     // Either the block's content is completely empty, or it consists only of
228     // cacheability metadata.
229     else {
230       // Abort rendering: render as the empty string and ensure this block is
231       // render cached, so we can avoid the work of having to repeatedly
232       // determine whether the block is empty. For instance, modifying or adding
233       // entities could cause the block to no longer be empty.
234       $build = [
235         '#markup' => '',
236         '#cache' => $build['#cache'],
237       ];
238       // If $content is not empty, then it contains cacheability metadata, and
239       // we must merge it with the existing cacheability metadata. This allows
240       // blocks to be empty, yet still bubble cacheability metadata, to indicate
241       // why they are empty.
242       if (!empty($content)) {
243         CacheableMetadata::createFromRenderArray($build)
244           ->merge(CacheableMetadata::createFromRenderArray($content))
245           ->applyTo($build);
246       }
247     }
248     return $build;
249   }
250
251 }