uc_product_kit.module

Tracking 5.x-1.x branch
  1. drupal
    1. 5 contributions/ubercart/uc_product_kit/uc_product_kit.module
    2. 6 contributions/ubercart/uc_product_kit/uc_product_kit.module
    3. 7 contributions/ubercart/uc_product_kit/uc_product_kit.module

The product kit module for Übercart.

Product kits are groups of products that are sold as a unit.

Coded by: Lyle Mantooth

Functions & methods

NameDescription
theme_uc_product_kit_add_to_cartWrap the "Add to Cart" form in a <div>.
uc_product_kit_accessImplementation of hook_access().
uc_product_kit_add_to_cart
uc_product_kit_add_to_cart_formLets the cart know how many of which products are included in a kit.
uc_product_kit_add_to_cart_form_submitSubmit function for uc_product_kit_add_to_cart_form().
uc_product_kit_buy_it_now_form
uc_product_kit_buy_it_now_form_submit
uc_product_kit_cart_displayImplementation of Ubercart's hook_cart_display().
uc_product_kit_cart_itemImplementation of Übercart's hook_cart_item().
uc_product_kit_deleteImplementation of hook_delete().
uc_product_kit_disable
uc_product_kit_enable
uc_product_kit_formImplementation of hook_form().
uc_product_kit_formsRegister an "Add to Cart" form for each product kit.
uc_product_kit_form_alterAdd product kit settings to the product settings form.
uc_product_kit_insertImplementation of hook_insert().
uc_product_kit_loadImplementation of hook_load().
uc_product_kit_nodeapi
uc_product_kit_node_infoImplementation of hook_node_info().
uc_product_kit_product_types
uc_product_kit_store_status
uc_product_kit_updateImplementation of hook_update().
uc_product_kit_update_cart_itemImplementation of Übercart's hook_update_cart_item().
uc_product_kit_viewImplementation of hook_view().
_uc_product_kit_sort_productsusort() callback.

Constants

