Projet

Général

Profil

Paste
Télécharger (29,1 ko) Statistiques
| Branche: | Révision:

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

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
                          '<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
                        '<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
    '#parents' => array('extra', 'optrand')
169
  );
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
    '#parents' => array('extra', 'qrand')
176
  );
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
    '#parents' => array('extra', 'sticky')
183
  );
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
    '#translatable' => array('title', 'description', 'grid_options', 'grid_questions'),
229
  );
230
231 a45e4bc1 Assos Assos
  // Enforce uniqueness.
232
  if ($component['extra']['unique']) {
233
    $element['#element_validate'][] = '_webform_edit_grid_unique_validate';
234 85ad3d82 Assos Assos
  }
235
236
  return $element;
237
}
238
239
/**
240
 * A Form API #process function for Webform grid fields.
241
 */
242
function webform_expand_grid($element) {
243
  $options = $element['#grid_options'];
244
  $questions = $element['#grid_questions'];
245 ba09eb79 Assos Assos
  $weights = array();
246 85ad3d82 Assos Assos
247 ba09eb79 Assos Assos
  // Process questions and options from nested components.
248
  foreach (element_children($element) as $key) {
249
    $question = $element[$key];
250
    // Both forms and grid displays have #webform_component.
251
    if (isset($question['#webform_component']) &&
252
        $question['#webform_component']['type'] == 'select' &&
253
        !$question['#webform_component']['extra']['aslist'] &&
254
        !$question['#webform_component']['extra']['other_option']) {
255
      $options = webform_grid_merge_options($options, $question['#options']);
256
      $weights[$key] = $question['#weight'];
257
    }
258 85ad3d82 Assos Assos
  }
259
260 ba09eb79 Assos Assos
  // Add the internal grid questions.
261
  $weight = -1000;
262
  $value = isset($element['#default_value']) ? $element['#default_value'] : array();
263 85ad3d82 Assos Assos
  foreach ($questions as $key => $question) {
264
    if ($question != '') {
265 ba09eb79 Assos Assos
      $question_value = isset($value[$key]) && $value[$key] !== '' ? $value[$key] : NULL;
266 85ad3d82 Assos Assos
      $element[$key] = array(
267 ba09eb79 Assos Assos
        '#grid_question' => TRUE,
268 85ad3d82 Assos Assos
        '#title' => $question,
269
        '#required' => $element['#required'],
270 ba09eb79 Assos Assos
        '#options' => $element['#grid_options'],
271 85ad3d82 Assos Assos
        '#type' => 'radios',
272 ba09eb79 Assos Assos
        '#default_value' => $question_value,
273
        '#value' => $question_value,
274 85ad3d82 Assos Assos
        '#process' => array('form_process_radios', 'webform_expand_select_ids'),
275
276
        // Webform handles validation manually.
277
        '#validated' => TRUE,
278
        '#webform_validated' => FALSE,
279
        '#translatable' => array('title'),
280 ba09eb79 Assos Assos
        '#weight' => $weight,
281 85ad3d82 Assos Assos
      );
282 a45e4bc1 Assos Assos
283
      // Add HTML5 required attribute, if needed.
284
      if ($element['#required']) {
285
        $element[$key]['#attributes']['required'] = 'required';
286
      }
287 ba09eb79 Assos Assos
288
      $weights[$key] = $weight;
289
      $weight++;
290 85ad3d82 Assos Assos
    }
291
  }
292
293 ba09eb79 Assos Assos
  if (!empty($element['#optrand'])) {
294
    _webform_shuffle_options($options);
295
  }
296
  $element['#grid_options'] = $options;
297
298
  asort($weights);
299
  if (!empty($element['#qrand'])) {
300
    _webform_shuffle_options($weights);
301
  }
302
  $weight = min($weights);
303
  foreach ($weights as $key => $old_weight) {
304
    $element[$key]['#options'] = webform_grid_remove_options($options, $element[$key]['#options']);
305
    $element[$key]['#weight'] = $weight++;
306
    $element['#grid_questions'][$key] = $element[$key]['#title'];
307
  }
308
309
  return $element;
310
}
311
312
/**
313
 * Helper. Merge select component options in order.
314
 *
315
 * @param array $existing
316
 *   An array of existing values into which any values from $new that aren't in
317
 *   $existing are inserted.
318
 * @param array $new
319
 *   Values to be inserted into $existing.
320
 * @return array
321
 *   The merged array.
322
 */
