diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc index 212dff6..965913d 100644 --- a/core/includes/ajax.inc +++ b/core/includes/ajax.inc @@ -5,6 +5,9 @@ * Functions for use with Drupal's Ajax framework. */ +use Drupal\Core\Ajax\OpenDialogCommand; +use Drupal\Core\Ajax\OpenModalDialogCommand; + /** * @defgroup ajax Ajax framework * @{ @@ -473,13 +476,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 +592,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']; @@ -1158,3 +1153,17 @@ function ajax_command_add_css($styles) { 'data' => $styles, ); } + +/** + * Helper function that checks whether this is an AJAX request. + * + * @return bool + * Return TRUE if the current request is asking for ajax content, else FALSE. + */ +function ajax_is_ajax_request() { + $container = drupal_container(); + if ($container->has('content_negotiation') && $container->isScopeActive('request')) { + return $container->get('content_negotiation')->getContentType($container->get('request')) == 'ajax'; + } + return FALSE; +} diff --git a/core/lib/Drupal/Core/Ajax/CloseDialogCommand.php b/core/lib/Drupal/Core/Ajax/CloseDialogCommand.php new file mode 100644 index 0000000..278c094 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/CloseDialogCommand.php @@ -0,0 +1,42 @@ +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..85c4057 --- /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..e35e041 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/OpenDialogCommand.php @@ -0,0 +1,110 @@ + $title); + $this->selector = $selector; + $this->html = $html; + $this->dialogSettings = $dialog_settings; + $this->settings = $settings; + } + + /** + * Returns the dialog settings. + * + * @return array + */ + public function getDialogSettings() { + return $this->dialogSettings; + } + + /** + * Sets the dialog settings array. + * + * @param array $dialog_settings + * An array of keys passed to the Drupal.dialog javascript object. + */ + public function setDialogSettings($dialog_settings) { + $this->dialogSettings = $dialog_settings; + } + + /** + * Sets a single dialog setting value. + * + * @param string $key + * Key of the dialog setting. + * @param mixed $value + * The value of the setting. + * + */ + public function setDialogSetting($key, $value) { + $this->dialogSettings[$key] = $value; + } + + /** + * Sets the dialog title (an alias of setDialogSetting). + * + * @param mixed $title + * The new title of the dialog. + * + */ + public function setDialogTitle($title) { + $this->setDialogSetting('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->dialogSettings['modal'] = isset($this->dialogSettings['modal']) && $this->dialogSettings['modal']; + return array( + 'command' => 'openDialog', + 'selector' => $this->selector, + 'settings' => $this->settings, + 'data' => $this->html, + 'dialog' => $this->dialogSettings + ); + } + +} diff --git a/core/lib/Drupal/Core/Ajax/OpenModalDialogCommand.php b/core/lib/Drupal/Core/Ajax/OpenModalDialogCommand.php new file mode 100644 index 0000000..bfb67c5 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/OpenModalDialogCommand.php @@ -0,0 +1,33 @@ +selector = $selector ? $selector : '#drupal-modal'; + $this->option_name = $option_name; + $this->option_value = $option_value; + } + + /** + * Implements \Drupal\Core\Ajax\CommandInterface::render(). + */ + public function render() { + return array( + 'command' => 'setDialogOption', + 'selector' => $this->selector, + 'optionName' => $this->option_name, + 'optionValue' => $this->option_value, + ); + } + +} diff --git a/core/lib/Drupal/Core/Ajax/SetDialogTitleCommand.php b/core/lib/Drupal/Core/Ajax/SetDialogTitleCommand.php new file mode 100644 index 0000000..d7aaf88 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/SetDialogTitleCommand.php @@ -0,0 +1,30 @@ +selector = $selector ? $selector : '#drupal-modal'; + $this->option_name = 'title'; + $this->option_value = $title; + } +} diff --git a/core/lib/Drupal/Core/Ajax/SetWindowLocationCommand.php b/core/lib/Drupal/Core/Ajax/SetWindowLocationCommand.php new file mode 100644 index 0000000..8dca714 --- /dev/null +++ b/core/lib/Drupal/Core/Ajax/SetWindowLocationCommand.php @@ -0,0 +1,46 @@ +url = $url; + } + + /** + * Implements \Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + return array( + 'command' => 'setWindowLocation', + 'url' => $this->url, + ); + } + +} diff --git a/core/misc/ajax.js b/core/misc/ajax.js index 7ca4463..08303f0 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. + */ + setWindowLocation: 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..271963b --- /dev/null +++ b/core/misc/dialog.ajax.js @@ -0,0 +1,63 @@ +/** + * @file + * Extends the Drupal AJAX functionality to integrate the dialog API. + */ + +(function ($, Drupal) { + "use strict"; + + /** + * 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'); + } + + // Open the dialog itself. + response.dialog = response.dialog || {}; + var dialog = new Drupal.dialog($dialog, response.dialog); + dialog.open(); + + // Use the ajax.js insert command to populate the dialog contents. + response.command = 'insert'; + response.method = 'html'; + ajax.commands.insert(ajax, response, status); + }; + + /** + * 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) || $('#drupal-modal'); + if ($dialog.length) { + var dialog = new Drupal.dialog($dialog); + dialog.close(); + } + }; + + /** + * Command to set a dialog property. + * + * If no selector is given, it defaults to setting the modal properties. + */ + Drupal.ajax.prototype.commands.setDialogOption = function (ajax, response, status) { + var $dialog = $(response.selector) || $('#drupal-modal'); + if ($dialog.length) { + var dialog = new Drupal.dialog($dialog); + dialog.element.dialog('option', response.optionName, response.optionValue); + } + }; + +})(jQuery, Drupal); diff --git a/core/misc/dialog.js b/core/misc/dialog.js index a0849bc..1ddc93c 100644 --- a/core/misc/dialog.js +++ b/core/misc/dialog.js @@ -10,10 +10,7 @@ drupalSettings.dialog = { autoOpen: true, - dialogClass: '', - close: function (e) { - Drupal.detachBehaviors(e.target, null, 'unload'); - } + dialogClass: '' }; Drupal.behaviors.dialog = { @@ -28,46 +25,38 @@ Drupal.behaviors.dialog = { }; Drupal.dialog = function (element, options) { + // Set a default for options, making it optional. + options = options ? options : {}; - function openDialog (settings) { - settings = $.extend({}, drupalSettings.dialog, options, settings); - // Trigger a global event to allow scripts to bind events to the dialog. - $(window).trigger('dialog:beforecreate', [dialog, $element, settings]); - $element.dialog(settings); - dialog.open = true; - $(window).trigger('dialog:aftercreate', [dialog, $element, settings]); - } - - function closeDialog (value) { - $(window).trigger('dialog:beforeclose', [dialog, $element]); - $element.dialog('close'); - dialog.returnValue = value; - dialog.open = false; - $(window).trigger('dialog:afterclose', [dialog, $element]); - } + this.element = $(element); + this.isOpen = false; + this.returnValue = undefined; + this.options = $.extend({}, drupalSettings.dialog, options); +}; - var undef; - var $element = $(element); - var dialog = { - open: false, - returnValue: undef, - show: function () { - openDialog({modal: false}); - }, - showModal: function () { - openDialog({modal: true}); - }, - close: closeDialog - }; +Drupal.dialog.prototype.open = function (options) { + options = $.extend({}, drupalSettings.dialog, this.options, options); + // Trigger a global event to allow scripts to bind events to the dialog. + $(window).trigger('dialog:beforecreate', [this, options]); + this.element.dialog(options); + this.isOpen = true; + $(window).trigger('dialog:aftercreate', [this, options]); +} - return dialog; -}; +Drupal.dialog.prototype.close = function (value) { + $(window).trigger('dialog:beforeclose', [this]); + Drupal.detachBehaviors(this.element[0], null, 'unload'); + this.element.dialog('close'); + this.returnValue = value; + this.isOpen = false; + $(window).trigger('dialog:afterclose', [this]); +} /** * 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) { +$(window).on('dialog:aftercreate', function (e, dialog, settings) { + dialog.element.on('click.dialog', '.dialog-cancel', function (e) { dialog.close('cancel'); e.preventDefault(); e.stopPropagation(); @@ -77,8 +66,8 @@ $(window).on('dialog:aftercreate', function (e, dialog, $element, settings) { /** * Removes all 'dialog' listeners. */ -$(window).on('dialog:beforeclose', function (e, dialog, $element) { - $element.off('.dialog'); +$(window).on('dialog:beforeclose', function (e, dialog) { + dialog.element.off('.dialog'); }); })(jQuery, Drupal, drupalSettings); 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..4a723c4 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,21 @@ 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; /** * Tests for all AJAX Commands. */ -class AjaxCommandsUnitTest extends UnitTestBase { +class AjaxCommandsUnitTest extends DrupalUnitTestBase { + + /** + * The modules to enable. + * @var array + */ + public static $modules = array('system'); public static function getInfo() { return array( @@ -305,5 +315,78 @@ 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!

', + 'dialog' => 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!

', + 'dialog' => 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.'); + } + +} 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..be61020 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1305,6 +1305,19 @@ 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', 'drupal.ajax'), + array('system', 'drupal.dialog'), + ), + ); + // Drupal's states library. $libraries['drupal.states'] = array( 'title' => 'Drupal states', @@ -3396,6 +3409,13 @@ function confirm_form($form, $question, $path, $description = NULL, $yes = NULL, '#type' => 'submit', '#value' => $yes ? $yes : t('Confirm'), ); + + if (!isset($options['attributes']) || !isset($options['attributes']['class'])) { + $options['attributes']['class'] = array(); + } + // Add the class for improved UX. OTOH, this is not proper. + $options['attributes']['class'][] = 'dialog-cancel'; + $form['actions']['cancel'] = array( '#type' => 'link', '#title' => $no ? $no : t('Cancel'), 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..406b9d8 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,21 +34,24 @@ 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', 'access callback' => TRUE, ); - $items['ajax-test/dialog-contents'] = array( + $items['ajax-test/%/dialog-contents'] = array( 'title' => 'AJAX Dialog contents', 'page callback' => 'ajax_test_dialog_contents', + 'page arguments' => array(1), 'access callback' => TRUE, ); + $items['ajax-test/dialog-close'] = array( + 'title' => 'AJAX Dialog close', + 'page callback' => 'ajax_test_dialog_close', + 'page arguments' => array(1), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -102,21 +108,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 +122,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/nojs/dialog-contents/1', + '#attributes' => array('class' => array('use-ajax')), ); // Dialog behavior applied to links rendered by theme_links(). @@ -143,18 +132,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/nojs/dialog-contents/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/nojs/dialog-contents', + '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')), ), ), ); @@ -169,33 +158,46 @@ function ajax_test_dialog_form($form, &$form_state) { '#type' => 'submit', '#value' => 'Button 1 (modal)', '#ajax' => array( - 'dialog' => array('modal' => TRUE), + 'callback' => 'ajax_test_dialog_form_callback_modal', ), ); $form['button2'] = array( '#type' => 'submit', '#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; +} diff --git a/modules/dialog_example/dialog_example.info.yml b/modules/dialog_example/dialog_example.info.yml new file mode 100644 index 0000000..dc2e85a --- /dev/null +++ b/modules/dialog_example/dialog_example.info.yml @@ -0,0 +1,6 @@ +name: dialog_example +description: 'Example module to test/prototype a dialog API that can handle complex use-cases..' +package: Core +version: VERSION +core: 8.x +configure: dialog_example diff --git a/modules/dialog_example/dialog_example.module b/modules/dialog_example/dialog_example.module new file mode 100644 index 0000000..9ad1cfa --- /dev/null +++ b/modules/dialog_example/dialog_example.module @@ -0,0 +1,210 @@ + 'Example dialogs', + 'page callback' => 'dialog_example_page', + 'access callback' => TRUE, + 'weight' => -1, + ); + $items['dialog_example/simple'] = array( + 'title' => 'Simple form callback', + 'page callback' => 'dialog_example_form', + 'page arguments' => array('simple'), + 'access callback' => TRUE, + ); + $items['dialog_example/complex'] = array( + 'title' => 'Complex form callback', + 'page callback' => 'dialog_example_form', + 'page arguments' => array('complex'), + 'access callback' => TRUE, + ); + return $items; +} + +function dialog_example_page() { + $links = array(); + $links[] = array( + 'href' => 'dialog_example/simple/nojs', + 'title' => t('Simple Form [modal]'), + 'attributes' => array('class' => array('use-ajax')), + 'query' => array( + 'dialog_settings' => array( + 'modal' => '1', + ), + ), + ); + + $links[] = array( + 'href' => 'dialog_example/simple/nojs', + 'title' => t('Simple Form [normal, i.e. non-js/non-ajax fallback]'), + ); + + $links[] = array( + 'href' => 'dialog_example/complex/nojs', + 'title' => t('Complex Form [modal]'), + 'attributes' => array('class' => array('use-ajax')), + 'query' => array( + 'dialog_settings' => array( + 'modal' => '1', + ), + ), + ); + + $links[] = array( + 'href' => 'dialog_example/complex/nojs', + 'title' => t('Complex Form [modal w/ dialog-settings: 750px wide]') . ' NOT WORKING', + 'attributes' => array('class' => array('use-ajax')), + 'query' => array( + 'dialog_settings' => array( + 'modal' => '1', + 'width' => '750px', + ), + ), + ); + + $links[] = array( + 'href' => 'dialog_example/complex', + 'title' => t('Complex Form [normal, i.e. non-js/non-ajax fallback]') . ' NOT WORKING', + ); + + $links[] = array( + 'href' => 'dialog_example/complex', + 'title' => t('Complex Form [non-modal dialog-1]') . ' NOT WORKING', + 'query' => array( + 'dialog_settings' => array( + 'wrapper' => '#dialog-1', + 'width' => '500px', + ), + ), + ); + + $links[] = array( + 'href' => 'dialog_example/complex/2', + 'title' => t('Complex Form [non-modal dialog-2]') . ' NOT WORKING', + ); + + return array( + '#theme' => 'links', + '#links' => $links, + '#attached' => array( + 'library' => array( + array('system', 'drupal.ajax'), + ), + ), + ); +} + +function dialog_example_form($form_type, $page_type = 'nojs') { + switch ($form_type) { + case 'simple': + $output = drupal_get_form('dialog_example_simple_form', $page_type === 'ajax'); + break; + case 'complex': + $output = drupal_get_form('dialog_example_complex_form', $page_type === 'ajax'); + break; + } + + if (ajax_is_ajax_request()) { + $response = new AjaxResponse(); + $response->addCommand(new openModalDialogCommand('Title', drupal_render($output))); + return $response; + } + + return $output; +} + +function dialog_example_simple_form($form, &$form_state, $use_ajax) { + $form = array(); + $cancel = 'dialog_example'; + $form = confirm_form($form, + t('Simple form'), + $cancel, + t('Click ok or cancel.'), + t('Ok'), + t('Cancel') + ); + + if ($use_ajax) { + $form['actions']['submit']['#ajax'] = array( + 'callback' => 'dialog_example_simple_form_dialog_save', + ); + } + return $form; +} + +function dialog_example_simple_form_dialog_save($form, &$form_state) { + foreach ($form['#submit'] as $submit_handler) { + $submit_handler($form, $form_state); + } + + $response = new AjaxResponse(); + $response->addCommand(new CloseModalDialogCommand()); + $response->addCommand(new SetWindowLocationCommand($form_state['redirect'])); + return $response; +} + +function dialog_example_simple_form_submit($form, &$form_state) { + drupal_set_message(t('Submitted and full page reload / redirect')); + $form_state['redirect'] = 'dialog_example'; +} + +function dialog_example_complex_form() { + $form = array(); + $cancel = 'dialog_example'; + $form = confirm_form($form, + t('Complex form'), + $cancel, + t('Click ok or cancel.'), + t('Ok'), + t('Cancel') + ); + $form['actions']['okayish'] = array( + '#type' => 'submit', + '#value' => t('Okayish'), + '#weight' => -10, + ); + + if (ajax_is_ajax_request()) { + $form['actions']['okayish']['#ajax'] = array( + 'callback' => 'dialog_example_complex_okayish', + ); + } + + $form['#validate'][] = 'dialog_example_complex_form_validate'; + return $form; +} + +function dialog_example_complex_okayish() { + // Change the settings of the dialog. + $response = new AjaxResponse(); + $response->addCommand(new SetDialogOptionCommand(NULL, 'width', '700' + rand(0,200) + 'px')); + $response->addCommand(new SetDialogOptionCommand(NULL, 'height', '200' + rand(0,200) + 'px')); + return $response; +} + +function dialog_example_complex_okay() { + $response->addCommand(new CloseDialogCommand($dialog_selector ? $dialog_selector : '#drupal-modal')); + $response->addCommand(new ReplaceCommand('h1', '

' . date('Ymd His') . ' - ' . t('thanks for confirming') . '

')); + return $response; +} + +function dialog_example_complex_form_validate(&$form, &$form_state) { + $values = $form_state['values']; + if ($values['op'] !== t('Ok')) { + form_set_error('example', t('Click "Ok" to execute this form. Note: if this message is w/in a dialog, you should see the dialog being resized randomly to illustrate changing dialog settings via AJAX.')); + } +} + +function dialog_example_complex_form_submit($form, &$form_state) { + drupal_set_message(t('You submitted the complex form')); + $form_state['redirect'] = 'dialog_example'; +}