home | career | drupal | java | mac | mysql | perl | php | scala | uml | unix

Drupal example source code file (hooks.php)

This example Drupal source code file (hooks.php) is included in the DevDaily.com "Drupal Source Code Warehouse" project. The intent of this project is to help you "Learn Drupal by Example".

PHP - Drupal tags/keywords

array, as, foreach, function, if, order, php, request, return, title, type, weight, xml_data

The hooks.php Drupal example source code

<?php
// $Id: hooks.php,v 1.1.2.24 2010/07/16 15:45:09 islandusurper Exp $

/**
 * @file
 * These are the hooks that are invoked by the Ubercart core.
 *
 * Core hooks are typically called in all modules at once using
 * module_invoke_all().
 */

/**
 * @addtogroup hooks
 * @{
 */

/**
 * Do extra processing when an item is added to the shopping cart.
 *
 * Some modules need to be able to hook into the process of adding items to a
 * cart. For example, an inventory system may need to check stock levels and
 * prevent an out of stock item from being added to a customer's cart. This hook
 * lets developers squeeze right in at the end of the process after the product
 * information is all loaded and the product is about to be added to the cart.
 * In the event that a product should not be added to the cart, you simply have
 * to return a failure message described below. This hook may also be used simply
 * to perform some routine action when products are added to the cart.
 *
 * @param $nid
 *   The node ID of the product
 * @param $qty
 *   The quantity being added
 * @param $data
 *   The data array, including attributes and model number adjustments
 * @return
 *   The function can use this data to whatever purpose to see if the item can
 *   be added to the cart or not. The function should return an array containing
 *   the result array. (This is due to the nature of Drupal's module_invoke_all()
 *   function. You must return an array within an array or other module data will
 *   end up getting ignored.) At this moment, there are only three keys:
 *   - "success": TRUE or FALSE for whether the specified quantity of the item
 *       may be added to the cart or not; defaults to TRUE.
 *   - "message": the fail message to display in the event of a failure; if
 *       omitted, Ubercart will display a default fail message.
 *   - "silent": return TRUE to suppress the display of any messages; useful
 *       when a module simply needs to do some other processing during an add to
 *       cart or fail silently.
 */
function hook_add_to_cart($nid, $qty, $data) {
  if ($qty > 1) {
    $result[] = array(
      'success' => FALSE,
      'message' => t('Sorry, you can only add one of those at a time.'),
    );
  }
  return $result;
}

/**
 * Add extra information to a cart item's "data" array.
 *
 * This is effectively the submit handler of any alterations to the Add to Cart
 * form. It provides a standard way to store the extra information so that it
 * can be used by hook_add_to_cart().
 *
 * @param $form_values
 *   The values submitted to the Add to Cart form.
 * @return
 *   An array of data to be merged into the item added to the cart.
 */
function hook_add_to_cart_data($form_values) {
  $node = node_load($form_values['nid']);
  return array('module' => 'uc_product', 'shippable' => $node->shippable);
}

/**
 * Calculate tax line items for an order.
 *
 * @param $order
 *   An order object or an order id.
 * @return
 *   An array of tax line item objects keyed by a module-specific id.
 */
function hook_calculate_tax($order) {
  global $user;
  if (is_numeric($order)) {
    $order = uc_order_load($order);
    $account = user_load(array('uid' => $order->uid));
  }
  elseif ((int)$order->uid) {
    $account = user_load(array('uid' => intval($order->uid)));
  }
  else {
    $account = $user;
  }
  if (!is_object($order)) {
    return array();
  }
  if (empty($order->delivery_postal_code)) {
    $order->delivery_postal_code = $order->billing_postal_code;
  }
  if (empty($order->delivery_zone)) {
    $order->delivery_zone = $order->billing_zone;
  }
  if (empty($order->delivery_country)) {
    $order->delivery_country = $order->billing_country;
  }

  $order->taxes = array();

  if (isset($order->order_status)) {
    $state = uc_order_status_data($order->order_status, 'state');
    $use_same_rates = in_array($state, array('payment_received', 'completed'));
  }
  else {
    $use_same_rates = FALSE;
  }

  $arguments = array(
    'order' => array(
      '#entity' => 'uc_order',
      '#title' => t('Order'),
      '#data' => $order,
    ),
    'tax' => array(
      '#entity' => 'tax',
      '#title' => t('Tax rule'),
      // #data => each $tax in the following foreach() loop;
    ),
    'account' => array(
      '#entity' => 'user',
      '#title' => t('User'),
      '#data' => $account,
    ),
  );

  $predicates = ca_load_trigger_predicates('calculate_taxes');
  foreach (uc_taxes_rate_load() as $tax) {
    if ($use_same_rates) {
      foreach ((array)$order->line_items as $old_line) {
        if ($old_line['type'] == 'tax' && $old_line['data']['tax_id'] == $tax->id) {
          $tax->rate = $old_line['data']['tax_rate'];
          break;
        }
      }
    }

    $arguments['tax']['#data'] = $tax;
    if (ca_evaluate_conditions($predicates['uc_taxes_'. $tax->id], $arguments)) {
      $line_item = uc_taxes_action_apply_tax($order, $tax);
      if ($line_item) {
        $order->taxes[$line_item->id] = $line_item;
      }
    }
  }

  return $order->taxes;
}

/**
 * Control the display of an item in the cart.
 *
 * Product type modules allow the creation of nodes that can be added to the
 * cart. The cart determines how they are displayed through this hook. This is
 * especially important for product kits, because it may be displayed as a single
 * unit in the cart even though it is represented as several items.
 *
 * @param $item
 *   The item in the cart to display.
 * @return
 *   A form array containing the following elements:
 *   - "nid"
 *     - #type: value
 *     - #value: The node id of the $item.
 *   - "module"
 *     - #type: value
 *     - #value: The module implementing this hook and the node represented by
 *       $item.
 *   - "remove"
 *     - #type: checkbox
 *     - #value: If selected, removes the $item from the cart.
 *   - "description"
 *     - #type: markup
 *     - #value: Themed markup (usually an unordered list) displaying extra information.
 *   - "title"
 *     - #type: markup
 *     - #value: The displayed title of the $item.
 *   - "#total"
 *     - "type": float
 *     - "value": Numeric price of $item. Notice the '#' signifying that this is
 *       not a form element but just a value stored in the form array.
 *   - "data"
 *     - #type: hidden
 *     - #value: The serialized $item->data.
 *   - "qty"
 *     - #type: textfield
 *     - #value: The quantity of $item in the cart. When "Update cart" is clicked,
 *         the customer's input is saved to the cart.
 */
function hook_cart_display($item) {
  $node = node_load($item->nid);
  $element = array();
  $element['nid'] = array('#type' => 'value', '#value' => $node->nid);
  $element['module'] = array('#type' => 'value', '#value' => 'uc_product');
  $element['remove'] = array('#type' => 'checkbox');

  $element['title'] = array(
    '#value' => node_access('view', $node) ? l($item->title, 'node/'. $node->nid) : check_plain($item->title),
  );

  $context = array(
    'revision' => 'altered',
    'type' => 'cart_item',
    'subject' => array(
      'cart_item' => $item,
      'node' => $node,
    ),
  );
  $price_info = array(
    'price' => $item->price,
    'qty' => $item->qty,
  );

  $element['#total'] = uc_price($price_info, $context);
  $element['data'] = array('#type' => 'hidden', '#value' => serialize($item->data));
  $element['qty'] = array(
    '#type' => 'textfield',
    '#default_value' => $item->qty,
    '#size' => 5,
    '#maxlength' => 6
  );

  if ($description = uc_product_get_description($item)) {
    $element['description'] = array('#value' => $description);
  }

  return $element;
}

