Projet

Général

Profil

Paste
Télécharger (43,2 ko) Statistiques
| Branche: | Révision:

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

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
    '#size' => 24,
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 a45e4bc1 Assos Assos
  $header = array(t('Label'), 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 a45e4bc1 Assos Assos
    array('data' => drupal_render($form['add']['name']), 'class' => array('webform-component-name')),
180
    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
  if (drupal_strlen($component['value']) > 30) {
220
    $component['value'] = drupal_substr($component['value'], 0, 30);
221
    $component['value'] .= '...';
222
  }
223
  $component['value'] = check_plain($component['value']);
224
225
  // Remove individual titles from the required and weight fields.
226
  unset($form['components'][$cid]['required']['#title']);
227
  unset($form['components'][$cid]['pid']['#title']);
228
  unset($form['components'][$cid]['weight']['#title']);
229
230
  // Add special classes for weight and parent fields.
231
  $form['components'][$cid]['cid']['#attributes']['class'] = array('webform-cid');
232
  $form['components'][$cid]['pid']['#attributes']['class'] = array('webform-pid');
233
  $form['components'][$cid]['weight']['#attributes']['class'] = array('webform-weight');
234
235
  // Build indentation for this row.
236
  $indents = '';
237
  for ($n = 1; $n <= $level; $n++) {
238
    $indents .= '<div class="indentation">&nbsp;</div>';
239
  }
240
241
  // Add each component to a table row.
242
  $row_data = array(
243
    array('data' => $indents . filter_xss($component['name']), 'class' => array('webform-component-name')),
244
    array('data' => $form['add']['type']['#options'][$component['type']], 'class' => array('webform-component-type')),
245
    array('data' => ($component['value'] == '') ? '-' : $component['value'], 'class' => array('webform-component-value')),
246
    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['#attached']['library'][] = array('webform', 'admin');
390
  $form['#tree'] = TRUE;
391
392
  // Print the correct field type specification.
393
  // We always need: name and description.
394
  $form['type'] = array(
395
    '#type' => 'value',
396
    '#value' => $component['type'],
397
  );
398
  $form['nid'] = array(
399
    '#type' => 'value',
400
    '#value' => $node->nid,
401
  );
402
  $form['cid'] = array(
403
    '#type' => 'value',
404
    '#value' => isset($component['cid']) ? $component['cid'] : NULL,
405
  );
406
  $form['clone'] = array(
407
    '#type' => 'value',
408
    '#value' => $clone,
409
  );
410
411
  if (webform_component_feature($component['type'], 'title')) {
412
    $form['name'] = array(
413
      '#type' => 'textfield',
414
      '#default_value' => $component['name'],
415
      '#title' => t('Label'),
416
      '#description' => t('This is used as a descriptive label when displaying this form element.'),
417
      '#required' => TRUE,
418
      '#weight' => -10,
419 a45e4bc1 Assos Assos
      '#maxlength' => NULL,
420 85ad3d82 Assos Assos
    );
421
  }
422
423
  $form['form_key'] = array(
424
    '#type' => 'textfield',
425
    '#default_value' => empty($component['form_key']) ? _webform_safe_name($component['name']) : $component['form_key'],
426
    '#title' => t('Field Key'),
427
    '#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.'),
428
    '#required' => TRUE,
429
    '#weight' => -9,
430
  );
431
432
  $form['extra'] = array();
433
  if (webform_component_feature($component['type'], 'description')) {
434
    $form['extra']['description'] = array(
435
      '#type' => 'textarea',
436
      '#default_value' => isset($component['extra']['description']) ? $component['extra']['description'] : '',
437
      '#title' => t('Description'),
438 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'),
439 85ad3d82 Assos Assos
      '#weight' => -1,
440
    );
441
  }
442
443
  // Display settings.
444
  $form['display'] = array(
445
    '#type' => 'fieldset',
446
    '#title' => t('Display'),
447
    '#collapsible' => TRUE,
448
    '#collapsed' => FALSE,
449
    '#weight' => 8,
450
  );
451
  if (webform_component_feature($component['type'], 'title_display')) {
452
    if (webform_component_feature($component['type'], 'title_inline')) {
453
      $form['display']['title_display'] = array(
454
        '#type' => 'select',
455
        '#title' => t('Label display'),
456
        '#default_value' => !empty($component['extra']['title_display']) ? $component['extra']['title_display'] : 'before',
457
        '#options' => array(
458
          'before' => t('Above'),
459
          'inline' => t('Inline'),
460
          'none' => t('None'),
461
        ),
462
        '#description' => t('Determines the placement of the component\'s label.'),
463
      );
464
    }
465
    else {
466
      $form['display']['title_display'] = array(
467
        '#type' => 'checkbox',
468
        '#title' => t('Hide label'),
469
        '#default_value' => strcmp($component['extra']['title_display'], 'none') === 0,
470
        '#return_value' => 'none',
471
        '#description' => t('Do not display the label of this component.'),
472
      );
473
    }
474
    $form['display']['title_display']['#weight'] = 8;
475
    $form['display']['title_display']['#parents'] = array('extra', 'title_display');
476
  }
477
478
  if (webform_component_feature($component['type'], 'private')) {
479
    // May user mark fields as Private?
480
    $form['display']['private'] = array(
481
      '#type' => 'checkbox',
482
      '#title' => t('Private'),
483
      '#default_value' => ($component['extra']['private'] == '1' ? TRUE : FALSE),
484
      '#description' => t('Private fields are shown only to users with results access.'),
485
      '#weight' => 45,
486
      '#parents' => array('extra', 'private'),
487
      '#disabled' => empty($node->nid) ? FALSE : !webform_results_access($node),
488
    );
489
  }
490
491 a45e4bc1 Assos Assos
  if (webform_component_feature($component['type'], 'wrapper_classes')) {
492
    $form['display']['wrapper_classes'] = array(
493
      '#type' => 'textfield',
494
      '#title' => t('Wrapper CSS classes'),
495
      '#default_value' => isset($component['extra']['wrapper_classes']) ? $component['extra']['wrapper_classes'] : '',
496
      '#description' => t('Apply a class to the wrapper around both the field and its label. Separate multiple by spaces.'),
497
      '#weight' => 50,
498
      '#parents' => array('extra', 'wrapper_classes'),
499
    );
500
  }
501
  if (webform_component_feature($component['type'], 'css_classes')) {
502
    $form['display']['css_classes'] = array(
503
      '#type' => 'textfield',
504
      '#title' => t('CSS classes'),
505
      '#default_value' => isset($component['extra']['css_classes']) ? $component['extra']['css_classes'] : '',
506
      '#description' => t('Apply a class to the field. Separate multiple by spaces.'),
507
      '#weight' => 51,
508
      '#parents' => array('extra', 'css_classes'),
509
    );
510
  }
511
512 85ad3d82 Assos Assos
  // Validation settings.
513
  $form['validation'] = array(
514
    '#type' => 'fieldset',
515
    '#title' => t('Validation'),
516
    '#collapsible' => TRUE,
517
    '#collapsed' => FALSE,
518
    '#weight' => 5,
519
  );
520
  if (webform_component_feature($component['type'], 'required')) {
521 a45e4bc1 Assos Assos
    $form['validation']['required'] = array(
522 85ad3d82 Assos Assos
      '#type' => 'checkbox',
523 a45e4bc1 Assos Assos
      '#title' => t('Required'),
524
      '#default_value' => ($component['required'] == '1' ? TRUE : FALSE),
525 85ad3d82 Assos Assos
      '#description' => t('Check this option if the user must enter a value.'),
526
      '#weight' => -1,
527 a45e4bc1 Assos Assos
      '#parents' => array('required'),
528 85ad3d82 Assos Assos
    );
529
  }
530
531
  // Position settings, only shown if JavaScript is disabled.
532
  $form['position'] = array(
533
    '#type' => 'fieldset',
534
    '#title' => t('Position'),
535
    '#collapsible' => TRUE,
536
    '#collapsed' => TRUE,
537
    '#tree' => FALSE,
538
    '#weight' => 20,
539
    '#attributes' => array('class' => array('webform-position')),
540
  );
541
542
  $options = array('0' => t('Root'));
543
  foreach ($node->webform['components'] as $existing_cid => $value) {
544
    if (webform_component_feature($value['type'], 'group') && (!isset($component['cid']) || $existing_cid != $component['cid'])) {
545
      $options[$existing_cid] = $value['name'];
546
    }
547
  }
548
  $form['position']['pid'] = array(
549
    '#type' => 'select',
550
    '#title' => t('Parent'),
551
    '#default_value' => $component['pid'],
552
    '#description' => t('Optional. You may organize your form by placing this component inside another fieldset.'),
553
    '#options' => $options,
554
    '#access' => count($options) > 1,
555
    '#weight' => 3,
556
  );
557
  $form['position']['weight'] = array(
558
    '#type' => 'textfield',
559
    '#size' => 4,
560
    '#title' => t('Weight'),
561
    '#default_value' => $component['weight'],
562
    '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'),
563
    '#weight' => 4,
564
  );
