Projet

Général

Profil

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

root / drupal7 / sites / all / modules / webform / includes / webform.components.inc @ e9f59ce0

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Webform module component handling.
6
 */
7
8
/**
9
 * Provides interface and database handling for editing components of a webform.
10
 *
11
 * @author Nathan Haug <nate@lullabot.com>
12
 */
13
14
/**
15
 * Overview page of all components for this webform.
16
 */
17
function webform_components_page($node, $page_number = 1) {
18
  $output = drupal_get_form('webform_components_form', $node);
19
20
  return array(
21
    '#theme' => 'webform_components_page',
22
    '#node' => $node,
23
    '#form' => $output,
24
  );
25
}
26
27
/**
28
 * Theme the output of the main components page.
29
 *
30
 * This theming provides a way to toggle between the editing modes if Form
31
 * Builder module is available.
32
 */
33
function theme_webform_components_page($variables) {
34
  $node = $variables['node'];
35
  $form = $variables['form'];
36
37
  return drupal_render($form);
38
}
39
40
/**
41
 * The table-based listing of all components for this webform.
42
 */
43
function webform_components_form($form, $form_state, $node) {
44
  $form = array(
45
    '#tree' => TRUE,
46
    '#node' => $node,
47
    'components' => array(),
48
  );
49
50
  $form['nid'] = array(
51
    '#type' => 'value',
52
    '#value' => $node->nid,
53
  );
54
55
  $options = array();
56
  foreach ($node->webform['components'] as $cid => $component) {
57
    $options[$cid] = check_plain($component['name']);
58
    $form['components'][$cid]['cid'] = array(
59
      '#type' => 'hidden',
60
      '#default_value' => $component['cid'],
61
    );
62
    $form['components'][$cid]['pid'] = array(
63
      '#type' => 'hidden',
64
      '#default_value' => $component['pid'],
65
    );
66
    $form['components'][$cid]['weight'] = array(
67
      '#type' => 'textfield',
68
      '#size' => 4,
69
      '#title' => t('Weight'),
70
      '#default_value' => $component['weight'],
71
    );
72 a45e4bc1 Assos Assos
    $form['components'][$cid]['required'] = array(
73 85ad3d82 Assos Assos
      '#type' => 'checkbox',
74 a45e4bc1 Assos Assos
      '#title' => t('Required'),
75
      '#default_value' => $component['required'],
76 85ad3d82 Assos Assos
      '#access' => webform_component_feature($component['type'], 'required'),
77
    );
78
    if (!isset($max_weight) || $component['weight'] > $max_weight) {
79
      $max_weight = $component['weight'];
80
    }
81
  }
82
83
  $form['add']['name'] = array(
84
    '#type' => 'textfield',
85 ba09eb79 Assos Assos
    '#size' => 30,
86 a45e4bc1 Assos Assos
    '#maxlength' => NULL,
87 85ad3d82 Assos Assos
  );
88
89
  $form['add']['type'] = array(
90
    '#type' => 'select',
91
    '#options' => webform_component_options(),
92
    '#weight' => 3,
93
    '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['type'] : 'textfield',
94
  );
95 a45e4bc1 Assos Assos
  $form['add']['required'] = array(
96 85ad3d82 Assos Assos
    '#type' => 'checkbox',
97
  );
98
  $form['add']['cid'] = array(
99
    '#type' => 'hidden',
100
    '#default_value' => '',
101
  );
102
  $form['add']['pid'] = array(
103
    '#type' => 'hidden',
104
    '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['pid'] : 0,
105
  );
106
  $form['add']['weight'] = array(
107
    '#type' => 'textfield',
108
    '#size' => 4,
109
    '#delta' => count($node->webform['components']) > 10 ? count($node->webform['components']) : 10,
110
  );
111
112
  if (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) {
113
    // Make the new component appear by default directly after the one that was
114
    // just added.
115
    $form['add']['weight']['#default_value'] = $node->webform['components'][$_GET['cid']]['weight'] + 1;
116
    foreach (array_keys($node->webform['components']) as $cid) {
117
      // Adjust all later components also, to make sure none of them have the
118
      // same weight as the new component.
119
      if ($form['components'][$cid]['weight']['#default_value'] >= $form['add']['weight']['#default_value']) {
120
        $form['components'][$cid]['weight']['#default_value']++;
121
      }
122
    }
123
  }
124
  else {
125
    // If no component was just added, the new component should appear by
126
    // default at the end of the list.
127
    $form['add']['weight']['#default_value'] = isset($max_weight) ? $max_weight + 1 : 0;
128
  }
129
130
  $form['add']['add'] = array(
131
    '#type' => 'submit',
132
    '#value' => t('Add'),
133
    '#weight' => 45,
134
    '#validate' => array('webform_components_form_add_validate', 'webform_components_form_validate'),
135
    '#submit' => array('webform_components_form_add_submit'),
136
  );
137
138 a45e4bc1 Assos Assos
  $form['actions'] = array(
139
    '#type' => 'actions',
140
    '#weight' => 45,
141
  );
142 3753f249 Assos Assos
  $form['actions']['submit'] = array(
143 85ad3d82 Assos Assos
    '#type' => 'submit',
144
    '#value' => t('Save'),
145
    '#access' => count($node->webform['components']) > 0,
146
  );
147 a45e4bc1 Assos Assos
  $form['warning'] = array(
148
    '#weight' => -1,
149
  );
150
  webform_input_vars_check($form, $form_state, 'components', 'warning');
151 85ad3d82 Assos Assos
152
  return $form;
153
}
154
155
/**
156 a45e4bc1 Assos Assos
 * Preprocess variables for theming the webform components form.
157 85ad3d82 Assos Assos
 */