323
function webform_grid_merge_options($existing, $new) {
324
  $insert = NULL;
325
  $queue = array();
326
  foreach ($new as $key => $value) {
327
    if (isset($existing[$key])) {
328
      // Insert the queue before the found item.
329
      $insert = array_search($key, array_keys($existing));
330
      if ($queue) {
331
        $existing = array_slice($existing, 0, $insert, TRUE) +
332
                    $queue +
333
                    array_slice($existing, $insert, NULL, TRUE);
334
        $insert += count($queue);
335
        $queue = array();
336
      }
337
      $insert++;
338
    }
339
    elseif (is_null($insert)) {
340
      // It is not yet clear yet where to put this item. Add it to the queue.
341
      $queue[$key] = $value;
342 85ad3d82 Assos Assos
    }
343
    else {
344 ba09eb79 Assos Assos
      // PHP array_splice does not preserved the keys of the inserted array,
345
      // but array_slice does (if the preserve keys parameter is TRUE).
346
      $existing = array_slice($existing, 0, $insert, TRUE) +
347
                  array($key => $value) +
348
                  array_slice($existing, $insert, NULL, TRUE);
349
      $insert++;
350 85ad3d82 Assos Assos
    }
351
  }
352 ba09eb79 Assos Assos
  // Append any left over queued items.
353
  $existing += $queue;
354
  return $existing;
355
}
356 85ad3d82 Assos Assos
357 ba09eb79 Assos Assos
/**
358
 * Helper. Replace missing options with empty values.
359
 *
360
 * @param array $header
361
 *   An array of options to be used at the grid table header.
362
 * @param array $row_options
363
 *   An array of options to be used for this row.
364
 * @return array
365
 *   The $row_options with any missing options replaced with empty values.
366
 **/
367
function webform_grid_remove_options($header, $row_options) {
368
  foreach ($header as $key => $value) {
369
    if (!isset($row_options[$key])) {
370
      $header[$key] = '';
371
    }
372
  }
373
  return $header;
374 85ad3d82 Assos Assos
}
375
376
/**
377
 * Implements _webform_display_component().
378
 */
379 a45e4bc1 Assos Assos
function _webform_display_grid($component, $value, $format = 'html', $submission = array()) {
380
  $node = node_load($component['nid']);
381 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
382 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
383 85ad3d82 Assos Assos
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
384 a45e4bc1 Assos Assos
  $options = _webform_select_replace_tokens($options, $node);
385 85ad3d82 Assos Assos
386
  $element = array(
387
    '#title' => $component['name'],
388 a45e4bc1 Assos Assos
    '#title_display' => $component['extra']['title_display'] ? $component['extra']['title_display'] : 'before',
389 85ad3d82 Assos Assos
    '#weight' => $component['weight'],
390
    '#format' => $format,
391
    '#grid_questions' => $questions,
392
    '#grid_options' => $options,
393 ba09eb79 Assos Assos
    '#default_value' => $value,
394 a45e4bc1 Assos Assos
    '#sticky' => $component['extra']['sticky'],
395 85ad3d82 Assos Assos
    '#theme' => 'webform_display_grid',
396
    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
397
    '#sorted' => TRUE,
398
    '#translatable' => array('#title', '#grid_questions', '#grid_options'),
399
  );
400
401
  foreach ($questions as $key => $question) {
402
    if ($question !== '') {
403
      $element[$key] = array(
404
        '#title' => $question,
405
        '#value' => isset($value[$key]) ? $value[$key] : NULL,
406
        '#translatable' => array('#title', '#value'),
407
      );
408
    }
409
  }
410
411
  return $element;
412
}
413
414 ba09eb79 Assos Assos
/**
415
 * Preprocess function for displaying a grid component.
416
 */