/**
 * Add extra data about an item in the cart.
 *
 * Products that are added to a customer's cart are referred as items until the
 * sale is completed. Just think of a grocery store having a bunch of products
 * on the shelves but putting a sign over the express lane saying "15 Items or
 * Less." hook_cart_item() is in charge of acting on items at various times like
 * when they are being added to a cart, saved, loaded, and checked out.
 *
 * Here's the rationale for this hook: Products may change on a live site during
 * a price increase or change to attribute adjustments. If a user has previously
 * added an item to their cart, when they go to checkout or view their cart
 * screen we want the latest pricing and model numbers to show. So, the essential
 * product information is stored in the cart, but when the items in a cart are
 * loaded, modules are given a chance to adjust the data against the latest settings.
 *
 * @param $op
 *   The action that is occurring. Possible values:
 *   - "load" - Passed for each item when a cart is being loaded in the function
 *       uc_cart_get_contents(). This gives modules the chance to tweak information
 *       for items when the cart is being loaded prior to being view or added to
 *       an order. No return value is expected.
 *   - "can_ship" - Passed when a cart is being scanned for items that are not
 *       shippable items. Übercart will bypass cart and checkout operations
 *       specifically related to tangible products if nothing in the cart is
 *       shippable. hook_cart_item functions that check for this op are expected
 *       to return TRUE or FALSE based on whether a product is shippable or not.
 * @return
 *   No return value for load.
 *   TRUE or FALSE for can_ship.
 */
function hook_cart_item($op, &$item) {
  switch ($op) {
    case 'load':
      $term = array_shift(taxonomy_node_get_terms_by_vocabulary($item->nid, variable_get('uc_manufacturer_vid', 0)));
      $arg1->manufacturer = $term->name;
      break;
  }
}

/**
 * Register callbacks for a cart pane.
 *
 * The default cart view page displays a table of the cart contents and a few
 * simple form features to manage the cart contents. For a module to add
 * information to this page, it must use hook_cart_pane to define extra panes
 * that may be ordered to appear above or below the default information.
 *
 * @param $items
 *   The current contents of the shopping cart.
 * @return
 *   The function is expected to return an array of pane arrays with the following
 *   keys:
 *   - "id"
 *     - type: string
 *     - value: The internal ID of the pane, using a-z, 0-9, and - or _.
 *   - "title"
 *     - type: string
 *     - value: The name of the cart pane displayed to the user.  Use t().
 *   - "enabled"
 *     - type: boolean
 *     - value: Whether the pane is enabled by default or not. (Defaults to TRUE.)
 *   - "weight"
 *     - type: integer
 *     - value: The weight of the pane to determine its display order. (Defaults
 *         to 0.)
 *   - "body"
 *     - type: string
 *     - value: The body of the pane when rendered on the cart view screen.
 *
 * The body gets printed to the screen if it is on the cart view page.  For the
 * settings page, the body field is ignored.  You may want your function to check
 * for a NULL argument before processing any queries or foreach() loops.
 */
function hook_cart_pane($items) {
  $panes[] = array(
    'id' => 'cart_form',
    'title' => t('Default cart form'),
    'enabled' => TRUE,
    'weight' => 0,
    'body' => !is_null($items) ? drupal_get_form('uc_cart_view_form', $items) : '',
  );

  return $panes;
}

/**
 * Alter cart pane definitions.
 *
 * @param $panes
 *   The array of pane information in the format defined in hook_cart_pane(), passed
 *   by reference.
 *
 * @param $items
 *   The array of item information.
 */
function hook_cart_pane_alter(&$panes, $items) {
  foreach ($panes as &$pane) {
    if ($pane['id'] == 'cart') {
      $pane['body'] = drupal_get_form('my_custom_pane_form_builder', $items);
    }
  }
}

/**
 * Register callbacks for a checkout pane.
 *
 * The checkout screen for Ubercart is a compilation of enabled checkout panes.
 * A checkout pane can be used to display order information, collect data from
 * the customer, or interact with other panes. Panes are defined in enabled modules
 * with hook_checkout_pane() and displayed and processed through specified callback
 * functions. Some of the settings for each pane are configurable from the checkout
 * settings page with defaults being specified in the hooks.
 *
 * The default panes are defined in uc_cart.module in the function
 * uc_cart_checkout_pane(). These include panes to display the contents of the
 * shopping cart and to collect essential site user information, a shipping address,
 * a payment address, and order comments. Other included modules offer panes for
 * shipping and payment purposes as well.
 *
 * @return
 *   An array of checkout pane arrays using the following keys:
 *   - "id"
 *     - type: string
 *     - value: The internal ID of the checkout pane, using a-z, 0-9, and - or _.
 *   - "title"
 *     - type: string
 *     - value:The name of the pane as it appears on the checkout form.
 *   - "desc"
 *     - type: string
 *     - value: A short description of the pane for the admin pages.
 *   - "callback"
 *     - type: string
 *     - value: The name of the callback function for this pane.  View
 *         @link http://www.ubercart.org/docs/developer/245/checkout this page @endlink
 *         for more documentation and examples of checkout pane callbacks.
 *   - "weight"
 *     - type: integer
 *     - value: Default weight of the pane, defining its order on the checkout form.
 *   - "enabled"
 *     - type: boolean
 *     - value: Optional. Whether or not the pane is enabled by default. Defaults
 *         to TRUE.
 *   - "process"
 *     - type: boolean
 *     - value: Optional. Whether or not this pane needs to be processed when the
 *         checkout form is submitted. Defaults to TRUE.
 *   - "collapsible"
 *     - type: boolean
 *     - value: Optional. Whether or not this pane is displayed as a collapsible
 *         fieldset. Defaults to TRUE.
 */
function hook_checkout_pane() {
  $panes[] = array(
    'id' => 'cart',
    'callback' => 'uc_checkout_pane_cart',
    'title' => t('Cart Contents'),
    'desc' => t("Display the contents of a customer's shopping cart."),
    'weight' => 1,
    'process' => FALSE,
    'collapsible' => FALSE,
  );
  return $panes;
}

/**
 * Alter checkout pane definitions.
 *
 * @param $panes
 *   Array with the panes information as defined in hook_checkout_pane(), passed
 *   by reference.
 */
function hook_checkout_pane_alter(&$panes) {
  foreach ($panes as &$pane) {
    if ($pane['id'] == 'cart') {
      $pane['callback'] = 'my_custom_module_callback';
    }
  }
}

/**
 * Give clearance to a user to download a file.
 *
 * By default the uc_file module can implement 3 restrictions on downloads: by
 * number of IP addresses downloaded from, by number of downloads, and by a set
 * expiration date. Developers wishing to add further restrictions can do so by
 * implementing this hook. After the 3 aforementioned restrictions are checked,
 * the uc_file module will check for implementations of this hook.
 *
 * @param $user
 *   The drupal user object that has requested the download
 * @param $file_download
 *   The file download object as defined as a row from the uc_file_users table
 *   that grants the user the download
 * @return
 *   TRUE or FALSE depending on whether the user is to be permitted download of
 *   the requested files. When a implementation returns FALSE it should set an
 *   error message in Drupal using drupal_set_message() to inform customers of
 *   what is going on.
 */
