diff --git includes/ajax.inc includes/ajax.inc index 4a71f1a..0ac07df 100644 --- includes/ajax.inc +++ includes/ajax.inc @@ -196,9 +196,25 @@ function ajax_get_form() { } /** - * Menu callback for AJAX callbacks through the #ajax['callback'] Form API property. + * Menu callback for AJAX callbacks through the #ajax['callback'] Form API + * property. + * + * 'ajax_form_callback' is the default value for #ajax['path'], and is + * sufficient for many AJAX needs. If #ajax['callback'] is set + * and #ajax['path'] is left set to 'ajax_form_callback', then the user + * function in #ajax['callback'] will be called with $form and $form_state. + * The user code need only return results as either an HTML string which will + * replace the appropriate content on the page (#ajax['wrapper'], or an array + * of AJAX framework commands which will be executed. + * @see @link ajax_commands AJAX framework commands @endlink */ function ajax_form_callback() { + // Find the triggering element, which was set up for us on the client side. + if (!empty($_POST['ajax_triggering_element'])) { + $triggering_element_path = $_POST['ajax_triggering_element']; + // Destroy field so it can't conflict in validation. + unset($_POST['ajax_triggering_element']); + } list($form, $form_state, $form_id, $form_build_id) = ajax_get_form(); // Build, validate and if possible, submit the form. @@ -208,10 +224,37 @@ function ajax_form_callback() { // drupal_process_form() set up. $form = drupal_rebuild_form($form_id, $form_state, $form_build_id); - // Get the callback function from the clicked button. - $ajax = $form_state['clicked_button']['#ajax']; - $callback = $ajax['callback']; - if (function_exists($callback)) { + // $triggering_element_path in a simple form might just be 'myselect', which + // would mean we should use the element $form['myselect']. However, if + // fieldsets are in use, it might be fieldset1/fieldset2/myselect in the case + // of a double-nested fieldset, so we would have to find our way to + // $form['fieldset1']['fieldset2']['myselect']. + // Here we break up the formpath and use it to traverse the form + // to find the element. + if (!empty($triggering_element_path)) { + if (!isset($form['#access']) || $form['#access']) { + $triggering_element = $form; + foreach (explode('/', $triggering_element_path) as $key) { + if (!empty($triggering_element[$key]) && (!isset($triggering_element[$key]['#access']) || $triggering_element[$key]['#access'])) { + $triggering_element = $triggering_element[$key]; + } + else { + // We did not find the $triggering_element or do not have #access, + // so break out and do not provide it. + $triggering_element = NULL; + break; + } + } + } + } + if (empty($triggering_element)) { + $triggering_element = $form_state['clicked_button']; + } + // Now that we have the element, get a callback if there is one. + if (!empty($triggering_element)) { + $callback = $triggering_element['#ajax']['callback']; + } + if (!empty($callback) && function_exists($callback)) { $html = $callback($form, $form_state); // If the returned value is a string, assume it is HTML, add the status @@ -311,6 +354,7 @@ function ajax_process_form($element) { 'method' => empty($element['#ajax']['method']) ? 'replace' : $element['#ajax']['method'], 'progress' => empty($element['#ajax']['progress']) ? array('type' => 'throbber') : $element['#ajax']['progress'], 'button' => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE, + 'formPath' => implode('/', $element['#array_parents']), ); // Convert a simple #ajax['progress'] type string into an array. diff --git misc/ajax.js misc/ajax.js index feb60d5..05294a4 100644 --- misc/ajax.js +++ misc/ajax.js @@ -183,6 +183,10 @@ Drupal.ajax.prototype.beforeSubmit = function (form_values, element, options) { // Disable the element that received the change. $(this.element).addClass('progress-disabled').attr('disabled', true); + // Server-side code needs to know what element triggered the call, so it can + // find the #ajax binding. + form_values.push({ name: 'ajax_triggering_element', value: this.formPath }); + // Insert progressbar or throbber. if (this.progress.type == 'bar') { var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback));