', 'cart/view', array('title' => t('View your cart items.')), NULL, NULL, FALSE, TRUE); $block['content'] = theme('cart_display_block'); break; } return $block; } else if ($op == 'configure') { $form['cart_empty_hide'] = array('#type' => 'checkbox', '#title' => t('Hide block if cart is empty'), '#default_value' => variable_get('cart_empty_hide', '0')); return $form; } elseif ($op == 'save' && isset($edit['cart_empty_hide'])) { variable_set('cart_empty_hide', $edit['cart_empty_hide']); } }*/ /** * Perform periodic actions */ function cart_cron() { // Remove items from the cart table that haven't been touched for more than a month. db_query('DELETE FROM {ec_cart} WHERE changed < %d', strtotime('-'.variable_get('accommodation_hold_cust_time',900).' seconds')); // Optimize table query removed - users can now install DB maintenance module // for this. } /** * Implementation of the hook_help() */ function cart_help($section = 'admin/help#cart') { switch ($section) { case 'admin/ecsettings/checkout': return t('This form is used to control the order the pages are viewed in the checkout process. Number the order you would like these screens to appear.'); } } /** * Implementation of hook_menu() */ function cart_menu($may_cache) { global $user; $items = array(); if ($may_cache) { $items[] = array( 'path' => 'cart', 'title' => t('Shopping cart'), 'callback' => 'cart_view', 'access' => user_access('access content'), 'type' => MENU_SUGGESTED_ITEM ); $items[] = array( 'path' => 'cart/checkout', 'title' => t('Checkout'), 'callback' => 'drupal_get_form', 'callback arguments' => array('checkout_form'), 'access' => user_access('access content'), 'type' => MENU_SUGGESTED_ITEM ); $items[] = array( 'path' => 'cart/review', 'title' => t('Please review and submit your order'), 'callback' => 'drupal_get_form', 'callback arguments' => array('checkout_review_form'), 'access' => user_access('access content'), 'type' => MENU_CALLBACK ); $items[] = array( 'path' => 'admin/ecsettings/checkout', 'title' => t('Screen order'), 'callback' => 'drupal_get_form', 'callback arguments' => array('checkout_admin_screen_form'), 'description' => t('Determine the order of your checkout screens.'), 'access' => user_access('administer store') ); } else { drupal_add_css(drupal_get_path('module', 'cart') .'/cart.css'); } return $items; } /** * Implementation of hook_exit(). * * Always clear the cart/view page for anonymous users from the cache otherwise * the cart/view page will be out of sync. */ function cart_exit() { global $user; if (strstr($_GET['q'], 'cart/view') && $user->uid == 0 && variable_get('cache', 0)) { cache_clear_all(url('cart/view'), 'cache_page', TRUE); } } /** * Implementation of hook_user(). */ function cart_user($op, &$edit, &$user, $category = NULL) { switch ($op) { case 'login': // Convert their cart session to a permanent user cart at login. db_query("UPDATE {ec_cart} SET cookie_id = %d WHERE cookie_id = '%s'", $user->uid, session_id()); break; } } /******************************************************************** * Theme Hooks ********************************************************************/ /** * Themes the cart block */ function theme_cart_display_block() { global $user; $output = ''; /** * Until Drupal can handle partial page caching, We should only display a * View Cart link for anonymous users and the full fancy items and total cart * for authenticated users since those pages aren't cached. */ if ($user->uid == 0 && variable_get('cache', 0)) { $output .= l(t('View your cart'), 'cart/view'); } else { $item = cart_get_items(); $item_count = count($item); $item_suffix = ($item_count == 1) ? t('Item') : t('Items'); $output .= '
'. payment_format($form['items'][$nid]['#total']+$form['items'][$nid]['#specials']) .'
'; $row = array( array('data' => $desc), array('data' => $form['items'][$nid]['qty'] ? drupal_render($form['items'][$nid]['qty']) : '', 'align' => 'center'), ); if ($extra && $form['items'][$nid]['extra']) { $row[] = array('data' => drupal_render($form['items'][$nid]['extra'])); } elseif ($extra) { $row[] = ''; } $row[] = array('data' => l(t('Remove'), "cart/delete/$nid")); $rows[] = $row; } $rows[] = array(array("data" => "". t('Subtotal:') . ' ' . payment_format($total), "colspan" => $extra ? 4 : 3, "align" => "right")); $output.= theme('table', $header, $rows); $output.= drupal_render($form); return $output; } function _cart_form_filter_extra($var) { if (is_array($var) && isset($var['extra'])) { return TRUE; } return FALSE; } /** * Themes the admin screen */ function theme_checkout_admin_screen_form($form) { $output = ''; $header = array(t('Module'), t('Screen order')); $screens = checkout_get_screens(); foreach ($screens as $i => $name) { $rows[] = array($name, drupal_render($form['module'][$name])); } $output .= theme('table', $header, $rows); $output .= drupal_render($form); return $output; } /******************************************************************** * Module Functions ********************************************************************/ /** * Routing function that handles different cart operations. * * @param $op * operation to be carried out. Can be 'add', 'edit', 'delete' and defaults * to a cart view. * @param $arg1 * optional parameter used by 'add' and 'edit' as a product node id. * @param $arg1 * optional parameter used by 'add' as a quantity. */ function cart_view($op = '', $arg1 = NULL, $arg2 = NULL) { $data = $_POST; if (isset($data['form_token'])) { unset($data['form_token']); } // Handle any extra args switch ($op) { case 'add': $message = cart_add_item($arg1, isset($_REQUEST['qty']) ? $_REQUEST['qty'] : $arg2, $data); if ($message) { drupal_set_message($message); } drupal_goto('cart/view'); break; case 'edit': cart_update_item($arg1, isset($_REQUEST['qty']) ? $_REQUEST['qty'] : $arg2, $data); drupal_goto('cart/view'); break; case 'delete': cart_remove_item($arg1); drupal_goto('cart/view'); break; default: // normal view break; } // Build the cart form $breadcrumbs[] = l(t('Home'), ""); drupal_set_breadcrumb($breadcrumbs); $items = cart_get_items(); if ($items) { return drupal_get_form('cart_view_form', $items); } else { drupal_set_title('itinerary'); if (module_exists('openresort')){ return openresort_empty_cart(); }else{ //single property only so we take the ecommerce enable business $result = db_result(db_query('SELECT nid FROM {business} WHERE ecommerce = 1')); return ''. t('Your itinerary is empty. You may add to it by clicking !product-page.', array('!product-page' => l(t('here'), 'node/'.$result))). '
'; } } } /** * This is a form to handle the cart view */ function cart_view_form($items) { $form['items'] = array('#tree' => TRUE); foreach ($items as $i) { $node = node_load($i->nid); $form['items'][$node->nid.'-'.$i->changed]['#node'] = $node; $form['items'][$node->nid.'-'.$i->changed]['title'] = array( '#value' => l($node->title, "node/". ($node->pparent ? $node->pparent : $node->nid)) ); $form['items'][$node->nid.'-'.$i->changed]['#total'] = $i->price; $form['items'][$node->nid.'-'.$i->changed]['#specials'] = product_get_specials($i, 'cart', true); if ($node->is_recurring) { $form['items'][$node->nid.'-'.$i->changed]['recurring'] = array( '#value' => theme('recurring_schedule', $node->schedule) ); } if ($node->ptype == 'tangible' && $node->availability != NULL && $node->availability != 1) { $form['items'][$node->nid.'-'.$i->changed]['availability'] = array( '#value' => availability_get_message($node->availability) ); } //we need to add something in here to deal with nights stay //and arrival date $subitem = $i->subitem; //unit type is also required //change this so as we use the key - we also need to read from accom inventory //and join on the node table while($key = key($subitem)){ //foreach ($subitem as $s){ $form['items'][$node->nid.'-'.$i->changed]['#arrive'] = $subitem[$key]->date; $form['items'][$node->nid.'-'.$i->changed]['#unitname'] = db_result(db_query("SELECT title FROM node n inner join accommodation_inventory i on n.nid = i.unit_id inner join accommodation_availability a on a.iid = i.iid where a.aid = " . $key )); break; } $form['items'][$node->nid.'-'.$i->changed]['#days'] = $i->days; if (product_has_quantity($node)) { //commented out so as no quantity displayed for booking //will need to readd and think of another solution once we //have activities /*$form['items'][$node->nid]['qty'] = array( '#type' => 'textfield', '#default_value' => $i->qty, '#size' => 2, '#maxlength' => 2 );*/ } if ($extra = product_invoke_productapi($i, 'cart form')) { $form['items'][$node->nid]['extra'] = $extra; $form['items'][$node->nid]['extra']['#parents'] = array('items', $node->nid, 'data'); } } $form['checkout'] = array('#type' => 'submit', '#value' => t('book it')); return $form; } /** * Implements the _submit hook for the cart_view form */ function cart_view_form_submit($form_id, $form_values) { switch($form_values['op']) { case t('Update Cart'): cache_clear_all(); cart_update_item_object((object)$form_values); return 'cart/view'; case t('book it'): // force it not to validate so we see the update quantity page again. return 'cart/checkout'; } } /** * Builds the checkout forms. * Moves through screens relating to each appropriate checkoutapi making sure * the cart doesn't get out of sync and keeping track of the transacion * structure. */ function checkout_form() { global $user; /* User needs to login or create an account before they can checkout. */ if ($user->uid == 0 && variable_get('ec_anon_policy', ECANON_POLICY_DEFAULT) == ECANON_POLICY_NEVER) { drupal_set_message(t('Login or register to continue the checkout process', array('!reg-link' => url('user/register', drupal_get_destination())))); drupal_goto('user', drupal_get_destination()); } /* Prebuild values from post and args if they exist */ $screen = 0; $data = new stdClass(); // my eclipse warning hack :( if (!isset($_REQUEST['op'])) { // This is a new transaction, so remove the existing checkout. ec_checkout_delete_data(); /* User has no items in their cart. Kick 'em to cart/view. * This comparison only needs to be done when rebuilding the way * things are right now */ $data->items = cart_get_items(); if (!$data->items) { drupal_goto('cart/view'); } /** * unserialize() all the $item->data fields so that they get stored * correctly and do not double serialize() */ foreach ($data->items as $nid => $item) { if (!empty($item->data)) { $data->items[$nid]->data = unserialize($item->data); } } /* If this is an anonymous user, check all the products and see if they * can be sold to them. If there are products which cannot be sold to an * anonymous user then get the user to login or register. */ if (!$user->uid) { foreach ($data->items as $item) { if (product_get_attributes($node, 'registered_user')) { drupal_set_message(t('Login or register to continue the checkout process', array('%reg-link' => url('user/register', drupal_get_destination())))); drupal_goto('user', drupal_get_destination()); } } } // Setup screens $data->screens = checkout_get_screens(); $data->review_screens = checkout_get_screens('review'); $data->screen = $screen; $valid_screen = $data->valid_screen; $data->shippable = FALSE; foreach ($data->items as $product) { if (!$data->shippable && product_is_shippable($product->vid)) { $data->shippable = TRUE; break; } } $data->uid = $user->uid; $data->type = 'cart'; /* process any specials and add them to misc */ foreach ($data->items as $item) { foreach (product_get_specials($item, 'checkout', FALSE, $data) as $type => $special) { if (!is_array($special)) { $special = array('price' => $special); } $misc = new stdClass; $misc->type = 'special_'. (isset($special['type']) ? $special['type'] : $type); $misc->vid = $item->vid; $misc->description = isset($special['description']) ? $special['description'] : 'special'; $misc->invisible = isset($special['invisible']) ? $special['invisible'] : 1; $misc->price = $special['price']; $misc->qty = product_has_quantity($item) ? ($special['qty'] ? $special['qty'] : $item->qty) : 1; $data->misc[] = $misc; } } ec_checkout_hide_data($data); } else { $data = (object)ec_checkout_get_data(); $screen = is_numeric($_GET['op']) ? ($data->valid_screen > $_GET['op'] ? $_GET['op'] : $data->screen) : $data->screen; $valid_screen = $data->valid_screen; } if ($valid_screen > $screen && !is_numeric($_GET['op'])) { $valid_screen = $screen; } // Make sure data screen is setup right $data->screen = $screen; // Catch dead functions and skip to get the current checkoutapi call, also // skip any blank forms. $num_screens = count($data->screens) - 1; while ($num_screens > $screen) { if (function_exists($f = $data->screens[$screen] . '_checkoutapi') && ($form = $f($data, 'form'))) { break; } $data->screen = ++$screen; } // if there is no form then go to the review page. if (!$form) { $data->valid_screen = $data->screen; ec_checkout_hide_data($data); drupal_goto('cart/review'); } $data->checkoutapi = $f; $form['txn'] = array('#type' => 'value', '#value' => $data); $form['screen'] = array( '#type' => 'hidden', '#default_value' => $data->screen ); // Add in our cart token $form['#token'] = 'ecommerce_checkout:'. $screen. ':'. (!$valid_screen ? 0 : $valid_screen); $form['#action'] = url('cart/checkout', (isset($_REQUEST['op']) ? 'op='. $_REQUEST['op'] : 'op=next'). (isset($_REQUEST['destination']) ? '&destination='.urlencode($_REQUEST['destination']) : '')); if (theme_get_function("{$data->screens[$data->screen]}_checkout_form")) { $form['#theme'] = "{$data->screens[$data->screen]}_checkout_form"; } return $form; } /** * Validates the checkout form by calling the api for the screen. * As long as the checkoutapi validate function is looking for a transaction variable * and not the form_values, this function is setup correctly */ function checkout_form_validate($form_id, &$form_values) { $txn =& $form_values['txn']; unset($form_values['txn']); // Merge in current form data into transaction foreach ($form_values as $key => $value) { $txn->$key = $value; } // Call the checkoutapi to do its validations $f = $txn->checkoutapi; if (function_exists($f)) { $f($txn, 'validate'); } } /** * Handles a valid submit of a screen. * Saves transaction to db, increments valid screen, and calls a screen handler * if one exists */ function checkout_form_submit($form_id, &$form_values) { $txn =& $form_values['txn']; unset($form_values['txn']); // Merge in current form data into transaction foreach ($form_values as $key => $value) { $txn->$key = $value; } if (function_exists($f = $txn->checkoutapi)) { $f($txn, 'save'); } if (!is_numeric($_GET['op'])) { $txn->valid_screen = $txn->screen; } else { $txn->screen = $txn->valid_screen; } // Store to database cache ec_checkout_hide_data($txn); // Seperate final screen redirect if($txn->screens[$txn->screen] == 'cart') { return 'cart/review'; } // Redirect to the next screen return array('cart/checkout', 'op=next'); } /** * Checkout Review Page */ function checkout_review_form() { $data = ec_checkout_get_data(); $num_screens = count($data->screens); if ($num_screens == 0 ) { drupal_goto('cart/checkout'); } if ($num_screens > $data->screen+1) { drupal_goto('cart/checkout', 'op=next'); } drupal_set_title(t('Please review and submit your itinerary')); foreach ($data->review_screens as $module) { $function = "{$module}_checkoutapi"; if (function_exists($function) && $subform = $function($data, 'review')) { $form['screens'][$module] = $subform; if (!$form['screens'][$module]['#theme'] && theme_get_function($module .'_review_form')) { $form['screens'][$module]['#theme'] = $module .'_review_form'; } } } $form['submit'] = array('#type' => 'submit', '#value' => t('book it')); $form['txn'] = array( '#type' => 'value', '#value' => $data ); if (theme_get_function('checkout_review_form')) { $form['#theme'] = 'checkout_review_form'; } return $form; } function checkout_review_form_validate($form_id, $form_values) { $txn = drupal_clone($form_values['txn']); unset($form_values['txn']); foreach ($form_values as $key => $value) { $txn->$key = $value; } foreach ($txn->review_screens as $module) { $function = "{$module}_checkoutapi"; function_exists($function) && $function($txn, 'review_validate'); } } /** * Process the transaction, saves it and passes on to the paymentapi * setup the txn variable * save the transaction and clear it out of the system * pass on the transaction variable to the appropriate payment module and let if finish. */ function checkout_review_form_submit($form_id, $form_values) { // Get the transaction stored by the last checkout step. // Since we know should be able to assume the screen is valid, // the transaction is valid. $txn = $form_values['txn']; unset($form_values['txn']); foreach ($form_values as $key => $value) { $txn->$key = $value; } // Do any final updates to the transaction foreach ($txn->review_screens as $module) { $function = "{$module}_checkoutapi"; function_exists($function) && $function($txn, 'review_submit'); } // If the user is submitting the order then process it, other-wise just // save it. if ($form_values['op'] == t('book it')) { // Make sure we have a user id if (!$txn->uid) { global $user; $txn->uid = $user->uid; } // If there is no gross cost we know the transaction is completed if ($txn->gross == 0) { $txn->payment_status = payment_get_status_id('completed'); } // Save the transaction in the store history and clear it out $txn->txnid = store_transaction_save($txn); ec_checkout_delete_data(); // If the transaction is done, tell them and send them home if ($txn->gross >= 0) { store_send_invoice_email($txn->txnid); drupal_set_message(t('Your order has been submitted.')); return 'reservas'; } /*This bit of code taken from EC4 to allow mails to be sent on order submission foreach ($txn->review_screens as $module) { $function = "{$module}_checkoutapi"; function_exists($function) && $function($txn, 'post_process'); } End code insertion */ // Finish of by passing the flow on to the appropriate paymentapi // TODO we don't pass by reference which isn't as the paymentapi // is planned to be used return module_invoke($txn->payment_method, 'paymentapi', $txn, 'payment page'); } else { ec_checkout_hide_data($txn); } } /** * Form for determining the order of the checkout screens. */ function checkout_admin_screen_form() { $form = array(); $screens = checkout_get_screens(); foreach ($screens as $i => $name) { $disabled = ($name == 'ec_anon' || $name == 'cart'); $form['module'][$name] = array( '#type' => 'textfield', '#default_value' => $i, '#size' => 5, '#maxlength' => 2, ); if ($disabled) { $form['module'][$name]['#disabled'] = TRUE; } } if (isset($form['module'])) { $form['module']['#tree'] = TRUE; } $form['submit'] = array( '#type' => 'submit', '#value' => t('Update screen order') ); return $form; } function checkout_admin_screen_form_submit($form_id, $form_values) { $modules = (array)$form_values['module']; asort($modules); $ordered = array('ec_anon'); foreach ($modules as $key => $value) { if ($key != 'ec_anon' && $key != 'cart') { $ordered[] = $key; } } $ordered[] = 'cart'; variable_set('ec_checkout_screens', $ordered); drupal_set_message(t('The screen order has been saved.')); menu_rebuild(); } /** * Implementation of hook_checkoutapi(). */ function cart_checkoutapi(&$txn, $op, $arg3 = NULL, $arg4 = NULL) { switch ($op) { case 'review': $txn->subtotal = 0; $txn->gross = store_transaction_calc_gross($txn); if (is_array($txn->misc)) { usort($txn->misc, 'store_transaction_misc_sort'); } foreach ((array)$txn->items as $product) { $price = store_adjust_misc($txn, $product); $node = node_load($product->nid); $subtotal = product_has_quantity($node) ? ($price * $product->qty) : $price; $subitem = $product->subitem; $txn->subtotal += $subtotal; foreach ($subitem as $s){ $date = $s->date; break; } $itemdata = $product; $form['items'][$product->nid.'-'.$product->changed] = array( 'qty' => array('#value' => product_has_quantity($node) ? $product->qty : ''), 'item' => array('#type' => 'value', '#value' => $itemdata), // allow more info to be displayed by the theme 'price' => array('#value' => $price), 'subtotal' => array('#value' => $txn->subtotal), 'days' => array('#value' => $product->days), 'date' => array('#value' => $date), 'ptype' => array('#value' => $node->ptype), 'options' => array('#value' => $txn->type == 'cart' ? l(t('Change'), 'cart/view') : '') ); } $form['totals'] = array(); // Since we may remove items, keep a track of the current key. $line = 0; // Keep track of which lines are subtotals. $st = array('#row_type' => 'ST'); // Create a subtotal line. // If the Total comes immediately afterwards, then it will be repressed later. $form['totals'][$line++] = array('#title' => t('Subtotal'), '#value' => $txn->subtotal, 'info' => $st); if ($txn->misc) { foreach ($txn->misc AS $key => $misc) { if (!$misc->seen) { // Only add the subtotal line if there are other items in misc. if ($misc->subtotal_before && ($form['totals'][$line-1]['info']['#row_type'] != $st['#row_type'])) { $form['totals'][$line++] = array('#title' => t('Subtotal'), '#value' => $txn->subtotal, 'info' => $st); } } // Here we calculate the misc item if (function_exists($misc->callback)) { $f = $misc->callback; $amount = $f($txn, $misc, $txn->subtotal); /* * Apply the total of this charge to the transaction object * for saving later */ $txn->misc[$key]->price = $amount; } else if ($misc->qty) { $amount = ($misc->price * $misc->qty); } else { $amount = $misc->price; } if(!$misc->already_added) { $txn->subtotal+= $amount; } if (!$misc->seen) { // Misc Item goes here. $form['totals'][$line++] = array('#title' => t($misc->description), '#value' => $amount); // Subtotal - check straight after the misc item if ($misc->subtotal_after) { $form['totals'][$line++] = array('#title' => t('Subtotal'), '#value' => $txn->subtotal, 'info' => $st); } } } } // Remove any subtotal occurring before the total. if ($form['totals'][$line-1]['info']['#row_type'] == $st['#row_type']) { unset($form['totals'][$line-1]); } // Grand total $form['totals'][] = array('#title' => t('Total'), '#value' => $txn->gross); $form = array('cart' => $form); return $form; } } function theme_cart_review_form(&$form) { $f =& $form['cart']; $header = array(t('Qty'), t('Item'), t('Price'), t('Subtotal'), ''); $rows = array(); if ($f['items']) { foreach ($f['items'] as $key => $line) { if (is_numeric($key)) { $rows[] = array( $line['qty']['#value'], $line['item']['#value']->title, array('data' => payment_format($line['price']['#value']), 'align' => 'right'), array('data' => payment_format($line['subtotal']['#value']), 'align' => 'right'), $line['options']['#value'] ); } } } $rows[] = array('', '', '', '', ''); foreach ($f['totals'] as $id => $line) { if (is_numeric($id)) { $rows[] = array( '', "{$line['#title']}", '', array('data' => payment_format($line['#value']), 'align' => 'right'), '' ); } } $content .= theme('table', $header, $rows); return theme('box', t('Order Summary'), $content); } /** * Implementation of hook_invoiceapi() */ function cart_invoiceapi(&$invoice, $op) { switch ($op) { case 'post_update': store_recalc_transaction($invoice); $invoice->gross = store_transaction_calc_gross($invoice); break; case 'review': $form = cart_checkoutapi($invoice, 'review'); $form['#theme'] = 'cart_review_form'; return $form; break; } } /** * When a product is deleted, also remove it from users' shopping carts. */ function cart_productapi(&$node, $op) { switch ($op) { case 'delete': return db_query('DELETE FROM {ec_cart} WHERE nid = %d', $node->nid); } } /** * Implementation of hook_ec_transactionapi() * Empty cart when transaction is completed and inserted */ function cart_ec_transactionapi(&$txn, $op, $arg1 = NULL, $arg2 = NULL) { switch ($op) { case 'insert': cart_empty($txn->uid); } } /******************************************************************** * Helper Functions ********************************************************************/ /** * Get the ordered screens for the checkout process. */ function checkout_get_screens($op = 'form') { $old_screens = variable_get('ec_checkout_screens', ''); if (is_array($old_screens)) array_pop($old_screens); // Grab the latest modules that may not be in our variable_get() foreach (module_implements('checkoutapi') as $module) { // Only count the $op cases $function = "{$module}_checkoutapi"; if ($module != 'cart' && ($return = $function($module, $op))) { $fresh_screens[] = $module; } } // 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_hook($module, 'checkoutapi') || $module == 'cart') { 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[] = 'cart'; return $screens; } /** * Return the unique id (and hence the cart_id) of the user. */ function cart_get_id() { global $user; // If a user is logged in, their cart_id is their user id. if ($user->uid) { return $user->uid; } elseif ($sid = session_id()) { // Use session id until the user registers, then switch to user id. return $sid; } } /** * */ function cart_add_item($nid, $qty = NULL, $data = NULL) { $incr = FALSE; if ($qty === NULL) { $incr = TRUE; $qty = 1; } /* Make sure we can add a product */ $node = node_load($nid); if (!$node->ptype) { drupal_set_message(t('%title is not a product. Unable to add to cart', array('%title' => $node->title)), 'error'); return; } $bool_cart_add = product_invoke_productapi($node, 'cart add item', (object)$data); if (is_null($bool_cart_add)) { $bool_cart_add = TRUE; } if (!$node->nid || !$bool_cart_add) { return FALSE; } if (!product_has_quantity($node)) { $qty = 1; } $qty = ($qty > 0) ? $qty : 1; $item_count = db_result(db_query("SELECT qty FROM {ec_cart} WHERE cookie_id = '%s' AND nid = %d", cart_get_id(), $node->nid)); /* We don't need to permanently store these drupal form elements in the data array. */ unset($data['form_id'], $data['form_token']); /* If the item isn't in the cart yet, add it. */ if ($item_count == 0) { db_query("INSERT INTO {ec_cart} (cookie_id, nid, qty, changed, data) VALUES ('%s', %d, %d, %d, '%s')", cart_get_id(), $node->nid, $qty, time(), $data ? serialize($data) : ''); $message = t('%product-title added to your shopping cart', array('!cart_view' => url('cart/view'), '%product-title' => $node->title)); } else { /* Update the item instead. */ cart_update_item($node->nid, (product_has_quantity($node) && $incr) ? $qty + $item_count : $qty, $data); $message = NULL; } cache_clear_all(); return $message; } /** * */ function cart_update_item($nid, $qty = NULL, $data = array()) { if (!$nid) return NULL; if ($qty === NULL) { $items = cart_get_items(); $qty = $items[$nid]->qty; } if ($qty > 0) { if ($data) { db_query("UPDATE {ec_cart} SET qty = %d, changed = %d, data = '%s' WHERE nid = %d AND cookie_id = '%s'", $qty, time(), serialize($data), $nid, cart_get_id()); } else { db_query("UPDATE {ec_cart} SET qty = %d, changed = %d WHERE nid = %d AND cookie_id = '%s'", $qty, time(), $nid, cart_get_id()); } cache_clear_all(); } else { cart_remove_item($nid); } // Rebuild the items hash cart_get_items(NULL, 'rebuild'); if (!strstr(request_uri(), 'cart/view')) { drupal_set_message(t('Your item(s) have been updated.')); } } /** * */ function cart_update_item_object($edit) { if (is_object($edit) && $edit->items) { foreach ($edit->items as $nid => $item) { cart_update_item($nid, $item['qty'], isset($item['data']) ? $item['data'] : array()); } } } /** * */ function cart_remove_item($nid) { if (!$nid) return NULL; $splitkey = explode('-',$nid); //need to update accommodation availability //subitem in the data field will give aid - go through the key if(module_exists('accommodation')){ $transaction = db_fetch_object(db_query("SELECT * FROM {ec_cart} WHERE cookie_id = '%s' AND nid = %d AND changed= %d", cart_get_id(), $splitkey[0],$splitkey[1])); //need to work out how to upack the object $transaction = unserialize($transaction->data); $localsubitem = $transaction->subitem; while($key = key($localsubitem)){ //use the aid to local up the iid $row = db_fetch_object(db_query("SELECT iid,date from accommodation_availability where aid = ".$key)); db_query("UPDATE accommodation_availability SET reserved_date = '". date('Y-m-d H:i:s',time()) ."' WHERE aid = " . $key); next($localsubitem); } } db_query("DELETE FROM {ec_cart} WHERE cookie_id = '%s' AND nid = %d AND changed= %d", cart_get_id(), $splitkey[0],$splitkey[1]); drupal_set_message(t('Your item has been removed.')); cache_clear_all(); } /** * */ function cart_empty($cookie_id = NULL) { if ($cookie_id == 0 || !$cookie_id) { $cookie_id = cart_get_id(); } $cookie_id = ($cookie_id) ? $cookie_id : cart_get_id(); return db_query("DELETE FROM {ec_cart} WHERE cookie_id = '%s'", $cookie_id); } /** * Grab the items in a shopping cart for an user. * * If $id is not passed in, this function uses the uid of the person currently * accessing this function. * * @param int $id The session or uid * @param string @action if 'rebuild', than rebuild the current item hash. * * @return array An array of all items for an id indexed by nid. */ function cart_get_items($id = NULL, $action = NULL) { static $items = array(); $id = $id ? $id : cart_get_id(); if ($action == 'rebuild') { $items = array(); } if ($items[$id] === NULL) { $result = db_query("SELECT c.*, n.title, n.vid FROM {node} n INNER JOIN {ec_cart} c ON n.nid = c.nid WHERE c.cookie_id = '%s'", $id); while ($cart = db_fetch_object($result)) { $cart = drupal_unpack($cart, 'data'); $p = product_load($cart); //foreach ($p as $key => $value) { // $cart->$key = $value; //} //change so as based on the key for none quantity driven items $items[$id][$cart->nid.$cart->changed] = $cart; } if (!$items[$id]) { $items[$id] = array(); } else { foreach ($items[$id] as $key => $item) { $items[$id][$key]->price = product_adjust_price($item, 'cart'); } } } return $items[$id]; } /** * Invoke a hook_checkoutapi() operation in all modules. * * @param &$txn * A txn object. * @param $op * A string containing the name of the checkoutapi operation. * @param $a3, $a4 * Arguments to pass on to the hook, after the $txn and $op arguments. * @return * The returned value of the invoked hooks. */ function checkout_invoke_checkoutapi(&$txn, $op, $a3 = NULL, $a4 = NULL) { $return = array(); foreach (module_list() as $name) { $function = $name .'_checkoutapi'; if (function_exists($function)) { $result = $function($txn, $op, $a3, $a4); if (is_array($result)) { $return = array_merge($return, $result); } else if ($result) { $return[] = $result; } } } return $return; } /** * Write the current transaction to the DB so it isn't persisted throughout the form pages. */ function ec_checkout_hide_data($edit) { ec_checkout_delete_data(); db_query("INSERT INTO {ec_tmp} (sid, tmp) VALUES ('%s', '%s')", cart_get_id(), serialize($edit)); } /** * Return the current transaction session. * * @param $edit sets the static copy(local cache of sorts) * @return FALSE on empty or an array of data from ec_tmp */ function ec_checkout_get_data($edit = NULL) { return unserialize(db_result(db_query("SELECT tmp FROM {ec_tmp} WHERE sid = '%s'", cart_get_id()))); } /** * Destroy the temp table data for this session. */ function ec_checkout_delete_data() { db_query("DELETE FROM {ec_tmp} WHERE sid = '%s'", cart_get_id()); }