function hook_download_authorize($user, $file_download) {
  if (!$user->status) {
    drupal_set_message(t("This account has been banned and can't download files anymore. "),'error');
    return FALSE;
  }
  else {
    return TRUE;
  }
}

/**
 * Perform actions on file products.
 *
 * The uc_file module comes with a file manager (found at Administer » Store
 * administration » Products » View file downloads) that provides some basic
 * functionality: deletion of multiple files and directories, and upload of single
 * files (those looking to upload multiple files should just directly upload them
 * to their file download directory then visit the file manager which automatically
 * updates new files found in its directory). Developers that need to create more
 * advanced actions with this file manager can do so by using this hook.
 *
 * @param $op
 *   The operation being taken by the hook, possible ops defined below.
 *   - 'info': Called before the uc_file module builds its list of possible file
 *       actions. This op is used to define new actions that will be placed in
 *       the file action select box.
 *   - 'insert': Called after uc_file discovers a new file in the file download
 *       directory.
 *   - 'form': When any defined file action is selected and submitted to the form
 *       this function is called to render the next form. Because this is called
 *       whenever a module-defined file action is selected, the variable
 *       $args['action'] can be used to define a new form or append to an existing
 *       form.
 *   - 'upload': After a file has been uploaded, via the file manager's built in
 *       file upload function, and moved to the file download directory this op
 *       can perform any remaining operations it needs to perform on the file
 *       before its placed into the uc_files table.
 *   - 'upload_validate': This op is called to validate the uploaded file that
 *       was uploaded via the file manager's built in file upload function. At
 *       this point, the file has been uploaded to PHP's temporary directory.
 *       Files passing this upload validate function will be moved into the file
 *       downloads directory.
 *   - 'validate': This op is called to validate the file action form.
 *   - 'submit': This op is called to submit the file action form.
 * @param $args
 *   A keyed array of values that varies depending on the op being performed,
 *   possible values defined below.
 *   - 'info': None
 *   - 'insert':
 *     - 'file_object': The file object of the newly discovered file
 *   - 'form':
 *     - 'action': The file action being performed as defined by the key in the
 *         array sent by hook_file_action($op = 'info')
 *     - 'file_ids' - The file ids (as defined in the uc_files table) of the
 *          selected files to perform the action on
 *   - 'upload':
 *     - 'file_object': The file object of the file moved into file downloads
 *         directory
 *     - 'form_id': The form_id variable of the form_submit function
 *     - 'form_values': The form_values variable of the form_submit function
 *   - 'upload_validate':
 *     - 'file_object': The file object of the file that has been uploaded into
 *         PHP's temporary upload directory
 *     - 'form_id': The form_id variable of the form_validate function
 *     - 'form_values': The form_values variable of the form_validate function
 *   - 'validate':
 *     - 'form_id': The form_id variable of the form_validate function
 *     - 'form_values': The form_values variable of the form_validate function
 *   - 'submit':
 *     - 'form_id': The form_id variable of the form_submit function
 *     - 'form_values': The form_values variable of the form_submit function
 * @return
 *   The return value of hook depends on the op being performed, possible return
 *   values defined below.
 *   - 'info': The associative array of possible actions to perform. The keys are
 *       unique strings that defines the actions to perform. The values are the
 *       text to be displayed in the file action select box.
 *   - 'insert': None
 *   - 'form': This op should return an array of drupal form elements as defined
 *       by the drupal form API.
 *   - 'upload': None
 *   - 'upload_validate': None
 *   - 'validate': None
 *   - 'submit': None
 */
function hook_file_action($op, $args) {
  switch ($op) {
    case 'info':
      return array('uc_image_watermark_add_mark' => 'Add Watermark');
    case 'insert':
      //automatically adds watermarks to any new files that are uploaded to the file download directory
      _add_watermark($args['file_object']->filepath);
    break;
    case 'form':
      if ($args['action'] == 'uc_image_watermark_add_mark') {
        $form['watermark_text'] = array(
          '#type' => 'textfield',
          '#title' => t('Watermark Text'),
        );
        $form['submit_watermark'] = array(
          '#type' => 'submit',
          '#value' => t('Add Watermark'),
        );
      }
    return $form;
    case 'upload':
      _add_watermark($args['file_object']->filepath);
      break;
    case 'upload_validate':
      //Given a file path, function checks if file is valid JPEG
      if(!_check_image($args['file_object']->filepath)) {
        form_set_error('upload',t('Uploaded file is not a valid JPEG'));
      }
    break;
    case 'validate':
      if ($args['form_values']['action'] == 'uc_image_watermark_add_mark') {
        if (empty($args['form_values']['watermark_text'])) {
          form_set_error('watermar_text',t('Must fill in text'));
        }
      }
    break;
    case 'submit':
      if ($args['form_values']['action'] == 'uc_image_watermark_add_mark') {
        foreach ($args['form_values']['file_ids'] as $file_id) {
          $filename = db_result(db_query("SELECT filename FROM {uc_files} WHERE fid = %d",$file_id));
          //Function adds watermark to image
          _add_watermark($filename);
        }
      }
    break;
  }
}

/**
 * Make changes to a file before it is downloaded by the customer.
 *
 * Stores, either for customization, copy protection or other reasons, might want
 * to send customized downloads to customers. This hook will allow this to happen.
 * Before a file is opened to be transfered to a customer, this hook will be called
 * to make any altercations to the file that will be used to transfer the download
 * to the customer. This, in effect, will allow a developer to create a new,
 * personalized, file that will get transfered to a customer.
 *
 * @param $file_user
 *   The file_user object (i.e. an object containing a row from the uc_file_users
 *   table) that corresponds with the user download being accessed.
 * @param $ip
 *   The IP address from which the customer is downloading the file
 * @param $fid
 *   The file id of the file being transfered
 * @param $file
 *   The file path of the file to be transfered
 * @return
 *   The path of the new file to transfer to customer.
 */
function hook_file_transfer_alter($file_user, $ip, $fid, $file) {
  $file_data = file_get_contents($file)." [insert personalized data]"; //for large files this might be too memory intensive
  $new_file = tempnam(file_directory_temp(),'tmp');
  file_put_contents($new_file,$file_data);
  return $new_file;
}

