comparison core/modules/layout_builder/src/Controller/LayoutBuilderController.php @ 18:af1871eacc83

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