- Patch #594518 by jhodgdon: better documentation for file_munge_filename().
[project/drupal.git] / modules / field / modules / list / list.module
CommitLineData
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 */
12function 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 48function 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 95function 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 */
136function 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 */
170function 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 */
198function 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 223function 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 */
240function 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 */
250function 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
266function 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
279function theme_field_formatter_list_key($variables) {
280 $element = $variables['element'];
f3ed3283
AB
281 return $element['#item']['safe'];
282}