Newer
Older
Angie Byron
committed
<?php
/**
* @file
Alex Pott
committed
* Contains \Drupal\block\BlockViewBuilder.
Angie Byron
committed
*/
namespace Drupal\block;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityViewBuilder;
Alex Pott
committed
use Drupal\Core\Entity\EntityViewBuilderInterface;
Angie Byron
committed
use Drupal\Core\Entity\EntityInterface;
/**
Alex Pott
committed
* Provides a Block view builder.
Angie Byron
committed
*/
class BlockViewBuilder extends EntityViewBuilder {
Angie Byron
committed
/**
Alex Pott
committed
* {@inheritdoc}
Angie Byron
committed
*/
public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) {
Angie Byron
committed
}
/**
Alex Pott
committed
* {@inheritdoc}
Angie Byron
committed
*/
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
$build = $this->viewMultiple(array($entity), $view_mode, $langcode);
return reset($build);
}
/**
Alex Pott
committed
* {@inheritdoc}
Angie Byron
committed
*/
public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
$build = array();
foreach ($entities as $key => $entity) {
$entity_id = $entity->id();
Alex Pott
committed
$plugin = $entity->getPlugin();
$plugin_id = $plugin->getPluginId();
$base_id = $plugin->getBasePluginId();
$derivative_id = $plugin->getDerivativeId();
$configuration = $plugin->getConfiguration();
Alex Pott
committed
// Create the render array for the block as a whole.
// @see template_preprocess_block().
$build[$entity_id] = array(
'#theme' => 'block',
'#attributes' => array(),
// All blocks get a "Configure block" contextual link.
'#contextual_links' => array(
'block' => array(
'route_parameters' => array('block' => $entity->id()),
),
),
'#weight' => $entity->get('weight'),
'#configuration' => $configuration,
'#plugin_id' => $plugin_id,
'#base_plugin_id' => $base_id,
'#derivative_plugin_id' => $derivative_id,
// @todo Remove after fixing http://drupal.org/node/1989568.
'#block' => $entity,
);
$build[$entity_id]['#configuration']['label'] = check_plain($configuration['label']);
// Set cache tags; these always need to be set, whether the block is
// cacheable or not, so that the page cache is correctly informed.
Alex Pott
committed
$build[$entity_id]['#cache']['tags'] = NestedArray::mergeDeepArray(array(
array('content' => TRUE),
$this->getCacheTag(), // Block view builder cache tag.
$entity->getCacheTag(), // Block entity cache tag.
$entity->getListCacheTags(), // Block entity list cache tags.
$plugin->getCacheTags(), // Block plugin cache tags.
));
if ($plugin->isCacheable()) {
$build[$entity_id]['#pre_render'][] = array($this, 'buildBlock');
// Generic cache keys, with the block plugin's custom keys appended
// (usually cache context keys like 'cache_context.user.roles').
$default_cache_keys = array(
'entity_view',
'block',
$entity->id(),
$entity->langcode,
// Blocks are always rendered in a "per theme" cache context.
'cache_context.theme',
);
$max_age = $plugin->getCacheMaxAge();
$build[$entity_id]['#cache'] += array(
'keys' => array_merge($default_cache_keys, $plugin->getCacheKeys()),
'bin' => $plugin->getCacheBin(),
'expire' => ($max_age === Cache::PERMANENT) ? Cache::PERMANENT : REQUEST_TIME + $max_age,
Alex Pott
committed
);
}
else {
$build[$entity_id] = $this->buildBlock($build[$entity_id]);
Alex Pott
committed
}
// @todo Remove after fixing http://drupal.org/node/1989568.
$build[$key]['#block'] = $entity;
// Don't run in ::buildBlock() to ensure cache keys can be altered. If an
// alter hook wants to modify the block contents, it can append another
// #pre_render hook.
$this->moduleHandler()->alter(array('block_view', "block_view_$base_id"), $build[$entity_id], $plugin);
Angie Byron
committed
}
return $build;
}
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* #pre_render callback for building a block.
*
* Renders the content using the provided block plugin, and then:
* - if there is no content, aborts rendering, and makes sure the block won't
* be rendered.
* - if there is content, moves the contextual links from the block content to
* the block itself.
*/
public function buildBlock($build) {
$content = $build['#block']->getPlugin()->build();
if (!empty($content)) {
// Place the $content returned by the block plugin into a 'content' child
// element, as a way to allow the plugin to have complete control of its
// properties and rendering (e.g., its own #theme) without conflicting
// with the properties used above, or alternate ones used by alternate
// block rendering approaches in contrib (e.g., Panels). However, the use
// of a child element is an implementation detail of this particular block
// rendering approach. Semantically, the content returned by the plugin
// "is the" block, and in particular, #attributes and #contextual_links is
// information about the *entire* block. Therefore, we must move these
// properties from $content and merge them into the top-level element.
foreach (array('#attributes', '#contextual_links') as $property) {
if (isset($content[$property])) {
$build[$property] += $content[$property];
unset($content[$property]);
}
}
$build['content'] = $content;
}
else {
// Abort rendering: render as the empty string and ensure this block is
// render cached, so we can avoid the work of having to repeatedly
// determine whether the block is empty. E.g. modifying or adding entities
// could cause the block to no longer be empty.
$build = array(
'#markup' => '',
'#cache' => $build['#cache'],
);
}
return $build;
}