158 a45e4bc1 Assos Assos
function template_preprocess_webform_components_form(&$variables) {
159 85ad3d82 Assos Assos
  $form = $variables['form'];
160
161
  $form['components']['#attached']['library'][] = array('webform', 'admin');
162
163
  // TODO: Attach these. See http://drupal.org/node/732022.
164
  drupal_add_tabledrag('webform-components', 'order', 'sibling', 'webform-weight');
165
  drupal_add_tabledrag('webform-components', 'match', 'parent', 'webform-pid', 'webform-pid', 'webform-cid');
166
167
  $node = $form['#node'];
168
169 ba09eb79 Assos Assos
  $header = array(t('Label'), t('Form key'), t('Type'), t('Value'), t('Required'), t('Weight'), array('data' => t('Operations'), 'colspan' => 3));
170 85ad3d82 Assos Assos
  $rows = array();
171
172
  // Add a row containing form elements for a new item.
173
  unset($form['add']['name']['#title'], $form['add_type']['#description']);
174 a45e4bc1 Assos Assos
  $form['add']['name']['#attributes']['placeholder'] = t('New component name');
175
  $form['add']['cid']['#attributes']['class'][] = 'webform-cid';
176
  $form['add']['pid']['#attributes']['class'][] = 'webform-pid';
177
  $form['add']['weight']['#attributes']['class'][] = 'webform-weight';
178 85ad3d82 Assos Assos
  $row_data = array(
179 ba09eb79 Assos Assos
    array('data' => drupal_render($form['add']['name']), 'class' => array('webform-component-name'), 'colspan' => 2),
180 a45e4bc1 Assos Assos
    array('data' => drupal_render($form['add']['type']), 'class' => array('webform-component-type')),
181
    array('data' => '', 'class' => array('webform-component-value')),
182
    array('data' => drupal_render($form['add']['required']), 'class' => array('webform-component-required', 'checkbox')),
183
    array('data' => drupal_render($form['add']['cid']) . drupal_render($form['add']['pid']) . drupal_render($form['add']['weight'])),
184
    array('colspan' => 3, 'data' => drupal_render($form['add']['add']), 'class' => array('webform-component-add')),
185 85ad3d82 Assos Assos
  );
186 a45e4bc1 Assos Assos
  $add_form = array('data' => $row_data, 'class' => array('draggable', 'webform-add-form', 'tabledrag-leaf'));
187 85ad3d82 Assos Assos
188
  if (!empty($node->webform['components'])) {
189
    $component_tree = array();
190
    $page_count = 1;
191
    _webform_components_tree_build($node->webform['components'], $component_tree, 0, $page_count);
192
    $component_tree = _webform_components_tree_sort($component_tree);
193 a45e4bc1 Assos Assos
    // Build the table rows recursively.
194 85ad3d82 Assos Assos
    foreach ($component_tree['children'] as $cid => $component) {
195
      _webform_components_form_rows($node, $cid, $component, 0, $form, $rows, $add_form);
196
    }
197
  }
198
  else {
199
    $rows[] = array(array('data' => t('No Components, add a component below.'), 'colspan' => 9));
200
  }
201
202
  // Append the add form if not already printed.
203
  if ($add_form) {
204
    $rows[] = $add_form;
205
  }
206
207 a45e4bc1 Assos Assos
  $variables['rows'] = $rows;
208
  $variables['header'] = $header;
209
  $variables['form'] = $form;
210
}
211
212
/**
213
 * Recursive function for nesting components into a table.
214
 *
215
 * @see preprocess_webform_components_form()
216
 */
217
function _webform_components_form_rows($node, $cid, $component, $level, &$form, &$rows, &$add_form) {
218
  // Create presentable values.
219 ba09eb79 Assos Assos
  $form_key = truncate_utf8($component['form_key'], 30, FALSE, TRUE);
220
  $value = truncate_utf8($component['value'], 30, TRUE, TRUE, 20);
221 a45e4bc1 Assos Assos
222
  // Remove individual titles from the required and weight fields.
223
  unset($form['components'][$cid]['required']['#title']);
224
  unset($form['components'][$cid]['pid']['#title']);
225
  unset($form['components'][$cid]['weight']['#title']);
226
227
  // Add special classes for weight and parent fields.
228
  $form['components'][$cid]['cid']['#attributes']['class'] = array('webform-cid');
229
  $form['components'][$cid]['pid']['#attributes']['class'] = array('webform-pid');
230
  $form['components'][$cid]['weight']['#attributes']['class'] = array('webform-weight');
231
232
  // Build indentation for this row.
233
  $indents = '';
234
  for ($n = 1; $n <= $level; $n++) {
235
    $indents .= '<div class="indentation">&nbsp;</div>';
236
  }
237
238
  // Add each component to a table row.
239
  $row_data = array(
240
    array('data' => $indents . filter_xss($component['name']), 'class' => array('webform-component-name')),
241 ba09eb79 Assos Assos
    array('data' => check_plain($form_key), 'class' => array('webform-component-formkey')) +
242
          ($component['form_key'] == $form_key ? array() : array('title' => $component['form_key'])),
243 a45e4bc1 Assos Assos
    array('data' => $form['add']['type']['#options'][$component['type']], 'class' => array('webform-component-type')),
244 ba09eb79 Assos Assos
    array('data' => ($value == '') ? '-' : check_plain($value), 'class' => array('webform-component-value')) +
245
          ($component['value'] == $value ? array() : array('title' => $component['value'])),
246 a45e4bc1 Assos Assos
    array('data' => drupal_render($form['components'][$cid]['required']), 'class' => array('webform-component-required', 'checkbox')),
247
    array('data' => drupal_render($form['components'][$cid]['cid']) . drupal_render($form['components'][$cid]['pid']) . drupal_render($form['components'][$cid]['weight'])),
248
    array('data' => l(t('Edit'), 'node/' . $node->nid . '/webform/components/' . $cid, array('query' => drupal_get_destination())), 'class' => array('webform-component-edit')),
249
    array('data' => l(t('Clone'), 'node/' . $node->nid . '/webform/components/' . $cid . '/clone', array('query' => drupal_get_destination())), 'class' => array('webform-component-clone')),
250
    array('data' => l(t('Delete'), 'node/' . $node->nid . '/webform/components/' . $cid . '/delete', array('query' => drupal_get_destination())), 'class' => array('webform-component-delete')),
251
  );
252
  $row_class = array('draggable');
253
  if (!webform_component_feature($component['type'], 'group')) {
254
    $row_class[] = 'tabledrag-leaf';
255
  }
256
  if ($component['type'] == 'pagebreak') {
257
    $row_class[] = 'tabledrag-root';
258
    $row_class[] = 'webform-pagebreak';
259
    $row_data[0]['class'][] = 'webform-pagebreak';
260
  }
261
  $rows[] = array('data' => $row_data, 'class' => $row_class, 'data-cid' => $cid);
262
  if (isset($component['children']) && is_array($component['children'])) {
263
    foreach ($component['children'] as $cid => $component) {
264
      _webform_components_form_rows($node, $cid, $component, $level + 1, $form, $rows, $add_form);
265
    }
266
  }
267
268
  // Add the add form if this was the last edited component.
269
  if (isset($_GET['cid']) && $component['cid'] == $_GET['cid'] && $add_form) {
270
    $add_form['data'][0]['data'] = $indents . $add_form['data'][0]['data'];
271
    $rows[] = $add_form;
272
    $add_form = FALSE;
273
  }
274
}
275
276
/**
277
 * Theme the node components form. Use a table to organize the components.
278
 *
279
 * @return
280
 *   Formatted HTML form, ready for display.
281
 */
