3 namespace Drupal\block\Controller;
5 use Drupal\Component\Serialization\Json;
6 use Drupal\Core\Block\BlockManagerInterface;
7 use Drupal\Core\Controller\ControllerBase;
8 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
9 use Drupal\Core\Menu\LocalActionManagerInterface;
10 use Drupal\Core\Plugin\Context\LazyContextRepository;
11 use Drupal\Core\Routing\RouteMatchInterface;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14 use Symfony\Component\HttpFoundation\Request;
17 * Provides a list of block plugins to be added to the layout.
19 class BlockLibraryController extends ControllerBase {
24 * @var \Drupal\Core\Block\BlockManagerInterface
26 protected $blockManager;
29 * The context repository.
31 * @var \Drupal\Core\Plugin\Context\LazyContextRepository
33 protected $contextRepository;
38 * @var \Drupal\Core\Routing\RouteMatchInterface
40 protected $routeMatch;
43 * The local action manager.
45 * @var \Drupal\Core\Menu\LocalActionManagerInterface
47 protected $localActionManager;
50 * Constructs a BlockLibraryController object.
52 * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
54 * @param \Drupal\Core\Plugin\Context\LazyContextRepository $context_repository
55 * The context repository.
56 * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
57 * The current route match.
58 * @param \Drupal\Core\Menu\LocalActionManagerInterface $local_action_manager
59 * The local action manager.
61 public function __construct(BlockManagerInterface $block_manager, LazyContextRepository $context_repository, RouteMatchInterface $route_match, LocalActionManagerInterface $local_action_manager) {
62 $this->blockManager = $block_manager;
63 $this->routeMatch = $route_match;
64 $this->localActionManager = $local_action_manager;
65 $this->contextRepository = $context_repository;
71 public static function create(ContainerInterface $container) {
73 $container->get('plugin.manager.block'),
74 $container->get('context.repository'),
75 $container->get('current_route_match'),
76 $container->get('plugin.manager.menu.local_action')
81 * Shows a list of blocks that can be added to a theme's layout.
83 * @param \Symfony\Component\HttpFoundation\Request $request
84 * The current request.
85 * @param string $theme
86 * Theme key of the block list.
89 * A render array as expected by the renderer.
91 public function listBlocks(Request $request, $theme) {
92 // Since modals do not render any other part of the page, we need to render
93 // them manually as part of this listing.
94 if ($request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_modal') {
95 $build['local_actions'] = $this->buildLocalActions();
99 ['data' => $this->t('Block')],
100 ['data' => $this->t('Category')],
101 ['data' => $this->t('Operations')],
104 $region = $request->query->get('region');
105 $weight = $request->query->get('weight');
107 // Only add blocks which work without any available context.
108 $definitions = $this->blockManager->getFilteredDefinitions('block_ui', $this->contextRepository->getAvailableContexts(), [
112 // Order by category, and then by admin label.
113 $definitions = $this->blockManager->getSortedDefinitions($definitions);
114 // Filter out definitions that are not intended to be placed by the UI.
115 $definitions = array_filter($definitions, function (array $definition) {
116 return empty($definition['_block_ui_hidden']);
120 foreach ($definitions as $plugin_id => $plugin_definition) {
122 $row['title']['data'] = [
123 '#type' => 'inline_template',
124 '#template' => '<div class="block-filter-text-source">{{ label }}</div>',
126 'label' => $plugin_definition['admin_label'],
129 $row['category']['data'] = $plugin_definition['category'];
131 'title' => $this->t('Place block'),
132 'url' => Url::fromRoute('block.admin_add', ['plugin_id' => $plugin_id, 'theme' => $theme]),
134 'class' => ['use-ajax'],
135 'data-dialog-type' => 'modal',
136 'data-dialog-options' => Json::encode([
142 $links['add']['query']['region'] = $region;
144 if (isset($weight)) {
145 $links['add']['query']['weight'] = $weight;
147 $row['operations']['data'] = [
148 '#type' => 'operations',
154 $build['#attached']['library'][] = 'block/drupal.block.admin';
158 '#title' => $this->t('Filter'),
159 '#title_display' => 'invisible',
161 '#placeholder' => $this->t('Filter by block name'),
163 'class' => ['block-filter-text'],
164 'data-element' => '.block-add-table',
165 'title' => $this->t('Enter a part of the block name to filter by.'),
171 '#header' => $headers,
173 '#empty' => $this->t('No blocks available.'),
175 'class' => ['block-add-table'],
183 * Builds the local actions for this listing.
186 * An array of local actions for this listing.
188 protected function buildLocalActions() {
189 $build = $this->localActionManager->getActionsForRoute($this->routeMatch->getRouteName());
190 // Without this workaround, the action links will be rendered as <li> with
191 // no wrapping <ul> element.
192 if (!empty($build)) {
193 $build['#prefix'] = '<ul class="action-links">';
194 $build['#suffix'] = '</ul>';