/**
 * Used to define line items that are attached to orders.
 *
 * A line item is a representation of charges, fees, and totals for an order.
 * Default line items include the subtotal and total line items, the tax line
 * item, and the shipping line item. There is also a generic line item that store
 * admins can use to add extra fees and discounts to manually created orders.
 * Module developers will use this hook to define new types of line items for
 * their stores. An example use would be for a module that allows customers to
 * use coupons and wants to represent an entered coupon as a line item.
 *
 * Once a line item has been defined in hook_line_item, Übercart will begin
 * interacting with it in various parts of the code. One of the primary ways this
 * is done is through the callback function you specify for the line item.
 *
 * @return
 *   Your hook should return an array of associative arrays. Each item in the
 *   array represents a single line item and should use the following keys:
 *   - "id"
 *     - type: string
 *     - value: The internal ID of the line item.
 *   - "title"
 *     - type: string
 *     - value: The title of the line item shown to the user in various interfaces.
 *         Use t().
 *   - "callback"
 *     - type: string
 *     - value: Name of the line item's callback function, called for various
 *         operations.
 *   - "weight"
 *     - type: integer
 *     - value: Display order of the line item in lists; "lighter" items are
 *         displayed first.
 *   - "stored"
 *     - type: boolean
 *     - value: Whether or not the line item will be stored in the database.
 *         Should be TRUE for any line item that is modifiable from the order
 *         edit screen.
 *   - "add_list"
 *     - type: boolean
 *     - value: Whether or not a line item should be included in the "Add a Line
 *         Item" select box on the order edit screen.
 *   - "calculated"
 *     - type: boolean
 *     - value: Whether or not the value of this line item should be added to the
 *         order total. (Ex: would be TRUE for a shipping charge line item but
 *         FALSE for the subtotal line item since the product prices are already
 *         taken into account.)
 *   - "display_only"
 *     - type: boolean
 *     - value: Whether or not this line item is simply a display of information
 *         but not calculated anywhere. (Ex: the total line item uses display to
 *         simply show the total of the order at the bottom of the list of line
 *         items.)
 */
function hook_line_item() {
  $items[] = array(
    'id' => 'generic',
    'title' => t('Empty Line'),
    'weight' => 2,
    'default' => FALSE,
    'stored' => TRUE,
    'add_list' => TRUE,
    'calculated' => TRUE,
    'callback' => 'uc_line_item_generic',
  );

  return $items;
}

/**
 * Alter a line item on an order when the order is loaded.
 *
 * @param &$item
 *   The line item array.
 * @param $order
 *   The order object containing the line item.
 */
function hook_line_item_alter(&$item, $order) {
  $account = user_load($order->uid);
  ca_pull_trigger('calculate_line_item_discounts', $item, $account);
}

/**
 * Alter the line item definitions declared in hook_line_item().
 *
 * @param &$items
 *   The combined return value of hook_line_item().
 */
function hook_line_item_data_alter(&$items) {
  foreach ($items as &$item) {
    // Tax amounts are added in to other line items, so the actual tax line
    // items should not be added to the order total.
    if ($item['id'] == 'tax') {
      $item['calculated'] = FALSE;
    }
    // Taxes are included already, so the subtotal without taxes doesn't
    // make sense.
    elseif ($item['id'] == 'tax_subtotal') {
      $item['callback'] = NULL;
    }
  }
}


/**
 * Perform actions on orders.
 *
 * An order in Übercart represents a single transaction. Orders are created
 * during the checkout process where they sit in the database with a status of In
 * Checkout. When a customer completes checkout, the order's status gets updated
 * to show that the sale has gone through. Once an order is created, and even
 * during its creation, it may be acted on by any module to connect extra
 * information to an order. Every time an action occurs to an order, hook_order()
 * gets invoked to let your modules know what's happening and make stuff happen.
 *
 * @param $op
 *   The action being performed.
 * @param &$arg1
 *   This is the order object or a reference to it as noted below.
 * @param $arg2
 *   This is variable and is based on the value of $op:
 *   - new: Called when an order is created. $arg1 is a reference to the new
 *       order object, so modules may add to or modify the order at creation.
 *   - save: When an order object is being saved, the hook gets invoked with this
 *       op to let other modules do any necessary saving. $arg1 is a reference to
 *       the order object.
 *   - load: Called when an order is loaded after the order and product data has
 *       been loaded from the database. Passes $arg1 as the reference to the
 *       order object, so modules may add to or modify the order object when it's
 *       loaded.
 *   - submit: When a sale is being completed and the customer has clicked the
 *       Submit order button from the checkout screen, the hook is invoked with
 *       this op. This gives modules a chance to determine whether or not the
 *       order should be allowed. An example use of this is the credit module
 *       attempting to process payments when an order is submitted and returning
 *       a failure message if the payment failed.
 *
 *       To prevent an order from passing through, you must return an array
 *       resembling the following one with the failure message:
 *       @code
 *         return array(array('pass' => FALSE, 'message' => t('We were unable to process your credit card.')));
 *       @endcode
 *   - can_update: Called before an order's status is changed to make sure the
 *       order can be updated. $arg1 is the order object with the old order
 *       status ID ($arg1->order_status), and $arg2 is simply the new order
 *       status ID. Return FALSE to stop the update for some reason.
 *   - update: Called when an order's status is changed. $arg1 is the order
 *       object with the old order status ID ($arg1->order_status), and $arg2 is
 *       the new order status ID.
 *   - can_delete: Called before an order is deleted to verify that the order may
 *       be deleted. Returning FALSE will prevent a delete from happening. (For
 *       example, the payment module returns FALSE by default when an order has
 *       already received payments.)
 *   - delete: Called when an order is deleted and before the rest of the order
 *       information is removed from the database. Passes $arg1 as the order
 *       object to let your module clean up it's tables.
 *   - total: Called when the total for an order is being calculated after the
 *       total of the products has been added. Passes $arg1 as the order object.
 *       Expects in return a value (positive or negative) by which to modify the
 *       order total.
 */
function hook_order($op, &$arg1, $arg2) {
  switch ($op) {
    case 'save':
      // Do something to save payment info!
      break;
  }
}

/**
 * Add links to local tasks for orders on the admin's list of orders.
 *
 * @param $order
 *   An order object.
 * @return
 *   An array of specialized link arrays. Each link has the following keys:
 *   - "name": The title of page being linked.
 *   - "url": The link path. Do not use url(), but do use the $order's order_id.
 *   - "icon": HTML of an image.
 *   - "title": Title attribute text (mouseover tool-tip).
 */
function hook_order_actions($order) {
  $actions = array();
  $module_path = base_path() . drupal_get_path('module', 'uc_shipping');
  if (user_access('fulfill orders')) {
    $result = db_query("SELECT nid FROM {uc_order_products} WHERE order_id = %d AND data LIKE '%%s:9:\"shippable\";s:1:\"1\";%%'", $order->order_id);
    if (db_num_rows($result)) {
      $title = t('Package order !order_id products.', array('!order_id' => $order->order_id));
      $actions[] = array(
        'name' => t('Package'),
        'url' => 'admin/store/orders/'. $order->order_id .'/packages',
        'icon' => '<img src="'. $module_path .'/images/package.gif" alt="'. $title .'" />',
        'title' => $title,
      );
      $result = db_query("SELECT package_id FROM {uc_packages} WHERE order_id = %d", $order->order_id);
      if (db_num_rows($result)) {
        $title = t('Ship order !order_id packages.', array('!order_id' => $order->order_id));
        $actions[] = array(
          'name' => t('Ship'),
          'url' => 'admin/store/orders/'. $order->order_id .'/shipments',
          'icon' => '<img src="'. $module_path .'/images/ship.gif" alt="'. $title .'" />',
          'title' => $title,
        );
      }
    }
  }
  return $actions;
}