282
function theme_webform_components_form($variables) {
283 85ad3d82 Assos Assos
  $output = '';
284 a45e4bc1 Assos Assos
  $output .= drupal_render_children($variables['form']['warning']);
285
  $output .= theme('table', array('header' => $variables['header'], 'rows' => $variables['rows'], 'attributes' => array('id' => 'webform-components')));
286
  $output .= drupal_render_children($variables['form']);
287 85ad3d82 Assos Assos
  return $output;
288
}
289
290
/**
291
 * Validate handler for webform_components_form().
292
 */
293
function webform_components_form_validate($form, &$form_state) {
294
  // Check that no two components end up with the same form key.
295
  $duplicates = array();
296
  $parents = array();
297
  if (isset($form_state['values']['components'])) {
298
    foreach ($form_state['values']['components'] as $cid => $component) {
299
      $form_key = $form['#node']->webform['components'][$cid]['form_key'];
300
      if (isset($parents[$component['pid']]) && ($existing = array_search($form_key, $parents[$component['pid']])) && $existing !== FALSE) {
301
        if (!isset($duplicates[$form_key])) {
302
          $duplicates[$form_key] = array($existing);
303
        }
304
        $duplicates[$form_key][] = $cid;
305
      }
306
      $parents[$component['pid']][$cid] = $form_key;
307
    }
308
  }
309
310
  if (!empty($duplicates)) {
311
    $error = t('The form order failed to save because the following elements have same form keys and are under the same parent. Edit each component and give them a unique form key, then try moving them again.');
312
    $items = array();
313
    foreach ($duplicates as $form_key => $cids) {
314
      foreach ($cids as $cid) {
315 a45e4bc1 Assos Assos
        $items[] = webform_filter_xss($form['#node']->webform['components'][$cid]['name']);
316 85ad3d82 Assos Assos
      }
317
    }
318
319
    form_error($form['components'], $error . theme('item_list', array('items' => $items)));
320
  }
321
}
322
323
/**
324
 * Validate handler for webform_component_form() when adding a new component.
325
 */
326
function webform_components_form_add_validate($form, &$form_state) {
327
  // Check that the entered component name is valid.
328
  if (drupal_strlen(trim($form_state['values']['add']['name'])) <= 0) {
329
    form_error($form['add']['name'], t('When adding a new component, the name field is required.'));
330
  }
331
}
332
333
/**
334
 * Submit handler for webform_components_form() to save component order.
335
 */
336
function webform_components_form_submit($form, &$form_state) {
337
  $node = node_load($form_state['values']['nid']);
338
339 a45e4bc1 Assos Assos
  // Update all required and weight values.
340 85ad3d82 Assos Assos
  $changes = FALSE;
341
  foreach ($node->webform['components'] as $cid => $component) {
342 a45e4bc1 Assos Assos
    if ($component['pid'] != $form_state['values']['components'][$cid]['pid'] || $component['weight'] != $form_state['values']['components'][$cid]['weight'] || $component['required'] != $form_state['values']['components'][$cid]['required']) {
343 85ad3d82 Assos Assos
      $changes = TRUE;
344
      $node->webform['components'][$cid]['weight'] = $form_state['values']['components'][$cid]['weight'];
345 a45e4bc1 Assos Assos
      $node->webform['components'][$cid]['required'] = $form_state['values']['components'][$cid]['required'];
346 85ad3d82 Assos Assos
      $node->webform['components'][$cid]['pid'] = $form_state['values']['components'][$cid]['pid'];
347
    }
348
  }
349
350
  if ($changes) {
351
    node_save($node);
352
  }
353
354 a45e4bc1 Assos Assos
  drupal_set_message(t('The component positions and required values have been updated.'));
355 85ad3d82 Assos Assos
}
356
357
/**
358
 * Submit handler for webform_components_form() that adds a new component.
359
 */
360
function webform_components_form_add_submit($form, &$form_state) {
361
  $node = node_load($form_state['values']['nid']);
362
363
  $component = $form_state['values']['add'];
364 a45e4bc1 Assos Assos
365
  // Set the values in the query string for the add component page.
366
  $query = array(
367
    'name' => $component['name'],
368
    'required' => $component['required'],
369
    'pid' => $component['pid'],
370
    'weight' => $component['weight'],
371
  );
372
373
  // Forward the "destination" query string value to the next form.
374
  if (isset($_GET['destination'])) {
375
    $query['destination'] = $_GET['destination'];
376
    unset($_GET['destination']);
377
    drupal_static_reset('drupal_get_destination');
378
  }
379
  $form_state['redirect'] = array('node/' . $node->nid . '/webform/components/new/' . $component['type'], array('query' => $query));
380 85ad3d82 Assos Assos
}
381
382
/**
383
 * Form to configure a webform component.
384
 */
