Warning: If any attributes are added to or removed from this product, all work on this page will be lost!");
}
}
/**
* Implementation of hook_menu().
*/
function uc_attribute_menu($may_cache){
if ($may_cache){
$items[] = array('path' => 'admin/store/products/attributes',
'title' => t('Manage attributes'),
'description' => t('Create and edit attributes and options.'),
'access' => user_access('administer products'),
'callback' => 'uc_attribute_default',
'type' => MENU_NORMAL_ITEM,
'weight' => -1,
);
$items[] = array('path' => 'admin/store/settings/attributes',
'title' => t('Attribute settings'),
'description' => t('Configure the attribute settings'),
'access' => user_access('administer products'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_attribute_admin_settings'),
'type' => MENU_NORMAL_ITEM,
);
}
else{
$items[] = array('path' => 'admin/store/products/attributes/'. arg(4),
'title' => t('Product attribute'),
'access' => user_access('administer products'),
'callback' => 'uc_attribute_view',
'callback arguments' => array(arg(4)),
'type' => MENU_CALLBACK,
);
$items[] = array('path' => 'admin/store/products/attributes/'. arg(4) .'/edit',
'title' => t('Edit attribute'),
'access' => user_access('administer products'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_attribute_form', arg(4)),
'type' => MENU_CALLBACK,
);
$items[] = array('path' => 'admin/store/products/attributes/'. arg(4) .'/add',
'title' => t('Add option'),
'access' => user_access('administer products'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_attribute_option_form', arg(4), null),
'type' => MENU_CALLBACK,
);
$items[] = array('path' => 'admin/store/products/attributes/'. arg(4) .'/delete',
'access' => user_access('administer products'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_attribute_delete_confirm', arg(4)),
'type' => MENU_CALLBACK,
);
$items[] = array('path' => 'admin/store/products/attributes/'. arg(4) .'/'. arg(5) .'/edit',
'title' => t('Edit option'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_attribute_option_form', arg(4), arg(5)),
'type' => MENU_CALLBACK,
);
$items[] = array('path' => 'admin/store/products/attributes/'. arg(4) .'/'. arg(5) .'/delete',
'title' => t('Delete option'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_attribute_option_delete_confirm', arg(4), arg(5)),
'type' => MENU_CALLBACK,
);
$items[] = array('path' => 'admin/store/products/classes/'. arg(4) .'/attributes',
'title' => t('Attributes'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_object_attributes_form', arg(4), 'class'),
'weight' => 1,
'type' => MENU_LOCAL_TASK,
);
$items[] = array('path' => 'admin/store/products/classes/'. arg(4) .'/options',
'title' => t('Attribute options'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_object_options_form', arg(4), 'class'),
'weight' => 2,
'type' => MENU_LOCAL_TASK,
);
// Insert subitems into the edit node page for product types.
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'edit'){
$node = node_load(arg(1));
$types = uc_product_node_info();
if ($node->nid && in_array($node->type, array_keys($types))){
$items[] = array('path' => 'node/'. arg(1) .'/edit/attributes',
'title' => t('Attributes'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_object_attributes_form', arg(1), 'product'),
'weight' => 1,
'type' => MENU_LOCAL_TASK,
);
$items[] = array('path' => 'node/'. arg(1) .'/edit/options',
'title' => t('Options'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_object_options_form', arg(1), 'product'),
'weight' => 2,
'type' => MENU_LOCAL_TASK,
);
$items[] = array('path' => 'node/'. arg(1) .'/edit/adjustments',
'title' => t('Adjustments'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_product_adjustments_form', arg(1)),
'weight' => 3,
'type' => MENU_LOCAL_TASK,
);
}
}
drupal_add_css(drupal_get_path('module', 'uc_attribute') .'/uc_attribute.css');
}
return $items;
}
/**
* Implementation of hook_form_alter().
*
* Attach option selectors to the form with the "Add to Cart" button.
*/
function uc_attribute_form_alter($form_id, &$form){
if (strpos($form_id, 'add_to_cart_form') || strpos($form_id, 'add_product_form')){
// If the node has a product list, add attributes to them
if (count(element_children($form['products']))){
foreach (element_children($form['products']) as $key){
$form['products'][$key] = _uc_attribute_alter_form($form['products'][$key]);
if (isset($form['products'][$key]['attributes'])){
$form['products'][$key]['#type'] = 'fieldset';
}
}
}
// If not, add attributes to the node.
else{
$form['attributes'] = array('#tree' => true, '#weight' => -1);
$form = _uc_attribute_alter_form($form);
}
}
}
/**
* Implementation of hook_nodeapi().
*/
function uc_attribute_nodeapi(&$node, $op, $arg3 = null, $arg4 = null){
if (in_array($node->type, array_keys(uc_product_node_info()))){
switch($op){
case 'insert':
db_query("INSERT IGNORE INTO {uc_product_attributes} (nid, aid, ordering, required, default_option) SELECT %d, aid, ordering, required, default_option FROM {uc_class_attributes} WHERE pcid = '%s'", $node->nid, $node->type);
db_query("INSERT IGNORE INTO {uc_product_options} (nid, oid, cost, price, weight, ordering) SELECT %d, oid, cost, price, weight, ordering FROM {uc_class_attribute_options} WHERE pcid = '%s'", $node->nid, $node->type);
break;
case 'delete':
db_query("DELETE FROM {uc_product_options} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {uc_product_attributes} WHERE nid = %d", $node->nid);
break;
case 'update index':
$output = '';
$attributes = uc_product_get_attributes($node->nid);
foreach ($attributes as $attribute){
$output .= ''. $attribute->name .'
';
foreach ($attribute->options as $option){
$output .= $option->name .' ';
}
$output .= "\n";
}
$result = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
while ($adjustment = db_fetch_object($result)){
$output .= ''. $adjustment->model ."\n";
}
return $output;
}
}
}
/******************************************************************************
* Ubercart Hooks *
******************************************************************************/
/**
* Stores the customer's choices in the cart.
*/
function uc_attribute_add_to_cart_data($form_values){
$combination = array();
if (!isset($form_values['attributes'])){
return array('attributes' => array(), 'model' => null);
}
foreach ($form_values['attributes'] as $aid => $value){
if (is_numeric($value)){
$combination[$aid] = $value;
}
}
$result = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = %d AND combination LIKE '%s'", $form_values['nid'], serialize($combination));
$model = db_result($result);
// drupal_set_message('Attributes to add to order Array(aid => oid):'. print_r($form_values['attributes'], true) . $model .'
');
// Preserve the 'attributes' key to allow other modules to add to the data field.
return array('attributes' => $form_values['attributes'], 'model' => $model);
}
/**
* Implementation of hook_product_class().
*/
function uc_attribute_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;
}
}
/**
* Implementation of hook_cart_item().
*/
function uc_attribute_cart_item($op, &$item) {
switch ($op) {
case 'load':
$item->options = _uc_cart_product_get_options($item);
$op_costs = 0;
$op_prices = 0;
$op_weight = 0;
foreach ($item->options as $option){
$op_costs += $option['cost'];
$op_prices += $option['price'];
$op_weight += $option['weight'];
}
$item->cost += $op_costs;
$item->price += $op_prices;
$item->weight += $op_weight;
if (!empty($item->data['model'])) {
$item->model = $item->data['model'];
}
break;
}
}
/******************************************************************************
* Menu Callbacks *
******************************************************************************/
/**
* Change the display of attribute option prices.
*
* @ingroup forms
*/
function uc_attribute_admin_settings(){
$form = array();
$form['uc_attribute_option_price_format'] = array('#type' => 'radios',
'#title' => t('Option price format'),
'#default_value' => variable_get('uc_attribute_option_price_format', 'adjustment'),
'#options' => array('none' => t('Do not display'),
'adjustment' => t('Display price adjustment'),
'total' => t('Display total price'),
),
'#description' => t('Formats the price is in the attribute selection form when the customer adds a product to their cart. The total price will only be displayed on products with only one attribute.'),
);
return system_settings_form($form);
}
/**
* Display a list of attributes and a form to add more.
*
* @see uc_attribute_form
*/
function uc_attribute_default(){
$result = db_query("SELECT a.aid, a.name, a.ordering, COUNT(ao.oid) AS options FROM {uc_attributes} AS a LEFT JOIN {uc_attribute_options} AS ao ON a.aid = ao.aid GROUP BY a.aid ORDER BY a.ordering, a.name");
$header = array(t('Name'), t('# of Options'), t('Order'), array('data' => t('Operations'), 'colspan' => '3'));
$rows = array();
while($attr = db_fetch_object($result)){
$rows[] = array($attr->name, $attr->options, $attr->ordering, l(t('options'), 'admin/store/products/attributes/'. $attr->aid .'/options'), l(t('edit'), 'admin/store/products/attributes/'. $attr->aid .'/edit'), l(t('delete'), 'admin/store/products/attributes/'. $attr->aid .'/delete'));
}
if (count($rows) == 0){
$rows[] = array(array('data' => t('No product attributes have been defined yet.'), 'colspan' => '6'));
}
$output = theme('table', $header, $rows);
$output .= ''. t('Add an Attribute') .'
';
$output .= drupal_get_form('uc_attribute_form');
return $output;
}
/**
* Display options and the modifications to products they represent.
*
* @see uc_attribute_option_form
*/
function uc_attribute_view($attr_id){
if (!($attribute = uc_attribute_load($attr_id))){
drupal_set_message(t('Attribute with aid @id does not exist.', array('@id' => $attr_id)), 'error');
drupal_goto('admin/store/products/attributes');
}
$breadcrumbs = drupal_get_breadcrumb();
$breadcrumbs[] = l(t('Attributes'), 'admin/store/products/attributes/');
drupal_set_breadcrumb($breadcrumbs);
$output = ''. $attribute->name .'
';
$rows = array();
foreach ($attribute->options as $key => $input){
$rows[] = array($input->name, $input->cost, $input->price, $input->weight, $input->ordering, l(t('edit'), 'admin/store/products/attributes/'. $attr_id .'/'. $key .'/edit'), l(t('delete'), 'admin/store/products/attributes/'. $attr_id .'/'. $key .'/delete'));
}
if (count($rows) == 0){
$rows[] = array(array('data' => t('No options defined for this product attribute.'), 'colspan' => '7'));
}
$header = array(t('Name'), t('Default cost'), t('Default price'), t('Default weight'), t('Order'), array('data' => t('Operations'), 'colspan' => '2'));
$output .= theme('table', $header, $rows);
$output .= ''. t('Add an option') .'
';
$output .= drupal_get_form('uc_attribute_option_form', $attr_id);
//$output .= l(t('Add a field'), 'admin/store/products/attributes/'. $attr_id .'/add');
return $output;
}
/**
* Form builder for product attributes.
*
* @ingroup forms
* @see uc_attribute_form_validate
* @see uc_attribute_form_submit
*/
function uc_attribute_form($attr_id = null){
$attribute = uc_attribute_load($attr_id);
if ($attr_id){
$form['aid'] = array('#type' => 'hidden', '#value' => $attr_id);
$crumbs = drupal_get_breadcrumb();
$crumbs[] = l(t('Attributes'), 'admin/store/products/attributes');
drupal_set_breadcrumb($crumbs);
}
$form['name'] = array('#type' => 'textfield',
'#title' => t('Attribute name'),
'#default_value' => $attribute->name,
'#weight' => 0,
);
$form['ordering'] = array('#type' => 'weight',
'#title' => t('Order'),
'#default_value' => $attribute->ordering,
'#weight' => 1,
);
$form['op'] = array('#type' => 'submit',
'#value' => t('Submit'),
'#weight' => 2,
);
return $form;
}
/**
* Validate function for uc_attribute_add_form().
*/
function uc_attribute_form_validate($form_id, $form_values){
if (empty($form_values['name'])){
form_set_error(t('Product attributes must have names.'));
}
}
/**
* Submit function for uc_attribute_add_form().
*/
function uc_attribute_form_submit($form_id, $form_values){
if (isset($form_values['aid'])){
db_query("UPDATE {uc_attributes} SET name = '%s', ordering = %d WHERE aid = %d", $form_values['name'], $form_values['ordering'], $form_values['aid']);
}
else{
db_query("INSERT INTO {uc_attributes} (name, ordering) VALUES ('%s', %d)", $form_values['name'], $form_values['ordering']);
}
return 'admin/store/products/attributes';
}
/**
* Confirms the deletion of the given attribute.
*/
function uc_attribute_delete_confirm($aid){
$result = db_query("SELECT COUNT(*) AS number FROM {uc_product_attributes} WHERE aid = %d", $aid);
$products = db_fetch_object($result);
$form['aid'] = array('#type' => 'value', '#value' => $aid);
$form['#redirect'] = 'admin/store/products/attributes';
$output = confirm_form($form, t('Be very sure you want to delete this product attribute. '), 'admin/store/products/attributes',
format_plural($products->number, 'There is 1 product with this attribute. ', 'There are @count products with this attribute. '),
t('Continue'), t('Cancel'));
return $output;
}
/**
* Submit function for uc_attribute_delete_confirm().
*/
function uc_attribute_delete_confirm_submit($form_id, $form_values){
if ($form_values['confirm']){
db_query("DELETE FROM co USING {uc_class_attribute_options} AS co, {uc_attribute_options} AS ao WHERE co.oid = ao.oid AND ao.aid = %d", $form_values['aid']);
db_query("DELETE FROM {uc_class_attributes} WHERE aid = %d", $form_values['aid']);
db_query("DELETE FROM po USING {uc_product_options} AS po, {uc_attribute_options} AS ao WHERE po.oid = ao.oid AND ao.aid = %d", $form_values['aid']);
db_query("DELETE FROM pd USING {uc_product_adjustments} AS pd, {uc_product_attributes} AS pa WHERE pd.nid = pa.nid AND pa.aid = %d", $form_values['aid']);
db_query("DELETE FROM {uc_product_attributes} WHERE aid = %d", $form_values['aid']);
db_query("DELETE FROM {uc_attribute_options} WHERE aid = %d", $form_values['aid']);
db_query("DELETE FROM {uc_attributes} WHERE aid = %d", $form_values['aid']);
drupal_set_message(t('Product attribute deleted.'));
}
}
/**
* Form builder for attribute options.
*
* @ingroup forms
* @see uc_attribute_option_form_validate
* @see uc_attribute_option_form_submit
*/
function uc_attribute_option_form($attr_id, $opt_id = null){
$attribute = uc_attribute_load($attr_id);
if ($opt_id){
$crumbs = drupal_get_breadcrumb();
$crumbs[] = l(t('Attributes'), 'admin/store/products/attributes');
$crumbs[] = l($attribute->name, 'admin/store/products/attributes/'. $attr_id);
drupal_set_breadcrumb($crumbs);
$option = $attribute->options[$opt_id];
}
else{
$option = new stdClass();
}
$form['name'] = array('#type' => 'textfield',
'#title' => t('Option name'),
'#default_value' => $option->name,
'#required' => true,
'#weight' => 0,
);
$form['cost'] = array('#type' => 'textfield',
'#title' => t('Default cost'),
'#default_value' => $option->cost,
'#description' => t('Adjusts the product cost by the specified dollar amount.'),
'#weight' => 1,
);
$form['price'] = array('#type' => 'textfield',
'#title' => t('Default price'),
'#default_value' => $option->price,
'#description' => t('Adjusts the product price by the specified dollar amount.'),
'#weight' => 2,
);
$form['weight'] = array('#type' => 'textfield',
'#title' => t('Default weight'),
'#default_value' => $option->weight,
'#description' => t('Adjusts the product weight by this value in pounds.'),
'#weight' => 3,
);
$form['ordering'] = array('#type' => 'weight',
'#title' => t('Order'),
'#default_value' => $option->ordering,
'#weight' => 4,
);
$form['aid'] = array('#type' => 'hidden', '#value' => $attr_id);
if ($opt_id){
$form['oid'] = array('#type' => 'hidden', '#value' => $opt_id);
}
$form['submit'] = array('#type' => 'submit', '#value' => t('Submit'),
'#weight' => 10,
);
return $form;
}
/**
* Validate number formats.
*/
function uc_attribute_option_form_validate($form_id, $form_values){
$pattern = '/^-?\d*(\.\d*)?$/';
$price_error = t('This must be in a valid number format. No commas and only one decimal point.');
if(!is_numeric($form_values['cost']['#value']) && !preg_match($pattern, $form_values['cost']['#value'])){
form_set_error('cost', $price_error);
}
if(!is_numeric($form_values['price']['#value']) && !preg_match($pattern, $form_values['price']['#value'])){
form_set_error('price', $price_error);
}
if(!is_numeric($form_values['weight']['#value']) && !preg_match($pattern, $form_values['weight']['#value'])){
form_set_error('weight', $price_error);
}
}
/**
* Submit function for uc_attribute_option_form().
*/
function uc_attribute_option_form_submit($form_id, $form_values){
if (!isset($form_values['oid'])){
db_query("INSERT INTO {uc_attribute_options} (aid, name, cost, price, weight, ordering) VALUES (%d, '%s', %f, %f, %f, %d)",
$form_values['aid'], $form_values['name'], $form_values['cost'], $form_values['price'], $form_values['weight'], $form_values['ordering']);
}
else{
db_query("UPDATE {uc_attribute_options} SET name = '%s', cost = %f, price = %f, weight = %f, ordering = %d WHERE aid = %d AND oid = %d",
$form_values['name'], $form_values['cost'], $form_values['price'], $form_values['weight'], $form_values['ordering'], $form_values['aid'], $form_values['oid']);
}
return 'admin/store/products/attributes/'. $form_values['aid'];
}
/**
* Confirms deletion of the given attribute option.
*/
function uc_attribute_option_delete_confirm($attr_id, $opt_id){
$form['aid'] = array('#type' => 'value', '#value' => $attr_id);
$form['oid'] = array('#type' => 'value', '#value' => $opt_id);
//$form['#redirect'] = 'admin/store/products/attributes/'. $attr_id .'/edit';
$output = confirm_form($form, t('Are you sure you want to delete this option?'), 'admin/store/products/attributes/'. $attr_id .'/edit',
'', t('Continue'), t('Cancel'));
return $output;
}
/**
* Submit function for uc_attribute_option_delete_confirm().
*/
function uc_attribute_option_delete_confirm_submit($form_id, $form_values){
if ($form_values['confirm']){
$match = 'i:'. $form_values['aid'] .';s:'. strlen($form_values['oid']) .':"'. $form_values['oid'] .'";';
db_query("DELETE FROM {uc_product_adjustments} WHERE combination LIKE '%%%s%%'", $match);
db_query("DELETE ao, co, po FROM {uc_attribute_options} AS ao LEFT JOIN {uc_product_options} AS po ON ao.oid = po.oid LEFT JOIN {uc_class_attribute_options} AS co ON ao.oid = co.oid WHERE ao.oid = %d", $form_values['oid']);
}
return 'admin/store/products/attributes/'. $form_values['aid'] .'/options';
}
/**
* Form builder to associate attributes with products or classes.
*
* @param $id The nid of the product, or the pcid of a class.
* @param $type Either 'product' or 'class'
* @ingroup forms
* @see theme_uc_object_attributes_form
* @see uc_object_attributes_form_submit
*/
function uc_object_attributes_form($id, $type){
$location = array();
$location[] = menu_get_item(null, 'admin');
$location[] = menu_get_item(null, 'admin/store');
$location[] = menu_get_item(null, 'admin/store/products');
$location[] = menu_get_item(null, 'admin/store/products/attributes');
$breadcrumb = drupal_get_breadcrumb();
if (count($breadcrumb) == 1){
foreach ($location as $item){
$breadcrumb[] = l($item['title'], $item['path']);
}
drupal_set_breadcrumb($breadcrumb);
}
$form['admin_link'] = array('#value' => l(t('Attributes administration'), 'admin/store/products/attributes'));
if ($type == 'product'){
$product = node_load($id);
drupal_set_title(check_plain($product->title));
// Get list of attributes already associated with the node.
$chosen_attr = uc_product_get_attributes($id);
}
else if ($type == 'class'){
$class = uc_product_class_load($id);
drupal_set_title(check_plain($class->name));
// Get list of attributes already associated with the class.
$chosen_attr = uc_class_get_attributes($id);
}
$form['chosen_attr'] = array();
if (count($chosen_attr)){
foreach ($chosen_attr as $prod_attr){
$opt = $prod_attr->options[$prod_attr->default_option];
/* if (!$opt){
$opt = reset($prod_attr->options); // non-destructive shift
} */
$form['chosen_attr'][$prod_attr->aid] = array(
'remove' => array('#type' => 'checkbox', '#default_value' => 0),
'name' => array('#type' => 'markup', '#value' => $prod_attr->name),
'option' => array('#type' => 'markup', '#value' => $opt ? ($opt->name .' ('. uc_currency_format($opt->price) .')' ) : t('n/a')),
'required' => array('#type' => 'checkbox', '#default_value' => $prod_attr->required),
'ordering' => array('#type' => 'weight', '#default_value' => $prod_attr->ordering),
);
}
}
//$form['table'] = array('#type' => 'markup', '#value' => theme('table', $header, $rows), '#weight' => -5);
$chosen_aids = array();
foreach($chosen_attr as $attr){
$chosen_aids[] = $attr->aid;
}
// Get list of attributes NOT associated with this node.
$result = db_query("SELECT a.aid, a.name FROM {uc_attributes} AS a LEFT JOIN {uc_attribute_options} AS ao ON a.aid = ao.aid GROUP BY aid ORDER BY a.name");
$other_attr = array();
while ($attr = db_fetch_object($result)){
if (!in_array($attr->aid, $chosen_aids)){
$other_attr[$attr->aid] = $attr->name;
}
}
//print ''. print_r($chosen_attr, true) .'
';
$form['#tree'] = true;
$form['attributes'] = array('#type' => 'select',
'#multiple' => true,
'#title' => t('Attributes'),
//'#default_value' => $form_values['attributes'],
'#options' => (count($other_attr) ? $other_attr : array(t('No attributes left to add.'))),
'#weight' => -1
);
$form['add'] = array('#type' => 'submit', '#value' => t('Continue'), '#weight' => 0);
if (!count($other_attr)){
$form['attributes']['#disabled'] = 'disabled';
}
$form['id'] = array('#type' => 'value', '#value' => $id);
$form['type'] = array('#type' => 'value', '#value' => $type);
//drupal_set_message(''. htmlspecialchars(print_r($form, true)) .'
');
return $form;
}
/**
* Display the formatted attribute form.
*
* @ingroup themeable
*/
function theme_uc_object_attributes_form($form){
$output = drupal_render($form['admin_link']);
$header = array(t('Remove'), t('Name'), t('Default option'), t('Required'), t('Order'));
if (count(element_children($form['chosen_attr']))){
foreach (element_children($form['chosen_attr']) as $attr){
$rows[] = array(drupal_render($form['chosen_attr'][$attr]['remove']), drupal_render($form['chosen_attr'][$attr]['name']), drupal_render($form['chosen_attr'][$attr]['option']), drupal_render($form['chosen_attr'][$attr]['required']), drupal_render($form['chosen_attr'][$attr]['ordering']));
}
}
else{
$rows[] = array(array('data' => t('No attributes have been selected. Add some below.'), 'colspan' => 4));
}
$output .= theme('table', $header, $rows);
$output .= drupal_render($form);
return $output;
}
/**
* Submit function for uc_product_attributes_form().
*/
function uc_object_attributes_form_submit($form_id, $form_values){
// adjustments also have no meaning if attributes are changed in any way.
if ($form_values['type'] == 'product'){
db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $form_values['id']);
$attr_table = '{uc_product_attributes}';
$opt_table = '{uc_product_options}';
$id = 'nid';
$type = '%d';
}
else if ($form_values['type'] == 'class'){
$attr_table = '{uc_class_attributes}';
$opt_table = '{uc_class_attribute_options}';
$id = 'pcid';
$type = "'%s'";
}
$values = array($form_values['id']);
if (is_array($form_values['chosen_attr'])){
foreach($form_values['chosen_attr'] as $aid => $attr){
if ($attr['remove']){
$replace .= ', %d';
$values[] = $aid;
}
else{
db_query("UPDATE $attr_table SET ordering = %d, required = %d WHERE aid = %d AND $id = $type", $attr['ordering'], $attr['required'], $aid, $form_values['id']);
}
}
}
$replace = '('. substr($replace, 2) .')';
if (count($values) > 1){
db_query("DELETE po FROM $opt_table AS po LEFT JOIN {uc_attribute_options} AS ao ON po.oid = ao.oid WHERE po.$id = %d AND ao.aid IN ". $replace, $values);
db_query("DELETE FROM $attr_table WHERE $id = $type AND aid IN ". $replace, $values);
}
foreach($form_values['attributes'] as $aid){
db_query("INSERT INTO $attr_table ($id, aid, ordering, default_option) SELECT $type, aid, ordering, %d FROM {uc_attributes} WHERE aid = %d",
$form_values['id'], 0, $aid);
}
//return 'node/'. $form_values['id'] .'/edit/attributes';
}
/**
* Form builder to attach attribute options to products or classes.
*
* @param $id The nid of the product, or the pcid of a class.
* @param $type Either 'product' or 'class'
* @ingroup forms
* @see theme_uc_object_options_form
* @see uc_object_options_form_submit
*/
function uc_object_options_form($obj_id, $type){
$location = array();
$location[] = menu_get_item(null, 'admin');
$location[] = menu_get_item(null, 'admin/store');
$location[] = menu_get_item(null, 'admin/store/products');
$location[] = menu_get_item(null, 'admin/store/products/attributes');
$breadcrumb = drupal_get_breadcrumb();
if (count($breadcrumb) == 1){
foreach ($location as $item){
$breadcrumb[] = l($item['title'], $item['path']);
}
drupal_set_breadcrumb($breadcrumb);
}
$form['admin_link'] = array('#value' => l(t('Attributes administration'), 'admin/store/products/attributes'));
if ($type == 'product'){
$product = node_load($obj_id);
drupal_set_title(check_plain($product->title));
$chosen_attr = uc_product_get_attributes($obj_id);
$table = '{uc_product_options}';
$id = 'nid';
$type = '%d';
$formtype = 'product';
}
else if ($type == 'class'){
$class = uc_product_class_load($obj_id);
drupal_set_title(check_plain($class->name));
$chosen_attr = uc_class_get_attributes($obj_id);
$table = '{uc_class_attribute_options}';
$id = 'pcid';
$type = "'%s'";
$formtype = 'class';
}
$form['prod_attr'] = array('#tree' => true);
foreach ($chosen_attr as $key => $attribute){
$form['prod_attr'][$key] = array('#prefix' => '', '#suffix' => '
');
$form['prod_attr'][$key]['name'] = array('#type' => 'markup',
'#value' => $attribute->name,
);
$form['prod_attr'][$key]['aid'] = array('#type' => 'hidden',
'#value' => $attribute->aid,
);
$form['prod_attr'][$key]['ordering'] = array('#type' => 'value',
'#value' => $attribute->ordering,
);
// Display options selector.
$form['prod_attr'][$key]['options'] = array('#weight' => 2,
);
$base_attr = uc_attribute_load($attribute->aid);
if ($base_attr->options){
$result = db_query("SELECT ao.aid, ao.oid, ao.name, ao.cost AS default_cost, ao.price AS default_price, ao.weight AS default_weight, ao.ordering AS default_ordering, po.cost, po.price, po.weight, po.ordering FROM {uc_attribute_options} AS ao LEFT JOIN $table AS po ON ao.oid = po.oid AND po.$id = $type WHERE aid = %d ORDER BY po.ordering, default_ordering, ao.name", $obj_id, $attribute->aid);
$options = array();
$n = db_num_rows($result);
$i = 0;
while ($op_array = db_fetch_object($result)){
$oid = $op_array->oid;
$form['prod_attr'][$key]['options'][$oid] = array();
$options[$oid] = $op_array->name;
$form['prod_attr'][$key]['options'][$oid]['select'] = array('#type' => 'checkbox',
'#default_value' => (isset($attribute->options[$oid]) ? 1 : 0),
'#title' => $op_array->name,
);
/* $form['prod_attr'][$key]['options'][$oid]['name'] = array('#type' => 'markup',
'#value' => $op_array->name,
); */
$form['prod_attr'][$key]['options'][$oid]['cost'] = array('#type' => 'textfield',
'#title' => t('Cost'),
'#default_value' => (is_null($op_array->cost) ? $op_array->default_cost : $op_array->cost),
'#size' => 6,
);
$form['prod_attr'][$key]['options'][$oid]['price'] = array('#type' => 'textfield',
'#title' => t('Price'),
'#default_value' => (is_null($op_array->price) ? $op_array->default_price : $op_array->price),
'#size' => 6,
);
$form['prod_attr'][$key]['options'][$oid]['weight'] = array('#type' => 'textfield',
'#title' => t('Weight'),
'#default_value' => (is_null($op_array->weight) ? $op_array->default_weight : $op_array->weight),
'#size' => 5,
);
$form['prod_attr'][$key]['options'][$oid]['ordering'] = array('#type' => 'weight',
'#title' => t('Order'),
'#default_value' => (is_null($op_array->ordering) ? $op_array->default_ordering : $op_array->ordering),
);
$i++;
}
$form['prod_attr'][$key]['default'] = array('#type' => 'radios',
'#options' => $options,
'#default_value' => $attribute->default_option,
);
}
else{
$form['prod_attr'][$key]['default'] = array('#type' => 'markup',
'#value' => t('(To be determined by customer.)'),
);
}
}
$form['submit'] = array('#type' => 'submit', '#value' => t('Submit'), '#weight' => 10);
$form['id'] = array('#type' => 'value', '#value' => $obj_id);
$form['type'] = array('#type' => 'value', '#value' => $formtype);
return $form;
}
/**
* Display the option form.
*
* @ingroup themeable
*/
function theme_uc_object_options_form($form){
$output = drupal_render($form['admin_link']);
$header = array(t('Attribute'), t('Default option'), array('colspan' => 5, 'data' => t('Options')));
$rows = array();
foreach (element_children($form['prod_attr']) as $key){
$row = array();
$row[] = array('data' => drupal_render($form['prod_attr'][$key]['aid']) . drupal_render($form['prod_attr'][$key]['name']), 'class' => 'attribute');
//$row[] = array('data' => drupal_render($form['prod_attr'][$key]['default']), 'class' => 'attribute');
$i = 0;
if (element_children($form['prod_attr'][$key]['default'])){
foreach (element_children($form['prod_attr'][$key]['default']) as $oid){
$row[] = drupal_render($form['prod_attr'][$key]['default'][$oid]);
$row[] = drupal_render($form['prod_attr'][$key]['options'][$oid]['select']);
//$row[] = drupal_render($form['prod_attr'][$key]['options'][$oid]['name']);
$row[] = drupal_render($form['prod_attr'][$key]['options'][$oid]['cost']);
$row[] = drupal_render($form['prod_attr'][$key]['options'][$oid]['price']);
$row[] = drupal_render($form['prod_attr'][$key]['options'][$oid]['weight']);
$row[] = drupal_render($form['prod_attr'][$key]['options'][$oid]['ordering']);
// if not the first row for the attribute, make empty cells on the left
if ($i != 0){
$row = array_pad($row, -7, '');
}
$i++;
$rows[] = $row;
$row = array();
}
unset($form['prod_attr'][$key]['default']);
}
else{
$row[] = array('data' => drupal_render($form['prod_attr'][$key]['default']), 'colspan' => 7);
$rows[] = $row;
}
$rows[] = array(array('data' => '
', 'colspan' => 7));
}
$output .= theme('table', $header, $rows, array('class' => 'product_attributes'));
$output .= drupal_render($form);
return $output;
}
/**
* Validate function for uc_product_options_form().
*/
function uc_object_options_form_validate($form_id, $form_values){
if (isset($form_values['prod_attr'])){
foreach ($form_values['prod_attr'] as $attribute){
$selected_opts = array();
if (is_array($attribute['options'])){
foreach ($attribute['options'] as $oid => $option){
if ($option['select'] == 1){
$selected_opts[] = $oid;
}
}
}
if (!empty($selected_opts) && !in_array($attribute['default'], $selected_opts)){
form_set_error($attribute['default'], t('Default value must be a valid value.'));
}
}
}
}
/**
* Submit handler for uc_object_options_form().
*/
function uc_object_options_form_submit($form_id, $form_values){
if ($form_values['type'] == 'product'){
$attr_table = '{uc_product_attributes}';
$opt_table = '{uc_product_options}';
$id = 'nid';
$type = '%d';
}
else if ($form_values['type'] == 'class'){
$attr_table = '{uc_class_attributes}';
$opt_table = '{uc_class_attribute_options}';
$id = 'pcid';
$type = "'%s'";
}
// Adjustments have no meaning if possible option combinations are changed in any way.
foreach ($form_values['prod_attr'] as $prod_attr){
db_query("DELETE FROM $attr_table WHERE $id = $type AND aid = %d", $form_values['id'], $prod_attr['aid']);
db_query("INSERT INTO $attr_table ($id, aid, ordering, required, default_option) VALUES ($type, %d, %d, %d, %d)",
$form_values['id'], $prod_attr['aid'], $prod_attr['ordering'], $prod_attr['required'], $prod_attr['default']);
if (is_array($prod_attr['options'])){
foreach ($prod_attr['options'] as $oid => $option){
db_query("DELETE FROM $opt_table WHERE $id = $type AND oid = %d", $form_values['id'], $oid);
if ($option['select']){
db_query("INSERT INTO $opt_table ($id, oid, cost, price, weight, ordering) VALUES ($type, %d, %f, %f, %f, %d)",
$form_values['id'], $oid, $option['cost'], $option['price'], $option['weight'], $option['ordering']);
}
else if ($form_values['type'] == 'product'){
$aid = $prod_attr['aid'];
$match = 'i:'. $aid .';s:'. strlen($oid) .':"'. $oid .'";';
db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d AND combination LIKE '%%%s%%'", $form_values['id'], $match);
}
}
}
}
drupal_set_message(t('The @type options have been saved.', array('@type' => $form_values['type'])));
}
/**
* Form builder to associate option combinations with mutations of a product's model number.
*
* @ingroup forms
* @see uc_product_adjustments_form_submit
*/
function uc_product_adjustments_form($nid){
$location = array();
$location[] = menu_get_item(null, 'admin');
$location[] = menu_get_item(null, 'admin/store');
$location[] = menu_get_item(null, 'admin/store/products');
$location[] = menu_get_item(null, 'admin/store/products/attributes');
$breadcrumb = drupal_get_breadcrumb();
if (count($breadcrumb)){
foreach ($location as $item){
$breadcrumb[] = l($item['title'], $item['path']);
}
drupal_set_breadcrumb($breadcrumb);
}
$form['admin_link'] = array('#value' => l(t('Attributes administration'), 'admin/store/products/attributes'));
$node = node_load($nid);
drupal_set_title(check_plain($node->title));
//$model = db_result(db_query("SELECT model FROM {uc_products} WHERE nid = %d", $nid));
$model = $node->model;
//Populate table and such.
$query_select = "SELECT DISTINCT";
$query_from = " FROM";
$query_where = " WHERE";
$query_order = " ORDER BY";
$result = db_query("SELECT * FROM {uc_product_attributes} AS pa LEFT JOIN {uc_attributes} AS a ON pa.aid = a.aid WHERE nid = %d", $nid);
$num_prod_attr = db_num_rows($result);
$i = 1;
$attribute_names = '';
$full_attributes = array();
$values = array();
while ($prod_attr = db_fetch_object($result)){
$query_select .= " ao$i.aid AS aid$i, ao$i.name AS name$i, ao$i.oid AS oid$i,";
$query_from .= " ({uc_product_options} AS po$i LEFT JOIN {uc_attribute_options} AS ao$i ON po$i.oid = ao$i.oid AND po$i.nid = %d),";
$values[] = $nid;
$query_where .= " ao$i.aid = ". $prod_attr->aid ." AND";
$query_order .= " po$i.ordering, ao$i.name,";
++$i;
$attribute_names .= ''. $prod_attr->name .' | ';
$attribute_ids[] = $prod_attr->aid;
}
// Remove last connecting parts (commas, "AND")
$query_select = rtrim($query_select, ',');
$query_from = rtrim($query_from, ',');
$query_where = substr($query_where, 0, strlen($query_where) - 4);
$query_order = rtrim($query_order, ',');
// $query_where .= " po1.nid = %d";
if ($num_prod_attr){
//Get previous values
$result = db_query("SELECT * FROM {uc_product_adjustments} WHERE nid = %d", $nid);
$old_vals = array();
while ($obj = db_fetch_object($result)){
$old_vals[] = $obj;
}
$result = pager_query($query_select . $query_from . $query_where . $query_order, 20, 0, null, $values);
$form['original'] = array('#value' => $model,
'#prefix' => '
',
'#suffix' => '
',
);
$form['default'] = array('#type' => 'value', '#value' => $model);
//$form['attributes'] = array('#value' => theme('uc_attribute', 'attributes', $attribute_ids, $nid));
//$form['query'] = array('#value' => ''. print_r($old_vals, true) .'
');
$form['table'] = array('#prefix' => '
');
$form['table']['head'] = array('#prefix' => '',
'#suffix' => '
',
'#value' => $attribute_names .'Model | ',
'#weight' => 0,
);
$form['table']['body'] = array('#prefix' => '', '#suffix' => '',
'#weight' => 1,
'#tree' => true,
);
$i = 0;
while ($combo = db_fetch_object($result)){
$cells = '';
$row_title = '';
$comb_array = array();
for ($j = 1; $j <= $num_prod_attr; ++$j){
$cells .= ''. $combo->{'name'.$j} .' | ';
$row_title .= $combo->{'name'.$j} .', ';
$comb_array[$combo->{'aid'.$j}] = $combo->{'oid'.$j};
}
$row_title = substr($row_title, 0, strlen($row_title) - 2);
$serial_array = serialize($comb_array);
$default_model = $model;
foreach ($old_vals as $ov){
if (!count(array_diff_assoc(unserialize($ov->combination), unserialize($serial_array)))){
$default_model = $ov->model;
break;
}
}
$form['table']['body'][$i] = array('#prefix' => '', '#suffix' => '
');
$form['table']['body'][$i]['combo'] = array('#type' => 'markup',
'#value' => $cells,
);
$form['table']['body'][$i]['combo_array'] = array('#type' => 'hidden', '#value' => $serial_array);
$form['table']['body'][$i]['model'] = array('#type' => 'textfield',
'#default_value' => $default_model,
'#prefix' => '',
'#suffix' => ' | ',
);
++$i;
}
$form['nid'] = array('#type' => 'hidden', '#value' => $nid);
$form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
}
else{
$form['error'] = array('#value' => t('There are no attributes associated with this product. Click "attributes" above to add some.'));
}
$form['pager'] = array('#value' => theme('pager'));
return $form;
}
/**
* Submit function for uc_product_adjustments_form().
*/
function uc_product_adjustments_form_submit($form_id, $form_values){
foreach ($form_values['body'] as $value){
if (!empty($value['model']) && $value['model'] != $form_values['default']){
db_query("UPDATE {uc_product_adjustments} SET model = '%s' WHERE nid = %d AND combination = '%s'",
$value['model'], $form_values['nid'], $value['combo_array']);
if (!db_affected_rows()){
db_query("INSERT INTO {uc_product_adjustments} (nid, combination, model) VALUES (%d, '%s', '%s')",
$form_values['nid'], $value['combo_array'], $value['model']);
}
}
}
drupal_set_message(t('Product adjustments have been saved.'));
$goto = array($_GET['q']);
if ($_GET['page']){
$goto[] = 'page='. $_GET['page'];
}
return $goto;
}
/******************************************************************************
* Module Functions *
******************************************************************************/
/**
* Load an attribute from the database.
*
* @param $attr_id
* The id of the attribute.
* @param $nid
* Node id. If given, the attribute will have the options that have been
* assigned to that node for the attribute.
*/
function uc_attribute_load($attr_id, $nid = null, $type = ''){
if ($nid){
switch ($type){
case 'product':
$attribute = db_fetch_object(db_query("SELECT a.aid, a.name, a.ordering AS default_ordering, pa.default_option, pa.required, pa.ordering FROM {uc_attributes} AS a LEFT JOIN {uc_product_attributes} AS pa ON a.aid = pa.aid AND pa.nid = %d WHERE a.aid = %d ORDER BY pa.ordering", $nid, $attr_id));
$result = db_query("SELECT po.nid, po.oid, po.cost, po.price, po.weight, po.ordering, ao.name, ao.aid FROM {uc_product_options} AS po LEFT JOIN {uc_attribute_options} AS ao ON po.oid = ao.oid AND nid = %d WHERE aid = %d ORDER BY po.ordering, ao.name", $nid, $attr_id);
break;
case 'class':
$attribute = db_fetch_object(db_query("SELECT a.aid, a.name, a.ordering AS default_ordering, ca.default_option, ca.ordering FROM {uc_attributes} AS a LEFT JOIN {uc_class_attributes} AS ca ON a.aid = ca.aid AND ca.pcid = '%s' WHERE a.aid = %d ORDER BY ca.ordering", $nid, $attr_id));
$result = db_query("SELECT co.pcid, co.oid, co.cost, co.price, co.weight, co.ordering, ao.name, ao.aid FROM {uc_class_attribute_options} AS co LEFT JOIN {uc_attribute_options} AS ao ON co.oid = ao.oid AND co.pcid = '%s' WHERE ao.aid = %d ORDER BY co.ordering, ao.name", $nid, $attr_id);
break;
default:
$attribute = db_fetch_object(db_query("SELECT * FROM {uc_attributes} WHERE aid = %d", $attr_id));
$result = db_query("SELECT * FROM {uc_attribute_options} WHERE aid = %d ORDER BY ordering, name", $attr_id);
break;
}
if (isset($attribute->default_ordering) && is_null($attribute->ordering)){
$attribute->ordering = $attribute->default_ordering;
}
}
else{
$attribute = db_fetch_object(db_query("SELECT * FROM {uc_attributes} WHERE aid = %d", $attr_id));
$result = db_query("SELECT * FROM {uc_attribute_options} WHERE aid = %d ORDER BY ordering, name", $attr_id);
}
if ($attribute){
$attribute->options = array();
while ($option = db_fetch_object($result)){
$attribute->options[$option->oid] = $option;
}
//uasort($attribute->options, '_uc_attribute_sort');
}
return $attribute;
}
/**
* Load the option identified by $opt_id.
*/
function uc_attribute_option_load($opt_id){
$result = db_query("SELECT * FROM {uc_attribute_options} WHERE oid = %d", $opt_id);
return db_fetch_object($result);
}
/**
* Load all attributes associated with a product node.
*/
function uc_product_get_attributes($nid){
$result = db_query("SELECT aid FROM {uc_product_attributes} WHERE nid = %d ORDER BY ordering", $nid);
$chosen_attr = array();
while ($attr = db_fetch_object($result)){
$chosen_attr[$attr->aid] = uc_attribute_load($attr->aid, $nid, 'product');
}
//uasort($chosen_attr, '_uc_attribute_sort');
return $chosen_attr;
}
/**
* Load all attributes associated with a product class.
*/
function uc_class_get_attributes($pcid){
$result = db_query("SELECT aid FROM {uc_class_attributes} WHERE pcid = '%s' ORDER BY ordering", $pcid);
$chosen_attr = array();
while ($attr = db_fetch_object($result)){
$chosen_attr[$attr->aid] = uc_attribute_load($attr->aid, $pcid, 'class');
}
uasort($chosen_attr, '_uc_attribute_sort');
return $chosen_attr;
}
/**
* Display formatted of data associated with attributes
*
* @param $type
* options | attributes
* Determines how to handle the $data array.
* @param $data
* An array of attribute ids or option ids. If option ids, they are indexed by attribute ids.
* @return
* HTML format of $data.
*
* @ingroup themeable
*/
function theme_uc_attribute($type, $data, $nid){
if ($type == 'options' && is_array($data)){
if (count($data)){
$output = '';
foreach ($data as $aid => $oid){
$attribute = uc_attribute_load($aid);
$option = $attribute->options[$oid];
$output .= '- '. $option->name .'
';
}
$output .= '
';
}
}
elseif ($type == 'attributes'){
if (is_array($data) && count($data)){
$output = '';
foreach ($data as $aid){
$output .= theme('uc_attribute_options', $aid, $nid);
}
$output .= '
';
}
elseif (is_numeric($data)){
$output = '';
$output .= theme('uc_attribute_options', $data);
$output .= '
';
}
}
return $output;
}
/**
* Format an attribute and its options.
*
* @ingroup themeable
*/
function theme_uc_attribute_options($aid, $nid){
$attribute = uc_attribute_load($aid, $nid, 'product');
$output .= ''. $attribute->name .'
';
$rows = array();
foreach ($attribute->options as $key => $input){
$rows[] = array($input->oid, $input->name);
}
if (count($rows) == 0){
$rows[] = array(array('data' => t('(To be determined by customer.)'), 'colspan' => '2'));
}
$header = array(t('Option ID'), t('Name'));
$output .= theme('table', $header, $rows);
return $output;
}
/**
* Get the options chosen for a product that is in the cart.
*
* @param $item
* An element of the array returned by uc_cart_get_contents.
* @return
* Array of options chosen by a customer, indexed by attribute ids. Each
* element stores the attribute name and the option object chosen.
*/
function _uc_cart_product_get_options($item){
$options = array();
$data = $item->data;
if (!empty($data['attributes']) && is_array($data['attributes'])){
foreach ($data['attributes'] as $aid => $oid){
$attribute = uc_attribute_load($aid, $item->nid, 'product');
if (count($attribute->options)){
$options[$aid] = (array)$attribute->options[$oid];
$options[$aid]['attribute'] = $attribute->name;
}
else{
$options[$aid] = array('attribute' => $attribute->name,
'oid' => 0,
'name' => $oid,
'cost' => 0,
'price' => 0,
'weight' => 0,
);
}
}
}
else{
$options = array();
}
return $options;
}
/**
* Helper function for uc_attribute_form_alter()
*/
function _uc_attribute_alter_form($product){
$result = db_query("SELECT * FROM {uc_product_attributes} WHERE nid = %d ORDER BY ordering", $product['nid']['#value']);
$total_attributes = db_num_rows($result);
if ($node = node_load((int)$product['nid']['#value'])){
while ($relation = db_fetch_object($result)){
$attribute = uc_attribute_load($relation->aid, $product['nid']['#value'], 'product');
$opt_array = array();
foreach($attribute->options as $option){
switch (variable_get('uc_attribute_option_price_format', 'adjustment')){
case 'total':
$display_price = ', '. uc_currency_format($node->sell_price + $option->price);
if ($total_attributes == 1){
break;
}
case 'adjustment':
$display_price = ($option->price != 0 ? ', '. ($option->price > 0 ? '+' : '') . uc_currency_format($option->price) : '');
break;
case 'none':
default:
$display_price = '';
break;
}
$opt_array[$option->oid] = $option->name . $display_price;
}
if (count($attribute->options)){
if($attribute->required) {
$opt_array = array('' => 'Please select an option') + $opt_array;
$relation->default_option = '';
}
$product['attributes'][$attribute->aid] = array('#type' => 'select',
'#title' => $attribute->name,
'#default_value' => $relation->default_option,
'#options' => $opt_array,
'#required' => $attribute->required,
);
}
else{
$product['attributes'][$attribute->aid] = array('#type' => 'textfield',
'#title' => $attribute->name,
'#required' => $attribute->required,
);
}
$product['attributes']['#theme'] = 'uc_attribute_add_to_cart';
}
}
return $product;
}
/**
* usort() callback.
*/
function _uc_attribute_sort($a, $b){
if ($a->ordering == $b->ordering){
return 0;
}
else{
return ($a->ordering < $b->ordering) ? -1 : 1;
}
}