417
function template_preprocess_webform_display_grid(&$variables) {
418
  $element =& $variables['element'];
419
  // Expand the grid, suppressing randomization. This builds the grid
420
  // questions and options.
421
  $element['#qrand'] = FALSE;
422
  $element['#optrand'] = FALSE;
423
  $element['#required'] = FALSE;
424
  $element = webform_expand_grid($element);
425
}
426
427 85ad3d82 Assos Assos
/**
428
 * Format the text output for this component.
429
 */
430
function theme_webform_display_grid($variables) {
431
  $element = $variables['element'];
432
433
  $component = $element['#webform_component'];
434
  $format = $element['#format'];
435
436
  if ($format == 'html') {
437 a45e4bc1 Assos Assos
    $right_titles = _webform_grid_right_titles($element);
438 85ad3d82 Assos Assos
    $rows = array();
439 ba09eb79 Assos Assos
440
    // Set the header for the table.
441
    $header = _webform_grid_header($element, $right_titles);
442
443
    foreach (element_children($element) as $question_key) {
444
      $question_element = $element[$question_key];
445 85ad3d82 Assos Assos
      $row = array();
446 ba09eb79 Assos Assos
      $questions = explode('|', $question_element['#title'], 2);
447
      $values = $question_element['#value'];
448
      $values = is_array($values) ? $values : array($values);
449 a45e4bc1 Assos Assos
      $row[] = array('data' => webform_filter_xss($questions[0]), 'class' => array('webform-grid-question'));
450 ba09eb79 Assos Assos
      if (isset($element['#grid_questions'][$question_key])) {
451
        foreach ($element['#grid_options'] as $option_value => $option_label) {
452
          if (in_array($option_value, $values)) {
453
            $row[] = array('data' => '<strong>X</strong>', 'class' => array('checkbox', 'webform-grid-option'));
454
          }
455
          else {
456
            $row[] = array('data' => '&nbsp;', 'class' => array('checkbox', 'webform-grid-option'));
457
          }
458 85ad3d82 Assos Assos
        }
459
      }
460 ba09eb79 Assos Assos
      else {
461
        $question_element['#title_display'] = 'none';
462
        $row[] = array(
463
          'data' => drupal_render($question_element),
464
          'colspan' => count($element['#grid_options']),
465
        );
466
      }
467 a45e4bc1 Assos Assos
      if ($right_titles) {
468
        $row[] = array('data' => isset($questions[1]) ? webform_filter_xss($questions[1]) : '', 'class' => array('webform-grid-question'));
469
      }
470 85ad3d82 Assos Assos
      $rows[] = $row;
471
    }
472
473
    $option_count = count($header) - 1;
474 a45e4bc1 Assos Assos
    $output = theme('table', array('header' => $header, 'rows' => $rows, 'sticky' => $element['#sticky'], 'attributes' => array('class' => array('webform-grid', 'webform-grid-' . $option_count))));
475 85ad3d82 Assos Assos
  }
476
  else {
477
    $items = array();
478 ba09eb79 Assos Assos
    foreach (element_children($element) as $question_key) {
479
      $question_element = $element[$question_key];
480
      if (isset($element['#grid_questions'][$question_key])) {
481
        $values = $question_element['#value'];
482
        $values = is_array($values) ? $values : array($values);
483
        foreach ($values as $value_key => $value) {
484
          if (isset($element['#grid_options'][$value])) {
485
            $values[$value_key] = $element['#grid_options'][$value];
486
          }
487
          else {
488
            unset($values[$value_key]);
489
          }
490
        }
491
        $value = implode(', ', $values);
492
      } else {
493
        $element[$question_key]['#title'] = '';
494
        $value = drupal_render($element[$question_key]);
495
      }
496
      $items[] = ' - ' . _webform_grid_question_header($question_element['#title']) . ': ' . $value;
497
   }
498 85ad3d82 Assos Assos
    $output = implode("\n", $items);
499
  }
500
501
  return $output;
502
}
503
504
/**
505
 * Implements _webform_analysis_component().
506
 */
