=== modified file 'docs/hooks.php' --- docs/hooks.php 2009-03-20 20:03:26 +0000 +++ docs/hooks.php 2009-04-06 23:41:58 +0000 @@ -1249,5 +1249,30 @@ function hook_update_cart_item($nid, $da } /** + * Use this hook to define price handlers for your module. You may define one + * price alterer and one price formatter. You may also define options that are + * merged into the options array in order of each price alterer's weight. + */ +function hook_uc_price_handler() { + return array( + 'alter' => array( + 'callback' => 'my_price_handler_alter', + ), + 'format' => array( + 'callback' => 'my_price_handler_format', + ), + 'options' => array( + 'sign' => variable_get('uc_currency_sign', '*'), + 'sign_after' => TRUE, + 'prec' => 4, + 'dec' => ',', + 'thou' => '.', + 'label' => FALSE, + 'my_option_that_my_formatter_recognizes' => 1337, + ) + ); +} + +/** * @} End of "addtogroup hooks". */ === modified file 'uc_product/uc_product.css' --- uc_product/uc_product.css 2009-01-07 15:08:16 +0000 +++ uc_product/uc_product.css 2009-03-16 10:36:33 +0000 @@ -9,6 +9,17 @@ margin-left: 4px; } +.uc-price-display { + float: right; + clear: right; + width: 100px; + text-align: center; + font-size: 1.3em; + font-weight: bold; + padding-bottom: 4px; + padding-left: 4px; +} + .display-price { float: right; clear: right; === modified file 'uc_product/uc_product.module' --- uc_product/uc_product.module 2009-04-07 18:50:58 +0000 +++ uc_product/uc_product.module 2009-04-06 23:41:59 +0000 @@ -695,27 +695,45 @@ function uc_product_view($node, $teaser 'add_to_cart' => 10, )); + $context = array( + 'location' => 'product-view', + 'class' => array( + 'product', + ), + 'subject' => array( + 'nid' => $node->nid, + ), + ); + + if (module_exists('imagecache') && ($field = variable_get('uc_image_'. $node->type, '')) && isset($node->$field) && file_exists($node->{$field}[0]['filepath'])) { $node->content['image'] = array('#value' => theme('uc_product_image', $node->$field, $teaser, $page), '#access' => $enabled['image'], '#weight' => $weight['image'], ); } - $node->content['display_price'] = array('#value' => theme('uc_product_price', $node->sell_price, 'display-price', TRUE), + + $context['class'][1] = 'display'; + $node->content['display_price'] = array('#value' => uc_price($node->sell_price, $context), '#access' => $enabled['display_price'], '#weight' => $weight['display_price'], ); + if (!$teaser) { $node->content['model'] = array('#value' => theme('uc_product_model', $node->model, $teaser, $page), '#access' => $enabled['model'], '#weight' => $weight['model'], ); $node->content['body']['#weight'] = 1; - $node->content['list_price'] = array('#value' => theme('uc_product_price', $node->list_price, 'list-price'), + + $context['class'][1] = 'list'; + $node->content['list_price'] = array('#value' => uc_price($node->list_price, $context), '#access' => $enabled['list_price'], '#weight' => $weight['list_price'], ); - $node->content['cost'] = array('#value' => theme('uc_product_price', $node->cost, 'cost'), + + $context['class'][1] = 'cost'; + $node->content['cost'] = array('#value' => uc_price($node->cost, $context), '#access' => $enabled['cost'] && user_access('administer products'), '#weight' => $weight['cost'], ); @@ -724,7 +742,8 @@ function uc_product_view($node, $teaser $node->content['#attributes'] = array('style' => 'display: inline'); } - $node->content['sell_price'] = array('#value' => theme('uc_product_price', $node->sell_price, 'sell-price', $teaser), + $context['class'][1] = 'sell'; + $node->content['sell_price'] = array('#value' => uc_price($node->sell_price, $context, array('label' => !$teaser)), '#access' => $enabled['sell_price'], '#weight' => $weight['sell_price'], ); === added file 'uc_store/includes/uc_price.inc' --- uc_store/includes/uc_price.inc 1970-01-01 00:00:00 +0000 +++ uc_store/includes/uc_price.inc 2009-04-06 23:34:51 +0000 @@ -0,0 +1,222 @@ + 'The original price', + * 'altered' => 'Price after passing through the alterer(s)', + * 'formatted' => 'Price after passing through the formatter', + * (default) 'themed' => 'Price after passing through the theme layer'. + * + * This function handles price alteration, formatting, theming, and caching. + * + */ +function uc_price($price_info, $context, $options = array(), $revision = NULL) { + // If we're passed just a number for price, we'll set the quantity to 1. + if (is_numeric($price_info)) { + $price_info = array( + 'value' => $price_info, + 'qty' => 1, + ); + } + + // Initialize options. + $options += array( + 'cache' => TRUE, + ); + // Clamp to allowed revisions. + switch ($revision) { + case 'original': + case 'altered': + case 'formatted': + case 'themed': + break; + + default: + $revision = 'themed'; + } + // Get all the active handlers. + $handlers = _uc_price_get_handlers($options); + // Use the global user if none was passed in. + if (!isset($context['uid'])) { + global $user; + $context['uid'] = $user->uid; + } + // Merge any incoming options, giving them precedence. + $options += $handlers['options']; + // Build the cache key with the original state. + global $user; + $key = 'uc-price-'. $context['uid'] .'-'. md5(serialize($price_info + $context + $options)); + // Is it cached? + if ($options['cache'] != FALSE && $cache = cache_get($key, 'cache_uc_price')) { + $cache = $cache->data; + } + // Otherwise, build it. + else { + $cache = array(); + $cache['original'] = $price_info['value'] * $price_info['qty']; + // Alter the price, context, and options. + foreach ($handlers['alterers'] as $alterer) { + $alterer($price_info, $context, $options); + } + // A placeholder can override any formatting. For instance, trying to format + // something like "FREE for a limited time!" as a price wouldn't be easy. + if (isset($price_info['placeholder'])) { + $value = $price_info['placeholder']; + $cache['altered'] = $value; + $cache['formatted'] = $value; + } + else { + $value = $price_info['value'] * $price_info['qty']; + $cache['altered'] = $value; + // Format the price. + $value = $handlers['formatter']($value, $options); + $cache['formatted'] = $value; + } + // Theme the price. + $cache['themed'] = theme('uc_price', $value, $context, $options);; + // Cache this price's revisions? + if ($options['cache']) { + cache_set($key, $cache, 'cache_uc_price', CACHE_TEMPORARY); + } + } + + // Return the requested revision. + return $cache[$revision]; +} + +function _uc_price_get_handlers($options = array()) { + // Set defaults. + $options += array( + 'rebuild_handlers' => FALSE, + 'all_handlers' => FALSE, + ); + // Get handlers only if we haven't already, or if this is a rebuild. + static $handlers = array(); + if (!$handlers || $options['rebuild_handlers']) { + // Get the handlers and sort them by weight. + $config = variable_get('uc_price_handler_config', array()); + $modules = module_implements('uc_price_handler'); + foreach ($modules as $module) { + // Sane defaults. + $hooks[$module] = module_invoke($module, 'uc_price_handler') + array( + 'weight' => 0, + 'enabled' => TRUE, + ); + // Merge any configuration state in. + if (isset($config[$module])) { + $hooks[$module] = $config[$module] + $hooks[$module]; + } + // Unset disabled ones, but only if we're not building the selection form. + if (!$options['all_handlers'] && $hooks[$module]['enabled'] == FALSE) { + unset($hooks[$module]); + } + } + uasort($hooks, 'uc_weight_sort'); + + // Store the raw data for selection form building. + $handlers['hook_data'] = $hooks; + // Store the selected formatter, defaulting to uc_store's implementation. + $format_func = variable_get('uc_store_price_format', 'uc_store_price_handler_format'); + if (!function_exists($format_func)) { + $format_func = 'uc_store_price_handler_format'; + } + $handlers['formatter'] = $format_func; + // Grab all the alter/format callbacks, as well as merging the options. + // This happens in order by weight, so we're kosher. + $handlers['alterers'] = array(); + // We set some default options here. We could set them in the uc_store price handler, + // but that means if that handler is disabled, we won't get them merged in. + $handlers['options'] = array( + 'sign' => variable_get('uc_currency_sign', '$'), + 'sign_after' => variable_get('uc_sign_after_amount', FALSE), + 'prec' => variable_get('uc_currency_prec', 2), + 'dec' => variable_get('uc_currency_dec', '.'), + 'thou' => variable_get('uc_currency_thou', ','), + 'label' => TRUE, + ); + + foreach ($hooks as $hook) { + $handlers['alterers'][] = $hook['alter']['callback']; + $handlers['formatters'][] = $hook['format']['callback']; + if (is_array($hook['options'])) { + $handlers['options'] += $hook['options']; + } + } + } + + return $handlers; +} + +/** + * Implementation of hook_uc_price_handler(). + */ +function uc_store_uc_price_handler() { + return array( + 'alter' => array( + 'callback' => 'uc_store_price_handler_alter', + ), + 'format' => array( + 'callback' => 'uc_store_price_handler_format', + ), + ); +} + +/** + * Price handler (alterer). Add the default prefixes to prices + * when viewing a product node. + */ +function uc_store_price_handler_alter(&$price, &$context, &$options) { + if (in_array('list', $context['class'])) { + $options['prefixes'][] = t('List Price: '); + } + if (in_array('cost', $context['class'])) { + $options['prefixes'][] = t('Cost: '); + } + if (in_array('sell', $context['class'])) { + $options['prefixes'][] = t('Price: '); + } +} + +/** + * Price handler (formatter). Ubercart's uc_currency_format(), repurposed + * for a brighter tomorrow. + */ +function uc_store_price_handler_format($value, $options) { + $output = ''; + // If the value is less than the minimum precision, zero it. + if ($options['prec'] > 0 && abs($value) < 1 / pow(10, $options['prec'])) { + $value = 0; + } + // Value is always positive, and we'll add the negative sign if necessary. + if ($value < 0) { + $value = abs($value); + $output .= '-'; + } + // Add the currency sign first? + if ($options['sign'] && !$options['sign_after']) { + $output .= $options['sign']; + } + // Format the number, like 1234.567 => 1,234.57 + $output .= number_format($value, $options['prec'], $options['dec'], $options['thou']); + // Add the currency sign last? + if ($options['sign'] && $options['sign_after']) { + $output .= $options['sign']; + } + + return $output; +} + === modified file 'uc_store/uc_store.admin.inc' --- uc_store/uc_store.admin.inc 2009-03-11 04:33:33 +0000 +++ uc_store/uc_store.admin.inc 2009-03-17 14:16:52 +0000 @@ -1079,3 +1079,100 @@ function uc_country_update($country_id, drupal_goto('admin/store/settings/countries/edit/import'); } +/** + * Form for enabling/weighting price alterers and selecting a price formatter. + */ +function uc_price_settings_form($form_state) { + + // Get all the handlers available. + $handlers = _uc_price_get_handlers(array('rebuild_handlers' => TRUE, 'all_handlers' => TRUE)); + $options = array(); + // Make our alterer table all slick with a tabledrag. + $form['uc_price_alterers']['#tree'] = TRUE; + foreach ($handlers['hook_data'] as $module => $hook_datum) { + if (isset($hook_datum['alter'])) { + $form['uc_price_alterers'][$module] = array( + 'module' => array( + '#type' => 'markup', + '#value' => $module, + ), + 'enabled' => array( + '#type' => 'checkbox', + '#default_value' => $hook_datum['enabled'], + ), + 'weight' => array( + '#type' => 'weight', + '#default_value' => $hook_datum['weight'], + '#attributes' => array('class' => 'uc-price-handler-table-weight'), + ), + ); + } + if (isset($hook_datum['format'])) { + $options[$module] = $module; + } + } + + $form['invocation_message'] = array( + '#type' => 'markup', + '#value' => t('