Simpletest Coverage - modules/field/modules/options/options.module

1 <?php
2 // $Id: options.module,v 1.8 2009/08/01 06:03:12 dries Exp $
3
4 /**
5 * @file
6 * Defines selection, check box and radio button widgets for text and numeric fields.
7 */
8
9 /**
10 * Implement hook_theme().
11 */
12 function options_theme() {
13 return array(
14 'options_select' => array(
15 'arguments' => array('element' => NULL),
16 ),
17 'options_buttons' => array(
18 'arguments' => array('element' => NULL),
19 ),
20 'options_onoff' => array(
21 'arguments' => array('element' => NULL),
22 ),
23 'options_none' => array(
24 'arguments' => array('widget_type' => NULL, 'field_name' => NULL, 'node_type' => NULL),
25 ),
26 );
27 }
28
29 /**
30 * Implement hook_field_widget_info().
31 *
32 * We need custom handling of multiple values because we need
33 * to combine them into a options list rather than display
34 * cardinality elements. We will use the field module's default
35 * handling for default values.
36 *
37 * Callbacks can be omitted if default handing is used.
38 * They're included here just so this module can be used
39 * as an example for custom modules that might do things
40 * differently.
41 */
42 function options_field_widget_info() {
43
44 return array(
45 'options_select' => array(
46 'label' => t('Select list'),
47 'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
48 'behaviors' => array(
49 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
50 ),
51 ),
52 'options_buttons' => array(
53 'label' => t('Check boxes/radio buttons'),
54 'field types' => array('list', 'list_boolean', 'list_text', 'list_number'),
55 'behaviors' => array(
56 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
57 ),
58 ),
59 'options_onoff' => array(
60 'label' => t('Single on/off checkbox'),
61 'field types' => array('list_boolean'),
62 'behaviors' => array(
63 'multiple values' => FIELD_BEHAVIOR_CUSTOM,
64 ),
65 ),
66 );
67 }
68
69 /**
70 * Implement FAPI hook_elements().
71 *
72 * Any FAPI callbacks needed for individual widgets can be declared here,
73 * and the element will be passed to those callbacks for processing.
74 *
75 * Drupal will automatically theme the element using a theme with
76 * the same name as the hook_elements key.
77 */
78 function options_elements() {
79 return array(
80 'options_select' => array(
81 '#input' => TRUE,
82 '#columns' => array('value'), '#delta' => 0,
83 '#process' => array('options_select_elements_process'),
84 ),
85 'options_buttons' => array(
86 '#input' => TRUE,
87 '#columns' => array('value'), '#delta' => 0,
88 '#process' => array('options_buttons_elements_process'),
89 ),
90 'options_onoff' => array(
91 '#input' => TRUE,
92 '#columns' => array('value'), '#delta' => 0,
93 '#process' => array('options_onoff_elements_process'),
94 ),
95 );
96 }
97
98 /**
99 * Implement hook_field_widget().
100 */
101 function options_field_widget(&$form, &$form_state, $field, $instance, $items, $delta = NULL) {
102 $element = array(
103 '#type' => $instance['widget']['type'],
104 '#default_value' => !empty($items) ? $items : array(),
105 );
106 return $element;
107 }
108
109 /**
110 * Implement hook_field_widget_error().
111 */
112 function options_field_widget_error($element, $error) {
113 $field_key = $element['#columns'][0];
114 form_error($element[$field_key], $error['message']);
115 }
116
117 /**
118 * Process an individual element.
119 *
120 * Build the form element. When creating a form using FAPI #process,
121 * note that $element['#value'] is already set.
122 *
123 * The $field and $instance arrays are in $form['#fields'][$element['#field_name']].
124 */
125 function options_buttons_elements_process($element, &$form_state, $form) {
126 $field = $form['#fields'][$element['#field_name']]['field'];
127 $instance = $form['#fields'][$element['#field_name']]['instance'];
128 $field_key = $element['#columns'][0];
129
130 // See if this element is in the database format or the transformed format,
131 // and transform it if necessary.
132 if (is_array($element['#value']) && !array_key_exists($field_key, $element['#value'])) {
133 $element['#value'] = options_data2form($element, $element['#default_value'], $field);
134 }
135 $options = options_options($field, $instance);
136 $multiple = isset($element['#multiple']) ? $element['#multiple'] : $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
137
138 $value = array();
139 foreach ($element['#value'][$field_key] as $key) {
140 // Multiple (checkboxes) need the default value in the form of an array.
141 if ($multiple) {
142 $value[$key] = 1;
143 }
144 // Non-multiple (radios) need single default value.
145 else {
146 $value = $key;
147 break;
148 }
149 }
150
151 $element[$field_key] = array(
152 '#type' => $multiple ? 'checkboxes' : 'radios',
153 '#title' => $element['#title'],
154 '#description' => $element['#description'],
155 '#required' => isset($element['#required']) ? $element['#required'] : $instance['required'],
156 '#multiple' => $multiple,
157 '#options' => $options,
158 '#default_value' => $value,
159 );
160
161 // Set #element_validate in a way that it will not wipe out other
162 // validation functions already set by other modules.
163 if (empty($element['#element_validate'])) {
164 $element['#element_validate'] = array();
165 }
166 array_unshift($element['#element_validate'], 'options_validate');
167
168 // Make sure field info will be available to the validator which
169 // does not get the values in $form.
170 $form_state['#fields'][$element['#field_name']] = $form['#fields'][$element['#field_name']];
171 return $element;
172 }
173
174 /**
175 * Process an individual element.
176 *
177 * Build the form element. When creating a form using FAPI #process,
178 * note that $element['#value'] is already set.
179 *
180 * The $field and $instance arrays are in $form['#fields'][$element['#field_name']].
181 */
182 function options_select_elements_process($element, &$form_state, $form) {
183 $field = $form['#fields'][$element['#field_name']]['field'];
184 $instance = $form['#fields'][$element['#field_name']]['instance'];
185 $field_key = $element['#columns'][0];
186
187 // See if this element is in the database format or the transformed format,
188 // and transform it if necessary.
189 if (is_array($element['#value']) && !array_key_exists($field_key, $element['#value'])) {
190 $element['#value'] = options_data2form($element, $element['#default_value'], $field);
191 }
192
193 $options = options_options($field, $instance);
194 $element[$field_key] = array(
195 '#type' => 'select',
196 '#title' => $element['#title'],
197 '#description' => $element['#description'],
198 '#required' => isset($element['#required']) ? $element['#required'] : $instance['required'],
199 '#multiple' => isset($element['#multiple']) ? $element['#multiple'] : $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED,
200 '#options' => $options,
201 '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
202 );
203
204 // Set #element_validate in a way that it will not wipe out other
205 // validation functions already set by other modules.
206 if (empty($element['#element_validate'])) {
207 $element['#element_validate'] = array();
208 }
209 array_unshift($element['#element_validate'], 'options_validate');
210
211 // Make sure field info will be available to the validator which
212 // does not get the values in $form.
213 $form_state['#fields'][$element['#field_name']] = $form['#fields'][$element['#field_name']];
214 return $element;
215 }
216
217 /**
218 * Process an individual element.
219 *
220 * Build the form element. When creating a form using FAPI #process,
221 * note that $element['#value'] is already set.
222 */
223 function options_onoff_elements_process($element, &$form_state, $form) {
224 $field = $form['#fields'][$element['#field_name']]['field'];
225 $instance = $form['#fields'][$element['#field_name']]['instance'];
226 $field_key = $element['#columns'][0];
227
228 // See if this element is in the database format or the transformed format,
229 // and transform it if necessary.
230 if (is_array($element['#value']) && !array_key_exists($field_key, $element['#value'])) {
231 $element['#value'] = options_data2form($element, $element['#default_value'], $field);
232 }
233 $options = options_options($field, $instance);
234 $keys = array_keys($options);
235 $on_value = (!empty($keys) && isset($keys[1])) ? $keys[1] : NULL;
236 $element[$field_key] = array(
237 '#type' => 'checkbox',
238 '#title' => isset($options[$on_value]) ? $options[$on_value] : '',
239 '#description' => $element['#description'],
240 '#default_value' => isset($element['#value'][$field_key][0]) ? $element['#value'][$field_key][0] == $on_value : FALSE,
241 '#return_value' => $on_value,
242 );
243
244 // Set #element_validate in a way that it will not wipe out other
245 // validation functions already set by other modules.
246 if (empty($element['#element_validate'])) {
247 $element['#element_validate'] = array();
248 }
249 array_unshift($element['#element_validate'], 'options_validate');
250
251 // Make sure field info will be available to the validator which
252 // does not get the values in $form.
253 $form_state['#fields'][$element['#field_name']] = $form['#fields'][$element['#field_name']];
254 return $element;
255 }
256
257 /**
258 * FAPI function to validate options element.
259 */
260 function options_validate($element, &$form_state) {
261 // Transpose selections from field => delta to delta => field,
262 // turning cardinality selected options into cardinality parent elements.
263 // Immediate parent is the delta, need to get back to parent's parent
264 // to create cardinality elements.
265 $field = $form_state['#fields'][$element['#field_name']]['field'];
266 $items = options_form2data($element, $field);
267 form_set_value($element, $items, $form_state);
268
269 // Check we don't exceed the allowed number of values.
270 if ($field['cardinality'] >= 2) {
271 // Filter out 'none' value (if present, will always be in key 0)
272 $field_key = $element['#columns'][0];
273 if (isset($items[0][$field_key]) && $items[0][$field_key] === '') {
274 unset($items[0]);
275 }
276 if (count($items) > $field['cardinality']) {
277 $field_key = $element['#columns'][0];
278 form_error($element[$field_key], t('%name: this field cannot hold more that @count values.', array('%name' => t($field['widget']['label']), '@count' => $field['cardinality'])));
279 }
280 }
281 }
282
283 /**
284 * Helper function to transpose the values as stored in the database
285 * to the format the widget needs. Can be called anywhere this
286 * transformation is needed.
287 */
288 function options_data2form($element, $items, $field) {
289 $field_key = $element['#columns'][0];
290 $field = field_info_field($element['#field_name']);
291 $instance = field_info_instance($element['#field_name'], $element['#bundle']);
292 $options = options_options($field, $instance);
293
294 $items_transposed = options_transpose_array_rows_cols($items);
295 $values = (isset($items_transposed[$field_key]) && is_array($items_transposed[$field_key])) ? $items_transposed[$field_key] : array();
296 $keys = array();
297 foreach ($values as $value) {
298 $key = array_search($value, array_keys($options));
299 if (isset($key)) {
300 $keys[] = $value;
301 }
302 }
303 if ($field['cardinality'] || $element['#type'] == 'options_onoff') {
304 return array($field_key => $keys);
305 }
306 else {
307 return !empty($keys) ? array($field_key => $value) : array();
308 }
309 }
310
311 /**
312 * Helper function to transpose the values returned by submitting the widget
313 * to the format to be stored in the field. Can be called anywhere this
314 * transformation is needed.
315 */
316 function options_form2data($element, $field) {
317 $field_key = $element['#columns'][0];
318 $field = field_info_field($element['#field_name']);
319 $instance = field_info_instance($element['#field_name'], $element['#bundle']);
320 $items = (array) $element[$field_key]['#value'];
321 $options = options_options($field, $instance);
322
323 $values = array_values($items);
324
325 if ($element['#type'] == 'options_onoff' && ($values[0] === 0)) {
326 $keys = array_keys($options);
327 $values = array(array_key_exists(0, $keys) ? $keys[0] : NULL);
328 }
329
330 if (empty($values)) {
331 $values[] = NULL;
332 }
333 $result = options_transpose_array_rows_cols(array($field_key => $values));
334 return $result;
335 }
336
337 /**
338 * Manipulate a 2D array to reverse rows and columns.
339 *
340 * The default data storage for fields is delta first, column names second.
341 * This is sometimes inconvenient for field modules, so this function can be
342 * used to present the data in an alternate format.
343 *
344 * @param $array
345 * The array to be transposed. It must be at least two-dimensional, and
346 * the subarrays must all have the same keys or behavior is undefined.
347 * @return
348 * The transposed array.
349 */
350 function options_transpose_array_rows_cols($array) {
351 $result = array();
352 if (is_array($array)) {
353 foreach ($array as $key1 => $value1) {
354 if (is_array($value1)) {
355 foreach ($value1 as $key2 => $value2) {
356 if (!isset($result[$key2])) {
357 $result[$key2] = array();
358 }
359 $result[$key2][$key1] = $value2;
360 }
361 }
362 }
363 }
364 return $result;
365 }
366
367 /**
368 * Helper function for finding the allowed values list for a field.
369 *
370 * See if there is a module hook for the option values.
371 * Otherwise, try list_allowed_values() for an options list.
372 */
373 function options_options($field, $instance) {
374 $function = $field['module'] . '_allowed_values';
375 $options = function_exists($function) ? $function($field) : (array) list_allowed_values($field);
376 // Add an empty choice for :
377 // - non required radios
378 // - non required selects
379 if (!$instance['required']) {
380 if ((in_array($instance['widget']['type'], array('options_buttons', 'node_reference_buttons', 'user_reference_buttons')) && !$field['cardinality'])
381 || (in_array($instance['widget']['type'], array('options_select', 'node_reference_select', 'user_reference_select')))) {
382 $options = array('' => theme('options_none', $instance)) + $options;
383 }
384 }
385 return $options;
386 }
387
388 /**
389 * Theme the label for the empty value for options that are not required.
390 * The default theme will display N/A for a radio list and blank for a select.
391 */
392 function theme_options_none($instance) {
393 switch ($instance['widget']['type']) {
394 case 'options_buttons':
395 case 'node_reference_buttons':
396 case 'user_reference_buttons':
397 return t('N/A');
398 case 'options_select':
399 case 'node_reference_select':
400 case 'user_reference_select':
401 return t('- None -');
402 default :
403 return '';
404 }
405 }
406
407 /**
408 * FAPI themes for options.
409 *
410 * The select, checkboxes or radios are already rendered by the
411 * select, checkboxes, or radios themes and the HTML output
412 * lives in $element['#children']. Override this theme to
413 * make custom changes to the output.
414 *
415 * $element['#field_name'] contains the field name
416 * $element['#delta] is the position of this element in the group
417 */
418 function theme_options_select($element) {
419 return $element['#children'];
420 }
421
422 function theme_options_onoff($element) {
423 return $element['#children'];
424 }
425
426 function theme_options_buttons($element) {
427 return $element['#children'];
428 }

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.