507 a45e4bc1 Assos Assos
function _webform_analysis_grid($component, $sids = array(), $single = FALSE, $join = NULL) {
508 85ad3d82 Assos Assos
  // Generate the list of options and questions.
509 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
510 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
511 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
512
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
513
  $options = _webform_select_replace_tokens($options, $node);
514 85ad3d82 Assos Assos
515
  // Generate a lookup table of results.
516
  $query = db_select('webform_submitted_data', 'wsd')
517
    ->fields('wsd', array('no', 'data'))
518 a45e4bc1 Assos Assos
    ->condition('wsd.nid', $component['nid'])
519
    ->condition('wsd.cid', $component['cid'])
520
    ->condition('wsd.data', '', '<>')
521
    ->groupBy('wsd.no')
522
    ->groupBy('wsd.data');
523
  $query->addExpression('COUNT(wsd.sid)', 'datacount');
524 85ad3d82 Assos Assos
525
  if (count($sids)) {
526 a45e4bc1 Assos Assos
    $query->condition('wsd.sid', $sids, 'IN');
527
  }
528
529
  if ($join) {
530
    $query->innerJoin($join, 'ws2_', 'wsd.sid = ws2_.sid');
531 85ad3d82 Assos Assos
  }
532
533
  $result = $query->execute();
534
  $counts = array();
535
  foreach ($result as $data) {
536
    $counts[$data->no][$data->data] = $data->datacount;
537
  }
538
539
  // Create an entire table to be put into the returned row.
540
  $rows = array();
541
  $header = array('');
542
543
  // Add options as a header row.
544
  foreach ($options as $option) {
545 a45e4bc1 Assos Assos
    $header[] = webform_filter_xss($option);
546 85ad3d82 Assos Assos
  }
547
548
  // Add questions as each row.
549
  foreach ($questions as $qkey => $question) {
550 a45e4bc1 Assos Assos
    $row = array(webform_filter_xss($question));
551 85ad3d82 Assos Assos
    foreach ($options as $okey => $option) {
552
      $row[] = !empty($counts[$qkey][$okey]) ? $counts[$qkey][$okey] : 0;
553
    }
554
    $rows[] = $row;
555
  }
556
557 ba09eb79 Assos Assos
  // Return return the table unless there are no internal questions in the grid.
558
  if ($rows) {
559
    return array(
560
      'table_header' => $header,
561
      'table_rows' => $rows,
562
    );
563
  }
564 85ad3d82 Assos Assos
}
565
566
/**
567
 * Implements _webform_table_component().
568
 */
569
function _webform_table_grid($component, $value) {
570 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
571 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
572 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
573 85ad3d82 Assos Assos
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
574 a45e4bc1 Assos Assos
  $options = _webform_select_replace_tokens($options, $node);
575 85ad3d82 Assos Assos
576
  $output = '';
577
  // Set the value as a single string.
578
  foreach ($questions as $key => $label) {
579
    if (isset($value[$key]) && isset($options[$value[$key]])) {
580 a45e4bc1 Assos Assos
      $output .= webform_filter_xss(_webform_grid_question_header($label)) . ': ' . webform_filter_xss($options[$value[$key]]) . '<br />';
581 85ad3d82 Assos Assos
    }
582
  }
583
584 ba09eb79 Assos Assos
  // Return output if the grid contains internal questions.
585
  if (count($questions)) {
586
    return $output;
587
  }
588 85ad3d82 Assos Assos
}
589
590
/**
591
 * Implements _webform_csv_headers_component().
592
 */
593
function _webform_csv_headers_grid($component, $export_options) {
594 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
595
  $items = _webform_select_options_from_text($component['extra']['questions'], TRUE);
596
  $items = _webform_select_replace_tokens($items, $node);
597
598 85ad3d82 Assos Assos
  $header = array();
599
  $header[0] = array('');
600 a45e4bc1 Assos Assos
  $header[1] = array($export_options['header_keys'] ? $component['form_key'] : $component['name']);
601 85ad3d82 Assos Assos
  $count = 0;
602
  foreach ($items as $key => $item) {
603
    // Empty column per sub-field in main header.
604
    if ($count != 0) {
605
      $header[0][] = '';
606
      $header[1][] = '';
607
    }
608
    // The value for this option.
609 a45e4bc1 Assos Assos
    $header[2][] = $export_options['header_keys'] ? $key : _webform_grid_question_header($item);
610 85ad3d82 Assos Assos
    $count++;
611
  }
612
613
  return $header;
614
}
615
616
/**
617
 * Implements _webform_csv_data_component().
618
 */