385
function webform_component_edit_form($form, $form_state, $node, $component, $clone = FALSE) {
386
  drupal_set_title(t('Edit component: @name', array('@name' => $component['name'])), PASS_THROUGH);
387
388 a45e4bc1 Assos Assos
  $form['#node'] = $node;
389 85ad3d82 Assos Assos
  $form['#tree'] = TRUE;
390
391
  // Print the correct field type specification.
392
  // We always need: name and description.
393
  $form['type'] = array(
394
    '#type' => 'value',
395
    '#value' => $component['type'],
396
  );
397
  $form['nid'] = array(
398
    '#type' => 'value',
399
    '#value' => $node->nid,
400
  );
401
  $form['cid'] = array(
402
    '#type' => 'value',
403
    '#value' => isset($component['cid']) ? $component['cid'] : NULL,
404
  );
405
  $form['clone'] = array(
406
    '#type' => 'value',
407
    '#value' => $clone,
408
  );
409
410
  if (webform_component_feature($component['type'], 'title')) {
411
    $form['name'] = array(
412
      '#type' => 'textfield',
413
      '#default_value' => $component['name'],
414
      '#title' => t('Label'),
415
      '#description' => t('This is used as a descriptive label when displaying this form element.'),
416
      '#required' => TRUE,
417
      '#weight' => -10,
418 a45e4bc1 Assos Assos
      '#maxlength' => NULL,
419 85ad3d82 Assos Assos
    );
420
  }
421
422
  $form['form_key'] = array(
423
    '#type' => 'textfield',
424
    '#default_value' => empty($component['form_key']) ? _webform_safe_name($component['name']) : $component['form_key'],
425
    '#title' => t('Field Key'),
426
    '#description' => t('Enter a machine readable key for this form element. May contain only alphanumeric characters and underscores. This key will be used as the name attribute of the form element. This value has no effect on the way data is saved, but may be helpful if doing custom form processing.'),
427
    '#required' => TRUE,
428
    '#weight' => -9,
429
  );
430
431
  $form['extra'] = array();
432
  if (webform_component_feature($component['type'], 'description')) {
433
    $form['extra']['description'] = array(
434
      '#type' => 'textarea',
435
      '#default_value' => isset($component['extra']['description']) ? $component['extra']['description'] : '',
436
      '#title' => t('Description'),
437 a45e4bc1 Assos Assos
      '#description' => t('A short description of the field used as help for the user when he/she uses the form.') . ' ' . theme('webform_token_help'),
438 85ad3d82 Assos Assos
      '#weight' => -1,
439
    );
440
  }
441
442
  // Display settings.
443
  $form['display'] = array(
444
    '#type' => 'fieldset',
445
    '#title' => t('Display'),
446
    '#collapsible' => TRUE,
447
    '#collapsed' => FALSE,
448
    '#weight' => 8,
449
  );
450
  if (webform_component_feature($component['type'], 'title_display')) {
451
    if (webform_component_feature($component['type'], 'title_inline')) {
452
      $form['display']['title_display'] = array(
453
        '#type' => 'select',
454
        '#title' => t('Label display'),
455
        '#default_value' => !empty($component['extra']['title_display']) ? $component['extra']['title_display'] : 'before',
456
        '#options' => array(
457
          'before' => t('Above'),
458
          'inline' => t('Inline'),
459
          'none' => t('None'),
460
        ),
461
        '#description' => t('Determines the placement of the component\'s label.'),
462
      );
463
    }
464
    else {
465
      $form['display']['title_display'] = array(
466
        '#type' => 'checkbox',
467
        '#title' => t('Hide label'),
468
        '#default_value' => strcmp($component['extra']['title_display'], 'none') === 0,
469
        '#return_value' => 'none',
470
        '#description' => t('Do not display the label of this component.'),
471
      );
472
    }
473
    $form['display']['title_display']['#weight'] = 8;
474
    $form['display']['title_display']['#parents'] = array('extra', 'title_display');
475
  }
476
477 01d522a6 Assos Assos
  if (webform_component_feature($component['type'], 'description')) {
478
    $form['display']['description_above'] = array(
479
      '#type' => 'checkbox',
480
      '#default_value' => !empty($component['extra']['description_above']),
481
      '#title' => t('Description above field'),
482
      '#description' => t('Place the description above &mdash; rather than below &mdash; the field.'),
483
      '#weight' => 8.5,
484
      '#parents' => array('extra', 'description_above'),
485
    );
486
  }
487
488 85ad3d82 Assos Assos
  if (webform_component_feature($component['type'], 'private')) {
489
    // May user mark fields as Private?
490
    $form['display']['private'] = array(
491
      '#type' => 'checkbox',
492
      '#title' => t('Private'),
493
      '#default_value' => ($component['extra']['private'] == '1' ? TRUE : FALSE),
494
      '#description' => t('Private fields are shown only to users with results access.'),
495
      '#weight' => 45,
496
      '#parents' => array('extra', 'private'),
497
      '#disabled' => empty($node->nid) ? FALSE : !webform_results_access($node),
498
    );
499
  }
500
501 a45e4bc1 Assos Assos
  if (webform_component_feature($component['type'], 'wrapper_classes')) {
502
    $form['display']['wrapper_classes'] = array(
503
      '#type' => 'textfield',
504
      '#title' => t('Wrapper CSS classes'),
505
      '#default_value' => isset($component['extra']['wrapper_classes']) ? $component['extra']['wrapper_classes'] : '',
506
      '#description' => t('Apply a class to the wrapper around both the field and its label. Separate multiple by spaces.'),
507
      '#weight' => 50,
508
      '#parents' => array('extra', 'wrapper_classes'),
509
    );
510
  }
511
  if (webform_component_feature($component['type'], 'css_classes')) {
512
    $form['display']['css_classes'] = array(
513
      '#type' => 'textfield',
514
      '#title' => t('CSS classes'),
515
      '#default_value' => isset($component['extra']['css_classes']) ? $component['extra']['css_classes'] : '',
516
      '#description' => t('Apply a class to the field. Separate multiple by spaces.'),
517
      '#weight' => 51,
518
      '#parents' => array('extra', 'css_classes'),
519
    );
520
  }
521
522 85ad3d82 Assos Assos
  // Validation settings.
523
  $form['validation'] = array(
524
    '#type' => 'fieldset',
525
    '#title' => t('Validation'),
526
    '#collapsible' => TRUE,
527
    '#collapsed' => FALSE,
528
    '#weight' => 5,
529
  );
530
  if (webform_component_feature($component['type'], 'required')) {
531 a45e4bc1 Assos Assos
    $form['validation']['required'] = array(
532 85ad3d82 Assos Assos
      '#type' => 'checkbox',
533 a45e4bc1 Assos Assos
      '#title' => t('Required'),
534
      '#default_value' => ($component['required'] == '1' ? TRUE : FALSE),
535 85ad3d82 Assos Assos
      '#description' => t('Check this option if the user must enter a value.'),
536
      '#weight' => -1,
537 a45e4bc1 Assos Assos
      '#parents' => array('required'),
538 85ad3d82 Assos Assos
    );
539
  }
540
541
  // Position settings, only shown if JavaScript is disabled.
542
  $form['position'] = array(
543
    '#type' => 'fieldset',
544
    '#title' => t('Position'),
545
    '#collapsible' => TRUE,
546
    '#collapsed' => TRUE,
547
    '#tree' => FALSE,
548
    '#weight' => 20,
549
    '#attributes' => array('class' => array('webform-position')),
550
  );
551
552
  $options = array('0' => t('Root'));
553
  foreach ($node->webform['components'] as $existing_cid => $value) {
554
    if (webform_component_feature($value['type'], 'group') && (!isset($component['cid']) || $existing_cid != $component['cid'])) {
555
      $options[$existing_cid] = $value['name'];
556
    }
557
  }
558
  $form['position']['pid'] = array(
559
    '#type' => 'select',
560
    '#title' => t('Parent'),
561
    '#default_value' => $component['pid'],
562
    '#description' => t('Optional. You may organize your form by placing this component inside another fieldset.'),
563
    '#options' => $options,
564
    '#access' => count($options) > 1,
565
    '#weight' => 3,
566
  );
567
  $form['position']['weight'] = array(
568
    '#type' => 'textfield',
569
    '#size' => 4,
570
    '#title' => t('Weight'),
571
    '#default_value' => $component['weight'],
572
    '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
573
    '#weight' => 4,
574
  );
575
576
  // Add the fields specific to this component type:
577
  $additional_form_elements = (array) webform_component_invoke($component['type'], 'edit', $component);
578
  if (empty($additional_form_elements)) {
579
    drupal_set_message(t('The webform component of type @type does not have an edit function defined.', array('@type' => $component['type'])));
580
  }
581
582
  // Merge the additional fields with the current fields:
583
  if (isset($additional_form_elements['extra'])) {
584
    $form['extra'] = array_merge($form['extra'], $additional_form_elements['extra']);
585
    unset($additional_form_elements['extra']);
586
  }
587
  if (isset($additional_form_elements['position'])) {
588
    $form['position'] = array_merge($form['position'], $additional_form_elements['position']);
589
    unset($additional_form_elements['position']);
590
  }
591
  if (isset($additional_form_elements['display'])) {
592
    $form['display'] = array_merge($form['display'], $additional_form_elements['display']);
593
    unset($additional_form_elements['display']);
594
  }
595
  if (isset($additional_form_elements['validation'])) {
596
    $form['validation'] = array_merge($form['validation'], $additional_form_elements['validation']);
597
    unset($additional_form_elements['validation']);
598
  }
599
  elseif (count(element_children($form['validation'])) == 0) {
600
    unset($form['validation']);
601
  }
602
  $form = array_merge($form, $additional_form_elements);
603 01d522a6 Assos Assos
  // Ensure that the webform admin library is attached, possibly in addition to
604
  // component-specific attachments.
605
  $form['#attached']['library'][] = array('webform', 'admin');
606 85ad3d82 Assos Assos
607
  // Add the submit button.
608 a45e4bc1 Assos Assos
  $form['actions'] = array(
609
    '#type' => 'actions',
610
    '#weight' => 50,
611
  );
612 3753f249 Assos Assos
  $form['actions']['submit'] = array(
613 85ad3d82 Assos Assos
    '#type' => 'submit',
614
    '#value' => t('Save component'),
615
  );
616
617 3753f249 Assos Assos
  // Remove fieldsets without any child form controls.
618 a45e4bc1 Assos Assos
  foreach (element_children($form) as $group_key) {
619
    $group = $form[$group_key];
620 3753f249 Assos Assos
    if (isset($group['#type']) && $group['#type'] === 'fieldset' && !element_children($group)) {
621
      unset($form[$group_key]);
622
    }
623
  }
624
625 85ad3d82 Assos Assos
  return $form;
626
}
627
628
/**
629
 * Field name validation for the webform unique key. Must be alphanumeric.
630
 */
