Issue #1870764 by quicksketch, dawehner, frega, larowlan, nod_: Add an ajax command which makes it easy to use the dialog API in complex cases.

8.0.x
webchick 2013-03-17 09:04:07 -07:00
parent ce27c58948
commit f37b8675d2
17 changed files with 800 additions and 122 deletions

View File

@ -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'];

View File

@ -0,0 +1,41 @@
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\CloseDialogCommand.
*/
namespace Drupal\Core\Ajax;
/**
* Defines an AJAX command that closes the current active dialog.
*/
class CloseDialogCommand implements CommandInterface {
/**
* A CSS selector string of the dialog to close.
*
* @var string
*/
protected $selector;
/**
* Constructs a CloseDialogCommand object.
*
* @param string $selector
* A CSS selector string of the dialog to close.
*/
public function __construct($selector = NULL) {
$this->selector = $selector ? $selector : '#drupal-modal';
}
/**
* Implements \Drupal\Core\Ajax\CommandInterface::render().
*/
public function render() {
return array(
'command' => 'closeDialog',
'selector' => $this->selector,
);
}
}

View File

@ -0,0 +1,22 @@
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\CloseModalDialogCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CloseDialogCommand;
/**
* Defines an AJAX command that closes the currently visible modal dialog.
*/
class CloseModalDialogCommand extends CloseDialogCommand {
/**
* Constructs a CloseModalDialogCommand object.
*/
public function __construct() {
$this->selector = '#drupal-modal';
}
}

View File