619
function _webform_csv_data_grid($component, $export_options, $value) {
620 a45e4bc1 Assos Assos
  $node = node_load($component['nid']);
621 85ad3d82 Assos Assos
  $questions = _webform_select_options_from_text($component['extra']['questions'], TRUE);
622 a45e4bc1 Assos Assos
  $questions = _webform_select_replace_tokens($questions, $node);
623 85ad3d82 Assos Assos
  $options = _webform_select_options_from_text($component['extra']['options'], TRUE);
624 a45e4bc1 Assos Assos
  $options = _webform_select_replace_tokens($options, $node);
625
626 85ad3d82 Assos Assos
  $return = array();
627
  foreach ($questions as $key => $question) {
628
    if (isset($value[$key]) && isset($options[$value[$key]])) {
629
      $return[] = $export_options['select_keys'] ? $value[$key] : $options[$value[$key]];
630
    }
631
    else {
632
      $return[] = '';
633
    }
634
  }
635
  return $return;
636
}
637
638 a45e4bc1 Assos Assos
/**
639
 * A Form API element validate function to check that all choices are unique.
640
 */
641
function _webform_edit_grid_unique_validate($element) {
642 ba09eb79 Assos Assos
  // Grids may contain nested multiple value select components.
643
  // Create a flat array of values.
644
  $values = array();
645
  array_walk_recursive($element['#value'], function($a) use (&$values) { $values[] = $a; });
646
647
  $nr_unique = count(array_unique($values));
648
  $nr_values = count($values);
649 a45e4bc1 Assos Assos
  $nr_possible = count($element['#grid_options']);
650
  if (strlen($element['#grid_default']) && isset($element['#grid_options'][$element['#grid_default']])) {
651
    // A default is defined and is one of the options. Don't count default values
652
    // toward uniqueness.
653
    $nr_defaults = count(array_keys($element['#value'], $element['#grid_default']));
654
    if ($nr_defaults) {
655
      $nr_values -= $nr_defaults;
656
      $nr_unique--;
657
    }
658
  }
659
  if ($nr_unique < $nr_values && $nr_unique < $nr_possible) {
660
    // Fewer unique values than values means that at least one value is duplicated.
661
    // Fewer unique values than possible values means that there is a possible choice
662
    // that wasn't used.
663
    form_error($element, t('!title is not allowed to have the same answer for more than one question.', array('!title' => $element['#title'])));
664
  }
665
}
666
667 85ad3d82 Assos Assos
function theme_webform_grid($variables) {
668
  $element = $variables['element'];
669 a45e4bc1 Assos Assos
  $right_titles = _webform_grid_right_titles($element);
670 85ad3d82 Assos Assos
671
  $rows = array();
672 ba09eb79 Assos Assos
673 85ad3d82 Assos Assos
  // Set the header for the table.
674 ba09eb79 Assos Assos
  $header = _webform_grid_header($element, $right_titles);
675 85ad3d82 Assos Assos
676
  foreach (element_children($element) as $key) {
677
    $question_element = $element[$key];
678 ba09eb79 Assos Assos
    $title_element =& $question_element;
679
    if ($question_element['#type'] == 'select_or_other') {
680
      $title_element =& $question_element['select'];
681
    }
682
    $question_titles = explode('|', $title_element['#title'], 2);
683 85ad3d82 Assos Assos
684
    // Create a row with the question title.
685 ba09eb79 Assos Assos
    $required = !empty($question_element['#required']) ? theme('form_required_marker', array('element' => $question_element)) : '';
686
    $row = array(array('data' => t('!title !required', array('!title' => webform_filter_xss($question_titles[0]), '!required' => $required)), 'class' => array('webform-grid-question')));
687 85ad3d82 Assos Assos
688
    // Render each radio button in the row.
689 ba09eb79 Assos Assos
    if ($question_element['#type'] == 'radios' || $question_element['#type'] == 'checkboxes') {
690
      $radios = form_process_radios($question_element);
691
      foreach (element_children($radios) as $key) {
692
        $radio_title = $radios[$key]['#title'];
693
        if (!strlen($radio_title)) {
694
          $row[] = '&nbsp;';
695
        }
696
        else {
697
          $radios[$key]['#title'] = $question_element['#title'] . ' - ' . $radio_title;
698
          $radios[$key]['#title_display'] = 'invisible';
699
          $row[] = array('data' => drupal_render($radios[$key]), 'class' => array('checkbox', 'webform-grid-option'), 'data-label' => array($radio_title));
700
        }
701
      }
702
    }
703
    else {
704
      $title_element['#title_display'] = 'none';
705
      $row[] = array(
706
        'data' => drupal_render($question_element),
707
        'colspan' => count($element['#grid_options']),
708
      );
709 a45e4bc1 Assos Assos
    }
710
    if ($right_titles) {
711
      $row[] = array('data' => isset($question_titles[1]) ? webform_filter_xss($question_titles[1]) : '', 'class' => array('webform-grid-question'));
712 85ad3d82 Assos Assos
    }
713 ba09eb79 Assos Assos
714
    // Convert the parents array into a string, excluding the "submitted" wrapper.
715
    $nested_level = $question_element['#parents'][0] == 'submitted' ? 1 : 0;
716
    $parents = str_replace('_', '-', implode('--', array_slice($question_element['#parents'], $nested_level)));
717
718
    $rows[] = array(
719
      'data' => $row,
720
      'class' => empty($question_element['#grid_question'])
721
                    ? array('webform-component', 'webform-component-' . str_replace('_', '-', $question_element['#type']), 'webform-component--' . $parents)
722
                    : array(),
723
    );
724 85ad3d82 Assos Assos
  }
725
726
  $option_count = count($header) - 1;
727 a45e4bc1 Assos Assos
  return theme('table', array('header' => $header, 'rows' => $rows, 'sticky' => $element['#sticky'], 'attributes' => array('class' => array('webform-grid', 'webform-grid-' . $option_count))));
728 85ad3d82 Assos Assos
}
729 a45e4bc1 Assos Assos
730 ba09eb79 Assos Assos
/**
731
 * Generate a table header suitable for form or html display.
732
 */
