Skip to content
commerce_cart.module 116 KiB
Newer Older
<?php

/**
 * @file
 * Implements the shopping cart system and add to cart features.
 *
 * In Drupal Commerce, the shopping cart is really just an order that makes
 * special considerations to associate it with a user and
 */

// Define a constant for the default cart provider.
define('COMMERCE_CART_DEFAULT_PROVIDER', 'session');
// Define constants for the shopping cart refresh modes.
define('COMMERCE_CART_REFRESH_ALWAYS', 'always');
define('COMMERCE_CART_REFRESH_OWNER_ONLY', 'owner_only');
define('COMMERCE_CART_REFRESH_ACTIVE_CART_ONLY', 'active_cart_only');
define('COMMERCE_CART_REFRESH_DEFAULT_FREQUENCY', 15);
/**
 * Implements hook_menu().
 */
function commerce_cart_menu() {
  $items = array();

Ryan Szrama's avatar
Ryan Szrama committed
    'title' => 'Shopping cart',
    'page callback' => 'commerce_cart_view',
    'access arguments' => array('access content'),
    'file' => 'includes/commerce_cart.pages.inc',
  );

  $items['cart/my'] = array(
    'title' => 'Shopping cart (# items)',
    'title callback' => 'commerce_cart_menu_item_title',
    'title arguments' => array(TRUE),
    'page callback' => 'commerce_cart_menu_item_redirect',
    'access arguments' => array('access content'),
    'type' => MENU_SUGGESTED_ITEM,
  );

  $items['checkout'] = array(
    'title' => 'Checkout',
    'page callback' => 'commerce_cart_checkout_router',
    'access arguments' => array('access checkout'),
    'type' => MENU_CALLBACK,
    'file' => 'includes/commerce_cart.pages.inc',
  );

  // If the Order UI module is installed, add a local action to it that lets an
  // administrator execute a cart order refresh on the order. Modules that
  // define their own order edit menu item are also responsible for defining
  // their own local action menu items if needed.
  if (module_exists('commerce_order_ui')) {
    $items['admin/commerce/orders/%commerce_order/edit/refresh'] = array(
      'title' => 'Apply pricing rules',
      'description' => 'Executes the cart order refresh used to apply all current pricing rules on the front end.',
      'page callback' => 'drupal_get_form',
      'page arguments' => array('commerce_cart_order_refresh_form', 3),
      'access callback' => 'commerce_cart_order_refresh_form_access',
      'access arguments' => array(3),
      'type' => MENU_LOCAL_ACTION,
      'file' => 'includes/commerce_cart.admin.inc',
    );
  }

/**
 * Returns the title of the shopping cart menu item with an item count.
 */
