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