diff -u b/core/modules/layout_builder/layout_builder.post_update.php b/core/modules/layout_builder/layout_builder.post_update.php --- b/core/modules/layout_builder/layout_builder.post_update.php +++ b/core/modules/layout_builder/layout_builder.post_update.php @@ -306,3 +306,3 @@ function layout_builder_post_update_section_move_form() { - // Empty post-update hook. + // Empty post-update hook. } interdiff impossible; taking evasive action reverted: --- b/core/modules/layout_builder/src/Form/DefaultsEntityForm.php +++ a/core/modules/layout_builder/src/Form/DefaultsEntityForm.php @@ -6,7 +6,6 @@ use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; -use Drupal\Core\Url; use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\SectionStorageInterface; @@ -172,29 +171,6 @@ '#submit' => ['::redirectOnSubmit'], '#redirect' => 'discard_changes', ]; - - $actions['move_sections'] = [ - '#type' => 'link', - '#title' => $this->t('Reorder sections'), - '#url' => Url::fromRoute('layout_builder.move_sections_form', - [ - 'section_storage_type' => $this->sectionStorage->getStorageType(), - 'section_storage' => $this->sectionStorage->getStorageId(), - ], - [ - 'attributes' => [ - 'class' => [ - 'use-ajax', - 'button', - ], - 'data-dialog-type' => 'dialog', - 'data-dialog-renderer' => 'off_canvas', - 'data-disable-refocus' => 'true', - ], - ] - ), - ]; - $actions['preview_toggle'] = $this->buildContentPreviewToggle(); return $actions; } unchanged: --- a/core/modules/layout_builder/src/Form/DefaultsEntityForm.php +++ b/core/modules/layout_builder/src/Form/DefaultsEntityForm.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Url; use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\SectionStorageInterface; @@ -152,27 +153,8 @@ public function buildEntity(array $form, FormStateInterface $form_state) { public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) { $route_parameters = $route_match->getParameters()->all(); - return $this->entityTypeManager->getStorage('entity_view_display')->load($route_parameters['entity_type_id'] . '.' . $route_parameters['bundle'] . '.' . $route_parameters['view_mode_name']); - } - - /** - * {@inheritdoc} - */ - protected function actions(array $form, FormStateInterface $form_state) { - $actions = parent::actions($form, $form_state); - $actions['#attributes']['role'] = 'region'; - $actions['#attributes']['aria-label'] = $this->t('Layout Builder tools'); - $actions['submit']['#value'] = $this->t('Save layout'); - $actions['#weight'] = -1000; - - $actions['discard_changes'] = [ - '#type' => 'submit', - '#value' => $this->t('Discard changes'), - '#submit' => ['::redirectOnSubmit'], - '#redirect' => 'discard_changes', - ]; - $actions['preview_toggle'] = $this->buildContentPreviewToggle(); - return $actions; + return $this->entityTypeManager->getStorage('entity_view_display') + ->load($route_parameters['entity_type_id'] . '.' . $route_parameters['bundle'] . '.' . $route_parameters['view_mode_name']); } /** @@ -203,4 +185,47 @@ public function getSectionStorage() { return $this->sectionStorage; } + /** + * {@inheritdoc} + */ + protected function actions(array $form, FormStateInterface $form_state) { + $actions = parent::actions($form, $form_state); + $actions['#attributes']['role'] = 'region'; + $actions['#attributes']['aria-label'] = $this->t('Layout Builder tools'); + $actions['submit']['#value'] = $this->t('Save layout'); + $actions['#weight'] = -1000; + + $actions['discard_changes'] = [ + '#type' => 'submit', + '#value' => $this->t('Discard changes'), + '#submit' => ['::redirectOnSubmit'], + '#redirect' => 'discard_changes', + ]; + + $actions['move_sections'] = [ + '#type' => 'link', + '#title' => $this->t('Reorder sections'), + '#url' => Url::fromRoute('layout_builder.move_sections_form', + [ + 'section_storage_type' => $this->sectionStorage->getStorageType(), + 'section_storage' => $this->sectionStorage->getStorageId(), + ], + [ + 'attributes' => [ + 'class' => [ + 'use-ajax', + 'button', + ], + 'data-dialog-type' => 'dialog', + 'data-dialog-renderer' => 'off_canvas', + 'data-disable-refocus' => 'true', + ], + ] + ), + ]; + + $actions['preview_toggle'] = $this->buildContentPreviewToggle(); + return $actions; + } + } diff -u b/core/modules/layout_builder/src/Form/OverridesEntityForm.php b/core/modules/layout_builder/src/Form/OverridesEntityForm.php --- b/core/modules/layout_builder/src/Form/OverridesEntityForm.php +++ b/core/modules/layout_builder/src/Form/OverridesEntityForm.php @@ -6,10 +6,10 @@ use Drupal\Core\Entity\ContentEntityForm; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Url; use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Drupal\layout_builder\OverridesSectionStorageInterface; use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage; @@ -82,16 +81,0 @@ - protected function init(FormStateInterface $form_state) { - parent::init($form_state); - - $form_display = EntityFormDisplay::collectRenderDisplay($this->entity, $this->getOperation(), FALSE); - $form_display->setComponent(OverridesSectionStorage::FIELD_NAME, [ - 'type' => 'layout_builder_widget', - 'weight' => -10, - 'settings' => [], - ]); - - $this->setFormDisplay($form_display, $form_state); - } - - /** - * {@inheritdoc} - */ @@ -179,10 +163,44 @@ $this->layoutTempstoreRepository->delete($this->sectionStorage); - $this->messenger()->addStatus($this->t('The layout override has been saved.')); + $this->messenger() + ->addStatus($this->t('The layout override has been saved.')); $form_state->setRedirectUrl($this->sectionStorage->getRedirectUrl()); return $return; } /** + * Form submission handler. + */ + public function redirectOnSubmit(array $form, FormStateInterface $form_state) { + $form_state->setRedirectUrl($this->sectionStorage->getLayoutBuilderUrl($form_state->getTriggeringElement()['#redirect'])); + } + + /** + * Retrieves the section storage object. + * + * @return \Drupal\layout_builder\SectionStorageInterface + * The section storage for the current form. + */ + public function getSectionStorage() { + return $this->sectionStorage; + } + + /** + * {@inheritdoc} + */ + protected function init(FormStateInterface $form_state) { + parent::init($form_state); + + $form_display = EntityFormDisplay::collectRenderDisplay($this->entity, $this->getOperation(), FALSE); + $form_display->setComponent(OverridesSectionStorage::FIELD_NAME, [ + 'type' => 'layout_builder_widget', + 'weight' => -10, + 'settings' => [], + ]); + + $this->setFormDisplay($form_display, $form_state); + } + + /** * {@inheritdoc} */ @@ -211,11 +229,10 @@ $actions['move_sections'] = [ '#type' => 'link', '#title' => $this->t('Reorder sections'), - '#url' => Url::fromRoute('layout_builder.move_sections_form', - [ - 'section_storage_type' => $this->sectionStorage->getStorageType(), - 'section_storage' => $this->sectionStorage->getStorageId(), - ], + '#url' => Url::fromRoute('layout_builder.move_sections_form', [ + 'section_storage_type' => $this->sectionStorage->getStorageType(), + 'section_storage' => $this->sectionStorage->getStorageId(), + ], [ 'attributes' => [ 'class' => [ @@ -231,23 +248,7 @@ ]; + $actions['preview_toggle'] = $this->buildContentPreviewToggle(); return $actions; } - /** - * Form submission handler. - */ - public function redirectOnSubmit(array $form, FormStateInterface $form_state) { - $form_state->setRedirectUrl($this->sectionStorage->getLayoutBuilderUrl($form_state->getTriggeringElement()['#redirect'])); - } - - /** - * Retrieves the section storage object. - * - * @return \Drupal\layout_builder\SectionStorageInterface - * The section storage for the current form. - */ - public function getSectionStorage() { - return $this->sectionStorage; - } - } diff -u b/core/themes/stable/css/layout_builder/layout-builder.css b/core/themes/stable/css/layout_builder/layout-builder.css --- b/core/themes/stable/css/layout_builder/layout-builder.css +++ b/core/themes/stable/css/layout_builder/layout-builder.css @@ -185,7 +185,7 @@ /** * @todo remove in https://www.drupal.org/project/drupal/issues/3042127 - * This rule ensures the row weight dropdowns in the Move Block dialog + * Ensure the row weight dropdowns in the Move Block & Sections dialogs * maintain the background color of their container when they are hovered * over or are inside the active row. */ reverted: --- b/core/modules/layout_builder/tests/src/FunctionalJavascript/MoveSectionsFormTest.php +++ /dev/null @@ -1,237 +0,0 @@ -createContentType(['type' => 'bundle_with_section_field']); - - $this->drupalLogin($this->drupalCreateUser([ - 'configure any layout', - 'administer node display', - 'administer node fields', - 'access contextual links', - ])); - } - - /** - * Tests moving sections. - */ - public function testMoveSections() { - $page = $this->getSession()->getPage(); - $assert_session = $this->assertSession(); - - // Enable layout builder. - $this->drupalPostForm( - static::FIELD_UI_PREFIX . '/display/default', - ['layout[enabled]' => TRUE], - 'Save' - ); - $page->clickLink('Manage layout'); - $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display/default/layout'); - - $expected_section_order = [ - '.layout--onecol', - ]; - $this->assertSectionsOrder($expected_section_order); - - // Add a top section using the Two column layout. - $page->clickLink('Add section'); - $assert_session->waitForElementVisible('css', '#drupal-off-canvas'); - $assert_session->assertWaitOnAjaxRequest(); - $page->clickLink('Two column'); - $assert_session->assertWaitOnAjaxRequest(); - $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'input[value="Add section"]')); - $page->pressButton('Add section'); - - $expected_section_order = [ - '.layout--twocol-section', - '.layout--onecol', - ]; - $this->assertSectionsOrder($expected_section_order); - - // Ensure the request has completed before the test starts. - $assert_session->assertWaitOnAjaxRequest(); - - // Reorder sections by dragging with keyboard. - $this->openSectionMoveForm(['Section 1', 'Section 2']); - $this->moveSectionWithKeyboard('up', 'Section 2', ['Section 2*', 'Section 1']); - $page->pressButton('Reorder'); - $expected_section_order = [ - '.layout--onecol', - '.layout--twocol-section', - ]; - $this->assertSectionsOrder($expected_section_order); - $page->pressButton('Save layout'); - $page->clickLink('Manage layout'); - $this->assertSectionsOrder($expected_section_order); - - // Reorder sections by setting delta values. - $this->openSectionMoveForm(['Section 1', 'Section 2']); - $page->pressButton('Show row weights'); - $page->selectFieldOption('Delta for Section 1 section', '1'); - $page->selectFieldOption('Delta for Section 2 section', '0'); - $page->pressButton('Hide row weights'); - $page->pressButton('Reorder'); - $expected_section_order = [ - '.layout--twocol-section', - '.layout--onecol', - ]; - $this->assertSectionsOrder($expected_section_order); - $page->pressButton('Save layout'); - $page->clickLink('Manage layout'); - $this->assertSectionsOrder($expected_section_order); - - // Drag section with keyboard and set delta values to be equal. - // When delta values are equal row order is respected. - $this->openSectionMoveForm(['Section 1', 'Section 2']); - $this->moveSectionWithKeyboard('up', 'Section 2', ['Section 2*', 'Section 1']); - $page->pressButton('Show row weights'); - $page->selectFieldOption('Delta for Section 1 section', '0'); - $page->pressButton('Reorder'); - $expected_section_order = [ - '.layout--onecol', - '.layout--twocol-section', - ]; - $this->assertSectionsOrder($expected_section_order); - $page->pressButton('Save layout'); - $page->clickLink('Manage layout'); - $this->assertSectionsOrder($expected_section_order); - } - - /** - * Asserts the correct section labels appear in the draggable tables. - * - * @param string[] $expected_section_labels - * The expected section labels. - */ - protected function assertSectionTable(array $expected_section_labels) { - $page = $this->getSession()->getPage(); - $this->assertSession()->assertWaitOnAjaxRequest(); - $section_tds = $page->findAll('css', '.layout-builder-sections-table__section-label'); - $this->assertCount(count($section_tds), $expected_section_labels); - /** @var \Behat\Mink\Element\NodeElement $section_td */ - foreach ($section_tds as $section_td) { - $this->assertSame(array_shift($expected_section_labels), trim($section_td->getText())); - } - } - - /** - * Moves a section in the draggable table. - * - * @param string $direction - * The direction to move the section in the table. - * @param string $section_label - * The section label. - * @param array $updated_sections - * The updated sections order. - */ - protected function moveSectionWithKeyboard($direction, $section_label, array $updated_sections) { - $keys = [ - 'up' => 38, - 'down' => 40, - ]; - $key = $keys[$direction]; - $handle = $this->findRowHandle($section_label); - - $handle->keyDown($key); - $handle->keyUp($key); - - $handle->blur(); - $this->assertSectionTable($updated_sections); - } - - /** - * Finds the row handle for a section in the draggable table. - * - * @param string $section_label - * The section label. - * - * @return \Behat\Mink\Element\NodeElement - * The row handle element. - */ - protected function findRowHandle($section_label) { - $assert_session = $this->assertSession(); - return $assert_session->elementExists('css', "[data-drupal-selector=\"edit-sections\"] td:contains(\"$section_label\") a.tabledrag-handle"); - } - - /** - * Asserts that sections are in the correct order for the layout. - * - * @param array $expected_section_selectors - * The section selectors. - */ - protected function assertSectionsOrder(array $expected_section_selectors) { - $page = $this->getSession()->getPage(); - $assert_session = $this->assertSession(); - - $assert_session->assertWaitOnAjaxRequest(); - $assert_session->assertNoElementAfterWait('css', '#drupal-off-canvas'); - - // Get all sections currently in the layout. - $sections = $page->findAll('css', "[data-layout-delta]"); - $this->assertCount(count($expected_section_selectors), $sections); - - /** @var \Behat\Mink\Element\NodeElement $section */ - foreach ($sections as $section) { - $section_selector = array_shift($expected_section_selectors); - $assert_session->elementsCount('css', "$section_selector", 1); - $expected_section = $page->find('css', "$section_selector"); - $this->assertSame($expected_section->getAttribute('data-layout-delta'), $section->getAttribute('data-layout-delta')); - } - } - - /** - * Open move sections form. - * - * @param array $initial_sections - * The initial sections that should be shown in the draggable table. - */ - protected function openSectionMoveForm(array $initial_sections) { - $assert_session = $this->assertSession(); - - $this->clickLink('Reorder sections'); - $assert_session->assertWaitOnAjaxRequest(); - $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'button.tabledrag-toggle-weight')); - $this->assertSectionTable($initial_sections); - } - -} reverted: --- b/core/modules/layout_builder/src/Form/MoveSectionsForm.php +++ /dev/null @@ -1,203 +0,0 @@ -layoutTempstore = $layout_tempstore_repository; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('layout_builder.tempstore_repository') - ); - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'layout_builder_section_move'; - } - - /** - * Builds the move section form. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * @param \Drupal\layout_builder\SectionStorageInterface $section_storage - * The section storage being configured. - * - * @return array - * The form array. - */ - public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) { - $this->sectionStorage = $section_storage; - - $storage_label = $section_storage->label(); - $aria_label = $this->t('Sections in @storage_label layout', ['@storage_label' => $storage_label]); - - $form['sections_wrapper']['sections'] = [ - '#type' => 'table', - '#header' => [ - $this->t('Sections'), - $this->t('Delta'), - ], - '#tabledrag' => [ - [ - 'action' => 'order', - 'relationship' => 'sibling', - 'group' => 'table-sort-delta', - ], - ], - '#theme_wrappers' => [ - 'container' => [ - '#attributes' => [ - 'id' => 'layout-builder-sections-table', - 'class' => ['layout-builder-sections-table'], - 'aria-label' => $aria_label, - ], - ], - ], - ]; - - $sections = $section_storage->getSections(); - - foreach ($sections as $section_delta => $section) { - $row_classes = [ - 'draggable', - 'layout-builder-sections-table__row', - ]; - - $layout_settings = $section->getLayoutSettings(); - $section_label = !empty($layout_settings['label']) ? $layout_settings['label'] : $this->t('Section @section', ['@section' => $section_delta + 1]); - - $label = [ - '#markup' => $section_label, - '#wrapper_attributes' => ['class' => ['layout-builder-sections-table__section-label']], - ]; - - $form['sections_wrapper']['sections'][$section_delta] = [ - '#attributes' => ['class' => $row_classes], - 'label' => $label, - 'delta' => [ - '#type' => 'select', - '#options' => range(0, count($sections) - 1), - '#default_value' => $section_delta, - '#title' => $this->t('Delta for @section section', ['@section' => $section_label]), - '#title_display' => 'invisible', - '#attributes' => [ - 'class' => ['table-sort-delta'], - ], - ], - ]; - - } - - $form['actions']['submit'] = [ - '#type' => 'submit', - '#value' => $this->t('Reorder'), - '#button_type' => 'primary', - ]; - - $form['#attributes']['data-add-layout-builder-wrapper'] = 'layout-builder--move-sections-active'; - - if ($this->isAjax()) { - $form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit'; - } - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { - $new_deltas = $this->getNewDeltas($form_state); - if (count($new_deltas)) { - $sections = $this->sectionStorage->getSections(); - // Create a numeric array with the section deltas reordered. - $deltas = array_combine(array_keys($new_deltas), array_column($new_deltas, 'delta')); - asort($deltas); - $order = array_keys($deltas); - // Reorder sections. - $sections = array_map(function ($delta) use ($sections) { - return $sections[$delta]; - }, $order); - $this->sectionStorage->removeAllSections(); - foreach ($sections as $section) { - $this->sectionStorage->appendSection($section); - } - $this->layoutTempstore->set($this->sectionStorage); - } - - } - - /** - * {@inheritdoc} - */ - protected function successfulAjaxSubmit(array $form, FormStateInterface $form_state) { - return $this->rebuildAndClose($this->sectionStorage); - } - - /** - * Gets the submitted section deltas. - * - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The form state. - * - * @return array - * The section deltas. - */ - protected function getNewDeltas(FormStateInterface $form_state) { - if ($form_state->hasValue('sections')) { - return $form_state->getValue('sections'); - } - return []; - } - -} only in patch2: unchanged: --- a/core/modules/layout_builder/src/InlineBlockEntityOperations.php +++ b/core/modules/layout_builder/src/InlineBlockEntityOperations.php @@ -45,12 +45,6 @@ class InlineBlockEntityOperations implements ContainerInjectionInterface { /** * Constructs a new EntityOperations object. * - * @todo This constructor has one optional parameter, $section_storage_manager - * and one totally unused $database parameter. Deprecate the current - * constructor signature in https://www.drupal.org/node/3031492 after the - * general policy for constructor backwards compatibility is determined in - * https://www.drupal.org/node/3030640. - * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager * The entity type manager service. * @param \Drupal\layout_builder\InlineBlockUsageInterface $usage @@ -60,7 +54,16 @@ class InlineBlockEntityOperations implements ContainerInjectionInterface { * @param \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface $section_storage_manager * (optional) The section storage manager. * - * @todo The current constructor signature is deprecated: + * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException + * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException + * + * @todo This constructor has one optional parameter, $section_storage_manager + * and one totally unused $database parameter. Deprecate the current + * constructor signature in https://www.drupal.org/node/3031492 after the + * general policy for constructor backwards compatibility is determined in + * https://www.drupal.org/node/3030640. + * + * The current constructor signature is deprecated: * - The $section_storage_manager parameter is optional, but should become * required. * - The $database parameter is unused and should be removed. only in patch2: unchanged: --- a/www/modules/custom/mbf/mbf_breadcrumb +++ b/www/modules/custom/mbf/mbf_breadcrumb @@ -1 +1 @@ -Subproject commit 389d31565fb06f01beeb1e3e8832489296ce9e9c +Subproject commit 389d31565fb06f01beeb1e3e8832489296ce9e9c-dirty