/**
 * Register callbacks for an order pane.
 *
 * This hook is used to add panes to the order viewing and administration screens.
 * The default panes include areas to display and edit addresses, products,
 * comments, etc. Developers should use this hook when they need to display or
 * modify any custom data pertaining to an order. For example, a store that uses
 * a custom checkout pane to find out a customer's desired delivery date would
 * then create a corresponding order pane to show the data on the order screens.
 *
 * hook_order_pane() works by defining new order panes and providing a little bit
 * of information about them. View the return value section below for information
 * about what parts of an order pane are defined by the hook.
 *
 * The real meat of an order pane is its callback function (which is specified in
 * the hook). The callback function handles what gets displayed on which screen
 * and what data can be manipulated. That is all somewhat out of the scope of
 * this API page, so you'll have to click here to read more about what a callback
 * function should contain.
*/
function hook_order_pane() {
  $panes[] = array(
    'id' => 'payment',
    'callback' => 'uc_order_pane_payment',
    'title' => t('Payment'),
    'desc' => t('Specify and collect payment for an order.'),
    'class' => 'pos-left',
    'weight' => 4,
    'show' => array('view', 'edit', 'customer'),
  );
  return $panes;
}

/**
 * Allows modules to alter ordered products when they're loaded with an order.
 *
 * @param &$product
 *   The product object as found in the $order object.
 * @param $order
 *   The order object to which the product belongs.
 * @return
 *   Nothing should be returned. Hook implementations should receive the
 *     $product object by reference and alter it directly.
 */
function hook_order_product_alter(&$product, $order) {
  drupal_set_message('hook_order_product_alter(&$product, $order):');
  drupal_set_message('&$product: <pre>'. print_r($product, TRUE) .'</pre>');
  drupal_set_message('$order: <pre>'. print_r($order, TRUE) .'</pre>');
}

/**
 * Register static order states.
 *
 * Order states are module-defined categories for order statuses. Each state
 * will have a default status that is used when modules need to move orders to
 * new state, but don't know which status to use.
 *
 * @return
 *   An array of order state definitions. Each definition is an array with the
 *   following keys:
 *   - id: The machine-readable name of the order state.
 *   - title: The human-readable, translated name.
 *   - weight: The list position of the state.
 *   - scope: Either "specific" or "general".
 */
function hook_order_state() {
  $states[] = array(
    'id' => 'canceled',
    'title' => t('Canceled'),
    'weight' => -20,
    'scope' => 'specific',
  );
  $states[] = array(
    'id' => 'in_checkout',
    'title' => t('In checkout'),
    'weight' => -10,
    'scope' => 'specific',
  );
  $states[] = array(
    'id' => 'post_checkout',
    'title' => t('Post checkout'),
    'weight' => 0,
    'scope' => 'general',
  );
  $states[] = array(
    'id' => 'completed',
    'title' => t('Completed'),
    'weight' => 20,
    'scope' => 'general',
  );

  return $states;
}

/**
 * Register payment gateway callbacks.
 *
 * @see @link http://www.ubercart.org/docs/api/hook_payment_gateway @endlink
 *
 * @return
 *   Returns an array of payment gateways, which are arrays with the following keys:
 *   - "id"
 *     - type: string
 *     - value: The internal ID of the payment gateway, using a-z, 0-9, and - or
 *         _.
 *   - "title"
 *     - type: string
 *     - value: The name of the payment gateway displayed to the user. Use t().
 *   - "description"
 *     - type: string
 *     - value: A short description of the payment gateway.
 *   - "settings"
 *     - type: string
 *     - value: The name of a function that returns an array of settings form
 *         elements for the gateway.
 */
function hook_payment_gateway() {
  $gateways[] = array(
    'id' => 'test_gateway',
    'title' => t('Test Gateway'),
    'description' => t('Process credit card payments through the Test Gateway.'),
    'credit' => 'test_gateway_charge',
  );
  return $gateways;
}

/**
 * Register callbacks for payment methods.
 *
 * Payment methods are different ways to collect payment. By default, Übercart
 * comes with support for check, credit card, and generic payments. Payment
 * methods show up at checkout or on the order administration screens, and they
 * collect different sorts of information from the user that is used to process
 * or track the payment.
 *
 * @return
 *   An array of payment methods.
 */
function hook_payment_method() {
  $methods[] = array(
    'id' => 'check',
    'name' => t('Check'),
    'title' => t('Check or Money Order'),
    'desc' => t('Pay by mailing a check or money order.'),
    'callback' => 'uc_payment_method_check',
    'weight' => 1,
    'checkout' => TRUE,
  );
  return $methods;
}

/**
 * Perform actions on product classes.
 *
 * @param $type
 *   The node type of the product class.
 * @param $op
 *   The action being performed on the product class:
 *   - "insert": A new node type is created, or an existing node type is being
 *       converted into a product type.
 *   - "update": A product class has been updated.
 *   - "delete": A product class has been deleted. Modules that have attached
 *       additional information to the node type because it is a product type
 *       should delete this information.
 */
function hook_product_class($type, $op) {
  switch ($op) {
    case 'delete':
      db_query("DELETE FROM {uc_class_attributes} WHERE pcid = '%s'", $type);
      db_query("DELETE FROM {uc_class_attribute_options} WHERE pcid = '%s'", $type);
    break;
  }
}

/**
 * Return a structured array representing the given product's description.
 *
 * Modules that add data to cart items when they are selected should display it
 * with this hook. The return values from each implementation will be
 * sent through to hook_product_description_alter() implementations and then
 * all descriptions are rendered using drupal_render().
 *
 * @param $product
 *   Product. Usually one of the values of the array returned by
 *   uc_cart_get_contents().
 * @return
 *   A structured array that can be fed into drupal_render().
 */
function hook_product_description($product) {
  $description = array(
    'attributes' => array(
      '#product' => array(
        '#type' => 'value',
        '#value' => $product,
      ),
      '#theme' => 'uc_product_attributes',
      '#weight' => 1,
    ),
  );

  $desc =& $description['attributes'];

  // Cart version of the product has numeric attribute => option values so we
  // need to retrieve the right ones
  $weight = 0;
  if (empty($product->order_id)) {
    foreach (_uc_cart_product_get_options($product) as $option) {
      if (!isset($desc[$option['aid']])) {
        $desc[$option['aid']]['#attribute_name'] = $option['attribute'];
        $desc[$option['aid']]['#options'] = array($option['name']);
      }
      else {
        $desc[$option['aid']]['#options'][] = $option['name'];
      }
      $desc[$option['aid']]['#weight'] = $weight++;
    }
  }
  else {
    foreach ((array)$product->data['attributes'] as $attribute => $option) {
      $desc[] = array(
        '#attribute_name' => $attribute,
        '#options' => $option,
        '#weight' => $weight++,
      );
    }
  }

  return $description;
}

/**
 * Alters the given product description.
 *
 * @param $description
 *   Description array reference.
 * @param $product
 *   The product being described.
 */
function hook_product_description_alter(&$description, $product) {
  $description['attributes']['#weight'] = 2;
}

/**
 * List node types which should be considered products.
 *
 * Trusts the duck philosophy of object identification: if it walks like a duck,
 * quacks like a duck, and has feathers like a duck, it's probably a duck.
 * Products are nodes with prices, SKUs, and everything else Übercart expects
 * them to have.
 *
 * @return
 *   Array of node type ids.
 */
function hook_product_types() {
  return array('product_kit');
}

/**
 * Handle additional data for shipments.
 *
 * @param $op
 *   The action being taken on the shipment. One of the following values:
 *   - "load": The shipment and its packages are loaded from the database.
 *   - "save": Changes to the shipment have been written.
 *   - "delete": The shipment has been deleted and the packages are available
 *     for reshipment.
 * @param &$shipment
 *   The shipment object.
 * @return
 *   Only given when $op is "load". An array of extra data to be added to the
 *   shipment object.
 */