565
566
  // Add the fields specific to this component type:
567
  $additional_form_elements = (array) webform_component_invoke($component['type'], 'edit', $component);
568
  if (empty($additional_form_elements)) {
569
    drupal_set_message(t('The webform component of type @type does not have an edit function defined.', array('@type' => $component['type'])));
570
  }
571
572
  // Merge the additional fields with the current fields:
573
  if (isset($additional_form_elements['extra'])) {
574
    $form['extra'] = array_merge($form['extra'], $additional_form_elements['extra']);
575
    unset($additional_form_elements['extra']);
576
  }
577
  if (isset($additional_form_elements['position'])) {
578
    $form['position'] = array_merge($form['position'], $additional_form_elements['position']);
579
    unset($additional_form_elements['position']);
580
  }
581
  if (isset($additional_form_elements['display'])) {
582
    $form['display'] = array_merge($form['display'], $additional_form_elements['display']);
583
    unset($additional_form_elements['display']);
584
  }
585
  if (isset($additional_form_elements['validation'])) {
586
    $form['validation'] = array_merge($form['validation'], $additional_form_elements['validation']);
587
    unset($additional_form_elements['validation']);
588
  }
589
  elseif (count(element_children($form['validation'])) == 0) {
590
    unset($form['validation']);
591
  }
