- Patch #748768 by fgm, yched: list_field_formatter_view() used incorrect variable.
[project/drupal.git] / modules / field / modules / list / list.module
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Defines list field types that can be used with the Options module.
7 */
8
9 /**
10 * Implements hook_help().
11 */
12 function list_help($path, $arg) {
13 switch ($path) {
14 case 'admin/help#list':
15 $output = '';
16 $output .= '<h3>' . t('About') . '</h3>';
17 $output .= '<p>' . t('The List module defines various fields for storing a list of items, for use with the Field module. Usually these items are entered through a select list, checkboxes, or radio buttons. See the <a href="@field-help">Field module help page</a> for more information about fields.', array('@field-help' => url('admin/help/field'))) . '</p>';
18 return $output;
19 }
20 }
21
22 /**
23 * Implements hook_field_info().
24 */
25 function list_field_info() {
26 return array(
27 'list' => array(
28 'label' => t('List'),
29 '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.'),
30 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''),
31 'default_widget' => 'options_select',
32 'default_formatter' => 'list_default',
33 ),
34 'list_boolean' => array(
35 'label' => t('Boolean'),
36 'description' => t('This field stores simple on/off or yes/no options.'),
37 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''),
38 'default_widget' => 'options_buttons',
39 'default_formatter' => 'list_default',
40 ),
41 'list_number' => array(
42 'label' => t('List (numeric)'),
43 '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.'),
44 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''),
45 'default_widget' => 'options_select',
46 'default_formatter' => 'list_default',
47 ),
48 'list_text' => array(
49 'label' => t('List (text)'),
50 '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'),
51 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''),
52 'default_widget' => 'options_select',
53 'default_formatter' => 'list_default',
54 ),
55 );
56 }
57
58 /**
59 * Implements hook_field_schema().
60 */
61 function list_field_schema($field) {
62 switch ($field['type']) {
63 case 'list_text':
64 $columns = array(
65 'value' => array(
66 'type' => 'varchar',
67 'length' => 255,
68 'not null' => FALSE,
69 ),
70 );
71 break;
72 case 'list_number':
73 $columns = array(
74 'value' => array(
75 'type' => 'float',
76 'unsigned' => TRUE,
77 'not null' => FALSE,
78 ),
79 );
80 break;
81 default:
82 $columns = array(
83 'value' => array(
84 'type' => 'int',
85 'unsigned' => TRUE,
86 'not null' => FALSE,
87 ),
88 );
89 break;
90 }
91 return array(
92 'columns' => $columns,
93 'indexes' => array(
94 'value' => array('value'),
95 ),
96 );
97 }
98
99 /**
100 * Implements hook_field_settings_form().
101 *
102 * @todo: If $has_data, add a form validate function to verify that the
103 * new allowed values do not exclude any keys for which data already
104 * exists in the databae (use field_attach_query()) to find out.
105 * Implement the validate function via hook_field_update_forbid() so
106 * list.module does not depend on form submission.
107 */
108 function list_field_settings_form($field, $instance, $has_data) {
109 $settings = $field['settings'];
110
111 $form['allowed_values'] = array(
112 '#type' => 'textarea',
113 '#title' => t('Allowed values list'),
114 '#default_value' => $settings['allowed_values'],
115 '#required' => FALSE,
116 '#rows' => 10,
117 '#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>',
118 '#element_validate' => array('list_allowed_values_setting_validate'),
119 '#list_field_type' => $field['type'],
120 '#access' => empty($settings['allowed_values_function']),
121 );
122
123 // Alter the description for allowed values depending on the widget type.
124 if ($instance['widget']['type'] == 'options_onoff') {
125 $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>';
126 }
127 elseif ($instance['widget']['type'] == 'options_buttons') {
128 $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>';
129 }
130 $form['allowed_values']['#description'] .= t('Allowed HTML tags in labels: @tags', array('@tags' => _field_filter_xss_display_allowed_tags()));
131
132 $form['allowed_values_function'] = array(
133 '#type' => 'value',
134 '#value' => $settings['allowed_values_function'],
135 );
136 $form['allowed_values_function_display'] = array(
137 '#type' => 'item',
138 '#title' => t('Allowed values list'),
139 '#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'])),
140 '#access' => !empty($settings['allowed_values_function']),
141 );
142
143 return $form;
144 }
145
146 /**
147 * Element validate callback; check that the entered values are valid.
148 */
149 function list_allowed_values_setting_validate($element, &$form_state) {
150 $values = list_extract_allowed_values($element['#value'], $element['#list_field_type'] == 'list');
151 $field_type = $element['#list_field_type'];
152
153 // Check that keys are valid for the field type.
154 foreach ($values as $key => $value) {
155 if ($field_type == 'list_number' && !is_numeric($key)) {
156 form_error($element, t('Allowed values list: each key must be a valid integer or decimal.'));
157 break;
158 }
159 elseif ($field_type == 'list_text' && drupal_strlen($key) > 255) {
160 form_error($element, t('Allowed values list: each key must be a string at most 255 characters long.'));
161 break;
162 }
163 elseif ($field_type == 'list' && !preg_match('/^-?\d+$/', $key)) {
164 form_error($element, t('Allowed values list: keys must be integers.'));
165 break;
166 }
167 elseif ($field_type == 'list_boolean' && !in_array($key, array('0', '1'))) {
168 form_error($element, t('Allowed values list: keys must be either 0 or 1.'));
169 break;
170 }
171 }
172
173 // Check that boolean fields get two values.
174 if ($field_type == 'list_boolean' && count($values) != 2) {
175 form_error($element, t('Allowed values list: two values are required.'));
176 }
177 }
178
179 /**
180 * Implements hook_field_update_field().
181 */
182 function list_field_update_field($field, $prior_field, $has_data) {
183 drupal_static_reset('list_allowed_values');
184 }
185
186 /**
187 * Returns the set of allowed values for a list field.
188 *
189 * The strings are not safe for output. Keys and values of the array should be
190 * sanitized through field_filter_xss() before being displayed.
191 *
192 * @param $field
193 * The field definition.
194 *
195 * @return
196 * The array of allowed values. Keys of the array are the raw stored values
197 * (integer or text), values of the array are the display aliases.
198 */
199 function list_allowed_values($field) {
200 $allowed_values = &drupal_static(__FUNCTION__, array());
201
202 if (!isset($allowed_values[$field['id']])) {
203 $values = array();
204
205 $function = $field['settings']['allowed_values_function'];
206 if (!empty($function) && function_exists($function)) {
207 $values = $function($field);
208 }
209 elseif (!empty($field['settings']['allowed_values'])) {
210 $position_keys = $field['type'] == 'list';
211 $values = list_extract_allowed_values($field['settings']['allowed_values'], $position_keys);
212 }
213
214 $allowed_values[$field['id']] = $values;
215 }
216
217 return $allowed_values[$field['id']];
218 }
219
220 /**
221 * Generates an array of values from a string.
222 *
223 * Explode a string with keys and labels separated with '|' and with each new
224 * value on its own line.
225 *
226 * @param $string_values
227 * The list of choices as a string, in the format expected by the
228 * 'allowed_values' setting:
229 * - Values are separated by a carriage return.
230 * - Each value is in the format "value|label" or "value".
231 * @param $position_keys
232 * Boolean value indicating whether to generate keys based on the position of
233 * the value if a key is not manually specified, effectively generating
234 * integer-based keys. This should only be TRUE for fields that have a type of
235 * "list". Otherwise the value will be used as the key if not specified.
236 */
237 function list_extract_allowed_values($string_values, $position_keys = FALSE) {
238 $values = array();
239
240 $list = explode("\n", $string_values);
241 $list = array_map('trim', $list);
242 $list = array_filter($list, 'strlen');
243 foreach ($list as $key => $value) {
244 // Check for a manually specified key.
245 if (strpos($value, '|') !== FALSE) {
246 list($key, $value) = explode('|', $value);
247 }
248 // Otherwise see if we need to use the value as the key. The "list" type
249 // will automatically convert non-keyed lines to integers.
250 elseif (!$position_keys) {
251 $key = $value;
252 }
253 $values[$key] = (isset($value) && $value !== '') ? $value : $key;
254 }
255
256 return $values;
257 }
258
259 /**
260 * Implements hook_field_validate().
261 *
262 * Possible error codes:
263 * - 'list_illegal_value': The value is not part of the list of allowed values.
264 */
265 function list_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
266 $allowed_values = list_allowed_values($field);
267 foreach ($items as $delta => $item) {
268 if (!empty($item['value'])) {
269 if (count($allowed_values) && !array_key_exists($item['value'], $allowed_values)) {
270 $errors[$field['field_name']][$langcode][$delta][] = array(
271 'error' => 'list_illegal_value',
272 'message' => t('%name: illegal value.', array('%name' => t($instance['label']))),
273 );
274 }
275 }
276 }
277 }
278
279 /**
280 * Implements hook_field_is_empty().
281 */
282 function list_field_is_empty($item, $field) {
283 if (empty($item['value']) && (string)$item['value'] !== '0') {
284 return TRUE;
285 }
286 return FALSE;
287 }
288
289 /**
290 * Implements hook_field_widget_info_alter().
291 *
292 * The List module does not implement widgets of its own, but reuses the
293 * widgets defined in options.module.
294 *
295 * @see list_options_list().
296 */
297 function list_field_widget_info_alter(&$info) {
298 $widgets = array(
299 'options_select' => array('list', 'list_text', 'list_number'),
300 'options_buttons' => array('list', 'list_text', 'list_number', 'list_boolean'),
301 'options_onoff' => array('list_boolean'),
302 );
303
304 foreach ($widgets as $widget => $field_types) {
305 $info[$widget]['field types'] = array_merge($info[$widget]['field types'], $field_types);
306 }
307 }
308
309 /**
310 * Implements hook_options_list().
311 */
312 function list_options_list($field) {
313 return list_allowed_values($field);
314 }
315
316 /**
317 * Implements hook_field_formatter_info().
318 */
319 function list_field_formatter_info() {
320 return array(
321 'list_default' => array(
322 'label' => t('Default'),
323 'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
324 ),
325 'list_key' => array(
326 'label' => t('Key'),
327 'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
328 ),
329 );
330 }
331
332 /**
333 * Implements hook_field_formatter_view().
334 */
335 function list_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
336 $element = array();
337
338 switch ($display['type']) {
339 case 'list_default':
340 $allowed_values = list_allowed_values($field);
341 foreach ($items as $delta => $item) {
342 if (isset($allowed_values[$item['value']])) {
343 $output = field_filter_xss($allowed_values[$item['value']]);
344 }
345 else {
346 // If no match was found in allowed values, fall back to the key.
347 $output = field_filter_xss($item['value']);
348 }
349 $element[$delta] = array('#markup' => $output);
350 }
351 break;
352
353 case 'list_key':
354 foreach ($items as $delta => $item) {
355 $element[$delta] = array('#markup' => field_filter_xss($item['value']));
356 }
357 break;
358 }
359
360 return $element;
361 }