From c1579841aa2eb6485d42d33bc02b057c943a985f Mon Sep 17 00:00:00 2001 From: Chris Oden Date: Fri, 16 Dec 2011 13:30:17 -0500 Subject: [PATCH] Issue #1373236 - Provide generic ajax manipulation for checkout page. --- shipping/uc_quote/uc_quote.module | 87 +++++++++--------------------------- uc_cart/uc_cart.api.php | 40 +++++++++++++++++ uc_cart/uc_cart.pages.inc | 79 +++++++++++++++++++++++++++++++++- 3 files changed, 140 insertions(+), 66 deletions(-) diff --git a/shipping/uc_quote/uc_quote.module b/shipping/uc_quote/uc_quote.module index ecc2fbd..457ffa2 100644 --- a/shipping/uc_quote/uc_quote.module +++ b/shipping/uc_quote/uc_quote.module @@ -240,72 +240,29 @@ function uc_quote_form_alter(&$form, &$form_state, $form_id) { } } -/** - * Implements hook_form_FORM_ID_alter() for uc_cart_checkout_form(). - * - * Adds Ajax shipping quote functionality to the checkout form. - */ -function uc_quote_form_uc_cart_checkout_form_alter(&$form, &$form_state) { - if (isset($form['panes']['delivery']['address'])) { - $form['panes']['delivery']['address']['#process'] = array('uc_store_process_address_field', 'uc_quote_process_checkout_address'); - } - if (isset($form['panes']['delivery']['select_address'])) { - $form['panes']['delivery']['select_address']['#ajax']['callback'] = 'uc_quote_select_address'; - } - // @todo: Figure out what needs to be done when the copy-address box is checked. - - if (isset($form['panes']['payment']['line_items'])) { - $form['panes']['quotes']['quotes']['quote_option']['#ajax'] = array( - 'callback' => 'uc_payment_get_totals', - 'wrapper' => 'line-items-div', - ); - } -} - -/** - * - */ -function uc_quote_select_address($form, $form_state) { - $return = uc_quote_checkout_returned_rates($form, $form_state); - $address = uc_checkout_pane_address_render($form, $form_state); - - $return['#commands'][] = ajax_command_replace('#' . $form_state['triggering_element']['#ajax']['wrapper'], drupal_render($address)); - - return $return; -} - -/** - * Wrapper for the uc_store country-change ajax callback to update shipping quotes when the country changes. - */ -function uc_quote_country_change_wrapper(&$form, &$form_state){ - $return = uc_quote_checkout_returned_rates($form, $form_state); - $return['#commands'][] = ajax_command_replace('#uc-store-address-delivery-zone-wrapper', drupal_render(uc_store_update_address_field_zones($form, $form_state))); - return $return; -} - -/** - * - */ -function uc_quote_process_checkout_address($element, $form_state) { - $ajax = array( - 'callback' => 'uc_quote_checkout_returned_rates', - 'effect' => 'slide', - 'progress' => array( - 'type' => 'throbber', - 'message' => t('Receiving shipping quotes...'), - ), - ); - - if (isset($element['delivery_postal_code'])) { - $element['delivery_postal_code']['#ajax'] = $ajax; - } - // The following replaces "uc_store_process_address_field" from uc_store, with a wrapper that will - // update the available quotes when the country is changed. - if (isset($element['delivery_country'])) { - $element['delivery_country']['#ajax']['callback'] = 'uc_quote_country_change_wrapper'; +function uc_quote_uc_cart_checkout_ajax($child, $parent, $form_state) { + switch ($child) { + case 'delivery_country': + case 'delivery_postal_code': + case 'select_address': + return array( + 'callback' => 'uc_quote_checkout_returned_rates', + 'effect' => 'slide', + 'progress' => array( + 'type' => 'throbber', + 'message' => t('Receiving shipping quotes...'), + ), + ); + break; + case 'quote_option': + if (isset($form_state['complete form']['panes']['payment']['line_items'])) { + return array( + 'callback' => 'uc_payment_get_totals', + 'wrapper' => 'line-items-div', + ); + } + break; } - - return $element; } /****************************************************************************** diff --git a/uc_cart/uc_cart.api.php b/uc_cart/uc_cart.api.php index 518db2d..06bd262 100644 --- a/uc_cart/uc_cart.api.php +++ b/uc_cart/uc_cart.api.php @@ -470,5 +470,45 @@ function hook_uc_update_cart_item($nid, $data = array(), $qty, $cid = NULL) { } /** + * Allows modules to add ajax callbacks to checkout form elements in an orderly manner. The callbacks + * are invoked in module weight order to generate a list of ajax commands which are then returned to + * the page. Note that this hook is invoked after the $parent element has been processed - so, for example, + * a 'radios' element will already have expanded to include its radio-button children. The children, + * however, will not yet have been processed. + * + * Ajax options (such as the effect, progress message, etc.) will be determined by the heaviest module + * implementing this hook. However, the *lightest* callback to invoke theme('status_messages', ...) will + * prevent their display by heavier modules. + * + * @param $child + * The key of the element to be modified. + * @param $parent + * The element which is the parent of $child. Can be used to provide additional context. + * @param $form_state + * The current $form_state array. + * + * @return + * An array as would be provided for the '#ajax' attribute of an element. Currently, you must specify a + * 'callback' key ('path' is not supported). If the callback returns a string or renderable array, you + * must also specify a wrapper. + */ +function hook_uc_cart_checkout_ajax($child, $parent, $form_state) { + if ($child == 'delivery_country') { + return array( + 'callback' => 'uc_cart_checkout_ajax_test', + 'wrapper' => 'cart-pane', + 'effect' => 'slide', + 'progress' => array( + 'type' => 'throbber', + 'message' => t('Modifying cart contents...'), + ), + ); + } + return FALSE; +} + + + +/** * @} End of "addtogroup hooks". */ diff --git a/uc_cart/uc_cart.pages.inc b/uc_cart/uc_cart.pages.inc index b6fc686..dcb94dc 100644 --- a/uc_cart/uc_cart.pages.inc +++ b/uc_cart/uc_cart.pages.inc @@ -87,6 +87,83 @@ function uc_cart_checkout() { return drupal_get_form('uc_cart_checkout_form'); } + +/** + * Generic ajax callback for the checkout form. + * Process a list of ajax commands attached to the specified triggering element via hook_uc_cart_checkout_ajax(). + */ +function uc_cart_checkout_ajax($form, $form_state) { + $element = $form_state['triggering_element']; + if (!empty($element['#ajax']['list'])) { + $commands = array(); + foreach ($element['#ajax']['list'] as $ajax) { + if (!empty($ajax['callback']) && function_exists($ajax['callback'])) { + $result = call_user_func($ajax['callback'], $form, $form_state); + if (!empty($result)) { + if (is_array($result) && !empty($result['#type']) && $result['#type'] == 'ajax') { + // If the callback returned an array of commands, simply add these to the list. + $commands = array_merge($commands, $result['#commands']); + } + elseif (!empty($ajax['wrapper'])) { + // Otherwise, assume the callback returned a string or render-array, and insert it into the wrapper. + $html = is_string($result) ? $result : drupal_render($result); + $commands[] = ajax_command_replace('#' . $ajax['wrapper'], $html); + $commands[] = ajax_command_prepend('#' . $ajax['wrapper'], theme('status_messages')); + } + } + } + } + } + if (!empty($commands)) { + return array('#type' => 'ajax', '#commands' => $commands); + } +} + +/** + * Process callback to add ajax to form elements. + */ +function uc_cart_checkout_form_process($form, $form_state) { + // We have to operate on the children rather than on the element itself, because the #process + // functions are called *after* form_handle_input_elements(), which is where the triggering + // element is determined. If we haven't added an '#ajax' key by that time, drupal won't be + // able to determine which callback to invoke. + foreach (element_children($form) as $child) { + $element =& $form[$child]; + + // First add this process function recursively to the children. + if (empty($element['#process']) && !empty($element['#type'])) { + // We want to be sure the default process functions for the element type are called. + $info = element_info($element['#type']); + if (!empty($info['#process'])) { + $element['#process'] = $info['#process']; + } + } + $element['#process'][] = 'uc_cart_checkout_form_process'; + + // Build a list of the ajax that modules want to add to this element. + $list = array(); + foreach (module_implements('uc_cart_checkout_ajax') as $module) { + $list[] = module_invoke($module, 'uc_cart_checkout_ajax', $child, $form, $form_state); + } + $list = array_filter($list); + if (!empty($list)) { + if (!empty($element['#ajax'])) { + array_unshift($list, $element['#ajax']); + } + $element['#ajax'] = array(); + foreach ($list as $ajax) { + $element['#ajax'] = $ajax + $element['#ajax']; + } + $element['#ajax'] = array( + 'callback' => 'uc_cart_checkout_ajax', + 'list' => $list + ) + $element['#ajax']; + } + } + + return $form; +} + /** * The checkout form built up from the enabled checkout panes. * @@ -227,9 +304,9 @@ function uc_cart_checkout_form($form, &$form_state) { '#type' => 'submit', '#value' => t('Review order'), ); + $form['#process'] = array('uc_cart_checkout_form_process'); unset($_SESSION['do_review']); - return $form; } -- 1.7.3.4