631
function webform_component_edit_form_validate($form, &$form_state) {
632 a45e4bc1 Assos Assos
  $node = $form['#node'];;
633 85ad3d82 Assos Assos
634
  if (!preg_match('/^[a-z0-9_]+$/i', $form_state['values']['form_key'])) {
635
    form_set_error('form_key', t('The field key %field_key is invalid. Please include only lowercase alphanumeric characters and underscores.', array('%field_key' => $form_state['values']['form_key'])));
636
  }
637
638
  foreach ($node->webform['components'] as $cid => $component) {
639
    if (($component['cid'] != $form_state['values']['cid'] || $form_state['values']['clone']) && ($component['pid'] == $form_state['values']['pid']) && (strcasecmp($component['form_key'], $form_state['values']['form_key']) == 0)) {
640
      form_set_error('form_key', t('The field key %field_key is already in use by the field labeled %existing_field. Please use a unique key.', array('%field_key' => $form_state['values']['form_key'], '%existing_field' => $component['name'])));
641
    }
642
  }
643
}
644
645
/**
646
 * Submit handler for webform_component_edit_form().
647
 */
648
function webform_component_edit_form_submit($form, &$form_state) {
649
  // Ensure a webform record exists.
650 a45e4bc1 Assos Assos
  $node = $form['#node'];
651 85ad3d82 Assos Assos
  webform_ensure_record($node);
652
653 a45e4bc1 Assos Assos
  // Remove extra values that match the default.
654 85ad3d82 Assos Assos
  if (isset($form_state['values']['extra'])) {
655 a45e4bc1 Assos Assos
    $default = array('type' => $form_state['values']['type'], 'extra' => array());
656
    webform_component_defaults($default);
657 85ad3d82 Assos Assos
    foreach ($form_state['values']['extra'] as $key => $value) {
658 a45e4bc1 Assos Assos
      if (isset($default['extra'][$key]) && $default['extra'][$key] === $value) {
659 85ad3d82 Assos Assos
        unset($form_state['values']['extra'][$key]);
660
      }
661
    }
662
  }
663
664
  // Remove empty attribute values.
665
  if (isset($form_state['values']['extra']['attributes'])) {
666
    foreach ($form_state['values']['extra']['attributes'] as $key => $value) {
667
      if ($value === '') {
668
        unset($form_state['values']['extra']['attributes'][$key]);
669
      }
670
    }
671
  }
672
673
  if ($form_state['values']['clone']) {
674
    webform_component_clone($node, $form_state['values']);
675
    drupal_set_message(t('Component %name cloned.', array('%name' => $form_state['values']['name'])));
676
  }
677
  elseif (!empty($form_state['values']['cid'])) {
678
    webform_component_update($form_state['values']);
679
    drupal_set_message(t('Component %name updated.', array('%name' => $form_state['values']['name'])));
680
  }
681
  else {
682
    $cid = webform_component_insert($form_state['values']);
683
    drupal_set_message(t('New component %name added.', array('%name' => $form_state['values']['name'])));
684
  }
685
686
  // Since Webform components have been updated but the node itself has not
687
  // been saved, it is necessary to explicitly clear the cache to make sure
688 a45e4bc1 Assos Assos
  // the updated webform is visible to anonymous users. This resets the page
689
  // and block caches (only);
690 85ad3d82 Assos Assos
  cache_clear_all();
691
692 a45e4bc1 Assos Assos
  // Refresh the entity cache, should it be cached in persistent storage.
693
  entity_get_controller('node')->resetCache(array($node->nid));
694 85ad3d82 Assos Assos
695
  $form_state['redirect'] = array('node/' . $node->nid . '/webform/components', isset($cid) ? array('query' => array('cid' => $cid)) : array());
696
}
697
698
/**
699
 * Form to confirm deletion of a component.
700
 */
