'Checkout', 'page callback' => 'drupal_get_form', 'page arguments' => array('ec_checkout_form'), 'access callback' => 1, 'type' => MENU_CALLBACK, ); $items['admin/config/store/checkout'] = array( 'title' => 'Checkout', 'page callback' => 'drupal_get_form', 'page arguments' => array('ec_checkout_admin_screen_form'), 'description' => 'Determine the order of your checkout screens.', 'access arguments' => array(EC_PERM_SETTINGS), 'file' => 'ec_checkout.admin.inc', ); return $items; } /** * Implements hook_theme(). */ function ec_checkout_theme() { return array( 'ec_checkout_checkout_review_form' => array( 'render element' => 'form', 'file' => 'ec_checkout.theme.inc', ), 'ec_checkout_admin_screen_form' => array( 'render element' => 'form', 'file' => 'ec_checkout.theme.inc', ), ); } /** * Implements hook_checkout_info(). */ function ec_checkout_checkout_info() { return array( 'summary' => array( 'name' => t('Transaction Summary'), 'description' => t('Provide summary of the product and charges of the checkout'), 'module' => 'ec_checkout', 'file' => 'ec_checkout.checkout.inc', 'weight' => 10, ), ); } /** * Implements hook_node_view(). */ function ec_checkout_node_view($node, $view_mode = 'full') { // Add an 'add cart' link or form if applicable. if (isset($node->ptype) && ($view_mode == 'teaser' || variable_get('ec_product_cart_on_teaser', TRUE)) && (!variable_get('ec_product_cart_addition_by_link', TRUE) || ec_product_attributes_get($node, 'use_product_cart_form')) && ec_product_can_purchase($node, 'ec_checkout_product_form')) { $node->content['ec_checkout'] = drupal_get_form('ec_checkout_product_form', $node); $node->content['ec_checkout']['#weight'] = 99; } } /** * Start the checkout process for a user */ function ec_checkout_initialize($type, $txn = NULL, $params = array()) { ctools_include('object-cache'); ctools_object_cache_clear('transaction', 'ec_checkout'); if (!isset($txn)) { $txn = new stdClass; $txn->type = $type; } else { $txn->type = $type; } $txn->allocation = EC_ALLOC_PENDING; $workflows = ec_store_transaction_workflow(); $txn->workflow = reset(array_keys($workflows)); foreach (ec_checkout_get_all_functions('init') as $function) { $function($txn, $params); } ctools_object_cache_set('transaction', 'ec_checkout', $txn); return $txn; } /** * Builds the checkout forms. * Moves through screens relating to each appropriate checkout api making sure * the cart doesn't get out of sync and keeping track of the transacion * structure. */ function ec_checkout_form($form, &$form_state) { ctools_include('object-cache'); // Disable Page Caching $GLOBALS['conf']['cache'] = 0; $txn = ctools_object_cache_get('transaction', 'ec_checkout', TRUE); if (isset($txn)) { $form_state['txn'] = & $txn; // If this is the first time this page as been displayed then we need to // store the txn away for later use. $form = array(); $form['#attached'] = array( 'css' => array( drupal_get_path('module', 'ec_checkout') . '/css/checkout.css', ), 'js' => array( drupal_get_path('module', 'ec_checkout') . '/js/checkout.js', ), ); // perform all calculations for the checkout page foreach (ec_checkout_get_all_functions('calculate') as $function) { $function($form_state); } // perform all custom form elements for checkout foreach (ec_checkout_get_all_functions('form') as $function) { $function($form, $form_state); } if (isset($txn->gross) && $txn->gross != ec_store_transaction_calc_gross($txn)) { $form_state['txn_changed'] = TRUE; } if (isset($txn->additional)) { $form['additional'] = $txn->additional; } $form['buttons'] = array( '#prefix' => '
', '#suffix' => '
', ); $form['buttons']['update'] = array( '#type' => 'submit', '#value' => t('Update order'), ); $form['buttons']['order'] = array( '#type' => 'submit', '#value' => t('Place your order'), '#submit' => array('ec_checkout_form_process_order'), '#create_txn' => TRUE, ); ctools_object_cache_set('transaction', 'ec_checkout', $txn); return $form; } } /** * @todo Please document this function. * @see http://drupal.org/node/1354 */ function ec_checkout_form_validate(&$form, &$form_state) { ctools_include('object-cache'); $txn = ctools_object_cache_get('transaction', 'ec_checkout'); $form_state['txn'] = & $txn; // invoke all the validation functions that each module provides foreach (ec_checkout_get_all_functions('validate') as $function) { $function($form, $form_state); } // check that all the fields have been used and create errors if needed. ec_store_fields_checkout_post_validate($form, $form_state); // invoke all the update functions that each module provides if (!form_get_errors()) { foreach (ec_checkout_get_all_functions('update') as $function) { $function($form, $form_state); } if (isset($form_state['clicked_button']['#create_txn']) && $form_state['clicked_button']['#create_txn']) { if (isset($form_state['txn_changed']) && $form_state['txn_changed']) { form_set_error('', t('Transaction charges has changed. Please check charges and place order again.')); } if (!form_get_errors() && (!isset($form_state['txn_changed']) || !$form_state['txn_changed'])) { foreach (ec_checkout_get_all_functions('post_update') as $function) { $function($form, $form_state); } } } ctools_object_cache_set('transaction', 'ec_checkout', $txn); } } /* * This is normally a _form_submit function but we need to gather all the * *submit* functions from all the various functions found by calling * ec_checkout_get_all_functions('submit') */ /** * @todo Please document this function. * @see http://drupal.org/node/1354 */ function ec_checkout_form_process_order($form, &$form_state) { foreach (ec_checkout_get_all_functions('submit') as $function) { $function($form, $form_state); } $txn = & $form_state['txn']; if ($txn_id = ec_store_transaction_save($txn)) { $new_txn = ec_store_transaction_load($txn_id); foreach ($new_txn as $key => $value) { $txn->{$key} = $value; } } // invoke all the post_submit functions that each module provides foreach (ec_checkout_get_all_functions('post_submit') as $function) { $goto = $function($txn, $form_state); if (isset($goto)) { $form_state['redirect'] = $goto; } } if (empty($form_state['redirect'])) { $form_state['redirect'] = 'node'; drupal_set_message(t('Your order has been submitted.')); } rules_invoke_event('ec_checkout_after_process', $txn); ctools_include('object-cache'); ctools_object_cache_clear('transaction', 'ec_checkout'); } /** * The "Add to cart" form with Quantity. * * @param $node * Object. The node. * @ingroup form */ function ec_checkout_product_form($form, &$form_state, $node, $minimal = FALSE) { $items = module_invoke('ec_cart', 'current'); $items = isset($items) ? $items : array(); $form['#product_cart_form'] = TRUE; $form['#minimal'] = $minimal; $form['#prefix'] = '
'; $form['#suffix'] = '
'; $form['#cache'] = TRUE; $form['products'] = array( '#tree' => TRUE, ); $form['products'][$node->nid]['nid'] = array( '#type' => 'value', '#value' => $node->nid, ); if (ec_product_has_quantity($node)) { $form['products'][$node->nid]['qty'] = array( '#type' => 'textfield', '#title' => t('Quantity'), '#default_value' => isset($items[$node->nid]) ? $items[$node->nid]->qty : 1, '#size' => 5, ); } else { $form['products'][$node->nid]['qty'] = array( '#type' => 'value', '#value' => 1, ); } $form['products'][$node->nid]['#node'] = !empty($items[$node->nid]) ? $items[$node->nid] : $node; if (ec_product_attributes_get($node, 'product_form_additional_fields')) { // Add place holder for Additional information to be added to the cart. $form['products'][$node->nid]['data'] = array( '#tree' => TRUE, ); } $form['submit'] = array( '#prefix' => '
', '#suffix' => '
', '#weight' => 99, ); $form['submit_callback'] = array( '#type' => 'value', ); return $form; } /** * @todo Please document this function. * @see http://drupal.org/node/1354 */ function ec_checkout_product_form_validate(&$form, &$form_state) { $item['qty'] = !empty($item['qty']) ? $item['qty'] : '1'; if (!is_numeric($item['qty'])) { form_set_error("products[{$nid}][qty]", t('Quantity must be a numeric value')); } } /** * @todo Please document this function. * @see http://drupal.org/node/1354 */ function ec_checkout_product_form_submit(&$form, &$form_state) { $args = func_get_args(); $return = NULL; foreach ($form_state['values']['submit_callback'] as $function) { $ret = call_user_func_array($function, $args); if (isset($ret)) { $return = $ret; } } $form_state['redirect'] = $return; } /** * Given new data for saving in the shopping cart, validate the * data via the productapi and feature api. This allows modules * which implement features to invalidate the cart change. * * @param $node * Product node that is to be validated. * @param $type * Specifies what type of purchase is taking place. * @param $qty * Number of items to be added to the cart. * @param $data * Assocated data that is added to the cart with the product. */ function ec_checkout_validate_item(&$node, $type, &$qty, &$data, $severity = EC_VALIDATE_ITEM_SEVERITY_MEDIUM) { $valid_change = NULL; if ($func = ec_product_productapi_get_function($node, 'ec_checkout_validate_item')) { $valid_change = $func($node, $type, $qty, $data, $severity); } if ($valid_change !== FALSE) { $funcs = ec_product_feature_get_all_functions($node, 'ec_checkout_validate_item'); foreach ($funcs as $func) { $return = $func($node, $type, $qty, $data, $severity, $valid_change); if (isset($return)) { $valid_change = $return; } if ($valid_change === FALSE) { if (variable_get('ec_checkout_debug', FALSE)) { drupal_set_message(t('Product feature validate item (@func): Item %title not validated for %type', array('@func' => $func, '%title' => $node->title, '%type' => $type))); } return FALSE; } } } else { if (variable_get('ec_checkout_debug', FALSE)) { drupal_set_message(t('Product API validate item (@func): Item %title not validated for %type', array('@func' => $func, '%title' => $node->title, '%type' => $type))); } } return !isset($valid_change) ? TRUE : $valid_change; } /** * @todo Please document this function. * @see http://drupal.org/node/1354 */ function ec_checkout_add_complete($node, $qty, $data = NULL) { ec_product_invoke_productapi($node, 'ec_checkout_add_complete', $qty, $data); ec_product_invoke_feature_all($node, 'ec_checkout_add_complete', $qty, $data); } /** * Get the ordered screens for the checkout process. */ function ec_checkout_get_screens($op = 'form') { $old_screens = variable_get('ec_checkout_screens', ''); if (is_array($old_screens)) { array_pop($old_screens); } // Merge any new screens with the old one, while preserving old screen order. if (is_array($old_screens)) { foreach ($old_screens as $i => $module) { if ($module == 'ec_checkout') { unset($old_screens[$i]); } elseif (is_scalar($key = array_search($module, $fresh_screens))) { unset($fresh_screens[$key]); } } $screens = array_merge($old_screens, $fresh_screens); } else { $screens = $fresh_screens; } // Cart is always the last screen. It generates the final overview page. $screens[] = 'ec_checkout'; return $screens; } /** * Return the unique id (and hence the cart_id) of the user. */ function ec_checkout_get_id($try = NULL) { // For convenience to the caller, we check the id and return it if applicable. // Possibly add a smarter check. if (!empty($try)) { return $try; } global $user; if (isset($_SESSION['ec_checkout_id'])) { return $_SESSION['ec_checkout_id']; } // If a user is logged in, their cart_id is their user id. elseif (!empty($user->uid)) { return $user->uid; } else { // There is no cart ATM, return a key that can be used but don't save it. return drupal_get_token('ec_checkout_'. time()); } } /** * Get all the different checkout plugins. */ function ec_checkout_get_types($op = 'types', $type = NULL) { $_checkout_types = & drupal_static(__FUNCTION__ . '__checkout_types'); $_checkout_names = & drupal_static(__FUNCTION__ . '__checkout_names'); if (!isset($_checkout_types)) { $_checkout_types = array(); $weights = variable_get('ec_checkout_weights', array()); foreach (module_implements('checkout_info') as $module) { $path = drupal_get_path('module', $module); $types = module_invoke($module, 'checkout_info'); foreach ($types as $ctype => $info) { if (isset($weights[$ctype])) { $info = $weights[$ctype] + $info; } if (isset($info['file'])) { $info['include'] = $path . '/' . $info['file']; } $info['type'] = $ctype; $_checkout_types[$ctype] = (object) $info; $_checkout_names[$ctype] = $info['name']; } } if (!empty($_checkout_types)) { uasort($_checkout_types, 'ec_sort'); asort($_checkout_names); } } if (isset($type) && !isset($_checkout_types[$type])) { return FALSE; } switch ($op) { case 'types': return $_checkout_types; break; case 'type': return $_checkout_types[$type]; break; case 'names': return $_checkout_names; break; case 'name': return $_checkout_names[$type]; break; case 'module': return $_checkout_names[$type]->module; break; } } /** * Get Checkout function. */ function ec_checkout_get_function($type, $hook) { if ($info = ec_checkout_get_types('type', $type)) { if (isset($info->include) && file_exists($info->include)) { include_once DRUPAL_ROOT . '/' . $info->include; } $function = $info->module . '_checkout_' . $hook; if (function_exists($function)) { return $function; } } } /** * Get all checkout functions for a hook. */ function ec_checkout_get_all_functions($hook) { $functions = array(); foreach (ec_checkout_get_types() as $type => $info) { if ($function = ec_checkout_get_function($info->type, $hook)) { $functions[] = $function; } } return $functions; } /** * Implements hook_views_api(). */ function ec_checkout_views_api() { return array('api' => 2.0); } /** * Allow modules to create transactions without needing a user interface. */ function ec_checkout_create_transaction($type, $params = array()) { $form_state = array(); $txn = new stdClass; $txn->type = $type; $txn->no_interface = TRUE; if (isset($params['ecid'])) { $txn->ecid = $params['ecid']; } elseif (isset($params['customer'])) { $txn->customer = $params['customer']; } if (isset($params['items'])) { $txn->items = $params['items']; } $form_state['txn'] = & $txn; foreach (ec_checkout_get_all_functions('init') as $function) { $function($txn, $params); } foreach (ec_checkout_get_all_functions('calculate') as $function) { $function($form_state); } $txn->gross = $txn->balance = ec_store_transaction_calc_gross($txn); if (!isset($params['trial']) || !$params['trial']) { if ($txn_id = ec_store_transaction_save($txn)) { $new_txn = ec_store_transaction_load($txn_id); foreach ($new_txn as $key => $value) { $txn->{$key} = $value; } } } return $txn; }