592
  $form = array_merge($form, $additional_form_elements);
593
594
  // Add the submit button.
595 a45e4bc1 Assos Assos
  $form['actions'] = array(
596
    '#type' => 'actions',
597
    '#weight' => 50,
598
  );
599 3753f249 Assos Assos
  $form['actions']['submit'] = array(
600 85ad3d82 Assos Assos
    '#type' => 'submit',
601
    '#value' => t('Save component'),
602
  );
603
604 3753f249 Assos Assos
  // Remove fieldsets without any child form controls.
605 a45e4bc1 Assos Assos
  foreach (element_children($form) as $group_key) {
606
    $group = $form[$group_key];
607 3753f249 Assos Assos
    if (isset($group['#type']) && $group['#type'] === 'fieldset' && !element_children($group)) {
608
      unset($form[$group_key]);
609
    }
610
  }
611
612 85ad3d82 Assos Assos
  return $form;
613
}
614
615
/**
616
 * Field name validation for the webform unique key. Must be alphanumeric.
617
 */
618
function webform_component_edit_form_validate($form, &$form_state) {
619 a45e4bc1 Assos Assos
  $node = $form['#node'];;
620 85ad3d82 Assos Assos
621
  if (!preg_match('/^[a-z0-9_]+$/i', $form_state['values']['form_key'])) {
622
    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'])));
623
  }
624
625
  foreach ($node->webform['components'] as $cid => $component) {
626
    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)) {
627
      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'])));
628
    }
629
  }
630
}
631
632
/**
633
 * Submit handler for webform_component_edit_form().
634
 */
