View source
<?php
namespace Drupal\context\Plugin\ContextReaction;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\Core\Form\FormState;
use Drupal\Core\Render\Element;
use Drupal\context\ContextInterface;
use Drupal\context\Form\AjaxFormTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\context\ContextReactionPluginBase;
use Drupal\Core\Block\TitleBlockPluginInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\context\Reaction\Blocks\BlockCollection;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Block\MainContentBlockPluginInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class Blocks extends ContextReactionPluginBase implements ContainerFactoryPluginInterface {
use AjaxFormTrait;
protected $blocks = [];
protected $blocksCollection;
protected $uuid;
protected $themeManager;
protected $themeHandler;
protected $contextRepository;
protected $contextHandler;
protected $account;
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;
}
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'));
}
public function execute(array $build = array(), $title = NULL, $main_content = NULL) {
$cacheability = CacheableMetadata::createFromRenderArray($build);
$theme = $this->themeManager
->getActiveTheme()
->getName();
$regions = $this
->getBlocks()
->getAllByRegion($theme);
foreach ($regions as $region => $blocks) {
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);
}
$access = $block
->access($this->account, TRUE);
$cacheability
->addCacheableDependency($access);
if (!$access
->isAllowed()) {
continue;
}
if ($block instanceof TitleBlockPluginInterface) {
if (isset($build['content']['messages'])) {
unset($build['content']['messages']);
}
$block
->setTitle($title);
}
if ($block instanceof ContextAwarePluginInterface) {
$contexts = $this->contextRepository
->getRuntimeContexts($block
->getContextMapping());
$this->contextHandler
->applyContextMapping($block, $contexts);
}
$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(),
],
];
$content = $block
->build();
if (isset($content['#contextual_links'])) {
$blockBuild['#contextual_links'] = $content['#contextual_links'];
}
$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'];
}
\Drupal::moduleHandler()
->alter([
'block_view',
'block_view_' . $block
->getBaseId(),
], $blockBuild, $block);
$build[$region][$block_placement_key] = $blockBuild;
$build[$region]['#sorted'] = FALSE;
if ($block instanceof MainContentBlockPluginInterface || $block instanceof TitleBlockPluginInterface) {
unset($build[$region][$block_placement_key]['#cache']['keys']);
}
$cacheability
->addCacheableDependency($block);
}
}
$cacheability
->applyTo($build);
return $build;
}
public function preRenderBlock($build) {
$content = $build['#block_plugin']
->build();
unset($build['#block_plugin']);
if (is_null($content) || Element::isEmpty($content)) {
$build = [
'#markup' => '',
'#cache' => $build['#cache'],
];
if (!empty($content)) {
CacheableMetadata::createFromRenderArray($build)
->merge(CacheableMetadata::createFromRenderArray($content))
->applyTo($build);
}
}
else {
$build['content'] = $content;
}
return $build;
}
public function defaultConfiguration() {
return [
'blocks' => [],
] + parent::defaultConfiguration();
}
public function setConfiguration(array $configuration) {
$this->configuration = $configuration + $this
->defaultConfiguration();
if (isset($configuration['blocks'])) {
$this->blocks = $configuration['blocks'];
}
return $this;
}
public function getConfiguration() {
return [
'blocks' => $this
->getBlocks()
->getConfiguration(),
] + parent::getConfiguration();
}
public function summary() {
return $this
->t('Lets you add blocks to the selected themes regions');
}
public function getBlocks() {
if (!$this->blocksCollection) {
$blockManager = \Drupal::service('plugin.manager.block');
$this->blocksCollection = new BlockCollection($blockManager, $this->blocks);
}
return $this->blocksCollection;
}
public function getBlock($blockId) {
return $this
->getBlocks()
->get($blockId);
}
public function addBlock(array $configuration) {
$configuration['uuid'] = $this->uuid
->generate();
$this
->getBlocks()
->addInstanceId($configuration['uuid'], $configuration);
return $configuration['uuid'];
}
public function updateBlock($blockId, array $configuration) {
$existingConfiguration = $this
->getBlock($blockId)
->getConfiguration();
$this
->getBlocks()
->setInstanceConfiguration($blockId, $configuration + $existingConfiguration);
return $this;
}
public function removeBlock($blockId) {
$this
->getBlocks()
->removeInstanceId($blockId);
return $this;
}
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();
$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(),
]),
],
];
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',
],
];
$theme = $form_state
->getValue('theme', $default_theme);
$blocks = $this
->getBlocks()
->getAllByRegion($theme);
$regions = $this
->getSystemRegionList($theme);
foreach ($regions as $region => $title) {
$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,
];
$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,
],
],
];
if (isset($blocks[$region])) {
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;
}
private function blockShouldBePlacedUniquely(BlockPluginInterface $block) {
$configuration = $block
->getConfiguration();
return isset($configuration['unique']) && $configuration['unique'];
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$blocks = $form_state
->getValue([
'blocks',
'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 ($block instanceof ContextAwarePluginInterface) {
$block
->setContextMapping($block_state
->getValue('context_mapping', []));
}
$this
->updateBlock($block_id, $block_state
->getValues());
}
}
}
public function includeDefaultBlocks() {
$config = $this
->getConfiguration();
return isset($config['include_default_blocks']) ? $config['include_default_blocks'] : FALSE;
}
protected function getSystemRegionList($theme, $show = REGIONS_ALL) {
return system_region_list($theme, $show);
}
}