Index: product/product.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ecommerce/product/product.module,v
retrieving revision 1.110
diff -u -u -F^f -r1.110 product.module
--- product/product.module 18 May 2006 12:42:24 -0000 1.110
+++ product/product.module 18 May 2006 19:01:25 -0000
@@ -296,28 +296,31 @@ function product_menu($may_cache) {
);
}
else {
- if (arg(0) == 'node' && is_numeric(arg(1)) && user_access('administer products')) {
- // Only add the product-tab for non-product pages:
- if (db_result(db_query(db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n WHERE n.nid = %d AND n.type != 'product'"), arg(1))) > 0) {
- $items[] = array(
- 'path' => 'node/'. arg(1) .'/product',
- 'title' => t('product'),
- 'callback' => 'product_to_product',
- 'access' => user_access('administer products'),
- 'type' => MENU_LOCAL_TASK,
- 'weight' => 2
- );
- $items[] = array(
- 'path' => 'node/'. arg(1) .'/product/delete',
- 'title' => t('delete product'),
- 'callback' => 'product_to_product_delete',
- 'access' => user_access('administer products'),
- 'type' => MENU_CALLBACK,
+ // These items are for transforming non-product nodes into products
+ if (arg(0) == 'node' && is_numeric(arg(1))) {
+ $node = node_load(arg(1));
+ $settings = _product_transform_get_settings($node->type);
+ if (count($settings['product_types'])) {
+ // Node can be a product.
+ $items[] = array('path' => 'node/'. arg(1) .'/product',
+ 'title' => t('product'),
+ 'callback' => 'product_transform_to_product',
+ 'callback arguments' => array($node->nid),
+ 'access' => (user_access('administer products') ||
+ user_access("administer $node->type products")),
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 2);
+ $items[] = array('path' => 'node/'. arg(1) .'/product/delete',
+ 'title' => t('delete product'),
+ 'callback' => 'product_transform_to_product_delete',
+ 'access' => (user_access('administer products') ||
+ user_access("administer $node->type products")),
+ 'type' => MENU_CALLBACK,
);
}
}
}
-
+
return $items;
}
@@ -337,15 +340,109 @@ function product_node_name($node) {
/**
* Implementation of hook_form_alter()
+ * Adds node-type settings to admin/settings/node-types/
+ * Adds product fields to node edit forms, where applicable
*/
function product_form_alter($form_id, &$form) {
- if (isset($form['type']) && $form_id == $form['type']['#value'] .'_node_form') {
- foreach (array('ptype') as $key) {
- $form[$key] = array('#type' => 'value', '#value' => $form['#node']->$key);
+ if (isset($form['type'])) {
+ $ntype = $form['type']['#value'];
+ $defaults = _product_transform_get_settings($ntype);
+ if (isset($form['#node']) && isset($form['#node']->ptype)) {
+ // ptype has been set for this node
+ $ptype = $form['#node']->ptype;
+ }
+ else if (count($defaults['product_types']) == 1) {
+ // this node type can be only one product type
+ $ptype = array_shift($defaults['product_types']);
+ }
+
+ if ($form_id == $ntype . '_node_settings' &&
+ $ntype != 'product') {
+ // admin/settings/content-types form
+ if (user_access('administer products')) {
+ $form['product_transform'] =
+ array('#type' => 'fieldset',
+ '#title' => t('Product'),
+ '#collapsible' => true,
+ '#collapsed' => (!count($defaults['product_types'])),
+ '#tree' => true,
+ );
+ $options = array_merge(product_get_ptypes());
+ $form['product_transform']['product_types'] =
+ array('#type' => 'checkboxes',
+ '#title' => t('Product type'),
+ '#multiple' => TRUE,
+ '#options' => $options,
+ '#default_value' => $defaults['product_types'],
+ '#description' => t('Select the product type(s), if any, that apply to %ntype nodes', array('%ntype' => $ntype)),
+ );
+
+ // Allow user to configure defaults
+ $form['product_transform']['product_defaults'] =
+ array('#type' => 'fieldset',
+ '#title' => t('Product defaults'),
+ );
+ $product_defaults = (object)$defaults['product_defaults']['values'];
+ $form['product_transform']['product_defaults']['values'] = product_base_form_elements($product_defaults);
+ unset($form['product_transform']['product_defaults']['values']['price']['#required']);
+
+ // system_settings_form should do this for us:
+ $form['buttons']['#weight'] = 99;
+
+ // make sure we are called when form is submitted
+ $form['#submit']['product_transform_node_settings_submit'] =
+ array($ntype);
+ }
+ }
+ else if ($form_id == $ntype . '_node_form') {
+ foreach (array('ptype') as $key) {
+ $form[$key] = array('#type' => 'value', '#value' => $form['#node']->$key);
+ }
+ if ($ntype != 'product') {
+ // node/add/$ntype form. or node/$nid/edit form
+ if (user_access('administer products') ||
+ user_access('administer '. $ntype . ' products')) {
+ $node = $form['#node'];
+ if ($ptype) {
+ $form['product_transform'] =
+ array('#type' => 'fieldset',
+ '#title' => t('Product'),
+ '#collapsible' => true,
+ '#collapsed' => false,
+ );
+ $form['product_transform']['content'] = _product_transform_node_form($node, $ptype);
+
+ if ($node->nid) {
+ $form['product_transform']['description'] =
+ array('#type' => 'markup',
+ '#value' => t('To delete product settings, see the product tab'),
+ );
+ }
+
+ $form['#validate']['product_transform_node_form_validate'] =
+ array($ptype);
+ }
+ else if (count($defaults['product_types']) && !$node->nid) {
+ // User may choose from several product types. They must do this on
+ // the product tab, after node has been created.
+ $form['product_transform'] =
+ array('#type' => 'fieldset',
+ '#title' => t('Product'),
+ '#collapsible' => FALSE, //collapse does not work with markup?
+ );
+ $form['product_transform']['content'] =
+ array('#type' => 'markup',
+ '#value' => t('To make this node a product: First submit your changes here. Then see the product tab.'),
+ );
+ }
+ }
+ }
}
}
}
+
+
/**
* Implementation of hook_nodeapi().
*
@@ -368,17 +465,9 @@ function product_nodeapi(&$node, $op, $a
case 'insert':
case 'update':
- // copy the product to the next revision.
- if ($node->type != 'product' && $node->revision) {
- $last_vid = db_result(db_query('SELECT p.vid AS pvid FROM {node_revisions} r LEFT JOIN {ec_product} p ON r.vid = p.vid WHERE r.nid = %d AND r.vid <> %d ORDER BY r.vid DESC LIMIT 1', $node->nid, $node->vid));
- if ($last_vid) {
- $product->nid = $node->nid;
- $product->vid = $last_vid;
- if ($product = product_load($product)) {
- $product->vid = $node->vid;
- product_save($product);
- }
- }
+ // product fields included in node form
+ if ($node->product_transform_make_product || $node->ptype) {
+ product_save($node);
}
break;
@@ -399,9 +488,25 @@ function product_nodeapi(&$node, $op, $a
/**
* Implementation of hook_perm().
+ *
+ * Creates 'administer TYPE products' permission for each node type
+ * which is also a product type. Granting a user that permission will
+ * allow them to edit the product settings for any node of that type.
+ *
+ * Granting 'administer products' allows
+ * the user to edit product settings for all node types.
*/
function product_perm() {
- return array('administer products');
+ $items[] = 'administer products';
+
+ // one perm for each node type that may be converted into a product
+ $settings = _product_transform_get_settings();
+ foreach ($settings as $ntype => $ntype_settings) {
+ if (count($ntype_settings['product_types'])) {
+ $items[] = "administer $ntype products";
+ }
+ }
+ return $items;
}
/**
@@ -530,15 +635,25 @@ function product_add() {
return $output;
}
-function product_types_listing($link = 'node/add/product') {
+/**
+ * @param $link
+ * base path when generating links
+ * @param $filter
+ * optional array of product types to display.
+ * Hide those types not included in $filter.
+ */
+function product_types_listing($link = 'node/add/product', $filter = NULL) {
// If no (valid) product type has been provided, display a product type overview.
$node = new StdClass();
foreach (product_get_ptypes() as $ptype => $name) {
- $node->ptype = $ptype;
- if (product_access('create', $node)) {
- $out = '
'. l($name, "$link/$ptype", array('title' => t('Add a %s.', array('%s' => $name)))) .'';
- $out .= ''. implode("\n", module_invoke_all('help', 'node/add/product#'. $ptype)) .'';
- $item[$name] = $out;
+ if ($filter == NULL ||
+ in_array($ptype, $filter)) {
+ $node->ptype = $ptype;
+ if (product_access('create', $node)) {
+ $out = ''. l($name, "$link/$ptype", array('title' => t('Add a %s.', array('%s' => $name)))) .'';
+ $out .= ''. implode("\n", module_invoke_all('help', 'node/add/product#'. $ptype)) .'';
+ $item[$name] = $out;
+ }
}
}
@@ -635,107 +750,12 @@ function product_page() {
return theme('product_view_collection');
}
-/**
- * Handles all product operations for non-product nodes.
- */
-function product_to_product() {
- $op = $_POST['op'];
- $node = node_load(arg(1));
-
- if ($node->nid) {
- drupal_set_title(t('%node-title (product)', array('%node-title' => $node->title)));
- $product = db_fetch_object(db_query('SELECT * FROM {ec_product} WHERE vid = %d', $node->vid));
- $ptypes = product_get_ptypes();
- if ($product || (arg(3) && in_array(arg(3), array_keys($ptypes)))) {
- $node->ptype = $node->ptype ? $node->ptype : arg(3);
- $form = product_base_form_elements($node);
- $form['#node'] = $node;
- foreach (array('nid', 'vid', 'ptype') as $key) {
- $form[$key] = array('#type' => 'value', '#value' => $node->$key);
- }
-
- $form = array_merge($form, (array)module_invoke($node->ptype, 'productapi', $node, 'form'));
- if ($product) {
- $form['update'] = array('#type' => 'submit', '#value' => t('Update product'), '#weight' => 40);
- $form['remove'] = array('#type' => 'submit', '#value' => t('Remove product'), '#weight' => 45);
- }
- else {
- $form['create'] = array('#type' => 'submit', '#value' => t('Create product'), '#weight' => 40);
- }
- return drupal_get_form('product_to_product', $form);
- }
- else {
- return product_types_listing("node/$node->nid/product");
- }
- }
-}
-
-function product_to_product_validate($form_id, $form_values) {
- $op = $_POST['op'];
- $edit = (object) $form_values;
-
- switch ($op) {
- case t('Create product'):
- case t('Update product'):
- product_form_validate($edit);
- break;
- }
-}
-
-function product_to_product_submit($form_id, $form_values) {
- $op = $_POST['op'];
- $edit = (object) $form_values;
-
- switch ($op) {
- case t('Create product'):
- case t('Update product'):
- product_save($edit);
- drupal_set_message(t('The product has been saved.'));
- return "node/$edit->nid";
- break;
- case t('Remove product'):
- //product_delete($edit, true);
- //drupal_set_message(t('Removed the post from the product listings.'));
- return "node/$edit->nid/product/delete";
- break;
- }
-}
-
-function product_to_product_delete() {
- $node = node_load(arg(1));
-
- $form['node'] = array('#type' => 'value', '#value' => $node);
-
- $output.= confirm_form(
- 'product_to_product_delete',
- $form,
- t('Are you sure that you want to delete the product information for %title', array('%title' => $node->title)),
- "node/{$node->nid}/product",
- t('The product information will be permenantly removed.'),
- t('Delete Product'),
- t('Cancel')
- );
- return $output;
-}
-
-function product_to_product_delete_submit($form_id, $form_values) {
- if ($form_values['confirm']) {
- product_delete($form_values['node'], true);
- drupal_set_message(t('Removed the post from the product listings.'));
- return "node/{$form_values['node']->nid}";
- }
- return "node/{$form_values['node']->nid}/product";
-}
function product_form_validate(&$edit) {
$errors = array();
- /* Remove the currency symbol at the beginning of the price if it exists */
+ /* Make sure price is a number */
if (isset($edit->price)) {
- if (substr($edit->price, 0, 1) == variable_get('payment_symbol', '$')) {
- $edit->price = substr($edit->price, count(variable_get('payment_symbol', '$')));
- }
- $edit->price = str_replace(',', '', $edit->price);
if (!is_numeric($edit->price)) {
$errors['price'] = t('Please enter a numeric value for the product price.');
}
@@ -1197,3 +1217,222 @@ function product_views_tables() {
function product_views_handler_filter_product_type() {
return product_get_ptypes();
}
+
+
+/****************************************************************
+ * Product transformation functions. Convert non-products to products
+ * See also hook_menu and hook_form_alter and hook_nodeapi
+ ****************************************************************/
+
+/**
+ * Return the product type settings.
+ * Settings are stored as a drupal variable, named product_transform_settings
+ *
+ * @param
+ * $ntype a node type (optional)
+ *
+ * @return
+ * If no node type provided, an associative array in which the keys
+ * are node type names and values are associative arrays. The values
+ * array contains settings specific to the node type (key). These
+ * nested settings include the product_type.
+ * If node type provided, the settings for that specific node type are
+ * returned.
+ */
+function _product_transform_get_settings($ntype = NULL) {
+ $settings = variable_get('product_transform_settings', array());
+ if (!$ntype)
+ return $settings;
+ else {
+ $ntype_settings = $settings[$ntype];
+ if (!$ntype_settings) {
+ // default node-type settings here:
+ $ntype_settings = array('product_types' => array());
+ }
+ return $ntype_settings;
+ }
+}
+
+/**
+ * Called when node-type settings form is submitted.
+ *
+ * We add the settings for this particular node type to the
+ * product_transform settings variable.
+ */
+function product_transform_node_settings_submit($form_id, $form, $ntype) {
+ // clean up data structure returned from checkboxes
+ foreach ($form['product_transform']['product_types'] as $ptype => $value) {
+ if (!$value) {
+ unset($form['product_transform']['product_types'][$ptype]);
+ }
+ }
+
+ $settings = _product_transform_get_settings();
+ $settings[$ntype] = $form['product_transform'];
+ variable_set('product_transform_settings', $settings);
+
+ // because of the way the settings form works, it saves a bogus variable.
+ // here we clean up after it
+ variable_del('product_transform');
+}
+
+
+/**
+ * Build the form fields for product details.
+ *
+ * The returned form may be included in node edit form, or product tab.
+ */
+function _product_transform_node_form($node, $ptype) {
+ // If node is not yet a product, provide default values
+ if (!$node->ptype && !count($_POST)) {
+ $settings = _product_transform_get_settings($node->type);
+ if (count($settings['product_defaults']['values'])) {
+ foreach ($settings['product_defaults']['values'] as $key => $value) {
+ $node->$key = $value;
+ }
+ }
+ }
+
+ // prime the form
+ $form = product_base_form_elements($node);
+
+ if (!$node->nid) {
+ // when creating a new node, include a "make product" checkbox
+ if (isset($node->product_transform_make_product)) {
+ $make_product = $node->product_transform_make_product;
+ }
+ else {
+ $make_product = TRUE;
+ }
+
+ $form['product_transform_make_product'] =
+ array('#type' => 'checkbox',
+ '#title' => t('Include this item in the product list.'),
+ '#default_value' => $make_product,
+ '#weight' => -99);
+ }
+
+ $form['ptype'] = array('#type' => 'value', '#value' => $ptype);
+ // fields specific to this product type
+ $form = array_merge($form, (array)module_invoke($ptype, 'productapi', $node, 'form'));
+
+ if (!$node->nid) {
+ // when creating a new node, price is only required if make_product is checked
+ unset($form['price']['#required']);
+ }
+
+ return $form;
+}
+
+/**
+ * Validate node form.
+ *
+ * Invokes product_form_validate() for both general validation, and
+ * product-type-specific validation.
+ */
+function product_transform_node_form_validate($form_id, $node, &$form, $ptype) {
+ if ($node->product_transform) {
+ $node_object = (object) $node;
+ product_form_validate($node_object);
+ }
+}
+
+/**
+ * Build product tab on node view. This appears after a node has been saved.
+ * The pages shown here enable functionality that is difficult to include in
+ * the node form. Including deleting product settings and changing product
+ * type.
+ */
+function product_transform_to_product($nid) {
+ $node = node_load($nid);
+ $settings = _product_transform_get_settings($node->type);
+ $ptype = $node->ptype;
+ if (!$ptype && (count($settings['product_types']) == 1))
+ $ptype = array_shift($settings['product_types']);
+ else if (arg(3) && in_array(arg(3), $settings['product_types'])) {
+ $ptype = arg(3);
+ }
+
+ if ($ptype) {
+ $already_product = $node->ptype;
+
+ $form = _product_transform_node_form($node, $ptype);
+
+ foreach (array('nid', 'vid') as $key) {
+ $form[$key] = array('#type' => 'value', '#value' => $node->$key);
+ }
+
+ // submit buttons
+ if ($already_product) {
+ $form['update'] = array('#type' => 'submit', '#value' => t('Update product'), '#weight' => 40);
+ $form['remove'] = array('#type' => 'submit', '#value' => t('Remove product'), '#weight' => 45);
+ }
+ else {
+ $form['create'] = array('#type' => 'submit', '#value' => t('Create product'), '#weight' => 40);
+ }
+
+ $form['#validate']['product_transform_node_form_validate'] =
+ array($ptype);
+ return drupal_get_form('product_transform_to_product', $form);
+ }
+ else if (count($settings['product_types']) > 1) {
+ // Let user choose amoung several product types
+ return product_types_listing("node/$node->nid/product",
+ $settings['product_types']);
+ }
+}
+
+/**
+ * Save data from product tab.
+ *
+ * Allows arbitrary node types to become products. Or, delete product
+ * information about a specific node.
+ */
+function product_transform_to_product_submit($form_id, $form_values) {
+ $op = $_POST['op'];
+ $edit = (object) $form_values;
+
+ switch ($op) {
+ case t('Create product'):
+ case t('Update product'):
+ product_save($edit);
+ drupal_set_message(t('The product has been saved.'));
+ return "node/$edit->nid";
+ break;
+ case t('Remove product'):
+ return "node/$edit->nid/product/delete";
+ break;
+ }
+}
+
+/**
+ * Callback to confirm deletion of product info from non-product nodes.
+ */
+function product_transform_to_product_delete() {
+ $node = node_load(arg(1));
+
+ $form['node'] = array('#type' => 'value', '#value' => $node);
+
+ $output.= confirm_form(
+ 'product_transform_to_product_delete',
+ $form,
+ t('Are you sure that you want to delete the product information for %title', array('%title' => $node->title)),
+ "node/{$node->nid}/product",
+ t('The product information will be permenantly removed.'),
+ t('Delete Product'),
+ t('Cancel')
+ );
+ return $output;
+}
+
+/**
+ * Delete product information from non-product nodes.
+ */
+function product_transform_to_product_delete_submit($form_id, $form_values) {
+ if ($form_values['confirm']) {
+ product_delete($form_values['node'], true);
+ drupal_set_message(t('Removed the post from the product listings.'));
+ return "node/{$form_values['node']->nid}";
+ }
+ return "node/{$form_values['node']->nid}/product_transform";
+}