701
function webform_component_delete_form($form, $form_state, $node, $component) {
702
  $cid = $component['cid'];
703
704
  $form = array();
705
  $form['node'] = array(
706
    '#type' => 'value',
707
    '#value' => $node,
708
  );
709
  $form['component'] = array(
710
    '#type' => 'value',
711
    '#value' => $component,
712
  );
713
714 ba09eb79 Assos Assos
  $component_type = $node->webform['components'][$cid]['type'];
715
  if (webform_component_feature($component_type, 'group')) {
716 85ad3d82 Assos Assos
    $question = t('Delete the %name fieldset?', array('%name' => $node->webform['components'][$cid]['name']));
717 ba09eb79 Assos Assos
    $description = t('This will immediately delete the %name @type component and all nested components within %name from the %webform webform. This cannot be undone.',
718
                     array('%name' => $node->webform['components'][$cid]['name'],
719
                           '@type' => webform_component_property($component_type, 'label'),
720
                           '%webform' => $node->title));
721 85ad3d82 Assos Assos
  }
722
  else {
723
    $question = t('Delete the %name component?', array('%name' => $node->webform['components'][$cid]['name']));
724
    $description = t('This will immediately delete the %name component from the %webform webform. This cannot be undone.', array('%name' => $node->webform['components'][$cid]['name'], '%webform' => $node->title));
725
  }
726
727
  return confirm_form($form, $question, 'node/' . $node->nid . '/webform/components', $description, t('Delete'));
728
}
729
730
/**
731
 * Submit handler for webform_component_delete_form().
732
 */
733
function webform_component_delete_form_submit($form, &$form_state) {
734
  // Delete the component.
735
  $node = $form_state['values']['node'];
736
  $component = $form_state['values']['component'];
737
  webform_component_delete($node, $component);
738
  drupal_set_message(t('Component %name deleted.', array('%name' => $component['name'])));
739
740
  // Check if this webform still contains any information.
741
  unset($node->webform['components'][$component['cid']]);
742
  webform_check_record($node);
743
744
  // Since Webform components have been updated but the node itself has not
745
  // been saved, it is necessary to explicitly clear the cache to make sure
746 a45e4bc1 Assos Assos
  // the updated webform is visible to anonymous users. This resets the page
747
  // and block caches (only);
748 85ad3d82 Assos Assos
  cache_clear_all();
749
750 a45e4bc1 Assos Assos
  // Refresh the entity cache, should it be cached in persistent storage.
751
  entity_get_controller('node')->resetCache(array($node->nid));
752 85ad3d82 Assos Assos
753
  $form_state['redirect'] = 'node/' . $node->nid . '/webform/components';
754
}
755
756
/**
757
 * Insert a new component into the database.
758
 *
759
 * @param $component
760
 *   A full component containing fields from the component form.
761
 */
762
function webform_component_insert(&$component) {
763
  // Allow modules to modify the component before saving.
764
  foreach (module_implements('webform_component_presave') as $module) {
765
    $function = $module . '_webform_component_presave';
766
    $function($component);
767
  }
768
769
  $component['value'] = isset($component['value']) ? $component['value'] : NULL;
770 a45e4bc1 Assos Assos
  $component['required'] = isset($component['required']) ? $component['required'] : 0;
771 85ad3d82 Assos Assos
  $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
772
773
  if (!isset($component['cid'])) {
774
    if (lock_acquire('webform_component_insert_' . $component['nid'], 5)) {
775
      $next_id_query = db_select('webform_component')->condition('nid', $component['nid']);
776
      $next_id_query->addExpression('MAX(cid) + 1', 'cid');
777
      $component['cid'] = $next_id_query->execute()->fetchField();
778
      if ($component['cid'] == NULL) {
779
        $component['cid'] = 1;
780
      }
781
      lock_release('webform_component_insert_' . $component['nid']);
782
    }
783
    else {
784
      watchdog('webform', 'A Webform component could not be saved because a timeout occurred while trying to acquire a lock for the node. Details: <pre>@component</pre>', array('@component' => print_r($component, TRUE)));
785
      return FALSE;
786
    }
787
  }
788
789
  $query = db_insert('webform_component')
790
    ->fields(array(
791
      'nid' => $component['nid'],
792
      'cid' => $component['cid'],
793
      'pid' => $component['pid'],
794
      'form_key' => $component['form_key'],
795
      'name' => $component['name'],
796
      'type' => $component['type'],
797
      'value' => (string) $component['value'],
798
      'extra' => serialize($component['extra']),
799 a45e4bc1 Assos Assos
      'required' => $component['required'],
800 85ad3d82 Assos Assos
      'weight' => $component['weight'],
801
    ))
802
    ->execute();
803
804
  // Post-insert actions.
805
  module_invoke_all('webform_component_insert', $component);
806
807
  return $component['cid'];
808
}
809
810
/**
811
 * Update an existing component with new values.
812
 *
813
 * @param $component
814
 *   A full component containing a nid, cid, and all other fields from the
815
 *   component form. Additional properties are stored in the extra array.
816
 */
817
function webform_component_update($component) {
818
  // Allow modules to modify the component before saving.
819
  foreach (module_implements('webform_component_presave') as $module) {
820
    $function = $module . '_webform_component_presave';
821
    $function($component);
822
  }
823
824
  $component['value'] = isset($component['value']) ? $component['value'] : NULL;
825 a45e4bc1 Assos Assos
  $component['required'] = isset($component['required']) ? $component['required'] : 0;
826 85ad3d82 Assos Assos
  $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
827
  db_update('webform_component')
828
    ->fields(array(
829
      'pid' => $component['pid'],
830
      'form_key' => $component['form_key'],
831
      'name' => $component['name'],
832
      'type' => $component['type'],
833
      'value' => isset($component['value']) ? $component['value'] : '',
834
      'extra' => serialize($component['extra']),
835 a45e4bc1 Assos Assos
      'required' => $component['required'],
836 85ad3d82 Assos Assos
      'weight' => $component['weight']
837
    ))
838
    ->condition('nid', $component['nid'])
839
    ->condition('cid', $component['cid'])
840
    ->execute();
841
842
  // Post-update actions.
843
  module_invoke_all('webform_component_update', $component);
844
}
845
846
function webform_component_delete($node, $component) {
847
  // Check if a delete function is available for this component. If so,
848
  // load all submissions and allow the component to delete each one.
849
  webform_component_include($component['type']);
850
  $delete_function = '_webform_delete_' . $component['type'];
851
  if (function_exists($delete_function)) {
852
    module_load_include('inc', 'webform', 'includes/webform.submissions');
853
    $submissions = webform_get_submissions($node->nid);
854
    foreach ($submissions as $submission) {
855
      if (isset($submission->data[$component['cid']])) {
856 a45e4bc1 Assos Assos
        webform_component_invoke($component['type'], 'delete', $component, $submission->data[$component['cid']]);
857 85ad3d82 Assos Assos
      }
858
    }
859
  }
860
861
  // Remove database entries.
862
  db_delete('webform_component')
863
    ->condition('nid', $node->nid)
864
    ->condition('cid', $component['cid'])
865
    ->execute();
866
  db_delete('webform_submitted_data')
867
    ->condition('nid', $node->nid)
868
    ->condition('cid', $component['cid'])
869
    ->execute();
870
871 a45e4bc1 Assos Assos
  // Delete any conditionals dependent on this component.
872
  module_load_include('inc', 'webform', 'includes/webform.conditionals');
873
  foreach ($node->webform['conditionals'] as $rgid => $conditional) {
874
    $delete_conditional = FALSE;
875
    $specs = array(
876
      array(
877
        'field' => 'rules',
878
        'table' => 'webform_conditional_rules',
879
        'component' => 'source',
880
        'component_type' => 'source_type',
881
        'index' => 'rid',
882
      ),
883
      array(
884
        'field' => 'actions',
885
        'table' => 'webform_conditional_actions',
886
        'component' => 'target',
887
        'component_type' => 'target_type',
888
        'index' => 'aid',
889
      ),
890
    );
891
    foreach ($specs as $spec) {
892
      $count = count($conditional[$spec['field']]);
893
      foreach ($conditional[$spec['field']] as $key => $thing) {
894
        if ($thing[$spec['component_type']] === 'component' && $thing[$spec['component']] == $component['cid']) {
895
          if ($count == 1) {
896
            // Delete the conditional if this component is the only source or target.
897
            webform_conditional_delete($node, $conditional);
898
            break 2;
899
          }
900
          db_delete($spec['table'])
901
            ->condition('nid', $node->nid)
902
            ->condition('rgid', $rgid)
903
            ->condition($spec['index'], $key)
904
            ->execute();
905
          $count--;
906
        }
907
      }
908
    }
909
  }
910
911 85ad3d82 Assos Assos
  // Delete all elements under this element.
912
  $result = db_select('webform_component', 'c')
913
    ->fields('c')
914
    ->condition('nid', $node->nid)
915
    ->condition('pid', $component['cid'])
916
    ->execute();
917
  foreach ($result as $row) {
918
    $child_component = $node->webform['components'][$row->cid];
919
    webform_component_delete($node, $child_component);
920
  }
921
922
  // Post-delete actions.
923
  module_invoke_all('webform_component_delete', $component);
924
}
925
926
/**
927
 * Recursively insert components into the database.
928
 *
929
 * @param $node
930
 *   The node object containing the current webform.
931
 * @param $component
932
 *   A full component containing fields from the component form.
933
 */
