layoutTempstoreRepository = $layout_tempstore_repository; $this->messenger = $messenger; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('layout_builder.tempstore_repository'), $container->get('messenger') ); } /** * Provides a title callback. * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * * @return string * The title for the layout page. */ public function title(SectionStorageInterface $section_storage) { return $this->t('Edit layout for %label', ['%label' => $section_storage->label()]); } /** * Renders the Layout UI. * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * @param bool $is_rebuilding * (optional) Indicates if the layout is rebuilding, defaults to FALSE. * * @return array * A render array. */ public function layout(SectionStorageInterface $section_storage, $is_rebuilding = FALSE) { $this->prepareLayout($section_storage, $is_rebuilding); $output = []; $count = 0; for ($i = 0; $i < $section_storage->count(); $i++) { $output[] = $this->buildAddSectionLink($section_storage, $count); $output[] = $this->buildAdministrativeSection($section_storage, $count); $count++; } $output[] = $this->buildAddSectionLink($section_storage, $count); $output['#attached']['library'][] = 'layout_builder/drupal.layout_builder'; $output['#type'] = 'container'; $output['#attributes']['id'] = 'layout-builder'; // Mark this UI as uncacheable. $output['#cache']['max-age'] = 0; return $output; } /** * Prepares a layout for use in the UI. * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * @param bool $is_rebuilding * Indicates if the layout is rebuilding. */ protected function prepareLayout(SectionStorageInterface $section_storage, $is_rebuilding) { // Only add sections if the layout is new and empty. if (!$is_rebuilding && $section_storage->count() === 0) { $sections = []; // If this is an empty override, copy the sections from the corresponding // default. if ($section_storage instanceof OverridesSectionStorageInterface) { $sections = $section_storage->getDefaultSectionStorage()->getSections(); } // For an empty layout, begin with a single section of one column. if (!$sections) { $sections[] = new Section('layout_onecol'); } foreach ($sections as $section) { $section_storage->appendSection($section); } $this->layoutTempstoreRepository->set($section_storage); } } /** * Builds a link to add a new section at a given delta. * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * @param int $delta * The delta of the section to splice. * * @return array * A render array for a link. */ protected function buildAddSectionLink(SectionStorageInterface $section_storage, $delta) { $storage_type = $section_storage->getStorageType(); $storage_id = $section_storage->getStorageId(); return [ 'link' => [ '#type' => 'link', '#title' => $this->t('Add Section'), '#url' => Url::fromRoute('layout_builder.choose_section', [ 'section_storage_type' => $storage_type, 'section_storage' => $storage_id, 'delta' => $delta, ], [ 'attributes' => [ 'class' => ['use-ajax'], 'data-dialog-type' => 'dialog', 'data-dialog-renderer' => 'off_canvas', ], ] ), ], '#type' => 'container', '#attributes' => [ 'class' => ['add-section'], ], ]; } /** * Builds the render array for the layout section while editing. * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * @param int $delta * The delta of the section. * * @return array * The render array for a given section. */ protected function buildAdministrativeSection(SectionStorageInterface $section_storage, $delta) { $storage_type = $section_storage->getStorageType(); $storage_id = $section_storage->getStorageId(); $section = $section_storage->getSection($delta); $layout = $section->getLayout(); $build = $section->toRenderArray($this->getAvailableContexts($section_storage), TRUE); $layout_definition = $layout->getPluginDefinition(); foreach ($layout_definition->getRegions() as $region => $info) { if (!empty($build[$region])) { foreach ($build[$region] as $uuid => $block) { $build[$region][$uuid]['#attributes']['class'][] = 'draggable'; $build[$region][$uuid]['#attributes']['data-layout-block-uuid'] = $uuid; $build[$region][$uuid]['#contextual_links'] = [ 'layout_builder_block' => [ 'route_parameters' => [ 'section_storage_type' => $storage_type, 'section_storage' => $storage_id, 'delta' => $delta, 'region' => $region, 'uuid' => $uuid, ], ], ]; } } $build[$region]['layout_builder_add_block']['link'] = [ '#type' => 'link', '#title' => $this->t('Add Block'), '#url' => Url::fromRoute('layout_builder.choose_block', [ 'section_storage_type' => $storage_type, 'section_storage' => $storage_id, 'delta' => $delta, 'region' => $region, ], [ 'attributes' => [ 'class' => ['use-ajax'], 'data-dialog-type' => 'dialog', 'data-dialog-renderer' => 'off_canvas', ], ] ), ]; $build[$region]['layout_builder_add_block']['#type'] = 'container'; $build[$region]['layout_builder_add_block']['#attributes'] = ['class' => ['add-block']]; $build[$region]['layout_builder_add_block']['#weight'] = 1000; $build[$region]['#attributes']['data-region'] = $region; $build[$region]['#attributes']['class'][] = 'layout-builder--layout__region'; } $build['#attributes']['data-layout-update-url'] = Url::fromRoute('layout_builder.move_block', [ 'section_storage_type' => $storage_type, 'section_storage' => $storage_id, ])->toString(); $build['#attributes']['data-layout-delta'] = $delta; $build['#attributes']['class'][] = 'layout-builder--layout'; return [ '#type' => 'container', '#attributes' => [ 'class' => ['layout-section'], ], 'configure' => [ '#type' => 'link', '#title' => $this->t('Configure section'), '#access' => $layout instanceof PluginFormInterface, '#url' => Url::fromRoute('layout_builder.configure_section', [ 'section_storage_type' => $storage_type, 'section_storage' => $storage_id, 'delta' => $delta, ]), '#attributes' => [ 'class' => ['use-ajax', 'configure-section'], 'data-dialog-type' => 'dialog', 'data-dialog-renderer' => 'off_canvas', ], ], 'remove' => [ '#type' => 'link', '#title' => $this->t('Remove section'), '#url' => Url::fromRoute('layout_builder.remove_section', [ 'section_storage_type' => $storage_type, 'section_storage' => $storage_id, 'delta' => $delta, ]), '#attributes' => [ 'class' => ['use-ajax', 'remove-section'], 'data-dialog-type' => 'dialog', 'data-dialog-renderer' => 'off_canvas', ], ], 'layout-section' => $build, ]; } /** * Saves the layout. * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * * @return \Symfony\Component\HttpFoundation\RedirectResponse * A redirect response. */ public function saveLayout(SectionStorageInterface $section_storage) { $section_storage->save(); $this->layoutTempstoreRepository->delete($section_storage); if ($section_storage instanceof OverridesSectionStorageInterface) { $this->messenger->addMessage($this->t('The layout override has been saved.')); } else { $this->messenger->addMessage($this->t('The layout has been saved.')); } return new RedirectResponse($section_storage->getRedirectUrl()->setAbsolute()->toString()); } /** * Cancels the layout. * * @param \Drupal\layout_builder\SectionStorageInterface $section_storage * The section storage. * * @return \Symfony\Component\HttpFoundation\RedirectResponse * A redirect response. */ public function cancelLayout(SectionStorageInterface $section_storage) { $this->layoutTempstoreRepository->delete($section_storage); $this->messenger->addMessage($this->t('The changes to the layout have been discarded.')); return new RedirectResponse($section_storage->getRedirectUrl()->setAbsolute()->toString()); } }