diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc index 212dff6..31c099e 100644 --- a/core/includes/ajax.inc +++ b/core/includes/ajax.inc @@ -473,13 +473,8 @@ function ajax_prepare_response($page_callback_result) { // manipulation method is used. The method used is specified by // #ajax['method']. The default method is 'replaceWith', which completely // replaces the old wrapper element and its content with the new HTML. - // Since this is the primary response content returned to the client, we - // also attach the page title. It is up to client code to determine if and - // how to display that. For example, if the requesting element is configured - // to display the response in a dialog (via #ajax['dialog']), it can use - // this for the dialog title. $html = is_string($page_callback_result) ? $page_callback_result : drupal_render($page_callback_result); - $commands[] = ajax_command_insert(NULL, $html) + array('title' => drupal_get_title()); + $commands[] = ajax_command_insert(NULL, $html); // Add the status messages inside the new content's wrapper element, so that // on subsequent Ajax requests, it is treated as old content. $commands[] = ajax_command_prepend(NULL, theme('status_messages')); @@ -594,9 +589,6 @@ function ajax_pre_render_element($element) { if (isset($element['#ajax']['event'])) { $element['#attached']['library'][] = array('system', 'jquery.form'); $element['#attached']['library'][] = array('system', 'drupal.ajax'); - if (!empty($element['#ajax']['dialog'])) { - $element['#attached']['library'][] = array('system', 'drupal.dialog'); - } $settings = $element['#ajax']; diff --git a/core/lib/Drupal/Core/Ajax/CloseDialogCommand.php b/core/lib/Drupal/Core/Ajax/CloseDialogCommand.php new file mode 100644 index 0000000..1788ceb --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/CloseDialogCommand.php @@ -0,0 +1,41 @@ +selector = $selector ? $selector : '#drupal-modal'; + } + + /** + * Implements \Drupal\Core\Ajax\CommandInterface::render(). + */ + public function render() { + return array( + 'command' => 'closeDialog', + 'selector' => $this->selector, + ); + } +} diff --git a/core/lib/Drupal/Core/Ajax/CloseModalDialogCommand.php b/core/lib/Drupal/Core/Ajax/CloseModalDialogCommand.php new file mode 100644 index 0000000..0897700 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/CloseModalDialogCommand.php @@ -0,0 +1,22 @@ +selector = '#drupal-modal'; + } +} diff --git a/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php b/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php new file mode 100644 index 0000000..45115df --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php @@ -0,0 +1,139 @@ + $title); + $this->selector = $selector; + $this->html = $html; + $this->dialogOptions = $dialog_options; + $this->settings = $settings; + } + + /** + * Returns the dialog options. + * + * @return array + */ + public function getDialogOptions() { + return $this->dialogOptions; + } + + /** + * Sets the dialog options array. + * + * @param array $dialog_options + * Options to be passed to the dialog implementation. Any jQuery UI option + * can be used. See http://api.jqueryui.com/dialog. + */ + public function setDialogOptions($dialog_options) { + $this->dialogOptions = $dialog_options; + } + + /** + * Sets a single dialog option value. + * + * @param string $key + * Key of the dialog option. Any jQuery UI option can be used. + * See http://api.jqueryui.com/dialog. + * @param mixed $value + * Option to be passed to the dialog implementation. + */ + public function setDialogOption($key, $value) { + $this->dialogOptions[$key] = $value; + } + + /** + * Sets the dialog title (an alias of setDialogOptions). + * + * @param string $title + * The new title of the dialog. + */ + public function setDialogTitle($title) { + $this->setDialogOptions('title', $title); + } + + /** + * Implements \Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + // Add the library for handling the dialog in the response. + drupal_add_library('system', 'drupal.dialog.ajax'); + + // For consistency ensure the modal option is set to TRUE or FALSE. + $this->dialogOptions['modal'] = isset($this->dialogOptions['modal']) && $this->dialogOptions['modal']; + return array( + 'command' => 'openDialog', + 'selector' => $this->selector, + 'settings' => $this->settings, + 'data' => $this->html, + 'dialogOptions' => $this->dialogOptions, + ); + } +} diff --git a/core/lib/Drupal/Core/Ajax/OpenModalDialogCommand.php b/core/lib/Drupal/Core/Ajax/OpenModalDialogCommand.php new file mode 100644 index 0000000..c78406e --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/OpenModalDialogCommand.php @@ -0,0 +1,40 @@ +url = $url; + } + + /** + * Implements \Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + return array( + 'command' => 'redirect', + 'url' => $this->url, + ); + } + +} diff --git a/core/lib/Drupal/Core/Ajax/SetDialogOptionCommand.php b/core/lib/Drupal/Core/Ajax/SetDialogOptionCommand.php new file mode 100644 index 0000000..088c63b --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/SetDialogOptionCommand.php @@ -0,0 +1,66 @@ +selector = $selector ? $selector : '#drupal-modal'; + $this->optionName = $option_name; + $this->optionValue = $option_value; + } + + /** + * Implements \Drupal\Core\Ajax\CommandInterface::render(). + */ + public function render() { + return array( + 'command' => 'setDialogOption', + 'selector' => $this->selector, + 'optionName' => $this->optionName, + 'optionValue' => $this->optionValue, + ); + } + +} diff --git a/core/lib/Drupal/Core/Ajax/SetDialogTitleCommand.php b/core/lib/Drupal/Core/Ajax/SetDialogTitleCommand.php new file mode 100644 index 0000000..b928e1e --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/SetDialogTitleCommand.php @@ -0,0 +1,31 @@ +selector = $selector ? $selector : '#drupal-modal'; + $this->optionName = 'title'; + $this->optionValue = $title; + } +} diff --git a/core/misc/ajax.js b/core/misc/ajax.js index 7ca4463..1b5d0ca 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -126,15 +126,6 @@ Drupal.ajax = function (base, element, element_settings) { this.wrapper = '#' + this.wrapper; } - // For Ajax responses that are wanted in a dialog, use the needed method. - // If wanted in a modal dialog, also use the needed wrapper. - if (this.dialog) { - this.method = 'html'; - if (this.dialog.modal) { - this.wrapper = '#drupal-modal'; - } - } - this.element = element; this.element_settings = element_settings; @@ -561,19 +552,6 @@ Drupal.ajax.prototype.commands = { // Add the new content to the page. wrapper[method](new_content); - // If the requesting object wanted the response in a dialog, open that - // dialog. However, a single server response can include multiple insert - // commands (e.g., one for the primary content and another one for status - // messages), but we only want to open the dialog once, so we assume that - // only commands with a title property are dialog eligible. - // @todo Consider whether this is overloading title inappropriately, and - // if so, find another way to determine dialog eligibility. - if (ajax.dialog && ('title' in response)) { - var dialogOptions = $.extend({title: response.title}, ajax.dialog); - var dialog = Drupal.dialog(wrapper, dialogOptions); - ajax.dialog.modal ? dialog.showModal() : dialog.show(); - } - // Immediately hide the new content if we're using any effects. if (effect.showEffect !== 'show') { new_content.hide(); @@ -629,6 +607,13 @@ Drupal.ajax.prototype.commands = { }, /** + * Command to set the window.location, redirecting the browser. + */ + redirect: function (ajax, response, status) { + window.location = response.url; + }, + + /** * Command to provide the jQuery css() function. */ css: function (ajax, response, status) { diff --git a/core/misc/dialog.ajax.js b/core/misc/dialog.ajax.js new file mode 100644 index 0000000..90c9d26 --- /dev/null +++ b/core/misc/dialog.ajax.js @@ -0,0 +1,96 @@ +/** + * @file + * Extends the Drupal AJAX functionality to integrate the dialog API. + */ + +(function ($, Drupal) { + + "use strict"; + + Drupal.behaviors.dialog = { + attach: function () { + // Provide a known 'drupal-modal' DOM element for Drupal-based modal + // dialogs. Non-modal dialogs are responsible for creating their own + // elements, since there can be multiple non-modal dialogs at a time. + if (!$('#drupal-modal').length) { + $('
').hide().appendTo('body'); + } + } + }; + + /** + * Command to open a dialog. + */ + Drupal.ajax.prototype.commands.openDialog = function (ajax, response, status) { + if (!response.selector) { + return false; + } + var $dialog = $(response.selector); + if (!$dialog.length) { + // Create the element if needed. + $dialog = $('
').appendTo('body'); + } + // Set up the wrapper, if there isn't one. + if (!ajax.wrapper) { + ajax.wrapper = $dialog.attr('id'); + } + + // Use the ajax.js insert command to populate the dialog contents. + response.command = 'insert'; + response.method = 'html'; + ajax.commands.insert(ajax, response, status); + + // Open the dialog itself. + response.dialogOptions = response.dialogOptions || {}; + var dialog = Drupal.dialog($dialog, response.dialogOptions); + if (response.dialogOptions.modal) { + dialog.showModal(); + } + else { + dialog.show(); + } + }; + + /** + * Command to close a dialog. + * + * If no selector is given, it defaults to trying to close the modal. + */ + Drupal.ajax.prototype.commands.closeDialog = function (ajax, response, status) { + var $dialog = $(response.selector); + if ($dialog.length) { + Drupal.dialog($dialog).close(); + } + }; + + /** + * Command to set a dialog property. + * + * jQuery UI specific way of setting dialog options. + */ + Drupal.ajax.prototype.commands.setDialogOption = function (ajax, response, status) { + var $dialog = $(response.selector); + if ($dialog.length) { + $dialog.dialog('option', response.optionName, response.optionValue); + } + }; + + /** + * Binds a listener on dialog creation to handle the cancel link. + */ + $(window).on('dialog:aftercreate', function (e, dialog, $element, settings) { + $element.on('click.dialog', '.dialog-cancel', function (e) { + dialog.close('cancel'); + e.preventDefault(); + e.stopPropagation(); + }); + }); + + /** + * Removes all 'dialog' listeners. + */ + $(window).on('dialog:beforeclose', function (e, dialog, $element) { + $element.off('.dialog'); + }); + +})(jQuery, Drupal); diff --git a/core/misc/dialog.js b/core/misc/dialog.js index a0849bc..62cbeb1 100644 --- a/core/misc/dialog.js +++ b/core/misc/dialog.js @@ -16,17 +16,6 @@ drupalSettings.dialog = { } }; -Drupal.behaviors.dialog = { - attach: function () { - // Provide a known 'drupal-modal' dom element for Drupal code to use for - // modal dialogs. Since there can be multiple non-modal dialogs at a time, - // it is the responsibility of calling code to create the elements it needs. - if (!$('#drupal-modal').length) { - $('
').hide().appendTo('body'); - } - } -}; - Drupal.dialog = function (element, options) { function openDialog (settings) { @@ -63,22 +52,4 @@ Drupal.dialog = function (element, options) { return dialog; }; -/** - * Binds a listener on dialog creation to handle the cancel link. - */ -$(window).on('dialog:aftercreate', function (e, dialog, $element, settings) { - $element.on('click.dialog', '.dialog-cancel', function (e) { - dialog.close('cancel'); - e.preventDefault(); - e.stopPropagation(); - }); -}); - -/** - * Removes all 'dialog' listeners. - */ -$(window).on('dialog:beforeclose', function (e, dialog, $element) { - $element.off('.dialog'); -}); - })(jQuery, Drupal, drupalSettings); diff --git a/core/modules/config/config.admin.inc b/core/modules/config/config.admin.inc index 718c773..e81c582 100644 --- a/core/modules/config/config.admin.inc +++ b/core/modules/config/config.admin.inc @@ -6,6 +6,8 @@ */ use Drupal\Core\Config\StorageInterface; +use Drupal\Core\Ajax\AjaxResponse; +use Drupal\Core\Ajax\OpenModalDialogCommand; /** * Helper function to construct the storage changes in a configuration synchronization form. @@ -37,6 +39,9 @@ function config_admin_sync_form(array &$form, array &$form_state, StorageInterfa return $form; } + // Add the AJAX library to the form for dialog support. + $form['#attached']['library'][] = array('system', 'drupal.ajax'); + foreach ($config_changes as $config_change_type => $config_files) { if (empty($config_files)) { continue; @@ -70,7 +75,9 @@ function config_admin_sync_form(array &$form, array &$form_state, StorageInterfa $links['view_diff'] = array( 'title' => t('View differences'), 'href' => 'admin/config/development/sync/diff/' . $config_file, - 'ajax' => array('dialog' => array('modal' =>TRUE, 'width' => '700px')), + 'attributes' => array( + 'class' => array('use-ajax'), + ), ); $form[$config_change_type]['list']['#rows'][] = array( 'name' => $config_file, @@ -149,12 +156,6 @@ function config_admin_diff_page($config_file) { // Add the CSS for the inline diff. $output['#attached']['css'][] = drupal_get_path('module', 'system') . '/system.diff.css'; - $output['title'] = array( - '#theme' => 'html_tag', - '#tag' => 'h3', - '#value' => t('View changes of @config_file', array('@config_file' => $config_file)), - ); - $diff = config_diff($target_storage, $source_storage, $config_file); $formatter = new DrupalDiffFormatter(); $formatter->show_header = FALSE; @@ -175,10 +176,31 @@ function config_admin_diff_page($config_file) { '#type' => 'link', '#title' => "Back to 'Synchronize configuration' page.", '#href' => 'admin/config/development/sync', - '#attributes' => array( - 'class' => array('dialog-cancel'), - ), ); + $title = t('View changes of @config_file', array('@config_file' => $config_file)); + + // Return AJAX requests as a dialog. + // @todo: Set up separate content callbacks for the non-JS and dialog versions + // of this page using the router system. See http://drupal.org/node/1944472. + if (Drupal::service('request')->isXmlHttpRequest()) { + // Add class to the close link. + $output['back']['#attributes']['class'][] = 'dialog-cancel'; + + $dialog_content = drupal_render($output); + $response = new AjaxResponse(); + $response->addCommand(new OpenModalDialogCommand($title, $dialog_content, array('width' => '700'))); + return $response; + } + // Otherwise show the page title as an element. + else { + $output['title'] = array( + '#theme' => 'html_tag', + '#tag' => 'h3', + '#value' => $title, + '#weight' => -10, + ); + } + return $output; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/AjaxCommandsUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/AjaxCommandsUnitTest.php index 2e6c421..9247297 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Ajax/AjaxCommandsUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/AjaxCommandsUnitTest.php @@ -7,7 +7,7 @@ namespace Drupal\system\Tests\Ajax; -use Drupal\simpletest\UnitTestBase; +use Drupal\simpletest\DrupalUnitTestBase; use Drupal\Core\Ajax\AddCssCommand; use Drupal\Core\Ajax\AfterCommand; use Drupal\Core\Ajax\AlertCommand; @@ -24,11 +24,18 @@ use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Ajax\RestripeCommand; use Drupal\Core\Ajax\SettingsCommand; +use Drupal\Core\Ajax\OpenDialogCommand; +use Drupal\Core\Ajax\OpenModalDialogCommand; +use Drupal\Core\Ajax\CloseDialogCommand; +use Drupal\Core\Ajax\CloseModalDialogCommand; +use Drupal\Core\Ajax\SetDialogOptionCommand; +use Drupal\Core\Ajax\SetDialogTitleCommand; +use Drupal\Core\Ajax\RedirectCommand; /** * Tests for all AJAX Commands. */ -class AjaxCommandsUnitTest extends UnitTestBase { +class AjaxCommandsUnitTest extends DrupalUnitTestBase { public static function getInfo() { return array( @@ -305,5 +312,121 @@ function testSettingsCommand() { $this->assertEqual($command->render(), $expected, 'SettingsCommand::render() returns a proper array.'); } -} + /** + * Tests that OpenDialogCommand objects can be constructed and rendered. + */ + function testOpenDialogCommand() { + $command = new OpenDialogCommand('#some-dialog', 'Title', '

Text!

', array( + 'url' => FALSE, + 'width' => 500, + )); + + $expected = array( + 'command' => 'openDialog', + 'selector' => '#some-dialog', + 'settings' => NULL, + 'data' => '

Text!

', + 'dialogOptions' => array( + 'url' => FALSE, + 'width' => 500, + 'title' => 'Title', + 'modal' => FALSE, + ), + ); + $this->assertEqual($command->render(), $expected, 'OpenDialogCommand::render() returns a proper array.'); + } + + /** + * Tests that OpenModalDialogCommand objects can be constructed and rendered. + */ + function testOpenModalDialogCommand() { + $command = new OpenModalDialogCommand('Title', '

Text!

', array( + 'url' => 'example', + 'width' => 500, + )); + + $expected = array( + 'command' => 'openDialog', + 'selector' => '#drupal-modal', + 'settings' => NULL, + 'data' => '

Text!

', + 'dialogOptions' => array( + 'url' => 'example', + 'width' => 500, + 'title' => 'Title', + 'modal' => TRUE, + ), + ); + $this->assertEqual($command->render(), $expected, 'OpenModalDialogCommand::render() returns a proper array.'); + } + + /** + * Tests that CloseModalDialogCommand objects can be constructed and rendered. + */ + function testCloseModalDialogCommand() { + $command = new CloseModalDialogCommand(); + $expected = array( + 'command' => 'closeDialog', + 'selector' => '#drupal-modal', + ); + + $this->assertEqual($command->render(), $expected, 'CloseModalDialogCommand::render() returns a proper array.'); + } + + /** + * Tests that CloseDialogCommand objects can be constructed and rendered. + */ + function testCloseDialogCommand() { + $command = new CloseDialogCommand('#some-dialog'); + $expected = array( + 'command' => 'closeDialog', + 'selector' => '#some-dialog', + ); + + $this->assertEqual($command->render(), $expected, 'CloseDialogCommand::render() with a selector returns a proper array.'); + } + + /** + * Tests that SetDialogOptionCommand objects can be constructed and rendered. + */ + function testSetDialogOptionCommand() { + $command = new SetDialogOptionCommand('#some-dialog', 'width', '500'); + $expected = array( + 'command' => 'setDialogOption', + 'selector' => '#some-dialog', + 'optionName' => 'width', + 'optionValue' => '500', + ); + + $this->assertEqual($command->render(), $expected, 'SetDialogOptionCommand::render() with a selector returns a proper array.'); + } + + /** + * Tests that SetDialogTitleCommand objects can be constructed and rendered. + */ + function testSetDialogTitleCommand() { + $command = new SetDialogTitleCommand('#some-dialog', 'Example'); + $expected = array( + 'command' => 'setDialogOption', + 'selector' => '#some-dialog', + 'optionName' => 'title', + 'optionValue' => 'Example', + ); + $this->assertEqual($command->render(), $expected, 'SetDialogTitleCommand::render() with a selector returns a proper array.'); + } + + /** + * Tests that RedirectCommand objects can be constructed and rendered. + */ + function testRedirectCommand() { + $command = new RedirectCommand('http://example.com'); + $expected = array( + 'command' => 'redirect', + 'url' => 'http://example.com', + ); + + $this->assertEqual($command->render(), $expected, 'RedirectCommand::render() with the expected command array.'); + } + +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php index d7d69da..32aea71 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php @@ -13,20 +13,87 @@ class DialogTest extends AjaxTestBase { public static function getInfo() { return array( - 'name' => 'Dialog', - 'description' => 'Performs tests on #ajax[\'dialog\'].', + 'name' => 'AJAX dialogs commands', + 'description' => 'Performs tests on opening and manipulating dialogs via AJAX commands.', 'group' => 'AJAX', ); } /** - * Ensure elements with #ajax['dialog'] render correctly. + * Test sending non-JS and AJAX requests to open and manipulate modals. */ function testDialog() { // Ensure the elements render without notices or exceptions. $this->drupalGet('ajax-test/dialog'); - // @todo What else should we assert? + // Set up variables for this test. + $dialog_renderable = ajax_test_dialog_contents(); + $dialog_contents = drupal_render($dialog_renderable); + $modal_expected_response = array( + 'command' => 'openDialog', + 'selector' => '#drupal-modal', + 'settings' => NULL, + 'data' => $dialog_contents, + 'dialogOptions' => array( + 'modal' => true, + 'title' => 'AJAX Dialog', + ), + ); + $normal_expected_response = array( + 'command' => 'openDialog', + 'selector' => '#ajax-test-dialog-wrapper-1', + 'settings' => NULL, + 'data' => $dialog_contents, + 'dialogOptions' => array( + 'modal' => false, + 'title' => 'AJAX Dialog', + ), + ); + $close_expected_response = array( + 'command' => 'closeDialog', + 'selector' => '#ajax-test-dialog-wrapper-1', + ); + + // Check that requesting a modal dialog without JS goes to a page. + $this->drupalGet('ajax-test/dialog-contents/nojs/1'); + $this->assertRaw($dialog_contents, 'Non-JS modal dialog page present.'); + + // Emulate going to the JS version of the page and check the JSON response. + $ajax_result = $this->drupalGetAJAX('ajax-test/dialog-contents/ajax/1'); + $this->assertEqual($modal_expected_response, $ajax_result[1], 'Modal dialog JSON response matches.'); + + // Check that requesting a "normal" dialog without JS goes to a page. + $this->drupalGet('ajax-test/dialog-contents/nojs'); + $this->assertRaw($dialog_contents, 'Non-JS normal dialog page present.'); + + // Emulate going to the JS version of the page and check the JSON response. + $ajax_result = $this->drupalGetAJAX('ajax-test/dialog-contents/ajax'); + $this->assertEqual($normal_expected_response, $ajax_result[1], 'Normal dialog JSON response matches.'); + + // Emulate closing the dialog via an AJAX request. There is no non-JS + // version of this test. + $ajax_result = $this->drupalGetAJAX('ajax-test/dialog-close'); + $this->assertEqual($close_expected_response, $ajax_result[0], 'Close dialog JSON response matches.'); + + // Test submitting via a POST request through the button for modals. This + // approach more accurately reflects the real responses by Drupal because + // all of the necessary page variables are emulated. + $ajax_result = $this->drupalPostAJAX('ajax-test/dialog', array(), 'button1'); + + // Check that CSS and JavaScript are "added" to the page dynamically. + $dialog_css_exists = strpos($ajax_result[1]['data'], 'jquery.ui.dialog.css') !== FALSE; + $this->assertTrue($dialog_css_exists, 'jQuery UI dialog CSS added to the page.'); + $dialog_js_exists = strpos($ajax_result[2]['data'], 'jquery.ui.dialog.js') !== FALSE; + $this->assertTrue($dialog_css_exists, 'jQuery UI dialog JS added to the page.'); + $dialog_js_exists = strpos($ajax_result[2]['data'], 'dialog.ajax.js') !== FALSE; + $this->assertTrue($dialog_css_exists, 'Drupal dialog JS added to the page.'); + + // Check that the response matches the expected value. + $this->assertEqual($modal_expected_response, $ajax_result[3], 'POST request modal dialog JSON response matches.'); + + // Abbreviated test for "normal" dialogs, testing only the difference. + $ajax_result = $this->drupalPostAJAX('ajax-test/dialog', array(), 'button2'); + $this->assertEqual($normal_expected_response, $ajax_result[3], 'POST request normal dialog JSON response matches.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php index 8c408ed..219bb44 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/FrameworkTest.php @@ -35,12 +35,6 @@ function testAJAXRender() { $commands = $this->drupalGetAJAX('ajax-test/render'); $expected = new SettingsCommand(array('ajax' => 'test'), TRUE); $this->assertCommand($commands, $expected->render(), 'ajax_render() loads settings added with drupal_add_js().'); - - // Verify that JavaScript settings are loaded for #type 'link'. - $this->drupalGet('ajax-test/link'); - $settings = $this->drupalGetSettings(); - $this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips')); - $this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main'); } /** diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 2c51ae0..fe54561 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1242,6 +1242,7 @@ function system_library_info() { array('system', 'drupal'), array('system', 'drupalSettings'), array('system', 'drupal.progress'), + array('system', 'jquery.once'), ), ); @@ -1305,6 +1306,22 @@ function system_library_info() { ), ); + // Drupal's integration between AJAX and dialogs. + $libraries['drupal.dialog.ajax'] = array( + 'title' => 'Drupal Dialog AJAX', + 'version' => VERSION, + 'js' => array( + 'core/misc/dialog.ajax.js' => array('group' => JS_LIBRARY, 'weight' => 3), + ), + 'dependencies' => array( + array('system', 'jquery'), + array('system', 'drupal'), + array('system', 'drupalSettings'), + array('system', 'drupal.ajax'), + array('system', 'drupal.dialog'), + ), + ); + // Drupal's states library. $libraries['drupal.states'] = array( 'title' => 'Drupal states', diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.module b/core/modules/system/tests/modules/ajax_test/ajax_test.module index c4b0503..1cb6bf2 100644 --- a/core/modules/system/tests/modules/ajax_test/ajax_test.module +++ b/core/modules/system/tests/modules/ajax_test/ajax_test.module @@ -6,6 +6,9 @@ */ use Drupal\Core\Ajax\AjaxResponse; +use Drupal\Core\Ajax\OpenDialogCommand; +use Drupal\Core\Ajax\OpenModalDialogCommand; +use Drupal\Core\Ajax\CloseDialogCommand; use Drupal\Core\Ajax\HtmlCommand; /** @@ -31,11 +34,6 @@ function ajax_test_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); - $items['ajax-test/link'] = array( - 'title' => 'AJAX Link', - 'page callback' => 'ajax_test_link', - 'access callback' => TRUE, - ); $items['ajax-test/dialog'] = array( 'title' => 'AJAX Dialog', 'page callback' => 'ajax_test_dialog', @@ -46,6 +44,12 @@ function ajax_test_menu() { 'page callback' => 'ajax_test_dialog_contents', 'access callback' => TRUE, ); + $items['ajax-test/dialog-close'] = array( + 'title' => 'AJAX Dialog close', + 'page callback' => 'ajax_test_dialog_close', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -102,21 +106,6 @@ function ajax_test_error() { } /** - * Menu callback: Renders a #type link with #ajax. - */ -function ajax_test_link() { - $build['link'] = array( - '#type' => 'link', - '#title' => 'Show help', - '#href' => 'filter/tips', - '#ajax' => array( - 'wrapper' => 'block-system-main', - ), - ); - return $build; -} - -/** * Menu callback: Renders a form elements and links with #ajax['dialog']. */ function ajax_test_dialog() { @@ -131,10 +120,8 @@ function ajax_test_dialog() { $build['link'] = array( '#type' => 'link', '#title' => 'Link 1 (modal)', - '#href' => 'ajax-test/dialog-contents', - '#ajax' => array( - 'dialog' => array('modal' => TRUE), - ), + '#href' => 'ajax-test/dialog-contents/nojs/1', + '#attributes' => array('class' => array('use-ajax')), ); // Dialog behavior applied to links rendered by theme_links(). @@ -143,18 +130,18 @@ function ajax_test_dialog() { '#links' => array( 'link2' => array( 'title' => 'Link 2 (modal)', - 'href' => 'ajax-test/dialog-contents', - 'ajax' => array( - 'dialog' => array('modal' => TRUE), - ), + 'href' => 'ajax-test/dialog-contents/nojs/1', + 'attributes' => array('class' => array('use-ajax')), ), 'link3' => array( 'title' => 'Link 3 (non-modal)', - 'href' => 'ajax-test/dialog-contents', - 'ajax' => array( - 'dialog' => array(), - 'wrapper' => 'ajax-test-dialog-wrapper-2', - ), + 'href' => 'ajax-test/dialog-contents/nojs', + 'attributes' => array('class' => array('use-ajax')), + ), + 'link4' => array( + 'title' => 'Link 4 (close non-modal if open)', + 'href' => 'ajax-test/dialog-close', + 'attributes' => array('class' => array('use-ajax')), ), ), ); @@ -167,35 +154,50 @@ function ajax_test_dialog() { function ajax_test_dialog_form($form, &$form_state) { $form['button1'] = array( '#type' => 'submit', + '#name' => 'button1', '#value' => 'Button 1 (modal)', '#ajax' => array( - 'dialog' => array('modal' => TRUE), + 'callback' => 'ajax_test_dialog_form_callback_modal', ), ); $form['button2'] = array( '#type' => 'submit', + '#name' => 'button2', '#value' => 'Button 2 (non-modal)', '#ajax' => array( - 'dialog' => array(), - 'wrapper' => 'ajax-test-dialog-wrapper-1', + 'callback' => 'ajax_test_dialog_form_callback_nonmodal', ), ); return $form; } /** - * Form submit handler for ajax_test_dialog_form(). + * Non-AJAX behavior of the dialog buttons. */ function ajax_test_dialog_form_submit($form, &$form_state) { $form_state['redirect'] = 'ajax-test/dialog-contents'; } /** + * AJAX callback handler for ajax_test_dialog_form(). + */ +function ajax_test_dialog_form_callback_modal($form, &$form_state) { + return ajax_test_dialog_contents('ajax', TRUE); +} + +/** + * AJAX callback handler for ajax_test_dialog_form(). + */ +function ajax_test_dialog_form_callback_nonmodal($form, &$form_state) { + return ajax_test_dialog_contents('ajax', FALSE); +} + +/** * Menu callback: Returns the contents for dialogs opened by ajax_test_dialog(). */ -function ajax_test_dialog_contents() { +function ajax_test_dialog_contents($page_mode = 'nojs', $is_modal = 0) { // This is a regular render array; the keys do not have special meaning. - return array( + $content = array( 'content' => array( '#markup' => 'Example message', ), @@ -210,5 +212,30 @@ function ajax_test_dialog_contents() { ), ), ); + + if ($page_mode === 'ajax') { + $response = new AjaxResponse(); + $title = t('AJAX Dialog'); + $html = drupal_render($content); + if ($is_modal) { + $response->addCommand(new OpenModalDialogCommand($title, $html)); + } + else { + $selector = '#ajax-test-dialog-wrapper-1'; + $response->addCommand(new OpenDialogCommand($selector, $title, $html)); + } + return $response; + } + else { + return $content; + } } +/** + * Menu callback: Close the ajax dialog. + */ +function ajax_test_dialog_close() { + $response = new AjaxResponse(); + $response->addCommand(new CloseDialogCommand('#ajax-test-dialog-wrapper-1')); + return $response; +}