@ -0,0 +1,139 @@
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\OpenDialogCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* Defines an AJAX command to open certain content in a dialog.
*/
class OpenDialogCommand implements CommandInterface {
/**
* The selector of the dialog.
*
* @var string
*/
protected $selector;
/**
* The title of the dialog.
*
* @var string
*/
protected $title;
/**
* HTML content that will placed in the dialog.
*
* @var string
*/
protected $html;
/**
* Stores dialog-specific options passed directly to jQuery UI dialogs. Any
* jQuery UI option can be used. See http://api.jqueryui.com/dialog.
*
* @var array
*/
protected $dialogOptions;
/**
* Custom settings that will be passed to the Drupal behaviors on the content
* of the dialog.
*
* @var array
*/
protected $settings;
/**
* Constructs an OpenDialogCommand object.
*
* @param string $selector
* The selector of the dialog.
* @param string $title
* The title of the dialog.
* @param string $html
* HTML that will be placed in the dialog.
* @param array $dialog_options
* (optional) Options to be passed to the dialog implementation. Any
* jQuery UI option can be used. See http://api.jqueryui.com/dialog.
* @param array|null $settings
* (optional) Custom settings that will be passed to the Drupal behaviors
* on the content of the dialog. If left empty, the settings will be
* populated automatically from the current request.
*/
public function __construct($selector, $title, $html, array $dialog_options = array(), $settings = NULL) {
$dialog_options += array('title' => $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,
);
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\OpenModalDialogCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\OpenDialogCommand;
/**
* Defines an AJAX command to open certain content in a dialog in a modal dialog.
*/
class OpenModalDialogCommand extends OpenDialogCommand {
/**
* Constructs an OpenModalDialog object.
*
* The modal dialog differs from the normal modal provided by
* OpenDialogCommand in that a modal prevents other interactions on the page
* until the modal has been completed. Drupal provides a built-in modal for
* this purpose, so no selector needs to be provided.
*
* @param string $title
* The title of the dialog.
* @param string $html
* HTML that will be placed in the dialog.
* @param array $dialog_options
* (optional) Settings to be passed to the dialog implementation. Any
* jQuery UI option can be used. See http://api.jqueryui.com/dialog.
* @param array|null $settings
* (optional) Custom settings that will be passed to the Drupal behaviors
* on the content of the dialog. If left empty, the settings will be
* populated automatically from the current request.
*/
public function __construct($title, $html, array $dialog_options = array(), $settings = NULL) {
$dialog_options['modal'] = TRUE;
parent::__construct('#drupal-modal', $title, $html, $dialog_options, $settings);
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\RedirectCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* Defines an AJAX command to set the window.location, loading that URL.
*/
class RedirectCommand implements CommandInterface {
/**
* The URL that will be loaded into window.location.
*
* @var string
*/
protected $url;
/**
* Constructs an RedirectCommand object.
*
* @param string $url
* The URL that will be loaded into window.location. This should be a full
* URL, one that has already been run through the url() function.
*/
public function __construct($url) {
$this->url = $url;
}
/**
* Implements \Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return array(
'command' => 'redirect',
'url' => $this->url,
);
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\SetDialogOptionCommand.
*/
namespace Drupal\Core\Ajax;
/**
* Defines an AJAX command that sets jQuery UI dialog properties.
*/
class SetDialogOptionCommand implements CommandInterface {
/**
* A CSS selector string.
*
* @var string
*/
protected $selector;
/**
* A jQuery UI dialog option name.
*
* @var string
*/
protected $optionName;
/**
* A jQuery UI dialog option value.
*
* @var mixed
*/
protected $optionValue;
/**
* Constructs a SetDialogOptionCommand object.
*
* @param string $selector
* The selector of the dialog whose title will be set. If set to an empty
* value, the default modal dialog will be selected.
* @param string $option_name
* The name of the option to set. May be any jQuery UI dialog option.
* See http://api.jqueryui.com/dialog.
* @param mixed $option_value
* The value of the option to be passed to the dialog.
*/
public function __construct($selector, $option_name, $option_value) {
$this->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,
);
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* @file
* Contains \Drupal\Core\Ajax\SetDialogTitleCommand.
*/
namespace Drupal\Core\Ajax;
use Drupal\Core\Ajax\SetDialogOptionCommand;
/**
* Defines an AJAX command that sets jQuery UI dialog properties.
*/
class SetDialogTitleCommand extends SetDialogOptionCommand {
/**
* Constructs a SetDialogTitleCommand object.
*
* @param string $selector
* The selector of the dialog whose title will be set. If set to an empty
* value, the default modal dialog will be selected.
* @param string $title
* The title that will be set on the dialog.
*/
public function __construct($selector, $title) {
$this->selector = $selector ? $selector : '#drupal-modal';
$this->optionName = 'title';
$this->optionValue = $title;
}
}

View File

@ -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();
@ -628,6 +606,13 @@ Drupal.ajax.prototype.commands = {
window.alert(response.text, response.title);
},
/**
* 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.
*/

96
core/misc/dialog.ajax.js Normal file
View File

@ -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) {
$('<div id="drupal-modal" />').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 = $('<div id="' + response.selector.replace(/^#/, '') + '"/>').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);

View File

@ -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) {
$('<div id="drupal-modal" />').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);

View File

@ -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;
}

View File

@ -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\RemoveCommand;
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 @@ class AjaxCommandsUnitTest extends UnitTestBase {
$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', '<p>Text!</p>', array(
'url' => FALSE,
'width' => 500,
));
$expected = array(
'command' => 'openDialog',
'selector' => '#some-dialog',
'settings' => NULL,
'data' => '<p>Text!</p>',
'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', '<p>Text!</p>', array(
'url' => 'example',
'width' => 500,
));
$expected = array(
'command' => 'openDialog',
'selector' => '#drupal-modal',
'settings' => NULL,
'data' => '<p>Text!</p>',
'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.');
}
}

View File

@ -13,20 +13,87 @@ namespace Drupal\system\Tests\Ajax;
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.');
}
}

View File

@ -35,12 +35,6 @@ class FrameworkTest extends AjaxTestBase {
$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');
}
/**

View File

@ -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',

View File

@ -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;
}
@ -101,21 +105,6 @@ function ajax_test_error() {
return array('#type' => 'ajax', '#error' => $message);
}
/**
* 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'].
*/
@ -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;
}