function commerce_cart_menu_item_title() {
  global $user;

  // Default to a static title.
  $title = t('Shopping cart');

  // If the user actually has a cart order...
  if ($order = commerce_cart_order_load($user->uid)) {
    // Count the number of product line items on the order.
    $wrapper = entity_metadata_wrapper('commerce_order', $order);
    $quantity = commerce_line_items_quantity($wrapper->commerce_line_items, commerce_product_line_item_types());

    // If there are more than 0 product line items on the order...
    if ($quantity > 0) {
      // Use the dynamic menu item title.
      $title = format_plural($quantity, 'Shopping cart (1 item)', 'Shopping cart (@count items)');
    }
/**
 * Redirects a valid page request to cart/my to the cart page.
 */
function commerce_cart_menu_item_redirect() {
  drupal_goto('cart');
}

/**
 * Access callback: determines access to the "Apply pricing rules" local action.
 */
function commerce_cart_order_refresh_form_access($order) {
  // Do not show the link for cart orders as they're refreshed automatically.
  if (commerce_cart_order_is_cart($order)) {
    return FALSE;
  }

  // Returns TRUE if the link is enabled via the order settings form and the
  // user has access to update the order.
  return variable_get('commerce_order_apply_pricing_rules_link', TRUE) && commerce_order_access('update', $order);
/**
 * Implements hook_hook_info().
 */
function commerce_cart_hook_info() {
  $hooks = array(
    'commerce_cart_order_id' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_order_is_cart' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_order_convert' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_line_item_refresh' => array(
    'commerce_cart_order_refresh' => array(
    'commerce_cart_order_empty' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_attributes_refresh_alter' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_product_comparison_properties_alter' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_product_prepare' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_product_add' => array(
      'group' => 'commerce',
    ),
    'commerce_cart_product_remove' => array(
      'group' => 'commerce',
    ),
/**
 * Implements hook_commerce_order_state_info().
 */
function commerce_cart_commerce_order_state_info() {
  $order_states = array();

  $order_states['cart'] = array(
    'name' => 'cart',
    'title' => t('Shopping cart'),
    'description' => t('Orders in this state have not been completed by the customer yet.'),
    'weight' => -5,
    'default_status' => 'cart',
  );

  return $order_states;
}

/**
 * Implements hook_commerce_order_status_info().
 */
function commerce_cart_commerce_order_status_info() {
  $order_statuses = array();

  $order_statuses['cart'] = array(
    'name' => 'cart',
    'title' => t('Shopping cart'),
    'state' => 'cart',
/**
 * Implements hook_commerce_checkout_pane_info().
 */
function commerce_cart_commerce_checkout_pane_info() {
  $checkout_panes = array();

  $checkout_panes['cart_contents'] = array(
    'title' => t('Shopping cart contents'),
    'base' => 'commerce_cart_contents_pane',
    'file' => 'includes/commerce_cart.checkout_pane.inc',
/**
 * Implements hook_commerce_checkout_complete().
 */
function commerce_cart_commerce_checkout_complete($order) {
  // Move the cart order ID to a completed order ID.
  if (commerce_cart_order_session_exists($order->order_id)) {
    commerce_cart_order_session_save($order->order_id, TRUE);
    commerce_cart_order_session_delete($order->order_id);
/**
 * Implements hook_commerce_line_item_summary_link_info().
 */
function commerce_cart_commerce_line_item_summary_link_info() {
  return array(
    'view_cart' => array(
      'title' => t('View cart'),
      'href' => 'cart',
      'attributes' => array('rel' => 'nofollow'),
      'weight' => 0,
    ),
    'checkout' => array(
      'title' => t('Checkout'),
      'href' => 'checkout',
      'attributes' => array('rel' => 'nofollow'),
      'weight' => 5,
      'access' => user_access('access checkout'),
/**
 * Implements hook_form_alter().
 */
function commerce_cart_form_alter(&$form, &$form_state, $form_id) {
  if (strpos($form_id, 'views_form_commerce_cart_form_') === 0) {
    // Only alter buttons if the cart form View shows line items.
    $view = reset($form_state['build_info']['args']);

    if (!empty($view->result)) {
      // Change the Save button to say Update cart.
      $form['actions']['submit']['#value'] = t('Update cart');
      $form['actions']['submit']['#submit'] = array_merge($form['#submit'], array('commerce_cart_line_item_views_form_submit'));

      // Change any Delete buttons to say Remove.
      if (!empty($form['edit_delete'])) {
        foreach(element_children($form['edit_delete']) as $key) {
          // Load and wrap the line item to have the title in the submit phase.
          if (!empty($form['edit_delete'][$key]['#line_item_id'])) {
            $line_item_id = $form['edit_delete'][$key]['#line_item_id'];
            $form_state['line_items'][$line_item_id] = commerce_line_item_load($line_item_id);

            $form['edit_delete'][$key]['#value'] = t('Remove');
            $form['edit_delete'][$key]['#submit'] = array_merge($form['#submit'], array('commerce_cart_line_item_delete_form_submit'));
          }
    else {
      // Otherwise go ahead and remove any buttons from the View.
      unset($form['actions']);
    }
  elseif (strpos($form_id, 'commerce_checkout_form_') === 0 && !empty($form['buttons']['cancel'])) {
    // Override the submit handler for changing the order status on checkout cancel.
    foreach ($form['buttons']['cancel']['#submit'] as $key => &$value) {
      if ($value == 'commerce_checkout_form_cancel_submit') {
        $value = 'commerce_cart_checkout_form_cancel_submit';
      }
    }
  }
  elseif (strpos($form_id, 'views_form_commerce_cart_block') === 0) {
    // No point in having a "Save" button on the shopping cart block.
    unset($form['actions']);
  }
}

/**
 * Submit handler to take back the order to cart status on cancel in checkout.
 */
function commerce_cart_checkout_form_cancel_submit($form, &$form_state) {
  // Update the order to the cart status.
  $order = commerce_order_load($form_state['order']->order_id);
  $form_state['order'] = commerce_order_status_update($order, 'cart', TRUE);

  // Skip saving in the status update and manually save here to force a save
  // even when the status doesn't actually change.
  if (variable_get('commerce_order_auto_revision', TRUE)) {
    $form_state['order']->revision = TRUE;
    $form_state['order']->log = t('Customer manually canceled the checkout process.');
  }
  commerce_order_save($form_state['order']);

  drupal_set_message(t('Checkout of your current order has been canceled and may be resumed when you are ready.'));

  // Redirect to cart on cancel.
  $form_state['redirect'] = 'cart';
/**
 * Submit handler to show the shopping cart updated message.
 */
function commerce_cart_line_item_views_form_submit($form, &$form_state) {
  // Reset the status of the order to cart.
  $order = commerce_order_load($form_state['order']->order_id);
  // Check order state for stale orders (cancelled, completed.)
  $status = commerce_order_status_load($order->status);
  if (in_array($status['state'], array('cart', 'checkout'))) {
    $form_state['order'] = commerce_order_status_update($order, 'cart', TRUE);
    // Skip saving in the status update and manually save here to force a save
    // even when the status doesn't actually change.
    if (variable_get('commerce_order_auto_revision', TRUE)) {
      $form_state['order']->revision = TRUE;
      $form_state['order']->log = t('Customer updated the order via the shopping cart form.');
    }
    commerce_order_save($form_state['order']);

    drupal_set_message(t('Your shopping cart has been updated.'));
  }
/**
 * Submit handler to show the line item delete message.
 */
function commerce_cart_line_item_delete_form_submit($form, &$form_state) {
  $line_item_id = $form_state['triggering_element']['#line_item_id'];

  // Get the corresponding wrapper to show the correct title.
  $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $form_state['line_items'][$line_item_id]);

  // If the deleted line item is a product...
  if (in_array($line_item_wrapper->getBundle(), commerce_product_line_item_types())) {
    $title = $line_item_wrapper->commerce_product->title->value();
  }
  else {
    $title = $line_item_wrapper->line_item_label->value();
  }
  drupal_set_message(t('%title removed from your cart.', array('%title' => $title)));
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Adds a checkbox to the order settings form to enable the local action on
 * order edit forms to apply pricing rules.
 */
function commerce_cart_form_commerce_order_settings_form_alter(&$form, &$form_state) {
  $form['commerce_order_apply_pricing_rules_link'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enable the local action link on order edit forms to apply pricing rules.'),
    '#description' => t('Even if enabled the link will not appear on shopping cart order edit forms.'),
    '#default_value' => variable_get('commerce_order_apply_pricing_rules_link', TRUE),
    '#weight' => 10,
  );
  $cart_providers = array();
  foreach (commerce_cart_get_providers() as $key => $cart_provider) {
    $cart_providers[$key] = $cart_provider['title'];
  }

  $form['commerce_cart_provider'] = array(
    '#title' => t('Cart provider'),
    '#type' => 'select',
    '#options' => $cart_providers,
    '#default_value' => variable_get('commerce_cart_provider', COMMERCE_CART_DEFAULT_PROVIDER),
    '#access' => count($cart_providers) > 1,
  );

  // Add a fieldset for settings pertaining to the shopping cart refresh.
  $form['cart_refresh'] = array(
    '#type' => 'fieldset',
    '#title' => t('Shopping cart refresh'),
    '#description' => t('Shopping cart orders comprise orders in shopping cart and some checkout related order statuses. These settings let you control how the shopping cart orders are refreshed, the process during which product prices are recalculated, to improve site performance in the case of excessive refreshes on sites with less dynamic pricing needs.'),
    '#weight' => 40,
  );
  $form['cart_refresh']['commerce_cart_refresh_mode'] = array(
    '#type' => 'radios',
    '#title' => t('Shopping cart refresh mode'),
    '#options' => array(
      COMMERCE_CART_REFRESH_ALWAYS => t('Refresh a shopping cart when it is loaded regardless of who it belongs to.'),
      COMMERCE_CART_REFRESH_OWNER_ONLY => t('Only refresh a shopping cart when it is loaded if it belongs to the current user.'),
      COMMERCE_CART_REFRESH_ACTIVE_CART_ONLY => t("Only refresh a shopping cart when it is loaded if it is the current user's active shopping cart."),
    ),
    '#default_value' => variable_get('commerce_cart_refresh_mode', COMMERCE_CART_REFRESH_OWNER_ONLY),
  );
  $form['cart_refresh']['commerce_cart_refresh_frequency'] = array(
    '#type' => 'textfield',
    '#title' => t('Shopping cart refresh frequency'),
    '#description' => t('Shopping carts will only be refreshed if more than the specified number of seconds have passed since they were last refreshed.'),
    '#default_value' => variable_get('commerce_cart_refresh_frequency', COMMERCE_CART_REFRESH_DEFAULT_FREQUENCY),
    '#required' => TRUE,
    '#size' => 32,
    '#field_suffix' => t('seconds'),
    '#element_validate' => array('commerce_cart_validate_refresh_frequency'),
  );
  $form['cart_refresh']['commerce_cart_refresh_force'] = array(
    '#type' => 'checkbox',
    '#title' => t('Always refresh shopping cart orders on shopping cart and checkout form pages regardless of other settings.'),
    '#description' => t('Note: this option only applies to the core /cart and /checkout/* paths.'),
    '#default_value' => variable_get('commerce_cart_refresh_force', TRUE),
  );
}

/**
 * Form element validation handler for the cart refresh frequency value.
 */
function commerce_cart_validate_refresh_frequency($element, &$form_state) {
  $value = $element['#value'];
  if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value < 0)) {
    form_error($element, t('%name must be 0 or a positive integer.', array('%name' => $element['#title'])));
  }
/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alter the order edit form so administrators cannot attempt to alter line item
 * unit prices for orders still in a shopping cart status. On order load, the
 * cart module refreshes these prices based on the current product price and
 * pricing rules, so any alterations would not be persistent anyways.
 *
 * @see commerce_cart_commerce_order_load()
function commerce_cart_form_commerce_order_ui_order_form_alter(&$form, &$form_state) {
  $order = $form_state['commerce_order'];

  // If the order being edited is in a shopping cart status and the form has the
  // commerce_line_items element present...
  if (commerce_cart_order_is_cart($order) && !empty($form['commerce_line_items'])) {
    // Grab the instance info for commerce_line_items and only alter the form if
    // it's using the line item manager widget.
    $instance = field_info_instance('commerce_order', 'commerce_line_items', field_extract_bundle('commerce_order', $order));

    if ($instance['widget']['type'] == 'commerce_line_item_manager') {
      // Loop over the line items on the form...
      foreach ($form['commerce_line_items'][$form['commerce_line_items']['#language']]['line_items'] as &$line_item) {
        // Disable the unit price amount and currency code fields.
        $language = $line_item['commerce_unit_price']['#language'];
        $line_item['commerce_unit_price'][$language][0]['amount']['#disabled'] = TRUE;
        $line_item['commerce_unit_price'][$language][0]['currency_code']['#disabled'] = TRUE;
      }
/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alters the Field UI field edit form to add per-instance settings for fields
 * on product types governing the use of product fields as attribute selection
 * fields on the Add to Cart form.
 */
function commerce_cart_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
  // Extract the instance info from the form.
  $instance = $form['#instance'];

  // If the current field instance is not locked, is attached to a product type,
  // and of a field type that defines an options list...
  if (empty($form['locked']) && $instance['entity_type'] == 'commerce_product' &&
    function_exists($form['#field']['module'] . '_options_list')) {
    // Get the current instance's attribute settings for use as default values.
    $commerce_cart_settings = commerce_cart_field_instance_attribute_settings($instance);

    $form['instance']['commerce_cart_settings'] = array(
      '#type' => 'fieldset',
      '#title' => t('Attribute field settings'),
      '#description' => t('Single value fields attached to products can function as attribute selection fields on Add to Cart forms. When an Add to Cart form contains multiple products, attribute field data can be used to allow customers to select a product based on the values of the field instead of just from a list of product titles.'),
      '#weight' => 5,
      '#collapsible' => FALSE,
    );
    $form['instance']['commerce_cart_settings']['attribute_field'] = array(
      '#type' => 'checkbox',
      '#title' => t('Enable this field to function as an attribute field on Add to Cart forms.'),
      '#default_value' => $commerce_cart_settings['attribute_field'],
    );
    $form['instance']['commerce_cart_settings']['attribute_widget'] = array(
      '#type' => 'radios',
      '#title' => t('Attribute selection widget'),
      '#description' => t('The type of element used to select an option if used on an Add to Cart form.'),
      '#options' => array(
        'select' => t('Select list'),
        'radios' => t('Radio buttons'),
      ),
      '#default_value' => $commerce_cart_settings['attribute_widget'],
      '#states' => array(
        'visible' => array(
          ':input[name="instance[commerce_cart_settings][attribute_field]"]' => array('checked' => TRUE),
        ),
      ),
    );

    // Determine the default attribute widget title.
    $attribute_widget_title = $commerce_cart_settings['attribute_widget_title'];

    if (empty($attribute_widget_title)) {
      $attribute_widget_title = $instance['label'];
    }

    $form['instance']['commerce_cart_settings']['attribute_widget_title'] = array(
      '#type' => 'textfield',
      '#title' => t('Attribute widget title'),
      '#description' => t('Specify the title to use for the attribute widget on the Add to Cart form.'),
      '#default_value' => $attribute_widget_title,
      '#states' => array(
        'visible' => array(
          ':input[name="instance[commerce_cart_settings][attribute_field]"]' => array('checked' => TRUE),
        ),
      ),
    );

    $form['field']['cardinality']['#description'] .= '<br />' . t('Must be 1 for this field to function as an attribute selection field on Add to Cart forms.');
  }

  // If the current field instance is not locked and is attached to a product
  // line item type...
  if (empty($form['locked']) && $instance['entity_type'] == 'commerce_line_item' &&
    in_array($instance['bundle'], commerce_product_line_item_types())) {
    // Get the current instance's line item form settings for use as default values.
    $commerce_cart_settings = commerce_cart_field_instance_access_settings($instance);

    $form['instance']['commerce_cart_settings'] = array(
      '#type' => 'fieldset',
      '#title' => t('Add to Cart form settings'),
      '#description' =>t('Fields attached to product line item types can be included in the Add to Cart form to collect additional information from customers in conjunction with their purchase of particular products.'),
      '#weight' => 5,
      '#collapsible' => FALSE,
    );
    $form['instance']['commerce_cart_settings']['field_access'] = array(
      '#type' => 'checkbox',
      '#title' => t('Include this field on Add to Cart forms for line items of this type.'),
      '#default_value' => $commerce_cart_settings['field_access'],
    );
  }
/**
 * Implements hook_commerce_order_delete().
 */
function commerce_cart_commerce_order_delete($order) {
  commerce_cart_order_session_delete($order->order_id);
  commerce_cart_order_session_delete($order->order_id, TRUE);
}

/**
 * Implements hook_commerce_product_calculate_sell_price_line_item_alter().
 */
function commerce_cart_commerce_product_calculate_sell_price_line_item_alter($line_item) {
  global $user;

  // Reference the current shopping cart order in the line item if it isn't set.
  // We load the complete order at this time to ensure it primes the order cache
  // and avoid any untraceable recursive loops.
  // @see http://drupal.org/node/1268472
    $order = commerce_cart_order_load($user->uid);

    if ($order) {
      $line_item->order_id = $order->order_id;
    }
/**
 * Implements hook_views_api().
 */
function commerce_cart_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'commerce_cart') . '/includes/views',
  );
}

/**
 * Implements hook_theme().
 */
function commerce_cart_theme() {
  return array(
    'commerce_cart_empty_block' => array(
      'variables' => array(),
    ),
    'commerce_cart_empty_page' => array(
      'variables' => array(),
    ),
      'variables' => array('order' => NULL, 'contents_view' => NULL),
      'path' => drupal_get_path('module', 'commerce_cart') . '/theme',
      'template' => 'commerce-cart-block',
 *
 * When a user logs into the site, if they have a shopping cart order it should
 * be updated to belong to their user account.
 */
function commerce_cart_user_login(&$edit, $account) {
  // Get the user's anonymous shopping cart order if it exists.
  if ($order = commerce_cart_order_load()) {
    // Convert it to an authenticated cart.
    commerce_cart_order_convert($order, $account);
/**
 * Implements hook_user_update().
 *
 * When a user account e-mail address is updated, update any shopping cart
 * orders owned by the user account to use the new e-mail address.
 */
function commerce_cart_user_update(&$edit, $account, $category) {
  // If the e-mail address was changed...
  if (!empty($edit['original']->mail) && $account->mail != $edit['original']->mail) {
    // Load the user's shopping cart orders.
    $query = new EntityFieldQuery();

    $query
      ->entityCondition('entity_type', 'commerce_order', '=')
      ->propertyCondition('uid', $account->uid, '=')
      ->propertyCondition('status', array_keys(commerce_order_statuses(array('cart' => TRUE))), 'IN');

    $result = $query->execute();

    if (!empty($result['commerce_order'])) {
      foreach (commerce_order_load_multiple(array_keys($result['commerce_order'])) as $order) {
        if ($order->mail != $account->mail) {
          $order->mail = $account->mail;
          commerce_order_save($order);
        }
      }
    }
  }
}

/**
 * Implements hook_block_info().
 */
function commerce_cart_block_info() {
  $blocks = array();

  // Define the basic shopping cart block and hide it on the checkout pages.
  $blocks['cart'] = array(
    'info' => t('Shopping cart'),
    'cache' => DRUPAL_NO_CACHE,
    'visibility' => 0,
    'pages' => 'checkout*',
  );

  return $blocks;
/**
 * Implements hook_block_view().
 */
function commerce_cart_block_view($delta) {
  global $user;

  // Prepare the display of the default Shopping Cart block.
    // First check to ensure there are products in the shopping cart.
    if ($order = commerce_cart_order_load($user->uid)) {
      $wrapper = entity_metadata_wrapper('commerce_order', $order);

      // If there are one or more products in the cart...
      if (commerce_line_items_quantity($wrapper->commerce_line_items, commerce_product_line_item_types()) > 0) {
        // Build the variables array to send to the cart block template.
        $variables = array(
          'order' => $order,
          'contents_view' => commerce_embed_view('commerce_cart_block', 'default', array($order->order_id), $_GET['q']),
        $content = theme('commerce_cart_block', $variables);
      }
    if (empty($content)) {
      // Default to an empty cart block message.
      $content = theme('commerce_cart_empty_block');
    }

    return array('subject' => t('Shopping cart'), 'content' => $content);
  }
}

 /**
 * Checks if a cart order should be refreshed based on the shopping cart refresh
 * settings on the order settings form.
 *
 * @param $order
 *   The cart order to check.
 *
 * @return
 *   Boolean indicating whether or not the cart order can be refreshed.
 */
function commerce_cart_order_can_refresh($order) {
  $can_refresh = module_invoke_all('commerce_cart_order_can_refresh', $order);

  // We refresh the cart order if both of the following conditions are met:
  // - No modules say to skip the cart refresh.
  // - At least one module allows it.
  // If no module specified either allow or skip, we fall back to the
  // logic below.
  if (in_array(FALSE, $can_refresh, TRUE)) {
    return FALSE;
  }
  elseif (in_array(TRUE, $can_refresh, TRUE)) {
    return TRUE;
  }
  global $user;

  // Force the shopping cart refresh on /cart and /checkout/* paths if enabled.
  if (variable_get('commerce_cart_refresh_force', TRUE) &&
    (current_path() == 'cart' || strpos(current_path(), 'checkout/') === 0)) {
    return TRUE;
  }

  // Prevent refresh for orders that don't match the current refresh mode.
  switch (variable_get('commerce_cart_refresh_mode', COMMERCE_CART_REFRESH_OWNER_ONLY)) {
    case COMMERCE_CART_REFRESH_OWNER_ONLY:
      // If the order is anonymous, check the session to see if the order
      // belongs to the current user. Otherwise just check that the order uid
      // matches the current user.
      if ($order->uid == 0 && !commerce_cart_order_session_exists($order->order_id)) {
        return FALSE;
      }
      elseif ($order->uid != $user->uid) {
        return FALSE;
      }
      break;

    case COMMERCE_CART_REFRESH_ACTIVE_CART_ONLY:
      // Check to see if the order ID matches the current user's cart order ID.
      if (commerce_cart_order_id($user->uid) != $order->order_id) {
        return FALSE;
      }
      break;

    case COMMERCE_CART_REFRESH_ALWAYS:
    default:
      // Continue on if shopping cart orders should always refresh.
      break;
  }

  // Check to see if the last cart refresh happened long enough ago.
  $seconds = variable_get('commerce_cart_refresh_frequency', COMMERCE_CART_REFRESH_DEFAULT_FREQUENCY);

  if (!empty($seconds) && !empty($order->data['last_cart_refresh']) &&
    REQUEST_TIME - $order->data['last_cart_refresh'] < $seconds) {
    return FALSE;
  }

  return TRUE;
}

/**
 * Implements hook_commerce_order_load().
 *
 * Because shopping carts are merely a special case of orders, we work through
 * the Order API to ensure that products in shopping carts are kept up to date.
 * Therefore, each time a cart is loaded, we calculate afresh the unit and total
 * prices of product line items and save them if any values have changed.
 */
function commerce_cart_commerce_order_load($orders) {
  $refreshed = &drupal_static(__FUNCTION__, array());
    // Refresh only if this order object represents the latest revision of a
    // shopping cart order, it hasn't been refreshed already in this request
    // and it meets the criteria in the shopping cart refresh settings.
    if (isset($refreshed[$order->order_id]) ||
      commerce_entity_is_unchanged('commerce_order', $order) ||
      !commerce_cart_order_is_cart($order) ||
      !commerce_order_is_latest_revision($order) ||
      !commerce_cart_order_can_refresh($order)) {
      continue;
    }
    // Update the last cart refresh timestamp and record the order's current
    // changed timestamp to detect if the order is actually updated.
    $order->data['last_cart_refresh'] = REQUEST_TIME;

    $unchanged_data = $order->data;
    $last_changed = $order->changed;

    // Refresh the order and add its ID to the refreshed array.
    $refreshed[$order->order_id] = TRUE;
    commerce_cart_order_refresh($order);

    // If order wasn't updated during the refresh, we need to manually update
    // the last cart refresh timestamp in the database.
    if ($order->changed == $last_changed) {
      db_update('commerce_order')
        ->fields(array('data' => serialize($unchanged_data)))
        ->condition('order_id', $order->order_id)
        ->execute();

      db_update('commerce_order_revision')
        ->fields(array('data' => serialize($unchanged_data)))
        ->condition('order_id', $order->order_id)
        ->condition('revision_id', $order->revision_id)
        ->execute();
/**
 * Themes an empty shopping cart block's contents.
 */
function theme_commerce_cart_empty_block() {
  return '<div class="cart-empty-block">' . t('Your shopping cart is empty.') . '</div>';
}

/**
 * Themes an empty shopping cart page.
 */
function theme_commerce_cart_empty_page() {
  return '<div class="cart-empty-page">' . t('Your shopping cart is empty.') . '</div>';
/**
 * Loads the shopping cart order for the specified user.
 *
 *   The uid of the customer whose cart to load. If left 0, attempts to load
 *   The fully loaded shopping cart order or FALSE if nonexistent.
function commerce_cart_order_load($uid = 0) {
  // Retrieve the order ID for the specified user's current shopping cart.
  $order_id = commerce_cart_order_id($uid);
  // If a valid cart order ID exists for the user, return it now.
  if (!empty($order_id)) {
    return commerce_order_load($order_id);
/**
 * Returns the current cart order ID for the given user.
 *
 * @param $uid
 *   The uid of the customer whose cart to load. If left 0, attempts to load
 *   an anonymous order from the session.
 *
 * @return
 *   The requested cart order ID or FALSE if none was found.
 */
function commerce_cart_order_id($uid = 0) {
  // Cart order IDs will be cached keyed by $uid.
  $cart_order_ids = &drupal_static(__FUNCTION__);

  // Cache the user's cart order ID if it hasn't been set already.
  if (isset($cart_order_ids[$uid])) {
    return $cart_order_ids[$uid];
  }

  // First let other modules attempt to provide a valid order ID for the given
  // uid. Instead of invoking hook_commerce_cart_order_id() directly, we invoke
  // it in each module implementing the hook and return the first valid order ID
  // returned (if any).
  foreach (module_implements('commerce_cart_order_id') as $module) {
    $order_id = module_invoke($module, 'commerce_cart_order_id', $uid);

    // If a hook said the user should not have a cart, that overrides any other
    // potentially valid order ID. Return FALSE now.
    if ($order_id === FALSE) {
      return FALSE;
    }

    // Otherwise only return a valid order ID.
    if (!empty($order_id) && is_int($order_id)) {
  // Create an array of valid shopping cart order statuses.
  $status_ids = array_keys(commerce_order_statuses(array('cart' => TRUE)));

  // If a customer uid was specified...
  if ($uid) {
    // Look for the user's most recent shopping cart order, although they
    // should never really have more than one.
    $cart_order_ids[$uid] = db_query('SELECT order_id FROM {commerce_order} WHERE uid = :uid AND status IN (:status_ids) ORDER BY order_id DESC', array(':uid' => $uid, ':status_ids' => $status_ids))->fetchField();
  }
  else {
    // Otherwise look for a shopping cart order ID in the session.
    if (commerce_cart_order_session_exists()) {
      // We can't trust a user's IP address to remain the same, especially since
      // it may be derived from a proxy server and not the actual client. As of
      // Commerce 1.4, this query no longer restricts order IDs based on IP
      // address, instead trusting Drupal to prevent session hijacking.
      $cart_order_ids[$uid] = db_query('SELECT order_id FROM {commerce_order} WHERE order_id IN (:order_ids) AND uid = 0 AND status IN (:status_ids) ORDER BY order_id DESC', array(':order_ids' => commerce_cart_order_session_order_ids(), ':status_ids' => $status_ids))->fetchField();
    }
    else {
      $cart_order_ids[$uid] = FALSE;
/**
 * Resets the cached array of shopping cart orders.
 */
function commerce_cart_order_ids_reset() {
  $cart_order_ids = &drupal_static('commerce_cart_order_id');
}

/**
 * Creates a new shopping cart order for the specified user.
 *
 *   The uid of the user for whom to create the order. If left 0, the order will
 *   be created for an anonymous user and associated with the current session
 *   if it is anonymous.
 * @param $type
 *   The type of the order; defaults to the standard 'commerce_order' type.
 * @return
 *   The newly created shopping cart order object.
 */
function commerce_cart_order_new($uid = 0, $type = 'commerce_order') {
  // Create the new order with the customer's uid and the cart order status.
  $order = commerce_order_new($uid, 'cart', $type);
  $order->log = t('Created as a shopping cart order.');

  // Save it so it gets an order ID and return the full object.
  commerce_order_save($order);
  // If the user is not logged in, ensure the order ID is stored in the session.
  if (!$uid && empty($user->uid)) {
    commerce_cart_order_session_save($order->order_id);
/**
 * Determines whether or not the given order is a shopping cart order.
 */
function commerce_cart_order_is_cart($order) {
  // If the order is in a shopping cart order status, assume it is a cart.
  $is_cart = array_key_exists($order->status, commerce_order_statuses(array('cart' => TRUE)));

  // Allow other modules to make the judgment based on some other criteria.
  foreach (module_implements('commerce_cart_order_is_cart') as $module) {
    $function = $module . '_commerce_cart_order_is_cart';

    // As of Drupal Commerce 1.2, $is_cart should be accepted by reference and
    // manipulated directly, but we still check for a return value to preserve
    // backward compatibility with the hook. In future versions, we will
    // deprecate hook_commerce_cart_order_is_cart() and force modules to update
    // to hook_commerce_cart_order_is_cart_alter().
    if ($function($order, $is_cart) === FALSE) {
      $is_cart = FALSE;
    }
  drupal_alter('commerce_cart_order_is_cart', $is_cart, $order);

 * Implements hook_commerce_entity_access_condition_commerce_order_alter().
 * This alter hook allows the Cart module to add conditions to the query used to
 * determine if a user has view access to a given order. The Cart module will
 * always grant users access to view their own carts (independent of any
 * permission settings) and also grants anonymous users access to view their
 * completed orders if they've been given the permission.
 */
function commerce_cart_commerce_entity_access_condition_commerce_order_alter(&$conditions, $context) {