934
function webform_component_clone(&$node, &$component) {
935
  $original_cid = $component['cid'];
936
  $component['cid'] = NULL;
937
  $new_cid = webform_component_insert($component);
938
  $component['cid'] = $new_cid;
939
  if (webform_component_feature($component['type'], 'group')) {
940
    foreach ($node->webform['components'] as $cid => $child_component) {
941
      if ($child_component['pid'] == $original_cid) {
942
        $child_component['pid'] = $new_cid;
943
        webform_component_clone($node, $child_component);
944
      }
945
    }
946
  }
947
  return $new_cid;
948
}
949
950
/**
951
 * Check if a component has a particular feature.
952
 *
953
 * @see hook_webform_component_info()
954
 */
955
function webform_component_feature($type, $feature) {
956
  $components = webform_components();
957
  $defaults = array(
958 a45e4bc1 Assos Assos
    'analysis' => TRUE,
959 85ad3d82 Assos Assos
    'csv' => TRUE,
960
    'default_value' => TRUE,
961
    'description' => TRUE,
962
    'email' => TRUE,
963
    'email_address' => FALSE,
964
    'email_name' => FALSE,
965
    'required' => TRUE,
966
    'title' => TRUE,
967
    'title_display' => TRUE,
968
    'title_inline' => TRUE,
969
    'conditional' => TRUE,
970 a45e4bc1 Assos Assos
    'conditional_action_set' => FALSE,
971 85ad3d82 Assos Assos
    'spam_analysis' => FALSE,
972
    'group' => FALSE,
973
    'attachment' => FALSE,
974
    'private' => TRUE,
975 a45e4bc1 Assos Assos
    'placeholder' => FALSE,
976
    'wrapper_classes' => TRUE,
977
    'css_classes' => TRUE,
978
    'views_range' => FALSE,
979 85ad3d82 Assos Assos
  );
980
  return isset($components[$type]['features'][$feature]) ? $components[$type]['features'][$feature] : !empty($defaults[$feature]);
981
}
982
983 a45e4bc1 Assos Assos
/**
984
 * Get a component property from the component definition.
985
 *
986
 * @see hook_webform_component_info()
987
 */
988
function webform_component_property($type, $property) {
989
  $components = webform_components();
990
  $defaults = array(
991
    'conditional_type' => 'string',
992
  );
993
  return isset($components[$type][$property]) ? $components[$type][$property] : $defaults[$property];
994
}
995
996 85ad3d82 Assos Assos
/**
997
 * Create a list of components suitable for a select list.
998
 *
999
 * @param $node
1000
 *   The webform node.
1001
 * @param $component_filter
1002
 *   Either an array of components, or a string containing a feature name (csv,
1003
 *   email, required, conditional) on which this list of components will be
1004
 *   restricted.
1005 a45e4bc1 Assos Assos
 * @param $prefix_group
1006
 *   TRUE to indent with a hyphen, or 'path" to Prepend enclosing group (e.g.
1007
 *   fieldset) name(s)
1008
 * @param $pagebreak_groups
1009 85ad3d82 Assos Assos
 *   Determine if pagebreaks should be converted to option groups in the
1010
 *   returned list of options.
1011
 */
1012 a45e4bc1 Assos Assos
function webform_component_list($node, $component_filter = NULL, $prepend_group = TRUE, $pagebreak_groups = FALSE) {
1013 85ad3d82 Assos Assos
  $options = array();
1014
  $page_names = array();
1015 a45e4bc1 Assos Assos
  $parent_names = array();
1016 85ad3d82 Assos Assos
1017
  $components = is_array($component_filter) ? $component_filter : $node->webform['components'];
1018
  $feature = is_string($component_filter) ? $component_filter : NULL;
1019
1020
  foreach ($components as $cid => $component) {
1021 a45e4bc1 Assos Assos
    // If this component is a group (e.g. fieldset), then remember its name, including any parents.
1022
    if ($prepend_group && webform_component_feature($component['type'], 'group')) {
1023
      $parent_names[$cid] = ($component['pid'] ? $parent_names[$component['pid']] : '') .
1024
                            ($prepend_group === 'path' ? $component['name'] . ': ' : '-');
1025
    }
1026
    $page_num = $component['page_num'];
1027
    // If this component is a pagebreak, then generate an option group, ensuring a unique name.
1028
    if ($pagebreak_groups && $component['type'] == 'pagebreak') {
1029
      $page_name = $component['name'];
1030
      $copy = 1;
1031
      while (in_array($page_name, $page_names)) {
1032
        $page_name = $component['name'] . '_' . ++$copy;
1033 85ad3d82 Assos Assos
      }
1034 a45e4bc1 Assos Assos
      $page_names[$page_num] = $page_name;
1035 85ad3d82 Assos Assos
    }
1036 a45e4bc1 Assos Assos
    // If this component should be included in the options, add it with any prefix, in a page group, as needed.
1037
    if (!isset($feature) || webform_component_feature($component['type'], $feature) || $prepend_group === TRUE) {
1038
      $prefix = ($prepend_group && $component['pid']) ? $parent_names[$component['pid']] : '';
1039
      if ($pagebreak_groups && $page_num > 1) {
1040
        $options[$page_names[$page_num]][$cid] = $prefix . $component['name'];
1041 85ad3d82 Assos Assos
      }
1042
      else {
1043 a45e4bc1 Assos Assos
        $options[$cid] = $prefix . $component['name'];
1044 85ad3d82 Assos Assos
      }
1045
    }
1046
  }
1047
1048
  return $options;
1049
}
1050
1051
/**
1052
 * A Form API process function to expand a component list into checkboxes.
1053
 */
