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