function hook_shipment($op, &$shipment) {
  switch ($op) {
    case 'save':
      $google_order_number = uc_google_checkout_get_google_number($shipment->order_id);
      if ($google_order_number && $shipment->is_new) {
        $xml_data = '';
        foreach ($shipment->packages as $package) {
          if ($package->tracking_number) {
            $tracking_number = $package->tracking_number;
          }
          else if ($shipment->tracking_number) {
            $tracking_number = $shipment->tracking_number;
          }
          if ($tracking_number) {
            foreach ($package->products as $product) {
              $xml_data .= '<item-shipping-information>';
              $xml_data .= '<item-id>';
              $xml_data .= '<merchant-item-id>'. check_plain($product->nid .'|'. $product->model) .'</merchant-item-id>';
              $xml_data .= '</item-id>';
              $xml_data .= '<tracking-data-list>';
              $xml_data .= '<tracking-data>';
              $xml_data .= '<carrier>'. check_plain($shipment->carrier) .'</carrier>';
              $xml_data .= '<tracking-number>'. check_plain($tracking_number) .'</tracking-number>';
              $xml_data .= '</tracking-data>';
              $xml_data .= '</tracking-data-list>';
              $xml_data .= '</item-shipping-information>';
            }
          }
        }
        if ($xml_data) {
          $request = '<?xml version="1.0" encoding="UTF-8"?>'. "\n";
          $request .= '<ship-items xmlns="http://checkout.google.com/schema/2" google-order-number="'. $google_order_number .'">';
          $request .= '<item-shipping-information-list>';
          $request .= $xml_data;
          $request .= '</item-shipping-information-list>';
          $request .= '<send-email>true</send-email>';
          $request .= '</ship-items>';
          $response = uc_google_checkout_send_request('request', $request);
        }
      }
    break;
    case 'delete':
      $google_order_number = uc_google_checkout_get_google_number($shipment->order_id);
      if ($google_order_number) {
        foreach ($shipment->packages as $package) {
          foreach ($package->products as $product) {
            $reset_ids[] = check_plain($product->nid .'|'. $product->model);
          }
        }
        $request = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        $request .= '<reset-items-shipping-information xmlns="http://checkout.google.com/schema/2" google-order-number="'. $google_order_number .'">';
        $request .= '<item-ids>';
        foreach (array_unique($reset_ids) as $item_id) {
          $request .= '<item-id>';
          $request .= '<merchant-item-id>'. $item_id .'</merchant-item-id>';
          $request .= '</item-id>';
        }
        $request .= '</item-ids>';
        $request .= '<send-email>false</send-email>';
        $request .= '</reset-items-shipping-information>';
      }
      $response = uc_google_checkout_send_request('request', $request);
    break;
  }
}

/**
 * Define callbacks and service options for shipping methods.
 *
 * The shipping quote controller module, uc_quote, expects a very specific
 * structured array of methods from the implementations of this hook.
 *
 * The weights and enabled flags for shipping methods and types are set at the
 * Shipping Quote Settings page under Store Configuration. They keys of the
 * variables are the ids of the shipping methods. The "quote" and "ship" arrays of
 * the method are both optional.
 *
 * @return
 *   An array of shipping methods which have the following keys.
 *   - "type": The quote and shipping types are ids of the product shipping type
 *       that these methods apply to. type may also be 'order' which indicates
 *       that the quote applies to the entire order, regardless of the shipping
 *       types of its products. This is used by quote methods that are base on
 *       the location of the customer rather than their purchase.
 *   - "callback": The function that is called by uc_quote when a shipping quote
 *       is requested. Its arguments are the array of products and an array of
 *       order details (the shipping address). The return value is an array
 *       representing the rates quoted and errors returned (if any) for each
 *       option in the accessorials array.
 *   - "accessorials": This array represents the different options the customer
 *       may choose for their shipment. The callback function should generate a
 *       quote for each option in accessorials and return them via an array.
 *       drupal_to_js() is very useful for this.
 *       @code
 *         return array(
 *           '03' => array('rate' => 15.75, 'format' => uc_price(15.75, $context) 'option_label' => t('UPS Ground'),
 *                         'error' => 'Additional handling charge automatically applied.'),
 *           '14' => array('error' => 'Invalid package type.'),
 *           '59' => array('rate' => 26.03, 'format' => uc_price(26.03, $context), 'option_label' => t('UPS 2nd Day Air A.M.'))
 *         );
 *       @endcode
 *   - "pkg_types": The list of package types that the shipping method can handle.
 *       This should be an associative array that can be used as the #options of
 *       a select form element. It is recommended that a function be written to
 *       output this array so the method doesn't need to be found just for the
 *       package types.
 */
function hook_shipping_method() {
  $methods = array();

  $enabled = variable_get('uc_quote_enabled', array('ups' => TRUE));
  $weight = variable_get('uc_quote_method_weight', array('ups' => 0));
  $methods['ups'] = array(
    'id' => 'ups',
    'title' => t('UPS'),
    'enabled' => $enabled['ups'],
    'weight' => $weight['ups'],
    'quote' => array(
      'type' => 'small package',
      'callback' => 'uc_ups_quote',
      'accessorials' => array(
        '03' => t('UPS Ground'),
        '11' => t('UPS Standard'),
        '01' => t('UPS Next Day Air'),
        '13' => t('UPS Next Day Air Saver'),
        '14' => t('UPS Next Day Early A.M.'),
        '02' => t('UPS 2nd Day Air'),
        '59' => t('UPS 2nd Day Air A.M.'),
        '12' => t('UPS 3-Day Select'),
      ),
    ),
    'ship' => array(
      'type' => 'small package',
      'callback' => 'uc_ups_fulfill_order',
      'pkg_types' => array(
        '01' => t('UPS Letter'),
        '02' => t('Customer Supplied Package'),
        '03' => t('Tube'),
        '04' => t('PAK'),
        '21' => t('UPS Express Box'),
        '24' => t('UPS 25KG Box'),
        '25' => t('UPS 10KG Box'),
        '30' => t('Pallet'),
      ),
    ),
  );

  return $methods;
}

/**
 * Define shipping types for shipping methods.
 *
 * This hook defines a shipping type that this module is designed to handle.
 * These types are specified by a machine- and human-readable name called 'id',
 * and 'title' respectively. Shipping types may be set for individual products,
 * manufacturers, and for the entire store catalog. Shipping modules should be
 * careful to use the same shipping type ids as other similar shipping modules
 * (i.e., FedEx and UPS both operate on "small package" shipments). Modules that
 * do not fulfill orders may not need to implement this hook.
 *
 * @return
 *   An array of shipping types keyed by a machine-readable name.
 */
function hook_shipping_type() {
  $weight = variable_get('uc_quote_type_weight', array('small_package' => 0));

  $types = array();
  $types['small_package'] = array(
    'id' => 'small_package',
    'title' => t('Small Packages'),
    'weight' => $weight['small_package'],
  );

  return $types;
}

