From 6fca4655e37bdf12680917d063fce5fb96c69908 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Thu, 7 Mar 2024 10:26:20 +0000 Subject: [PATCH] Issue #3408738 by omkar.podey, srishtiiee, kunal.sachdev, lauriii, Utkarsh_33, arisen, smustgrave, Wim Leers, catch, benjifisher: Create a new OpenModalDialogWithUrl command --- .../Core/Ajax/OpenModalDialogWithUrl.php | 57 +++++++++++++++++++ core/misc/dialog/dialog.ajax.js | 23 ++++++++ .../ajax_test/src/Form/AjaxTestDialogForm.php | 30 +++++++++- .../Ajax/DialogTest.php | 17 ++++++ 4 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 core/lib/Drupal/Core/Ajax/OpenModalDialogWithUrl.php diff --git a/core/lib/Drupal/Core/Ajax/OpenModalDialogWithUrl.php b/core/lib/Drupal/Core/Ajax/OpenModalDialogWithUrl.php new file mode 100644 index 000000000000..bd91415f810f --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/OpenModalDialogWithUrl.php @@ -0,0 +1,57 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Core\Ajax; + +use Drupal\Component\Utility\UrlHelper; + +/** + * Provides an AJAX command for opening a modal with URL. + * + * OpenDialogCommand is a similar class which opens modals but works + * differently as it needs all data to be passed through dialogOptions while + * OpenModalDialogWithUrl fetches the data from routing info of the URL. + * + * @see \Drupal\Core\Ajax\OpenDialogCommand + */ +class OpenModalDialogWithUrl implements CommandInterface { + + /** + * Constructs a OpenModalDialogWithUrl object. + * + * @param string $url + * Only Internal URLs or URLs with the same domain and base path are + * allowed. + * @param array $settings + * The dialog settings. + */ + public function __construct( + protected string $url, + protected array $settings, + ) {} + + /** + * {@inheritdoc} + */ + public function render() { + // @see \Drupal\Core\Routing\LocalAwareRedirectResponseTrait::isLocal() + if (!UrlHelper::isExternal($this->url) || UrlHelper::externalIsLocal($this->url, $this->getBaseURL())) { + return [ + 'command' => 'openModalDialogWithUrl', + 'url' => $this->url, + 'dialogOptions' => $this->settings, + ]; + } + throw new \LogicException('External URLs are not allowed.'); + } + + /** + * Gets the complete base URL. + */ + private function getBaseUrl() { + $requestContext = \Drupal::service('router.request_context'); + return $requestContext->getCompleteBaseUrl(); + } + +} diff --git a/core/misc/dialog/dialog.ajax.js b/core/misc/dialog/dialog.ajax.js index d1fb5b7e94b2..f8a1b5f90cf8 100644 --- a/core/misc/dialog/dialog.ajax.js +++ b/core/misc/dialog/dialog.ajax.js @@ -291,4 +291,27 @@ $(window).on('dialog:beforeclose', (e, dialog, $element) => { $element.off('.dialog'); }); + + /** + * Ajax command to open URL in a modal dialog. + * + * @param {Drupal.Ajax} [ajax] + * An Ajax object. + * @param {object} response + * The Ajax response. + */ + Drupal.AjaxCommands.prototype.openModalDialogWithUrl = function ( + ajax, + response, + ) { + const dialogOptions = response.dialogOptions || {}; + const elementSettings = { + progress: { type: 'throbber' }, + dialogType: 'modal', + dialog: dialogOptions, + url: response.url, + httpMethod: 'GET', + }; + Drupal.ajax(elementSettings).execute(); + }; })(jQuery, Drupal, window.tabbable); diff --git a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php index 6fa939c21703..fd31dc6ed946 100644 --- a/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php +++ b/core/modules/system/tests/modules/ajax_test/src/Form/AjaxTestDialogForm.php @@ -5,9 +5,11 @@ use Drupal\ajax_test\Controller\AjaxTestController; use Drupal\Core\Form\FormBase; use Drupal\Core\Ajax\AjaxResponse; -use Drupal\Core\Ajax\OpenModalDialogCommand; use Drupal\Core\Ajax\OpenDialogCommand; +use Drupal\Core\Ajax\OpenModalDialogCommand; +use Drupal\Core\Ajax\OpenModalDialogWithUrl; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; /** * Dummy form for testing DialogRenderer with _form routes. @@ -43,6 +45,14 @@ public function buildForm(array $form, FormStateInterface $form_state) { 'callback' => '::nonModal', ], ]; + $form['button3'] = [ + '#type' => 'submit', + '#name' => 'button3', + '#value' => 'Button 3 (modal from url)', + '#ajax' => [ + 'callback' => '::modalFromUrl', + ], + ]; return $form; } @@ -68,6 +78,13 @@ public function modal(&$form, FormStateInterface $form_state) { return $this->dialog(TRUE); } + /** + * AJAX callback handler for Url modal, AjaxTestDialogForm. + */ + public function modalFromUrl(&$form, FormStateInterface $form_state) { + return $this->dialog(TRUE, TRUE); + } + /** * AJAX callback handler for AjaxTestDialogForm. */ @@ -80,11 +97,13 @@ public function nonModal(&$form, FormStateInterface $form_state) { * * @param bool $is_modal * (optional) TRUE if modal, FALSE if plain dialog. Defaults to FALSE. + * @param bool $is_url + * (optional) True if modal is from a URL, Defaults to FALSE. * * @return \Drupal\Core\Ajax\AjaxResponse * An ajax response object. */ - protected function dialog($is_modal = FALSE) { + protected function dialog(bool $is_modal = FALSE, bool $is_url = FALSE) { $content = AjaxTestController::dialogContents(); $response = new AjaxResponse(); $title = $this->t('AJAX Dialog & contents'); @@ -94,7 +113,12 @@ protected function dialog($is_modal = FALSE) { $content['#attached']['library'][] = 'core/drupal.dialog.ajax'; if ($is_modal) { - $response->addCommand(new OpenModalDialogCommand($title, $content)); + if ($is_url) { + $response->addCommand(new OpenModalDialogWithUrl(Url::fromRoute('ajax_test.dialog_form')->toString(), [])); + } + else { + $response->addCommand(new OpenModalDialogCommand($title, $content)); + } } else { $selector = '#ajax-test-dialog-wrapper-1'; diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/DialogTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/DialogTest.php index 3d9f82bfb007..7438e1af1ca1 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/DialogTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/DialogTest.php @@ -5,6 +5,7 @@ namespace Drupal\FunctionalJavascriptTests\Ajax; use Drupal\ajax_test\Controller\AjaxTestController; +use Drupal\Core\Ajax\OpenModalDialogWithUrl; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; /** @@ -129,6 +130,22 @@ public function testDialog() { // Use a link to close the panel opened by button 2. $this->getSession()->getPage()->clickLink('Link 4 (close non-modal if open)'); + // Test dialogs opened using OpenModalDialogWithUrl. + $this->getSession()->getPage()->findButton('Button 3 (modal from url)')->press(); + // Check that title was fetched properly. + // @see \Drupal\ajax_test\Form\AjaxTestDialogForm::dialog. + $form_dialog_title = $this->assertSession()->waitForElement('css', "span.ui-dialog-title:contains('Ajax Form contents')"); + $this->assertNotNull($form_dialog_title, 'Dialog form has the expected title.'); + $button1_dialog->findButton('Close')->press(); + // Test external URL. + $dialog_obj = new OpenModalDialogWithUrl('http://example.com', []); + try { + $dialog_obj->render(); + } + catch (\LogicException $e) { + $this->assertEquals('External URLs are not allowed.', $e->getMessage()); + } + // Form modal. $this->clickLink('Link 5 (form)'); // Two links have been clicked in succession - This time wait for a change -- GitLab