635
function webform_component_edit_form_submit($form, &$form_state) {
636
  // Ensure a webform record exists.
637 a45e4bc1 Assos Assos
  $node = $form['#node'];
638 85ad3d82 Assos Assos
  webform_ensure_record($node);
639
640 a45e4bc1 Assos Assos
  // Remove extra values that match the default.
641 85ad3d82 Assos Assos
  if (isset($form_state['values']['extra'])) {
642 a45e4bc1 Assos Assos
    $default = array('type' => $form_state['values']['type'], 'extra' => array());
643
    webform_component_defaults($default);
644 85ad3d82 Assos Assos
    foreach ($form_state['values']['extra'] as $key => $value) {
645 a45e4bc1 Assos Assos
      if (isset($default['extra'][$key]) && $default['extra'][$key] === $value) {
646 85ad3d82 Assos Assos
        unset($form_state['values']['extra'][$key]);
647
      }
648
    }
649
  }
650
651
  // Remove empty attribute values.
652
  if (isset($form_state['values']['extra']['attributes'])) {
653
    foreach ($form_state['values']['extra']['attributes'] as $key => $value) {
654
      if ($value === '') {
655
        unset($form_state['values']['extra']['attributes'][$key]);
656
      }
657
    }
658
  }
659
660
  if ($form_state['values']['clone']) {
661
    webform_component_clone($node, $form_state['values']);
662
    drupal_set_message(t('Component %name cloned.', array('%name' => $form_state['values']['name'])));
663
  }
664
  elseif (!empty($form_state['values']['cid'])) {
665
    webform_component_update($form_state['values']);
666
    drupal_set_message(t('Component %name updated.', array('%name' => $form_state['values']['name'])));
667
  }
668
  else {
669
    $cid = webform_component_insert($form_state['values']);
670
    drupal_set_message(t('New component %name added.', array('%name' => $form_state['values']['name'])));
671
  }
672
673
  // Since Webform components have been updated but the node itself has not
674
  // been saved, it is necessary to explicitly clear the cache to make sure
675 a45e4bc1 Assos Assos
  // the updated webform is visible to anonymous users. This resets the page
676
  // and block caches (only);
677 85ad3d82 Assos Assos
  cache_clear_all();
678
679 a45e4bc1 Assos Assos
  // Refresh the entity cache, should it be cached in persistent storage.
680
  entity_get_controller('node')->resetCache(array($node->nid));
681 85ad3d82 Assos Assos
682
  $form_state['redirect'] = array('node/' . $node->nid . '/webform/components', isset($cid) ? array('query' => array('cid' => $cid)) : array());
683
}
684
685
/**
686
 * Form to confirm deletion of a component.
687
 */
688
function webform_component_delete_form($form, $form_state, $node, $component) {
689
  $cid = $component['cid'];
690
691
  $form = array();
692
  $form['node'] = array(
693
    '#type' => 'value',
694
    '#value' => $node,
695
  );
696
  $form['component'] = array(
697
    '#type' => 'value',
698
    '#value' => $component,
699
  );
700
701
  if (webform_component_feature($node->webform['components'][$cid]['type'], 'group')) {
702
    $question = t('Delete the %name fieldset?', array('%name' => $node->webform['components'][$cid]['name']));
703
    $description = t('This will immediately delete the %name fieldset and all children elements within %name from the %webform webform. This cannot be undone.', array('%name' => $node->webform['components'][$cid]['name'], '%webform' => $node->title));
704
  }
705
  else {
706
    $question = t('Delete the %name component?', array('%name' => $node->webform['components'][$cid]['name']));
707
    $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));
708
  }
709
710
  return confirm_form($form, $question, 'node/' . $node->nid . '/webform/components', $description, t('Delete'));
711
}
712
713
/**
714
 * Submit handler for webform_component_delete_form().
715
 */
716
function webform_component_delete_form_submit($form, &$form_state) {
717
  // Delete the component.
718
  $node = $form_state['values']['node'];
719
  $component = $form_state['values']['component'];
720
  webform_component_delete($node, $component);
721
  drupal_set_message(t('Component %name deleted.', array('%name' => $component['name'])));
722
723
  // Check if this webform still contains any information.
724
  unset($node->webform['components'][$component['cid']]);
725
  webform_check_record($node);
726
727
  // Since Webform components have been updated but the node itself has not
728
  // been saved, it is necessary to explicitly clear the cache to make sure
729 a45e4bc1 Assos Assos
  // the updated webform is visible to anonymous users. This resets the page
730
  // and block caches (only);
731 85ad3d82 Assos Assos
  cache_clear_all();
732
733 a45e4bc1 Assos Assos
  // Refresh the entity cache, should it be cached in persistent storage.
734
  entity_get_controller('node')->resetCache(array($node->nid));
735 85ad3d82 Assos Assos
736
  $form_state['redirect'] = 'node/' . $node->nid . '/webform/components';
737
}
738
739
/**
740
 * Insert a new component into the database.
741
 *
742
 * @param $component
743
 *   A full component containing fields from the component form.
744
 */
