comparison core/modules/layout_builder/src/Controller/LayoutBuilderController.php @ 14:1fec387a4317

Update Drupal core to 8.5.2 via Composer
author Chris Cannam
date Mon, 23 Apr 2018 09:46:53 +0100
parents
children c2387f117808
comparison
equal deleted inserted replaced
13:5fb285c0d0e3 14:1fec387a4317
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'],
165 'data-dialog-type' => 'dialog',
166 'data-dialog-renderer' => 'off_canvas',
167 ],
168 ]
169 ),
170 ],
171 '#type' => 'container',
172 '#attributes' => [
173 'class' => ['add-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'],
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' => ['add-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 }