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' => '', '#suffix' => '
'); $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 = ''; } } 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; } }