Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\block;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Component\Utility\Html;
|
Chris@0
|
6 use Drupal\Component\Serialization\Json;
|
Chris@0
|
7 use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
|
Chris@0
|
8 use Drupal\Core\Entity\EntityInterface;
|
Chris@0
|
9 use Drupal\Core\Entity\EntityStorageInterface;
|
Chris@0
|
10 use Drupal\Core\Entity\EntityTypeInterface;
|
Chris@0
|
11 use Drupal\Core\Form\FormBuilderInterface;
|
Chris@0
|
12 use Drupal\Core\Form\FormInterface;
|
Chris@0
|
13 use Drupal\Core\Form\FormStateInterface;
|
Chris@17
|
14 use Drupal\Core\Messenger\MessengerInterface;
|
Chris@0
|
15 use Drupal\Core\Theme\ThemeManagerInterface;
|
Chris@0
|
16 use Drupal\Core\Url;
|
Chris@0
|
17 use Symfony\Component\DependencyInjection\ContainerInterface;
|
Chris@0
|
18 use Symfony\Component\HttpFoundation\Request;
|
Chris@0
|
19
|
Chris@0
|
20 /**
|
Chris@0
|
21 * Defines a class to build a listing of block entities.
|
Chris@0
|
22 *
|
Chris@0
|
23 * @see \Drupal\block\Entity\Block
|
Chris@0
|
24 */
|
Chris@0
|
25 class BlockListBuilder extends ConfigEntityListBuilder implements FormInterface {
|
Chris@0
|
26
|
Chris@0
|
27 /**
|
Chris@0
|
28 * The theme containing the blocks.
|
Chris@0
|
29 *
|
Chris@0
|
30 * @var string
|
Chris@0
|
31 */
|
Chris@0
|
32 protected $theme;
|
Chris@0
|
33
|
Chris@0
|
34 /**
|
Chris@0
|
35 * The current request.
|
Chris@0
|
36 *
|
Chris@0
|
37 * @var \Symfony\Component\HttpFoundation\Request
|
Chris@0
|
38 */
|
Chris@0
|
39 protected $request;
|
Chris@0
|
40
|
Chris@0
|
41 /**
|
Chris@0
|
42 * The theme manager.
|
Chris@0
|
43 *
|
Chris@0
|
44 * @var \Drupal\Core\Theme\ThemeManagerInterface
|
Chris@0
|
45 */
|
Chris@0
|
46 protected $themeManager;
|
Chris@0
|
47
|
Chris@0
|
48 /**
|
Chris@0
|
49 * The form builder.
|
Chris@0
|
50 *
|
Chris@0
|
51 * @var \Drupal\Core\Form\FormBuilderInterface
|
Chris@0
|
52 */
|
Chris@0
|
53 protected $formBuilder;
|
Chris@0
|
54
|
Chris@0
|
55 /**
|
Chris@0
|
56 * {@inheritdoc}
|
Chris@0
|
57 */
|
Chris@0
|
58 protected $limit = FALSE;
|
Chris@0
|
59
|
Chris@0
|
60 /**
|
Chris@17
|
61 * The messenger.
|
Chris@17
|
62 *
|
Chris@17
|
63 * @var \Drupal\Core\Messenger\MessengerInterface
|
Chris@17
|
64 */
|
Chris@17
|
65 protected $messenger;
|
Chris@17
|
66
|
Chris@17
|
67 /**
|
Chris@0
|
68 * Constructs a new BlockListBuilder object.
|
Chris@0
|
69 *
|
Chris@0
|
70 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
Chris@0
|
71 * The entity type definition.
|
Chris@0
|
72 * @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
Chris@0
|
73 * The entity storage class.
|
Chris@0
|
74 * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
|
Chris@0
|
75 * The theme manager.
|
Chris@0
|
76 * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
|
Chris@0
|
77 * The form builder.
|
Chris@0
|
78 */
|
Chris@17
|
79 public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, ThemeManagerInterface $theme_manager, FormBuilderInterface $form_builder, MessengerInterface $messenger) {
|
Chris@0
|
80 parent::__construct($entity_type, $storage);
|
Chris@0
|
81
|
Chris@0
|
82 $this->themeManager = $theme_manager;
|
Chris@0
|
83 $this->formBuilder = $form_builder;
|
Chris@17
|
84 $this->messenger = $messenger;
|
Chris@0
|
85 }
|
Chris@0
|
86
|
Chris@0
|
87 /**
|
Chris@0
|
88 * {@inheritdoc}
|
Chris@0
|
89 */
|
Chris@0
|
90 public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
Chris@0
|
91 return new static(
|
Chris@0
|
92 $entity_type,
|
Chris@18
|
93 $container->get('entity_type.manager')->getStorage($entity_type->id()),
|
Chris@0
|
94 $container->get('theme.manager'),
|
Chris@17
|
95 $container->get('form_builder'),
|
Chris@17
|
96 $container->get('messenger')
|
Chris@0
|
97 );
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@0
|
100 /**
|
Chris@0
|
101 * {@inheritdoc}
|
Chris@0
|
102 *
|
Chris@0
|
103 * @param string|null $theme
|
Chris@0
|
104 * (optional) The theme to display the blocks for. If NULL, the current
|
Chris@0
|
105 * theme will be used.
|
Chris@0
|
106 * @param \Symfony\Component\HttpFoundation\Request $request
|
Chris@0
|
107 * The current request.
|
Chris@0
|
108 *
|
Chris@0
|
109 * @return array
|
Chris@0
|
110 * The block list as a renderable array.
|
Chris@0
|
111 */
|
Chris@0
|
112 public function render($theme = NULL, Request $request = NULL) {
|
Chris@0
|
113 $this->request = $request;
|
Chris@0
|
114 $this->theme = $theme;
|
Chris@0
|
115
|
Chris@0
|
116 return $this->formBuilder->getForm($this);
|
Chris@0
|
117 }
|
Chris@0
|
118
|
Chris@0
|
119 /**
|
Chris@0
|
120 * {@inheritdoc}
|
Chris@0
|
121 */
|
Chris@0
|
122 public function getFormId() {
|
Chris@0
|
123 return 'block_admin_display_form';
|
Chris@0
|
124 }
|
Chris@0
|
125
|
Chris@0
|
126 /**
|
Chris@0
|
127 * {@inheritdoc}
|
Chris@0
|
128 */
|
Chris@0
|
129 public function buildForm(array $form, FormStateInterface $form_state) {
|
Chris@0
|
130 $form['#attached']['library'][] = 'core/drupal.tableheader';
|
Chris@0
|
131 $form['#attached']['library'][] = 'block/drupal.block';
|
Chris@0
|
132 $form['#attached']['library'][] = 'block/drupal.block.admin';
|
Chris@0
|
133 $form['#attributes']['class'][] = 'clearfix';
|
Chris@0
|
134
|
Chris@0
|
135 // Build the form tree.
|
Chris@0
|
136 $form['blocks'] = $this->buildBlocksForm();
|
Chris@0
|
137
|
Chris@0
|
138 $form['actions'] = [
|
Chris@0
|
139 '#tree' => FALSE,
|
Chris@0
|
140 '#type' => 'actions',
|
Chris@0
|
141 ];
|
Chris@0
|
142 $form['actions']['submit'] = [
|
Chris@0
|
143 '#type' => 'submit',
|
Chris@0
|
144 '#value' => $this->t('Save blocks'),
|
Chris@0
|
145 '#button_type' => 'primary',
|
Chris@0
|
146 ];
|
Chris@0
|
147
|
Chris@0
|
148 return $form;
|
Chris@0
|
149 }
|
Chris@0
|
150
|
Chris@0
|
151 /**
|
Chris@0
|
152 * Builds the main "Blocks" portion of the form.
|
Chris@0
|
153 *
|
Chris@0
|
154 * @return array
|
Chris@0
|
155 */
|
Chris@0
|
156 protected function buildBlocksForm() {
|
Chris@0
|
157 // Build blocks first for each region.
|
Chris@0
|
158 $blocks = [];
|
Chris@0
|
159 $entities = $this->load();
|
Chris@0
|
160 /** @var \Drupal\block\BlockInterface[] $entities */
|
Chris@0
|
161 foreach ($entities as $entity_id => $entity) {
|
Chris@0
|
162 $definition = $entity->getPlugin()->getPluginDefinition();
|
Chris@0
|
163 $blocks[$entity->getRegion()][$entity_id] = [
|
Chris@0
|
164 'label' => $entity->label(),
|
Chris@0
|
165 'entity_id' => $entity_id,
|
Chris@0
|
166 'weight' => $entity->getWeight(),
|
Chris@0
|
167 'entity' => $entity,
|
Chris@0
|
168 'category' => $definition['category'],
|
Chris@0
|
169 'status' => $entity->status(),
|
Chris@0
|
170 ];
|
Chris@0
|
171 }
|
Chris@0
|
172
|
Chris@0
|
173 $form = [
|
Chris@0
|
174 '#type' => 'table',
|
Chris@0
|
175 '#header' => [
|
Chris@0
|
176 $this->t('Block'),
|
Chris@0
|
177 $this->t('Category'),
|
Chris@0
|
178 $this->t('Region'),
|
Chris@0
|
179 $this->t('Weight'),
|
Chris@0
|
180 $this->t('Operations'),
|
Chris@0
|
181 ],
|
Chris@0
|
182 '#attributes' => [
|
Chris@0
|
183 'id' => 'blocks',
|
Chris@0
|
184 ],
|
Chris@0
|
185 ];
|
Chris@0
|
186
|
Chris@0
|
187 // Weights range from -delta to +delta, so delta should be at least half
|
Chris@0
|
188 // of the amount of blocks present. This makes sure all blocks in the same
|
Chris@0
|
189 // region get an unique weight.
|
Chris@0
|
190 $weight_delta = round(count($entities) / 2);
|
Chris@0
|
191
|
Chris@0
|
192 $placement = FALSE;
|
Chris@0
|
193 if ($this->request->query->has('block-placement')) {
|
Chris@0
|
194 $placement = $this->request->query->get('block-placement');
|
Chris@0
|
195 $form['#attached']['drupalSettings']['blockPlacement'] = $placement;
|
Chris@17
|
196 // Remove the block placement from the current request so that it is not
|
Chris@17
|
197 // passed on to any redirect destinations.
|
Chris@17
|
198 $this->request->query->remove('block-placement');
|
Chris@0
|
199 }
|
Chris@0
|
200
|
Chris@0
|
201 // Loop over each region and build blocks.
|
Chris@0
|
202 $regions = $this->systemRegionList($this->getThemeName(), REGIONS_VISIBLE);
|
Chris@0
|
203 foreach ($regions as $region => $title) {
|
Chris@0
|
204 $form['#tabledrag'][] = [
|
Chris@0
|
205 'action' => 'match',
|
Chris@0
|
206 'relationship' => 'sibling',
|
Chris@0
|
207 'group' => 'block-region-select',
|
Chris@0
|
208 'subgroup' => 'block-region-' . $region,
|
Chris@0
|
209 'hidden' => FALSE,
|
Chris@0
|
210 ];
|
Chris@0
|
211 $form['#tabledrag'][] = [
|
Chris@0
|
212 'action' => 'order',
|
Chris@0
|
213 'relationship' => 'sibling',
|
Chris@0
|
214 'group' => 'block-weight',
|
Chris@0
|
215 'subgroup' => 'block-weight-' . $region,
|
Chris@0
|
216 ];
|
Chris@0
|
217
|
Chris@0
|
218 $form['region-' . $region] = [
|
Chris@0
|
219 '#attributes' => [
|
Chris@0
|
220 'class' => ['region-title', 'region-title-' . $region],
|
Chris@0
|
221 'no_striping' => TRUE,
|
Chris@0
|
222 ],
|
Chris@0
|
223 ];
|
Chris@0
|
224 $form['region-' . $region]['title'] = [
|
Chris@0
|
225 '#theme_wrappers' => [
|
Chris@0
|
226 'container' => [
|
Chris@0
|
227 '#attributes' => ['class' => 'region-title__action'],
|
Chris@17
|
228 ],
|
Chris@0
|
229 ],
|
Chris@0
|
230 '#prefix' => $title,
|
Chris@0
|
231 '#type' => 'link',
|
Chris@0
|
232 '#title' => $this->t('Place block <span class="visually-hidden">in the %region region</span>', ['%region' => $title]),
|
Chris@0
|
233 '#url' => Url::fromRoute('block.admin_library', ['theme' => $this->getThemeName()], ['query' => ['region' => $region]]),
|
Chris@0
|
234 '#wrapper_attributes' => [
|
Chris@0
|
235 'colspan' => 5,
|
Chris@0
|
236 ],
|
Chris@0
|
237 '#attributes' => [
|
Chris@0
|
238 'class' => ['use-ajax', 'button', 'button--small'],
|
Chris@0
|
239 'data-dialog-type' => 'modal',
|
Chris@0
|
240 'data-dialog-options' => Json::encode([
|
Chris@0
|
241 'width' => 700,
|
Chris@0
|
242 ]),
|
Chris@0
|
243 ],
|
Chris@0
|
244 ];
|
Chris@0
|
245
|
Chris@0
|
246 $form['region-' . $region . '-message'] = [
|
Chris@0
|
247 '#attributes' => [
|
Chris@0
|
248 'class' => [
|
Chris@0
|
249 'region-message',
|
Chris@0
|
250 'region-' . $region . '-message',
|
Chris@0
|
251 empty($blocks[$region]) ? 'region-empty' : 'region-populated',
|
Chris@0
|
252 ],
|
Chris@0
|
253 ],
|
Chris@0
|
254 ];
|
Chris@0
|
255 $form['region-' . $region . '-message']['message'] = [
|
Chris@0
|
256 '#markup' => '<em>' . $this->t('No blocks in this region') . '</em>',
|
Chris@0
|
257 '#wrapper_attributes' => [
|
Chris@0
|
258 'colspan' => 5,
|
Chris@0
|
259 ],
|
Chris@0
|
260 ];
|
Chris@0
|
261
|
Chris@0
|
262 if (isset($blocks[$region])) {
|
Chris@0
|
263 foreach ($blocks[$region] as $info) {
|
Chris@0
|
264 $entity_id = $info['entity_id'];
|
Chris@0
|
265
|
Chris@0
|
266 $form[$entity_id] = [
|
Chris@0
|
267 '#attributes' => [
|
Chris@0
|
268 'class' => ['draggable'],
|
Chris@0
|
269 ],
|
Chris@0
|
270 ];
|
Chris@0
|
271 $form[$entity_id]['#attributes']['class'][] = $info['status'] ? 'block-enabled' : 'block-disabled';
|
Chris@0
|
272 if ($placement && $placement == Html::getClass($entity_id)) {
|
Chris@0
|
273 $form[$entity_id]['#attributes']['class'][] = 'color-success';
|
Chris@0
|
274 $form[$entity_id]['#attributes']['class'][] = 'js-block-placed';
|
Chris@0
|
275 }
|
Chris@0
|
276 $form[$entity_id]['info'] = [
|
Chris@0
|
277 '#plain_text' => $info['status'] ? $info['label'] : $this->t('@label (disabled)', ['@label' => $info['label']]),
|
Chris@0
|
278 '#wrapper_attributes' => [
|
Chris@0
|
279 'class' => ['block'],
|
Chris@0
|
280 ],
|
Chris@0
|
281 ];
|
Chris@0
|
282 $form[$entity_id]['type'] = [
|
Chris@0
|
283 '#markup' => $info['category'],
|
Chris@0
|
284 ];
|
Chris@0
|
285 $form[$entity_id]['region-theme']['region'] = [
|
Chris@0
|
286 '#type' => 'select',
|
Chris@0
|
287 '#default_value' => $region,
|
Chris@0
|
288 '#required' => TRUE,
|
Chris@0
|
289 '#title' => $this->t('Region for @block block', ['@block' => $info['label']]),
|
Chris@0
|
290 '#title_display' => 'invisible',
|
Chris@0
|
291 '#options' => $regions,
|
Chris@0
|
292 '#attributes' => [
|
Chris@0
|
293 'class' => ['block-region-select', 'block-region-' . $region],
|
Chris@0
|
294 ],
|
Chris@0
|
295 '#parents' => ['blocks', $entity_id, 'region'],
|
Chris@0
|
296 ];
|
Chris@0
|
297 $form[$entity_id]['region-theme']['theme'] = [
|
Chris@0
|
298 '#type' => 'hidden',
|
Chris@0
|
299 '#value' => $this->getThemeName(),
|
Chris@0
|
300 '#parents' => ['blocks', $entity_id, 'theme'],
|
Chris@0
|
301 ];
|
Chris@0
|
302 $form[$entity_id]['weight'] = [
|
Chris@0
|
303 '#type' => 'weight',
|
Chris@0
|
304 '#default_value' => $info['weight'],
|
Chris@0
|
305 '#delta' => $weight_delta,
|
Chris@0
|
306 '#title' => $this->t('Weight for @block block', ['@block' => $info['label']]),
|
Chris@0
|
307 '#title_display' => 'invisible',
|
Chris@0
|
308 '#attributes' => [
|
Chris@0
|
309 'class' => ['block-weight', 'block-weight-' . $region],
|
Chris@0
|
310 ],
|
Chris@0
|
311 ];
|
Chris@0
|
312 $form[$entity_id]['operations'] = $this->buildOperations($info['entity']);
|
Chris@0
|
313 }
|
Chris@0
|
314 }
|
Chris@0
|
315 }
|
Chris@0
|
316
|
Chris@0
|
317 // Do not allow disabling the main system content block when it is present.
|
Chris@0
|
318 if (isset($form['system_main']['region'])) {
|
Chris@0
|
319 $form['system_main']['region']['#required'] = TRUE;
|
Chris@0
|
320 }
|
Chris@0
|
321 return $form;
|
Chris@0
|
322 }
|
Chris@0
|
323
|
Chris@0
|
324 /**
|
Chris@0
|
325 * Gets the name of the theme used for this block listing.
|
Chris@0
|
326 *
|
Chris@0
|
327 * @return string
|
Chris@0
|
328 * The name of the theme.
|
Chris@0
|
329 */
|
Chris@0
|
330 protected function getThemeName() {
|
Chris@0
|
331 // If no theme was specified, use the current theme.
|
Chris@0
|
332 if (!$this->theme) {
|
Chris@0
|
333 $this->theme = $this->themeManager->getActiveTheme()->getName();
|
Chris@0
|
334 }
|
Chris@0
|
335 return $this->theme;
|
Chris@0
|
336 }
|
Chris@0
|
337
|
Chris@0
|
338 /**
|
Chris@0
|
339 * {@inheritdoc}
|
Chris@0
|
340 */
|
Chris@0
|
341 protected function getEntityIds() {
|
Chris@0
|
342 return $this->getStorage()->getQuery()
|
Chris@0
|
343 ->condition('theme', $this->getThemeName())
|
Chris@0
|
344 ->sort($this->entityType->getKey('id'))
|
Chris@0
|
345 ->execute();
|
Chris@0
|
346 }
|
Chris@0
|
347
|
Chris@0
|
348 /**
|
Chris@0
|
349 * {@inheritdoc}
|
Chris@0
|
350 */
|
Chris@0
|
351 public function getDefaultOperations(EntityInterface $entity) {
|
Chris@0
|
352 $operations = parent::getDefaultOperations($entity);
|
Chris@0
|
353
|
Chris@0
|
354 if (isset($operations['edit'])) {
|
Chris@0
|
355 $operations['edit']['title'] = $this->t('Configure');
|
Chris@0
|
356 }
|
Chris@0
|
357
|
Chris@0
|
358 if (isset($operations['delete'])) {
|
Chris@0
|
359 $operations['delete']['title'] = $this->t('Remove');
|
Chris@0
|
360 }
|
Chris@0
|
361 return $operations;
|
Chris@0
|
362 }
|
Chris@0
|
363
|
Chris@0
|
364 /**
|
Chris@0
|
365 * {@inheritdoc}
|
Chris@0
|
366 */
|
Chris@0
|
367 public function validateForm(array &$form, FormStateInterface $form_state) {
|
Chris@0
|
368 // No validation.
|
Chris@0
|
369 }
|
Chris@0
|
370
|
Chris@0
|
371 /**
|
Chris@0
|
372 * {@inheritdoc}
|
Chris@0
|
373 */
|
Chris@0
|
374 public function submitForm(array &$form, FormStateInterface $form_state) {
|
Chris@0
|
375 $entities = $this->storage->loadMultiple(array_keys($form_state->getValue('blocks')));
|
Chris@0
|
376 /** @var \Drupal\block\BlockInterface[] $entities */
|
Chris@0
|
377 foreach ($entities as $entity_id => $entity) {
|
Chris@0
|
378 $entity_values = $form_state->getValue(['blocks', $entity_id]);
|
Chris@0
|
379 $entity->setWeight($entity_values['weight']);
|
Chris@0
|
380 $entity->setRegion($entity_values['region']);
|
Chris@0
|
381 $entity->save();
|
Chris@0
|
382 }
|
Chris@17
|
383 $this->messenger->addStatus($this->t('The block settings have been updated.'));
|
Chris@0
|
384 }
|
Chris@0
|
385
|
Chris@0
|
386 /**
|
Chris@0
|
387 * Wraps system_region_list().
|
Chris@0
|
388 */
|
Chris@0
|
389 protected function systemRegionList($theme, $show = REGIONS_ALL) {
|
Chris@0
|
390 return system_region_list($theme, $show);
|
Chris@0
|
391 }
|
Chris@0
|
392
|
Chris@0
|
393 }
|