745
function webform_component_insert(&$component) {
746
  // Allow modules to modify the component before saving.
747
  foreach (module_implements('webform_component_presave') as $module) {
748
    $function = $module . '_webform_component_presave';
749
    $function($component);
750
  }
751
752
  $component['value'] = isset($component['value']) ? $component['value'] : NULL;
753 a45e4bc1 Assos Assos
  $component['required'] = isset($component['required']) ? $component['required'] : 0;
754 85ad3d82 Assos Assos
  $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
755
756
  if (!isset($component['cid'])) {
757
    if (lock_acquire('webform_component_insert_' . $component['nid'], 5)) {
758
      $next_id_query = db_select('webform_component')->condition('nid', $component['nid']);
759
      $next_id_query->addExpression('MAX(cid) + 1', 'cid');
760
      $component['cid'] = $next_id_query->execute()->fetchField();
761
      if ($component['cid'] == NULL) {
762
        $component['cid'] = 1;
763
      }
764
      lock_release('webform_component_insert_' . $component['nid']);
765
    }
766
    else {
767
      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)));
768
      return FALSE;
769
    }
770
  }
771
772
  $query = db_insert('webform_component')
773
    ->fields(array(
774
      'nid' => $component['nid'],
775
      'cid' => $component['cid'],
776
      'pid' => $component['pid'],
777
      'form_key' => $component['form_key'],
778
      'name' => $component['name'],
779
      'type' => $component['type'],
780
      'value' => (string) $component['value'],
781
      'extra' => serialize($component['extra']),
782 a45e4bc1 Assos Assos
      'required' => $component['required'],
783 85ad3d82 Assos Assos
      'weight' => $component['weight'],
784
    ))
785
    ->execute();
786
787
  // Post-insert actions.
788
  module_invoke_all('webform_component_insert', $component);
789
790
  return $component['cid'];
791
}
792
793
/**
794
 * Update an existing component with new values.
795
 *
796
 * @param $component
797
 *   A full component containing a nid, cid, and all other fields from the
798
 *   component form. Additional properties are stored in the extra array.
799
 */
800
function webform_component_update($component) {
801
  // Allow modules to modify the component before saving.
802
  foreach (module_implements('webform_component_presave') as $module) {
803
    $function = $module . '_webform_component_presave';
804
    $function($component);
805
  }
806
807
  $component['value'] = isset($component['value']) ? $component['value'] : NULL;
808 a45e4bc1 Assos Assos
  $component['required'] = isset($component['required']) ? $component['required'] : 0;
809 85ad3d82 Assos Assos
  $component['extra']['private'] = isset($component['extra']['private']) ? $component['extra']['private'] : 0;
810
  db_update('webform_component')
811
    ->fields(array(
812
      'pid' => $component['pid'],
813
      'form_key' => $component['form_key'],
814
      'name' => $component['name'],
815
      'type' => $component['type'],
816
      'value' => isset($component['value']) ? $component['value'] : '',
817
      'extra' => serialize($component['extra']),
818 a45e4bc1 Assos Assos
      'required' => $component['required'],
819 85ad3d82 Assos Assos
      'weight' => $component['weight']
820
    ))
821
    ->condition('nid', $component['nid'])
822
    ->condition('cid', $component['cid'])
823
    ->execute();
824
825
  // Post-update actions.
826
  module_invoke_all('webform_component_update', $component);
827
}
828
829
function webform_component_delete($node, $component) {
830
  // Check if a delete function is available for this component. If so,
831
  // load all submissions and allow the component to delete each one.
832
  webform_component_include($component['type']);
833
  $delete_function = '_webform_delete_' . $component['type'];
834
  if (function_exists($delete_function)) {
835
    module_load_include('inc', 'webform', 'includes/webform.submissions');
836
    $submissions = webform_get_submissions($node->nid);
837
    foreach ($submissions as $submission) {
838
      if (isset($submission->data[$component['cid']])) {
839 a45e4bc1 Assos Assos
        webform_component_invoke($component['type'], 'delete', $component, $submission->data[$component['cid']]);
840 85ad3d82 Assos Assos
      }
841
    }
842
  }
843
844
  // Remove database entries.
845
  db_delete('webform_component')
846
    ->condition('nid', $node->nid)
847
    ->condition('cid', $component['cid'])
848
    ->execute();
849
  db_delete('webform_submitted_data')
850
    ->condition('nid', $node->nid)
851
    ->condition('cid', $component['cid'])
852
    ->execute();
853
854 a45e4bc1 Assos Assos
  // Delete any conditionals dependent on this component.
855
  module_load_include('inc', 'webform', 'includes/webform.conditionals');
856
  foreach ($node->webform['conditionals'] as $rgid => $conditional) {
857
    $delete_conditional = FALSE;
858
    $specs = array(
859
      array(
860
        'field' => 'rules',
861
        'table' => 'webform_conditional_rules',
862
        'component' => 'source',
863
        'component_type' => 'source_type',
864
        'index' => 'rid',
865
      ),
866
      array(
867
        'field' => 'actions',
868
        'table' => 'webform_conditional_actions',
869
        'component' => 'target',
870
        'component_type' => 'target_type',
871
        'index' => 'aid',
872
      ),
873
    );
874
    foreach ($specs as $spec) {
875
      $count = count($conditional[$spec['field']]);
876
      foreach ($conditional[$spec['field']] as $key => $thing) {
877
        if ($thing[$spec['component_type']] === 'component' && $thing[$spec['component']] == $component['cid']) {
878
          if ($count == 1) {
879
            // Delete the conditional if this component is the only source or target.
880
            webform_conditional_delete($node, $conditional);
881
            break 2;
882
          }
883
          db_delete($spec['table'])
884
            ->condition('nid', $node->nid)
885
            ->condition('rgid', $rgid)
886
            ->condition($spec['index'], $key)
887
            ->execute();
888
          $count--;
889
        }
890
      }
891
    }
892
  }
893
894 85ad3d82 Assos Assos
  // Delete all elements under this element.
895
  $result = db_select('webform_component', 'c')
896
    ->fields('c')
897
    ->condition('nid', $node->nid)
898
    ->condition('pid', $component['cid'])
899
    ->execute();
900
  foreach ($result as $row) {
901
    $child_component = $node->webform['components'][$row->cid];
902
    webform_component_delete($node, $child_component);
903
  }
904
905
  // Post-delete actions.
906
  module_invoke_all('webform_component_delete', $component);
907
}
908
909
/**
910
 * Recursively insert components into the database.
911
 *
912
 * @param $node
913
 *   The node object containing the current webform.
914
 * @param $component
915
 *   A full component containing fields from the component form.
916
 */
