Index: updates.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/updates.inc,v
retrieving revision 1.6.2.5
diff -u -F^f -r1.6.2.5 updates.inc
--- updates.inc 30 Oct 2005 19:02:46 -0000 1.6.2.5
+++ updates.inc 13 Nov 2005 05:19:48 -0000
@@ -17,7 +17,8 @@
'2005-06-09' => 'update_7',
'2005-06-25' => 'update_8',
'2005-06-30' => 'update_9',
- '2005-10-02' => 'update_10'
+ '2005-10-02' => 'update_10',
+ '2005-10-22' => 'update_11'
);
function update_1() {
@@ -144,6 +145,16 @@ function update_10() {
return $ret;
}
+function update_11() {
+ if ($GLOBALS['db_type'] == 'mysql') {
+ $ret[] = update_sql('ALTER TABLE {ec_product} ADD parent INT(10) NOT NULL, ADD children VARCHAR(255) NOT NULL, ADD variation LONGTEXT NOT NULL');
+ }
+ else {
+ // pgsql goes here.
+ }
+ return $ret;
+}
+
function update_sql($sql) {
$edit = $_POST["edit"];
$result = db_query($sql);
Index: cart/cart.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/cart/cart.module,v
retrieving revision 1.44.2.11
diff -u -F^f -r1.44.2.11 cart.module
--- cart/cart.module 30 Oct 2005 19:02:46 -0000 1.44.2.11
+++ cart/cart.module 13 Nov 2005 05:19:49 -0000
@@ -140,7 +140,7 @@ function theme_cart_display_block() {
foreach ($item as $i) {
$node = node_load(array("nid" => $i->nid));
$total += product_adjust_price($node) * $i->qty;
- $output .= l("$i->qty x $node->title", "node/$node->nid"). "
";
+ $output .= l("$i->qty x $node->title", "node/" .($node->parent ? $node->parent : $node->nid)). "
";
}
$output .= "
". payment_format($total) . "
";
$output .= ''. t('Ready to
checkout?', array('%checkout-url' => url('cart/checkout'))) .'
';
@@ -207,7 +207,7 @@ function cart_page() {
switch (arg(1)) {
case 'add':
- cart_add_item(arg(2), arg(3));
+ cart_add_item(arg(2), arg(3), 'redirect', $edit);
$output = theme("cart_view");
break;
@@ -581,8 +581,27 @@ function cart_get_id() {
function cart_add_item($nid, $qty = 1, $action = "redirect", $data = array()) {
/* Make sure we can add a product */
+ $bool_cart_add = true; /* we assume we can until we can't */
$node = node_load(array('nid' => $nid));
- $bool_cart_add = module_invoke($node->ptype, 'productapi', $node, 'cart add item');
+
+ /* if this is a parent product, we do not add this directly, but the child
+ * instead. The parent is not really a product, just a place holder for
+ * all the related/sub products. */
+ if ($node->children) {
+ if ($subproduct = module_invoke($node->ptype, 'productapi', $node, 'cart get subproduct', $data)) {
+ /* we have a sub product. We need to change the the parent to the
+ * child so the child is added to the cart, and not the parent */
+ $node = $subproduct;
+ $nid = $node->nid;
+ }
+ else {
+ $bool_cart_add = false;
+ }
+ }
+
+ /* if the parent has a subproduct then the $bool_cart_add may already be
+ * no, so why bother calling the hook */
+ $bool_cart_add = $bool_cart_add ? module_invoke($node->ptype, 'productapi', $node, 'cart add item') : false;
if (is_null($bool_cart_add)) {
$bool_cart_add = true;
}
Index: product/product.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/product/product.module,v
retrieving revision 1.58.2.7
diff -u -F^f -r1.58.2.7 product.module
--- product/product.module 10 Nov 2005 14:43:23 -0000 1.58.2.7
+++ product/product.module 13 Nov 2005 05:19:55 -0000
@@ -41,7 +41,7 @@ function theme_product_view_collection()
$columns = 3;
$rows = 5;
- $result = pager_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {ec_product} p ON n.nid = p.nid WHERE n.status = 1 ORDER BY n.sticky DESC, n.created DESC'), $rows * $columns, 0);
+ $result = pager_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {ec_product} p ON n.nid = p.nid WHERE n.status = 1 AND p.parent = 0 ORDER BY n.sticky DESC, n.created DESC'), $rows * $columns, 0);
$output = '';
for ($i = 0; $node = db_fetch_object($result); $i++) {
@@ -171,6 +171,12 @@ function product_link($type, $node = nul
$links[] = t('sold out');
}
}
+ if (user_access('create products') && !$node->parent && module_invoke($node->ptype, 'productapi', $node, 'subproduct_types')) {
+ $links[] = l(t('add sub product'), "node/add/product/parent/$node->nid", array(), drupal_get_destination());
+ }
+ if ($node->parent) {
+ $links[] = l(t('view parent product'), "node/$node->parent");
+ }
}
return $links;
@@ -184,6 +190,12 @@ function product_load($node) {
if ($products[$node->nid] === NULL) {
$product = db_fetch_object(db_query('SELECT * FROM {ec_product} WHERE nid = %d', $node->nid));
+
+ /* unpack the variation fields stored in the ec_product table */
+ if ($product->variation) {
+ $product->variation = unserialize($product->variation);
+ }
+
/* Merge the product info for the specific type. */
if ($product_type = module_invoke($product->ptype, 'productapi', $product, 'load')) {
foreach ($product_type as $key => $value) {
@@ -240,6 +252,12 @@ function product_menu($may_cache) {
'callback' => 'product_to_product', 'access' => user_access('administer products'),
'type' => MENU_LOCAL_TASK, 'weight' => 2);
}
+
+ if (db_result(db_query(db_rewrite_sql("SELECT COUNT(n.nid) FROM {ec_product} p INNER JOIN {node} n ON p.nid = n.nid WHERE p.nid = %d AND p.children <> ''"), arg(1))) > 0) {
+ $items[] = array('path' => 'node/' .arg(1) .'/subproducts', 'title' => t('sub products'),
+ 'callback' => 'product_sub_products', 'access' => user_access('administer products'),
+ 'type' => MENU_LOCAL_TASK, 'weight' => 3);
+ }
}
}
@@ -325,6 +343,28 @@ function product_to_product() {
}
}
+function product_sub_products() {
+ drupal_set_title(t('view sub products'));
+ $node = node_load(array('nid' => arg(1)));
+ $products = product_get_variations($node);
+
+ $headers = array(
+ array('data' => t('title')),
+ array('data' => t('operations'), 'colspan' => 2)
+ );
+
+ foreach ($products as $nid => $product) {
+ $rows[] = array(
+ array('data' => $product->title),
+ array('data' => l(t('view'), "node/$nid")),
+ array('data' => l(t('edit'), "node/$nid/edit", array(), drupal_get_destination())),
+ );
+ }
+ $output.= theme('table', $headers, $rows);
+ $output.= form_item('', t('%add addition sub products', array('%add' => l(t('add'), "node/add/product/parent/$node->nid", array(), drupal_get_destination()))));
+ print theme('page', $output);
+}
+
/**
* Implementation of hook_node_info().
*/
@@ -485,6 +525,9 @@ function product_get_base_form_elements(
$ptype = module_invoke($edit->ptype, 'productapi', $edit, 'wizard_select');
$output .= t('Product type: %ptype
', array('%ptype' => $ptype[$edit->ptype]));
}
+ if ($options = product_get_valid_parents($edit)) {
+ $output .= form_select(t('Parent Product'), 'parent', $edit->parent, $options);
+ }
$output .= form_textfield(t('Price'), 'price', $edit->price, 25, 50, t('How much does this product retail for? Note: This price may be different from the selling price due to price adjustments elsewhere.'). $help);
if (variable_get('payment_recurring', 0)) {
$interval = drupal_map_assoc(range(0, 31));
@@ -500,7 +543,6 @@ function product_get_base_form_elements(
}
$output .= form_textfield(t('SKU'), 'sku', $edit->sku, 25, 50, t('If you have an unique identifier for this product from another system or database, enter that here. This is optional, as system IDs are automatically created for each product.'));
$output .= form_radios(t("'Add to cart' link"), 'hide_cart_link', $edit->hide_cart_link, array(t("Visible"), t("Hidden")), t("Choose whether or not you want the 'Add to cart' link visible for this product."));
-
return $output;
}
@@ -511,12 +553,38 @@ function product_get_base_form_elements(
*/
function product_wizard_form($edit) {
+ /* check to see if we have a parent product. and load it as the template */
+
+ if (!$edit && arg(3) == 'parent') {
+ if ($parent = node_load(array('nid' => arg(4)))) {
+ if ($ptypes = module_invoke($parent->ptype, 'productapi', $edit, 'subproduct_types')) {
+ foreach ($parent as $key => $value) {
+ if (!in_array($key, array('nid', 'status', 'children', 'parent', 'ptype', 'path'))) {
+ $edit->$key = $parent->$key;
+ }
+ if ($edit->type != 'product') {
+ $edit->type = 'product';
+ }
+ }
+ if (count($ptypes) == 1) {
+ $edit->ptype = $ptypes[0];
+ }
+ }
+ $edit->parent = $parent->nid;
+ }
+ }
+
/* Ask node.module to build the node form and we'll strip off the form tags. */
$node_form = ($edit->uid && $edit->name && $edit->type) ? node_form($edit) : node_add('product');
$node_form = preg_replace("'?(form).*?>'", '', $node_form);
$node_form = preg_replace("'?(input type=\"submit\").*?>'", '', $node_form);
- $node_form .= product_form_product_types();
+ if ($edit->ptype) {
+ $node_form .= form_submit(t('Create product'));
+ }
+ else {
+ $node_form .= product_form_product_types();
+ }
return form($node_form, 'post', null, array('id' => 'node-form'));
}
@@ -532,6 +600,16 @@ function product_form_product_types() {
form_set_error('ptype', t('No product types modules are installed! Please install a product type module such as tangible.module or file.module.'));
}
else {
+ /* purge types that are not compatible with the parent product */
+ if ($edit->parent) {
+ $parent = node_load(array('nid' => $edit->parent));
+ $subproduct_types = module_invoke($parent->ptype, 'productapi', $edit, 'subproduct_types');
+ foreach ($ptypes as $key => $value) {
+ if (!array_key_exists($key, $subproduct_types)) {
+ unset($ptypes[$key]);
+ }
+ }
+ }
$ptypes_display = $ptypes_display + $ptypes;
}
$output = form_select(t('Type of product to create'), 'ptype', $edit->ptype, $ptypes_display, t('You cannot change the product type once it\'s created.'));
@@ -569,6 +647,9 @@ function product_type_form($edit) {
$output .= ''. t('Add to cart link') . ': '. $cart_link . '
';
$output .= ''. t('Title') . ': '. $edit->title . '
';
$output .= ''. t('Product type') . ': '. $edit->ptype . '
';
+ if ($options = product_get_valid_parents($edit)) {
+ $output .= form_select(t('Parent Product'), 'parent', $edit->parent, $options);
+ }
// Display shipping options if we're building a shippable product and we
// have the per product shipping option enabled.
@@ -639,6 +720,12 @@ function product_form_validate(&$edit) {
function product_save($node) {
if ($node->ptype) {
$node->is_recurring = ($node->price_interval) ? 1 : 0;
+
+ /* simple product variations will be stored in the ec_product table.
+ * More complex ones may need to be stored in another table. */
+ if ($node->variation) {
+ $node->variation = serialize($node->variation);
+ }
$fields = product_fields();
/*
@@ -674,6 +761,23 @@ function product_save($node) {
module_invoke(variable_get('shipping_method', 'none'), 'shippingapi', $node, 'per_product_insert');
}
}
+
+ /* update the parent product's list of children. */
+
+ if ($node->parent) {
+ if ($parent = node_load(array('nid' => $node->parent))) {
+ if ($parent->children) {
+ $children = explode(',', $parent->children);
+ }
+ else {
+ $children = array();
+ }
+ if (!in_array($node->nid, $children)) {
+ $children[] = $node->nid;
+ db_query('UPDATE {ec_product} SET children = \'%s\' WHERE nid = %d', implode(',', $children), $parent->nid);
+ }
+ }
+ }
}
}
@@ -1035,9 +1139,64 @@ function product_send_recurring_payment_
}
/**
+ * retrieve all the subproducts for a parent product
+ */
+function product_get_variations($node) {
+
+ /* check and see if this product has been cached */
+ if ($data = cache_get("sub-products:{$node->nid}")) {
+ return unserialize($data->data);
+ }
+ else {
+ $products = array();
+
+ foreach (explode(',', $node->children) as $nid) {
+ if ($child = node_load(array('nid' => $nid))) {
+ $products[$child->nid] = $child;
+ }
+ }
+
+ /* store in cache so we can get it quicker */
+ cache_set("sub-products:{$node->nid}", serialize($products), time());
+
+ return $products;
+ }
+}
+
+/**
+ * Return an Array of valid parents for the current child.
+ */
+function product_get_valid_parents($edit) {
+ if (!$edit->ptype) {
+ return false;
+ }
+
+ foreach (module_implements('productapi') as $module) {
+ $plist = module_invoke($module, 'productapi', $edit, 'subproduct_types', array());
+ if (is_array($plist) && in_array($edit->ptype, $plist)) {
+ $pnames = module_invoke($module, 'productapi', $edit, 'wizard_select', array());
+ $ptypes = array_merge((array)$ptypes, array_keys($pnames));
+ }
+ }
+
+ if (isset($ptypes)) {
+ $options[''] = t('-- parent product --');
+ $result = db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n LEFT JOIN {ec_product} p ON n.nid = p.nid WHERE p.parent = 0 AND p.ptype IN ('" .implode("','", $ptypes) ."') ORDER BY n.created"));
+
+ while ($node = db_fetch_object($result)) {
+ $options[$node->nid] = $node->title;
+ }
+ return $options;
+ }
+ else {
+ return false;
+ }
+}
+
+/**
* The names of the database columns in the table.
*/
function product_fields($table = 'ec_product') {
- return array('nid', 'sku', 'price', 'is_recurring', 'price_interval', 'price_unit', 'price_cycle', 'auto_charge', 'ptype', 'hide_cart_link');
+ return array('nid', 'sku', 'price', 'is_recurring', 'price_interval', 'price_unit', 'price_cycle', 'auto_charge', 'ptype', 'hide_cart_link', 'parent', 'children', 'children', 'variation');
}
?>
Index: product/product.mysql
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/product/product.mysql,v
retrieving revision 1.6
diff -u -F^f -r1.6 product.mysql
--- product/product.mysql 5 Mar 2005 19:47:54 -0000 1.6
+++ product/product.mysql 13 Nov 2005 05:19:55 -0000
@@ -9,6 +9,9 @@
auto_charge tinyint(3) unsigned NOT NULL default '0',
ptype varchar(75) NOT NULL default '',
hide_cart_link int(2) unsigned NOT NULL default '0',
+ parent int(10) NOT NULL default '0',
+ children varchar(255) NOT NULL default '',
+ variation longtext NOT NULL,
UNIQUE KEY nid (nid),
KEY ptype (ptype)
) TYPE=MyISAM;
Index: store/store.mysql
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/store/Attic/store.mysql,v
retrieving revision 1.16.2.4
diff -u -F^f -r1.16.2.4 store.mysql
--- store/store.mysql 2 Oct 2005 18:15:26 -0000 1.16.2.4
+++ store/store.mysql 13 Nov 2005 05:19:55 -0000
@@ -108,6 +108,9 @@
auto_charge tinyint(3) unsigned NOT NULL default '0',
ptype varchar(75) NOT NULL default '',
hide_cart_link int(2) unsigned NOT NULL default '0',
+ parent int(10) NOT NULL default '0',
+ children varchar(255) NOT NULL default '',
+ variation longtext NOT NULL,
UNIQUE KEY nid (nid),
KEY ptype (ptype)
) TYPE=MyISAM;