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