917
function webform_component_clone(&$node, &$component) {
918
  $original_cid = $component['cid'];
919
  $component['cid'] = NULL;
920
  $new_cid = webform_component_insert($component);
921
  $component['cid'] = $new_cid;
922
  if (webform_component_feature($component['type'], 'group')) {
923
    foreach ($node->webform['components'] as $cid => $child_component) {
924
      if ($child_component['pid'] == $original_cid) {
925
        $child_component['pid'] = $new_cid;
926
        webform_component_clone($node, $child_component);
927
      }
928
    }
929
  }
930
  return $new_cid;
931
}
932
933
/**
934
 * Check if a component has a particular feature.
935
 *
936
 * @see hook_webform_component_info()
937
 */
938
function webform_component_feature($type, $feature) {
939
  $components = webform_components();
940
  $defaults = array(
941 a45e4bc1 Assos Assos
    'analysis' => TRUE,
942 85ad3d82 Assos Assos
    'csv' => TRUE,
943
    'default_value' => TRUE,
944
    'description' => TRUE,
945
    'email' => TRUE,
946
    'email_address' => FALSE,
947
    'email_name' => FALSE,
948
    'required' => TRUE,
949
    'title' => TRUE,
950
    'title_display' => TRUE,
951
    'title_inline' => TRUE,
952
    'conditional' => TRUE,
953 a45e4bc1 Assos Assos
    'conditional_action_set' => FALSE,
954 85ad3d82 Assos Assos
    'spam_analysis' => FALSE,
955
    'group' => FALSE,
956
    'attachment' => FALSE,
957
    'private' => TRUE,
958 a45e4bc1 Assos Assos
    'placeholder' => FALSE,
959
    'wrapper_classes' => TRUE,
960
    'css_classes' => TRUE,
961
    'views_range' => FALSE,
962 85ad3d82 Assos Assos
  );
963
  return isset($components[$type]['features'][$feature]) ? $components[$type]['features'][$feature] : !empty($defaults[$feature]);
964
}
965
966 a45e4bc1 Assos Assos
/**
967
 * Get a component property from the component definition.
968
 *
969
 * @see hook_webform_component_info()
970
 */