NameDescription
UC_PRODUCT_KIT_MUTABLE
UC_PRODUCT_KIT_UNMUTABLE_NO_LIST
UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * The product kit module for Übercart.
  5. *
  6. * Product kits are groups of products that are sold as a unit.
  7. *
  8. * Coded by: Lyle Mantooth
  9. */
  10. define('UC_PRODUCT_KIT_UNMUTABLE_NO_LIST', -1);
  11. define('UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST', 0);
  12. define('UC_PRODUCT_KIT_MUTABLE', 1);
  13. /******************************************************************************
  14. * Drupal Hooks *
  15. ******************************************************************************/
  16. /**
  17. * Implementation of hook_access().
  18. */
  19. function uc_product_kit_access($op, $node) {
  20. global $user;
  21. switch ($op) {
  22. case 'create':
  23. return user_access('create products');
  24. case 'update':
  25. case 'delete':
  26. if ($user->uid == $node->uid) {
  27. return user_access('edit own products');
  28. }
  29. else {
  30. return user_access('edit products');
  31. }
  32. }
  33. }
  34. function uc_product_kit_enable() {
  35. $node_type = node_get_types('type', 'product_kit');
  36. if ($node_type->module == 'node') {
  37. $node_type->module = 'uc_product_kit';
  38. $node_type->custom = 0;
  39. node_type_save($node_type);
  40. }
  41. }
  42. function uc_product_kit_disable() {
  43. $node_type = node_get_types('type', 'product_kit');
  44. $node_type->module = 'node';
  45. $node_type->custom = 1;
  46. node_type_save($node_type);
  47. }
  48. /**
  49. * Implementation of hook_node_info().
  50. *
  51. * @return Node type information for product kits.
  52. */
  53. function uc_product_kit_node_info() {
  54. return array(
  55. 'product_kit' => array(
  56. 'name' => t('Product kit'),
  57. 'module' => 'uc_product_kit',
  58. 'description' => t('This node represents two or more products that have been listed together. This presents a logical and convenient grouping of items to the customer.'),
  59. 'title_label' => t('Name'),
  60. 'body_label' => t('Description'),
  61. ),
  62. );
  63. }
  64. /**
  65. * Implementation of hook_insert().
  66. *
  67. * Add a row to {uc_products} to make a product. Extra information about the
  68. * component products are stored in {uc_product_kits}.
  69. * @param &$node The node object being saved.
  70. * @see uc_product_insert
  71. */
  72. function uc_product_kit_insert(&$node) {
  73. $obj = new stdClass();
  74. $obj->vid = $node->vid;
  75. $obj->nid = $node->nid;
  76. $obj->model = '';
  77. $obj->list_price = 0;
  78. $obj->cost = 0;
  79. $obj->sell_price = 0;
  80. $obj->weight = 0;
  81. $obj->weight_units = variable_get('uc_weight_unit', 'lb');
  82. $obj->ordering = $node->ordering;
  83. $obj->shippable = false;
  84. $values = array();
  85. $placeholders = array();
  86. foreach ($node->products as $product) {
  87. $product = node_load($product);
  88. $values[] = $node->vid;
  89. $values[] = $node->nid;
  90. $values[] = $product->nid;
  91. $values[] = $node->mutable;
  92. $values[] = 1;
  93. $values[] = $product->sell_price;
  94. $placeholders[] = '(%d, %d, %d, %d, %d, %f)';
  95. $obj->model .= $product->model . ' / ';
  96. $obj->list_price += $product->list_price;
  97. $obj->cost += $product->cost;
  98. $obj->sell_price += $product->sell_price;
  99. $obj->weight += $product->weight * uc_weight_conversion($product->weight_units, $obj->weight_units);
  100. if ($product->shippable) {
  101. $obj->shippable = true;
  102. }
  103. }
  104. db_query("INSERT INTO {uc_product_kits} (vid, nid, product_id, mutable, qty, discount) VALUES ". implode(',', $placeholders), $values);
  105. $obj->model = rtrim($obj->model, ' / ');
  106. db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %d, '%s', %d, %d)",
  107. $obj->vid, $obj->nid, $obj->model, $obj->list_price, $obj->cost, $obj->sell_price, $obj->weight, $obj->weight_units, $obj->default_qty,
  108. md5($obj->vid . $obj->nid . $obj->model . $obj->list_price . $obj->cost . $obj->sell_price . $obj->weight . $obj->weight_units . $obj->default_qty . $obj->ordering . $obj->shippable . time()),
  109. $obj->ordering, $obj->shippable
  110. );
  111. }
  112. /**
  113. * Implementation of hook_update().
  114. *
  115. * Updates information in {uc_products} as well as {uc_product_kits}. Because
  116. * component products are known when the form is loaded, discount information
  117. * can be input and saved.
  118. * @param &$node The node to be updated.
  119. * @see uc_product_update
  120. */
  121. function uc_product_kit_update(&$node) {
  122. $obj = new stdClass();
  123. $obj->vid = $node->vid;
  124. $obj->nid = $node->nid;
  125. $obj->model = '';
  126. $obj->list_price = 0;
  127. $obj->cost = 0;
  128. $obj->sell_price = 0;
  129. $obj->weight = 0;
  130. $obj->weight_units = variable_get('uc_weight_unit', 'lb');
  131. $obj->default_qty = $node->default_qty;
  132. $obj->ordering = $node->ordering;
  133. $obj->shippable = false;
  134. $values = array();
  135. $placeholders = array();
  136. foreach ($node->products as $nid) {
  137. $values[] = $node->vid;
  138. $values[] = $node->nid;
  139. $values[] = $nid;
  140. $values[] = $node->mutable;
  141. $product = node_load($nid);
  142. if (is_null($node->items[$nid]['qty']) || $node->items[$nid]['qty'] === '') {
  143. $node->items[$nid]['qty'] = 1;
  144. }
  145. $values[] = $node->items[$nid]['qty'];
  146. if (is_null($node->items[$nid]['discount']) || $node->items[$nid]['discount'] === '') {
  147. $node->items[$nid]['discount'] = $product->sell_price;
  148. }
  149. $values[] = $node->items[$nid]['discount'];
  150. $values[] = $node->items[$nid]['ordering'];
  151. $placeholders[] = '(%d, %d, %d, %d, %d, %f, %d)';
  152. $product->qty = $node->items[$nid]['qty'];
  153. if ($node->items[$nid]['discount'] < 0) {
  154. $product->sell_price += $node->items[$nid]['discount'];
  155. }
  156. else {
  157. $product->sell_price = $node->items[$nid]['discount'];
  158. }
  159. $obj->model .= $product->model . ' / ';
  160. $obj->list_price += $product->list_price * $product->qty;
  161. $obj->cost += $product->cost * $product->qty;
  162. $obj->sell_price += $product->sell_price * $product->qty;
  163. $obj->weight += $product->weight * $product->qty * uc_weight_conversion($product->weight_units, $obj->weight_units);
  164. if ($product->shippable) {
  165. $obj->shippable = true;
  166. }
  167. }
  168. $obj->model = rtrim($obj->model, ' / ');
  169. if (!$node->revision) {
  170. db_query("DELETE FROM {uc_product_kits} WHERE nid = %d", $node->nid);
  171. }
  172. db_query("INSERT INTO {uc_product_kits} (vid, nid, product_id, mutable, qty, discount, ordering) VALUES ". implode(',', $placeholders), $values);
  173. if ($node->revision) {
  174. db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %d, '%s', %d, %d)",
  175. $obj->vid, $obj->nid, $obj->model, $obj->list_price, $obj->cost, $obj->sell_price, $obj->weight, $obj->weight_units, $obj->default_qty,
  176. md5($obj->vid . $obj->nid . $obj->model . $obj->list_price . $obj->cost . $obj->sell_price . $obj->weight . $obj->weight_units . $obj->default_qty . $obj->ordering . time()),
  177. $obj->ordering, $obj->shippable
  178. );
  179. }
  180. else {
  181. db_query("UPDATE {uc_products} SET model = '%s', list_price = %f, cost = %f, sell_price = %f, weight = %f, weight_units = '%s', default_qty = %d, ordering = %d, shippable = %d WHERE vid = %d",
  182. $obj->model, $obj->list_price, $obj->cost, $obj->sell_price, $obj->weight, $obj->weight_units, $obj->default_qty, $obj->ordering, $obj->shippable, $obj->vid);
  183. if (!db_affected_rows()) {
  184. db_query("INSERT INTO {uc_products} (vid, nid, model, list_price, cost, sell_price, weight, weight_units, default_qty, unique_hash, ordering, shippable) VALUES (%d, %d, '%s', %f, %f, %f, %f, '%s', %d, '%s', %d, %d)",
  185. $obj->vid, $obj->nid, $obj->model, $obj->list_price, $obj->cost, $obj->sell_price, $obj->weight, $obj->weight_units, $obj->default_qty,
  186. md5($obj->vid . $obj->nid . $obj->model . $obj->list_price . $obj->cost . $obj->sell_price . $obj->weight . $obj->weight_units . $obj->default_qty . $obj->ordering . $obj->shippable . time()),
  187. $obj->ordering, $boj->shippable
  188. );
  189. }
  190. }
  191. }
  192. /**
  193. * Implementation of hook_delete().
  194. */
  195. function uc_product_kit_delete(&$node) {
  196. if (module_exists('uc_cart')) {
  197. db_query("DELETE FROM {uc_cart_products} WHERE data LIKE '%%s:6:\"kit_id\";s:%d:\"%s\";%%'", strlen($node->nid), $node->nid);
  198. }
  199. db_query("DELETE FROM {uc_product_kits} WHERE nid = %d", $node->nid);
  200. db_query("DELETE FROM {uc_products} WHERE nid = %d", $node->nid);
  201. }
  202. /**
  203. * Implementation of hook_load().
  204. */
  205. function uc_product_kit_load(&$node) {
  206. $obj = new stdClass();
  207. $products = array();
  208. $result = db_query("SELECT product_id, mutable, qty, discount, ordering FROM {uc_product_kits} WHERE vid = %d ORDER BY ordering", $node->vid);
  209. while ($prod = db_fetch_object($result)) {
  210. $obj->mutable = $prod->mutable;
  211. $product = node_load($prod->product_id);
  212. $product->qty = $prod->qty;
  213. $product->discount = $prod->discount;
  214. $product->ordering = $prod->ordering;
  215. $products[$prod->product_id] = $product;
  216. }
  217. uasort($products, '_uc_product_kit_sort_products');
  218. $obj->products = $products;
  219. if ($extra = uc_product_load($node)) {
  220. foreach ($extra as $key => $value) {
  221. $obj->$key = $value;
  222. }
  223. }
  224. return $obj;
  225. }
  226. function uc_product_kit_nodeapi(&$node, $op, $arg3 = null, $arg4 = null) {
  227. switch ($op) {
  228. case 'update':
  229. $result = db_query("SELECT DISTINCT nid FROM {uc_product_kits} WHERE product_id = %d", $node->nid);
  230. while ($nid = db_fetch_object($result)) {
  231. $kit = node_load($nid, NULL, TRUE);
  232. $redirect = drupal_execute('product_kit_node_form', array('nid' => $kit->nid), $kit);
  233. }
  234. break;
  235. }
  236. }
  237. /**
  238. * Register an "Add to Cart" form for each product kit.
  239. * @see uc_product_kit_add_to_cart_form
  240. * @see uc_catalog_buy_it_now_form
  241. */
  242. function uc_product_kit_forms($saved_args) {
  243. $forms = array();
  244. if (substr($saved_args[0], 0, 31) == 'uc_product_kit_add_to_cart_form' || substr($saved_args[0], 0, 27) == 'uc_product_add_to_cart_form' || substr($saved_args[0], 0, 26) == 'uc_catalog_buy_it_now_form') {
  245. $product = $saved_args[1];
  246. if ($product->type == 'product_kit') {
  247. $forms['uc_product_kit_add_to_cart_form_'. $product->nid] = array('callback' => 'uc_product_kit_add_to_cart_form');
  248. $forms['uc_product_add_to_cart_form_'. $product->nid] = array('callback' => 'uc_product_kit_add_to_cart_form');
  249. $forms['uc_catalog_buy_it_now_form_'. $product->nid] = array('callback' => 'uc_product_kit_buy_it_now_form');
  250. }
  251. }
  252. return $forms;
  253. }
  254. /**
  255. * Add product kit settings to the product settings form.
  256. */
  257. function uc_product_kit_form_alter($form_id, &$form) {
  258. if ($form_id == 'uc_product_settings_form') {
  259. $form['uc_product_kit_mutable'] = array('#type' => 'radios',
  260. '#title' => t('How are product kits handled by the cart?'),
  261. '#options' => array(
  262. UC_PRODUCT_KIT_UNMUTABLE_NO_LIST => t('As a unit. Customers may only change how many kits they are buying. Do not list component products.'),
  263. UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST => t('As a unit. Customers may only change how many kits they are buying. List component products.'),
  264. UC_PRODUCT_KIT_MUTABLE => t('As individual products. Customers may add or remove kit components at will.'),
  265. ),
  266. '#default_value' => variable_get('uc_product_kit_mutable', 0),
  267. '#weight' => -5,
  268. );
  269. }
  270. }
  271. /**
  272. * Implementation of hook_form().
  273. *
  274. * @ingroup forms
  275. */
  276. function uc_product_kit_form(&$node) {
  277. $form = array();
  278. $form['title'] = array('#type' => 'textfield',
  279. '#title' => t('Name'),
  280. '#required' => TRUE,
  281. '#weight' => -5,
  282. '#default_value' => $node->title,
  283. '#description' => t('Name of the product kit')
  284. );
  285. $form['body_filter']['body'] = array('#type' => 'textarea',
  286. '#title' => t('Description'),
  287. '#required' => FALSE,
  288. '#default_value' => $node->body,
  289. '#rows' => 20,
  290. '#description' => t('Explain these whatchamacallits.'),
  291. );
  292. $form['body_filter']['format'] = filter_form($node->format);
  293. $form['body_filter']['#weight'] = -4;
  294. $product_types = module_invoke_all('product_types');
  295. $products = array();
  296. unset($product_types[array_search('product_kit', $product_types)]);
  297. $result = db_query("SELECT nid, title FROM {node} WHERE type IN ('". implode("','", $product_types) ."') ORDER BY title, nid");
  298. while ($product = db_fetch_object($result)) {
  299. $products[$product->nid] = $product->title;
  300. }
  301. $form['base'] = array('#type' => 'fieldset',
  302. '#title' => t('Product Information'),
  303. '#collapsible' => true,
  304. '#collapsed' => false,
  305. '#weight' => -1,
  306. '#attributes' => array('class' => 'product-field'),
  307. );
  308. $form['base']['mutable'] = array('#type' => 'radios',
  309. '#title' => t('How is this product kit handled by the cart?'),
  310. '#options' => array(
  311. UC_PRODUCT_KIT_UNMUTABLE_NO_LIST => t('As a unit. Customers may only change how many kits they are buying. Do not list component products.'),
  312. UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST => t('As a unit. Customers may only change how many kits they are buying. List component products.'),
  313. UC_PRODUCT_KIT_MUTABLE => t('As individual products. Customers may add or remove kit components at will.'),
  314. ),
  315. '#default_value' => isset($node->mutable) ? $node->mutable : variable_get('uc_product_kit_mutable', UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST),
  316. );
  317. $form['base']['products'] = array('#type' => 'select',
  318. '#multiple' => true,
  319. '#required' => true,
  320. '#title' => t('Products'),
  321. '#options' => $products,
  322. '#default_value' => isset($node->products) ? array_keys($node->products) : array(),
  323. );
  324. $form['base']['items'] = array('#tree' => true);
  325. if (isset($node->products)) {
  326. foreach ($node->products as $i => $product) {
  327. $form['base']['items'][$i] = array('#type' => 'fieldset',
  328. //'#title' => $product->title,
  329. );
  330. $form['base']['items'][$i]['link'] = array('#type' => 'item',
  331. '#value' => l($product->title, 'node/'. $i),
  332. );
  333. $form['base']['items'][$i]['qty'] = array('#type' => 'textfield',
  334. '#title' => t('Quantity'),
  335. '#default_value' => $product->qty,
  336. '#size' => 5,
  337. );
  338. $form['base']['items'][$i]['ordering'] = array('#type' => 'weight',
  339. '#title' => t('Ordering'),
  340. '#default_value' => isset($product->ordering) ? $product->ordering : 0,
  341. );
  342. $item = node_load($i);
  343. $form['base']['items'][$i]['discount'] = array('#type' => 'textfield',
  344. '#title' => t('Discount'),
  345. '#description' => t('Enter a negative value to lower the item price by that amount. Enter a postive value to set the item price to that amount. This discount is applied to each %product in the kit.', array('%product' => $product->title)),
  346. '#default_value' => (is_null($product->discount) || $product->discount === '' ? $item->sell_price : $product->discount),
  347. '#size' => 5,
  348. );
  349. }
  350. }
  351. $form['base']['default_qty'] = array('#type' => 'textfield',
  352. '#title' => t('Default quantity to add to cart'),
  353. '#default_value' => !is_null($node->default_qty) ? $node->default_qty : 1,
  354. '#description' => t('Leave blank or zero to disable the quantity field in the add to cart form.'),
  355. '#weight' => 27,
  356. '#size' => 5,
  357. '#maxlength' => 6,
  358. );
  359. $form['base']['ordering'] = array('#type' => 'weight',
  360. '#title' => t('List order'),
  361. '#delta' => 25,
  362. '#default_value' => $node->ordering,
  363. '#weight' => 30,
  364. );
  365. return $form;
  366. }
  367. /**
  368. * Implementation of hook_view().
  369. */
  370. function uc_product_kit_view($node, $teaser = 0, $page = 0) {
  371. $node = node_prepare($node, $teaser);
  372. $enabled = variable_get('uc_product_field_enabled', array(
  373. 'image' => 1,
  374. 'display_price' => 1,
  375. 'model' => 1,
  376. 'list_price' => 0,
  377. 'cost' => 0,
  378. 'sell_price' => 1,
  379. 'weight' => 0,
  380. 'dimensions' => 0,
  381. 'add_to_cart' => 1,
  382. ));
  383. $weight = variable_get('uc_product_field_weight', array(
  384. 'image' => -2,
  385. 'display_price' => -1,
  386. 'model' => 0,
  387. 'list_price' => 2,
  388. 'cost' => 3,
  389. 'sell_price' => 4,
  390. 'weight' => 5,
  391. 'dimensions' => 6,
  392. 'add_to_cart' => 10,
  393. ));
  394. if (isset($node->field_image_cache) && file_exists($node->field_image_cache[0]['filepath'])) {
  395. $node->content['image'] = array('#value' => theme('uc_product_image', $node->field_image_cache),
  396. '#access' => $enabled['image'] && module_exists('imagecache'),
  397. '#weight' => $weight['image'],
  398. );
  399. }
  400. $node->content['display_price'] = array('#value' => theme('uc_product_display_price', $node->sell_price),
  401. '#access' => $enabled['display_price'],
  402. '#weight' => $weight['display_price'],
  403. );
  404. if (!$teaser) {
  405. $node->content['model'] = array('#value' => theme('uc_product_model', $node->model),
  406. '#access' => $enabled['model'],
  407. '#weight' => $weight['model'],
  408. );
  409. $node->content['body']['#weight'] = 1;
  410. $node->content['list_price'] = array('#value' => theme('uc_product_price', $node->list_price, 'list_price'),
  411. '#access' => $enabled['list_price'],
  412. '#weight' => $weight['list_price'],
  413. );
  414. $node->content['cost'] = array('#value' => theme('uc_product_price', $node->cost, 'cost'),
  415. '#access' => $enabled['cost'] && user_access('administer products'),
  416. '#weight' => $weight['cost'],
  417. );
  418. }
  419. else {
  420. $node->content['#attributes'] = array('style' => 'display: inline');
  421. }
  422. $node->content['sell_price'] = array('#value' => theme('uc_product_sell_price', $node->sell_price, $teaser),
  423. '#access' => $enabled['sell_price'],
  424. '#weight' => $weight['sell_price'],
  425. );
  426. if (!$teaser) {
  427. $node->content['weight'] = array('#value' => theme('uc_product_weight', $node->weight, $node->weight_units),
  428. '#access' => $enabled['weight'],
  429. '#weight' => $weight['weight'],
  430. );
  431. if ($node->mutable != UC_PRODUCT_KIT_UNMUTABLE_NO_LIST) {
  432. $node->content['products'] = array('#weight' => 6);
  433. foreach ($node->products as $product) {
  434. if (node_access('view', $product)) {
  435. $title = l($product->title, 'node/'. $product->nid);
  436. }
  437. else {
  438. $title = check_plain($product->title);
  439. }
  440. $node->content['products'][$product->nid]['qty'] = array('#value' => '<div class="product-qty">'. $product->qty .' x '. $title .'</div>');
  441. $node->content['products'][$product->nid]['#weight'] = $product->ordering;
  442. }
  443. }
  444. if (module_exists('uc_cart')) {
  445. $node->content['add_to_cart'] = array('#value' => theme('uc_product_kit_add_to_cart', $node),
  446. '#weight' => 10,
  447. );
  448. }
  449. }
  450. else if (module_exists('uc_cart') && variable_get('uc_product_add_to_cart_teaser', true)) {
  451. $node->content['add_to_cart'] = array('#value' => theme('uc_product_kit_add_to_cart', $node),
  452. '#weight' => 10,
  453. );
  454. }
  455. return $node;
  456. }
  457. /**
  458. * Wrap the "Add to Cart" form in a <div>.
  459. *
  460. * @ingroup themeable
  461. */
  462. function theme_uc_product_kit_add_to_cart($node) {
  463. $output = '<div class="add_to_cart" title="'. t('Click to add to cart.') .'">';
  464. if ($node->nid) {
  465. $output .= drupal_get_form('uc_product_kit_add_to_cart_form_'. $node->nid, $node);
  466. }
  467. else {
  468. $output .= drupal_get_form('uc_product_kit_add_to_cart_form', $node);
  469. }
  470. $output .= '</div>';
  471. return $output;
  472. }
  473. /**
  474. * Lets the cart know how many of which products are included in a kit.
  475. *
  476. * uc_attribute_form_alter() hooks into this form to add attributes to each
  477. * element in $form['products'].
  478. * @ingroup forms
  479. * @see uc_product_kit_add_to_cart_form_submit
  480. */
  481. function uc_product_kit_add_to_cart_form($node) {
  482. $form = array();
  483. $form['#base'] = 'uc_product_kit_add_to_cart_form';
  484. $form['nid'] = array('#type' => 'value', '#value' => $node->nid, );
  485. $form['products'] = array('#tree' => true);
  486. foreach ($node->products as $i => $product) {
  487. $form['products'][$i] = array(/* '#type' => 'fieldset', */'#title' => $product->title);
  488. $form['products'][$i]['nid'] = array('#type' => 'hidden', '#value' => $product->nid);
  489. $form['products'][$i]['qty'] = array('#type' => 'hidden', '#value' => $product->qty);
  490. }
  491. if ($node->default_qty > 0 && variable_get('uc_product_add_to_cart_qty', false)) {
  492. $form['qty'] = array('#type' => 'textfield',
  493. '#title' => t('Quantity'),
  494. '#default_value' => $node->default_qty,
  495. '#size' => 5,
  496. '#maxlength' => 6,
  497. );
  498. }
  499. else {
  500. $form['qty'] = array('#type' => 'hidden', '#value' => 1);
  501. }
  502. $form['submit'] = array(
  503. '#type' => 'submit',
  504. '#value' => variable_get('uc_product_add_to_cart_text', t('Add to cart')),
  505. '#id' => 'edit-submit-'. $node->nid,
  506. '#attributes' => array(
  507. 'class' => 'node-add-to-cart',
  508. ),
  509. );
  510. return $form;
  511. }
  512. /**
  513. * Submit function for uc_product_kit_add_to_cart_form().
  514. *
  515. * Iterates through the kit's product list and adds each to the cart in the
  516. * correct quantities.
  517. */
  518. function uc_product_kit_add_to_cart_form_submit($form_id, $form_values) {
  519. $node = node_load($form_values['nid']);
  520. drupal_set_message(t('<strong>@product-title</strong> added to <a href="!url">your shopping cart</a>.', array('@product-title' => $node->title, '!url' => url('cart'))));
  521. return uc_cart_add_item($form_values['nid'], $form_values['qty'], $form_values);
  522. }
  523. function uc_product_kit_buy_it_now_form($node) {
  524. $form = array();
  525. $form['#base'] = 'uc_product_kit_buy_it_now_form';
  526. $form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
  527. if ($node->type == 'product_kit') {
  528. $form['products'] = array('#tree' => true);
  529. foreach ($node->products as $i => $product) {
  530. $form['products'][$i] = array(/* '#type' => 'fieldset', */'#title' => $product->title);
  531. $form['products'][$i]['nid'] = array('#type' => 'hidden', '#value' => $product->nid);
  532. $form['products'][$i]['qty'] = array('#type' => 'hidden', '#value' => $product->qty);
  533. }
  534. }
  535. $form['submit'] = array(
  536. '#type' => 'submit',
  537. '#value' => variable_get('uc_teaser_add_to_cart_text', t('Add to cart')),
  538. '#id' => 'edit-submit-'. $node->nid,
  539. '#attributes' => array(
  540. 'class' => 'list-add-to-cart',
  541. ),
  542. );
  543. return $form;
  544. }
  545. function uc_product_kit_buy_it_now_form_submit($form_id, $form_values) {
  546. $node = node_load($form_values['nid']);
  547. if (module_exists('uc_attribute')) {
  548. $attributes = uc_product_get_attributes($node->nid);
  549. if (!empty($attributes)) {
  550. drupal_set_message(t('This product has options that need to be selected before purchase. Please select them in the form below.'), 'error');
  551. return drupal_get_path_alias('node/'. $form_values['nid']);
  552. }
  553. if (is_array($node->products)) {
  554. foreach ($node->products as $nid => $product) {
  555. $attributes = uc_product_get_attributes($nid);
  556. if (!empty($attributes)) {
  557. drupal_set_message(t('This product has options that need to be selected before purchase. Please select them in the form below.'), 'error');
  558. return drupal_get_path_alias('node/'. $form_values['nid']);
  559. }
  560. }
  561. }
  562. }
  563. return uc_cart_add_item($form_values['nid'], 1, $form_values);
  564. }
  565. /******************************************************************************
  566. * Übercart Hooks *
  567. ******************************************************************************/
  568. function uc_product_kit_product_types() {
  569. return array('product_kit');
  570. }
  571. function uc_product_kit_store_status() {
  572. if (module_exists('imagefield')) {
  573. $result = db_query("SELECT field_name FROM {node_field_instance} WHERE field_name = 'field_image_cache' AND widget_type = 'image' AND type_name = 'product_kit'");
  574. if (!db_num_rows($result)) {
  575. return array(array('status' => 'ok', 'title' => t('Images'), 'desc' => t('Product kits do not have an image field. You may add the existing <em>field_image_cache</em> at the <a href="!url">Add field page</a>.', array('!url' => url('admin/content/types/product-kit/add_field')))));
  576. }
  577. }
  578. }
  579. function uc_product_kit_add_to_cart($nid, $qty, $kit_data) {
  580. $node = node_load($nid);
  581. if ($node->type == 'product_kit') {
  582. $unique = uniqid('', true);
  583. foreach ($node->products as $product) {
  584. $data = array('kit_id' => $node->nid, 'unique_id' => $unique, 'module' => 'uc_product_kit');
  585. uc_cart_add_item($product->nid, $product->qty * $qty, $data + module_invoke_all('add_to_cart_data', $kit_data['products'][$product->nid]), null, false, false);
  586. }
  587. if ($check_redirect) {
  588. if (isset($_GET['destination'])) {
  589. drupal_goto();
  590. }
  591. $redirect = variable_get('uc_add_item_redirect', 'cart');
  592. if ($redirect != '<none>') {
  593. $_SESSION['last_url'] = uc_referer_uri();
  594. return $redirect;
  595. }
  596. else {
  597. return uc_referer_uri();
  598. }
  599. }
  600. return array(array('success' => false, 'silent' => true, 'message' => ''));
  601. }
  602. }
  603. /**
  604. * Implementation of Übercart's hook_cart_item().
  605. */
  606. function uc_product_kit_cart_item($op, &$item) {
  607. switch ($op) {
  608. case 'load':
  609. if (isset($item->data['kit_id']) && ($kit = node_load($item->data['kit_id'])) && $kit->mutable != UC_PRODUCT_KIT_MUTABLE) {
  610. $kit_discount = $kit->products[$item->nid]->discount;
  611. if ($kit_discount !== '') {
  612. if ($kit_discount < 0) {
  613. $item->price += $kit_discount;
  614. }
  615. else {
  616. $item->price += $kit_discount - $kit->products[$item->nid]->sell_price;
  617. }
  618. }
  619. }
  620. break;
  621. }
  622. }
  623. /**
  624. * Implementation of Ubercart's hook_cart_display().
  625. *
  626. * Displays either the kit as a whole, or each individual product based on the
  627. * store configuration. Each product in the cart that was added by uc_product_kit
  628. * was also given a unique kit id in order to help prevent collisions. The side
  629. * effect is that identical product kits are listed separately if added separately.
  630. * The customer may still change the quantity of kits like other products.
  631. *
  632. * @param $item
  633. * An item in the shopping cart.
  634. * @return
  635. * A form element array to be processed by uc_cart_view_form().
  636. */
  637. function uc_product_kit_cart_display($item) {
  638. static $elements = array();
  639. static $products;
  640. $unique_id = $item->data['unique_id'];
  641. $kit = node_load($item->data['kit_id']);
  642. //print '<pre>'. print_r($kit, true) .'</pre>';
  643. if ($kit->mutable == UC_PRODUCT_KIT_MUTABLE) {
  644. return uc_product_cart_display($item);
  645. }
  646. else {
  647. if (!isset($products[$unique_id])) {
  648. // Initialize table row
  649. $element = array();
  650. $element['nid'] = array('#type' => 'value', '#value' => $kit->nid);
  651. $element['module'] = array('#type' => 'value', '#value' => 'uc_product_kit');
  652. $element['remove'] = array('#type' => 'checkbox');
  653. if ($kit->mutable == UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST) {
  654. $element['options'] = array('#value' => '<div class="item-list"><ul class="cart-options">'."\n");
  655. }
  656. $element['title'] = array('#value' => l($kit->title, 'node/'. $kit->nid));
  657. $element['#total'] = 0;
  658. $element['qty'] = array(
  659. '#type' => 'textfield',
  660. '#default_value' => $item->qty / $kit->products[$item->nid]->qty,
  661. '#size' => 5,
  662. '#maxlength' => 6,
  663. );
  664. $elements[$unique_id] = $element;
  665. }
  666. // Add product specific information
  667. $op_names = '';
  668. foreach ($item->options as $option) {
  669. $op_names .= $option['name'] .', ';
  670. }
  671. $op_names = substr($op_names, 0, strlen($op_names) - 2);
  672. if ($op_names) {
  673. $op_names = '-- '. $op_names;
  674. }
  675. if (node_access('view', node_load($item->nid))) {
  676. $title = l($item->title, 'node/'. $item->nid);
  677. }
  678. else {
  679. $title = $item->title;
  680. }
  681. if ($kit->mutable == UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST) {
  682. $elements[$unique_id]['options']['#value'] .= '<li>'. $item->qty .' x '. $title ." <em>$op_names</em></li>\n";
  683. }
  684. $elements[$unique_id]['#total'] += $item->price * $item->qty;
  685. $elements[$unique_id]['data'][$item->nid] = $item;
  686. $products[$unique_id][] = $item->nid;
  687. // Check if all products in this kit have been accounted for.
  688. $done = true;
  689. foreach ($kit->products as $product) {
  690. if (!in_array($product->nid, $products[$unique_id])) {
  691. $done = false;
  692. break;
  693. }
  694. }
  695. if ($done) {
  696. $elements[$unique_id]['data'] = array('#type' => 'value', '#value' => serialize($elements[$unique_id]['data']));
  697. $elements[$unique_id]['options']['#value'] .= "</ul></div>\n";
  698. $element = $elements[$unique_id];
  699. unset($products[$unique_id]);
  700. unset($elements[$unique_id]);
  701. return $element;
  702. }
  703. }
  704. return array();
  705. }
  706. /**
  707. * Implementation of Übercart's hook_update_cart_item().
  708. *
  709. * Handles individual products or entire kits.
  710. */
  711. function uc_product_kit_update_cart_item($nid, $data = array(), $qty, $cid = null) {
  712. if (!$nid) return NULL;
  713. $cid = !(is_null($cid) || empty($cid)) ? $cid : uc_cart_get_id();
  714. if ($qty < 1) {
  715. foreach ($data as $p_nid => $product) {
  716. uc_cart_remove_item($p_nid, $cid, $product->data);
  717. }
  718. }
  719. else {
  720. if (isset($data['kit_id'])) { // Product was listed individually
  721. uc_product_update_cart_item($nid, $data, $qty, $cid);
  722. }
  723. else {
  724. $kit = node_load($nid);
  725. foreach ($data as $p_nid => $product) {
  726. uc_product_update_cart_item($p_nid, $product->data, $qty * $kit->products[$p_nid]->qty, $cid);
  727. }
  728. }
  729. }
  730. }
  731. /**
  732. * usort() callback.
  733. */
  734. function _uc_product_kit_sort_products($a, $b) {
  735. if ($a->ordering == $b->ordering) {
  736. return 0;
  737. }
  738. else {
  739. return ($a->ordering < $b->ordering) ? -1 : 1;
  740. }
  741. }