1054
function webform_component_select($element) {
1055
  // Split the select list into checkboxes.
1056
  foreach ($element['#options'] as $key => $label) {
1057
    $label_length = strlen($label);
1058
    $label = preg_replace('/^(\-)+/', '', $label);
1059
    $indents = $label_length - strlen($label);
1060
    $element[$key] = array(
1061
      '#title' => $label,
1062
      '#type' => 'checkbox',
1063
      '#default_value' => array_search($key, $element['#value']) !== FALSE,
1064
      '#return_value' => $key,
1065
      '#parents' => array_merge($element['#parents'], array($key)),
1066
      '#indent' => $indents,
1067
    );
1068
  }
1069
1070
  $element['#theme_wrappers'] = array();
1071
  $element['#type'] = 'webform_component_select';
1072
  $element['#theme'] = 'webform_component_select';
1073
  $element['#attached'] = array(
1074
    'library' => array(
1075
      array('webform', 'admin'),
1076 a45e4bc1 Assos Assos
      array('system', 'drupal.collapse'),
1077 85ad3d82 Assos Assos
    ),
1078
    'js' => array(
1079
      'misc/tableselect.js' => array(),
1080
    ),
1081
  );
1082
1083
  return $element;
1084
}
1085
1086
/**
1087
 * Theme the contents of a Webform component select element.
1088
 */
1089
function theme_webform_component_select($variables) {
1090
  $element = $variables['element'];
1091
1092
  $rows = array();
1093
  $header = array();
1094
  if (!isset($element['#all_checkbox']) || $element['#all_checkbox']) {
1095
    $header = array(array('class' => array('select-all'), 'data' => ' ' . t('Include all components')));
1096
  }
1097
  foreach (element_children($element) as $key) {
1098 a45e4bc1 Assos Assos
    if ($key != 'suffix') {
1099
      $rows[] = array(
1100
        theme('indentation', array('size' => $element[$key]['#indent'])) . drupal_render($element[$key]),
1101
      );
1102
    }
1103 85ad3d82 Assos Assos
  }
1104
1105
  $element['#type'] = 'fieldset';
1106
  $element['#value'] = NULL;
1107
  $element['#attributes']['class'] = array('webform-component-select-table');
1108
  if (!isset($element['#collapsible']) || $element['#collapsible']) {
1109
    $element['#attributes']['class'][] = 'collapsible';
1110
  }
1111
  if (!isset($element['#collapsed']) || $element['#collapsed']) {
1112
    $element['#attributes']['class'][] = 'collapsed';
1113
  }
1114
1115
  if (empty($rows)) {
1116
    $element['#children'] = t('No available components.');
1117
  }
1118
  else {
1119 a45e4bc1 Assos Assos
    $element['#children'] = '<div class="webform-component-select-wrapper">' . theme('table', array('header' => $header, 'rows' => $rows, 'sticky' => FALSE)) . '</div>';
1120 85ad3d82 Assos Assos
  }
1121
1122 a45e4bc1 Assos Assos
  if (isset($element['suffix'])) {
1123
    $element['#children'] .= '<div class="webform-component-select-suffix">' . drupal_render($element['suffix']) . '</div>';
1124
  }
1125
1126
1127 85ad3d82 Assos Assos
  return theme('fieldset', array('element' => $element));
1128
}
1129
1130
/**
1131
 * Find a components parents within a node.
1132
 */
1133
function webform_component_parent_keys($node, $component) {
1134
  $parents = array($component['form_key']);
1135
  $pid = $component['pid'];
1136
  while ($pid) {
1137
    $parents[] = $node->webform['components'][$pid]['form_key'];
1138
    $pid = $node->webform['components'][$pid]['pid'];
1139
  }
1140
  return array_reverse($parents);
1141
}
1142
1143
/**
1144
 * Populate a component with the defaults for that type.
1145
 */
1146
function webform_component_defaults(&$component) {
1147 3753f249 Assos Assos
  $defaults = webform_component_invoke($component['type'], 'defaults');
1148
  drupal_alter('webform_component_defaults', $defaults, $component['type']);
1149
  if (!empty($defaults)) {
1150 85ad3d82 Assos Assos
    foreach ($defaults as $key => $default) {
1151
      if (!isset($component[$key])) {
1152
        $component[$key] = $default;
1153
      }
1154
    }
1155
    foreach ($defaults['extra'] as $extra => $default) {
1156
      if (!isset($component['extra'][$extra])) {
1157
        $component['extra'][$extra] = $default;
1158
      }
1159
    }
1160
  }
1161
}
1162
1163
/**
1164
 * Validate an element value is unique with no duplicates in the database.
1165
 */
1166
function webform_validate_unique($element, $form_state) {
1167
  if ($element['#value'] !== '') {
1168
    $nid = $form_state['values']['details']['nid'];
1169
    $sid = $form_state['values']['details']['sid'];
1170
    $query = db_select('webform_submitted_data')
1171
      ->fields('webform_submitted_data', array('sid'))
1172
      ->condition('nid', $nid)
1173
      ->condition('cid', $element['#webform_component']['cid'])
1174
      ->condition('data', $element['#value'])
1175
      ->range(0, 1); // More efficient than using countQuery() for data checks.
1176
    if ($sid) {
1177
      $query->condition('sid', $sid, '<>');
1178
    }
1179
    $count = $query->execute()->fetchField();
1180
    if ($count) {
1181
      form_error($element, t('The value %value has already been submitted once for the %title field. You may have already submitted this form, or you need to use a different value.', array('%value' => $element['#value'], '%title' => $element['#title'])));
1182
    }
1183
  }
1184
}