Projet

Général

Profil

Paste
Télécharger (30,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / webform / components / grid.inc @ 8d02775b

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Webform module grid component.
6
 */
7
8
// Grid depends on functions provided by select.
9
webform_component_include('select');
10
11
/**
12
 * Implements _webform_defaults_component().
13
 */
14
function _webform_defaults_grid() {
15
  return array(
16
    'name' => '',
17
    'form_key' => NULL,
18 a45e4bc1 Assos Assos
    'required' => 0,
19 85ad3d82 Assos Assos
    'pid' => 0,
20
    'weight' => 0,
21 a45e4bc1 Assos Assos
    'value' => '',
22 85ad3d82 Assos Assos
    'extra' => array(
23
      'options' => '',
24
      'questions' => '',
25
      'optrand' => 0,
26
      'qrand' => 0,
27 a45e4bc1 Assos Assos
      'unique' => 0,
28 85ad3d82 Assos Assos
      'title_display' => 0,
29
      'custom_option_keys' => 0,
30
      'custom_question_keys' => 0,
31 a45e4bc1 Assos Assos
      'sticky' => TRUE,
32 85ad3d82 Assos Assos
      'description' => '',
33 01d522a6 Assos Assos
      'description_above' => FALSE,
34 85ad3d82 Assos Assos
      'private' => FALSE,
35 a45e4bc1 Assos Assos
      'analysis' => TRUE,
36 85ad3d82 Assos Assos
    ),
37
  );
38
}
39
40
/**
41
 * Implements _webform_theme_component().
42
 */
43
function _webform_theme_grid() {
44
  return array(
45
    'webform_grid' => array(
46
      'render element' => 'element',
47
      'file' => 'components/grid.inc',
48
    ),
49
    'webform_display_grid' => array(
50
      'render element' => 'element',
51
      'file' => 'components/grid.inc',
52
    ),
53
  );
54
}
55
56
/**
57
 * Implements _webform_edit_component().
58
 */
59
function _webform_edit_grid($component) {
60
  $form = array();
61
62 ba09eb79 Assos Assos
  $form['help'] = array(
63
    '#type' => 'fieldset',
64
    '#collapsible' => TRUE,
65
    '#collapsed' => !empty($component['cid']),
66
    '#title' => t('About options and questions&hellip;'),
67
    '#description' => t('Options and questions may be configured here, in additional nested Select Options components, or even both.'),
68
    '#weight' => -4,
69
    'pros_and_cons' => array(
70
      '#theme' => 'table',
71
      '#header' => array('', t('Options and questions configured <strong>here</strong>'), t('Configured in additional <strong>nested</strong> components'), t('Both')),
72
      '#rows' => array(
73
        array(t('Questions'), t('Enter the questions below.'), t('Configure and save this grid, then add additional Select Options components nested (indented) below this grid.'), t('Additional questions from nested components will be displayed below any questions configured here.')),
74
        array(t('Options'), t('Enter options below.'), t('May be different for each question. Initially the same as defined below.'), t('Options from additional nested components will be merged with any options configured here.')),
75
        array(t('Checkboxes'), t('No. Radio buttons only.'), t('Yes. Some or all questions may be multiple choice with check boxes.'), ''),
76
        array(t('Default'), t('Yes. Must be same for all questions.'), t('Yes. May all be the same or different.'), ''),
77
        array(t('Pre-built option lists'), t('No.'), t('Yes.'), ''),
78
        array(t('Required'), t('Yes. Must be same for all questions.'), t('Yes. May all be the same or different.'), ''),
79
        array(t('Question conditionals'), t('No.'), t('Yes. Individual questions may be used in conditional rules and/or actions.'), t('The whole grid may be conditionally shown or required.')),
80
        array(t('Other types of nested components'), t('No.'), t('Yes. Other component types may also be included in the grid. They will be displayed where the options would normally be.'), ''),
81
      ),
82
    ),
83
  );
84
85 85ad3d82 Assos Assos
  if (module_exists('options_element')) {
86
    $form['options'] = array(
87
      '#type' => 'fieldset',
88
      '#title' => t('Options'),
89
      '#collapsible' => TRUE,
90
      '#attributes' => array('class' => array('webform-options-element')),
91
      '#element_validate' => array('_webform_edit_validate_options'),
92 ba09eb79 Assos Assos
      '#weight' => -3,
93 85ad3d82 Assos Assos
    );
94
    $form['options']['options'] = array(
95
      '#type' => 'options',
96
      '#options' => _webform_select_options_from_text($component['extra']['options'], TRUE),
97 a45e4bc1 Assos Assos
      '#default_value' => $component['value'],
98
      '#default_value_allowed' => TRUE,
99 85ad3d82 Assos Assos
      '#optgroups' => FALSE,
100
      '#key_type' => 'mixed',
101
      '#key_type_toggle' => t('Customize option keys (Advanced)'),
102
      '#key_type_toggled' => $component['extra']['custom_option_keys'],
103 a45e4bc1 Assos Assos
      '#default_value_pattern' => '^%.+\[.+\]$',
104 ba09eb79 Assos Assos
      '#description' => t('<strong>Options to select across the top</strong>, such as "Poor" through "Excellent". Indicate the default to the left of the desired item. Use of only alphanumeric characters and underscores is recommended in keys.') . ' ' . theme('webform_token_help'),
105 85ad3d82 Assos Assos
    );
106
107
    $form['questions'] = array(
108
      '#type' => 'fieldset',
109
      '#title' => t('Questions'),
110
      '#collapsible' => TRUE,
111
      '#attributes' => array('class' => array('webform-options-element')),
112
      '#element_validate' => array('_webform_edit_validate_options'),
113 ba09eb79 Assos Assos
      '#weight' => -2,
114 85ad3d82 Assos Assos
    );
115
    $form['questions']['options'] = array(
116
      '#type' => 'options',
117
      '#options' => _webform_select_options_from_text($component['extra']['questions'], TRUE),
118
      '#optgroups' => FALSE,
119
      '#default_value' => FALSE,
120
      '#default_value_allowed' => FALSE,
121
      '#key_type' => 'mixed',
122
      '#key_type_toggle' => t('Customize question keys (Advanced)'),
123
      '#key_type_toggled' => $component['extra']['custom_question_keys'],
124 ba09eb79 Assos Assos
      '#description' => t('<strong>Questions list down the side of the grid.</strong> For a heading column on the right, append "|" and the right-side heading. Use of only alphanumeric characters and underscores is recommended in keys.') . ' ' . theme('webform_token_help'),
125 85ad3d82 Assos Assos
    );
126
  }
127
  else {
128
    $form['extra']['options'] = array(
129
      '#type' => 'textarea',
130
      '#title' => t('Options'),
131
      '#default_value' => $component['extra']['options'],
132 ba09eb79 Assos Assos
      '#description' => t('Options to select across the top, such as "Poor" through "Excellent" or "Stronly Disagree" through "Strongly Agree".') .
133 feca1e4a Assos Assos
      '<p>' . t('One key-value option per line. <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.') . '</p>' . theme('webform_token_help'),
134 85ad3d82 Assos Assos
      '#cols' => 60,
135
      '#rows' => 5,
136
      '#weight' => -3,
137
      '#wysiwyg' => FALSE,
138
      '#element_validate' => array('_webform_edit_validate_select'),
139
    );
140
    $form['extra']['questions'] = array(
141
      '#type' => 'textarea',
142
      '#title' => t('Questions'),
143
      '#default_value' => $component['extra']['questions'],
144 ba09eb79 Assos Assos
      '#description' => t('Questions list down the side of the grid. One question per line. <strong>Key-value pairs MUST be specified as "safe_key|Some readable question"</strong>. For a heading column on the right, append "|" and the right-side heading. Use of only alphanumeric characters and underscores is recommended in keys.') . ' ' . theme('webform_token_help') . ' ' .
145 feca1e4a Assos Assos
      '<p>' . t('<strong>Or for more control</strong> over the appearance and configuration, create additional additional Select Options or other type components nested under this grid. They will operate as separate components, but be displayed within this grid.') . '</p>',
146 85ad3d82 Assos Assos
      '#cols' => 60,
147
      '#rows' => 5,
148
      '#weight' => -2,
149
      '#wysiwyg' => FALSE,
150
      '#element_validate' => array('_webform_edit_validate_select'),
151
    );
152 a45e4bc1 Assos Assos
    $form['value'] = array(
153
      '#type' => 'textfield',
154
      '#title' => t('Default value'),
155
      '#default_value' => $component['value'],
156
      '#description' => t('The default option of the grid identified by its key.') . ' ' . theme('webform_token_help'),
157
      '#size' => 60,
158
      '#maxlength' => 1024,
159
      '#weight' => 1,
160
    );
161 85ad3d82 Assos Assos
  }
162
163
  $form['display']['optrand'] = array(
164
    '#type' => 'checkbox',
165
    '#title' => t('Randomize Options'),
166
    '#default_value' => $component['extra']['optrand'],
167
    '#description' => t('Randomizes the order of options on the top when they are displayed in the form.'),
168 feca1e4a Assos Assos
    '#parents' => array('extra', 'optrand'),
169 85ad3d82 Assos Assos
  );
170
  $form['display']['qrand'] = array(
171
    '#type' => 'checkbox',
172
    '#title' => t('Randomize Questions'),
173
    '#default_value' => $component['extra']['qrand'],
174
    '#description' => t('Randomize the order of the questions on the side when they are displayed in the form.'),
175 feca1e4a Assos Assos
    '#parents' => array('extra', 'qrand'),
176 85ad3d82 Assos Assos
  );
177 a45e4bc1 Assos Assos
  $form['display']['sticky'] = array(
178
    '#type' => 'checkbox',
179
    '#title' => t('Sticky table header'),
180
    '#default_value' => $component['extra']['sticky'],
181
    '#description' => t('Use a sticky (non-scrolling) table header.'),
182 feca1e4a Assos Assos
    '#parents' => array('extra', 'sticky'),
183 a45e4bc1 Assos Assos
  );
184
185
  $form['validation']['unique'] = array(
186
    '#type' => 'checkbox',
187
    '#title' => t('Unique'),
188
    '#return_value' => 1,
189
    '#description' => t('Check that all entered values for this field are unique. The same value is not allowed to be used twice.'),
190
    '#weight' => 1,
191
    '#default_value' => $component['extra']['unique'],
192
    '#parents' => array('extra', 'unique'),
193
  );
194
195 85ad3d82 Assos Assos
  return $form;
196
}
197
198
/**
199
 * Implements _webform_render_component().
200
 */
201 a45e4bc1 Assos Assos
function _webform_render_grid($component, $value = NULL, $filter = TRUE, $submission = NULL) {
202 85ad3d82 Assos Assos
  $node = isset($component['nid']) ? node_load($component['nid']) : NULL;
203
204 a45e4bc1 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
205
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
206
  if ($filter) {
207
    $questions = _webform_select_replace_tokens($questions, $node);
208
    $options = _webform_select_replace_tokens($options, $node);
209
  }
210
211 85ad3d82 Assos Assos
  $element = array(
212
    '#type' => 'webform_grid',
213 a45e4bc1 Assos Assos
    '#title' => $filter ? webform_filter_xss($component['name']) : $component['name'],
214 85ad3d82 Assos Assos
    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
215 a45e4bc1 Assos Assos
    '#required' => $component['required'],
216 85ad3d82 Assos Assos
    '#weight' => $component['weight'],
217 a45e4bc1 Assos Assos
    '#description' => $filter ? webform_filter_descriptions($component['extra']['description'], $node) : $component['extra']['description'],
218
    '#grid_questions' => $questions,
219
    '#grid_options' => $options,
220
    '#default_value' => isset($value) || !strlen($component['value']) ? $value : array_fill_keys(array_keys($questions), $component['value']),
221
    '#grid_default' => $component['value'],
222 85ad3d82 Assos Assos
    '#optrand' => $component['extra']['optrand'],
223
    '#qrand' => $component['extra']['qrand'],
224 a45e4bc1 Assos Assos
    '#sticky' => $component['extra']['sticky'],
225 85ad3d82 Assos Assos
    '#theme' => 'webform_grid',
226
    '#theme_wrappers' => array('webform_element'),
227
    '#process' => array('webform_expand_grid'),
228 6a93dd76 Assos Assos
    '#element_validate' => array('webform_validate_grid'),
229 85ad3d82 Assos Assos
    '#translatable' => array('title', 'description', 'grid_options', 'grid_questions'),
230
  );
231
232 a45e4bc1 Assos Assos
  // Enforce uniqueness.
233
  if ($component['extra']['unique']) {
234
    $element['#element_validate'][] = '_webform_edit_grid_unique_validate';
235 85ad3d82 Assos Assos
  }
236
237
  return $element;
238
}
239
240
/**
241
 * A Form API #process function for Webform grid fields.
242
 */
243
function webform_expand_grid($element) {
244
  $options = $element['#grid_options'];
245
  $questions = $element['#grid_questions'];
246 ba09eb79 Assos Assos
  $weights = array();
247 85ad3d82 Assos Assos
248 ba09eb79 Assos Assos
  // Process questions and options from nested components.
249
  foreach (element_children($element) as $key) {
250
    $question = $element[$key];
251
    // Both forms and grid displays have #webform_component.
252
    if (isset($question['#webform_component']) &&
253
        $question['#webform_component']['type'] == 'select' &&
254
        !$question['#webform_component']['extra']['aslist'] &&
255
        !$question['#webform_component']['extra']['other_option']) {
256
      $options = webform_grid_merge_options($options, $question['#options']);
257
      $weights[$key] = $question['#weight'];
258
    }
259 85ad3d82 Assos Assos
  }
260
261 ba09eb79 Assos Assos
  // Add the internal grid questions.
262
  $weight = -1000;
263
  $value = isset($element['#default_value']) ? $element['#default_value'] : array();
264 85ad3d82 Assos Assos
  foreach ($questions as $key => $question) {
265
    if ($question != '') {
266 ba09eb79 Assos Assos
      $question_value = isset($value[$key]) && $value[$key] !== '' ? $value[$key] : NULL;
267 85ad3d82 Assos Assos
      $element[$key] = array(
268 ba09eb79 Assos Assos
        '#grid_question' => TRUE,
269 85ad3d82 Assos Assos
        '#title' => $question,
270
        '#required' => $element['#required'],
271 ba09eb79 Assos Assos
        '#options' => $element['#grid_options'],
272 85ad3d82 Assos Assos
        '#type' => 'radios',
273 ba09eb79 Assos Assos
        '#default_value' => $question_value,
274
        '#value' => $question_value,
275 85ad3d82 Assos Assos
        '#process' => array('form_process_radios', 'webform_expand_select_ids'),
276
277
        // Webform handles validation manually.
278
        '#validated' => TRUE,
279
        '#webform_validated' => FALSE,
280
        '#translatable' => array('title'),
281 ba09eb79 Assos Assos
        '#weight' => $weight,
282 85ad3d82 Assos Assos
      );
283 a45e4bc1 Assos Assos
284
      // Add HTML5 required attribute, if needed.
285
      if ($element['#required']) {
286
        $element[$key]['#attributes']['required'] = 'required';
287
      }
288 ba09eb79 Assos Assos
289
      $weights[$key] = $weight;
290
      $weight++;
291 85ad3d82 Assos Assos
    }
292
  }
293
294 ba09eb79 Assos Assos
  if (!empty($element['#optrand'])) {
295
    _webform_shuffle_options($options);
296
  }
297
  $element['#grid_options'] = $options;
298
299
  asort($weights);
300
  if (!empty($element['#qrand'])) {
301
    _webform_shuffle_options($weights);
302
  }
303 6a93dd76 Assos Assos
  if ($weights) {
304
    $weight = min($weights);
305
  }
306 ba09eb79 Assos Assos
  foreach ($weights as $key => $old_weight) {
307
    $element[$key]['#options'] = webform_grid_remove_options($options, $element[$key]['#options']);
308
    $element[$key]['#weight'] = $weight++;
309
    $element['#grid_questions'][$key] = $element[$key]['#title'];
310
  }
311
312
  return $element;
313
}
314
315
/**
316
 * Helper. Merge select component options in order.
317
 *
318
 * @param array $existing
319
 *   An array of existing values into which any values from $new that aren't in
320
 *   $existing are inserted.
321
 * @param array $new
322
 *   Values to be inserted into $existing.
323 feca1e4a Assos Assos
 *
324 ba09eb79 Assos Assos
 * @return array
325
 *   The merged array.
326
 */
327 01f36513 Assos Assos
function webform_grid_merge_options(array $existing, array $new) {
328 ba09eb79 Assos Assos
  $insert = NULL;
329
  $queue = array();
330
  foreach ($new as $key => $value) {
331
    if (isset($existing[$key])) {
332
      // Insert the queue before the found item.
333
      $insert = array_search($key, array_keys($existing));
334
      if ($queue) {
335
        $existing = array_slice($existing, 0, $insert, TRUE) +
336
                    $queue +
337
                    array_slice($existing, $insert, NULL, TRUE);
338
        $insert += count($queue);
339
        $queue = array();
340
      }
341
      $insert++;
342
    }
343
    elseif (is_null($insert)) {
344
      // It is not yet clear yet where to put this item. Add it to the queue.
345
      $queue[$key] = $value;
346 85ad3d82 Assos Assos
    }
347
    else {
348 ba09eb79 Assos Assos
      // PHP array_splice does not preserved the keys of the inserted array,
349
      // but array_slice does (if the preserve keys parameter is TRUE).
350
      $existing = array_slice($existing, 0, $insert, TRUE) +
351
                  array($key => $value) +
352
                  array_slice($existing, $insert, NULL, TRUE);
353
      $insert++;
354 85ad3d82 Assos Assos
    }
355
  }
356 ba09eb79 Assos Assos
  // Append any left over queued items.
357
  $existing += $queue;
358
  return $existing;
359
}
360 85ad3d82 Assos Assos
361 ba09eb79 Assos Assos
/**
362
 * Helper. Replace missing options with empty values.
363
 *
364
 * @param array $header
365
 *   An array of options to be used at the grid table header.
366
 * @param array $row_options
367
 *   An array of options to be used for this row.
368 feca1e4a Assos Assos
 *
369 ba09eb79 Assos Assos
 * @return array
370
 *   The $row_options with any missing options replaced with empty values.
371 8c72e82a Assos Assos
 */
372 01f36513 Assos Assos
function webform_grid_remove_options(array $header, array $row_options) {
373 ba09eb79 Assos Assos
  foreach ($header as $key => $value) {
374
    if (!isset($row_options[$key])) {
375
      $header[$key] = '';
376
    }
377
  }
378
  return $header;
379 85ad3d82 Assos Assos
}
380
381
/**
382
 * Implements _webform_display_component().
383
 */
384 a45e4bc1 Assos Assos
function _webform_display_grid($component, $value, $format = 'html', $submission = array()) {
385
  $node = node_load($component['nid']);
386 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
387 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
388 85ad3d82 Assos Assos
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
389 a45e4bc1 Assos Assos
  $options = _webform_select_replace_tokens($options, $node);
390 85ad3d82 Assos Assos
391
  $element = array(
392
    '#title' => $component['name'],
393 a45e4bc1 Assos Assos
    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
394 85ad3d82 Assos Assos
    '#weight' => $component['weight'],
395
    '#format' => $format,
396
    '#grid_questions' => $questions,
397
    '#grid_options' => $options,
398 ba09eb79 Assos Assos
    '#default_value' => $value,
399 a45e4bc1 Assos Assos
    '#sticky' => $component['extra']['sticky'],
400 85ad3d82 Assos Assos
    '#theme' => 'webform_display_grid',
401
    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
402
    '#sorted' => TRUE,
403
    '#translatable' => array('#title', '#grid_questions', '#grid_options'),
404
  );
405
406
  foreach ($questions as $key => $question) {
407
    if ($question !== '') {
408
      $element[$key] = array(
409
        '#title' => $question,
410
        '#value' => isset($value[$key]) ? $value[$key] : NULL,
411
        '#translatable' => array('#title', '#value'),
412
      );
413
    }
414
  }
415
416
  return $element;
417
}
418
419 ba09eb79 Assos Assos
/**
420
 * Preprocess function for displaying a grid component.
421
 */
422
function template_preprocess_webform_display_grid(&$variables) {
423
  $element =& $variables['element'];
424
  // Expand the grid, suppressing randomization. This builds the grid
425
  // questions and options.
426
  $element['#qrand'] = FALSE;
427
  $element['#optrand'] = FALSE;
428
  $element['#required'] = FALSE;
429
  $element = webform_expand_grid($element);
430
}
431
432 85ad3d82 Assos Assos
/**
433
 * Format the text output for this component.
434
 */
435
function theme_webform_display_grid($variables) {
436
  $element = $variables['element'];
437
  $format = $element['#format'];
438
439
  if ($format == 'html') {
440 a45e4bc1 Assos Assos
    $right_titles = _webform_grid_right_titles($element);
441 85ad3d82 Assos Assos
    $rows = array();
442 ba09eb79 Assos Assos
443
    // Set the header for the table.
444
    $header = _webform_grid_header($element, $right_titles);
445
446
    foreach (element_children($element) as $question_key) {
447
      $question_element = $element[$question_key];
448 85ad3d82 Assos Assos
      $row = array();
449 ba09eb79 Assos Assos
      $questions = explode('|', $question_element['#title'], 2);
450
      $values = $question_element['#value'];
451
      $values = is_array($values) ? $values : array($values);
452 a45e4bc1 Assos Assos
      $row[] = array('data' => webform_filter_xss($questions[0]), 'class' => array('webform-grid-question'));
453 ba09eb79 Assos Assos
      if (isset($element['#grid_questions'][$question_key])) {
454
        foreach ($element['#grid_options'] as $option_value => $option_label) {
455
          if (in_array($option_value, $values)) {
456
            $row[] = array('data' => '<strong>X</strong>', 'class' => array('checkbox', 'webform-grid-option'));
457
          }
458
          else {
459
            $row[] = array('data' => '&nbsp;', 'class' => array('checkbox', 'webform-grid-option'));
460
          }
461 85ad3d82 Assos Assos
        }
462
      }
463 ba09eb79 Assos Assos
      else {
464
        $question_element['#title_display'] = 'none';
465
        $row[] = array(
466
          'data' => drupal_render($question_element),
467
          'colspan' => count($element['#grid_options']),
468
        );
469
      }
470 a45e4bc1 Assos Assos
      if ($right_titles) {
471
        $row[] = array('data' => isset($questions[1]) ? webform_filter_xss($questions[1]) : '', 'class' => array('webform-grid-question'));
472
      }
473 85ad3d82 Assos Assos
      $rows[] = $row;
474
    }
475
476
    $option_count = count($header) - 1;
477 a45e4bc1 Assos Assos
    $output = theme('table', array('header' => $header, 'rows' => $rows, 'sticky' => $element['#sticky'], 'attributes' => array('class' => array('webform-grid', 'webform-grid-' . $option_count))));
478 85ad3d82 Assos Assos
  }
479
  else {
480
    $items = array();
481 ba09eb79 Assos Assos
    foreach (element_children($element) as $question_key) {
482
      $question_element = $element[$question_key];
483
      if (isset($element['#grid_questions'][$question_key])) {
484
        $values = $question_element['#value'];
485
        $values = is_array($values) ? $values : array($values);
486
        foreach ($values as $value_key => $value) {
487
          if (isset($element['#grid_options'][$value])) {
488
            $values[$value_key] = $element['#grid_options'][$value];
489
          }
490
          else {
491
            unset($values[$value_key]);
492
          }
493
        }
494
        $value = implode(', ', $values);
495 e9984459 Assos Assos
      }
496
      else {
497 ba09eb79 Assos Assos
        $element[$question_key]['#title'] = '';
498
        $value = drupal_render($element[$question_key]);
499
      }
500
      $items[] = ' - ' . _webform_grid_question_header($question_element['#title']) . ': ' . $value;
501 8c72e82a Assos Assos
    }
502 85ad3d82 Assos Assos
    $output = implode("\n", $items);
503
  }
504
505
  return $output;
506
}
507
508
/**
509
 * Implements _webform_analysis_component().
510
 */
511 a45e4bc1 Assos Assos
function _webform_analysis_grid($component, $sids = array(), $single = FALSE, $join = NULL) {
512 85ad3d82 Assos Assos
  // Generate the list of options and questions.
513 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
514 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
515 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
516
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
517
  $options = _webform_select_replace_tokens($options, $node);
518 85ad3d82 Assos Assos
519
  // Generate a lookup table of results.
520
  $query = db_select('webform_submitted_data', 'wsd')
521
    ->fields('wsd', array('no', 'data'))
522 a45e4bc1 Assos Assos
    ->condition('wsd.nid', $component['nid'])
523
    ->condition('wsd.cid', $component['cid'])
524
    ->condition('wsd.data', '', '<>')
525
    ->groupBy('wsd.no')
526
    ->groupBy('wsd.data');
527
  $query->addExpression('COUNT(wsd.sid)', 'datacount');
528 85ad3d82 Assos Assos
529
  if (count($sids)) {
530 a45e4bc1 Assos Assos
    $query->condition('wsd.sid', $sids, 'IN');
531
  }
532
533
  if ($join) {
534
    $query->innerJoin($join, 'ws2_', 'wsd.sid = ws2_.sid');
535 85ad3d82 Assos Assos
  }
536
537
  $result = $query->execute();
538
  $counts = array();
539
  foreach ($result as $data) {
540
    $counts[$data->no][$data->data] = $data->datacount;
541
  }
542
543
  // Create an entire table to be put into the returned row.
544
  $rows = array();
545
  $header = array('');
546
547
  // Add options as a header row.
548
  foreach ($options as $option) {
549 a45e4bc1 Assos Assos
    $header[] = webform_filter_xss($option);
550 85ad3d82 Assos Assos
  }
551
552
  // Add questions as each row.
553
  foreach ($questions as $qkey => $question) {
554 a45e4bc1 Assos Assos
    $row = array(webform_filter_xss($question));
555 85ad3d82 Assos Assos
    foreach ($options as $okey => $option) {
556
      $row[] = !empty($counts[$qkey][$okey]) ? $counts[$qkey][$okey] : 0;
557
    }
558
    $rows[] = $row;
559
  }
560
561 ba09eb79 Assos Assos
  // Return return the table unless there are no internal questions in the grid.
562
  if ($rows) {
563
    return array(
564
      'table_header' => $header,
565
      'table_rows' => $rows,
566
    );
567
  }
568 85ad3d82 Assos Assos
}
569
570
/**
571
 * Implements _webform_table_component().
572
 */
573
function _webform_table_grid($component, $value) {
574 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
575 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
576 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
577 85ad3d82 Assos Assos
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
578 a45e4bc1 Assos Assos
  $options = _webform_select_replace_tokens($options, $node);
579 85ad3d82 Assos Assos
580
  $output = '';
581
  // Set the value as a single string.
582
  foreach ($questions as $key => $label) {
583
    if (isset($value[$key]) && isset($options[$value[$key]])) {
584 a45e4bc1 Assos Assos
      $output .= webform_filter_xss(_webform_grid_question_header($label)) . ': ' . webform_filter_xss($options[$value[$key]]) . '<br />';
585 85ad3d82 Assos Assos
    }
586
  }
587
588 ba09eb79 Assos Assos
  // Return output if the grid contains internal questions.
589
  if (count($questions)) {
590
    return $output;
591
  }
592 85ad3d82 Assos Assos
}
593
594
/**
595
 * Implements _webform_csv_headers_component().
596
 */
597
function _webform_csv_headers_grid($component, $export_options) {
598 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
599
  $items = _webform_select_options_from_text($component['extra']['questions'], TRUE);
600
  $items = _webform_select_replace_tokens($items, $node);
601
602 85ad3d82 Assos Assos
  $header = array();
603
  $header[0] = array('');
604 a45e4bc1 Assos Assos
  $header[1] = array($export_options['header_keys'] ? $component['form_key'] : $component['name']);
605 85ad3d82 Assos Assos
  $count = 0;
606
  foreach ($items as $key => $item) {
607
    // Empty column per sub-field in main header.
608
    if ($count != 0) {
609
      $header[0][] = '';
610
      $header[1][] = '';
611
    }
612
    // The value for this option.
613 a45e4bc1 Assos Assos
    $header[2][] = $export_options['header_keys'] ? $key : _webform_grid_question_header($item);
614 85ad3d82 Assos Assos
    $count++;
615
  }
616
617
  return $header;
618
}
619
620
/**
621
 * Implements _webform_csv_data_component().
622
 */
623
function _webform_csv_data_grid($component, $export_options, $value) {
624 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
625 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
626 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
627 85ad3d82 Assos Assos
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
628 a45e4bc1 Assos Assos
  $options = _webform_select_replace_tokens($options, $node);
629
630 85ad3d82 Assos Assos
  $return = array();
631
  foreach ($questions as $key => $question) {
632
    if (isset($value[$key]) && isset($options[$value[$key]])) {
633
      $return[] = $export_options['select_keys'] ? $value[$key] : $options[$value[$key]];
634
    }
635
    else {
636
      $return[] = '';
637
    }
638
  }
639
  return $return;
640
}
641
642 a45e4bc1 Assos Assos
/**
643
 * A Form API element validate function to check that all choices are unique.
644
 */
645
function _webform_edit_grid_unique_validate($element) {
646 ba09eb79 Assos Assos
  // Grids may contain nested multiple value select components.
647
  // Create a flat array of values.
648
  $values = array();
649 8c72e82a Assos Assos
  $element['#value'] = (array) $element['#value'];
650 feca1e4a Assos Assos
  array_walk_recursive($element['#value'], function ($a) use (&$values) {
651
    $values[] = $a;
652
  });
653 ba09eb79 Assos Assos
654
  $nr_unique = count(array_unique($values));
655
  $nr_values = count($values);
656 a45e4bc1 Assos Assos
  $nr_possible = count($element['#grid_options']);
657
  if (strlen($element['#grid_default']) && isset($element['#grid_options'][$element['#grid_default']])) {
658
    // A default is defined and is one of the options. Don't count default values
659
    // toward uniqueness.
660
    $nr_defaults = count(array_keys($element['#value'], $element['#grid_default']));
661
    if ($nr_defaults) {
662
      $nr_values -= $nr_defaults;
663
      $nr_unique--;
664
    }
665
  }
666
  if ($nr_unique < $nr_values && $nr_unique < $nr_possible) {
667
    // Fewer unique values than values means that at least one value is duplicated.
668
    // Fewer unique values than possible values means that there is a possible choice
669
    // that wasn't used.
670
    form_error($element, t('!title is not allowed to have the same answer for more than one question.', array('!title' => $element['#title'])));
671
  }
672
}
673
674 feca1e4a Assos Assos
/**
675 01f36513 Assos Assos
 * Theme function to render a grid component.
676 feca1e4a Assos Assos
 */
677 85ad3d82 Assos Assos
function theme_webform_grid($variables) {
678
  $element = $variables['element'];
679 a45e4bc1 Assos Assos
  $right_titles = _webform_grid_right_titles($element);
680 85ad3d82 Assos Assos
681
  $rows = array();
682 ba09eb79 Assos Assos
683 85ad3d82 Assos Assos
  // Set the header for the table.
684 ba09eb79 Assos Assos
  $header = _webform_grid_header($element, $right_titles);
685 85ad3d82 Assos Assos
686
  foreach (element_children($element) as $key) {
687
    $question_element = $element[$key];
688 ba09eb79 Assos Assos
    $title_element =& $question_element;
689
    if ($question_element['#type'] == 'select_or_other') {
690
      $title_element =& $question_element['select'];
691
    }
692
    $question_titles = explode('|', $title_element['#title'], 2);
693 85ad3d82 Assos Assos
694
    // Create a row with the question title.
695 ba09eb79 Assos Assos
    $required = !empty($question_element['#required']) ? theme('form_required_marker', array('element' => $question_element)) : '';
696
    $row = array(array('data' => t('!title !required', array('!title' => webform_filter_xss($question_titles[0]), '!required' => $required)), 'class' => array('webform-grid-question')));
697 85ad3d82 Assos Assos
698
    // Render each radio button in the row.
699 ba09eb79 Assos Assos
    if ($question_element['#type'] == 'radios' || $question_element['#type'] == 'checkboxes') {
700
      $radios = form_process_radios($question_element);
701
      foreach (element_children($radios) as $key) {
702
        $radio_title = $radios[$key]['#title'];
703
        if (!strlen($radio_title)) {
704
          $row[] = '&nbsp;';
705
        }
706
        else {
707
          $radios[$key]['#title'] = $question_element['#title'] . ' - ' . $radio_title;
708
          $radios[$key]['#title_display'] = 'invisible';
709
          $row[] = array('data' => drupal_render($radios[$key]), 'class' => array('checkbox', 'webform-grid-option'), 'data-label' => array($radio_title));
710
        }
711
      }
712
    }
713
    else {
714
      $title_element['#title_display'] = 'none';
715
      $row[] = array(
716
        'data' => drupal_render($question_element),
717
        'colspan' => count($element['#grid_options']),
718
      );
719 a45e4bc1 Assos Assos
    }
720
    if ($right_titles) {
721
      $row[] = array('data' => isset($question_titles[1]) ? webform_filter_xss($question_titles[1]) : '', 'class' => array('webform-grid-question'));
722 85ad3d82 Assos Assos
    }
723 ba09eb79 Assos Assos
724
    // Convert the parents array into a string, excluding the "submitted" wrapper.
725
    $nested_level = $question_element['#parents'][0] == 'submitted' ? 1 : 0;
726
    $parents = str_replace('_', '-', implode('--', array_slice($question_element['#parents'], $nested_level)));
727
728
    $rows[] = array(
729
      'data' => $row,
730
      'class' => empty($question_element['#grid_question'])
731 01f36513 Assos Assos
      ? array(
732
        'webform-component',
733
        'webform-component-' . str_replace('_', '-', $question_element['#type']),
734
        'webform-component--' . $parents,
735
      )
736 feca1e4a Assos Assos
      : array(),
737 ba09eb79 Assos Assos
    );
738 85ad3d82 Assos Assos
  }
739
740
  $option_count = count($header) - 1;
741 01f36513 Assos Assos
  return theme('table', array(
742
    'header' => $header,
743
    'rows' => $rows,
744
    'sticky' => $element['#sticky'],
745
    'attributes' => array(
746
      'class' => array(
747
        'webform-grid',
748
        'webform-grid-' . $option_count,
749
      ),
750
    ),
751
  ));
752 85ad3d82 Assos Assos
}
753 a45e4bc1 Assos Assos
754 ba09eb79 Assos Assos
/**
755
 * Generate a table header suitable for form or html display.
756 01f36513 Assos Assos
 *
757
 * @param array $element
758
 *   The element array.
759
 * @param bool $right_titles
760
 *   If TRUE, display a right-side title column.
761
 *
762
 * @return array
763
 *   An array of headers.
764 ba09eb79 Assos Assos
 */
765 01f36513 Assos Assos
function _webform_grid_header(array $element, $right_titles) {
766 ba09eb79 Assos Assos
  $titles = explode('|', $element['#title'], 2);
767 8d02775b Assos Assos
  $title_left = $titles[0];
768 01f36513 Assos Assos
  $header = array(
769
    array(
770 8d02775b Assos Assos
      'data' => _webform_grid_header_title($element, $title_left),
771 01f36513 Assos Assos
      'class' => array('webform-grid-question'),
772 8d02775b Assos Assos
      'scope' => 'col',
773 01f36513 Assos Assos
    ),
774
  );
775 ba09eb79 Assos Assos
  foreach ($element['#grid_options'] as $option) {
776 01f36513 Assos Assos
    $header[] = array(
777
      'data' => webform_filter_xss($option),
778
      'class' => array('checkbox', 'webform-grid-option'),
779
      'scope' => 'col',
780
    );
781 ba09eb79 Assos Assos
  }
782
  if ($right_titles) {
783 8d02775b Assos Assos
    $title_right = isset($titles[1]) ? $titles[1] : $title_left;
784 01f36513 Assos Assos
    $header[] = array(
785 8d02775b Assos Assos
      'data' => _webform_grid_header_title($element, $title_right),
786 01f36513 Assos Assos
      'class' => array('webform-grid-question'),
787 8d02775b Assos Assos
      'scope' => 'col',
788 01f36513 Assos Assos
    );
789 ba09eb79 Assos Assos
  }
790
  return $header;
791
}
792
793
/**
794
 * Create internal component title for table header, if any.
795
 */
796
function _webform_grid_header_title($element, $title) {
797
  $header_title = '';
798
  if ($element['#title_display'] == 'internal') {
799 8d02775b Assos Assos
    $header_title = $title;
800 ba09eb79 Assos Assos
  }
801
  return $header_title;
802
}
803
804 a45e4bc1 Assos Assos
/**
805
 * Determine if a right-side title column has been specified.
806
 */
807
function _webform_grid_right_titles($element) {
808 ba09eb79 Assos Assos
  if ($element['#title_display'] == 'internal' && substr_count($element['#title'], '|')) {
809
    return TRUE;
810
  }
811 a45e4bc1 Assos Assos
  foreach ($element['#grid_questions'] as $question_key => $question) {
812
    if (substr_count($question, '|')) {
813
      return TRUE;
814
    }
815
  }
816
  return FALSE;
817
}
818
819
/**
820
 * Create a question header for left, right or left/right question headers.
821
 */
822
function _webform_grid_question_header($text) {
823
  return implode('/', array_filter(explode('|', $text)));
824
}
825 6a93dd76 Assos Assos
826
/**
827
 * Element validation for Webform grid fields.
828
 *
829
 * Requires a component implementation because the default required validation
830
 * passes when at least one value is supplied, rather than every value. This
831
 * makes the server validation match the browser validation.
832
 */
833
function webform_validate_grid($element, $form_state) {
834
  if ($element['#required']) {
835
    $values = $form_state['input'];
836
    foreach ($element['#parents'] as $key) {
837
      $values = isset($values[$key]) ? $values[$key] : $values;
838
    }
839 01f36513 Assos Assos
    // Remove any values that aren't grid question (i.e. nested components).
840 8c72e82a Assos Assos
    $grid_questions = $element['#grid_questions'];
841
    $values = array_intersect_key($values, $grid_questions);
842
    // Remove any unanswered grid questions.
843 feca1e4a Assos Assos
    $answers = array_filter($values, function ($item) {
844
      return !is_null($item);
845
    });
846 8c72e82a Assos Assos
    // Give required errors for any questions that aren't answered.
847
    foreach (array_diff_key($grid_questions, $answers) as $question_key => $question) {
848 01f36513 Assos Assos
      // If the question is still required (e.g not modified by an after_build
849
      // function), give the required error.
850
      if (!empty($element[$question_key]['#required'])) {
851
        form_error($element[$question_key], t('!question field within !name is required.', array('!question' => $question, '!name' => $element['#title'])));
852
      }
853 6a93dd76 Assos Assos
    }
854
  }
855
}