/[drupal]/contributions/modules/money/money.module
ViewVC logotype

Contents of /contributions/modules/money/money.module

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


Revision 1.1 - (show annotations) (download) (as text)
Thu Aug 9 13:39:45 2007 UTC (2 years, 3 months ago) by wimleers
Branch: MAIN
CVS Tags: DRUPAL-5--1-0, HEAD
Branch point for: DRUPAL-5, DRUPAL-6--1
File MIME type: text/x-php
Initial commit of money.module. This module defines the "money" CCK field. It uses the Currency API, which is included in the Currency module, to get a list of valid currencies. Decimal separators and digit group separators are configurable.
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * This module defines the "money" CCK field. It uses the Currency API, which
7 * is included in the Currency module, to get a list of valid currencies.
8 *
9 * Only amounts with 2 decimals can be used. Any decimal separator and any
10 * digit group separator can be used, but it defaults to the comma and the dot
11 * respectively, which is according to ISO 31-0. The separators can be changed
12 * at any point, only integers are stored in the database.
13 */
14
15 //----------------------------------------------------------------------------
16 // CCK hooks.
17
18 /**
19 * Implementation of hook_field_info().
20 */
21 function money_field_info() {
22 return array('money' => array('label' => t('Money')));
23 }
24
25 /**
26 * Implementation of hook_field_settings().
27 */
28 function money_field_settings($op, $field) {
29 switch ($op) {
30 case 'form':
31 $form = array();
32 $form['currency_list'] = array(
33 '#value' => theme('money_field_settings_currency_list', currency_api_get_list()),
34 );
35 $form['allowed_currencies'] = array(
36 '#type' => 'textarea',
37 '#rows' => 5,
38 '#title' => t('Currencies'),
39 '#description' => t('Enter the 3-letter ISO codes for the currencies that you want to allow, separated by commas. Leave empty to allow all currencies.'),
40 '#default_value' => (isset($field['allowed_currencies'])) ? $field['allowed_currencies'] : '',
41 );
42 return $form;
43
44 case 'validate':
45 $valid_currencies = array_keys(currency_api_get_list());
46 $allowed_currencies = _money_parse_currencies($field['allowed_currencies']);
47 foreach ($allowed_currencies as $currency) {
48 if (!in_array($currency, $valid_currencies)) {
49 form_set_error('allowed_currencies', t('The currency %currency is not a valid currency.', array('%currency' => $currency)));
50 }
51 }
52 break;
53
54 case 'save':
55 return array('allowed_currencies');
56
57 case 'database columns':
58 $columns['amount'] = array(
59 'type' => 'int',
60 'length' => 13,
61 'not null' => TRUE,
62 'default' => 0,
63 'unsigned' => FALSE,
64 );
65 $columns['currency'] = array('type' => 'varchar', 'length' => 3);
66 return $columns;
67
68 case 'filters':
69 return array(
70 'default' => array(
71 'name' => t('Default'),
72 'operator' => 'views_handler_operator_gtlt',
73 ),
74 'currency_order' => array(
75 'name' => t('Order by currency'),
76 'operator' => 'views_handler_operator_gtlt',
77 ),
78 );
79 }
80 }
81
82 /**
83 * Implementation of hook_field().
84 */
85 function money_field($op, &$node, $field, &$items, $teaser, $page) {
86 switch ($op) {
87 case 'validate':
88 $allowed_currencies = _money_parse_currencies($field['allowed_currencies']);
89
90 if (is_array($items)) {
91 foreach ($items as $delta => $item) {
92 // Validate the currency.
93 if (!in_array($item['currency'], $allowed_currencies)) {
94 form_set_error($field['field_name'] .']['. $delta .'][currency', t('The currency %currency is not allowed.', array('%currency' => t($item['currency']))));
95 }
96 // Validate the amount.
97 if (!is_numeric($item['amount'])) {
98 form_set_error($field['field_name'] .']['. $delta .'][amount', t('You entered an invalid amount.'));
99 }
100 }
101 }
102 break;
103 }
104 }
105
106 /**
107 * Implementation of hook_field_formatter_info().
108 */
109 function money_field_formatter_info() {
110 return array(
111 'default' => array(
112 'label' => t('Default'),
113 'field types' => array('money'),
114 ),
115 );
116 }
117
118 /**
119 * Implementation of hook_field_formatter().
120 */
121 function money_field_formatter($field, $item, $formatter, $node) {
122 if (empty($item['amount'])) {
123 return '';
124 }
125 else {
126 $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
127 $digit_group_separator = _money_get_digit_group_separator($field['widget']['digit_group_separator']);
128 return check_plain(number_format($item['amount']/100, 2, $decimal_separator, $digit_group_separator));
129 }
130 }
131
132 /**
133 * Implementation of hook_widget_info().
134 */
135 function money_widget_info() {
136 return array(
137 'money_default' => array(
138 'label' => 'Select list for the currency, textfield for the amount',
139 'field types' => array('money'),
140 ),
141 );
142 }
143
144 /**
145 * Implementation of hook_widget_settings().
146 */
147 function money_widget_settings($op, $widget) {
148 switch ($op) {
149 case 'form':
150 $form = array();
151 $form['decimal_separator'] = array(
152 '#type' => 'textfield',
153 '#title' => t('Decimal separator'),
154 '#default_value' => _money_get_decimal_separator($widget['decimal_separator']),
155 '#size' => 5,
156 '#maxlength' => 255,
157 '#description' => t(
158 'Three decimal separators are used across the planet: the dot
159 (English-speaking countries), the comma (Europe) and the momayyez
160 (Arab world and Iran). ISO 31-0 specifies both the dot and the comma
161 as valid, but prefers the comma, this is also the default.'
162 ),
163 );
164 $form['digit_group_separator'] = array(
165 '#type' => 'textfield',
166 '#title' => t('Digit group separator'),
167 '#default_value' => _money_get_digit_group_separator($widget['digit_group_separator']),
168 '#size' => 5,
169 '#maxlength' => 255,
170 '#description' => t(
171 'Three digit group separators are used across the planet: the comma
172 (English-speaking countries), the dot (Europe) and the space. ISO
173 31-0 specifies only the space as valid, this is also the default.'
174 ),
175 );
176 return $form;
177 case 'save':
178 return array('decimal_separator', 'digit_group_separator');
179 }
180 }
181
182 /**
183 * Implementation of hook_widget().
184 */
185 function money_widget($op, &$node, $field, &$items) {
186 if ($field['widget']['type'] == 'money_default') {
187 switch ($op) {
188 case 'prepare form values':
189 $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
190 $digit_group_separator = _money_get_digit_group_separator($field['widget']['digit_group_separator']);
191
192 if (!count($items)) {
193 $items[0] = array();
194 }
195 else {
196 foreach ($items as $delta => $item) {
197 $items[$delta]['amount'] = check_plain(number_format($item['amount']/100, 2, $decimal_separator, $digit_group_separator));
198 }
199 }
200 break;
201
202 case 'form':
203 drupal_add_css(drupal_get_path('module', 'money') .'/money.css');
204
205 $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
206 $allowed_currencies = _money_parse_currencies($field['allowed_currencies']);
207
208 // Variables to be used in the "currency" form item.
209 $currency_options = array_combine($allowed_currencies, $allowed_currencies);
210
211 // Variables to be used in the "amount" form item.
212 if (isset($field['widget']['default_value'][0]['amount'])) {
213 $amount_default = check_plain(number_format($field['widget']['default_value'][0]['amount']/100, 2, $decimal_separator, $digit_group_separator));
214 }
215 else {
216 $amount_default = check_plain(number_format("0{$decimal_separator}00"/100, 2, $decimal_separator, $digit_group_separator));
217 }
218 $amount_description = t(
219 'Use "@decimal_separator" as the decimal separator and (optionally)
220 "@digit_group_separator" as the digit group separator. You can
221 only enter two decimals.',
222 array(
223 '@decimal_separator' => $field['widget']['decimal_separator'],
224 '@digit_group_separator' => $field['widget']['digit_group_separator'],
225 )
226 );
227
228 // If this field is configured as a multiple value field, make sure
229 // that there are at least 3 form items.
230 while ($field['multiple'] && count($items) < 3) {
231 $items[] = array();
232 }
233
234 // Create the prefix in which we'll store first the label, then a
235 // container div in which we'll put the actual form elements.
236 $prefix = '<div class="form-item">';
237 $prefix .= '<label>'. t($field['widget']['label']);
238 if (!empty($field['required'])) {
239 $prefix .= '<span class="form-required" title="'. t('This field is required.') .'">*</span>';
240 }
241 $prefix .= '</label>';
242
243 // Actual form creation begins here.
244 $form = array();
245 $form[$field['field_name']]['#tree'] = TRUE;
246 $form[$field['field_name']]['#prefix'] = $prefix;
247 $form[$field['field_name']]['#type'] = ($field['multiple']) ? 'fieldset' : 'markup';
248 $form[$field['field_name']]['#suffix'] = '</div>';
249
250 foreach ($items as $delta => $item) {
251 // These are the actual form items for each money field.
252 $form[$field['field_name']][$delta]['#tree'] = TRUE;
253 $form[$field['field_name']][$delta]['currency'] = array(
254 '#type' => 'select',
255 '#options' => $currency_options,
256 '#default_value' => isset($item['currency']) ? $item['currency'] : $field['widget']['default_value'][0]['currency'],
257 '#attributes' => array('class' => 'money-field money-field-currency'),
258 '#prefix' => '<div class="container-inline money-field-form-items">',
259 );
260 $form[$field['field_name']][$delta]['amount'] = array(
261 '#type' => 'textfield',
262 '#size' => 20,
263 '#maxlength' => 25,
264 '#default_value' => isset($item['amount']) ? $item['amount'] : $amount_default,
265 '#attributes' => array('class' => 'money-field money-field-amount'),
266 '#description' => ($delta == end(array_keys($items))) ? $amount_description : NULL,
267 '#suffix' => '</div>',
268 );
269 }
270
271 return $form;
272
273 case 'validate':
274 // Generate the regular expression to validate the entered amounts.
275 $decimal_separator = preg_quote(_money_get_decimal_separator($field['widget']['decimal_separator']));
276 $digit_group_separator = preg_quote(_money_get_digit_group_separator($field['widget']['digit_group_separator']));
277 $regexp = "/^-?(((\d{1,3}". $digit_group_separator .")?(\d{3}". $digit_group_separator .")*(\d{3}){1})|\d+)(". $decimal_separator ."\d{1,2})?$/";
278
279 // Make sure the amount is entered using the correct format.
280 foreach ($items as $delta => $item) {
281 if (!empty($item['amount']) && !preg_match($regexp, $item['amount'])) {
282 form_set_error($field['field_name'] .']['. $delta .'][amount', t('The amount is formatted invalidly.'));
283 }
284 }
285 break;
286
287 case 'process form values':
288 $decimal_separator = _money_get_decimal_separator($field['widget']['decimal_separator']);
289 $digit_group_separator = _money_get_digit_group_separator($field['widget']['digit_group_separator']);
290
291 foreach ($items as $delta => $item) {
292 if (empty($item['amount'])) {
293 unset($items[$delta]['amount']);
294 }
295 else {
296 // Convert the entered amount to be compatible with PHP's number
297 // notation: a dot as a decimal separator, nothing as a digit
298 // group separator.
299 $converted_amount = str_replace(array($decimal_separator, $digit_group_separator), array('.', ''), $item['amount']);
300
301 // Now convert the amount to make it storable as an integer.
302 // We are always working with a maximum of 2 decimals, this means
303 // that one unit in the database corresponds to 1/100th of a unit
304 // in reality (i.e. in forms and on display).
305 $items[$delta]['amount'] = $converted_amount * 100;
306 }
307 }
308 break;
309 }
310 }
311 }
312
313
314 //----------------------------------------------------------------------------
315 // Private functions.
316
317 /**
318 * Parse currency codes from a comma-separated list.
319 *
320 * @param $currencies_string
321 * A string containing a list of currency codes, separated by commas.
322 * @return
323 * An array of currency code.
324 */
325 function _money_parse_currencies($currencies_string) {
326 return explode(',', str_replace(' ', '', trim($currencies_string)));
327 }
328
329 /**
330 * Get the decimal separator from a variable, use the default if the variable
331 * is empty.
332 *
333 * @param $decimal_separator
334 * A variable that possibly contains a decimal separator.
335 * @return
336 * A decimal separator, either the variable or the default (a comma).
337 */
338 function _money_get_decimal_separator($decimal_separator = NULL) {
339 return (!empty($decimal_separator)) ? $decimal_separator : ',';
340 }
341
342 /**
343 * Get the digit group separator from a variable, use the default if the
344 * variable is empty.
345 *
346 * @param $digit_group_separator
347 * A variable that possibly contains a digit group separator.
348 * @return
349 * A digit group separator, either the variable or the default (a space).
350 */
351 function _money_get_digit_group_separator($digit_group_separator = NULL) {
352 return (!empty($digit_group_separator)) ? $digit_group_separator : ' ';
353 }
354
355
356 //----------------------------------------------------------------------------
357 // Theming functions.
358
359 /**
360 * @ingroup themeable
361 * @{
362 */
363
364 /**
365 * Format the list of currencies that is displayed in the money field settings
366 * form.
367 *
368 * @param $currencies
369 * An array of currencies, where the keys are the currency codes and the
370 * values are the full names, with bracketed currency codes appended. (An
371 * array returned by currency_api_get_list()).
372 * @return
373 * A rendered list of currencies.
374 */
375 function theme_money_field_settings_currency_list($currencies) {
376 $output = '';
377
378 $output .= '<div id="money-field-settings-currency-list">';
379 $output .= theme_item_list(array_values($currencies), t('Available currencies'));
380 $output .= '</div>';
381
382 return $output;
383 }
384
385 /**
386 * @} End of "ingroup themeable".
387 */

  ViewVC Help
Powered by ViewVC 1.1.2