class Blocks in Context 8
Same name and namespace in other branches
- 8.4 src/Plugin/ContextReaction/Blocks.php \Drupal\context\Plugin\ContextReaction\Blocks
- 8.0 src/Plugin/ContextReaction/Blocks.php \Drupal\context\Plugin\ContextReaction\Blocks
Provides a content reaction that will let you place blocks in the current themes regions.
Plugin annotation
@ContextReaction(
id = "blocks",
label = @Translation("Blocks")
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\context\ContextReactionPluginBase implements ContextReactionInterface
- class \Drupal\context\Plugin\ContextReaction\Blocks implements ContainerFactoryPluginInterface uses AjaxFormTrait
- class \Drupal\context\ContextReactionPluginBase implements ContextReactionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of Blocks
4 files declare their use of Blocks
- BlockDeleteForm.php in src/
Reaction/ Blocks/ Form/ BlockDeleteForm.php - BlockPageDisplayVariantSubscriber.php in src/
EventSubscriber/ BlockPageDisplayVariantSubscriber.php - Contains \Drupal\context\EventSubscriber\BlockPageDisplayVariantSubscriber.
- context.install in ./
context.install - Install, update and uninstall functions for the context module.
- ContextManager.php in src/
ContextManager.php
File
- src/
Plugin/ ContextReaction/ Blocks.php, line 36
Namespace
Drupal\context\Plugin\ContextReactionView source
class Blocks extends ContextReactionPluginBase implements ContainerFactoryPluginInterface {
use AjaxFormTrait;
/**
* An array of blocks to be displayed with this reaction.
*
* @var array
*/
protected $blocks = [];
/**
* Contains a temporary collection of blocks.
*
* @var BlockCollection
*/
protected $blocksCollection;
/**
* The Drupal UUID service.
*
* @var \Drupal\Component\Uuid\UuidInterface
*/
protected $uuid;
/**
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* @var ContextRepositoryInterface
*/
protected $contextRepository;
/**
* @var ContextHandlerInterface
*/
protected $contextHandler;
/**
* @var AccountInterface
*/
protected $account;
/**
* {@inheritdoc}
*/
function __construct(array $configuration, $pluginId, $pluginDefinition, UuidInterface $uuid, ThemeManagerInterface $themeManager, ThemeHandlerInterface $themeHandler, ContextRepositoryInterface $contextRepository, ContextHandlerInterface $contextHandler, AccountInterface $account) {
parent::__construct($configuration, $pluginId, $pluginDefinition);
$this->uuid = $uuid;
$this->themeManager = $themeManager;
$this->themeHandler = $themeHandler;
$this->contextRepository = $contextRepository;
$this->contextHandler = $contextHandler;
$this->account = $account;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) {
return new static($configuration, $pluginId, $pluginDefinition, $container
->get('uuid'), $container
->get('theme.manager'), $container
->get('theme_handler'), $container
->get('context.repository'), $container
->get('context.handler'), $container
->get('current_user'));
}
/**
* Executes the plugin.
*
* @param array $build
* The current build of the page.
*
* @param string|null $title
* The page title.
*
* @param string|null $main_content
* The main page content.
*
* @return array
*/
public function execute(array $build = array(), $title = NULL, $main_content = NULL) {
$cacheability = CacheableMetadata::createFromRenderArray($build);
// Use the currently active theme to fetch blocks.
$theme = $this->themeManager
->getActiveTheme()
->getName();
$regions = $this
->getBlocks()
->getAllByRegion($theme);
// Add each block to the page build.
foreach ($regions as $region => $blocks) {
/** @var $blocks BlockPluginInterface[] */
foreach ($blocks as $block_id => $block) {
$configuration = $block
->getConfiguration();
$block_placement_key = $this
->blockShouldBePlacedUniquely($block) ? $block_id : $block
->getConfiguration()['id'];
if ($block instanceof MainContentBlockPluginInterface) {
if (isset($build['content']['system_main'])) {
unset($build['content']['system_main']);
}
$block
->setMainContent($main_content);
}
// Make sure the user is allowed to view the block.
$access = $block
->access($this->account, TRUE);
$cacheability
->addCacheableDependency($access);
// If the user is not allowed then do not render the block.
if (!$access
->isAllowed()) {
continue;
}
if ($block instanceof TitleBlockPluginInterface) {
if (isset($build['content']['messages'])) {
unset($build['content']['messages']);
}
$block
->setTitle($title);
}
// Inject runtime contexts.
if ($block instanceof ContextAwarePluginInterface) {
$contexts = $this->contextRepository
->getRuntimeContexts($block
->getContextMapping());
$this->contextHandler
->applyContextMapping($block, $contexts);
}
// Create the render array for the block as a whole.
// @see template_preprocess_block().
$blockBuild = [
'#theme' => 'block',
'#attributes' => [
'class' => [
$configuration['css_class'],
],
],
'#configuration' => $configuration,
'#plugin_id' => $block
->getPluginId(),
'#base_plugin_id' => $block
->getBaseId(),
'#derivative_plugin_id' => $block
->getDerivativeId(),
'#id' => $block
->getConfiguration()['custom_id'],
'#block_plugin' => $block,
'#pre_render' => [
[
$this,
'preRenderBlock',
],
],
'#cache' => [
'keys' => [
'context_blocks_reaction',
'block',
$block_placement_key,
],
'tags' => $block
->getCacheTags(),
'contexts' => $block
->getCacheContexts(),
'max-age' => $block
->getCacheMaxAge(),
],
];
// Add contextual links to block.
$content = $block
->build();
if (isset($content['#contextual_links'])) {
$blockBuild['#contextual_links'] = $content['#contextual_links'];
}
// Add additional contextual link, for editing block configuration.
$blockBuild['#contextual_links']['context_block'] = [
'route_parameters' => [
'context' => $configuration['context_id'],
'reaction_id' => 'blocks',
'block_id' => $block
->getConfiguration()['uuid'],
],
];
if (array_key_exists('weight', $configuration)) {
$blockBuild['#weight'] = $configuration['weight'];
}
// Invoke block_view_alter().
// If an alter hook wants to modify the block contents, it can append
// another #pre_render hook.
\Drupal::moduleHandler()
->alter([
'block_view',
'block_view_' . $block
->getBaseId(),
], $blockBuild, $block);
$build[$region][$block_placement_key] = $blockBuild;
// After merging with blocks from Block layout, we want to sort all of
// them again.
$build[$region]['#sorted'] = FALSE;
// 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 instanceof MainContentBlockPluginInterface || $block instanceof TitleBlockPluginInterface) {
unset($build[$region][$block_placement_key]['#cache']['keys']);
}
$cacheability
->addCacheableDependency($block);
}
}
$cacheability
->applyTo($build);
return $build;
}
/**
* Renders the content using the provided block plugin.
*
* @param array $build
* @return array
*/
public function preRenderBlock($build) {
$content = $build['#block_plugin']
->build();
unset($build['#block_plugin']);
// 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.
if (is_null($content) || Element::isEmpty($content)) {
$build = [
'#markup' => '',
'#cache' => $build['#cache'],
];
// If $content is not empty, then it contains cacheability metadata, and
// we must merge it with the existing cacheability metadata. This allows
// blocks to be empty, yet still bubble cacheability metadata, to indicate
// why they are empty.
if (!empty($content)) {
CacheableMetadata::createFromRenderArray($build)
->merge(CacheableMetadata::createFromRenderArray($content))
->applyTo($build);
}
}
else {
$build['content'] = $content;
}
return $build;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'blocks' => [],
] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration) {
$this->configuration = $configuration + $this
->defaultConfiguration();
if (isset($configuration['blocks'])) {
$this->blocks = $configuration['blocks'];
}
return $this;
}
/**
* {@inheritdoc}
*/
public function getConfiguration() {
return [
'blocks' => $this
->getBlocks()
->getConfiguration(),
] + parent::getConfiguration();
}
/**
* {@inheritdoc}
*/
public function summary() {
return $this
->t('Lets you add blocks to the selected themes regions');
}
/**
* Get all blocks as a collection.
*
* @return BlockPluginInterface[]|BlockCollection
*/
public function getBlocks() {
if (!$this->blocksCollection) {
$blockManager = \Drupal::service('plugin.manager.block');
$this->blocksCollection = new BlockCollection($blockManager, $this->blocks);
}
return $this->blocksCollection;
}
/**
* Get a block by id.
*
* @param string $blockId
* The ID of the block to get.
*
* @return BlockPluginInterface
*/
public function getBlock($blockId) {
return $this
->getBlocks()
->get($blockId);
}
/**
* Add a new block.
*
* @param array $configuration
*/
public function addBlock(array $configuration) {
$configuration['uuid'] = $this->uuid
->generate();
$this
->getBlocks()
->addInstanceId($configuration['uuid'], $configuration);
return $configuration['uuid'];
}
/**
* Update an existing blocks configuration.
*
* @param string $blockId
* The ID of the block to update.
*
* @param $configuration
* The updated configuration for the block.
*
* @return $this
*/
public function updateBlock($blockId, array $configuration) {
$existingConfiguration = $this
->getBlock($blockId)
->getConfiguration();
$this
->getBlocks()
->setInstanceConfiguration($blockId, $configuration + $existingConfiguration);
return $this;
}
/**
* @param $blockId
* @return $this
*/
public function removeBlock($blockId) {
$this
->getBlocks()
->removeInstanceId($blockId);
return $this;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state, ContextInterface $context = NULL) {
$form['#attached']['library'][] = 'block/drupal.block';
$themes = $this->themeHandler
->listInfo();
$default_theme = $this->themeHandler
->getDefault();
// Select list for changing themes.
$form['theme'] = [
'#type' => 'select',
'#title' => $this
->t('Theme'),
'#options' => [],
'#description' => $this
->t('Select the theme you want to display regions for.'),
'#default_value' => $form_state
->getValue('theme', $default_theme),
'#ajax' => [
'url' => Url::fromRoute('context.reaction.blocks.regions', [
'context' => $context
->id(),
]),
],
];
// Add each theme to the theme select.
foreach ($themes as $theme_id => $theme) {
if ($theme_id === $default_theme) {
$form['theme']['#options'][$theme_id] = $this
->t('%theme (Default)', [
'%theme' => $theme->info['name'],
]);
}
else {
$form['theme']['#options'][$theme_id] = $theme->info['name'];
}
}
$form['blocks'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'context-reaction-blocks-container',
],
];
$form['blocks']['include_default_blocks'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Include blocks from Block layout'),
'#description' => $this
->t('if checked, all blocks from default Block layout will also be included in page build.'),
'#weight' => -10,
'#default_value' => isset($this
->getConfiguration()['include_default_blocks']) ? $this
->getConfiguration()['include_default_blocks'] : FALSE,
];
$form['blocks']['block_add'] = [
'#type' => 'link',
'#title' => $this
->t('Place block'),
'#attributes' => [
'id' => 'context-reaction-blocks-region-add',
] + $this
->getAjaxButtonAttributes(),
'#url' => Url::fromRoute('context.reaction.blocks.library', [
'context' => $context
->id(),
'reaction_id' => $this
->getPluginId(),
], [
'query' => [
'theme' => $form_state
->getValue('theme', $default_theme),
],
]),
];
$form['blocks']['blocks'] = [
'#type' => 'table',
'#header' => [
$this
->t('Block'),
$this
->t('Category'),
$this
->t('Unique'),
$this
->t('Region'),
$this
->t('Weight'),
$this
->t('Operations'),
],
'#empty' => $this
->t('No regions available to place blocks in.'),
'#attributes' => [
'id' => 'blocks',
],
];
// If a theme has been selected use that to get the regions otherwise use
// the default theme.
$theme = $form_state
->getValue('theme', $default_theme);
// Get all blocks by their regions.
$blocks = $this
->getBlocks()
->getAllByRegion($theme);
// Get regions of the selected theme.
$regions = $this
->getSystemRegionList($theme);
// Add each region.
foreach ($regions as $region => $title) {
// Add the tabledrag details for this region.
$form['blocks']['blocks']['#tabledrag'][] = [
'action' => 'match',
'relationship' => 'sibling',
'group' => 'block-region-select',
'subgroup' => 'block-region-' . $region,
'hidden' => FALSE,
];
$form['blocks']['blocks']['#tabledrag'][] = [
'action' => 'order',
'relationship' => 'sibling',
'group' => 'block-weight',
'subgroup' => 'block-weight-' . $region,
];
// Add the theme region.
$form['blocks']['blocks']['region-' . $region] = [
'#attributes' => [
'class' => [
'region-title',
],
],
'title' => [
'#markup' => $title,
'#wrapper_attributes' => [
'colspan' => 6,
],
],
];
$regionEmptyClass = empty($blocks[$region]) ? 'region-empty' : 'region-populated';
$form['blocks']['blocks']['region-' . $region . '-message'] = [
'#attributes' => [
'class' => [
'region-message',
'region-' . $region . '-message',
$regionEmptyClass,
],
],
'message' => [
'#markup' => '<em>' . $this
->t('No blocks in this region') . '</em>',
'#wrapper_attributes' => [
'colspan' => 6,
],
],
];
// Add each block specified for the region if there are any.
if (isset($blocks[$region])) {
/** @var BlockPluginInterface $block */
foreach ($blocks[$region] as $block_id => $block) {
$configuration = $block
->getConfiguration();
$operations = [
'edit' => [
'title' => $this
->t('Edit'),
'url' => Url::fromRoute('context.reaction.blocks.block_edit', [
'context' => $context
->id(),
'reaction_id' => $this
->getPluginId(),
'block_id' => $block_id,
], [
'query' => [
'theme' => $theme,
],
]),
'attributes' => $this
->getAjaxAttributes(),
],
'delete' => [
'title' => $this
->t('Delete'),
'url' => Url::fromRoute('context.reaction.blocks.block_delete', [
'context' => $context
->id(),
'block_id' => $block_id,
]),
'attributes' => $this
->getAjaxAttributes(),
],
];
$form['blocks']['blocks'][$block_id] = [
'#attributes' => [
'class' => [
'draggable',
],
],
'label' => [
'#markup' => $block
->label(),
],
'category' => [
'#markup' => $block
->getPluginDefinition()['category'],
],
'unique' => [
'#markup' => $this
->blockShouldBePlacedUniquely($block) ? $this
->t('Yes') : $this
->t('No'),
],
'region' => [
'#type' => 'select',
'#title' => $this
->t('Region for @block block', [
'@block' => $block
->label(),
]),
'#title_display' => 'invisible',
'#default_value' => $region,
'#options' => $regions,
'#attributes' => [
'class' => [
'block-region-select',
'block-region-' . $region,
],
],
],
'weight' => [
'#type' => 'weight',
'#default_value' => isset($configuration['weight']) ? $configuration['weight'] : 0,
'#title' => $this
->t('Weight for @block block', [
'@block' => $block
->label(),
]),
'#title_display' => 'invisible',
'#attributes' => [
'class' => [
'block-weight',
'block-weight-' . $region,
],
],
],
'operations' => [
'#type' => 'operations',
'#links' => $operations,
],
];
}
}
}
return $form;
}
/**
* Check to see if the block should be uniquely placed.
*
* @param BlockPluginInterface $block
*
* @return bool
*/
private function blockShouldBePlacedUniquely(BlockPluginInterface $block) {
$configuration = $block
->getConfiguration();
return isset($configuration['unique']) && $configuration['unique'];
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$blocks = $form_state
->getValue([
'blocks',
'blocks',
], []);
// Save configuration for including default blocks.
$config = $this
->getConfiguration();
$config['include_default_blocks'] = $form_state
->getValue([
'blocks',
'include_default_blocks',
], FALSE);
$this
->setConfiguration($config);
if (is_array($blocks)) {
foreach ($blocks as $block_id => $configuration) {
$block = $this
->getBlock($block_id);
$configuration += $block
->getConfiguration();
$block_state = (new FormState())
->setValues($configuration);
$block
->submitConfigurationForm($form, $block_state);
// If the block is context aware then add context mapping to the block.
if ($block instanceof ContextAwarePluginInterface) {
$block
->setContextMapping($block_state
->getValue('context_mapping', []));
}
$this
->updateBlock($block_id, $block_state
->getValues());
}
}
}
/**
* Should reaction include default blocks from Block layout.
*
* @return bool
*/
public function includeDefaultBlocks() {
$config = $this
->getConfiguration();
return isset($config['include_default_blocks']) ? $config['include_default_blocks'] : FALSE;
}
/**
* Wraps system_region_list().
*
* @param string $theme
* The theme to get a list of regions for.
*
* @param string $show
* What type of regions that should be returned, defaults to all regions.
*
* @return array
*
* @todo This could be moved to a service since we use it in a couple of places.
*/
protected function getSystemRegionList($theme, $show = REGIONS_ALL) {
return system_region_list($theme, $show);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
AjaxFormTrait:: |
public static | function | Gets attributes for use with an AJAX modal. | |
AjaxFormTrait:: |
public static | function | Gets attributes for use with an add button AJAX modal. | |
Blocks:: |
protected | property | ||
Blocks:: |
protected | property | An array of blocks to be displayed with this reaction. | |
Blocks:: |
protected | property | Contains a temporary collection of blocks. | |
Blocks:: |
protected | property | ||
Blocks:: |
protected | property | ||
Blocks:: |
protected | property | ||
Blocks:: |
protected | property | ||
Blocks:: |
protected | property | The Drupal UUID service. | |
Blocks:: |
public | function | Add a new block. | |
Blocks:: |
private | function | Check to see if the block should be uniquely placed. | |
Blocks:: |
public | function |
Form constructor. Overrides PluginFormInterface:: |
|
Blocks:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
|
Blocks:: |
public | function |
Gets default configuration for this plugin. Overrides ContextReactionPluginBase:: |
|
Blocks:: |
public | function |
Executes the plugin. Overrides ExecutableInterface:: |
|
Blocks:: |
public | function | Get a block by id. | |
Blocks:: |
public | function | Get all blocks as a collection. | |
Blocks:: |
public | function |
Gets this plugin's configuration. Overrides ContextReactionPluginBase:: |
|
Blocks:: |
protected | function | Wraps system_region_list(). | |
Blocks:: |
public | function | Should reaction include default blocks from Block layout. | |
Blocks:: |
public | function | Renders the content using the provided block plugin. | |
Blocks:: |
public | function | ||
Blocks:: |
public | function |
Sets the configuration for this plugin instance. Overrides ContextReactionPluginBase:: |
|
Blocks:: |
public | function |
Form submission handler. Overrides PluginFormInterface:: |
|
Blocks:: |
public | function |
Provides a human readable summary of the condition's configuration. Overrides ContextReactionInterface:: |
|
Blocks:: |
public | function | Update an existing blocks configuration. | |
Blocks:: |
function |
Constructs a \Drupal\Component\Plugin\PluginBase object. Overrides ContextReactionPluginBase:: |
||
ContextReactionPluginBase:: |
public | function |
Calculates dependencies for the configured plugin. Overrides DependentPluginInterface:: |
|
ContextReactionPluginBase:: |
public | function |
Get the unique ID of this context reaction. Overrides ContextReactionInterface:: |
|
ContextReactionPluginBase:: |
public | function |
Form validation handler is optional. Overrides PluginFormInterface:: |
|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |