diff --git a/core/misc/ajax.es6.js b/core/misc/ajax.es6.js index 27795c8..709f4a6 100644 --- a/core/misc/ajax.es6.js +++ b/core/misc/ajax.es6.js @@ -521,15 +521,13 @@ else if (this.element && element.form) { else { ajax.options.url += '&'; } - // Add a wrapper format, if none exists. - if (ajax.options.url.indexOf(`${Drupal.ajax.WRAPPER_FORMAT}`) === -1) { - // If this element has a dialog type use if for the wrapper if not use 'ajax'. - let wrapper = `drupal_${(element_settings.dialogType || 'ajax')}`; - if (element_settings.dialogRenderer) { - wrapper += `.${element_settings.dialogRenderer}`; - } - ajax.options.url += `${Drupal.ajax.WRAPPER_FORMAT}=${wrapper}`; + // If this element has a dialog type use if for the wrapper if not use 'ajax'. + let wrapper = `drupal_${(element_settings.dialogType || 'ajax')}`; + if (element_settings.dialogRenderer) { + wrapper += `.${element_settings.dialogRenderer}`; } + ajax.options.url += `${Drupal.ajax.WRAPPER_FORMAT}=${wrapper}`; + // Bind the ajaxSubmit function to the element event. $(ajax.element).on(element_settings.event, function (event) { diff --git a/core/misc/ajax.js b/core/misc/ajax.js index 07644bd..a805a83 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -245,13 +245,11 @@ function loadAjaxBehavior(base) { ajax.options.url += '&'; } - if (ajax.options.url.indexOf('' + Drupal.ajax.WRAPPER_FORMAT) === -1) { - var wrapper = 'drupal_' + (element_settings.dialogType || 'ajax'); - if (element_settings.dialogRenderer) { - wrapper += '.' + element_settings.dialogRenderer; - } - ajax.options.url += Drupal.ajax.WRAPPER_FORMAT + '=' + wrapper; + var wrapper = 'drupal_' + (element_settings.dialogType || 'ajax'); + if (element_settings.dialogRenderer) { + wrapper += '.' + element_settings.dialogRenderer; } + ajax.options.url += Drupal.ajax.WRAPPER_FORMAT + '=' + wrapper; $(ajax.element).on(element_settings.event, function (event) { if (!drupalSettings.ajaxTrustedUrl[ajax.url] && !Drupal.url.isLocal(ajax.url)) { diff --git a/core/modules/layout_builder/src/Controller/AddSectionController.php b/core/modules/layout_builder/src/Controller/AddSectionController.php index 550442a..56af3f2 100644 --- a/core/modules/layout_builder/src/Controller/AddSectionController.php +++ b/core/modules/layout_builder/src/Controller/AddSectionController.php @@ -2,17 +2,21 @@ namespace Drupal\layout_builder\Controller; -use Drupal\Core\Ajax\AjaxResponse; +use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Url; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\RequestStack; /** * Returns responses for Layout Builder routes. */ class AddSectionController implements ContainerInjectionInterface { + use AjaxHelperTrait; use LayoutRebuildTrait; /** @@ -27,9 +31,15 @@ class AddSectionController implements ContainerInjectionInterface { * * @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository * The layout tempstore repository. + * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver + * The class resolver. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) { + public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, ClassResolverInterface $class_resolver, RequestStack $request_stack) { + $this->classResolver = $class_resolver; $this->layoutTempstoreRepository = $layout_tempstore_repository; + $this->requestStack = $request_stack; } /** @@ -37,7 +47,9 @@ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore */ public static function create(ContainerInterface $container) { return new static( - $container->get('layout_builder.tempstore_repository') + $container->get('layout_builder.tempstore_repository'), + $container->get('class_resolver'), + $container->get('request_stack') ); } @@ -51,8 +63,8 @@ public static function create(ContainerInterface $container) { * @param string $plugin_id * The plugin ID of the layout to add. * - * @return \Drupal\Core\Ajax\AjaxResponse - * The render array. + * @return \Symfony\Component\HttpFoundation\Response + * The controller response. */ public function build(EntityInterface $entity, $delta, $plugin_id) { /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */ @@ -64,7 +76,14 @@ public function build(EntityInterface $entity, $delta, $plugin_id) { ]); $this->layoutTempstoreRepository->set($entity); - return $this->rebuildAndClose(new AjaxResponse(), $entity); + + if ($this->isAjax()) { + return $this->rebuildAndClose($entity); + } + else { + $url = Url::fromRoute("entity.{$entity->getEntityTypeId()}.layout", [$entity->getEntityTypeId() => $entity->id()]); + return new RedirectResponse($url->setAbsolute()->toString()); + } } } diff --git a/core/modules/layout_builder/src/Controller/AjaxHelperTrait.php b/core/modules/layout_builder/src/Controller/AjaxHelperTrait.php new file mode 100644 index 0000000..9aee82e --- /dev/null +++ b/core/modules/layout_builder/src/Controller/AjaxHelperTrait.php @@ -0,0 +1,34 @@ +requestStack->getCurrentRequest()->get(MainContentViewSubscriber::WRAPPER_FORMAT), [ + 'drupal_ajax', + 'drupal_dialog', + 'drupal_dialog.off_canvas', + 'drupal_modal', + ]); + } + +} diff --git a/core/modules/layout_builder/src/Controller/ChooseBlockController.php b/core/modules/layout_builder/src/Controller/ChooseBlockController.php index 06ea8ae..7bb5819 100644 --- a/core/modules/layout_builder/src/Controller/ChooseBlockController.php +++ b/core/modules/layout_builder/src/Controller/ChooseBlockController.php @@ -7,12 +7,15 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * Returns responses for Layout Builder routes. */ class ChooseBlockController implements ContainerInjectionInterface { + use AjaxHelperTrait; + /** * The block manager. * @@ -25,9 +28,12 @@ class ChooseBlockController implements ContainerInjectionInterface { * * @param \Drupal\Core\Block\BlockManagerInterface $block_manager * The block manager. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(BlockManagerInterface $block_manager) { + public function __construct(BlockManagerInterface $block_manager, RequestStack $request_stack) { $this->blockManager = $block_manager; + $this->requestStack = $request_stack; } /** @@ -35,7 +41,8 @@ public function __construct(BlockManagerInterface $block_manager) { */ public static function create(ContainerInterface $container) { return new static( - $container->get('plugin.manager.block') + $container->get('plugin.manager.block'), + $container->get('request_stack') ); } @@ -64,7 +71,7 @@ public function build(EntityInterface $entity, $delta, $region) { '#type' => 'table', ]; foreach ($blocks as $block_id => $block) { - $build[$category]['links'][]['data'] = [ + $link = [ '#type' => 'link', '#title' => $block['admin_label'], '#url' => Url::fromRoute('layout_builder.add_block', @@ -76,12 +83,13 @@ public function build(EntityInterface $entity, $delta, $region) { 'plugin_id' => $block_id, ] ), - '#attributes' => [ - 'class' => ['use-ajax'], - 'data-dialog-type' => 'dialog', - 'data-dialog-renderer' => 'off_canvas', - ], ]; + if ($this->isAjax()) { + $link['#attributes']['class'][] = 'use-ajax'; + $link['#attributes']['data-dialog-type'][] = 'dialog'; + $link['#attributes']['data-dialog-renderer'][] = 'off_canvas'; + } + $build[$category]['links'][]['data'] = $link; } } return $build; diff --git a/core/modules/layout_builder/src/Controller/ChooseSectionController.php b/core/modules/layout_builder/src/Controller/ChooseSectionController.php index 7fb138c..6dd89eb 100644 --- a/core/modules/layout_builder/src/Controller/ChooseSectionController.php +++ b/core/modules/layout_builder/src/Controller/ChooseSectionController.php @@ -9,12 +9,14 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * Returns responses for Layout Builder routes. */ class ChooseSectionController implements ContainerInjectionInterface { + use AjaxHelperTrait; use StringTranslationTrait; /** @@ -29,9 +31,12 @@ class ChooseSectionController implements ContainerInjectionInterface { * * @param \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_manager * The layout manager. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(LayoutPluginManagerInterface $layout_manager) { + public function __construct(LayoutPluginManagerInterface $layout_manager, RequestStack $request_stack) { $this->layoutManager = $layout_manager; + $this->requestStack = $request_stack; } /** @@ -39,7 +44,8 @@ public function __construct(LayoutPluginManagerInterface $layout_manager) { */ public static function create(ContainerInterface $container) { return new static( - $container->get('plugin.manager.core.layout') + $container->get('plugin.manager.core.layout'), + $container->get('request_stack') ); } @@ -59,32 +65,31 @@ public function build(EntityInterface $entity, $delta) { $items = []; foreach ($this->layoutManager->getDefinitions() as $plugin_id => $definition) { $layout = $this->layoutManager->createInstance($plugin_id); - $items[] = [ - 'label' => [ - '#type' => 'link', - '#title' => [ - $definition->getIcon(60, 80, 1, 3), - [ - '#type' => 'container', - '#children' => $definition->getLabel(), - ], - ], - '#url' => Url::fromRoute( - $layout instanceof PluginFormInterface ? 'layout_builder.configure_section' : 'layout_builder.add_section', - [ - 'entity_type_id' => $entity->getEntityTypeId(), - 'entity' => $entity->id(), - 'delta' => $delta, - 'plugin_id' => $plugin_id, - ] - ), - '#attributes' => [ - 'class' => ['use-ajax'], - 'data-dialog-type' => 'dialog', - 'data-dialog-renderer' => 'off_canvas', + $item = [ + '#type' => 'link', + '#title' => [ + $definition->getIcon(60, 80, 1, 3), + [ + '#type' => 'container', + '#children' => $definition->getLabel(), ], ], + '#url' => Url::fromRoute( + $layout instanceof PluginFormInterface ? 'layout_builder.configure_section' : 'layout_builder.add_section', + [ + 'entity_type_id' => $entity->getEntityTypeId(), + 'entity' => $entity->id(), + 'delta' => $delta, + 'plugin_id' => $plugin_id, + ] + ), ]; + if ($this->isAjax()) { + $item['#attributes']['class'][] = 'use-ajax'; + $item['#attributes']['data-dialog-type'][] = 'dialog'; + $item['#attributes']['data-dialog-renderer'][] = 'off_canvas'; + } + $items[] = $item; } $output['layouts'] = [ '#type' => 'details', diff --git a/core/modules/layout_builder/src/Controller/LayoutRebuildFormTrait.php b/core/modules/layout_builder/src/Controller/LayoutRebuildFormTrait.php new file mode 100644 index 0000000..c0cfd48 --- /dev/null +++ b/core/modules/layout_builder/src/Controller/LayoutRebuildFormTrait.php @@ -0,0 +1,44 @@ +hasAnyErrors()) { + $form['status_messages'] = [ + '#type' => 'status_messages', + '#weight' => -1000, + ]; + $response = new AjaxResponse(); + $response->addCommand(new ReplaceCommand('[data-drupal-selector="' . $form['#attributes']['data-drupal-selector'] . '"]', $form)); + } + else { + $response = $this->rebuildAndClose($this->entity); + } + return $response; + } + +} diff --git a/core/modules/layout_builder/src/Controller/LayoutRebuildTrait.php b/core/modules/layout_builder/src/Controller/LayoutRebuildTrait.php index 547838f..2a52b38 100644 --- a/core/modules/layout_builder/src/Controller/LayoutRebuildTrait.php +++ b/core/modules/layout_builder/src/Controller/LayoutRebuildTrait.php @@ -4,12 +4,8 @@ use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Ajax\CloseDialogCommand; -use Drupal\Core\Ajax\RedirectCommand; use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\EventSubscriber\MainContentViewSubscriber; -use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Url; /** * Provides AJAX responses to rebuild the Layout Builder. @@ -17,37 +13,15 @@ trait LayoutRebuildTrait { /** - * Submit form dialog #ajax callback. + * The class resolver. * - * @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. - * - * @return \Drupal\Core\Ajax\AjaxResponse - * An AJAX response that display validation error messages or redirects - * to a URL + * @var \Drupal\Core\DependencyInjection\ClassResolverInterface */ - public function ajaxSubmit(array &$form, FormStateInterface $form_state) { - if ($form_state->hasAnyErrors()) { - $form['status_messages'] = [ - '#type' => 'status_messages', - '#weight' => -1000, - ]; - $response = new AjaxResponse(); - $response->addCommand(new ReplaceCommand('[data-drupal-selector="' . $form['#attributes']['data-drupal-selector'] . '"]', $form)); - } - else { - $response = $this->rebuildAndClose(new AjaxResponse(), $this->entity); - } - return $response; - } + protected $classResolver; /** * Rebuilds the layout. * - * @param \Drupal\Core\Ajax\AjaxResponse $response - * The AJAX response. * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * @@ -55,23 +29,15 @@ public function ajaxSubmit(array &$form, FormStateInterface $form_state) { * An AJAX response to either rebuild the layout and close the dialog, or * reload the page. */ - protected function rebuildAndClose(AjaxResponse $response, EntityInterface $entity) { - $response = $this->rebuildLayout($response, $entity); - $url = Url::fromRoute("entity.{$entity->getEntityTypeId()}.layout", [$entity->getEntityTypeId() => $entity->id()]); - if ($this->isDialog()) { - $response->addCommand(new CloseDialogCommand('#drupal-off-canvas')); - } - else { - $response->addCommand(new RedirectCommand($url->setAbsolute()->toString())); - } + protected function rebuildAndClose(EntityInterface $entity) { + $response = $this->rebuildLayout($entity); + $response->addCommand(new CloseDialogCommand('#drupal-off-canvas')); return $response; } /** * Rebuilds the layout. * - * @param \Drupal\Core\Ajax\AjaxResponse $response - * The AJAX response. * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * @@ -79,47 +45,12 @@ protected function rebuildAndClose(AjaxResponse $response, EntityInterface $enti * An AJAX response to either rebuild the layout and close the dialog, or * reload the page. */ - protected function rebuildLayout(AjaxResponse $response, EntityInterface $entity) { - $layout_controller = $this->getClassResolver()->getInstanceFromDefinition(LayoutBuilderController::class); + protected function rebuildLayout(EntityInterface $entity) { + $response = new AjaxResponse(); + $layout_controller = $this->classResolver->getInstanceFromDefinition(LayoutBuilderController::class); $layout = $layout_controller->layout($entity); $response->addCommand(new ReplaceCommand('#layout-builder', $layout)); return $response; } - /** - * Determines if the current request is within a dialog. - * - * @return bool - * TRUE if the current request is within a dialog, FALSE otherwise. - */ - protected function isDialog() { - return $this->getRequest()->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_dialog.off_canvas'; - } - - /** - * Gets the request object. - * - * @return \Symfony\Component\HttpFoundation\Request - * The request object. - */ - protected function getRequest() { - if (!$this->requestStack) { - $this->requestStack = \Drupal::requestStack(); - } - return $this->requestStack->getCurrentRequest(); - } - - /** - * Gets the class resolver. - * - * @return \Drupal\Core\DependencyInjection\ClassResolver - * The class resolver. - */ - protected function getClassResolver() { - if (!$this->classResolver) { - $this->classResolver = \Drupal::classResolver(); - } - return $this->classResolver; - } - } diff --git a/core/modules/layout_builder/src/Controller/MoveBlockController.php b/core/modules/layout_builder/src/Controller/MoveBlockController.php index a30e4d3..ff0d5e8 100644 --- a/core/modules/layout_builder/src/Controller/MoveBlockController.php +++ b/core/modules/layout_builder/src/Controller/MoveBlockController.php @@ -2,18 +2,20 @@ namespace Drupal\layout_builder\Controller; -use Drupal\Core\Ajax\AjaxResponse; +use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; /** * Returns responses for Layout Builder routes. */ class MoveBlockController implements ContainerInjectionInterface { + use AjaxHelperTrait; use LayoutRebuildTrait; /** @@ -28,9 +30,15 @@ class MoveBlockController implements ContainerInjectionInterface { * * @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository * The layout tempstore repository. + * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver + * The class resolver. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) { + public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, ClassResolverInterface $class_resolver, RequestStack $request_stack) { $this->layoutTempstoreRepository = $layout_tempstore_repository; + $this->classResolver = $class_resolver; + $this->requestStack = $request_stack; } /** @@ -38,7 +46,9 @@ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore */ public static function create(ContainerInterface $container) { return new static( - $container->get('layout_builder.tempstore_repository') + $container->get('layout_builder.tempstore_repository'), + $container->get('class_resolver'), + $container->get('request_stack') ); } @@ -85,7 +95,7 @@ public function build(EntityInterface $entity, Request $request) { $field->section = array_filter($values); $this->layoutTempstoreRepository->set($entity); - return $this->rebuildLayout(new AjaxResponse(), $entity); + return $this->rebuildLayout($entity); } } diff --git a/core/modules/layout_builder/src/Form/ConfigureBlockForm.php b/core/modules/layout_builder/src/Form/ConfigureBlockForm.php index c004a32..8a47a5f 100644 --- a/core/modules/layout_builder/src/Form/ConfigureBlockForm.php +++ b/core/modules/layout_builder/src/Form/ConfigureBlockForm.php @@ -15,9 +15,10 @@ use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\Plugin\PluginFormFactoryInterface; use Drupal\Core\Plugin\PluginWithFormsInterface; -use Drupal\layout_builder\Controller\LayoutRebuildTrait; +use Drupal\layout_builder\Controller\LayoutRebuildFormTrait; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * Provides a form to configure a block. @@ -25,7 +26,7 @@ class ConfigureBlockForm extends FormBase { use ContextAwarePluginAssignmentTrait; - use LayoutRebuildTrait; + use LayoutRebuildFormTrait; /** * The plugin being configured. @@ -63,13 +64,6 @@ class ConfigureBlockForm extends FormBase { protected $uuid; /** - * The class resolver. - * - * @var \Drupal\Core\DependencyInjection\ClassResolver - */ - protected $classResolver; - - /** * The plugin form manager. * * @var \Drupal\Core\Plugin\PluginFormFactoryInterface @@ -110,15 +104,18 @@ class ConfigureBlockForm extends FormBase { * The UUID generator. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver * The class resolver. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. * @param \Drupal\Core\Plugin\PluginFormFactoryInterface $plugin_form_manager * The plugin form manager. */ - public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, ContextRepositoryInterface $context_repository, BlockManagerInterface $block_manager, UuidInterface $uuid, ClassResolverInterface $class_resolver, PluginFormFactoryInterface $plugin_form_manager) { + public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, ContextRepositoryInterface $context_repository, BlockManagerInterface $block_manager, UuidInterface $uuid, ClassResolverInterface $class_resolver, RequestStack $request_stack, PluginFormFactoryInterface $plugin_form_manager) { $this->layoutTempstoreRepository = $layout_tempstore_repository; $this->contextRepository = $context_repository; $this->blockManager = $block_manager; $this->uuid = $uuid; $this->classResolver = $class_resolver; + $this->requestStack = $request_stack; $this->pluginFormFactory = $plugin_form_manager; } @@ -132,6 +129,7 @@ public static function create(ContainerInterface $container) { $container->get('plugin.manager.block'), $container->get('uuid'), $container->get('class_resolver'), + $container->get('request_stack'), $container->get('plugin_form.factory') ); } @@ -194,12 +192,10 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt '#type' => 'submit', '#value' => $uuid ? $this->t('Update') : $this->t('Add Block'), '#button_type' => 'primary', - '#ajax' => [ - 'callback' => '::ajaxSubmit', - ], ]; - - $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; + if ($this->isAjax()) { + $form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit'; + } return $form; } diff --git a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php index 23bf9a7..082eaec 100644 --- a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php +++ b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php @@ -8,16 +8,17 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\SubformState; use Drupal\Core\Layout\LayoutPluginManagerInterface; -use Drupal\layout_builder\Controller\LayoutRebuildTrait; +use Drupal\layout_builder\Controller\LayoutRebuildFormTrait; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * Provides a form for configuring a layout section. */ class ConfigureSectionForm extends FormBase { - use LayoutRebuildTrait; + use LayoutRebuildFormTrait; /** * The layout tempstore repository. @@ -34,13 +35,6 @@ class ConfigureSectionForm extends FormBase { protected $layout; /** - * The class resolver. - * - * @var \Drupal\Core\DependencyInjection\ClassResolverInterface - */ - protected $classResolver; - - /** * The layout manager. * * @var \Drupal\Core\Layout\LayoutPluginManagerInterface @@ -77,11 +71,14 @@ class ConfigureSectionForm extends FormBase { * The layout manager. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver * The class resolver. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, LayoutPluginManagerInterface $layout_manager, ClassResolverInterface $class_resolver) { + public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, LayoutPluginManagerInterface $layout_manager, ClassResolverInterface $class_resolver, RequestStack $request_stack) { $this->layoutTempstoreRepository = $layout_tempstore_repository; $this->layoutManager = $layout_manager; $this->classResolver = $class_resolver; + $this->requestStack = $request_stack; } /** @@ -91,7 +88,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('layout_builder.tempstore_repository'), $container->get('plugin.manager.core.layout'), - $container->get('class_resolver') + $container->get('class_resolver'), + $container->get('request_stack') ); } @@ -128,12 +126,10 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt '#type' => 'submit', '#value' => $this->isUpdate ? $this->t('Update') : $this->t('Add section'), '#button_type' => 'primary', - '#ajax' => [ - 'callback' => '::ajaxSubmit', - ], ]; - - $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; + if ($this->isAjax()) { + $form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit'; + } return $form; } diff --git a/core/modules/layout_builder/src/Form/LayoutRebuildConfirmFormBase.php b/core/modules/layout_builder/src/Form/LayoutRebuildConfirmFormBase.php index cbe0ea1..3537a8b 100644 --- a/core/modules/layout_builder/src/Form/LayoutRebuildConfirmFormBase.php +++ b/core/modules/layout_builder/src/Form/LayoutRebuildConfirmFormBase.php @@ -2,20 +2,22 @@ namespace Drupal\layout_builder\Form; +use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Url; -use Drupal\layout_builder\Controller\LayoutRebuildTrait; +use Drupal\layout_builder\Controller\LayoutRebuildFormTrait; use Drupal\layout_builder\LayoutTempstoreRepositoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RequestStack; /** * Provides a base class for confirmation forms that rebuild the Layout Builder. */ abstract class LayoutRebuildConfirmFormBase extends ConfirmFormBase { - use LayoutRebuildTrait; + use LayoutRebuildFormTrait; /** * The layout tempstore repository. @@ -43,9 +45,15 @@ * * @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository * The layout tempstore repository. + * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver + * The class resolver. + * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack + * The request stack. */ - public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) { + public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, ClassResolverInterface $class_resolver, RequestStack $request_stack) { $this->layoutTempstoreRepository = $layout_tempstore_repository; + $this->classResolver = $class_resolver; + $this->requestStack = $request_stack; } /** @@ -53,7 +61,9 @@ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore */ public static function create(ContainerInterface $container) { return new static( - $container->get('layout_builder.tempstore_repository') + $container->get('layout_builder.tempstore_repository'), + $container->get('class_resolver'), + $container->get('request_stack') ); } @@ -73,10 +83,11 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt $form = parent::buildForm($form, $form_state); - $form['#attached']['library'][] = 'core/drupal.dialog.ajax'; - $form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit'; + if ($this->isAjax()) { + $form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit'; + $form['actions']['cancel']['#attributes']['class'][] = 'dialog-cancel'; + } - $form['actions']['cancel']['#attributes']['class'][] = 'dialog-cancel'; return $form; } diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderTest.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderTest.php index 8d9008f..11f5874 100644 --- a/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderTest.php +++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderTest.php @@ -13,6 +13,8 @@ */ class LayoutBuilderTest extends JavascriptTestBase { + use PageReloadHelperTrait; + /** * {@inheritdoc} */ @@ -86,6 +88,7 @@ public function test() { // Enter the layout editing mode. $this->clickLink('Layout'); + $this->markCurrentPage(); $assert_session->linkExists('Add Section'); $assert_session->linkNotExists('Add Block'); @@ -122,6 +125,7 @@ public function test() { $assert_session->addressEquals('node/1/layout'); $assert_session->pageTextContains('Powered by Drupal'); $assert_session->pageTextContains('This is the label'); + $this->assertPageNotReloaded(); // Until the layout is saved, the new block is not visible on the node page. $this->drupalGet('node/1'); @@ -140,6 +144,8 @@ public function test() { // Drag one block from one region to another. $this->drupalGet('node/1/layout'); + $this->markCurrentPage(); + $this->clickLink('Add Section'); $assert_session->assertWaitOnAjaxRequest(); @@ -154,10 +160,13 @@ public function test() { // Ensure the drag succeeded. $assert_session->elementExists('css', '.layout__region--second .block-system-powered-by-block'); $assert_session->elementTextContains('css', '.layout__region--second', 'Powered by Drupal'); + $this->assertPageNotReloaded(); + // Ensure the drag persisted after reload. $this->drupalGet('node/1/layout'); $assert_session->elementExists('css', '.layout__region--second .block-system-powered-by-block'); $assert_session->elementTextContains('css', '.layout__region--second', 'Powered by Drupal'); + // Ensure the drag persisted after save. $this->clickLink('Save Layout'); $assert_session->elementExists('css', '.layout__region--second .block-system-powered-by-block'); @@ -165,7 +174,8 @@ public function test() { // Configure a block. $this->drupalGet('node/1/layout'); - $assert_session->assertWaitOnAjaxRequest(); + $this->markCurrentPage(); + $this->toggleContextualTriggerVisibility('.block-system-powered-by-block'); $assert_session->assertWaitOnAjaxRequest(); $page->find('css', '.block-system-powered-by-block .contextual .trigger')->click(); @@ -186,8 +196,6 @@ public function test() { $assert_session->pageTextNotContains('This is the label'); // Remove a block. - $this->drupalGet('node/1/layout'); - $this->toggleContextualTriggerVisibility('.block-system-powered-by-block'); $page->find('css', '.block-system-powered-by-block .contextual .trigger')->click(); $this->clickLink('Remove block'); @@ -201,12 +209,15 @@ public function test() { $assert_session->pageTextNotContains('Powered by Drupal'); $assert_session->linkExists('Add Block'); $assert_session->addressEquals('node/1/layout'); + $this->assertPageNotReloaded(); $this->clickLink('Save Layout'); $assert_session->elementExists('css', '.layout'); // Test deriver-based blocks. $this->drupalGet('node/1/layout'); + $this->markCurrentPage(); + $this->clickLink('Add Block'); $assert_session->assertWaitOnAjaxRequest(); @@ -232,6 +243,8 @@ public function test() { $assert_session->pageTextNotContains('This is the block content'); $assert_session->linkNotExists('Add Block'); + $this->assertPageNotReloaded(); + $this->clickLink('Save Layout'); $assert_session->elementNotExists('css', '.layout'); } @@ -244,6 +257,8 @@ public function testConfigurableLayouts() { $page = $this->getSession()->getPage(); $this->drupalGet('node/1/layout'); + $this->markCurrentPage(); + $this->clickLink('Add Section'); $assert_session->assertWaitOnAjaxRequest(); $assert_session->elementExists('css', '#drupal-off-canvas'); @@ -274,6 +289,34 @@ public function testConfigurableLayouts() { $assert_session->assertWaitOnAjaxRequest(); $assert_session->elementNotExists('css', '#drupal-off-canvas'); $assert_session->pageTextContains('Test setting value'); + $this->assertPageNotReloaded(); + } + + /** + * Tests bypassing the Off Canvas dialog. + */ + public function testLayoutNoDialog() { + $assert_session = $this->assertSession(); + $page = $this->getSession()->getPage(); + + // Set up a layout with one section. + $this->drupalGet('node/1/layout'); + $this->clickLink('Add Section'); + $assert_session->assertWaitOnAjaxRequest(); + $this->clickLink('One column'); + $assert_session->assertWaitOnAjaxRequest(); + + // Test bypassing the Off Canvas dialog by visiting the URL directly. + $this->drupalGet('layout_builder/add/block/node/1/0/content/system_powered_by_block'); + $assert_session->elementNotExists('css', '#drupal-off-canvas'); + $page->fillField('settings[label]', 'The block label'); + $page->fillField('settings[label_display]', TRUE); + $page->pressButton('Add Block'); + $assert_session->assertWaitOnAjaxRequest(); + + $assert_session->addressEquals('node/1/layout'); + $assert_session->pageTextContains('Powered by Drupal'); + $assert_session->pageTextContains('The block label'); } /** diff --git a/core/modules/layout_builder/tests/src/FunctionalJavascript/PageReloadHelperTrait.php b/core/modules/layout_builder/tests/src/FunctionalJavascript/PageReloadHelperTrait.php new file mode 100644 index 0000000..c6f0b49 --- /dev/null +++ b/core/modules/layout_builder/tests/src/FunctionalJavascript/PageReloadHelperTrait.php @@ -0,0 +1,41 @@ +pageReloadMarker = $this->randomMachineName(); + $this->getSession()->executeScript('document.body.appendChild(document.createTextNode("' . $this->pageReloadMarker . '"));'); + } + + /** + * Asserts that the page has not been reloaded. + */ + protected function assertPageNotReloaded() { + $this->assertSession()->pageTextContains($this->pageReloadMarker); + } + + /** + * Asserts that the page has been reloaded. + */ + protected function assertPageReloaded() { + $this->assertSession()->pageTextNotContains($this->pageReloadMarker); + } + +}