971
function webform_component_property($type, $property) {
972
  $components = webform_components();
973
  $defaults = array(
974
    'conditional_type' => 'string',
975
  );
976
  return isset($components[$type][$property]) ? $components[$type][$property] : $defaults[$property];
977
}
978
979 85ad3d82 Assos Assos
/**
980
 * Create a list of components suitable for a select list.
981
 *
982
 * @param $node
983
 *   The webform node.
984
 * @param $component_filter
985
 *   Either an array of components, or a string containing a feature name (csv,
986
 *   email, required, conditional) on which this list of components will be
987
 *   restricted.
988 a45e4bc1 Assos Assos
 * @param $prefix_group
989
 *   TRUE to indent with a hyphen, or 'path" to Prepend enclosing group (e.g.
990
 *   fieldset) name(s)
991
 * @param $pagebreak_groups
992 85ad3d82 Assos Assos
 *   Determine if pagebreaks should be converted to option groups in the
993
 *   returned list of options.
994
 */
995 a45e4bc1 Assos Assos
function webform_component_list($node, $component_filter = NULL, $prepend_group = TRUE, $pagebreak_groups = FALSE) {
996 85ad3d82 Assos Assos
  $options = array();
997
  $page_names = array();
998 a45e4bc1 Assos Assos
  $parent_names = array();
999 85ad3d82 Assos Assos
1000
  $components = is_array($component_filter) ? $component_filter : $node->webform['components'];
1001
  $feature = is_string($component_filter) ? $component_filter : NULL;
1002
1003
  foreach ($components as $cid => $component) {
1004 a45e4bc1 Assos Assos
    // If this component is a group (e.g. fieldset), then remember its name, including any parents.
1005
    if ($prepend_group && webform_component_feature($component['type'], 'group')) {
1006
      $parent_names[$cid] = ($component['pid'] ? $parent_names[$component['pid']] : '') .
1007
                            ($prepend_group === 'path' ? $component['name'] . ': ' : '-');
1008
    }
1009
    $page_num = $component['page_num'];
1010
    // If this component is a pagebreak, then generate an option group, ensuring a unique name.
1011
    if ($pagebreak_groups && $component['type'] == 'pagebreak') {
1012
      $page_name = $component['name'];
1013
      $copy = 1;
1014
      while (in_array($page_name, $page_names)) {
1015
        $page_name = $component['name'] . '_' . ++$copy;
1016 85ad3d82 Assos Assos
      }
1017 a45e4bc1 Assos Assos
      $page_names[$page_num] = $page_name;
1018 85ad3d82 Assos Assos
    }
1019 a45e4bc1 Assos Assos
    // If this component should be included in the options, add it with any prefix, in a page group, as needed.
1020
    if (!isset($feature) || webform_component_feature($component['type'], $feature) || $prepend_group === TRUE) {
1021
      $prefix = ($prepend_group && $component['pid']) ? $parent_names[$component['pid']] : '';
1022
      if ($pagebreak_groups && $page_num > 1) {
1023
        $options[$page_names[$page_num]][$cid] = $prefix . $component['name'];
1024 85ad3d82 Assos Assos
      }
1025
      else {
1026 a45e4bc1 Assos Assos
        $options[$cid] = $prefix . $component['name'];
1027 85ad3d82 Assos Assos
      }
1028
    }
1029
  }
1030
1031
  return $options;
1032
}
1033
1034
/**
1035
 * A Form API process function to expand a component list into checkboxes.
1036
 */
1037
function webform_component_select($element) {
1038
  // Split the select list into checkboxes.
1039
  foreach ($element['#options'] as $key => $label) {
1040
    $label_length = strlen($label);
1041
    $label = preg_replace('/^(\-)+/', '', $label);
1042
    $indents = $label_length - strlen($label);
1043
    $element[$key] = array(
1044
      '#title' => $label,
1045
      '#type' => 'checkbox',
1046
      '#default_value' => array_search($key, $element['#value']) !== FALSE,
1047
      '#return_value' => $key,
1048
      '#parents' => array_merge($element['#parents'], array($key)),
1049
      '#indent' => $indents,
1050
    );
1051
  }
1052
1053
  $element['#theme_wrappers'] = array();
1054
  $element['#type'] = 'webform_component_select';
1055
  $element['#theme'] = 'webform_component_select';
1056
  $element['#attached'] = array(
1057
    'library' => array(
1058
      array('webform', 'admin'),
1059 a45e4bc1 Assos Assos
      array('system', 'drupal.collapse'),
1060 85ad3d82 Assos Assos
    ),
1061
    'js' => array(
1062
      'misc/tableselect.js' => array(),
1063
    ),
1064
  );
1065
1066
  return $element;
1067
}
1068
1069
/**
1070
 * Theme the contents of a Webform component select element.
1071
 */
