'Products',
'description' => 'Administer products, classes, and more.',
'access arguments' => array('administer products'),
'page callback' => 'uc_product_administration',
'type' => MENU_NORMAL_ITEM,
'weight' => -2,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/products/view'] = array(
'title' => 'View products',
'description' => 'Build and view a list of product nodes.',
'access arguments' => array('administer products'),
'type' => MENU_NORMAL_ITEM,
'weight' => -10,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/products/classes'] = array(
'title' => 'Manage classes',
'description' => 'Create and edit product node types.',
'access arguments' => array('administer product classes'),
'page callback' => 'uc_product_class_default',
'type' => MENU_NORMAL_ITEM,
'weight' => -2,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/settings/products'] = array(
'title' => 'Product settings',
'description' => 'Configure product settings.',
'access arguments' => array('administer products'),
'page callback' => 'uc_product_settings_overview',
'type' => MENU_NORMAL_ITEM,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/settings/products/overview'] = array(
'title' => 'Overview',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/store/settings/products/edit'] = array(
'title' => 'Edit',
'access arguments' => array('administer products'),
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_product_settings_form'),
'weight' => -5,
'type' => MENU_LOCAL_TASK,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/settings/products/edit/general'] = array(
'title' => 'Product settings',
'access arguments' => array('administer products'),
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/settings/products/edit/fields'] = array(
'title' => 'Product fields',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_product_field_settings_form'),
'access arguments' => array('administer products'),
'weight' => -5,
'type' => MENU_LOCAL_TASK,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/settings/products/edit/features'] = array(
'title' => 'Product features',
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_product_feature_settings_form'),
'access arguments' => array('administer product features'),
'weight' => 0,
'type' => MENU_LOCAL_TASK,
'file' => 'uc_product.admin.inc',
);
// Insert subitems into the edit node page for product types.
$items['node/%node/edit/product'] = array(
'title' => 'Product',
'access callback' => 'uc_product_edit_access',
'access arguments' => array(1),
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'uc_product.admin.inc',
);
$features = module_invoke_all('product_feature');
if (!empty($features)) {
$items['node/%node/edit/features'] = array(
'title' => 'Features',
'page callback' => 'uc_product_features',
'page arguments' => array(1),
'access callback' => 'uc_product_feature_access',
'access arguments' => array(1),
'weight' => 10,
'type' => MENU_LOCAL_TASK,
'file' => 'uc_product.admin.inc',
);
}
$items['admin/store/settings/products/defaults'] = array(
'access arguments' => array('administer products'),
'page callback' => 'uc_product_image_defaults',
'type' => MENU_CALLBACK,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/products/classes/%uc_product_class'] = array(
'title' => 'Product class',
'access arguments' => array('administer product classes'),
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_product_class_form', 4),
'type' => MENU_CALLBACK,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/products/classes/%uc_product_class/edit'] = array(
'title' => 'Edit',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -5,
'file' => 'uc_product.admin.inc',
);
$items['admin/store/products/classes/%uc_product_class/delete'] = array(
'access arguments' => array('administer product classes'),
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_product_class_delete_confirm', 4),
'type' => MENU_CALLBACK,
'file' => 'uc_product.admin.inc',
);
// Define an autocomplete path for products using the title or SKU.
$items['autocomplete/uc_product_title_sku'] = array(
'page callback' => 'uc_product_title_sku_autocomplete',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'uc_product.pages.inc',
);
return $items;
}
/**
* Implementation of hook_help().
*/
function uc_product_help($path, $arg) {
// Do things here later. Figure out what you need to say for each section.
$output = '';
switch ($path) {
case 'admin/settings/module#description':
$output .= t('Create products for sale in an online store.');
break;
default:
$output = '';
break;
}
return $output;
}
/**
* Implementation of hook_perm().
*/
function uc_product_perm() {
$perms = array('administer products', 'administer product classes', 'administer product features');
foreach (node_get_types() as $type) {
if ($type->module == 'uc_product') {
$name = check_plain($type->type);
if ($name == 'product') {
$name = '';
}
else {
$name .= ' ';
}
$perms[] = 'create '. $name .'products';
$perms[] = 'edit own '. $name .'products';
$perms[] = 'edit all '. $name .'products';
$perms[] = 'delete own '. $name .'products';
$perms[] = 'delete all '. $name .'products';
}
}
return $perms;
}
/**
* Implementation of hook_access().
*/
function uc_product_access($op, $node, $account) {
$type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
if ($type == 'product') {
$type = '';
}
else {
$type .= ' ';
}
switch ($op) {
case 'create':
return user_access('create '. $type .'products', $account);
case 'update':
if (user_access('edit all '. $type .'products', $account) || (user_access('edit own '. $type .'products', $account) && ($account->uid == $node->uid))) {
return TRUE;
}
break;
case 'delete':
if (user_access('delete all '. $type .'products', $account) || (user_access('delete own '. $type .'products', $account) && ($account->uid == $node->uid))) {
return TRUE;
}
break;
}
}
/**
* Menu access callback for 'node/%node/edit/features'.
*/
function uc_product_feature_access($node) {
return uc_product_is_product($node) && user_access('administer product features');
}
/**
* Implementation of hook_init().
*/
function uc_product_init() {
drupal_add_css(drupal_get_path('module', 'uc_product') .'/uc_product.css');
global $conf;
$conf['i18n_variables'][] = 'uc_product_add_to_cart_text';
$conf['i18n_variables'][] = 'uc_teaser_add_to_cart_text';
}
/**
* Implementation of hook_enable().
*
* Set up default imagefield and imagecache settings.
*/
function uc_product_enable() {
// For some time in Drupal 5, CCK would delete its field data if a node
// type was unavailable because its module was disabled. This function
// worked around that by giving the product classes to node.module when
// uc_product was disabled. This is no longer necessary as of CCK 5.x-1.9,
// but the workaround was left in to prevent accidents. This block of
// code is here to reclaim the product nodes after an upgrade to Drupal
// 6, and then should not be used again as the corresponding code in
// uc_product_disable() was removed.
if (variable_get('uc_product_enable_nodes', TRUE)) {
$node_types = node_get_types('types');
$product_classes = array('product');
$result = db_query("SELECT pcid, name, description FROM {uc_product_classes}");
while ($product_class = db_fetch_object($result)) {
$product_classes[] = $product_class->pcid;
}
foreach ($node_types as $type => $info) {
if ($info->module == 'node' && in_array($type, $product_classes)) {
$info->module = 'uc_product';
$info->custom = 0;
node_type_save($info);
}
}
variable_set('uc_product_enable_nodes', FALSE);
}
if (module_exists('imagefield')) {
uc_product_add_default_image_field();
}
}
/**
* Implementation of hook_theme().
*/
function uc_product_theme() {
return array(
'uc_product_form_prices' => array(
'arguments' => array('form' => NULL),
),
'uc_product_form_weight' => array(
'arguments' => array('form' => NULL),
),
'uc_product_dimensions_form' => array(
'arguments' => array('form' => NULL),
),
'uc_product_field_settings_form' => array(
'arguments' => array('form' => NULL),
'file' => 'uc_product.admin.inc',
),
'uc_product_model' => array(
'arguments' => array('model' => '', 'teaser' => 0, 'page' => 0),
),
'uc_product_body' => array(
'arguments' => array('body' => '', 'teaser' => 0, 'page' => 0),
),
'uc_product_add_to_cart' => array(
'arguments' => array('node' => NULL, 'teaser' => 0, 'page' => 0),
),
'uc_product_price' => array(
'arguments' => array('price' => 0, 'context' => array(), 'options' => array()),
),
'uc_product_weight' => array(
'arguments' => array('weight' => 0, 'unit' => NULL, 'teaser' => 0, 'page' => 0),
),
'uc_product_dimensions' => array(
'arguments' => array('length' => 0, 'width' => 0, 'height' => 0, 'units' => NULL, 'teaser' => 0, 'page' => 0),
),
'uc_product_image' => array(
'arguments' => array('images' => array(), 'teaser' => 0, 'page' => 0),
),
'uc_product_feature_add_form' => array(
'arguments' => array('form' => NULL),
'file' => 'uc_product.admin.inc',
),
);
}
/**
* Implementation of hook_node_info().
*
* Create node types for each product class and other product modules.
*/
function uc_product_node_info($reset = FALSE) {
static $types = array();
$title_label = t('Name');
$body_label = t('Description');
if (empty($types) || $reset) {
$types = array();
$types['product'] = array(
'name' => t('Product'),
'module' => 'uc_product',
'description' => t('This node displays the representation of a product for sale on the website. It includes all the unique information that can be attributed to a specific model number.'),
'title_label' => $title_label,
'body_label' => $body_label,
);
$result = db_query("SELECT pcid, name, description FROM {uc_product_classes}");
while ($class = db_fetch_object($result)) {
$types[$class->pcid] = array(
'name' => $class->name,
'module' => 'uc_product',
'description' => $class->description,
'title_label' => $title_label,
'body_label' => $body_label,
);
}
}
return $types;
}
/**
* Implementation of hook_forms().
*
* Register an "add to cart" form for each product to prevent id collisions.
*/
function uc_product_forms($form_id, $args) {
$forms = array();
if (isset($args[0]) && isset($args[0]->nid) && isset($args[0]->type)) {
$product = $args[0];
if (in_array($product->type, array_keys(uc_product_node_info()))) {
$forms['uc_product_add_to_cart_form_'. $product->nid] = array('callback' => 'uc_product_add_to_cart_form');
$forms['uc_catalog_buy_it_now_form_'. $product->nid] = array('callback' => 'uc_catalog_buy_it_now_form');
}
}
return $forms;
}
/**
* Menu access callback for 'node/%node/edit/product'.
*/
function uc_product_edit_access($node) {
// Re-inherit access callback for 'node/%node/edit'
return uc_product_is_product($node) && node_access('update', $node);
}
/**
* Implementation of hook_form().
*
* @ingroup forms
* @see
* theme_uc_product_form_prices()
* theme_uc_product_form_weight()
* theme_uc_product_dimensions()
* uc_product_form_validate()
*/
function uc_product_form(&$node) {
$type = node_get_types('type', $node);
$location = array();
$location[] = menu_get_item('admin');
$location[] = menu_get_item('admin/store');
$location[] = menu_get_item('admin/store/products');
$location[] = menu_get_item('admin/store/settings/products');
$breadcrumb = array(l('Home', ''));
foreach ($location as $item) {
$breadcrumb[] = l($item['title'], $item['path']);
}
drupal_set_breadcrumb($breadcrumb);
$sign_flag = variable_get('uc_sign_after_amount', FALSE);
$currency_sign = variable_get('uc_currency_sign', '$');
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($type->title_label),
'#required' => TRUE,
'#default_value' => $node->title,
'#maxlength' => 255,
'#weight' => -5,
);
if ($type->has_body) {
$form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
$form['body_field']['body']['#description'] = t('Enter the product description used for product teasers and pages.');
$form['body_field']['#weight'] = -4;
}
$form['base'] = array('#type' => 'fieldset',
'#title' => t('Product information'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
'#weight' => -1,
'#attributes' => array('class' => 'product-field'),
);
$form['base']['model'] = array('#type' => 'textfield',
'#title' => t('SKU'),
'#required' => TRUE,
'#default_value' => isset($node->model) ? $node->model : '',
'#description' => t('Product SKU/model.'),
'#weight' => 0,
'#size' => 32,
);
$form['base']['prices'] = array(
'#weight' => 5,
'#theme' => 'uc_product_form_prices',
);
$form['base']['prices']['list_price'] = array(
'#type' => 'textfield',
'#title' => t('List price'),
'#required' => FALSE,
'#default_value' => isset($node->list_price) ? uc_store_format_price_field_value($node->list_price) : 0,
'#description' => t('The listed MSRP.'),
'#weight' => 0,
'#size' => 20,
'#maxlength' => 35,
'#field_prefix' => $sign_flag ? '' : $currency_sign,
'#field_suffix' => $sign_flag ? $currency_sign : '',
);
$form['base']['prices']['cost'] = array(
'#type' => 'textfield',
'#title' => t('Cost'),
'#required' => FALSE,
'#default_value' => isset($node->cost) ? uc_store_format_price_field_value($node->cost) : 0,
'#description' => t("Your store's cost."),
'#weight' => 1,
'#size' => 20,
'#maxlength' => 35,
'#field_prefix' => $sign_flag ? '' : $currency_sign,
'#field_suffix' => $sign_flag ? $currency_sign : '',
);
$form['base']['prices']['sell_price'] = array(
'#type' => 'textfield',
'#title' => t('Sell price'),
'#required' => TRUE,
'#default_value' => isset($node->sell_price) ? uc_store_format_price_field_value($node->sell_price) : 0,
'#description' => t('Customer purchase price.'),
'#weight' => 2,
'#size' => 20,
'#maxlength' => 35,
'#field_prefix' => $sign_flag ? '' : $currency_sign,
'#field_suffix' => $sign_flag ? $currency_sign : '',
);
$form['base']['shippable'] = array(
'#type' => 'checkbox',
'#title' => t('Product and its derivatives are shippable.'),
'#default_value' => isset($node->shippable) ? $node->shippable : variable_get('uc_product_shippable_'. $node->type, 1),
'#weight' => 10,
);
$form['base']['weight'] = array(
'#weight' => 15,
'#theme' => 'uc_product_form_weight',
);
$form['base']['weight']['weight'] = array('#type' => 'textfield',
'#title' => t('Weight'),
'#default_value' => isset($node->weight) ? $node->weight : 0,
'#size' => 10,
'#maxlength' => 15,
);
$units = array(
'lb' => t('Pounds'),
'kg' => t('Kilograms'),
'oz' => t('Ounces'),
'g' => t('Grams'),
);
$form['base']['weight']['weight_units'] = array('#type' => 'select',
'#title' => t('Unit of measurement'),
'#default_value' => isset($node->weight_units) ? $node->weight_units : variable_get('uc_weight_unit', 'lb'),
'#options' => $units,
);
$form['base']['dimensions'] = array('#type' => 'fieldset',
'#title' => t('Dimensions'),
'#description' => t('Physical dimensions of the packaged product.'),
'#weight' => 20,
'#theme' => 'uc_product_dimensions_form',
);
$form['base']['dimensions']['length_units'] = array('#type' => 'select',
'#title' => t('Units of measurement'),
'#options' => array(
'in' => t('Inches'),
'ft' => t('Feet'),
'cm' => t('Centimeters'),
'mm' => t('Millimeters'),
),
'#default_value' => isset($node->length_units) ? $node->length_units : variable_get('uc_length_unit', 'in'),
);
$form['base']['dimensions']['dim_length'] = array('#type' => 'textfield',
'#title' => t('Length'),
'#default_value' => isset($node->length) ? $node->length : '',
'#size' => 10,
);
$form['base']['dimensions']['dim_width'] = array('#type' => 'textfield',
'#title' => t('Width'),
'#default_value' => isset($node->width) ? $node->width : '',
'#size' => 10,
);
$form['base']['dimensions']['dim_height'] = array('#type' => 'textfield',
'#title' => t('Height'),
'#default_value' => isset($node->height) ? $node->height : '',
'#size' => 10,
);
$form['base']['pkg_qty'] = array('#type' => 'textfield',
'#title' => t('Package quantity'),
'#default_value' => isset($node->pkg_qty) ? $node->pkg_qty : 1,
'#description' => t('For a package containing only this product, how many are in it?'),
'#weight' => 25,
);
$form['base']['default_qty'] = array('#type' => 'textfield',
'#title' => t('Default quantity to add to cart'),
'#default_value' => isset($node->default_qty) ? $node->default_qty : 1,
'#description' => t('Leave blank or zero to disable the quantity field next to the add to cart button, if it is enabled in general. If it is disabled, this field is ignored.', array('!settings_url' => url('admin/store/settings/products/edit'))),
'#weight' => 27,
'#size' => 5,
'#maxlength' => 6,
);
$form['base']['ordering'] = array('#type' => 'weight',
'#title' => t('List position'),
'#description' => t("Specify a value to set this product's position in product lists.
Products in the same position will be sorted alphabetically."),
'#delta' => 25,
'#default_value' => isset($node->ordering) ? $node->ordering : 0,
'#weight' => 30,
);
return $form;
}
/**
* @ingroup themeable
*/
function theme_uc_product_form_prices($form) {
return "
\n". drupal_render($form['list_price'])
.' | '. drupal_render($form['cost'])
.' | '. drupal_render($form['sell_price'])
." |
\n". drupal_render($form);
}
/**
* @ingroup themeable
*/
function theme_uc_product_form_weight($form) {
return ''. drupal_render($form['weight']) .' | '
. drupal_render($form['weight_units']) .' |
';
}
/**
* Put length, width, and height fields on the same line.
*
* @ingroup themeable
*/
function theme_uc_product_dimensions_form($form) {
$output = '';
$row = array();
foreach (element_children($form) as $dimension) {
$row[] = drupal_render($form[$dimension]);
}
$output .= theme('table', array(), array($row));
return $output;
}
/**
* Implementation of hook_validate().
*
* Ensure that prices, weight, dimensions, and quantity are positive numbers.
*/
function uc_product_validate($node) {
$pattern = '/^\d*(\.\d*)?$/';
$price_error = t('Price must be in a valid number format. No commas and only one decimal point.');
if (!empty($node->list_price) && !is_numeric($node->list_price) && !preg_match($pattern, $node->list_price)) {
form_set_error('list_price', $price_error);
}
if (!empty($node->cost) && !is_numeric($node->cost) && !preg_match($pattern, $node->cost)) {
form_set_error('cost', $price_error);
}
if (!is_numeric($node->sell_price) && !preg_match($pattern, $node->sell_price)) {
form_set_error('sell_price', $price_error);
}
foreach (array('weight', 'length', 'width', 'height') as $property) {
if (!empty($node->$property) && (!is_numeric($node->$property) || $node->$property < 0)) {
form_set_error($property, t('@property must be a positive number. No commas and only one decimal point.', array('@property' => ucfirst($property))));
}
}
if ($node->default_qty) {
if (!is_numeric($node->default_qty)) {
form_set_error('default_qty', t('Quantities should be numeric.'));
}
elseif ($node->default_qty < 0) {
form_set_error('default_qty', t("Adding negative items to the cart doesn't make sense, so don't make it easy."));
}
}
}
/**
* Implementation of hook_insert().
*/
function uc_product_insert($node) {
if (isset($node->dim_length)) {
$node->length = $node->dim_length;
}
if (isset($node->dim_width)) {
$node->width = $node->dim_width;
}
if (isset($node->dim_height)) {
$node->height = $node->dim_height;
}
if (!isset($node->unique_hash)) {
$node->unique_hash = md5($node->vid . $node->nid . $node->model . $node->list_price . $node->cost . $node->sell_price . $node->weight . $node->weight_units . $node->length . $node->width . $node->height . $node->length_units . $node->pkg_qty . $node->default_qty . $node->shippable . time());
}
db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, length, width, height, length_units, pkg_qty, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %f, %f, %f, '%s', %d, %d, '%s', %d, %d)",
$node->vid, $node->nid, $node->model, $node->list_price, $node->cost, $node->sell_price, $node->weight, $node->weight_units, $node->length, $node->width, $node->height, $node->length_units, $node->pkg_qty, $node->default_qty, $node->unique_hash, $node->ordering, $node->shippable
);
}
/**
* Implementation of hook_update().
*/
function uc_product_update($node) {
if (isset($node->dim_length)) {
$node->length = $node->dim_length;
}
if (isset($node->dim_width)) {
$node->width = $node->dim_width;
}
if (isset($node->dim_height)) {
$node->height = $node->dim_height;
}
if ($node->revision) {
db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, length, width, height, length_units, pkg_qty, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %f, %f, %f, '%s', %d, %d, '%s', %d, %d)",
$node->vid, $node->nid, $node->model, $node->list_price, $node->cost, $node->sell_price, $node->weight, $node->weight_units, $node->length, $node->width, $node->height, $node->length_units, $node->pkg_qty, $node->default_qty, $node->unique_hash, $node->ordering, $node->shippable
);
}
else {
//drupal_set_message(''. print_r($node, TRUE) .'
');drupal_set_message(''. print_r($node, TRUE) .'
');
db_query("UPDATE {uc_products} SET model = '%s', list_price = %f, cost = %f, sell_price = %f, weight = %f, weight_units = '%s', length = %f, width = %f, height = %f, length_units = '%s', pkg_qty = %d, default_qty = %d, ordering = %d, shippable = %d WHERE vid = %d",
$node->model, $node->list_price, $node->cost, $node->sell_price, $node->weight, $node->weight_units, $node->length, $node->width, $node->height, $node->length_units, $node->pkg_qty, $node->default_qty, $node->ordering, $node->shippable, $node->vid);
}
}
/**
* Implementation of hook_load().
*/
function uc_product_load(&$node) {
return db_fetch_object(db_query('SELECT model, list_price, cost, sell_price, weight, weight_units, length, width, height, length_units, pkg_qty, default_qty, unique_hash, ordering, shippable FROM {uc_products} WHERE vid = %d', $node->vid));
}
/**
* Implementation of hook_delete().
*/
function uc_product_delete(&$node) {
db_query("DELETE from {uc_products} WHERE nid = %d", $node->nid);
}
/**
* Convenience function to get the enabled fields.
*/
function uc_product_field_enabled() {
$enabled = variable_get('uc_product_field_enabled', array(
'image' => 1,
'display_price' => 1,
'model' => 1,
'list_price' => 0,
'cost' => 0,
'sell_price' => 1,
'weight' => 0,
'dimensions' => 0,
'add_to_cart' => 1,
));
return $enabled;
}
/**
* Implementation of hook_view().
*/
function uc_product_view($node, $teaser = 0, $page = 0) {
$node = node_prepare($node, $teaser);
$enabled = uc_product_field_enabled();
$weight = variable_get('uc_product_field_weight', array(
'image' => -2,
'display_price' => -1,
'model' => 0,
'list_price' => 2,
'cost' => 3,
'sell_price' => 4,
'weight' => 5,
'dimensions' => 6,
'add_to_cart' => 10,
));
$context = array(
'revision' => 'themed',
'type' => 'product',
'class' => array(
'product',
),
'subject' => array(
'node' => $node,
),
);
if (module_exists('imagecache') && ($field = variable_get('uc_image_'. $node->type, '')) && isset($node->$field) && file_exists($node->{$field}[0]['filepath'])) {
$node->content['image'] = array('#value' => theme('uc_product_image', $node->$field, $teaser, $page),
'#access' => $enabled['image'],
'#weight' => $weight['image'],
);
}
$context['class'][1] = 'display';
$context['field'] = 'sell_price';
$node->content['display_price'] = array('#value' => theme('uc_product_price', $node->sell_price, $context),
'#access' => $enabled['display_price'],
'#weight' => $weight['display_price'],
);
if (!$teaser) {
$node->content['model'] = array('#value' => theme('uc_product_model', $node->model, $teaser, $page),
'#access' => $enabled['model'],
'#weight' => $weight['model'],
);
$node->content['body']['#value'] = theme('uc_product_body', $node->body, $teaser, $page);
$node->content['body']['#weight'] = 1;
$context['class'][1] = 'list';
$context['field'] = 'list_price';
$node->content['list_price'] = array('#value' => theme('uc_product_price', $node->list_price, $context),
'#access' => $enabled['list_price'],
'#weight' => $weight['list_price'],
);
$context['class'][1] = 'cost';
$context['field'] = 'cost';
$node->content['cost'] = array('#value' => theme('uc_product_price', $node->cost, $context),
'#access' => $enabled['cost'] && user_access('administer products'),
'#weight' => $weight['cost'],
);
}
else {
$node->content['body']['#value'] = theme('uc_product_body', $node->teaser, $teaser, $page);
$node->content['#attributes'] = array('style' => 'display: inline');
}
$context['class'][1] = 'sell';
$context['field'] = 'sell_price';
$node->content['sell_price'] = array('#value' => theme('uc_product_price', $node->sell_price, $context, array('label' => !$teaser)),
'#access' => $enabled['sell_price'],
'#weight' => $weight['sell_price'],
);
if (!$teaser) {
$node->content['weight'] = array('#value' => theme('uc_product_weight', $node->weight, $node->weight_units, $teaser, $page),
'#access' => $enabled['weight'],
'#weight' => $weight['weight'],
);
$node->content['dimensions'] = array('#value' => theme('uc_product_dimensions', $node->length, $node->width, $node->height, $node->length_units, $teaser, $page),
'#access' => $enabled['dimensions'],
'#weight' => $weight['dimensions'],
);
if (module_exists('uc_cart')) {
$node->content['add_to_cart'] = array('#value' => theme('uc_product_add_to_cart', $node, $teaser, $page),
'#access' => $enabled['add_to_cart'],
'#weight' => $weight['add_to_cart'],
);
}
}
elseif (module_exists('uc_cart') && variable_get('uc_product_add_to_cart_teaser', TRUE)) {
$node->content['add_to_cart'] = array('#value' => theme('uc_product_add_to_cart', $node, $teaser, $page),
'#access' => $enabled['add_to_cart'],
'#weight' => $weight['add_to_cart'],
);
}
return $node;
}
/**
* Implementation of hook_preprocess_node().
*
* Default product classes to use node-product.tpl.php if they don't have their
* own template.
*
* @see theme(), MODULE_preprocess_HOOK()
*/
function uc_product_preprocess_node(&$variables) {
if (uc_product_is_product($variables['type'])) {
array_unshift($variables['template_files'], 'node-product');
}
}
/**
* Implementation of hook_form_alter().
*
* @see uc_product_save_continue_submit()
*/
function uc_product_form_alter(&$form, $form_state, $form_id) {
if ($form_id == 'search_form' && arg(0) == 'admin' && arg(1) == 'store' && arg(2) == 'products' && user_access('use advanced search')) {
// Keyword boxes:
$form['advanced'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced search'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#attributes' => array('class' => 'search-advanced'),
);
$form['advanced']['keywords'] = array(
'#prefix' => '',
'#suffix' => '
',
);
$form['advanced']['keywords']['or'] = array(
'#type' => 'textfield',
'#title' => t('Containing any of the words'),
'#size' => 30,
'#maxlength' => 255,
);
$form['advanced']['keywords']['phrase'] = array(
'#type' => 'textfield',
'#title' => t('Containing the phrase'),
'#size' => 30,
'#maxlength' => 255,
);
$form['advanced']['keywords']['negative'] = array(
'#type' => 'textfield',
'#title' => t('Containing none of the words'),
'#size' => 30,
'#maxlength' => 255,
);
// Taxonomy box:
if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
$form['advanced']['category'] = array(
'#type' => 'select',
'#title' => t('Only in the category(s)'),
'#prefix' => '',
'#size' => 10,
'#suffix' => '
',
'#options' => $taxonomy,
'#multiple' => TRUE,
);
}
// Node types:
$form['advanced']['type'] = array(
'#type' => 'checkboxes',
'#title' => t('Only of the type(s)'),
'#prefix' => '',
'#suffix' => '
',
'#options' => uc_product_type_names(),
);
$form['advanced']['submit'] = array(
'#type' => 'submit',
'#value' => t('Advanced search'),
'#prefix' => '',
'#suffix' => '
',
);
$form['#validate'][] = 'node_search_validate';
}
// Add a button to product node forms to continue editing after saving.
if (uc_product_is_product_form($form)) {
$form['buttons']['save_continue'] = array(
'#type' => 'submit',
'#value' => t('Save and continue'),
'#weight' => 7,
'#submit' => array('node_form_submit', 'uc_product_save_continue_submit'),
);
}
}
/**
* After the node is saved, redirect to the edit page.
*
* Some modules add local tasks to product edit forms, but only after it has an
* nid. Redirecting facilitates the common workflow of continuing to those
* tasks.
*/
function uc_product_save_continue_submit($form, &$form_state) {
$form_state['redirect'] = 'node/'. $form_state['nid'] .'/edit';
}
/**
* Implementation of hook_form_{$form_id}_alter().
*
* Add a default image field setting to product content types.
*/
function uc_product_form_node_type_form_alter(&$form, &$form_state) {
$type = $form['#node_type'];
if (!uc_product_is_product($type->type)) {
return;
}
$form['uc_product'] = array(
'#type' => 'fieldset',
'#title' => t('Ubercart product settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
// shippable
$form['uc_product']['uc_product_shippable'] = array(
'#type' => 'checkbox',
'#title' => t('Product and its derivatives are shippable.'),
'#default_value' => variable_get('uc_product_shippable_'. $type->type, 1),
'#description' => t('This setting can still be overridden on the node form.'),
'#return_value' => 1,
'#weight' => -5,
);
// image field
if (module_exists('content')) {
$fields = content_types($type->type);
$fields = (array)$fields['fields'];
$options = array('' => t('None'));
foreach ($fields as $field_name => $field) {
if (strpos($field['widget']['type'], 'image') !== FALSE) {
$options[$field_name] = $field['widget']['label'];
}
}
$form['uc_product']['uc_image'] = array(
'#type' => 'select',
'#title' => t('Product image field'),
'#default_value' => variable_get('uc_image_'. $type->type, NULL),
'#options' => $options,
'#description' => t('The selected field will be used on Ubercart pages to represent the products of this content type.'),
'#weight' => -4,
);
}
}
/******************************************************************************
* CCK Hooks *
******************************************************************************/
/**
* Implementation of hook_content_extra_fields().
*
* Adds the "Product information"
*/
function uc_product_content_extra_fields($type_name) {
$type = node_get_types('type', $type_name);
$extra = array();
if ($type->module == 'uc_product') {
$extra['base'] = array(
'label' => t('Product information'),
'description' => t('Product module form.'),
'weight' => -1
);
}
return $extra;
}
/**
* Implementation of hook_content_extra_fields_alter().
*/
function uc_product_content_extra_fields_alter(&$extra, $type_name) {
$type = node_get_types('type', $type_name);
if ($type->module == 'uc_product') {
if (isset($extra['menu'])) {
$extra['menu']['weight'] = 0;
}
if ($type->has_body) {
$extra['body_field']['weight'] = -4;
unset($extra['body_field']['view']);
$extra['body'] = array(
'label' => t('Description'),
'description' => t('Node description. (View tab)'),
'weight' => 1,
);
}
}
}
/**
* Implement hook_content_fieldapi().
*
* Reset a content type's default image field setting when that field instance
* is removed.
*/
function uc_product_content_fieldapi($op, $field) {
switch ($op) {
case 'delete instance':
if ($field->field_name == variable_get('uc_image_'. $field->type_name, NULL)) {
variable_set('uc_image_'. $field->type_name, NULL);
}
break;
}
}
/******************************************************************************
* Token Hooks *
******************************************************************************/
/**
* Implementation of hook_token_values().
*/
function uc_product_token_values($type, $object = NULL, $options = array()) {
if ($type == 'node' && uc_product_is_product($object)) {
$tokens = array();
$tokens['model'] = $object->model;
$tokens['list_price'] = $object->list_price;
$tokens['cost'] = $object->cost;
$tokens['sell_price'] = $object->sell_price;
$tokens['weight_units'] = $object->weight_units;
$tokens['weight-raw'] = $object->weight;
$tokens['weight'] = uc_weight_format($object->weight, $object->weight_units);
$tokens['length_units'] = $object->length_units;
$tokens['length-raw'] = $object->length;
$tokens['length'] = uc_length_format($object->length, $object->length_units);
$tokens['width-raw'] = $object->width;
$tokens['width'] = uc_length_format($object->width, $object->length_units);
$tokens['height-raw'] = $object->height;
$tokens['height'] = uc_length_format($object->height, $object->length_units);
return $tokens;
}
}
/**
* Implementation of hook_token_list().
*/
function uc_product_token_list($type = 'all') {
$tokens = array();
if ($type == 'node' || $type == 'product' || $type == 'ubercart' || $type == 'all') {
$tokens['product']['model'] = t("The product's model number.");
$tokens['product']['list_price'] = t("The product's list price.");
$tokens['product']['cost'] = t("The product's cost.");
$tokens['product']['sell_price'] = t("The product's sell price.");
$tokens['product']['weight_units'] = t("The unit of measurement for the product's weight.");
$tokens['product']['weight-raw'] = t("The numerical value of the product's weight.");
$tokens['product']['weight'] = t("The product's formatted weight.");
$tokens['product']['length_units'] = t("The unit of measurement for the product's length, width, and height.");
$tokens['product']['length-raw'] = t("The numerical value of the product's length.");
$tokens['product']['length'] = t("The product's formatted length.");
$tokens['product']['width-raw'] = t("The numerical value of the product's width.");
$tokens['product']['width'] = t("The product's formatted width.");
$tokens['product']['height-raw'] = t("The numerical value of the product's height.");
$tokens['product']['height'] = t("The product's formatted height.");
}
return $tokens;
}
/******************************************************************************
* Ubercart Hooks *
******************************************************************************/
/**
* Implementation of hook_product_types().
*/
function uc_product_product_types() {
return array_keys(uc_product_node_info());
}
/**
* Display the status of the product image handlers.
*
* @see uc_product_image_defaults()
*/
function uc_product_store_status() {
if (!module_exists('imagefield') || !module_exists('imagecache')) {
$status = 'warning';
$description = t('To automatically configure core image support, enable the Content, CCK Image field, and Imagecache modules.', array('!url' => url('admin/build/modules')));
}
else {
module_load_include('inc', 'content', 'includes/content.crud');
// Check for filefields on products.
if ($field = variable_get('uc_image_product', '')) {
$instances = content_field_instance_read(array('field_name' => $field, 'type_name' => 'product'));
$field_check = (bool) count($instances);
}
else {
$field_check = FALSE;
}
$presets = array('product', 'product_full', 'product_list', 'uc_thumbnail');
if (module_exists('uc_catalog')) {
$presets[] = 'uc_category';
}
if (module_exists('uc_cart')) {
$presets[] = 'cart';
}
if (module_exists('uc_manufacturer')) {
$presets[] = 'manufacturer';
}
sort($presets);
$preset_check = 1;
$action_check = 1;
foreach ($presets as $preset_name) {
$preset = imagecache_preset_by_name($preset_name);
if (empty($preset)) {
$preset_check = 0;
$action_check = 0;
break;
}
else {
if (empty($preset['actions']) && $preset_name != 'product_full') {
$action_check = 0;
break;
}
}
}
if ($field_check && $preset_check && $action_check) {
$status = 'ok';
$description = t('Product image support has been automatically configured by Ubercart.');
}
else {
$status = 'warning';
$description = t('Click here to automatically configure the following items for core image support:', array('!url' => url('admin/store/settings/products/defaults')));
if (!$field_check) {
$items[] = t('The Image file field has not been created for products.');
}
if (!$preset_check) {
$items[] = t('Some or all of the expected Imagecache presets ("!presets") have not been created. Ubercart will not display images in certain places.', array('!presets' => implode('", "', $presets)));
}
if (!$action_check) {
$items[] = t('Some Imagecache presets do not contain actions to perform on images. Images may be displayed in their original formats.');
}
$description .= theme('item_list', $items) . t('(This action is not required and should not be taken if you do not need images or have implemented your own image support.)');
}
}
return array(array('status' => $status, 'title' => t('Images'), 'desc' => $description));
}
/**
* Implementation of hook_cart_display().
*/
function uc_product_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' => 'submit', '#value' => t('Remove'));
$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;
}
/**
* Implementation of hook_update_cart_item().
*/
function uc_product_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));
}
}
/**
* Implementation of hook_add_to_cart_data().
*/
function uc_product_add_to_cart_data($form_values) {
$node = node_load($form_values['nid']);
return array('shippable' => $node->shippable);
}
/**
* Implmentation of hook_product_class().
*/
function uc_product_product_class($pcid, $op) {
switch ($op) {
case 'insert':
db_query("UPDATE {node_type} SET module = 'uc_product', custom = 0 WHERE type = '%s'", $pcid);
$result = db_query("SELECT n.vid, n.nid, p.unique_hash FROM {node} AS n LEFT JOIN {uc_products} AS p ON n.vid = p.vid WHERE n.type = '%s'", $pcid);
while ($node = db_fetch_object($result)) {
if (!$node->unique_hash) {
$node->weight_units = variable_get('uc_weight_unit', 'lb');
$node->length_units = variable_get('uc_length_unit', 'in');
$node->pkg_qty = 1;
$node->default_qty = 1;
$node->shippable = 1;
uc_product_insert($node);
}
}
break;
}
}
/******************************************************************************
* Module Functions *
******************************************************************************/
/**
* Return the table header for the product view table.
*
* @see uc_product_table()
*/
function uc_product_table_header() {
static $columns = array();
if (empty($columns)) {
$enabled = uc_product_field_enabled();
if (module_exists('imagecache') && $enabled['image']) {
$columns['image'] = array(
'weight' => -5,
'cell' => array('data' => t('Image')),
);
}
$columns['name'] = array(
'weight' => 0,
'cell' => array('data' => t('Name'), 'field' => 'n.title'),
);
if ($enabled['list_price']) {
$columns['list_price'] = array(
'weight' => 3,
'cell' => array('data' => t('List price'), 'field' => 'p.list_price'),
);
}
if ($enabled['sell_price']) {
$columns['price'] = array(
'weight' => 5,
'cell' => array('data' => t('Price'), 'field' => 'p.sell_price'),
);
}
if (module_exists('uc_cart') && (arg(0) != 'admin' || $_GET['q'] == 'admin/store/settings/tables/uc_product_table') && $enabled['add_to_cart']) {
$columns['add_to_cart'] = array(
'weight' => 10,
'cell' => array('data' => variable_get('uc_teaser_add_to_cart_text', t('Add to cart')), 'nowrap' => 'nowrap'),
);
}
drupal_alter('tapir_table_header', $columns, 'uc_product_table');
}
return $columns;
}
/**
* Display product fields in a TAPIr table.
*
* Display image, name, price, and add to cart form.
*/
function uc_product_table($args = array()) {
$enabled = uc_product_field_enabled();
$table = array(
'#type' => 'tapir_table',
'#attributes' => array(
'class' => 'category-products',
),
'#columns' => uc_product_table_header(),
'#rows' => array(),
);
$context = array(
'revision' => 'themed',
'type' => 'product',
'class' => array('product'),
);
$options = array('label' => FALSE);
foreach ($args as $nid) {
$data = array();
$node = node_load($nid);
if ($enabled['image']) {
if (module_exists('imagecache')) {
if (($field = variable_get('uc_image_'. $node->type, '')) && isset($node->$field) && file_exists($node->{$field}[0]['filepath'])) {
$image = $node->{$field}[0];
$data['image'] = array('#value' => l(theme('imagecache', 'product_list', $image['filepath'], $image['alt'], $image['title']), 'node/'. $node->nid, array('html' => TRUE)));
}
else {
$data['image'] = array('#value' => t('n/a'));
}
}
}
$data['name'] = array(
'#value' => l($node->title, 'node/'. $node->nid),
'#cell_attributes' => array('width' => '100%'),
);
$context['subject'] = array('node' => $node);
if ($enabled['list_price']) {
$context['class'][1] = 'list';
$context['field'] = 'list_price';
$data['list_price'] = array('#value' => uc_price($node->list_price, $context, $options), '#cell_attributes' => array('nowrap' => 'nowrap'));
}
if ($enabled['sell_price']) {
$context['class'][1] = 'sell';
$context['field'] = 'sell_price';
$data['price'] = array('#value' => uc_price($node->sell_price, $context, $options), '#cell_attributes' => array('nowrap' => 'nowrap'));
}
if (module_exists('uc_cart') && arg(0) != 'admin' && $enabled['add_to_cart']) {
$data['add_to_cart'] = array('#value' => drupal_get_form('uc_catalog_buy_it_now_form_'. $node->nid, $node));
}
$table[] = $data;
}
if (!count(element_children($table))) {
$table[] = array(
'name' => array(
'#value' => t('No products available.'),
'#cell_attributes' => array(
'colspan' => 'full',
),
),
);
}
return $table;
}
/**
* @ingroup forms
* @see
* uc_product_forms()
* uc_catalog_buy_it_now_form_validate()
* uc_catalog_buy_it_now_form_submit()
*/
function uc_catalog_buy_it_now_form($form_state, $node) {
$form = array();
$form['#validate'][] = 'uc_catalog_buy_it_now_form_validate';
$form['#submit'][] = 'uc_catalog_buy_it_now_form_submit';
$form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
$form['submit'] = array(
'#type' => 'submit',
'#value' => variable_get('uc_teaser_add_to_cart_text', t('Add to cart')),
'#id' => 'edit-submit-'. $node->nid,
'#attributes' => array(
'class' => 'list-add-to-cart',
),
);
uc_form_alter($form, $form_state, __FUNCTION__);
return $form;
}
/**
* Redirect to the product page if attributes need to be selected.
*
* @see uc_catalog_buy_it_now_form()
*/
function uc_catalog_buy_it_now_form_validate($form, &$form_state) {
if (module_exists('uc_attribute')) {
$node = node_load($form_state['values']['nid']);
$attributes = uc_product_get_attributes($node->nid);
if (!empty($attributes)) {
drupal_set_message(t('This product has options that need to be selected before purchase. Please select them in the form below.'), 'error');
drupal_goto('node/'. $form_state['values']['nid']);
}
}
}
/**
* @see uc_catalog_buy_it_now_form()
*/
function uc_catalog_buy_it_now_form_submit($form, &$form_state) {
$form_state['redirect'] = uc_cart_add_item($form_state['values']['nid'], 1, module_invoke_all('add_to_cart_data', $form_state['values']), NULL, variable_get('uc_cart_add_item_msg', TRUE));
}
/**
* Format a product's model number.
*
* @ingroup themeable
*/
function theme_uc_product_model($model, $teaser = 0, $page = 0) {
$output = '';
$output .= t('SKU: @sku', array('@sku' => $model));
$output .= '
';
return $output;
}
/**
* Format a product's body.
*
* @ingroup themeable
*/
function theme_uc_product_body($body, $teaser = 0, $page = 0) {
$output = '';
$output .= $body;
$output .='
';
return $output;
}
/**
* Wrap the "Add to Cart" form in a .
*
* @ingroup themeable
*/
function theme_uc_product_add_to_cart($node, $teaser = 0, $page = 0) {
$output = '
';
if ($node->nid) {
$output .= drupal_get_form('uc_product_add_to_cart_form_'. $node->nid, $node);
}
else {
$output .= drupal_get_form('uc_product_add_to_cart_form', $node);
}
$output .= '
';
return $output;
}
/**
* Form to add the $node product to the cart.
*
* @ingroup forms
* @param $node A product node.
* @see
* uc_product_forms()
* uc_product_add_to_cart_form_validate()
* uc_product_add_to_cart_form_submit()
*/
function uc_product_add_to_cart_form($form_state, $node) {
$form = array();
$form['#validate'][] = 'uc_product_add_to_cart_form_validate';
$form['#submit'][] = 'uc_product_add_to_cart_form_submit';
$form['nid'] = array('#type' => 'value', '#value' => $node->nid);
if ($node->default_qty > 0 && variable_get('uc_product_add_to_cart_qty', FALSE)) {
$form['qty'] = array('#type' => 'textfield',
'#title' => t('Quantity'),
'#default_value' => $node->default_qty,
'#size' => 5,
'#maxlength' => 6,
);
}
else {
$form['qty'] = array('#type' => 'hidden', '#value' => 1);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => variable_get('uc_product_add_to_cart_text', t('Add to cart')),
'#id' => 'edit-submit-'. $node->nid,
'#attributes' => array(
'class' => 'node-add-to-cart',
),
);
$form['node'] = array(
'#type' => 'value',
'#value' => $node,
);
uc_form_alter($form, $form_state, __FUNCTION__);
return $form;
}
/**
* Allow only positive, numeric quantities.
*
* @see uc_product_add_to_cart_form()
*/
function uc_product_add_to_cart_form_validate($form, &$form_state) {
if (!is_numeric($form_state['values']['qty']) || intval($form_state['values']['qty']) <= 0) {
form_set_error('qty', t('You have entered an invalid quantity.'));
}
}
/**
* @see uc_product_add_to_cart_form()
*/
function uc_product_add_to_cart_form_submit($form, &$form_state) {
$form_state['redirect'] = uc_cart_add_item($form_state['values']['nid'], $form_state['values']['qty'], module_invoke_all('add_to_cart_data', $form_state['values']), NULL, variable_get('uc_cart_add_item_msg', TRUE));
}
/**
* Format a product's price.
*
* This is an extra wrapper theme around the output of uc_price() when it is
* used in the product body. For expedience, it takes the same parameters as
* uc_price().
*
* @param $price
* The monetary amount.
* @param $context
* Determines the CSS class of the
, and helps determine if the price
* needs to be altered.
* @param $options
* Toggles the label and other formatting.
* @ingroup themeable
* @see uc_price()
*/
function theme_uc_product_price($price, $context, $options = array()) {
$output = '
';
$output .= uc_price($price, $context, $options);
$output .= '
';
return $output;
}
/**
* Format a product's weight.
*
* @ingroup themeable
*/
function theme_uc_product_weight($weight, $unit = NULL, $teaser = 0, $page = 0) {
$output = '
';
$output .= t('Weight: !weight', array('!weight' => uc_weight_format($weight, $unit)));
$output .= '
';
return $output;
}
/**
* Format a product's length, width, and height.
*
* @ingroup themeable
*/
function theme_uc_product_dimensions($length, $width, $height, $units = NULL, $teaser = 0, $page = 0) {
$output = '
';
$output .= t('Dimensions: !length × !width × !height', array('!length' => uc_length_format($length, $units), '!width' => uc_length_format($width, $units), '!height' => uc_length_format($height, $units)));
$output .= '
';
return $output;
}
/**
* Format a product's images with imagecache and an image widget
* (Colorbox, Thickbox, or Lightbox2).
*
* @ingroup themeable
*/
function theme_uc_product_image($images, $teaser = 0, $page = 0) {
static $rel_count = 0;
// Get the current product image widget.
$image_widget = uc_product_get_image_widget();
$image_widget_func = $image_widget['callback'];
$first = array_shift($images);
$output = '
';
$output .= '
type;
}
elseif (is_array($node)) {
$type = $node['type'];
}
elseif (is_string($node)) {
$type = $node;
}
// If no node type was found, go ahead and return FALSE.
if (!$type) {
return FALSE;
}
// Return TRUE or FALSE depending on whether or not the node type is in the
// product types array.
return in_array($type, uc_product_types());
}
/**
* Determine whether or not a given form array is a product node form.
*
* @param $form
* The form array to examine.
* @return
* TRUE or FALSE indicating whether or not the form is a product node form.
*/
function uc_product_is_product_form($form) {
return $form['#id'] == 'node-form' && uc_product_is_product($form['#node']);
}
/**
* Get all models of a product (node).
*
* Gather any modules' models on this node, then add the node's SKU and the
* optional 'Any' option.
*
* @param $nid
* The node ID of the product.
* @param $add_blank
* String to use for the initial blank entry. If not desired, set to NULL
* or FALSE. Make sure to localize the string first. Defaults to '- Any -'.
* @return
* An associative array of model numbers. The key for '- Any -' is the empty
* string.
*/
function uc_product_get_models($node, $add_blank = TRUE) {
// Get any modules' SKUs on this node.
$models = module_invoke_all('uc_product_models', $node);
// Add the base SKU of the node.
$models[] = $node->model;
// Now we map the SKUs to the keys, for form handling, etc.
$models = drupal_map_assoc($models);
// Sort the SKUs
asort($models);
// And finally, we prepend 'Any' so it's the first option.
if (!empty($add_blank) || $add_blank === '') {
if ($add_blank === TRUE) {
$add_blank = t('- Any -');
}
return array('' => $add_blank) + $models;
}
return $models;
}
/**
* Get the cost of a product node.
*
* @param $node_id
* nid of the selected node
* @return
* float - cost
*/
function uc_product_get_cost($node_id) {
$product = node_load($node_id);
return $product->cost;
}
/**
* Returns an HTML img tag based on a node's attached image.
*
* @param $node_id
* The node's id.
* @param $format
* The imagecache preset used to format the image. 'product' by default.
* @return
* An HTML img. When $format is not 'product', the image is a link to the
* 'product_full' preset if a JavaScript display widget is available
* (Colorbox, Thickbox, and Lightbox2 are possible). For other values
* of $format, the image links to the node page.
*/
function uc_product_get_picture($node_id, $format = 'product') {
$output = '';
$product = node_load($node_id);
if (!module_exists('imagecache') || !($field = variable_get('uc_image_'. $product->type, ''))) {
return '';
}
// Get the current product image widget.
$image_widget = uc_product_get_image_widget();
$image_widget_func = $image_widget['callback'];
if (isset($product->$field)) {
$image = $product->{$field}[0];
$path = $image['filepath'];
if (file_exists($path)) {
$img = theme('imagecache', $format, $path, $image['alt'], $image['title']);
if ($format == 'product') {
if ($image_widget) {
$output .= '';
}
$output .= $img;
if ($image_widget) {
$output .= '';
}
}
else {
$output = l($img, 'node/'. $product->nid, array('html' => TRUE));
}
}
}
return $output;
}
/**
* Find the JavaScript image display module to use on product pages.
*/
function uc_product_get_image_widget() {
static $got_widget = FALSE, $image_widget = array();
// Get the current image widget. (if any)
if (!$got_widget) {
// Invoke hook to find widgets
$image_widgets = uc_store_get_image_widgets();
// Find widget preference, if any
$widget_name = variable_get('uc_product_image_widget', NULL);
if ($widget_name != NULL) {
// Widget to use has been set in admin menu
$image_widget = $image_widgets[$widget_name];
}
else {
// Widget to use has not been chosen, so default to
// first element of array, if any
$keys = array_keys($image_widgets);
$image_widget = $image_widgets[$keys[0]];
variable_set('uc_product_image_widget', $keys[0]);
}
$got_widget = TRUE;
}
return $image_widget;
}
/**
* Return HTML for the product description.
*
* Modules adding information use hook_product_description() and modules
* wanting to alter the output before rendering can do so by implementing
* hook_product_description_alter(). By default, all descriptions supplied by
* modules via hook_product_description() are concatenated together.
*
* NOTE: hook_product_description() supercedes the deprecated
* hook_cart_item_description().
*
* @param $product
* Product
* @return
* HTML rendered product description.
*/
function uc_product_get_description($product) {
// Run through implementations of hook_product_description()
$description = module_invoke_all('product_description', $product);
// Now allow alterations via hook_product_description_alter()
drupal_alter('product_description', $description, $product);
return drupal_render($description);
}
/**
* Load a product class.
*/
function uc_product_class_load($class_id) {
static $classes = array();
if (empty($classes[$class_id])) {
$result = db_query("SELECT * FROM {uc_product_classes} WHERE pcid = '%s'", $class_id);
$class = db_fetch_object($result);
$classes[$class_id] = $class;
}
return $classes[$class_id];
}
/**
* Return a bit of data from a product feature array based on the feature ID
* and array key.
*
* @param $fid
* The string ID of the product feature you want to get data from.
* @param $key
* The key in the product feature array you want: title, callback, delete,
* settings
* @return
* The value of the key you specify.
*/
function uc_product_feature_data($fid, $key) {
static $features;
if (empty($features)) {
foreach (module_invoke_all('product_feature') as $feature) {
$features[$feature['id']] = $feature;
}
}
return $features[$fid][$key];
}
/**
* Returns a form array with some default hidden values and submit button.
*
* @param $form
* The form array you wish to add the elements to.
* @return
* The form array with elements added for the nid, pfid, submit button, and
* cancel link.
* @ingroup forms
*/
function uc_product_feature_form($form) {
if (!isset($form['nid'])) {
$form['nid'] = array(
'#type' => 'hidden',
'#value' => intval(arg(1)),
);
}
if (!isset($form['pfid'])) {
$form['pfid'] = array(
'#type' => 'hidden',
'#value' => intval(arg(5)),
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save feature'),
);
$form['cancel'] = array(
'#value' => l(t('Cancel'), 'node/'. intval(arg(1)) .'/edit/features'),
);
return $form;
}
/**
* Save a product feature to a product node.
*
* @param $data
* An array consisting of the following keys:
* - pfid: the numeric ID of the product feature when editing an existing one
* - nid: the numeric ID of the product node
* - fid: the string ID of the feature type
* - description: the string description of the feature for the overview table
*/
function uc_product_feature_save($data) {
if (empty($data['nid']) && arg(0) == 'node' && intval(arg(1)) > 0) {
$data['nid'] = intval(arg(1));
}
if (empty($data['pfid'])) {
if (arg(0) == 'node' && arg(3) == 'features' && intval(arg(5)) > 0) {
$data['pfid'] = intval(arg(5));
}
}
if(!empty($data['pfid']) && db_result(db_query("SELECT COUNT(*) FROM {uc_product_features} WHERE pfid = %d", intval($data['pfid'])))) {
// First attempt to update an existing row.
db_query("UPDATE {uc_product_features} SET description = '%s' WHERE pfid = %d", $data['description'], intval($data['pfid']));
drupal_set_message(t('The product feature has been updated.'));
}
else {
// Otherwise insert this feature as a new row.
db_query("INSERT INTO {uc_product_features} (nid, fid, description) VALUES (%d, '%s', '%s')",
$data['nid'], $data['fid'], $data['description']);
drupal_set_message(t('The product feature has been added.'));
}
return 'node/'. $data['nid'] .'/edit/features';
}
/**
* Load all product feature for a node.
*
* @param $nid
* The product node ID.
* @returns
* The array of all product features object.
*/
function uc_product_feature_load_multiple($nid) {
$result = db_query("SELECT * FROM {uc_product_features} WHERE nid = %d ORDER BY pfid ASC", $nid);
while ($feature = db_fetch_object($result)) {
$features[$feature->pfid] = $feature;
}
return $features;
}
/**
* Load a product feature object.
*
* @todo: should return an object instead of array.
*
* @param $pfid
* The product feature ID.
* @param $fid
* Optional. Specify a specific feature id.
* @returns
* The product feature object.
*/
function uc_product_feature_load($pfid, $fid = NULL) {
if (isset($fid)) {
$feature = db_fetch_array(db_query("SELECT * FROM {uc_product_features} WHERE pfid = %d AND fid = '%s'", $pfid, $fid));
}
else {
$feature = db_fetch_array(db_query("SELECT * FROM {uc_product_features} WHERE pfid = %d", $pfid));
}
return $feature;
}
/**
* Delete a product feature object.
*
* @param $pfid
* The product feature ID.
* @returns
* The product feature object.
*/
function uc_product_feature_delete($pfid) {
$feature = uc_product_feature_load($pfid);
// Call the delete function for this product feature if it exists.
$func = uc_product_feature_data($feature['fid'], 'delete');
if (function_exists($func)) {
$func($feature);
}
db_query("DELETE FROM {uc_product_features} WHERE pfid = %d", $pfid);
return SAVED_DELETED;
}
/**
* Create a file field with an image field widget, and attach it to products.
*
* This field is used by default on the product page, as well as on the cart
* and catalog pages to represent the products they list. Instances are added
* to new product classes, and other node types that claim product-ness should
* call this function for themselves.
*
* @param $type
* The content type to which the image field is to be attached. This may be a
* a single type as a string, or an array of types. If NULL, all product
* types get an instance of the field.
*/
function uc_product_add_default_image_field($type = NULL) {
module_load_include('inc', 'imagefield', 'imagefield_widget');
module_load_include('inc', 'filefield', 'filefield_widget');
module_load_include('inc', 'content', 'includes/content.crud');
$label = t('Image');
$field = array(
'label' => $label,
'type' => 'filefield',
'widget_type' => 'imagefield_widget',
'weight' => -2,
'file_extensions' => 'gif jpg png',
'custom_alt' => 1,
'custom_title' => 1,
'description' => '',
'required' => 0,
'multiple' => '1',
'list_field' => '0',
'list_default' => 1,
'description_field' => '0',
'module' => 'filefield',
'widget_module' => 'imagefield',
'columns' => array(
'fid' => array(
'type' => 'int',
'not null' => FALSE,
),
'list' => array(
'type' => 'int',
'size' => 'tiny',
'not null' => FALSE,
),
'data' => array(
'type' => 'text',
'serialize' => TRUE,
),
),
'display_settings' => array(
'label' => array(
'format' => 'hidden',
),
'teaser' => array(
'format' => 'hidden',
),
'full' => array(
'format' => 'hidden',
),
4 => array(
'format' => 'hidden',
),
),
);
if (module_exists('imagefield_tokens')) {
$field['alt'] = '[title]';
$field['title'] = '[title]';
}
if ($type) {
// Accept single or multiple types as input.
$types = (array) $type;
}
else {
$types = uc_product_types();
}
foreach ($types as $type) {
$field['type_name'] = $type;
$field_name = variable_get('uc_image_'. $type, '');
if (empty($field_name)) {
$field_name = 'field_image_cache';
}
$field['field_name'] = $field_name;
$instances = content_field_instance_read(array('field_name' => $field_name, 'type_name' => $type));
if (count($instances) < 1) {
// Only add the field if it doesn't exist. Don't overwrite any changes.
content_field_instance_create($field);
variable_set('uc_image_'. $type, $field_name);
}
}
}
/**
* Implementation of hook_imagecache_default_presets().
*/
function uc_product_imagecache_default_presets() {
$presets = array();
$presets['product'] = array(
'presetname' => 'product',
'actions' => array(
array(
'weight' => '0',
'module' => 'uc_product',
'action' => 'imagecache_scale',
'data' => array(
'width' => '100',
'height' => '100',
'upscale' => 0,
),
),
),
);
$presets['product_full'] = array(
'presetname' => 'product_full',
'actions' => array(),
);
$presets['product_list'] = array(
'presetname' => 'product_list',
'actions' => array(
array(
'weight' => '0',
'module' => 'uc_product',
'action' => 'imagecache_scale',
'data' => array(
'width' => '100',
'height' => '100',
'upscale' => 0,
),
),
),
);
$presets['uc_thumbnail'] = array(
'presetname' => 'uc_thumbnail',
'actions' => array(
array(
'weight' => '0',
'module' => 'uc_product',
'action' => 'imagecache_scale',
'data' => array(
'width' => '35',
'height' => '35',
'upscale' => 0,
),
),
),
);
return $presets;
}
/**
* Implementation of hook_views_api().
*/
function uc_product_views_api() {
return array(
'api' => '2.0',
'path' => drupal_get_path('module', 'uc_product') .'/views',
);
}