733
function _webform_grid_header($element, $right_titles) {
734
  $titles = explode('|', $element['#title'], 2);
735
  $header = array(array('data' => _webform_grid_header_title($element, $titles[0]), 'class' => array('webform-grid-question')));
736
  foreach ($element['#grid_options'] as $option) {
737
    $header[] = array('data' => webform_filter_xss($option), 'class' => array('checkbox', 'webform-grid-option'));
738
  }
739
  if ($right_titles) {
740
    $header[] = array('data' => _webform_grid_header_title($element, isset($titles[1]) ? $titles[1] : ''), 'class' => array('webform-grid-question'));
741
  }
742
  return $header;
743
}
744
745
/**
746
 * Create internal component title for table header, if any.
747
 */
748
function _webform_grid_header_title($element, $title) {
749
  $header_title = '';
750
  if ($element['#title_display'] == 'internal') {
751
    $variables = array('element' => $element);
752
    $variables['element']['#title_display'] = 'before';
753
    $variables['element']['#title'] = $title;
754
    $header_title = theme('form_element_label', $variables);
755
  }
756
  return $header_title;
757
}
758
759 a45e4bc1 Assos Assos
/**
760
 * Determine if a right-side title column has been specified.
761
 */
762
function _webform_grid_right_titles($element) {
763 ba09eb79 Assos Assos
  if ($element['#title_display'] == 'internal' && substr_count($element['#title'], '|')) {
764
    return TRUE;
765
  }
766 a45e4bc1 Assos Assos
  foreach ($element['#grid_questions'] as $question_key => $question) {
767
    if (substr_count($question, '|')) {
768
      return TRUE;
769
    }
770
  }
771
  return FALSE;
772
}
773
774
/**
775
 * Create a question header for left, right or left/right question headers.
776
 */
777
function _webform_grid_question_header($text) {
778
  return implode('/', array_filter(explode('|', $text)));
779
}