' . t('About') . ''; $output .= '

' . t('The Number module defines various numeric field types for the Field module. Numbers can be in integer, decimal, or floating-point form, and they can be formatted when displayed. Number fields can be limited to a specific set of input values or to a range of values. See the Field module help page for more information about fields.', array('@field-help' => url('admin/help/field'))) . '

'; return $output; } } /** * Implements hook_field_info(). */ function number_field_info() { return array( 'number_integer' => array( 'label' => t('Integer'), 'description' => t('This field stores a number in the database as an integer.'), 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''), 'default_widget' => 'number', 'default_formatter' => 'number_integer', ), 'number_decimal' => array( 'label' => t('Decimal'), 'description' => t('This field stores a number in the database in a fixed decimal format.'), 'settings' => array('precision' => 10, 'scale' => 2, 'decimal_separator' => '.'), 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''), 'default_widget' => 'number', 'default_formatter' => 'number_decimal', ), 'number_float' => array( 'label' => t('Float'), 'description' => t('This field stores a number in the database in a floating point format.'), 'settings' => array('decimal_separator' => '.'), 'instance_settings' => array('min' => '', 'max' => '', 'prefix' => '', 'suffix' => ''), 'default_widget' => 'number', 'default_formatter' => 'number_decimal', ), ); } /** * Implements hook_field_settings_form(). */ function number_field_settings_form($field, $instance, $has_data) { $settings = $field['settings']; $form = array(); if ($field['type'] == 'number_decimal') { $form['precision'] = array( '#type' => 'select', '#title' => t('Precision'), '#options' => drupal_map_assoc(range(10, 32)), '#default_value' => $settings['precision'], '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'), '#disabled' => $has_data, ); $form['scale'] = array( '#type' => 'select', '#title' => t('Scale'), '#options' => drupal_map_assoc(range(0, 10)), '#default_value' => $settings['scale'], '#description' => t('The number of digits to the right of the decimal.'), '#disabled' => $has_data, ); } if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') { $form['decimal_separator'] = array( '#type' => 'select', '#title' => t('Decimal marker'), '#options' => array('.' => t('Decimal point'), ',' => t('Comma')), '#default_value' => $settings['decimal_separator'], '#description' => t('The character users will input to mark the decimal point in forms.'), ); } return $form; } /** * Implements hook_field_instance_settings_form(). */ function number_field_instance_settings_form($field, $instance) { $settings = $instance['settings']; $form['min'] = array( '#type' => 'textfield', '#title' => t('Minimum'), '#default_value' => $settings['min'], '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'), '#element_validate' => array('element_validate_number'), ); $form['max'] = array( '#type' => 'textfield', '#title' => t('Maximum'), '#default_value' => $settings['max'], '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'), '#element_validate' => array('element_validate_number'), ); $form['prefix'] = array( '#type' => 'textfield', '#title' => t('Prefix'), '#default_value' => $settings['prefix'], '#size' => 60, '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '€ '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), ); $form['suffix'] = array( '#type' => 'textfield', '#title' => t('Suffix'), '#default_value' => $settings['suffix'], '#size' => 60, '#description' => t("Define a string that should be suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), ); return $form; } /** * Implements hook_field_validate(). * * Possible error codes: * - 'number_min': The value is less than the allowed minimum value. * - 'number_max': The value is greater than the allowed maximum value. */ function number_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { foreach ($items as $delta => $item) { if ($item['value'] != '') { if (is_numeric($instance['settings']['min']) && $item['value'] < $instance['settings']['min']) { $errors[$field['field_name']][$langcode][$delta][] = array( 'error' => 'number_min', 'message' => t('%name: the value may be no less than %min.', array('%name' => $instance['label'], '%min' => $instance['settings']['min'])), ); } if (is_numeric($instance['settings']['max']) && $item['value'] > $instance['settings']['max']) { $errors[$field['field_name']][$langcode][$delta][] = array( 'error' => 'number_max', 'message' => t('%name: the value may be no greater than %max.', array('%name' => $instance['label'], '%max' => $instance['settings']['max'])), ); } } } } /** * Implements hook_field_presave(). */ function number_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { if ($field['type'] == 'number_decimal') { // Let PHP round the value to ensure consistent behavior across storage // backends. foreach ($items as $delta => $item) { if (isset($item['value'])) { $items[$delta]['value'] = round($item['value'], $field['settings']['scale']); } } } } /** * Implements hook_field_is_empty(). */ function number_field_is_empty($item, $field) { if (empty($item['value']) && (string) $item['value'] !== '0') { return TRUE; } return FALSE; } /** * Implements hook_field_formatter_info(). */ function number_field_formatter_info() { return array( // The 'Default' formatter is different for integer fields on the one hand, // and for decimal and float fields on the other hand, in order to be able // to use different default values for the settings. 'number_integer' => array( 'label' => t('Default'), 'field types' => array('number_integer'), 'settings' => array( 'thousand_separator' => '', // The 'decimal_separator' and 'scale' settings are not configurable // through the UI, and will therefore keep their default values. They // are only present so that the 'number_integer' and 'number_decimal' // formatters can use the same code. 'decimal_separator' => '.', 'scale' => 0, 'prefix_suffix' => TRUE, ), ), 'number_decimal' => array( 'label' => t('Default'), 'field types' => array('number_decimal', 'number_float'), 'settings' => array( 'thousand_separator' => '', 'decimal_separator' => '.', 'scale' => 2, 'prefix_suffix' => TRUE, ), ), 'number_unformatted' => array( 'label' => t('Unformatted'), 'field types' => array('number_integer', 'number_decimal', 'number_float'), ), ); } /** * Implements hook_field_formatter_settings_form(). */ function number_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; $element = array(); if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') { $options = array( '' => t(''), '.' => t('Decimal point'), ',' => t('Comma'), ' ' => t('Space'), ); $element['thousand_separator'] = array( '#type' => 'select', '#title' => t('Thousand marker'), '#options' => $options, '#default_value' => $settings['thousand_separator'], ); if ($display['type'] == 'number_decimal') { $element['decimal_separator'] = array( '#type' => 'select', '#title' => t('Decimal marker'), '#options' => array('.' => t('Decimal point'), ',' => t('Comma')), '#default_value' => $settings['decimal_separator'], ); $element['scale'] = array( '#type' => 'select', '#title' => t('Scale'), '#options' => drupal_map_assoc(range(0, 10)), '#default_value' => $settings['scale'], '#description' => t('The number of digits to the right of the decimal.'), ); } $element['prefix_suffix'] = array( '#type' => 'checkbox', '#title' => t('Display prefix and suffix.'), '#default_value' => $settings['prefix_suffix'], ); } return $element; } /** * Implements hook_field_formatter_settings_summary(). */ function number_field_formatter_settings_summary($field, $instance, $view_mode) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; $summary = array(); if ($display['type'] == 'number_decimal' || $display['type'] == 'number_integer') { $summary[] = number_format(1234.1234567890, $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']); if ($settings['prefix_suffix']) { $summary[] = t('Display with prefix and suffix.'); } } return implode('
', $summary); } /** * Implements hook_field_formatter_view(). */ function number_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $element = array(); $settings = $display['settings']; switch ($display['type']) { case 'number_integer': case 'number_decimal': foreach ($items as $delta => $item) { $output = number_format($item['value'], $settings['scale'], $settings['decimal_separator'], $settings['thousand_separator']); if ($settings['prefix_suffix']) { $prefixes = isset($instance['settings']['prefix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['prefix'])) : array(''); $suffixes = isset($instance['settings']['suffix']) ? array_map('field_filter_xss', explode('|', $instance['settings']['suffix'])) : array(''); $prefix = (count($prefixes) > 1) ? format_plural($item['value'], $prefixes[0], $prefixes[1]) : $prefixes[0]; $suffix = (count($suffixes) > 1) ? format_plural($item['value'], $suffixes[0], $suffixes[1]) : $suffixes[0]; $output = $prefix . $output . $suffix; } $element[$delta] = array('#markup' => $output); } break; case 'number_unformatted': foreach ($items as $delta => $item) { $element[$delta] = array('#markup' => $item['value']); } break; } return $element; } /** * Implements hook_field_widget_info(). */ function number_field_widget_info() { return array( 'number' => array( 'label' => t('Text field'), 'field types' => array('number_integer', 'number_decimal', 'number_float'), ), ); } /** * Implements hook_field_widget_form(). */ function number_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : ''; // Substitute the decimal separator. if ($field['type'] == 'number_decimal' || $field['type'] == 'number_float') { $value = strtr($value, '.', $field['settings']['decimal_separator']); } $element += array( '#type' => 'textfield', '#default_value' => $value, // Allow a slightly larger size that the field length to allow for some // configurations where all characters won't fit in input field. '#size' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 4 : 12, // Allow two extra characters for signed values and decimal separator. '#maxlength' => $field['type'] == 'number_decimal' ? $field['settings']['precision'] + 2 : 10, // Extract the number type from the field type name for easier validation. '#number_type' => str_replace('number_', '', $field['type']), ); // Add prefix and suffix. if (!empty($instance['settings']['prefix'])) { $prefixes = explode('|', $instance['settings']['prefix']); $element['#field_prefix'] = field_filter_xss(array_pop($prefixes)); } if (!empty($instance['settings']['suffix'])) { $suffixes = explode('|', $instance['settings']['suffix']); $element['#field_suffix'] = field_filter_xss(array_pop($suffixes)); } $element['#element_validate'][] = 'number_field_widget_validate'; return array('value' => $element); } /** * FAPI validation of an individual number element. */ function number_field_widget_validate($element, &$form_state) { $field = field_widget_field($element, $form_state); $instance = field_widget_instance($element, $form_state); $type = $element['#number_type']; $value = $element['#value']; // Reject invalid characters. if (!empty($value)) { switch ($type) { case 'float': case 'decimal': $regexp = '@([^-0-9\\' . $field['settings']['decimal_separator'] . '])|(.-)@'; $message = t('Only numbers and the decimal separator (@separator) allowed in %field.', array('%field' => $instance['label'], '@separator' => $field['settings']['decimal_separator'])); break; case 'integer': $regexp = '@([^-0-9])|(.-)@'; $message = t('Only numbers are allowed in %field.', array('%field' => $instance['label'])); break; } if ($value != preg_replace($regexp, '', $value)) { form_error($element, $message); } else { if ($type == 'decimal' || $type == 'float') { // Verify that only one decimal separator exists in the field. if (substr_count($value, $field['settings']['decimal_separator']) > 1) { $message = t('%field: There should only be one decimal separator (@separator).', array( '%field' => t($instance['label']), '@separator' => $field['settings']['decimal_separator'], ) ); form_error($element, $message); } else { // Substitute the decimal separator; things should be fine. $value = strtr($value, $field['settings']['decimal_separator'], '.'); } } form_set_value($element, $value, $form_state); } } } /** * Implements hook_field_widget_error(). */ function number_field_widget_error($element, $error, $form, &$form_state) { form_error($element['value'], $error['message']); }