1072
function theme_webform_component_select($variables) {
1073
  $element = $variables['element'];
1074
1075
  $rows = array();
1076
  $header = array();
1077
  if (!isset($element['#all_checkbox']) || $element['#all_checkbox']) {
1078
    $header = array(array('class' => array('select-all'), 'data' => ' ' . t('Include all components')));
1079
  }
1080
  foreach (element_children($element) as $key) {
1081 a45e4bc1 Assos Assos
    if ($key != 'suffix') {
1082
      $rows[] = array(
1083
        theme('indentation', array('size' => $element[$key]['#indent'])) . drupal_render($element[$key]),
1084
      );
1085
    }
1086 85ad3d82 Assos Assos
  }
1087
1088
  $element['#type'] = 'fieldset';
1089
  $element['#value'] = NULL;
1090
  $element['#attributes']['class'] = array('webform-component-select-table');
1091
  if (!isset($element['#collapsible']) || $element['#collapsible']) {
1092
    $element['#attributes']['class'][] = 'collapsible';
1093
  }
1094
  if (!isset($element['#collapsed']) || $element['#collapsed']) {
1095
    $element['#attributes']['class'][] = 'collapsed';
1096
  }
1097
1098
  if (empty($rows)) {
1099
    $element['#children'] = t('No available components.');
1100
  }
1101
  else {
1102 a45e4bc1 Assos Assos
    $element['#children'] = '<div class="webform-component-select-wrapper">' . theme('table', array('header' => $header, 'rows' => $rows, 'sticky' => FALSE)) . '</div>';
1103 85ad3d82 Assos Assos
  }
1104
1105 a45e4bc1 Assos Assos
  if (isset($element['suffix'])) {
1106
    $element['#children'] .= '<div class="webform-component-select-suffix">' . drupal_render($element['suffix']) . '</div>';
1107
  }
1108
1109
1110 85ad3d82 Assos Assos
  return theme('fieldset', array('element' => $element));
1111
}
1112
1113
/**
1114
 * Find a components parents within a node.
1115
 */
1116
function webform_component_parent_keys($node, $component) {
1117
  $parents = array($component['form_key']);
1118
  $pid = $component['pid'];
1119
  while ($pid) {
1120
    $parents[] = $node->webform['components'][$pid]['form_key'];
1121
    $pid = $node->webform['components'][$pid]['pid'];
1122
  }
1123
  return array_reverse($parents);
1124
}
1125
1126
/**
1127
 * Populate a component with the defaults for that type.
1128
 */
1129
function webform_component_defaults(&$component) {
1130 3753f249 Assos Assos
  $defaults = webform_component_invoke($component['type'], 'defaults');
1131
  drupal_alter('webform_component_defaults', $defaults, $component['type']);
1132
  if (!empty($defaults)) {
1133 85ad3d82 Assos Assos
    foreach ($defaults as $key => $default) {
1134
      if (!isset($component[$key])) {
1135
        $component[$key] = $default;
1136
      }
1137
    }
1138
    foreach ($defaults['extra'] as $extra => $default) {
1139
      if (!isset($component['extra'][$extra])) {
1140
        $component['extra'][$extra] = $default;
1141
      }
1142
    }
1143
  }
1144
}
1145
1146
/**
1147
 * Validate an element value is unique with no duplicates in the database.
1148
 */
1149
function webform_validate_unique($element, $form_state) {
1150
  if ($element['#value'] !== '') {
1151
    $nid = $form_state['values']['details']['nid'];
1152
    $sid = $form_state['values']['details']['sid'];
1153
    $query = db_select('webform_submitted_data')
1154
      ->fields('webform_submitted_data', array('sid'))
1155
      ->condition('nid', $nid)
1156
      ->condition('cid', $element['#webform_component']['cid'])
1157
      ->condition('data', $element['#value'])
1158
      ->range(0, 1); // More efficient than using countQuery() for data checks.
1159
    if ($sid) {
1160
      $query->condition('sid', $sid, '<>');
1161
    }
1162
    $count = $query->execute()->fetchField();
1163
    if ($count) {
1164
      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'])));
1165
    }
1166
  }
1167
}