Skip to content
Snippets Groups Projects
Commit e6580647 authored by Angie Byron's avatar Angie Byron
Browse files

#995854 by rfay, effulgentsia, sun, merlinofchaos, Damien Tournoud, manimejia:...

#995854 by rfay, effulgentsia, sun, merlinofchaos, Damien Tournoud, manimejia: Fixed #ajax doesn't work at all if a file element (or enctype => 'multipart/form-data')  is included in the form
parent bb344c82
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
......@@ -407,19 +407,49 @@ function ajax_base_page_theme() {
/**
* Package and send the result of a page callback to the browser as an AJAX response.
*
* This function is the equivalent of drupal_deliver_html_page(), but for AJAX
* requests. Like that function, it:
* - Adds needed HTTP headers.
* - Prints rendered output.
* - Performs end-of-request tasks.
*
* @param $page_callback_result
* The result of a page callback. Can be one of:
* - NULL: to indicate no content.
* - An integer menu status constant: to indicate an error condition.
* - A string of HTML content.
* - A renderable array of content.
*
* @see drupal_deliver_html_page()
*/
function ajax_deliver($page_callback_result) {
$commands = array();
$header = TRUE;
// Emit a Content-Type HTTP header if none has been added by the page callback
// or by a wrapping delivery callback.
if (is_null(drupal_get_http_header('Content-Type'))) {
// The standard header for JSON is application/json.
// @see http://www.ietf.org/rfc/rfc4627.txt?number=4627
// However, browsers do not allow JavaScript to read the contents of a
// user's local files. To work around that, jQuery submits forms containing
// a file input element to an IFRAME, instead of using XHR.
// @see http://malsup.com/jquery/form/#file-upload
// When Internet Explorer receives application/json content in an IFRAME, it
// treats it as a file download and prompts the user to save it. To prevent
// that, we return the content as text/plain. But only for POST requests,
// since jQuery should always use XHR for GET requests and the incorrect
// mime type should not end up in page or proxy server caches.
// @see http://drupal.org/node/995854
$iframe_upload = !isset($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest';
if ($iframe_upload && $_SERVER['REQUEST_METHOD'] == 'POST') {
drupal_add_http_header('Content-Type', 'text/plain; charset=utf-8');
}
else {
drupal_add_http_header('Content-Type', 'application/json; charset=utf-8');
}
}
// Normalize whatever was returned by the page callback to an AJAX commands
// array.
$commands = array();
if (!isset($page_callback_result)) {
// Simply delivering an empty commands array is sufficient. This results
// in the AJAX request being completed, but nothing being done to the page.
......@@ -444,7 +474,6 @@ function ajax_deliver($page_callback_result) {
// Complex AJAX callbacks can return a result that contains an error message
// or a specific set of commands to send to the browser.
$page_callback_result += element_info('ajax');
$header = $page_callback_result['#header'];
$error = $page_callback_result['#error'];
if (isset($error) && $error !== FALSE) {
if ((empty($error) || $error === TRUE)) {
......@@ -470,24 +499,10 @@ function ajax_deliver($page_callback_result) {
$commands[] = ajax_command_prepend(NULL, theme('status_messages'));
}
// This function needs to do the same thing that drupal_deliver_html_page()
// does: add any needed http headers, print rendered output, and perform
// end-of-request tasks. By default, $header=TRUE, and we add a
// 'text/javascript' header. The page callback can override $header by
// returning an 'ajax' element with a #header property. This can be set to
// FALSE to prevent the 'text/javascript' header from being output, necessary
// when outputting to an IFRAME. This can also be set to 'multipart', in which
// case, we don't output JSON, but JSON content wrapped in a textarea, making
// a 'text/javascript' header incorrect.
if ($header && $header !== 'multipart') {
drupal_add_http_header('Content-Type', 'text/javascript; charset=utf-8');
}
$output = ajax_render($commands);
if ($header === 'multipart') {
// jQuery file uploads: http://malsup.com/jquery/form/#code-samples
$output = '<textarea>' . $output . '</textarea>';
}
print $output;
// Unlike the recommendation in http://malsup.com/jquery/form/#file-upload,
// we do not have to wrap the JSON string in a TEXTAREA, because
// drupal_json_encode() returns an HTML-safe JSON string.
print ajax_render($commands);
ajax_footer();
}
......
......@@ -152,9 +152,9 @@ Drupal.ajax = function (base, element, element_settings) {
ajax.ajaxing = true;
return ajax.beforeSubmit(form_values, element_settings, options);
},
beforeSend: function (xmlhttprequest) {
beforeSend: function (xmlhttprequest, options) {
ajax.ajaxing = true;
return ajax.beforeSend(xmlhttprequest, ajax.options);
return ajax.beforeSend(xmlhttprequest, options);
},
success: function (response, status) {
// Sanity check for browser support (object expected).
......@@ -318,7 +318,20 @@ Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) {
* Prepare the AJAX request before it is sent.
*/
Drupal.ajax.prototype.beforeSend = function (xmlhttprequest, options) {
// Disable the element that received the change.
// Disable the element that received the change to prevent user interface
// interaction while the AJAX request is in progress. ajax.ajaxing prevents
// the element from triggering a new request, but does not prevent the user
// from changing its value.
// Forms without file inputs are already serialized before this function is
// called. Forms with file inputs use an IFRAME to perform a POST request
// similar to a browser, so disabled elements are not contained in the
// submitted values. Therefore, we manually add the element's value to
// options.extraData.
var v = $.fieldValue(this.element);
if (v !== null) {
options.extraData = options.extraData || {};
options.extraData[this.element.name] = v;
}
$(this.element).addClass('progress-disabled').attr('disabled', true);
// Insert progressbar or throbber.
......
......@@ -237,7 +237,7 @@ function file_ajax_upload() {
drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
$commands = array();
$commands[] = ajax_command_replace(NULL, theme('status_messages'));
return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE);
return array('#type' => 'ajax', '#commands' => $commands);
}
list($form, $form_state) = ajax_get_form();
......@@ -247,7 +247,7 @@ function file_ajax_upload() {
drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error');
$commands = array();
$commands[] = ajax_command_replace(NULL, theme('status_messages'));
return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE);
return array('#type' => 'ajax', '#commands' => $commands);
}
// Get the current element and count the number of files.
......@@ -280,7 +280,7 @@ function file_ajax_upload() {
$commands = array();
$commands[] = ajax_command_replace(NULL, $output, $settings);
return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE);
return array('#type' => 'ajax', '#commands' => $commands);
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment