Mercurial > hg > rr-repo
comparison sites/all/modules/webform/components/select.inc @ 0:ff03f76ab3fe
initial version
author | danieleb <danielebarchiesi@me.com> |
---|---|
date | Wed, 21 Aug 2013 18:51:11 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:ff03f76ab3fe |
---|---|
1 <?php | |
2 | |
3 /** | |
4 * @file | |
5 * Webform module multiple select component. | |
6 */ | |
7 | |
8 /** | |
9 * Implements _webform_defaults_component(). | |
10 */ | |
11 function _webform_defaults_select() { | |
12 return array( | |
13 'name' => '', | |
14 'form_key' => NULL, | |
15 'mandatory' => 0, | |
16 'pid' => 0, | |
17 'weight' => 0, | |
18 'value' => '', | |
19 'extra' => array( | |
20 'items' => '', | |
21 'multiple' => NULL, | |
22 'aslist' => NULL, | |
23 'optrand' => 0, | |
24 'other_option' => NULL, | |
25 'other_text' => t('Other...'), | |
26 'title_display' => 0, | |
27 'description' => '', | |
28 'custom_keys' => FALSE, | |
29 'options_source' => '', | |
30 'private' => FALSE, | |
31 ), | |
32 ); | |
33 } | |
34 | |
35 /** | |
36 * Implements _webform_theme_component(). | |
37 */ | |
38 function _webform_theme_select() { | |
39 return array( | |
40 'webform_display_select' => array( | |
41 'render element' => 'element', | |
42 'file' => 'components/select.inc', | |
43 ), | |
44 ); | |
45 } | |
46 | |
47 /** | |
48 * Implements _webform_edit_component(). | |
49 */ | |
50 function _webform_edit_select($component) { | |
51 $form = array( | |
52 '#attached' => array( | |
53 'js' => array( | |
54 drupal_get_path('module', 'webform') . '/js/select-admin.js' => array('preprocess' => FALSE), | |
55 array('data' => array('webform' => array('selectOptionsUrl' => url('webform/ajax/options/' . $component['nid']))), 'type' => 'setting'), | |
56 ), | |
57 ), | |
58 ); | |
59 | |
60 $other = array(); | |
61 if ($info = _webform_select_options_info()) { | |
62 $options = array('' => t('None')); | |
63 foreach ($info as $name => $source) { | |
64 $options[$name] = $source['title']; | |
65 } | |
66 | |
67 $other['options_source'] = array( | |
68 '#title' => t('Load a pre-built option list'), | |
69 '#type' => 'select', | |
70 '#options' => $options, | |
71 '#default_value' => $component['extra']['options_source'], | |
72 '#weight' => 1, | |
73 '#description' => t('Use a pre-built list of options rather than entering options manually. Options will not be editable if using pre-built list.'), | |
74 '#parents' => array('extra', 'options_source'), | |
75 '#weight' => 5, | |
76 ); | |
77 } | |
78 | |
79 if (module_exists('select_or_other')) { | |
80 $other['other_option'] = array( | |
81 '#type' => 'checkbox', | |
82 '#title' => t('Allow "Other..." option'), | |
83 '#default_value' => $component['extra']['other_option'], | |
84 '#description' => t('Check this option if you want to allow users to enter an option not on the list.'), | |
85 '#parents' => array('extra', 'other_option'), | |
86 '#weight' => 2, | |
87 ); | |
88 $other['other_text'] = array( | |
89 '#type' => 'textfield', | |
90 '#title' => t('Text for "Other..." option'), | |
91 '#default_value' => $component['extra']['other_text'], | |
92 '#description' => t('If allowing other options, enter text to be used for other-enabling option.'), | |
93 '#parents' => array('extra', 'other_text'), | |
94 '#weight' => 3, | |
95 ); | |
96 } | |
97 | |
98 if (module_exists('options_element')) { | |
99 $options = _webform_select_options($component, FALSE, FALSE); | |
100 | |
101 $form['items'] = array( | |
102 '#type' => 'fieldset', | |
103 '#title' => t('Options'), | |
104 '#collapsible' => TRUE, | |
105 '#attributes' => array('class' => array('webform-options-element')), | |
106 '#element_validate' => array('_webform_edit_validate_options'), | |
107 '#weight' => 2, | |
108 ); | |
109 | |
110 $form['items']['options'] = array( | |
111 '#type' => 'options', | |
112 '#limit' => 500, | |
113 '#optgroups' => $component['extra']['aslist'], | |
114 '#multiple' => $component['extra']['multiple'], | |
115 '#multiple_toggle' => t('Multiple'), | |
116 '#default_value' => $component['value'], | |
117 '#options' => $options, | |
118 '#options_readonly' => !empty($component['extra']['options_source']), | |
119 '#key_type' => 'mixed', | |
120 '#key_type_toggle' => t('Customize keys (Advanced)'), | |
121 '#key_type_toggled' => $component['extra']['custom_keys'], | |
122 '#default_value_pattern' => '^%.+\[.+\]$', | |
123 '#weight' => 1, | |
124 ); | |
125 | |
126 $form['items']['options']['option_settings'] = $other; | |
127 } | |
128 else { | |
129 $form['extra']['items'] = array( | |
130 '#type' => 'textarea', | |
131 '#title' => t('Options'), | |
132 '#default_value' => $component['extra']['items'], | |
133 '#description' => t('<strong>Key-value pairs MUST be specified as "safe_key|Some readable option"</strong>. Use of only alphanumeric characters and underscores is recommended in keys. One option per line. Option groups may be specified with <Group Name>. <> can be used to insert items at the root of the menu after specifying a group.') . theme('webform_token_help'), | |
134 '#cols' => 60, | |
135 '#rows' => 5, | |
136 '#weight' => 0, | |
137 '#required' => TRUE, | |
138 '#wysiwyg' => FALSE, | |
139 '#element_validate' => array('_webform_edit_validate_select'), | |
140 ); | |
141 | |
142 if (!empty($component['extra']['options_source'])) { | |
143 $form['extra']['items']['#attributes'] = array('readonly' => 'readonly'); | |
144 } | |
145 | |
146 $form['extra'] = array_merge($form['extra'], $other); | |
147 $form['value'] = array( | |
148 '#type' => 'textfield', | |
149 '#title' => t('Default value'), | |
150 '#default_value' => $component['value'], | |
151 '#description' => t('The default value of the field identified by its key. For multiple selects use commas to separate multiple defaults.') . theme('webform_token_help'), | |
152 '#size' => 60, | |
153 '#maxlength' => 1024, | |
154 '#weight' => 0, | |
155 ); | |
156 $form['extra']['multiple'] = array( | |
157 '#type' => 'checkbox', | |
158 '#title' => t('Multiple'), | |
159 '#default_value' => $component['extra']['multiple'], | |
160 '#description' => t('Check this option if the user should be allowed to choose multiple values.'), | |
161 '#weight' => 0, | |
162 ); | |
163 } | |
164 | |
165 $form['display']['aslist'] = array( | |
166 '#type' => 'checkbox', | |
167 '#title' => t('Listbox'), | |
168 '#default_value' => $component['extra']['aslist'], | |
169 '#description' => t('Check this option if you want the select component to be displayed as a select list box instead of radio buttons or checkboxes. Option groups (nested options) are only supported with listbox components.'), | |
170 '#parents' => array('extra', 'aslist'), | |
171 ); | |
172 $form['display']['optrand'] = array( | |
173 '#type' => 'checkbox', | |
174 '#title' => t('Randomize options'), | |
175 '#default_value' => $component['extra']['optrand'], | |
176 '#description' => t('Randomizes the order of the options when they are displayed in the form.'), | |
177 '#parents' => array('extra', 'optrand'), | |
178 ); | |
179 | |
180 return $form; | |
181 } | |
182 | |
183 /** | |
184 * Element validation callback. Ensure keys are not duplicated. | |
185 */ | |
186 function _webform_edit_validate_select($element, &$form_state) { | |
187 // Check for duplicate key values to prevent unexpected data loss. Require | |
188 // all options to include a safe_key. | |
189 if (!empty($element['#value'])) { | |
190 $lines = explode("\n", trim($element['#value'])); | |
191 $existing_keys = array(); | |
192 $duplicate_keys = array(); | |
193 $missing_keys = array(); | |
194 $long_keys = array(); | |
195 $group = ''; | |
196 foreach ($lines as $line) { | |
197 $matches = array(); | |
198 $line = trim($line); | |
199 if (preg_match('/^\<([^>]*)\>$/', $line, $matches)) { | |
200 $group = $matches[1]; | |
201 $key = NULL; // No need to store group names. | |
202 } | |
203 elseif (preg_match('/^([^|]*)\|(.*)$/', $line, $matches)) { | |
204 $key = $matches[1]; | |
205 if (strlen($key) > 128) { | |
206 $long_keys[] = $key; | |
207 } | |
208 } | |
209 else { | |
210 $missing_keys[] = $line; | |
211 } | |
212 | |
213 if (isset($key)) { | |
214 if (isset($existing_keys[$group][$key])) { | |
215 $duplicate_keys[$key] = $key; | |
216 } | |
217 else { | |
218 $existing_keys[$group][$key] = $key; | |
219 } | |
220 } | |
221 } | |
222 | |
223 if (!empty($missing_keys)) { | |
224 form_error($element, t('Every option must have a key specified. Specify each option as "safe_key|Some readable option".')); | |
225 } | |
226 | |
227 if (!empty($long_keys)) { | |
228 form_error($element, t('Option keys must be less than 128 characters. The following keys exceed this limit:') . theme('item_list', $long_keys)); | |
229 } | |
230 | |
231 if (!empty($duplicate_keys)) { | |
232 form_error($element, t('Options within the select list must be unique. The following keys have been used multiple times:') . theme('item_list', array('items' => $duplicate_keys))); | |
233 } | |
234 | |
235 // Set the listbox option if needed. | |
236 if (empty($missing_keys) && empty($long_keys) && empty($duplicate_keys)) { | |
237 $options = _webform_select_options_from_text($element['#value']); | |
238 _webform_edit_validate_set_aslist($options, $form_state); | |
239 } | |
240 } | |
241 | |
242 return TRUE; | |
243 } | |
244 | |
245 /** | |
246 * Set the appropriate webform values when using the options element module. | |
247 */ | |
248 function _webform_edit_validate_options($element, &$form_state) { | |
249 $key = end($element['#parents']); | |
250 $element_options = $form_state['values'][$key]['options']; | |
251 unset($form_state['values'][$key]); | |
252 | |
253 $form_state['values']['extra'][$key] = form_options_to_text($element_options['options'], 'custom'); | |
254 | |
255 // Options saved for select components. | |
256 if ($key == 'items') { | |
257 $form_state['values']['extra']['multiple'] = $element_options['multiple']; | |
258 $form_state['values']['extra']['custom_keys'] = $element_options['custom_keys']; | |
259 $form_state['values']['value'] = is_array($element_options['default_value']) ? implode(', ', $element_options['default_value']) : $element_options['default_value']; | |
260 | |
261 // Set the listbox option if needed. | |
262 _webform_edit_validate_set_aslist($element_options['options'], $form_state); | |
263 } | |
264 // Options saved for grid components. | |
265 else { | |
266 $form_state['values']['extra']['custom_' . rtrim($key, 's') . '_keys'] = $element_options['custom_keys']; | |
267 } | |
268 } | |
269 | |
270 /** | |
271 * Ensure "aslist" is used for option groups. Called from options validations. | |
272 */ | |
273 function _webform_edit_validate_set_aslist($options, &$form_state) { | |
274 if (empty($form_state['values']['extra']['aslist']) && !empty($options)) { | |
275 foreach ($options as $option) { | |
276 if (is_array($option)) { | |
277 $form_state['values']['extra']['aslist'] = 1; | |
278 drupal_set_message(t('The component %name has automatically been set to display as a listbox in order to support option groups.', array('%name' => $form_state['values']['name'])), 'warning'); | |
279 break; | |
280 } | |
281 } | |
282 } | |
283 } | |
284 | |
285 /** | |
286 * Implements _webform_render_component(). | |
287 */ | |
288 function _webform_render_select($component, $value = NULL, $filter = TRUE) { | |
289 $node = isset($component['nid']) ? node_load($component['nid']) : NULL; | |
290 | |
291 $element = array( | |
292 '#title' => $filter ? _webform_filter_xss($component['name']) : $component['name'], | |
293 '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before', | |
294 '#required' => $component['mandatory'], | |
295 '#weight' => $component['weight'], | |
296 '#description' => $filter ? _webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'], | |
297 '#theme_wrappers' => array('webform_element'), | |
298 '#pre_render' => array(), // Needed to disable double-wrapping of radios and checkboxes. | |
299 '#translatable' => array('title', 'description', 'options'), | |
300 ); | |
301 | |
302 // Convert the user-entered options list into an array. | |
303 $default_value = $filter ? _webform_filter_values($component['value'], $node, NULL, NULL, FALSE) : $component['value']; | |
304 $options = _webform_select_options($component, !$component['extra']['aslist'], $filter); | |
305 | |
306 if ($component['extra']['optrand']) { | |
307 _webform_shuffle_options($options); | |
308 } | |
309 | |
310 // Add default options if using a select list with no default. This trigger's | |
311 // Drupal 7's adding of the option for us. See @form_process_select(). | |
312 if ($component['extra']['aslist'] && !$component['extra']['multiple'] && $default_value === '') { | |
313 $element['#empty_value'] = ''; | |
314 } | |
315 | |
316 // Set the component options. | |
317 $element['#options'] = $options; | |
318 | |
319 // Set the default value. | |
320 if (isset($value)) { | |
321 if ($component['extra']['multiple']) { | |
322 // Set the value as an array. | |
323 $element['#default_value'] = array(); | |
324 foreach ((array) $value as $key => $option_value) { | |
325 $element['#default_value'][] = $option_value; | |
326 } | |
327 } | |
328 else { | |
329 // Set the value as a single string. | |
330 $element['#default_value'] = ''; | |
331 foreach ((array) $value as $option_value) { | |
332 $element['#default_value'] = $option_value; | |
333 } | |
334 } | |
335 } | |
336 elseif ($default_value !== '') { | |
337 // Convert default value to a list if necessary. | |
338 if ($component['extra']['multiple']) { | |
339 $varray = explode(',', $default_value); | |
340 foreach ($varray as $key => $v) { | |
341 $v = trim($v); | |
342 if ($v !== '') { | |
343 $element['#default_value'][] = $v; | |
344 } | |
345 } | |
346 } | |
347 else { | |
348 $element['#default_value'] = $default_value; | |
349 } | |
350 } | |
351 elseif ($component['extra']['multiple']) { | |
352 $element['#default_value'] = array(); | |
353 } | |
354 | |
355 if ($component['extra']['other_option'] && module_exists('select_or_other')) { | |
356 // Set display as a select_or_other element: | |
357 $element['#type'] = 'select_or_other'; | |
358 $element['#other'] = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...'); | |
359 $element['#other_title'] = $element['#title'] . ' ' . $element['#other']; | |
360 $element['#other_title_display'] = 'invisible'; | |
361 $element['#other_unknown_defaults'] = 'other'; | |
362 $element['#other_delimiter'] = ', '; | |
363 // Merge in Webform's #process function for Select or other. | |
364 $element['#process'] = array_merge(element_info_property('select_or_other', '#process'), array('webform_expand_select_or_other')); | |
365 | |
366 if ($component['extra']['multiple']) { | |
367 $element['#multiple'] = TRUE; | |
368 $element['#select_type'] = 'checkboxes'; | |
369 } | |
370 else { | |
371 $element['#multiple'] = FALSE; | |
372 $element['#select_type'] = 'radios'; | |
373 } | |
374 if ($component['extra']['aslist']) { | |
375 $element['#select_type'] = 'select'; | |
376 } | |
377 } | |
378 elseif ($component['extra']['aslist']) { | |
379 // Set display as a select list: | |
380 $element['#type'] = 'select'; | |
381 if ($component['extra']['multiple']) { | |
382 $element['#size'] = 4; | |
383 $element['#multiple'] = TRUE; | |
384 } | |
385 } | |
386 else { | |
387 if ($component['extra']['multiple']) { | |
388 // Set display as a checkbox set. | |
389 $element['#type'] = 'checkboxes'; | |
390 $element['#theme_wrappers'] = array_merge(array('checkboxes'), $element['#theme_wrappers']); | |
391 $element['#process'] = array_merge(element_info_property('checkboxes', '#process'), array('webform_expand_select_ids')); | |
392 | |
393 // Entirely replace the normal expand checkboxes with our custom version. | |
394 // This helps render checkboxes in multipage forms. | |
395 $process_key = array_search('form_process_checkboxes', $element['#process']); | |
396 $element['#process'][$process_key] = 'webform_expand_checkboxes'; | |
397 } | |
398 else { | |
399 // Set display as a radio set. | |
400 $element['#type'] = 'radios'; | |
401 $element['#theme_wrappers'] = array_merge(array('radios'), $element['#theme_wrappers']); | |
402 $element['#process'] = array_merge(element_info_property('radios', '#process'), array('webform_expand_select_ids')); | |
403 } | |
404 } | |
405 | |
406 return $element; | |
407 } | |
408 | |
409 /** | |
410 * Process function to ensure select_or_other elements validate properly. | |
411 */ | |
412 function webform_expand_select_or_other($element) { | |
413 // Disable validation for back-button and save draft. | |
414 $element['select']['#validated'] = TRUE; | |
415 $element['select']['#webform_validated'] = FALSE; | |
416 | |
417 $element['other']['#validated'] = TRUE; | |
418 $element['other']['#webform_validated'] = FALSE; | |
419 | |
420 // The Drupal FAPI does not support #title_display inline so we need to move | |
421 // to a supported value here to be compatible with select_or_other. | |
422 $element['select']['#title_display'] = $element['#title_display'] === 'inline' ? 'before' : $element['#title_display']; | |
423 | |
424 // If the default value contains "select_or_other" (the key of the select | |
425 // element for the "other..." choice), discard it and set the "other" value. | |
426 if (is_array($element['#default_value']) && in_array('select_or_other', $element['#default_value'])) { | |
427 $key = array_search('select_or_other', $element['#default_value']); | |
428 unset($element['#default_value'][$key]); | |
429 $element['#default_value'] = array_values($element['#default_value']); | |
430 $element['other']['#default_value'] = implode(', ', $element['#default_value']); | |
431 } | |
432 | |
433 // Sanitize the options in Select or other check boxes and radio buttons. | |
434 if ($element['#select_type'] == 'checkboxes' || $element['#select_type'] == 'radios') { | |
435 $element['select']['#process'] = array_merge(element_info_property($element['#select_type'], '#process'), array('webform_expand_select_ids')); | |
436 } | |
437 | |
438 return $element; | |
439 } | |
440 | |
441 /** | |
442 * Drupal 6 hack that properly *renders* checkboxes in multistep forms. This is | |
443 * different than the value hack needed in Drupal 5, which is no longer needed. | |
444 */ | |
445 function webform_expand_checkboxes($element) { | |
446 // Elements that have a value set are already in the form structure cause | |
447 // them not to be written when the expand_checkboxes function is called. | |
448 $default_value = array(); | |
449 foreach (element_children($element) as $key) { | |
450 if (isset($element[$key]['#default_value'])) { | |
451 $default_value[$key] = $element[$key]['#default_value']; | |
452 unset($element[$key]); | |
453 } | |
454 } | |
455 | |
456 $element = form_process_checkboxes($element); | |
457 | |
458 // Escape the values of checkboxes. | |
459 foreach (element_children($element) as $key) { | |
460 $element[$key]['#return_value'] = check_plain($element[$key]['#return_value']); | |
461 $element[$key]['#name'] = $element['#name'] . '[' . $element[$key]['#return_value'] . ']'; | |
462 } | |
463 | |
464 foreach ($default_value as $key => $val) { | |
465 $element[$key]['#default_value'] = $val; | |
466 } | |
467 return $element; | |
468 } | |
469 | |
470 /** | |
471 * FAPI process function to rename IDs attached to checkboxes and radios. | |
472 */ | |
473 function webform_expand_select_ids($element) { | |
474 $id = $element['#id'] = str_replace('_', '-', _webform_safe_name(strip_tags($element['#id']))); | |
475 $delta = 0; | |
476 foreach (element_children($element) as $key) { | |
477 $delta++; | |
478 // Convert the #id for each child to a safe name, regardless of key. | |
479 $element[$key]['#id'] = $id . '-' . $delta; | |
480 | |
481 // Prevent scripts or CSS in the labels for each checkbox or radio. | |
482 $element[$key]['#title'] = _webform_filter_xss($element[$key]['#title']); | |
483 } | |
484 return $element; | |
485 } | |
486 | |
487 /** | |
488 * Implements _webform_display_component(). | |
489 */ | |
490 function _webform_display_select($component, $value, $format = 'html') { | |
491 return array( | |
492 '#title' => $component['name'], | |
493 '#weight' => $component['weight'], | |
494 '#multiple' => $component['extra']['multiple'], | |
495 '#theme' => 'webform_display_select', | |
496 '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'), | |
497 '#format' => $format, | |
498 '#options' => _webform_select_options($component, !$component['extra']['aslist']), | |
499 '#value' => (array) $value, | |
500 '#translatable' => array('title', 'options'), | |
501 ); | |
502 } | |
503 | |
504 /** | |
505 * Implements _webform_submit_component(). | |
506 * | |
507 * Convert FAPI 0/1 values into something saveable. | |
508 */ | |
509 function _webform_submit_select($component, $value) { | |
510 // Build a list of all valid keys expected to be submitted. | |
511 $options = _webform_select_options($component, TRUE); | |
512 | |
513 $return = NULL; | |
514 if (is_array($value)) { | |
515 $return = array(); | |
516 foreach ($value as $key => $option_value) { | |
517 // Handle options that are specified options. | |
518 if ($option_value !== '' && isset($options[$option_value])) { | |
519 // Checkboxes submit an integer value of 0 when unchecked. A checkbox | |
520 // with a value of '0' is valid, so we can't use empty() here. | |
521 if ($option_value === 0 && !$component['extra']['aslist'] && $component['extra']['multiple']) { | |
522 unset($value[$option_value]); | |
523 } | |
524 else { | |
525 $return[] = $option_value; | |
526 } | |
527 } | |
528 // Handle options that are added through the "other" field. Specifically | |
529 // exclude the "select_or_other" value, which is added by the select list. | |
530 elseif ($component['extra']['other_option'] && module_exists('select_or_other') && $option_value != 'select_or_other') { | |
531 $return[] = $option_value; | |
532 } | |
533 } | |
534 } | |
535 elseif (is_string($value)) { | |
536 $return = $value; | |
537 } | |
538 | |
539 return $return; | |
540 } | |
541 | |
542 /** | |
543 * Format the text output for this component. | |
544 */ | |
545 function theme_webform_display_select($variables) { | |
546 $element = $variables['element']; | |
547 | |
548 // Flatten the list of options so we can get values easily. These options | |
549 // may be translated by hook_webform_display_component_alter(). | |
550 $options = array(); | |
551 foreach ($element['#options'] as $key => $value) { | |
552 if (is_array($value)) { | |
553 foreach ($value as $subkey => $subvalue) { | |
554 $options[$subkey] = $subvalue; | |
555 } | |
556 } | |
557 else { | |
558 $options[$key] = $value; | |
559 } | |
560 } | |
561 | |
562 $items = array(); | |
563 if ($element['#multiple']) { | |
564 foreach ((array) $element['#value'] as $option_value) { | |
565 if ($option_value !== '') { | |
566 // Administer provided values. | |
567 if (isset($options[$option_value])) { | |
568 $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$option_value]) : $options[$option_value]; | |
569 } | |
570 // User-specified in the "other" field. | |
571 else { | |
572 $items[] = $element['#format'] == 'html' ? check_plain($option_value) : $option_value; | |
573 } | |
574 } | |
575 } | |
576 } | |
577 else { | |
578 if (isset($element['#value'][0]) && $element['#value'][0] !== '') { | |
579 // Administer provided values. | |
580 if (isset($options[$element['#value'][0]])) { | |
581 $items[] = $element['#format'] == 'html' ? _webform_filter_xss($options[$element['#value'][0]]) : $options[$element['#value'][0]]; | |
582 } | |
583 // User-specified in the "other" field. | |
584 else { | |
585 $items[] = $element['#format'] == 'html' ? check_plain($element['#value'][0]) : $element['#value'][0]; | |
586 } | |
587 } | |
588 } | |
589 | |
590 if ($element['#format'] == 'html') { | |
591 $output = count($items) > 1 ? theme('item_list', array('items' => $items)) : (isset($items[0]) ? $items[0] : ' '); | |
592 } | |
593 else { | |
594 if (count($items) > 1) { | |
595 foreach ($items as $key => $item) { | |
596 $items[$key] = ' - ' . $item; | |
597 } | |
598 $output = implode("\n", $items); | |
599 } | |
600 else { | |
601 $output = isset($items[0]) ? $items[0] : ' '; | |
602 } | |
603 } | |
604 | |
605 return $output; | |
606 } | |
607 | |
608 /** | |
609 * Implements _webform_analysis_component(). | |
610 */ | |
611 function _webform_analysis_select($component, $sids = array(), $single = FALSE) { | |
612 $options = _webform_select_options($component, TRUE); | |
613 $show_other_results = $single; | |
614 | |
615 $sid_placeholders = count($sids) ? array_fill(0, count($sids), "'%s'") : array(); | |
616 $sid_filter = count($sids) ? " AND sid IN (" . implode(",", $sid_placeholders) . ")" : ""; | |
617 | |
618 $option_operator = $show_other_results ? 'NOT IN' : 'IN'; | |
619 $query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC)) | |
620 ->fields('wsd', array('data')) | |
621 ->condition('nid', $component['nid']) | |
622 ->condition('cid', $component['cid']) | |
623 ->condition('data', '', '<>') | |
624 ->condition('data', array_keys($options), $option_operator) | |
625 ->groupBy('data'); | |
626 $query->addExpression('COUNT(data)', 'datacount'); | |
627 | |
628 if (count($sids)) { | |
629 $query->condition('sid', $sids, 'IN'); | |
630 } | |
631 | |
632 $count_query = db_select('webform_submitted_data', 'wsd', array('fetch' => PDO::FETCH_ASSOC)) | |
633 ->condition('nid', $component['nid']) | |
634 ->condition('cid', $component['cid']) | |
635 ->condition('data', '', '<>'); | |
636 $count_query->addExpression('COUNT(*)', 'datacount'); | |
637 if (count($sids)) { | |
638 $count_query->condition('sid', $sids, 'IN'); | |
639 } | |
640 | |
641 $result = $query->execute(); | |
642 $rows = array(); | |
643 $normal_count = 0; | |
644 foreach ($result as $data) { | |
645 $display_option = $single ? $data['data'] : $options[$data['data']]; | |
646 $rows[$data['data']] = array(_webform_filter_xss($display_option), $data['datacount']); | |
647 $normal_count += $data['datacount']; | |
648 } | |
649 | |
650 if (!$show_other_results) { | |
651 // Order the results according to the normal options array. | |
652 $ordered_rows = array(); | |
653 foreach (array_intersect_key($options, $rows) as $key => $label) { | |
654 $ordered_rows[] = $rows[$key]; | |
655 } | |
656 | |
657 // Add a row for any unknown or user-entered values. | |
658 if ($component['extra']['other_option']) { | |
659 $full_count = $count_query->execute()->fetchField(); | |
660 $other_count = $full_count - $normal_count; | |
661 $display_option = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...'); | |
662 $other_text = $other_count ? $other_count . ' (' . l(t('view'), 'node/' . $component['nid'] . '/webform-results/analysis/' . $component['cid']) . ')' : $other_count; | |
663 $ordered_rows[] = array($display_option, $other_text); | |
664 } | |
665 | |
666 $rows = $ordered_rows; | |
667 } | |
668 | |
669 return $rows; | |
670 } | |
671 | |
672 /** | |
673 * Implements _webform_table_component(). | |
674 */ | |
675 function _webform_table_select($component, $value) { | |
676 // Convert submitted 'safe' values to un-edited, original form. | |
677 $options = _webform_select_options($component, TRUE); | |
678 | |
679 $value = (array) $value; | |
680 $items = array(); | |
681 // Set the value as a single string. | |
682 foreach ($value as $option_value) { | |
683 if ($option_value !== '') { | |
684 if (isset($options[$option_value])) { | |
685 $items[] = _webform_filter_xss($options[$option_value]); | |
686 } | |
687 else { | |
688 $items[] = check_plain($option_value); | |
689 } | |
690 } | |
691 } | |
692 | |
693 return implode('<br />', $items); | |
694 } | |
695 | |
696 /** | |
697 * Implements _webform_csv_headers_component(). | |
698 */ | |
699 function _webform_csv_headers_select($component, $export_options) { | |
700 $headers = array( | |
701 0 => array(), | |
702 1 => array(), | |
703 2 => array(), | |
704 ); | |
705 | |
706 if ($component['extra']['multiple'] && $export_options['select_format'] == 'separate') { | |
707 $headers[0][] = ''; | |
708 $headers[1][] = $component['name']; | |
709 $items = _webform_select_options($component, TRUE, FALSE); | |
710 if ($component['extra']['other_option']) { | |
711 $other_label = !empty($component['extra']['other_text']) ? check_plain($component['extra']['other_text']) : t('Other...'); | |
712 $items[$other_label] = $other_label; | |
713 } | |
714 $count = 0; | |
715 foreach ($items as $key => $item) { | |
716 // Empty column per sub-field in main header. | |
717 if ($count != 0) { | |
718 $headers[0][] = ''; | |
719 $headers[1][] = ''; | |
720 } | |
721 if ($export_options['select_keys']) { | |
722 $headers[2][] = $key; | |
723 } | |
724 else { | |
725 $headers[2][] = $item; | |
726 } | |
727 $count++; | |
728 } | |
729 } | |
730 else { | |
731 $headers[0][] = ''; | |
732 $headers[1][] = ''; | |
733 $headers[2][] = $component['name']; | |
734 } | |
735 return $headers; | |
736 } | |
737 | |
738 /** | |
739 * Implements _webform_csv_data_component(). | |
740 */ | |
741 function _webform_csv_data_select($component, $export_options, $value) { | |
742 $options = _webform_select_options($component, TRUE, FALSE); | |
743 $return = array(); | |
744 | |
745 if ($component['extra']['multiple']) { | |
746 foreach ($options as $key => $item) { | |
747 $index = array_search($key, (array) $value); | |
748 if ($index !== FALSE) { | |
749 if ($export_options['select_format'] == 'separate') { | |
750 $return[] = 'X'; | |
751 } | |
752 else { | |
753 $return[] = $export_options['select_keys'] ? $key : $item; | |
754 } | |
755 unset($value[$index]); | |
756 } | |
757 elseif ($export_options['select_format'] == 'separate') { | |
758 $return[] = ''; | |
759 } | |
760 } | |
761 | |
762 // Any remaining items in the $value array will be user-added options. | |
763 if ($component['extra']['other_option']) { | |
764 $return[] = count($value) ? implode(',', $value) : ''; | |
765 } | |
766 } | |
767 else { | |
768 $key = $value[0]; | |
769 if ($export_options['select_keys']) { | |
770 $return = $key; | |
771 } | |
772 else { | |
773 $return = isset($options[$key]) ? $options[$key] : $key; | |
774 } | |
775 } | |
776 | |
777 if ($component['extra']['multiple'] && $export_options['select_format'] == 'compact') { | |
778 $return = implode(',', (array) $return); | |
779 } | |
780 | |
781 return $return; | |
782 } | |
783 | |
784 /** | |
785 * Menu callback; Return a predefined list of select options as JSON. | |
786 */ | |
787 function webform_select_options_ajax($source_name = '') { | |
788 $info = _webform_select_options_info(); | |
789 | |
790 $component['extra']['options_source'] = $source_name; | |
791 if ($source_name && isset($info[$source_name])) { | |
792 $options = _webform_select_options_to_text(_webform_select_options($component, !$component['extra']['aslist'], FALSE)); | |
793 } | |
794 else { | |
795 $options = ''; | |
796 } | |
797 | |
798 $return = array( | |
799 'elementId' => module_exists('options_element') ? 'edit-items-options-options-field-widget' : 'edit-extra-items', | |
800 'options' => $options, | |
801 ); | |
802 | |
803 drupal_json_output($return); | |
804 } | |
805 | |
806 /** | |
807 * Generate a list of options for a select list. | |
808 */ | |
809 function _webform_select_options($component, $flat = FALSE, $filter = TRUE) { | |
810 if ($component['extra']['options_source']) { | |
811 $options = _webform_select_options_callback($component['extra']['options_source'], $component, $flat, $filter); | |
812 } | |
813 else { | |
814 $options = _webform_select_options_from_text($component['extra']['items'], $flat, $filter); | |
815 } | |
816 | |
817 return isset($options) ? $options : array(); | |
818 } | |
819 | |
820 /** | |
821 * Load Webform select option info from 3rd party modules. | |
822 */ | |
823 function _webform_select_options_info() { | |
824 static $info; | |
825 if (!isset($info)) { | |
826 $info = array(); | |
827 | |
828 foreach (module_implements('webform_select_options_info') as $module) { | |
829 $additions = module_invoke($module, 'webform_select_options_info'); | |
830 foreach ($additions as $key => $addition) { | |
831 $additions[$key]['module'] = $module; | |
832 } | |
833 $info = array_merge($info, $additions); | |
834 } | |
835 drupal_alter('webform_select_options_info', $info); | |
836 } | |
837 return $info; | |
838 } | |
839 | |
840 /** | |
841 * Execute a select option callback. | |
842 * | |
843 * @param $name | |
844 * The name of the options group. | |
845 * @param $component | |
846 * The full Webform component. | |
847 * @param $flat | |
848 * Whether the information returned should exclude any nested groups. | |
849 * @param $filter | |
850 * Whether information returned should be sanitized. Defaults to TRUE. | |
851 */ | |
852 function _webform_select_options_callback($name, $component, $flat = FALSE, $filter = TRUE) { | |
853 $info = _webform_select_options_info(); | |
854 | |
855 // Include any necessary files. | |
856 if (isset($info[$name]['file'])) { | |
857 $pathinfo = pathinfo($info[$name]['file']); | |
858 $path = ($pathinfo['dirname'] ? $pathinfo['dirname'] . '/' : '') . basename($pathinfo['basename'], '.' . $pathinfo['extension']); | |
859 module_load_include($pathinfo['extension'], $info[$name]['module'], $path); | |
860 } | |
861 | |
862 // Execute the callback function. | |
863 if (isset($info[$name]['options callback']) && function_exists($info[$name]['options callback'])) { | |
864 $function = $info[$name]['options callback']; | |
865 | |
866 $arguments = array(); | |
867 if (isset($info[$name]['options arguments'])) { | |
868 $arguments = $info[$name]['options arguments']; | |
869 } | |
870 | |
871 return $function($component, $flat, $filter, $arguments); | |
872 } | |
873 } | |
874 | |
875 /** | |
876 * Utility function to split user-entered values from new-line separated | |
877 * text into an array of options. | |
878 * | |
879 * @param $text | |
880 * Text to be converted into a select option array. | |
881 * @param $flat | |
882 * Optional. If specified, return the option array and exclude any optgroups. | |
883 * @param $filter | |
884 * Optional. Whether or not to filter returned values. | |
885 */ | |
886 function _webform_select_options_from_text($text, $flat = FALSE, $filter = TRUE) { | |
887 static $option_cache = array(); | |
888 | |
889 // Keep each processed option block in an array indexed by the MD5 hash of | |
890 // the option text and the value of the $flat variable. | |
891 $md5 = md5($text); | |
892 | |
893 // Check if this option block has been previously processed. | |
894 if (!isset($option_cache[$flat][$md5])) { | |
895 $options = array(); | |
896 $rows = array_filter(explode("\n", trim($text))); | |
897 $group = NULL; | |
898 foreach ($rows as $option) { | |
899 $option = trim($option); | |
900 /** | |
901 * If the Key of the option is within < >, treat as an optgroup | |
902 * | |
903 * <Group 1> | |
904 * creates an optgroup with the label "Group 1" | |
905 * | |
906 * <> | |
907 * Unsets the current group, allowing items to be inserted at the root element. | |
908 */ | |
909 if (preg_match('/^\<([^>]*)\>$/', $option, $matches)) { | |
910 if (empty($matches[1])) { | |
911 unset($group); | |
912 } | |
913 elseif (!$flat) { | |
914 $group = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1]; | |
915 } | |
916 } | |
917 elseif (preg_match('/^([^|]+)\|(.*)$/', $option, $matches)) { | |
918 $key = $filter ? _webform_filter_values($matches[1], NULL, NULL, NULL, FALSE) : $matches[1]; | |
919 $value = $filter ? _webform_filter_values($matches[2], NULL, NULL, NULL, FALSE) : $matches[2]; | |
920 isset($group) ? $options[$group][$key] = $value : $options[$key] = $value; | |
921 } | |
922 else { | |
923 $filtered_option = $filter ? _webform_filter_values($option, NULL, NULL, NULL, FALSE) : $option; | |
924 isset($group) ? $options[$group][$filtered_option] = $filtered_option : $options[$filtered_option] = $filtered_option; | |
925 } | |
926 } | |
927 | |
928 $option_cache[$flat][$md5] = $options; | |
929 } | |
930 | |
931 // Return our options from the option_cache array. | |
932 return $option_cache[$flat][$md5]; | |
933 } | |
934 | |
935 /** | |
936 * Convert an array of options into text. | |
937 */ | |
938 function _webform_select_options_to_text($options) { | |
939 $output = ''; | |
940 $previous_key = FALSE; | |
941 | |
942 foreach ($options as $key => $value) { | |
943 // Convert groups. | |
944 if (is_array($value)) { | |
945 $output .= '<' . $key . '>' . "\n"; | |
946 foreach ($value as $subkey => $subvalue) { | |
947 $output .= $subkey . '|' . $subvalue . "\n"; | |
948 } | |
949 $previous_key = $key; | |
950 } | |
951 // Typical key|value pairs. | |
952 else { | |
953 // Exit out of any groups. | |
954 if (isset($options[$previous_key]) && is_array($options[$previous_key])) { | |
955 $output .= "<>\n"; | |
956 } | |
957 // Skip empty rows. | |
958 if ($options[$key] !== '') { | |
959 $output .= $key . '|' . $value . "\n"; | |
960 } | |
961 $previous_key = $key; | |
962 } | |
963 } | |
964 | |
965 return $output; | |
966 } | |
967 | |
968 /** | |
969 * Utility function to shuffle an array while preserving key-value pairs. | |
970 */ | |
971 function _webform_shuffle_options(&$array) { | |
972 // First shuffle the array keys, then use them as the basis for ordering | |
973 // the options. | |
974 $aux = array(); | |
975 $keys = array_keys($array); | |
976 shuffle($keys); | |
977 foreach ($keys as $key) { | |
978 $aux[$key] = $array[$key]; | |
979 } | |
980 $array = $aux; | |
981 } |