| Commit | Line | Data |
|---|---|---|
| f3ed3283 AB |
1 | <?php |
| 2 | // $Id$ | |
| 3 | ||
| 4 | /** | |
| 5 | * @file | |
| 6 | * Defines list field types that can be used with the Options module. | |
| 7 | */ | |
| 8 | ||
| 9 | /** | |
| 0f4060f3 | 10 | * Implement hook_field_info(). |
| f3ed3283 AB |
11 | */ |
| 12 | function list_field_info() { | |
| 13 | return array( | |
| 14 | 'list' => array( | |
| 15 | 'label' => t('List'), | |
| 16 | 'description' => t('This field stores numeric keys from key/value lists of allowed values where the key is a simple alias for the position of the value, i.e. 0|First option, 1|Second option, 2|Third option.'), | |
| e998857e | 17 | 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), |
| f3ed3283 AB |
18 | 'default_widget' => 'options_select', |
| 19 | 'default_formatter' => 'list_default', | |
| 20 | ), | |
| 21 | 'list_boolean' => array( | |
| 22 | 'label' => t('Boolean'), | |
| 23 | 'description' => t('This field stores simple on/off or yes/no options.'), | |
| e998857e | 24 | 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), |
| f3ed3283 AB |
25 | 'default_widget' => 'options_select', |
| 26 | 'default_formatter' => 'list_default', | |
| 27 | ), | |
| 28 | 'list_number' => array( | |
| 29 | 'label' => t('List (numeric)'), | |
| 30 | 'description' => t('This field stores keys from key/value lists of allowed numbers where the stored numeric key has significance and must be preserved, i.e. \'Lifetime in days\': 1|1 day, 7|1 week, 31|1 month.'), | |
| e998857e | 31 | 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), |
| f3ed3283 AB |
32 | 'default_widget' => 'options_select', |
| 33 | 'default_formatter' => 'list_default', | |
| 34 | ), | |
| 35 | 'list_text' => array( | |
| 36 | 'label' => t('List (text)'), | |
| 37 | 'description' => t('This field stores keys from key/value lists of allowed values where the stored key has significance and must be a varchar, i.e. \'US States\': IL|Illinois, IA|Iowa, IN|Indiana'), | |
| e998857e | 38 | 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), |
| f3ed3283 AB |
39 | 'default_widget' => 'options_select', |
| 40 | 'default_formatter' => 'list_default', | |
| 41 | ), | |
| 42 | ); | |
| 43 | } | |
| 44 | ||
| 45 | /** | |
| 0f4060f3 | 46 | * Implement hook_field_schema(). |
| f3ed3283 | 47 | */ |
| ba29dbde | 48 | function list_field_schema($field) { |
| f3ed3283 AB |
49 | switch ($field['type']) { |
| 50 | case 'list_text': | |
| 51 | $columns = array( | |
| 52 | 'value' => array( | |
| 53 | 'type' => 'varchar', | |
| 54 | 'length' => 255, | |
| 55 | 'not null' => FALSE, | |
| 56 | ), | |
| 57 | ); | |
| 58 | break; | |
| 59 | case 'list_number': | |
| 60 | $columns = array( | |
| 61 | 'value' => array( | |
| 62 | 'type' => 'float', | |
| 63 | 'unsigned' => TRUE, | |
| 64 | 'not null' => FALSE, | |
| 65 | ), | |
| 66 | ); | |
| 67 | break; | |
| 68 | default: | |
| 69 | $columns = array( | |
| 70 | 'value' => array( | |
| 71 | 'type' => 'int', | |
| 72 | 'unsigned' => TRUE, | |
| 73 | 'not null' => FALSE, | |
| 74 | ), | |
| 75 | ); | |
| 76 | break; | |
| 77 | } | |
| ba29dbde DB |
78 | return array( |
| 79 | 'columns' => $columns, | |
| 80 | 'indexes' => array( | |
| 81 | 'value' => array('value'), | |
| 82 | ), | |
| 83 | ); | |
| f3ed3283 AB |
84 | } |
| 85 | ||
| 86 | /** | |
| e998857e | 87 | * Implement hook_field_settings_form(). |
| db09a617 DB |
88 | * |
| 89 | * @todo: If $has_data, add a form validate function to verify that the | |
| 90 | * new allowed values do not exclude any keys for which data already | |
| 91 | * exists in the databae (use field_attach_query()) to find out. | |
| 92 | * Implement the validate function via hook_field_update_forbid() so | |
| 93 | * list.module does not depend on form submission. | |
| e998857e | 94 | */ |
| db09a617 | 95 | function list_field_settings_form($field, $instance, $has_data) { |
| e998857e AB |
96 | $settings = $field['settings']; |
| 97 | ||
| 98 | $form['allowed_values'] = array( | |
| 99 | '#type' => 'textarea', | |
| 100 | '#title' => t('Allowed values list'), | |
| 101 | '#default_value' => $settings['allowed_values'], | |
| 102 | '#required' => FALSE, | |
| 103 | '#rows' => 10, | |
| 104 | '#description' => '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and must be a %type value. The label is optional, and the key will be used as the label if no label is specified.', array('%type' => $field['type'] == 'list_text' ? t('text') : t('numeric'))) . '</p>', | |
| 105 | '#element_validate' => array('list_allowed_values_validate'), | |
| 106 | '#list_field_type' => $field['type'], | |
| 107 | '#access' => empty($settings['allowed_values_function']), | |
| 108 | ); | |
| 109 | ||
| 110 | // Alter the description for allowed values depending on the widget type. | |
| 111 | if ($instance['widget']['type'] == 'options_onoff') { | |
| 112 | $form['allowed_values']['#description'] .= '<p>' . t("For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the <strong>Allowed values</strong> section. Note that the checkbox will be labeled with the label of the 'on' value.") . '</p>'; | |
| 113 | } | |
| 114 | elseif ($instance['widget']['type'] == 'options_buttons') { | |
| 115 | $form['allowed_values']['#description'] .= '<p>' . t("The 'checkboxes/radio buttons' widget will display checkboxes if the <em>Number of values</em> option is greater than 1 for this field, otherwise radios will be displayed.") . '</p>'; | |
| 116 | } | |
| 117 | $form['allowed_values']['#description'] .= t('Allowed HTML tags in labels: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())); | |
| 118 | ||
| 119 | $form['allowed_values_function'] = array( | |
| 120 | '#type' => 'value', | |
| 121 | '#value' => $settings['allowed_values_function'], | |
| 122 | ); | |
| 123 | $form['allowed_values_function_display'] = array( | |
| 124 | '#type' => 'item', | |
| 125 | '#title' => t('Allowed values list'), | |
| 126 | '#markup' => t('The value of this field is being determined by the %function function and may not be changed.', array('%function' => $settings['allowed_values_function'])), | |
| 127 | '#access' => !empty($settings['allowed_values_function']), | |
| 128 | ); | |
| 129 | ||
| 130 | return $form; | |
| 131 | } | |
| 132 | ||
| 133 | /** | |
| 134 | * Create an array of allowed values for this field. | |
| 135 | */ | |
| 136 | function list_allowed_values($field) { | |
| b1cf3f1d | 137 | $allowed_values = &drupal_static(__FUNCTION__, array()); |
| e998857e AB |
138 | |
| 139 | if (isset($allowed_values[$field['field_name']])) { | |
| 140 | return $allowed_values[$field['field_name']]; | |
| 141 | } | |
| 142 | ||
| 143 | $allowed_values[$field['field_name']] = array(); | |
| 144 | ||
| 145 | $function = $field['settings']['allowed_values_function']; | |
| 2c552193 | 146 | if (!empty($function) && function_exists($function)) { |
| e998857e AB |
147 | $allowed_values[$field['field_name']] = $function($field); |
| 148 | } | |
| 149 | elseif (!empty($field['settings']['allowed_values'])) { | |
| 150 | $allowed_values[$field['field_name']] = list_allowed_values_list($field['settings']['allowed_values'], $field['type'] == 'list'); | |
| 151 | } | |
| 152 | ||
| 153 | return $allowed_values[$field['field_name']]; | |
| 154 | } | |
| 155 | ||
| 156 | /** | |
| 157 | * Create an array of the allowed values for this field. | |
| 158 | * | |
| 159 | * Explode a string with keys and labels separated with '|' and with each new | |
| 160 | * value on its own line. | |
| 161 | * | |
| 162 | * @param $string_values | |
| 163 | * The list of choices as a string. | |
| 164 | * @param $position_keys | |
| 165 | * Boolean value indicating whether to generate keys based on the position of | |
| 166 | * the value if a key is not manually specified, effectively generating | |
| 167 | * integer-based keys. This should only be TRUE for fields that have a type of | |
| 168 | * "list". Otherwise the value will be used as the key if not specified. | |
| 169 | */ | |
| 170 | function list_allowed_values_list($string_values, $position_keys = FALSE) { | |
| 171 | $allowed_values = array(); | |
| 172 | ||
| 173 | $list = explode("\n", $string_values); | |
| 174 | $list = array_map('trim', $list); | |
| 175 | $list = array_filter($list, 'strlen'); | |
| 176 | foreach ($list as $key => $value) { | |
| 177 | // Sanitize the user input with a permissive filter. | |
| 178 | $value = field_filter_xss($value); | |
| 179 | ||
| 180 | // Check for a manually specified key. | |
| 181 | if (strpos($value, '|') !== FALSE) { | |
| 182 | list($key, $value) = explode('|', $value); | |
| 183 | } | |
| 184 | // Otherwise see if we need to use the value as the key. The "list" type | |
| 185 | // will automatically convert non-keyed lines to integers. | |
| 186 | elseif (!$position_keys) { | |
| 187 | $key = $value; | |
| 188 | } | |
| 189 | $allowed_values[$key] = (isset($value) && $value !== '') ? $value : $key; | |
| 190 | } | |
| 191 | ||
| 192 | return $allowed_values; | |
| 193 | } | |
| 194 | ||
| 195 | /** | |
| 196 | * Element validate callback; check that the entered values are valid. | |
| 197 | */ | |
| 198 | function list_allowed_values_validate($element, &$form_state) { | |
| 199 | $values = list_allowed_values_list($element['#value'], $element['#list_field_type'] == 'list'); | |
| 200 | $field_type = $element['#list_field_type']; | |
| 201 | foreach ($values as $key => $value) { | |
| 202 | if ($field_type == 'list_number' && !is_numeric($key)) { | |
| 203 | form_error($element, t('The entered available values are not valid. Each key must be a valid integer or decimal.')); | |
| 204 | break; | |
| 205 | } | |
| 206 | elseif ($field_type == 'list_text' && strlen($key) > 255) { | |
| 207 | form_error($element, t('The entered available values are not valid. Each key must be a string less than 255 characters.')); | |
| 208 | break; | |
| 209 | } | |
| 210 | elseif ($field_type == 'list' && (!preg_match('/^-?\d+$/', $key))) { | |
| 211 | form_error($element, t('The entered available values are not valid. All specified keys must be integers.')); | |
| 212 | break; | |
| 213 | } | |
| 214 | } | |
| 215 | } | |
| 216 | ||
| 217 | /** | |
| 0f4060f3 | 218 | * Implement hook_field_validate(). |
| eecab108 AB |
219 | * |
| 220 | * Possible error codes: | |
| 221 | * - 'list_illegal_value': The value is not part of the list of allowed values. | |
| f3ed3283 | 222 | */ |
| 34a8a369 | 223 | function list_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) { |
| f3ed3283 | 224 | $allowed_values = list_allowed_values($field); |
| eecab108 AB |
225 | foreach ($items as $delta => $item) { |
| 226 | if (!empty($item['value'])) { | |
| 227 | if (count($allowed_values) && !array_key_exists($item['value'], $allowed_values)) { | |
| 34a8a369 | 228 | $errors[$field['field_name']][$langcode][$delta][] = array( |
| eecab108 AB |
229 | 'error' => 'list_illegal_value', |
| 230 | 'message' => t('%name: illegal value.', array('%name' => t($instance['label']))), | |
| 231 | ); | |
| f3ed3283 AB |
232 | } |
| 233 | } | |
| 234 | } | |
| 235 | } | |
| 236 | ||
| 237 | /** | |
| 0f4060f3 | 238 | * Implement hook_field_is_empty(). |
| f3ed3283 AB |
239 | */ |
| 240 | function list_field_is_empty($item, $field) { | |
| 241 | if (empty($item['value']) && (string)$item['value'] !== '0') { | |
| 242 | return TRUE; | |
| 243 | } | |
| 244 | return FALSE; | |
| 245 | } | |
| 246 | ||
| 247 | /** | |
| 0f4060f3 | 248 | * Implement hook_field_formatter_info(). |
| f3ed3283 AB |
249 | */ |
| 250 | function list_field_formatter_info() { | |
| 251 | return array( | |
| 252 | 'list_default' => array( | |
| 253 | 'label' => t('Default'), | |
| 254 | 'field types' => array('list', 'list_boolean', 'list_text', 'list_number'), | |
| f3ed3283 AB |
255 | ), |
| 256 | 'list_key' => array( | |
| 257 | 'label' => t('Key'), | |
| 258 | 'field types' => array('list', 'list_boolean', 'list_text', 'list_number'), | |
| f3ed3283 AB |
259 | ), |
| 260 | ); | |
| 261 | } | |
| 262 | ||
| 263 | /** | |
| 264 | * Theme function for 'default' list field formatter. | |
| 265 | */ | |
| c05f2181 DB |
266 | function theme_field_formatter_list_default($variables) { |
| 267 | $element = $variables['element']; | |
| f3ed3283 AB |
268 | $field = field_info_field($element['#field_name']); |
| 269 | if (($allowed_values = list_allowed_values($field)) && isset($allowed_values[$element['#item']['value']])) { | |
| 270 | return $allowed_values[$element['#item']['value']]; | |
| 271 | } | |
| 272 | // If no match was found in allowed values, fall back to the key. | |
| 273 | return $element['#item']['safe']; | |
| 274 | } | |
| 275 | ||
| 276 | /** | |
| 277 | * Theme function for 'key' list field formatter. | |
| 278 | */ | |
| c05f2181 DB |
279 | function theme_field_formatter_list_key($variables) { |
| 280 | $element = $variables['element']; | |
| f3ed3283 AB |
281 | return $element['#item']['safe']; |
| 282 | } |