Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / modules / layout_builder / src / Controller / LayoutBuilderController.php
1 <?php
2
3 namespace Drupal\layout_builder\Controller;
4
5 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
6 use Drupal\Core\Messenger\MessengerInterface;
7 use Drupal\Core\Plugin\PluginFormInterface;
8 use Drupal\Core\StringTranslation\StringTranslationTrait;
9 use Drupal\Core\Url;
10 use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
11 use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
12 use Drupal\layout_builder\OverridesSectionStorageInterface;
13 use Drupal\layout_builder\Section;
14 use Drupal\layout_builder\SectionStorageInterface;
15 use Symfony\Component\DependencyInjection\ContainerInterface;
16 use Symfony\Component\HttpFoundation\RedirectResponse;
17
18 /**
19  * Defines a controller to provide the Layout Builder admin UI.
20  *
21  * @internal
22  */
23 class LayoutBuilderController implements ContainerInjectionInterface {
24
25   use LayoutBuilderContextTrait;
26   use StringTranslationTrait;
27
28   /**
29    * The layout tempstore repository.
30    *
31    * @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface
32    */
33   protected $layoutTempstoreRepository;
34
35   /**
36    * The messenger service.
37    *
38    * @var \Drupal\Core\Messenger\MessengerInterface
39    */
40   protected $messenger;
41
42   /**
43    * LayoutBuilderController constructor.
44    *
45    * @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
46    *   The layout tempstore repository.
47    * @param \Drupal\Core\Messenger\MessengerInterface $messenger
48    *   The messenger service.
49    */
50   public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, MessengerInterface $messenger) {
51     $this->layoutTempstoreRepository = $layout_tempstore_repository;
52     $this->messenger = $messenger;
53   }
54
55   /**
56    * {@inheritdoc}
57    */
58   public static function create(ContainerInterface $container) {
59     return new static(
60       $container->get('layout_builder.tempstore_repository'),
61       $container->get('messenger')
62     );
63   }
64
65   /**
66    * Provides a title callback.
67    *
68    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
69    *   The section storage.
70    *
71    * @return string
72    *   The title for the layout page.
73    */
74   public function title(SectionStorageInterface $section_storage) {
75     return $this->t('Edit layout for %label', ['%label' => $section_storage->label()]);
76   }
77
78   /**
79    * Renders the Layout UI.
80    *
81    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
82    *   The section storage.
83    * @param bool $is_rebuilding
84    *   (optional) Indicates if the layout is rebuilding, defaults to FALSE.
85    *
86    * @return array
87    *   A render array.
88    */
89   public function layout(SectionStorageInterface $section_storage, $is_rebuilding = FALSE) {
90     $this->prepareLayout($section_storage, $is_rebuilding);
91
92     $output = [];
93     $count = 0;
94     for ($i = 0; $i < $section_storage->count(); $i++) {
95       $output[] = $this->buildAddSectionLink($section_storage, $count);
96       $output[] = $this->buildAdministrativeSection($section_storage, $count);
97       $count++;
98     }
99     $output[] = $this->buildAddSectionLink($section_storage, $count);
100     $output['#attached']['library'][] = 'layout_builder/drupal.layout_builder';
101     $output['#type'] = 'container';
102     $output['#attributes']['id'] = 'layout-builder';
103     // Mark this UI as uncacheable.
104     $output['#cache']['max-age'] = 0;
105     return $output;
106   }
107
108   /**
109    * Prepares a layout for use in the UI.
110    *
111    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
112    *   The section storage.
113    * @param bool $is_rebuilding
114    *   Indicates if the layout is rebuilding.
115    */
116   protected function prepareLayout(SectionStorageInterface $section_storage, $is_rebuilding) {
117     // Only add sections if the layout is new and empty.
118     if (!$is_rebuilding && $section_storage->count() === 0) {
119       $sections = [];
120       // If this is an empty override, copy the sections from the corresponding
121       // default.
122       if ($section_storage instanceof OverridesSectionStorageInterface) {
123         $sections = $section_storage->getDefaultSectionStorage()->getSections();
124       }
125
126       // For an empty layout, begin with a single section of one column.
127       if (!$sections) {
128         $sections[] = new Section('layout_onecol');
129       }
130
131       foreach ($sections as $section) {
132         $section_storage->appendSection($section);
133       }
134       $this->layoutTempstoreRepository->set($section_storage);
135     }
136   }
137
138   /**
139    * Builds a link to add a new section at a given delta.
140    *
141    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
142    *   The section storage.
143    * @param int $delta
144    *   The delta of the section to splice.
145    *
146    * @return array
147    *   A render array for a link.
148    */
149   protected function buildAddSectionLink(SectionStorageInterface $section_storage, $delta) {
150     $storage_type = $section_storage->getStorageType();
151     $storage_id = $section_storage->getStorageId();
152     return [
153       'link' => [
154         '#type' => 'link',
155         '#title' => $this->t('Add Section'),
156         '#url' => Url::fromRoute('layout_builder.choose_section',
157           [
158             'section_storage_type' => $storage_type,
159             'section_storage' => $storage_id,
160             'delta' => $delta,
161           ],
162           [
163             'attributes' => [
164               'class' => ['use-ajax', 'new-section__link'],
165               'data-dialog-type' => 'dialog',
166               'data-dialog-renderer' => 'off_canvas',
167             ],
168           ]
169         ),
170       ],
171       '#type' => 'container',
172       '#attributes' => [
173         'class' => ['new-section'],
174       ],
175     ];
176   }
177
178   /**
179    * Builds the render array for the layout section while editing.
180    *
181    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
182    *   The section storage.
183    * @param int $delta
184    *   The delta of the section.
185    *
186    * @return array
187    *   The render array for a given section.
188    */
189   protected function buildAdministrativeSection(SectionStorageInterface $section_storage, $delta) {
190     $storage_type = $section_storage->getStorageType();
191     $storage_id = $section_storage->getStorageId();
192     $section = $section_storage->getSection($delta);
193
194     $layout = $section->getLayout();
195     $build = $section->toRenderArray($this->getAvailableContexts($section_storage), TRUE);
196     $layout_definition = $layout->getPluginDefinition();
197
198     foreach ($layout_definition->getRegions() as $region => $info) {
199       if (!empty($build[$region])) {
200         foreach ($build[$region] as $uuid => $block) {
201           $build[$region][$uuid]['#attributes']['class'][] = 'draggable';
202           $build[$region][$uuid]['#attributes']['data-layout-block-uuid'] = $uuid;
203           $build[$region][$uuid]['#contextual_links'] = [
204             'layout_builder_block' => [
205               'route_parameters' => [
206                 'section_storage_type' => $storage_type,
207                 'section_storage' => $storage_id,
208                 'delta' => $delta,
209                 'region' => $region,
210                 'uuid' => $uuid,
211               ],
212             ],
213           ];
214         }
215       }
216
217       $build[$region]['layout_builder_add_block']['link'] = [
218         '#type' => 'link',
219         '#title' => $this->t('Add Block'),
220         '#url' => Url::fromRoute('layout_builder.choose_block',
221           [
222             'section_storage_type' => $storage_type,
223             'section_storage' => $storage_id,
224             'delta' => $delta,
225             'region' => $region,
226           ],
227           [
228             'attributes' => [
229               'class' => ['use-ajax', 'new-block__link'],
230               'data-dialog-type' => 'dialog',
231               'data-dialog-renderer' => 'off_canvas',
232             ],
233           ]
234         ),
235       ];
236       $build[$region]['layout_builder_add_block']['#type'] = 'container';
237       $build[$region]['layout_builder_add_block']['#attributes'] = ['class' => ['new-block']];
238       $build[$region]['layout_builder_add_block']['#weight'] = 1000;
239       $build[$region]['#attributes']['data-region'] = $region;
240       $build[$region]['#attributes']['class'][] = 'layout-builder--layout__region';
241     }
242
243     $build['#attributes']['data-layout-update-url'] = Url::fromRoute('layout_builder.move_block', [
244       'section_storage_type' => $storage_type,
245       'section_storage' => $storage_id,
246     ])->toString();
247     $build['#attributes']['data-layout-delta'] = $delta;
248     $build['#attributes']['class'][] = 'layout-builder--layout';
249
250     return [
251       '#type' => 'container',
252       '#attributes' => [
253         'class' => ['layout-section'],
254       ],
255       'configure' => [
256         '#type' => 'link',
257         '#title' => $this->t('Configure section'),
258         '#access' => $layout instanceof PluginFormInterface,
259         '#url' => Url::fromRoute('layout_builder.configure_section', [
260           'section_storage_type' => $storage_type,
261           'section_storage' => $storage_id,
262           'delta' => $delta,
263         ]),
264         '#attributes' => [
265           'class' => ['use-ajax', 'configure-section'],
266           'data-dialog-type' => 'dialog',
267           'data-dialog-renderer' => 'off_canvas',
268         ],
269       ],
270       'remove' => [
271         '#type' => 'link',
272         '#title' => $this->t('Remove section'),
273         '#url' => Url::fromRoute('layout_builder.remove_section', [
274           'section_storage_type' => $storage_type,
275           'section_storage' => $storage_id,
276           'delta' => $delta,
277         ]),
278         '#attributes' => [
279           'class' => ['use-ajax', 'remove-section'],
280           'data-dialog-type' => 'dialog',
281           'data-dialog-renderer' => 'off_canvas',
282         ],
283       ],
284       'layout-section' => $build,
285     ];
286   }
287
288   /**
289    * Saves the layout.
290    *
291    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
292    *   The section storage.
293    *
294    * @return \Symfony\Component\HttpFoundation\RedirectResponse
295    *   A redirect response.
296    */
297   public function saveLayout(SectionStorageInterface $section_storage) {
298     $section_storage->save();
299     $this->layoutTempstoreRepository->delete($section_storage);
300
301     if ($section_storage instanceof OverridesSectionStorageInterface) {
302       $this->messenger->addMessage($this->t('The layout override has been saved.'));
303     }
304     else {
305       $this->messenger->addMessage($this->t('The layout has been saved.'));
306     }
307
308     return new RedirectResponse($section_storage->getRedirectUrl()->setAbsolute()->toString());
309   }
310
311   /**
312    * Cancels the layout.
313    *
314    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
315    *   The section storage.
316    *
317    * @return \Symfony\Component\HttpFoundation\RedirectResponse
318    *   A redirect response.
319    */
320   public function cancelLayout(SectionStorageInterface $section_storage) {
321     $this->layoutTempstoreRepository->delete($section_storage);
322
323     $this->messenger->addMessage($this->t('The changes to the layout have been discarded.'));
324
325     return new RedirectResponse($section_storage->getRedirectUrl()->setAbsolute()->toString());
326   }
327
328 }