Pull merge.
[yaffs-website] / web / core / modules / block / src / Controller / BlockLibraryController.php
1 <?php
2
3 namespace Drupal\block\Controller;
4
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;
12 use Drupal\Core\Url;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14 use Symfony\Component\HttpFoundation\Request;
15
16 /**
17  * Provides a list of block plugins to be added to the layout.
18  */
19 class BlockLibraryController extends ControllerBase {
20
21   /**
22    * The block manager.
23    *
24    * @var \Drupal\Core\Block\BlockManagerInterface
25    */
26   protected $blockManager;
27
28   /**
29    * The context repository.
30    *
31    * @var \Drupal\Core\Plugin\Context\LazyContextRepository
32    */
33   protected $contextRepository;
34
35   /**
36    * The route match.
37    *
38    * @var \Drupal\Core\Routing\RouteMatchInterface
39    */
40   protected $routeMatch;
41
42   /**
43    * The local action manager.
44    *
45    * @var \Drupal\Core\Menu\LocalActionManagerInterface
46    */
47   protected $localActionManager;
48
49   /**
50    * Constructs a BlockLibraryController object.
51    *
52    * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
53    *   The 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.
60    */
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;
66   }
67
68   /**
69    * {@inheritdoc}
70    */
71   public static function create(ContainerInterface $container) {
72     return new static(
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')
77     );
78   }
79
80   /**
81    * Shows a list of blocks that can be added to a theme's layout.
82    *
83    * @param \Symfony\Component\HttpFoundation\Request $request
84    *   The current request.
85    * @param string $theme
86    *   Theme key of the block list.
87    *
88    * @return array
89    *   A render array as expected by the renderer.
90    */
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();
96     }
97
98     $headers = [
99       ['data' => $this->t('Block')],
100       ['data' => $this->t('Category')],
101       ['data' => $this->t('Operations')],
102     ];
103
104     $region = $request->query->get('region');
105     $weight = $request->query->get('weight');
106
107     // Only add blocks which work without any available context.
108     $definitions = $this->blockManager->getFilteredDefinitions('block_ui', $this->contextRepository->getAvailableContexts(), [
109       'theme' => $theme,
110       'region' => $region,
111     ]);
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']);
117     });
118
119     $rows = [];
120     foreach ($definitions as $plugin_id => $plugin_definition) {
121       $row = [];
122       $row['title']['data'] = [
123         '#type' => 'inline_template',
124         '#template' => '<div class="block-filter-text-source">{{ label }}</div>',
125         '#context' => [
126           'label' => $plugin_definition['admin_label'],
127         ],
128       ];
129       $row['category']['data'] = $plugin_definition['category'];
130       $links['add'] = [
131         'title' => $this->t('Place block'),
132         'url' => Url::fromRoute('block.admin_add', ['plugin_id' => $plugin_id, 'theme' => $theme]),
133         'attributes' => [
134           'class' => ['use-ajax'],
135           'data-dialog-type' => 'modal',
136           'data-dialog-options' => Json::encode([
137             'width' => 700,
138           ]),
139         ],
140       ];
141       if ($region) {
142         $links['add']['query']['region'] = $region;
143       }
144       if (isset($weight)) {
145         $links['add']['query']['weight'] = $weight;
146       }
147       $row['operations']['data'] = [
148         '#type' => 'operations',
149         '#links' => $links,
150       ];
151       $rows[] = $row;
152     }
153
154     $build['#attached']['library'][] = 'block/drupal.block.admin';
155
156     $build['filter'] = [
157       '#type' => 'search',
158       '#title' => $this->t('Filter'),
159       '#title_display' => 'invisible',
160       '#size' => 30,
161       '#placeholder' => $this->t('Filter by block name'),
162       '#attributes' => [
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.'),
166       ],
167     ];
168
169     $build['blocks'] = [
170       '#type' => 'table',
171       '#header' => $headers,
172       '#rows' => $rows,
173       '#empty' => $this->t('No blocks available.'),
174       '#attributes' => [
175         'class' => ['block-add-table'],
176       ],
177     ];
178
179     return $build;
180   }
181
182   /**
183    * Builds the local actions for this listing.
184    *
185    * @return array
186    *   An array of local actions for this listing.
187    */
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>';
195     }
196     return $build;
197   }
198
199 }