/[drupal]/contributions/modules/ubercart/uc_attribute/uc_attribute.module
ViewVC logotype

Contents of /contributions/modules/ubercart/uc_attribute/uc_attribute.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.12 - (show annotations) (download) (as text)
Thu Jul 10 12:41:00 2008 UTC (16 months, 2 weeks ago) by islandusurper
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-6--2
Changes since 1.11: +992 -697 lines
File MIME type: text/x-php
Begin the Ubercart 6.x-2.x branch.
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Übercart Attribute module.
7 *
8 * Allows customers to buy slightly different products from the same listing.
9 *
10 * Many manufacturers provide options to their products. This module provides a way
11 * for store administrators to consolidate these options into one product instead of
12 * listing each combination separately. This is accomplished through the use of Drupal's
13 * hook system.
14 * Coded by: Lyle Mantooth
15 */
16
17 /******************************************************************************
18 * Drupal Hooks *
19 ******************************************************************************/
20
21 /**
22 * Implementation of hook_help().
23 */
24 function uc_attribute_help($path, $arg) {
25 switch ($path) {
26 // Help messages for the attributes overview on products and classes.
27 case 'node/%/edit/attributes':
28 return t('Add attributes to this product using the <a href="!url">add attributes form</a>. You may then adjust the settings for these attributes on this page and go on to configure their options in the <em>Options</em> tab.', array('!url' => url('node/'. $arg[1] .'/edit/attributes/add')));
29 case 'admin/store/products/classes/'. $arg[4] .'/attributes':
30 return t('Add attributes to the product class using the <a href="!url">add attributes form</a>. You may then adjust the settings for these attributes on this page and go on to configure their options in the <em>Options</em> tab.', array('!url' => url('admin/store/products/classes/'. $arg[4] .'/attributes/add')));
31
32 // Help message for adding an attribute to a product or class.
33 case 'node/%/edit/attributes/add':
34 case 'admin/store/products/classes/'. $arg[4] .'/attributes/add':
35 return t('Select the attributes you want to add and submit the form.');
36
37 // Help message for adjusting the options on a product or class.
38 case 'node/%/edit/options':
39 case 'admin/store/products/classes/'. $arg[4] .'/options':
40 return t('Use the checkboxes to enable options for attributes and the radio buttons to specify defaults for the enabled options. Use the other fields to override the default settings for each option. Attributes with no enabled options will be displayed as text fields.');
41
42 // Help message for the product Adjustments tab.
43 case 'node/%/edit/adjustments':
44 return t('Enter an alternate SKU to be used when the specified set of options are chosen and the product is added to the cart. <b>Warning:</b> Adding or removing attributes from this product will reset all the SKUs on this page to the default product SKU.');
45 }
46 }
47
48 /**
49 * Implementation of hook_menu().
50 */
51 function uc_attribute_menu() {
52 $items['admin/store/attributes'] = array(
53 'title' => 'Attributes',
54 'description' => 'Create and edit attributes and options.',
55 'page callback' => 'uc_attribute_admin',
56 'access arguments' => array('administer products'),
57 'type' => MENU_NORMAL_ITEM,
58 'weight' => -1,
59 );
60 $items['admin/store/attributes/overview'] = array(
61 'title' => 'Overview',
62 'type' => MENU_DEFAULT_LOCAL_TASK,
63 'weight' => 0,
64 );
65 $items['admin/store/attributes/add'] = array(
66 'title' => 'Add an attribute',
67 'page callback' => 'drupal_get_form',
68 'page arguments' => array('uc_attribute_form'),
69 'access arguments' => array('administer products'),
70 'type' => MENU_LOCAL_TASK,
71 'weight' => 5,
72 );
73 $items['admin/store/settings/attributes'] = array(
74 'title' => 'Attribute settings',
75 'description' => 'Configure the attribute settings',
76 'page callback' => 'drupal_get_form',
77 'page arguments' => array('uc_attribute_admin_settings'),
78 'access arguments' => array('administer products'),
79 'type' => MENU_NORMAL_ITEM,
80 );
81 $items['admin/store/attributes/%uc_attribute/edit'] = array(
82 'title' => 'Edit attribute',
83 'page callback' => 'drupal_get_form',
84 'page arguments' => array('uc_attribute_form', 3),
85 'access arguments' => array('administer products'),
86 'type' => MENU_CALLBACK,
87 );
88 $items['admin/store/attributes/%uc_attribute/delete'] = array(
89 'page callback' => 'drupal_get_form',
90 'page arguments' => array('uc_attribute_delete_confirm', 3),
91 'access arguments' => array('administer products'),
92 'type' => MENU_CALLBACK,
93 );
94 $items['admin/store/attributes/%uc_attribute/options'] = array(
95 'title' => 'Options',
96 'page callback' => 'uc_attribute_options',
97 'page arguments' => array(3),
98 'access arguments' => array('administer products'),
99 'type' => MENU_CALLBACK,
100 );
101 $items['admin/store/attributes/%uc_attribute/options/overview'] = array(
102 'title' => 'Overview',
103 'type' => MENU_DEFAULT_LOCAL_TASK,
104 'weight' => 0,
105 );
106 $items['admin/store/attributes/%uc_attribute/options/add'] = array(
107 'title' => 'Add an option',
108 'page callback' => 'drupal_get_form',
109 'page arguments' => array('uc_attribute_option_form', 3, NULL),
110 'access arguments' => array('administer products'),
111 'type' => MENU_LOCAL_TASK,
112 'weight' => 5,
113 );
114 $items['admin/store/attributes/%uc_attribute/options/%uc_attribute_option/edit'] = array(
115 'title' => 'Edit option',
116 'page callback' => 'drupal_get_form',
117 'page arguments' => array('uc_attribute_option_form', 3, 5),
118 'access arguments' => array('administer products'),
119 'type' => MENU_CALLBACK,
120 );
121 $items['admin/store/attributes/%uc_attribute/options/%uc_attribute_option/delete'] = array(
122 'title' => 'Delete option',
123 'page callback' => 'drupal_get_form',
124 'page arguments' => array('uc_attribute_option_delete_confirm', 3, 5),
125 'access arguments' => array('administer products'),
126 'type' => MENU_CALLBACK,
127 );
128
129 // Menu items for default product class attributes and options.
130 $items['admin/store/products/classes/%uc_product_class/attributes'] = array(
131 'title' => 'Attributes',
132 'page callback' => 'drupal_get_form',
133 'page arguments' => array('uc_object_attributes_form', 4, 'class'),
134 'access arguments' => array('administer product classes'),
135 'type' => MENU_LOCAL_TASK,
136 'weight' => 1,
137 );
138 $items['admin/store/products/classes/%uc_product_class/options'] = array(
139 'title' => 'Options',
140 'page callback' => 'drupal_get_form',
141 'page arguments' => array('uc_object_options_form', 4, 'class'),
142 'access arguments' => array('administer product classes'),
143 'type' => MENU_LOCAL_TASK,
144 'weight' => 2,
145 );
146
147 // Insert subitems into the edit node page for product types.
148 $items['node/%node/edit/attributes'] = array(
149 'title' => 'Attributes',
150 'page callback' => 'drupal_get_form',
151 'page arguments' => array('uc_object_attributes_form', 1, 'product', 'overview'),
152 'access callback' => 'uc_product_is_product',
153 'access arguments' => array(1),
154 'type' => MENU_LOCAL_TASK,
155 'weight' => 1,
156 );
157 $items['node/%node/edit/attributes/add'] = array(
158 'title' => 'Attributes',
159 'page callback' => 'drupal_get_form',
160 'page arguments' => array('uc_object_attributes_form', 1, 'product', 'add'),
161 'access callback' => 'uc_product_is_product',
162 'access arguments' => array(1),
163 'type' => MENU_LOCAL_TASK,
164 'weight' => 1,
165 );
166 $items['node/%node/edit/options'] = array(
167 'title' => 'Options',
168 'page callback' => 'drupal_get_form',
169 'page arguments' => array('uc_object_options_form', 1, 'product'),
170 'access callback' => 'uc_product_is_product',
171 'access arguments' => array(1),
172 'type' => MENU_LOCAL_TASK,
173 'weight' => 2,
174 );
175 $items['node/%node/edit/adjustments'] = array(
176 'title' => 'Adjustments',
177 'page callback' => 'drupal_get_form',
178 'page arguments' => array('uc_product_adjustments_form', 1),
179 'access callback' => 'uc_product_is_product',
180 'access arguments' => array(1),
181 'type' => MENU_LOCAL_TASK,
182 'weight' => 3,
183 );
184
185 return $items;
186 }
187
188 function uc_attribute_init() {
189 drupal_add_css(drupal_get_path('module', 'uc_attribute') .'/uc_attribute.css');
190 }
191
192 function uc_attribute_theme() {
193 return array(
194 'uc_object_attributes_form' => array(
195 'arguments' => array('form' => NULL),
196 ),
197 'uc_object_options_form' => array(
198 'arguments' => array('form' => NULL),
199 ),
200 'uc_attribute' => array(
201 'arguments' => array('type' => NULL, 'data' => NULL, 'nid' => NULL),
202 ),
203 'uc_attribute_options' => array(
204 'arguments' => array('aid' => NULL, 'oid' => NULL),
205 ),
206 );
207 }
208
209 /**
210 * Implementation of hook_form_alter().
211 *
212 * Attach option selectors to the form with the "Add to Cart" button.
213 */
214 function uc_attribute_form_alter(&$form, &$form_state, $form_id) {
215 if (strpos($form_id, 'add_to_cart_form') || strpos($form_id, 'add_product_form')) {
216 // If the node has a product list, add attributes to them
217 if (count(element_children($form['products']))) {
218 foreach (element_children($form['products']) as $key) {
219 $form['products'][$key] = _uc_attribute_alter_form($form['products'][$key]);
220 if (isset($form['products'][$key]['attributes'])) {
221 $form['products'][$key]['attributes']['#tree'] = true;
222 $form['products'][$key]['#type'] = 'fieldset';
223 }
224 }
225 }
226 // If not, add attributes to the node.
227 else {
228 $form['attributes'] = array('#tree' => true, '#weight' => -1);
229 $form = _uc_attribute_alter_form($form);
230 }
231 }
232 }
233
234 /**
235 * Implementation of hook_nodeapi().
236 */
237 function uc_attribute_nodeapi(&$node, $op, $arg3 = null, $arg4 = null) {
238 if (in_array($node->type, module_invoke_all('product_types'))) {
239 switch ($op) {
240 case 'insert':
241 switch ($GLOBALS['db_type']) {
242 case 'mysqli':
243 case 'mysql':
244 db_query("INSERT IGNORE INTO {uc_product_attributes} (nid, aid, ordering, required, display, default_option) SELECT %d, aid, ordering, required, display, default_option FROM {uc_class_attributes} WHERE pcid = '%s'", $node->nid, $node->type);
245 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);
246 break;
247 case 'pgsql':
248 db_query("INSERT INTO {uc_product_attributes} (nid, aid, ordering, required, display, default_option) SELECT %d, aid, ordering, required, display, default_option FROM {uc_class_attributes} WHERE pcid = '%s'", $node->nid, $node->type);
249 db_query("INSERT 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);
250 break;
251 }
252
253 break;
254 case 'delete':
255 db_query("DELETE FROM {uc_product_options} WHERE nid = %d", $node->nid);
256 db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
257 db_query("DELETE FROM {uc_product_attributes} WHERE nid = %d", $node->nid);
258 break;
259 case 'update index':
260 $output = '';
261 $attributes = uc_product_get_attributes($node->nid);
262 foreach ($attributes as $attribute) {
263 $output .= '<h3>'. $attribute->name .'</h3>';
264 foreach ($attribute->options as $option) {
265 $output .= $option->name .' ';
266 }
267 $output .= "\n";
268 }
269 $result = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
270 while ($adjustment = db_fetch_object($result)) {
271 $output .= '<h2>'. $adjustment->model ."<h2>\n";
272 }
273 return $output;
274 }
275 }
276 }
277
278 /******************************************************************************
279 * Ubercart Hooks *
280 ******************************************************************************/
281
282 /**
283 * Stores the customer's choices in the cart.
284 */
285 function uc_attribute_add_to_cart_data($form_state) {
286 $combination = array();
287 if (!isset($form_state['values']['attributes'])) {
288 return array('attributes' => array(), 'model' => null);
289 }
290 foreach ($form_state['values']['attributes'] as $aid => $value) {
291 if (is_numeric($value)) {
292 $combination[$aid] = $value;
293 }
294 }
295 $result = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = %d AND combination LIKE '%s'", $form_state['values']['nid'], serialize($combination));
296 $model = db_result($result);
297 // drupal_set_message('Attributes to add to order Array(aid => oid):<pre>'. print_r($form_state['values']['attributes'], true) . $model .'</pre>');
298 // Preserve the 'attributes' key to allow other modules to add to the data field.
299 return array('attributes' => $form_state['values']['attributes'], 'model' => $model);
300 }
301
302 /**
303 * Implementation of hook_product_class().
304 */
305 function uc_attribute_product_class($type, $op) {
306 switch ($op) {
307 case 'delete':
308 db_query("DELETE FROM {uc_class_attributes} WHERE pcid = '%s'", $type);
309 db_query("DELETE FROM {uc_class_attribute_options} WHERE pcid = '%s'", $type);
310 break;
311 }
312 }
313
314 /**
315 * Implementation of hook_cart_item().
316 */
317 function uc_attribute_cart_item($op, &$item) {
318 switch ($op) {
319 case 'load':
320 $item->options = _uc_cart_product_get_options($item);
321 $op_costs = 0;
322 $op_prices = 0;
323 $op_weight = 0;
324 foreach ($item->options as $option) {
325 $op_costs += $option['cost'];
326 $op_prices += $option['price'];
327 $op_weight += $option['weight'];
328 }
329 $item->cost += $op_costs;
330 $item->price += $op_prices;
331 $item->weight += $op_weight;
332 if (!empty($item->data['model'])) {
333 $item->model = $item->data['model'];
334 }
335 break;
336 }
337 }
338
339 /******************************************************************************
340 * Menu Callbacks *
341 ******************************************************************************/
342
343 /**
344 * Change the display of attribute option prices.
345 *
346 * @ingroup forms
347 */
348 function uc_attribute_admin_settings() {
349 $form = array();
350
351 $form['uc_attribute_option_price_format'] = array('#type' => 'radios',
352 '#title' => t('Option price format'),
353 '#default_value' => variable_get('uc_attribute_option_price_format', 'adjustment'),
354 '#options' => array('none' => t('Do not display'),
355 'adjustment' => t('Display price adjustment'),
356 'total' => t('Display total price'),
357 ),
358 '#description' => t('Formats the price 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 price affecting attribute.'),
359 );
360
361 return system_settings_form($form);
362 }
363
364 // Displays a paged list and overview of existing product attributes.
365 function uc_attribute_admin() {
366 $header = array(
367 array('data' => t('Name'), 'field' => 'a.name', 'sort' => 'asc'),
368 t('Required'),
369 array('data' => t('Order'), 'field' => 'a.ordering'),
370 t('Number of options'),
371 t('Display type'),
372 t('Operations'),
373 );
374
375 $display_types = _uc_attribute_display_types();
376
377 $result = pager_query("SELECT a.*, 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, a.name, a.ordering". tablesort_sql($header), 30, 0, "SELECT COUNT(aid) FROM {uc_attributes}");
378 while ($attr = db_fetch_object($result)) {
379 $ops = array(
380 l(t('edit'), 'admin/store/attributes/'. $attr->aid .'/edit'),
381 l(t('options'), 'admin/store/attributes/'. $attr->aid .'/options'),
382 l(t('delete'), 'admin/store/attributes/'. $attr->aid .'/delete'),
383 );
384 $rows[] = array(
385 $attr->name,
386 $attr->required == 1 ? t('Yes') : t('No'),
387 array('data' => $attr->ordering, 'align' => 'center'),
388 array('data' => $attr->options, 'align' => 'center'),
389 $display_types[$attr->display],
390 implode(' ', $ops),
391 );
392 }
393
394 if (count($rows) == 0) {
395 $rows[] = array(
396 array('data' => t('No product attributes have been added yet.'), 'colspan' => '6')
397 );
398 }
399
400 $output = theme('table', $header, $rows) . theme('pager', NULL, 30)
401 . l(t('Add an attribute'), 'admin/store/attributes/add');
402
403 return $output;
404 }
405
406 /**
407 * Form builder for product attributes.
408 *
409 * @ingroup forms
410 * @see uc_attribute_form_validate
411 * @see uc_attribute_form_submit
412 */
413 function uc_attribute_form($form_state, $attribute = NULL) {
414 // If an attribute specified, add its ID as a hidden value.
415
416 if (!empty($attribute)) {
417 $form['aid'] = array('#type' => 'hidden', '#value' => $attribute->aid);
418 drupal_set_title(t('Edit attribute: %name', array('%name' => $attribute->name)));
419 }
420
421 $form['name'] = array(
422 '#type' => 'textfield',
423 '#title' => t('Name'),
424 '#description' => t('This name will appear to customers on product add to cart forms.'),
425 '#default_value' => $attribute->name,
426 '#required' => TRUE,
427 );
428 $form['description'] = array(
429 '#type' => 'textfield',
430 '#title' => t('Help text'),
431 '#description' => t('<b>Optional.</b> Enter the help text that will display beneath the attribute on product add to cart forms.'),
432 '#default_value' => $attribute->description,
433 '#maxlength' => 255,
434 );
435 $form['required'] = array(
436 '#type' => 'checkbox',
437 '#title' => t('Make this attribute required, forcing the customer to choose an option.'),
438 '#description' => t('Selecting this for an attribute will disregard any default option you specify.<br />May be overridden at the product level.'),
439 '#default_value' => $attribute->required,
440 );
441 $form['display'] = array(
442 '#type' => 'select',
443 '#title' => t('Display type'),
444 '#description' => t('This specifies how the options for this attribute will be presented.<br />May be overridden at the product level.'),
445 '#options' => _uc_attribute_display_types(),
446 '#default_value' => isset($attribute->display) ? $attribute->display : 1,
447 );
448 $form['ordering'] = array(
449 '#type' => 'weight',
450 '#title' => t('Order'),
451 '#description' => t('Multiple attributes on an add to cart form are sorted by this value and then by their name.<br />May be overridden at the product level.'),
452 '#default_value' => isset($attribute->ordering) ? $attribute->ordering : 0,
453 );
454
455 $form['submit'] = array(
456 '#type' => 'submit',
457 '#value' => t('Submit'),
458 '#suffix' => l(t('Cancel'), 'admin/store/attributes'),
459 );
460
461 return $form;
462 }
463
464 /**
465 * Submit function for uc_attribute_add_form().
466 */
467 function uc_attribute_form_submit($form, &$form_state) {
468 if (!empty($form_state['values']['aid'])) {
469 db_query("UPDATE {uc_attributes} SET name = '%s', ordering = %d, required = %d, display = %d, description = '%s' WHERE aid = %d", $form_state['values']['name'], $form_state['values']['ordering'], $form_state['values']['required'], $form_state['values']['display'], $form_state['values']['description'], $form_state['values']['aid']);
470 }
471 else {
472 db_query("INSERT INTO {uc_attributes} (name, ordering, required, display, description) VALUES ('%s', %d, %d, %d, '%s')", $form_state['values']['name'], $form_state['values']['ordering'], $form_state['values']['required'], $form_state['values']['display'], $form_state['values']['description']);
473 }
474
475 $form_state['redirect'] = 'admin/store/attributes';
476 }
477
478 // Confirms the deletion of the given attribute.
479 function uc_attribute_delete_confirm($form_state, $attribute) {
480 // If we got a bunk attribute, kick out an error message.
481 if (empty($attribute)) {
482 drupal_set_message(t('There is no attribute with that ID.'), 'error');
483 drupal_goto('admin/store/attributes');
484 }
485
486 $form['aid'] = array('#type' => 'value', '#value' => $attribute->aid);
487 $form['#redirect'] = 'admin/store/attributes';
488
489 $count = db_result(db_query("SELECT COUNT(*) FROM {uc_product_attributes} WHERE aid = %d", $attribute->aid));
490
491 $output = confirm_form($form, t('Are you sure you want to delete the attribute %name?', array('%name' => $attribute->name)),
492 'admin/store/attributes', format_plural($count, 'There is @count product with this attribute.', 'There are @count products with this attribute.'),
493 t('Delete'), t('Cancel'));
494
495 return $output;
496 }
497
498 function uc_attribute_delete_confirm_submit($form_id, &$form_state) {
499 if ($form_state['values']['confirm']) {
500 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_state['values']['aid']);
501 db_query("DELETE FROM {uc_class_attributes} WHERE aid = %d", $form_state['values']['aid']);
502 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_state['values']['aid']);
503 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_state['values']['aid']);
504 db_query("DELETE FROM {uc_product_attributes} WHERE aid = %d", $form_state['values']['aid']);
505 db_query("DELETE FROM {uc_attribute_options} WHERE aid = %d", $form_state['values']['aid']);
506 db_query("DELETE FROM {uc_attributes} WHERE aid = %d", $form_state['values']['aid']);
507 drupal_set_message(t('Product attribute deleted.'));
508 }
509 }
510
511 // Display options and the modifications to products they represent.
512 function uc_attribute_options($attribute) {
513 // If we got a bunk attribute, kick out an error message.
514 if (empty($attribute)) {
515 drupal_set_message(t('There is no attribute with that ID.'), 'error');
516 drupal_goto('admin/store/attributes');
517 }
518 $aid = $attribute->aid;
519
520 drupal_set_title(t('Options for %name', array('%name' => $attribute->name)));
521
522 $header = array(t('Name'), t('Default cost'), t('Default price'), t('Default weight'), t('Order'), t('Operations'));
523
524 foreach ($attribute->options as $key => $data) {
525 $ops = array(
526 l(t('edit'), 'admin/store/attributes/'. $aid .'/options/'. $key .'/edit'),
527 l(t('delete'), 'admin/store/attributes/'. $aid .'/options/'. $key .'/delete'),
528 );
529 $rows[] = array($data->name, $data->cost, $data->price, $data->weight, $data->ordering, implode(' ', $ops));
530 }
531 if (count($rows) == 0) {
532 $rows[] = array(
533 array('data' => t('No options for this attribute have been added yet.'), 'colspan' => '6')
534 );
535 }
536
537 $output .= theme('table', $header, $rows)
538 . l(t('Add an option'), 'admin/store/attributes/'. $aid .'/options/add');
539
540 return $output;
541 }
542
543 /**
544 * Form builder for attribute options.
545 *
546 * @ingroup forms
547 * @see uc_attribute_option_form_validate
548 * @see uc_attribute_option_form_submit
549 */
550 function uc_attribute_option_form($form_state, $attribute, $option = NULL) {
551 // If we got a bunk attribute, kick out an error message.
552 if (empty($attribute)) {
553 drupal_set_message(t('There is no attribute with that ID.'), 'error');
554 drupal_goto('admin/store/attributes');
555 }
556 $aid = $attribute->aid;
557
558 $form['aid'] = array('#type' => 'hidden', '#value' => $aid);
559
560 if (!empty($option)) {
561 $form['oid'] = array('#type' => 'hidden', '#value' => $option->oid);
562 drupal_set_title(t('Edit option: %name', array('%name' => $option->name)));
563 }
564 else {
565 drupal_set_title(t('Options for %name', array('%name' => $attribute->name)));
566 }
567
568 $form['name'] = array(
569 '#type' => 'textfield',
570 '#title' => t('Name'),
571 '#description' => t('This name will appear to customers on product add to cart forms.'),
572 '#default_value' => $option->name,
573 '#required' => TRUE,
574 '#weight' => 0,
575 );
576 $form['ordering'] = array(
577 '#type' => 'weight',
578 '#title' => t('Order'),
579 '#description' => t('Options will be listed sorted by this value and then by their name.<br />May be overridden at the product level.'),
580 '#default_value' => isset($option->ordering) ? $option->ordering : 0,
581 '#weight' => 4,
582 );
583 $form['adjustments'] = array(
584 '#type' => 'fieldset',
585 '#title' => t('Default adjustments'),
586 '#description' => t('Enter a positive or negative value for each adjustment applied when this option is selected.<br />Any of these may be overriden at the product level.'),
587 '#collapsible' => FALSE,
588 '#weight' => 8,
589 );
590 $form['adjustments']['cost'] = array(
591 '#type' => 'textfield',
592 '#title' => t('Cost'),
593 '#default_value' => $option->cost,
594 '#weight' => 1,
595 );
596 $form['adjustments']['price'] = array(
597 '#type' => 'textfield',
598 '#title' => t('Price'),
599 '#default_value' => $option->price,
600 '#weight' => 2,
601 );
602 $form['adjustments']['weight'] = array(
603 '#type' => 'textfield',
604 '#title' => t('Weight'),
605 '#default_value' => $option->weight,
606 '#weight' => 3,
607 );
608
609 $form['submit'] = array(
610 '#type' => 'submit',
611 '#value' => t('Submit'),
612 '#suffix' => l(t('Cancel'), 'admin/store/attributes/'. $aid .'/options'),
613 '#weight' => 10,
614 );
615
616 return $form;
617 }
618
619 /**
620 * Validate number formats.
621 */
622 function uc_attribute_option_form_validate($form, &$form_state) {
623 $pattern = '/^-?\d*(\.\d*)?$/';
624 $price_error = t('This must be in a valid number format. No commas and only one decimal point.');
625 if (!is_numeric($form_state['values']['cost']['#value']) && !preg_match($pattern, $form_state['values']['cost']['#value'])) {
626 form_set_error('cost', $price_error);
627 }
628 if (!is_numeric($form_state['values']['price']['#value']) && !preg_match($pattern, $form_state['values']['price']['#value'])) {
629 form_set_error('price', $price_error);
630 }
631 if (!is_numeric($form_state['values']['weight']['#value']) && !preg_match($pattern, $form_state['values']['weight']['#value'])) {
632 form_set_error('weight', $price_error);
633 }
634 }
635
636 /**
637 * Submit function for uc_attribute_option_form().
638 */
639 function uc_attribute_option_form_submit($form, &$form_state) {
640 if (!isset($form_state['values']['oid'])) {
641 db_query("INSERT INTO {uc_attribute_options} (aid, name, cost, price, weight, ordering) VALUES (%d, '%s', %f, %f, %f, %d)",
642 $form_state['values']['aid'], $form_state['values']['name'], $form_state['values']['cost'], $form_state['values']['price'], $form_state['values']['weight'], $form_state['values']['ordering']);
643 }
644 else {
645 db_query("UPDATE {uc_attribute_options} SET name = '%s', cost = %f, price = %f, weight = %f, ordering = %d WHERE aid = %d AND oid = %d",
646 $form_state['values']['name'], $form_state['values']['cost'], $form_state['values']['price'], $form_state['values']['weight'], $form_state['values']['ordering'], $form_state['values']['aid'], $form_state['values']['oid']);
647 }
648 $form_state['redirect'] = 'admin/store/attributes/'. $form_state['values']['aid'] .'/options';
649 }
650
651 // Confirms deletion of the given attribute option.
652 function uc_attribute_option_delete_confirm($form_state, $attribute, $option) {
653 if (empty($option)) {
654 drupal_set_message(t('There is no option with that ID.'), 'error');
655 drupal_goto('admin/store/attributes/'. $aid .'/options');
656 }
657 $aid = $attribute->aid;
658 $oid = $option->oid;
659
660 $form['aid'] = array('#type' => 'value', '#value' => $aid);
661 $form['oid'] = array('#type' => 'value', '#value' => $oid);
662
663 $output = confirm_form($form, t('Are you sure you want to delete the option %name?', array('%name' => $option->name)),
664 'admin/store/attributes/'. $aid .'/options', '',
665 t('Delete'), t('Cancel'));
666
667 return $output;
668 }
669
670 /**
671 * Submit function for uc_attribute_option_delete_confirm().
672 */
673 function uc_attribute_option_delete_confirm_submit($form, &$form_state) {
674 if ($form_state['values']['confirm']) {
675 $match = 'i:'. $form_state['values']['aid'] .';s:'. strlen($form_state['values']['oid']) .':"'. $form_state['values']['oid'] .'";';
676 db_query("DELETE FROM {uc_product_adjustments} WHERE combination LIKE '%%%s%%'", $match);
677 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_state['values']['oid']);
678 }
679 $form_state['redirect'] = 'admin/store/attributes/'. $form_state['values']['aid'] .'/options';
680 }
681
682 // Form to associate attributes with products or classes.
683 function uc_object_attributes_form($form_state, $object, $type, $view = 'overview') {
684 switch ($type) {
685 case 'class':
686 $class = $object;
687 $id = $class->pcid;
688 if (empty($class->name)) {
689 drupal_goto('admin/store/products/classes/'. $id);
690 }
691 drupal_set_title(check_plain($class->name));
692 $attributes = uc_class_get_attributes($id);
693 break;
694 case 'product':
695 default:
696 $product = $object;
697 $id = $product->nid;
698 if (empty($product->title)) {
699 drupal_goto('node/'. $id);
700 }
701 drupal_set_title(check_plain($product->title));
702 $attributes = uc_product_get_attributes($id);
703 }
704
705 $used_aids = array();
706 foreach ($attributes as $attribute) {
707 $used_aids[] = $attribute->aid;
708 }
709
710 if ($view == 'overview') {
711 $form['#tree'] = TRUE;
712
713 if (count($attributes) > 0) {
714 foreach ($attributes as $attribute) {
715 $option = $attribute->options[$attribute->default_option];
716
717 $form['attributes'][$attribute->aid] = array(
718 'remove' => array(
719 '#type' => 'checkbox',
720 '#default_value' => 0,
721 ),
722 'name' => array(
723 '#value' => $attribute->name,
724 ),
725 'option' => array(
726 '#value' => $option ? ($option->name .' ('. uc_currency_format($option->price) .')' ) : t('n/a'),
727 ),
728 'required' => array(
729 '#type' => 'checkbox',
730 '#default_value' => $attribute->required,
731 ),
732 'ordering' => array(
733 '#type' => 'weight',
734 '#default_value' => $attribute->ordering,
735 ),
736 'display' => array(
737 '#type' => 'select',
738 '#default_value' => $attribute->display,
739 '#options' => _uc_attribute_display_types(),
740 ),
741 );
742 }
743
744 $form['save'] = array(
745 '#type' => 'submit',
746 '#value' => t('Save changes'),
747 '#weight' => -2,
748 );
749 }
750 }
751 else if ($view == 'add') {
752 // Get list of attributes not already assigned to this node or class.
753 $unused_attributes = array();
754 $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 a.aid, a.name ORDER BY a.name");
755 while ($attribute = db_fetch_object($result)) {
756 if (!in_array($attribute->aid, $used_aids)) {
757 $unused_attributes[$attribute->aid] = $attribute->name;
758 }
759 }
760
761 $form['add_attributes'] = array(
762 '#type' => 'select',
763 '#title' => t('Attributes'),
764 '#description' => t('Hold Ctrl + click to select multiple attributes.'),
765 '#options' => count($unused_attributes) > 0 ? $unused_attributes : array(t('No attributes left to add.')),
766 '#disabled' => count($unused_attributes) == 0 ? TRUE : FALSE,
767 '#multiple' => TRUE,
768 '#weight' => -1
769 );
770 $form['add'] = array(
771 '#type' => 'submit',
772 '#value' => t('Add attributes'),
773 '#suffix' => l(t('Cancel'), $type == 'product' ? 'node/'. $id .'/edit/attributes' : 'admin/store/products/classes/'. $class->pcid .'/attributes'),
774 '#weight' => 0,
775 );
776 }
777
778 $form['id'] = array(
779 '#type' => 'value',
780 '#value' => $id,
781 );
782 $form['type'] = array(
783 '#type' => 'value',
784 '#value' => $type,
785 );
786 $form['view'] = array(
787 '#type' => 'value',
788 '#value' => $view,
789 );
790
791 return $form;
792 }
793
794 /**
795 * Display the formatted attribute form.
796 *
797 * @ingroup themeable
798 */
799 function theme_uc_object_attributes_form($form) {
800 if ($form['view']['#value'] == 'overview') {
801 $header = array(t('Remove'), t('Name'), t('Default'), t('Required'), t('Order'), t('Display'));
802
803 if (count(element_children($form['attributes'])) > 0) {
804 foreach (element_children($form['attributes']) as $aid) {
805 $rows[] = array(
806 drupal_render($form['attributes'][$aid]['remove']),
807 drupal_render($form['attributes'][$aid]['name']),
808 drupal_render($form['attributes'][$aid]['option']),
809 drupal_render($form['attributes'][$aid]['required']),
810 drupal_render($form['attributes'][$aid]['ordering']),
811 drupal_render($form['attributes'][$aid]['display']),
812 );
813 }
814 }
815 else {
816 $rows[] = array(
817 array('data' => t('You must first <a href="!url">add attributes to this !type</a>.', array('!url' => request_uri() .'/add', '!type' => $form['type']['#value'])), 'colspan' => 6),
818 );
819 }
820
821 $output = theme('table', $header, $rows);
822 }
823
824 $output .= drupal_render($form);
825
826 return $output;
827 }
828
829 function uc_object_attributes_form_submit($form, &$form_state) {
830 if ($form_state['values']['type'] == 'product') {
831 $attr_table = '{uc_product_attributes}';
832 $opt_table = '{uc_product_options}';
833 $id = 'nid';
834 $sql_type = '%d';
835 }
836 else if ($form_state['values']['type'] == 'class') {
837 $attr_table = '{uc_class_attributes}';
838 $opt_table = '{uc_class_attribute_options}';
839 $id = 'pcid';
840 $sql_type = "'%s'";
841 }
842
843 if ($form_state['values']['view'] == 'overview' && is_array($form_state['values']['attributes'])) {
844 foreach ($form_state['values']['attributes'] as $aid => $attribute) {
845 if ($attribute['remove']) {
846 $remove_aids[] = $aid;
847 }
848 else {
849 db_query("UPDATE $attr_table SET ordering = %d, required = %d, display = %d WHERE aid = %d AND $id = $sql_type", $attribute['ordering'], $attribute['required'], $attribute['display'], $aid, $form_state['values']['id']);
850 $changed = TRUE;
851 }
852 }
853
854 if (count($remove_aids) > 0) {
855 $values = array($form_state['values']['id'], implode(', ', $remove_aids));
856
857 db_query("DELETE po FROM $opt_table AS po LEFT JOIN {uc_attribute_options} AS ao ON po.oid = ao.oid WHERE po.$id = $sql_type AND ao.aid IN (%s)", $values);
858 db_query("DELETE FROM $attr_table WHERE $id = $sql_type AND aid IN (%s)", $values);
859 if ($form_state['values']['type'] == 'product') {
860 db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $form_state['values']['id']);
861 }
862
863 drupal_set_message(format_plural(count($remove_aids), '@count attribute has been removed.', '@count attributes have been removed.'));
864 }
865
866 if ($changed) {
867 drupal_set_message(t('The changes have been saved.'));
868 }
869 }
870 else if ($form_state['values']['view'] == 'add') {
871 foreach ($form_state['values']['add_attributes'] as $aid) {
872 db_query("INSERT INTO $attr_table ($id, aid, ordering, default_option, required, display) SELECT $sql_type, aid, ordering, 0, required, display FROM {uc_attributes} WHERE aid = %d", $form_state['values']['id'], $aid);
873 }
874 if (count($form_state['values']['add_attributes']) > 0) {
875 if ($form_state['values']['type'] == 'product') {
876 db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $form_state['values']['id']);
877 }
878 drupal_set_message(format_plural(count($form_state['values']['add_attributes']), '@count attribute has been added.', '@count attributes have been added.'));
879 }
880 }
881
882 if ($form_state['values']['type'] == 'product') {
883 $form_state['redirect'] = 'node/'. $form_state['values']['id'] .'/edit/attributes';
884 }
885 else {
886 $form_state['redirect'] = 'admin/store/products/classes/'. $form_state['values']['id'] .'/attributes';
887 }
888 }
889
890 // Form to assign and modify attribute options on products or classes.
891 function uc_object_options_form($form_state, $object, $type) {
892 if ($type == 'product') {
893 $product = $object;
894 $id = $product->nid;
895 drupal_set_title(check_plain($product->title));
896 $attributes = uc_product_get_attributes($id);
897 $table = '{uc_product_options}';
898 $id_type = 'nid';
899 $sql_type = '%d';
900 }
901 else if ($type == 'class') {
902 $class = $object;
903 $id = $class->pcid;
904 drupal_set_title(check_plain($class->name));
905 $attributes = uc_class_get_attributes($id);
906 $table = '{uc_class_attribute_options}';
907 $id_type = 'pcid';
908 $sql_type = "'%s'";
909 }
910
911 foreach ($attributes as $aid => $attribute) {
912 $form['attributes'][$aid] = array(
913 '#prefix' => '<tr>',
914 '#suffix' => '</tr>',
915 );
916 $form['attributes'][$aid]['name'] = array(
917 '#value' => $attribute->name,
918 );
919 $form['attributes'][$aid]['aid'] = array(
920 '#type' => 'hidden',
921 '#value' => $attribute->aid,
922 );
923 $form['attributes'][$aid]['ordering'] = array(
924 '#type' => 'value',
925 '#value' => $attribute->ordering,
926 );
927
928 $form['attributes'][$aid]['options'] = array('#weight' => 2);
929
930 $base_attr = uc_attribute_load($attribute->aid);
931
932 if ($base_attr->options) {
933 $options = array();
934
935 $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 = $sql_type WHERE aid = %d ORDER BY po.ordering, default_ordering, ao.name", $id, $attribute->aid);
936 while ($option = db_fetch_object($result)) {
937 $oid = $option->oid;
938 $options[$oid] = '';
939
940 $form['attributes'][$aid]['options'][$oid]['select'] = array(
941 '#type' => 'checkbox',
942 '#default_value' => isset($attribute->options[$oid]) ? TRUE : FALSE,
943 '#title' => $option->name,
944 );
945 $form['attributes'][$aid]['options'][$oid]['cost'] = array(
946 '#type' => 'textfield',
947 '#default_value' => is_null($option->cost) ? $option->default_cost : $option->cost,
948 '#size' => 6,
949 );
950 $form['attributes'][$aid]['options'][$oid]['price'] = array(
951 '#type' => 'textfield',
952 '#default_value' => is_null($option->price) ? $option->default_price : $option->price,
953 '#size' => 6,
954 );
955 $form['attributes'][$aid]['options'][$oid]['weight'] = array(
956 '#type' => 'textfield',
957 '#default_value' => is_null($option->weight) ? $option->default_weight : $option->weight,
958 '#size' => 5,
959 );
960 $form['attributes'][$aid]['options'][$oid]['ordering'] = array(
961 '#type' => 'weight',
962 '#default_value' => is_null($option->ordering) ? $option->default_ordering : $option->ordering,
963 );
964 }
965
966 $form['attributes'][$aid]['default'] = array(
967 '#type' => 'radios',
968 '#options' => $options,
969 '#default_value' => /* $attribute->required ? NULL : */ $attribute->default_option,
970 //'#disabled' => $attribute->required,
971 );
972 }
973 else {
974 $form['attributes'][$aid]['default'] = array(
975 '#value' => t('This attribute does not have any options.'),
976 );
977 }
978 }
979
980 if (!empty($form['attributes'])) {
981 $form['attributes']['#tree'] = TRUE;
982
983 $form['submit'] = array(
984 '#type' => 'submit',
985 '#value' => t('Submit'),
986 '#weight' => 10,
987 );
988 }
989
990 $form['id'] = array(
991 '#type' => 'value',
992 '#value' => $id,
993 );
994 $form['type'] = array(
995 '#type' => 'value',
996 '#value' => $type,
997 );
998
999 return $form;
1000 }
1001
1002 /**
1003 * Display the option form.
1004 *
1005 * @ingroup themeable
1006 */
1007 function theme_uc_object_options_form($form) {
1008 $header = array(t('Attribute'), t('Options'), t('Default'), t('Cost'), t('Price'), t('Weight'), t('Order'));
1009
1010 foreach (element_children($form['attributes']) as $key) {
1011 $row = array();
1012 $row[] = array('data' => drupal_render($form['attributes'][$key]['aid']) . drupal_render($form['attributes'][$key]['name']), 'class' => 'attribute');
1013
1014 if (element_children($form['attributes'][$key]['default'])) {
1015 $first = TRUE;
1016 foreach (element_children($form['attributes'][$key]['default']) as $oid) {
1017 $row[] = drupal_render($form['attributes'][$key]['options'][$oid]['select']);
1018 $row[] = drupal_render($form['attributes'][$key]['default'][$oid]);
1019 $row[] = drupal_render($form['attributes'][$key]['options'][$oid]['cost']);
1020 $row[] = drupal_render($form['attributes'][$key]['options'][$oid]['price']);
1021 $row[] = drupal_render($form['attributes'][$key]['options'][$oid]['weight']);
1022 $row[] = drupal_render($form['attributes'][$key]['options'][$oid]['ordering']);
1023
1024 if (!$first) {