/**
 * Add status messages to the "Store administration" page.
 *
 * This hook is used to add items to the store status table on the main store
 * administration screen. Each item gets a row in the table that consists of a
 * status icon, title, and description. These items should be used to give
 * special instructions, notifications, or indicators for components of the cart
 * enabled by the modules. At a glance, a store owner should be able to look here
 * and see if a critical component of your module is not functioning properly.
 *
 * For example, if the catalog module is installed and it cannot find the catalog
 * taxonomy vocabulary, it will show an error message here to alert the store
 * administrator.
 *
 * @return
 *   An array of tore status items which are arrays with the following keys:
 *   - "status": "ok", "warning", or "error" depending on the message.
 *   - "title" The title of the status message or module that defines it.
 *   - "desc": The description; can be any message, including links to pages and
 *       forms that deal with the issue being reported.
 */
function hook_store_status() {
  if ($key = uc_credit_encryption_key()) {
    $statuses[] = array(
      'status' => 'ok',
      'title' => t('Credit card encryption'),
      'desc' => t('Credit card data in the database is currently being encrypted.'),
    );
  }
  return $statuses;
}

/**
 * Allow modules to alter the TAPIr table after the rows are populated.
 *
 * The example below adds a value for the custom 'designer' column to the table
 * rows. Each table row has a numeric key in $table and these keys can be
 * accessed using element_children() from the Form API.
 *
 * @param $table Table declaration containing header and populated rows.
 * @param $table_id Table ID. Also the function called to build the table
 *   declaration.
 */
function hook_tapir_table_alter(&$table, $table_id) {
  if ($table_id == 'uc_product_table') {
    foreach (element_children($table) as $key) {
      $node = node_load($table['#parameters'][1][$key]);

      $table[$key]['designer'] = array(
        '#value' => l($node->designer, 'collections/'.$node->designer_tid),
        '#cell_attributes' => array(
          'nowrap' => 'nowrap',
        ),
      );
    }
  }
}

/**
 * Allow modules to alter TAPIr table headers.
 *
 * This is most often done when a developer wants to add a sortable field to
 * the table. A sortable field is one where the header can be clicked to sort
 * the table results. This cannot be done using hook_tapir_table_alter() as
 * once that is called the query has already executed.
 *
 * The example below adds a 'designer' column to the catalog product table. The
 * example module would also have added joins to the query using
 * hook_db_rewrite_sql() in order for table 'td2' to be valid. The 'name' field
 * is displayed in the table and the header has the title 'Designer'.
 *
 * Also shown are changes made to the header titles for list_price and
 * price fields.
 *
 * @see hook_db_rewrite_sql()
 *
 * @param $header Reference to the array header declaration
 *   (i.e $table['#header']).
 * @param $table_id Table ID. Also the function called to build the table
 *   declaration.
 */
function hook_tapir_table_header_alter(&$header, $table_id) {
  if ($table_id == 'uc_product_table') {
    $header['designer'] = array(
      'weight' => 2,
      'cell' => array(
        'data' => t('Designer'),
        'field' => 'td2.name',
      ),
    );

    $header['list_price']['cell']['data'] = t('RRP');
    $header['price']['cell']['data'] = t('Sale');
    $header['add_to_cart']['cell']['data'] = '';
  }
}

/**
 * Take action when checkout is completed.
 *
 * @param $order
 *   The resulting order object from the completed checkout.
 * @param $account
 *   The customer that completed checkout, either the current user, or the
 *   account created for an anonymous customer.
 */
function hook_uc_checkout_complete($order, $account) {
  // Get previous records of customer purchases.
  $nids = array();
  $result = db_query("SELECT uid, nid, qty FROM {uc_customer_purchases} WHERE uid = %d", $account->uid);
  while ($record = db_fetch_object($result)) {
    $nids[$record->nid] = $record->qty;
  }

  // Update records with new data.
  $record = array('uid' => $account->uid);
  foreach ($order->products as $product) {
    $record['nid'] = $product->nid;
    if (isset($nids[$product->nid])) {
      $record['qty'] = $nids[$product->nid] + $product->qty;
      db_write_record($record, 'uc_customer_purchases', array('uid', 'nid'));
    }
    else {
      $record['qty'] = $product->qty;
      db_write_record($record, 'uc_customer_purchases');
    }
  }
}

/**
 * Allow modules to modify forms before Drupal invokes hook_form_alter().
 *
 * This hook will normally be used by core modules so any form modifications
 * they make can be further modified by contrib modules using a normal
 * hook_form_alter(). At this point, drupal_prepare_form() has not been called,
 * so none of the automatic form data (e.g.: #parameters, #build_id, etc.) has
 * been added yet.
 *
 * For a description of the hook parameters:
 * @see hook_form_alter()
 */
function hook_uc_form_alter(&$form, &$form_state, $form_id) {
  // If the node has a product list, add attributes to them
  if (isset($form['products']) && count(element_children($form['products']))) {
    foreach (element_children($form['products']) as $key) {
      $form['products'][$key]['attributes'] = _uc_attribute_alter_form(node_load($key));
      if (is_array($form['products'][$key]['attributes'])) {
        $form['products'][$key]['attributes']['#tree'] = TRUE;
        $form['products'][$key]['#type'] = 'fieldset';
      }
    }
  }
  // If not, add attributes to the node.
  else {
    $form['attributes'] = _uc_attribute_alter_form($node);

    if (is_array($form['attributes'])) {
      $form['attributes']['#tree'] = TRUE;
      $form['attributes']['#weight'] = -1;
    }
  }
}

/**
 * Add invoice templates to the list of suggested template files.
 *
 * Allows modules to declare new "types" of invoice templates (other than the
 * default 'admin' and 'customer').
 *
 * @return
 *   Array of template names that are available choices when mailing an
 *   invoice.
 */
function hook_uc_invoice_templates() {
  return array('admin', 'customer');
}

/**
 * Convenience function to display large blocks of text in several places.
 *
 * There are many instances where Ubercart modules have configurable blocks of
 * text. These usually come with default messages, like e-mail templates for new
 * orders. Because of the way default values are normally set, you're then stuck
 * having to copy and paste a large chunk of text in at least two different
 * places in the module (when you're wanting to use the variable or to display
 * the settings form with the default value). To cut down code clutter, this hook
 * was introduced. It lets you put your messages in one place and use the
 * function uc_get_message() to retrieve the default value at any time (and from
 * any module).
 *
 * The function is very simple, expecting no arguments and returning a basic
 * associative array with keys being message IDs and their values being the
 * default message. When you call uc_get_message(), use the message ID you set
 * here to refer to the message you want.
 *
 * Note: When using t(), you must not pass it a concatenated string! So our
 * example has no line breaks in the message even though it is much wider than 80
 * characters. Using concatenation breaks translation.
 *
 * @return
 *   An array of messages.
 */
function hook_uc_message() {
  $messages['configurable_message_example'] = t('This block of text represents a configurable message such as a set of instructions or an e-mail template.  Using hook_uc_message to handle the default values for these is so easy even your grandma can do it!');

  return $messages;
}

/**
 * Take action when a payment is entered for an order.
 *
 * @param $order
 *   The order object.
 * @param $method
 *   The name of the payment method used.
 * @param $amount
 *   The value of the payment.
 * @param $account
 *   The user account that entered the order. When the payment is entered
 *   during checkout, this is probably the order's user. Otherwise, it is
 *   likely a store administrator.
 * @param $data
 *   Extra data associated with the transaction.
 * @param $comment
 *   Any comments from the user about the transaction.
 */
