diff --git a/includes/batch.inc b/includes/batch.inc
index 10fc2a737bc..9f094eab64e 100644
--- a/includes/batch.inc
+++ b/includes/batch.inc
@@ -109,11 +109,15 @@ function _batch_progress_page_js() {
$current_set = _batch_current_set();
drupal_set_title($current_set['title'], PASS_THROUGH);
+ // Merge required query parameters for batch processing into those provided by
+ // batch_set() or hook_batch_alter().
+ $batch['url_options']['query']['id'] = $batch['id'];
+
$js_setting = array(
'batch' => array(
'errorMessage' => $current_set['error_message'] . '
' . $batch['error_message'],
'initMessage' => $current_set['init_message'],
- 'uri' => url($batch['url'], array('query' => array('id' => $batch['id']))),
+ 'uri' => url($batch['url'], $batch['url_options']),
),
);
drupal_add_js($js_setting, 'setting');
@@ -189,7 +193,12 @@ function _batch_progress_page_nojs() {
ob_end_clean();
}
- $url = url($batch['url'], array('query' => array('id' => $batch['id'], 'op' => $new_op)));
+ // Merge required query parameters for batch processing into those provided by
+ // batch_set() or hook_batch_alter().
+ $batch['url_options']['query']['id'] = $batch['id'];
+ $batch['url_options']['query']['op'] = $new_op;
+
+ $url = url($batch['url'], $batch['url_options']);
drupal_add_html_head('');
return theme('progress_bar', array('percent' => $percentage, 'message' => $message));
diff --git a/includes/form.inc b/includes/form.inc
index e2d28138428..0de410ea596 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -2963,7 +2963,9 @@ function _form_set_class(&$element, $class = array()) {
* 'operations' and 'finished' functions, for instance if they don't
* reside in the original '.module' file. The path should be relative to
* the base_path(), and thus should be built using drupal_get_path().
- * 'css' : an array of paths to CSS files to be used on the progress page.
+ * 'css': an array of paths to CSS files to be used on the progress page.
+ * 'url_options': options passed to url() when constructing redirect
+ * URLs for the batch.
*
* Operations are added as new batch sets. Batch sets are used to ensure
* clean code independence, ensuring that several batches submitted by
@@ -3051,6 +3053,7 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = 'd
'current_set' => 0,
'progressive' => TRUE,
'url' => $url,
+ 'url_options' => array(),
'source_page' => $_GET['q'],
'redirect' => $redirect,
'theme' => $GLOBALS['theme_key'],
diff --git a/misc/drupal.js b/misc/drupal.js
index ea688a499a3..87a5972c618 100644
--- a/misc/drupal.js
+++ b/misc/drupal.js
@@ -344,4 +344,15 @@ Drupal.theme.prototype = {
}
};
+/**
+ * Return whether the given variable is an object.
+ *
+ * The HEAD version of jQuery (http://code.jquery.com/jquery-nightly.js)
+ * includes an isObject() function, so when that gets released and incorporated
+ * into Drupal, this can be removed.
+ */
+$.extend({isObject: function(value) {
+ return (value !== null && typeof value === 'object');
+}});
+
})(jQuery);
diff --git a/misc/tableheader.js b/misc/tableheader.js
index 813e4c7cab7..0192d30b206 100644
--- a/misc/tableheader.js
+++ b/misc/tableheader.js
@@ -18,8 +18,9 @@ Drupal.behaviors.tableHeader = {
var headers = [];
$('table.sticky-enabled thead', context).once('tableheader', function () {
- // Clone thead so it inherits original jQuery properties.
- var headerClone = $(this).clone(true).insertBefore(this.parentNode).wrap('