function hook_uc_payment_entered($order, $method, $amount, $account, $data, $comment) {
  drupal_set_message(t('User @uid entered a @method payment of @amount for order @order_id.',
    array(
      '@uid' => $account->uid,
      '@method' => $method,
      '@amount' => uc_price($amount, array('location' => 'hook-payment', 'revision' => 'formatted-original')),
      '@order_id' => $order->order_id,
    ))
  );
}

/**
 * Use this hook to define price handlers for your module. You may define one
 * price alterer and one price formatter. You may also define options that are
 * merged into the options array in order of each price alterer's weight.
 */
function hook_uc_price_handler() {
  return array(
    'alter' => array(
      'title' => t('My price handler'),
      'description' => t('Handles my price alteration needs.'),
      'callback' => 'my_price_handler_alter',
    ),
    'format' => array(
      'title' => t('My price handler'),
      'description' => t('Handles my price formatting needs.'),
      'callback' => 'my_price_handler_format',
    ),
    'options' => array(
      'sign' => variable_get('uc_currency_sign', '*'),
      'sign_after' => TRUE,
      'prec' => 4,
      'dec' => ',',
      'thou' => '.',
      'label' => FALSE,
      'my_option_that_my_formatter_recognizes' => 1337,
    )
  );
}

/**
 * Notify core of any SKUs your module adds to a given node.
 *
 * NOTE: DO NOT map the array keys, as the possibility for numeric SKUs exists, and
 * this will conflict with the behavior of module_invoke_all(), specifically
 * array_merge_recursive().
 *
 * Code lifted from uc_attribute.module.
 */
function hook_uc_product_models($node) {
  $models = array();

  // Get all the SKUs for all the attributes on this node.
  $adjustments = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
  while ($adjustment = db_fetch_object($adjustments)) {
    if (!in_array($adjustment->model, $models)) {
      $models[] = $adjustment->model;
    }
  }

  return $models;
}

/**
 * Allow modules to take action when a stock level is changed.
 *
 * @param $sku
 *   The SKU whose stock level is being changed.
 * @param $stock
 *   The stock level before the adjustment.
 * @param $qty
 *   The amount by which the stock level was changed.
 */
function hook_uc_stock_adjusted($sku, $stock, $qty) {
  $params = array(
    'sku' => $sku,
    'stock' => $stock,
    'qty' => $qty,
  );

  drupal_mail('uc_stock_notify', 'stock-adjusted', uc_store_email_from(), language_default(), $params);
}

/**
 * Used to determine whether or not UC Google Analytics should add e-commerce
 *   tracking code to the bottom of the page.
 *
 * The Google Analytics module takes care of adding the necessary .js file from
 * Google for tracking general statistics.  The UC Google Analytics module works
 * in conjunction with this code to add e-commerce specific code.  However, the
 * e-commerce code should only be added on appropriate pages.  Generally, the
 * correct page will be the checkout completion page at cart/checkout/complete.
 * However, because modules can change the checkout flow as necessary, it must
 * be possible for alternate pages to be used.
 *
 * This hook allows other modules to tell the UC Google Analytics module that
 * it should go ahead and add the e-commerce tracking code to the current page.
 * A module simply needs to implement this hook and return TRUE on the proper
 * order completion page to let UC Google Analytics know it should add the
 * e-commerce tracking code to the current page.
 *
 * The implementation below comes from the 2Checkout.com module which uses an
 * alternate checkout completion page.
 *
 * @return
 *   TRUE if e-commerce tracking code should be added to the current page.
 */
function hook_ucga_display() {
  // Tell UC Google Analytics to display the e-commerce JS on the custom
  // order completion page for this module.
  if (arg(0) == 'cart' && arg(1) == '2checkout' && arg(2) == 'complete') {
    return TRUE;
  }
}

/**
 * Allows modules to alter items before they're added to the UC Google Analytics
 *   e-commerce tracking code.
 *
 * The UC Google Analytics module constructs function calls that work through
 * the Google Analytics JS API to report purchased items for e-commerce tracking
 * purposes.  The module builds the argument list for each product on an order
 * and uses this hook to give other modules a chance to alter what gets reported
 * to Google Analytics.  Additional arguments passed to implementations of this
 * hook are provided for context.
 *
 * @param $item
 *   An array of arguments being passed to Google Analytics representing an item
 *   on the order, including order_id, sku, name, category, price, and qty.
 * @param $product
 *   The product object as found in the $order object.
 * @param $trans
 *   The array of arguments that were passed to Google Analytics to represent
 *     the transaction.
 * @param $order
 *   The order object being reported to Google Analytics.
 * @return
 *   Nothing should be returned. Hook implementations should receive the $item
 *     array by reference and alter it directly.
 */
function hook_ucga_item_alter(&$item, $product, $trans, $order) {
  // Example implementation: always set the category to "UBERCART".
  $item['category'] = 'UBERCART';
}

/**
 * Allows modules to alter transaction info before it's added to the UC Google
 *   Analytics e-commerce tracking code.
 *
 * The UC Google Analytics module constructs function calls that work through
 * the Google Analytics JS API to report order information for e-commerce
 * tracking purposes.  The module builds the argument list for the transaction
 * and uses this hook to give other modules a chance to alter what gets reported
 * to Google Analytics.
 *
 * @param $trans
 *   An array of arguments being passed to Google Analytics representing the
 *     transaction, including order_id, store, total, tax, shipping, city,
 *     state, and country.
 * @param $order
 *   The order object being reported to Google Analytics.
 * @return
 *   Nothing should be returned. Hook implementations should receive the $trans
 *     array by reference and alter it directly.
 */
function hook_ucga_trans_alter(&$trans, $order) {
  // Example implementation: prefix all orders with "UC-".
  $trans['order_id'] = 'UC-'. $trans['order_id'];
}

/**
 * Handle requests to update a cart item.
 *
 * @param $nid
 *   Node id of the cart item.
 * @param $data
 *   Array of extra information about the item.
 * @param $qty
 *   The quantity of this item in the cart.
 * @param $cid
 *   The cart id. Defaults to NULL, which indicates that the current user's cart
 *   should be retrieved with uc_cart_get_id().
 */
function hook_update_cart_item($nid, $data = array(), $qty, $cid = NULL) {
  if (!$nid) return NULL;
  $cid = !(is_null($cid) || empty($cid)) ? $cid : uc_cart_get_id();
  if ($qty < 1) {
    uc_cart_remove_item($nid, $cid, $data);
  }
  else {
    db_query("UPDATE {uc_cart_products} SET qty = %d, changed = %d WHERE nid = %d AND cart_id = '%s' AND data = '%s'", $qty, time(), $nid, $cid, serialize($data));
  }

  // Rebuild the items hash
  uc_cart_get_contents(NULL, 'rebuild');
  if (!strpos(request_uri(), 'cart', -4)) {
    drupal_set_message(t('Your item(s) have been updated.'));
  }
}

/**
 * @} End of "addtogroup hooks".
 */

Other Drupal examples (source code examples)

Here is a short list of links related to this Drupal hooks.php source code file:

new blog posts

"Drupal" is a registered trademark of Dries Buytaert.

my drupal tutorials and examples  

Copyright 1998-2016 Alvin Alexander, alvinalexander.com
All Rights Reserved.

Beginning in 2016, a portion of the proceeds from pages under the '/drupal-code-examples/' URI will be donated to charity.