Project

General

Profile

Paste
Download (23.2 KB) Statistics
| Branch: | Revision:

root / drupal7 / sites / all / modules / webform_validation / webform_validation.module @ d0ed0aa6

1
<?php
2

    
3
/**
4
 * @file
5
 * Add validation rules to webforms.
6
 */
7

    
8
include_once 'webform_validation.validators.inc';
9
include_once 'webform_validation.rules.inc';
10

    
11
/**
12
 * Implements hook_menu().
13
 */
14
function webform_validation_menu() {
15
  $items = array();
16

    
17
  $items['node/%webform_menu/webform/validation'] = array(
18
    'title' => 'Form validation',
19
    'page callback' => 'webform_validation_manage',
20
    'page arguments' => array(1),
21
    'access callback' => 'node_access',
22
    'access arguments' => array('update', 1),
23
    'file' => 'webform_validation.admin.inc',
24
    'weight' => 3,
25
    'type' => MENU_LOCAL_TASK,
26
  );
27

    
28
  $items['node/%webform_menu/webform/validation/add/%'] = array(
29
    'title' => 'Add validation',
30
    'page callback' => 'drupal_get_form',
31
    'page arguments' => array('webform_validation_manage_rule', 1, 'add', 5),
32
    'access callback' => 'node_access',
33
    'access arguments' => array('update', 1),
34
    'file' => 'webform_validation.admin.inc',
35
    'type' => MENU_CALLBACK,
36
  );
37

    
38
  $items['node/%webform_menu/webform/validation/edit/%/%webform_validation_rule'] = array(
39
    'title' => 'Edit rule',
40
    'page callback' => 'drupal_get_form',
41
    'page arguments' => array('webform_validation_manage_rule', 1, 'edit', 5, 6),
42
    'access callback' => 'node_access',
43
    'access arguments' => array('update', 1),
44
    'file' => 'webform_validation.admin.inc',
45
    'type' => MENU_CALLBACK,
46
  );
47

    
48
  $items['node/%webform_menu/webform/validation/delete/%webform_validation_rule'] = array(
49
    'title' => 'Delete rule',
50
    'page callback' => 'drupal_get_form',
51
    'page arguments' => array('webform_validation_delete_rule', 5),
52
    'access callback' => 'node_access',
53
    'access arguments' => array('update', 1),
54
    'file' => 'webform_validation.admin.inc',
55
    'type' => MENU_CALLBACK,
56
  );
57

    
58
  return $items;
59
}
60

    
61
/**
62
 * Load a validation rule.
63
 *
64
 * @param int $ruleid
65
 *   The rule ID.
66
 *
67
 * @return array|false
68
 *   The rule or FALSE if no rule exists.
69
 */
70
function webform_validation_rule_load($ruleid) {
71
  $result = db_query("SELECT ruleid, rulename, nid, validator, data, error_message, negate, weight FROM {webform_validation_rule} WHERE ruleid = :ruleid", array(':ruleid' => $ruleid), array('fetch' => PDO::FETCH_ASSOC));
72
  $rule = $result->fetchAssoc();
73
  if (!$rule) {
74
    return FALSE;
75
  }
76
  $rule['components'] = webform_validation_get_rule_components($ruleid, $rule['nid']);
77
  $rule['negate'] = (bool) $rule['negate'];
78
  return $rule;
79
}
80

    
81
/**
82
 * Implements hook_theme().
83
 */
84
function webform_validation_theme() {
85
  return array(
86
    'webform_validation_manage_add_rule' => array(
87
      'variables' => array(
88
        'nid' => NULL,
89
      ),
90
    ),
91
    'webform_validation_manage_overview_form' => array(
92
      'render element' => 'form',
93
    ),
94
  );
95
}
96

    
97
/**
98
 * Implements hook_form_BASE_FORM_ID_alter().
99
 */
100
function webform_validation_form_webform_client_form_alter(&$form, &$form_state, $form_id) {
101
  $form['#validate'][] = 'webform_validation_validate';
102
  if (module_exists('maxlength')) {
103
    $nid = substr($form_id, strlen('webform_client_form') + 1);
104
    $rules = webform_validation_get_node_rules($nid);
105
    foreach ($rules as $ruleid => $rule) {
106
      if ($rule['validator'] == 'max_length') {
107
        $length_limit = $rule['data'];
108
        $components = $rule['components'];
109
        foreach ($components as $cid => $component) {
110
          // Define $form_element as the webform element representing this
111
          // component, even if it's nested in multiple arrays, as webform
112
          // elemens often are (e.g., fieldsets). Assign by reference here,
113
          // since we need to modify the form element itself and don't know
114
          // its array depth or keys by which to access it.
115
          $form_element = &_webform_validation_get_webform_element($component, $form);
116
          // Append to this form element the relevant properties which are
117
          // supported by maxlength module.
118
          $form_element['#pre_render'][] = 'maxlength_pre_render';
119
          $form_element['#maxlength'] = $length_limit;
120
          $form_element['#maxlength_js'] = TRUE;
121
        }
122
      }
123
    }
124
  }
125
}
126

    
127
/**
128
 * Implements hook_i18n_string_info().
129
 */
130
function webform_validation_i18n_string_info() {
131
  $groups = array();
132
  $groups['webform_validation'] = array(
133
    'title' => t('Webform Validation'),
134
    'description' => t('Translatable strings for webform validation translation'),
135
    // This group doesn't have strings with format.
136
    'format' => FALSE,
137
    // This group cannot list all strings.
138
    'list' => FALSE,
139
    'refresh callback' => 'webform_validation_i18n_string_refresh',
140
  );
141
  return $groups;
142
}
143

    
144
/**
145
 * Webform validation handler to validate against the given rules.
146
 */
147
function webform_validation_validate($form, &$form_state) {
148
  $static_error_messages = &drupal_static(__FUNCTION__, array());
149
  $page_count = 1;
150
  $nid = $form_state['values']['details']['nid'];
151
  $node = node_load($nid);
152
  $values = isset($form_state['values']['submitted']) ? $form_state['values']['submitted'] : NULL;
153
  $flat_values = _webform_client_form_submit_flatten($node, $values);
154
  $rules = webform_validation_get_node_rules($nid);
155
  $sid = empty($form_state['values']['details']['sid']) ? 0 : $form_state['values']['details']['sid'];
156

    
157
  // Get number of pages for this webform.
158
  if (isset($form_state['webform']['page_count'])) {
159
    $page_count = $form_state['webform']['page_count'];
160
  }
161
  elseif (isset($form_state['storage']['page_count'])) {
162
    $page_count = $form_state['storage']['page_count'];
163
  }
164

    
165
  // Filter out rules that don't apply to this step in the multistep form.
166
  if ($values && $page_count && $page_count > 1) {
167
    $validators = webform_validation_get_validators();
168
    foreach ($rules as $ruleid => $rule) {
169
      // Skip the rule if it does not have any components on the current page.
170
      if (!array_intersect_key($flat_values, $rule['components'])) {
171
        unset($rules[$ruleid]);
172
      }
173
      // For validators that require at least 2 components, skip the rule if any
174
      // of the components are on a page past the current page.
175
      elseif (isset($validators[$rule['validator']]['min_components']) && $validators[$rule['validator']]['min_components'] > 1) {
176
        foreach (array_keys($rule['components']) as $cid) {
177
          if ($node->webform['components'][$cid]['page_num'] > $form_state['webform']['page_num']) {
178
            unset($rules[$ruleid]);
179
            break;
180
          }
181
        }
182
      }
183
    }
184
  }
185

    
186
  if ($rules) {
187
    // Remove hidden components.
188
    if (defined('WebformConditionals::componentShown')) {
189
      // New conditionals system.
190
      $sorter = webform_get_conditional_sorter($node);
191
      // If the form was retrieved from the form cache, the conditionals may not
192
      // have been executed yet.
193
      if (!$sorter->isExecuted()) {
194
        $sorter->executeConditionals(array(), 0);
195
      }
196
      foreach ($node->webform['components'] as $key => $component) {
197
        if ($sorter->componentVisibility($component['cid'], $component['page_num']) !== WebformConditionals::componentShown) {
198
          unset($flat_values[$key]);
199
        }
200
      }
201
    }
202
    else {
203
      // Old conditionals system removed in Webform 7.x-4.8.
204
      // Webform 7.x-3.x does not define WEBFORM_CONDITIONAL_INCLUDE.
205
      // Define if needed.
206
      if (!defined('WEBFORM_CONDITIONAL_INCLUDE')) {
207
        define('WEBFORM_CONDITIONAL_INCLUDE', 1);
208
      }
209
      foreach ($node->webform['components'] as $key => $component) {
210
        // In Webform 7.x-3.x, _webform_client_form_rule_check() returns
211
        // boolean.
212
        // Cast to int so that the function behaves as it does in 7.x-4.x.
213
        if (isset($flat_values[$key]) && (int) _webform_client_form_rule_check($node, $component, 0, $form_state['values']['submitted']) !== WEBFORM_CONDITIONAL_INCLUDE) {
214
          unset($flat_values[$key]);
215
        }
216
      }
217
    }
218

    
219
    foreach ($rules as $rule) {
220
      // Create a list of components that need validation against this rule
221
      // (component id => user submitted value).
222
      $items = array();
223
      foreach ($rule['components'] as $cid => $component) {
224
        if (array_key_exists($cid, $flat_values)) {
225
          $items[$cid] = $flat_values[$cid];
226
        }
227
      }
228
      $rule['sid'] = $sid;
229
      // Have the submitted values validated.
230
      $components = webform_validation_prefix_keys($node->webform['components']);
231
      // Allow translation for all components item names if available.
232
      if (module_exists('webform_localization')) {
233
        module_load_include('inc', 'webform_localization', 'includes/webform_localization.i18n');
234
        foreach ($components as &$component) {
235
          $dummy_element = array(
236
            '#title' => '',
237
          );
238
          _webform_localization_translate_component($dummy_element, $component);
239
          if (isset($dummy_element['#title']) && (string) $dummy_element['#title']) {
240
            $component['name'] = $dummy_element['#title'];
241
          }
242
        }
243
      }
244

    
245
      $errors = module_invoke_all("webform_validation_validate", $rule['validator'], webform_validation_prefix_keys($items), $components, $rule);
246
      if ($errors) {
247
        $errors = webform_validation_unprefix_keys($errors);
248
        // Create hook_webform_validation_validate_alter(). Allow other modules
249
        // to alter error messages.
250
        $context = array(
251
          'validator_name' => $rule['validator'],
252
          'items' => $items,
253
          'components' => $node->webform['components'],
254
          'rule' => $rule,
255
        );
256
        drupal_alter('webform_validation_validate', $errors, $context);
257

    
258
        foreach ($errors as $item_key => $error) {
259
          // Do not set error message if an identical message has already been
260
          // set.
261
          if (in_array($error, $static_error_messages, TRUE)) {
262
            continue;
263
          }
264
          $static_error_messages[] = $error;
265

    
266
          // Build the proper form element error key, taking into account
267
          // hierarchy.
268
          $error_key = 'submitted][' . webform_validation_parent_tree($item_key, $node->webform['components']) . $node->webform['components'][$item_key]['form_key'];
269
          if (is_array($error)) {
270
            foreach ($error as $sub_item_key => $sub_error) {
271
              form_set_error($error_key . '][' . $sub_item_key, $sub_error);
272
            }
273
          }
274
          else {
275
            // filter_xss() is run in _webform_validation_i18n_error_message().
276
            // @ignore security_form_set_error.
277
            form_set_error($error_key, $error);
278
          }
279
        }
280
      }
281
    }
282
  }
283
}
284

    
285
/**
286
 * Helper function to get all field keys (including fields in fieldsets).
287
 *
288
 * @deprecated in webform_validation:7.x-1.14 and is removed from
289
 * webform_validation:7.x-2.0. No longer used.
290
 * @see https://www.drupal.org/project/webform_validation/issues/2841817
291
 */
292
function webform_validation_get_field_keys($submitted, $node) {
293
  static $fields = array();
294
  foreach (element_children($submitted) as $child) {
295
    if (is_array($submitted[$child]) && element_children($submitted[$child])) {
296
      // Only keep searching recursively if it's a fieldset.
297
      $group_components = _webform_validation_get_group_types();
298
      if (in_array(_webform_validation_get_component_type($node, $child), $group_components)) {
299
        webform_validation_get_field_keys($submitted[$child], $node);
300
      }
301
      else {
302
        $fields[$child] = $child;
303
      }
304

    
305
    }
306
    else {
307
      $fields[$child] = $child;
308
    }
309
  }
310
  return $fields;
311
}
312

    
313
/**
314
 * Recursively add the parents for the element.
315
 *
316
 * These are used as the first argument to form_set_error().
317
 */
318
function webform_validation_parent_tree($cid, $components) {
319
  $output = '';
320
  if ($pid = $components[$cid]['pid']) {
321
    $output .= webform_validation_parent_tree($pid, $components);
322
    $output .= $components[$pid]['form_key'] . '][';
323
  }
324
  return $output;
325
}
326

    
327
/**
328
 * Get array of formkeys for all components that have been assigned to a rule.
329
 *
330
 * @deprecated in webform_validation:7.x-1.14 and is removed from
331
 * webform_validation:7.x-2.0. No longer used.
332
 * @see https://www.drupal.org/project/webform_validation/issues/2841817
333
 */
334
function webform_validation_rule_get_formkeys($rule) {
335
  $formkeys = array();
336
  if (isset($rule['components'])) {
337
    foreach ($rule['components'] as $cid => $component) {
338
      $formkeys[] = $component['form_key'];
339
    }
340
  }
341
  return $formkeys;
342
}
343

    
344
/**
345
 * Prefix numeric array keys to avoid them being reindexed.
346
 *
347
 * Reindexing done in module_invoke_all().
348
 *
349
 * Opposite of webform_validation_unprefix_keys().
350
 */
351
function webform_validation_prefix_keys($arr) {
352
  $ret = array();
353
  foreach ($arr as $k => $v) {
354
    $ret['item_' . $k] = $v;
355
  }
356
  return $ret;
357
}
358

    
359
/**
360
 * Undo prefixing numeric array keys.
361
 *
362
 * Opposite of webform_validation_prefix_keys().
363
 */
364
function webform_validation_unprefix_keys($arr) {
365
  $ret = array();
366
  foreach ($arr as $k => $v) {
367
    $new_key = str_replace('item_', '', $k);
368
    $ret[$new_key] = $v;
369
  }
370
  return $ret;
371
}
372

    
373
/**
374
 * Theme the 'add rule' list.
375
 */
376
function theme_webform_validation_manage_add_rule($variables) {
377
  $nid = $variables['nid'];
378
  $output = '';
379
  $validators = webform_validation_get_validators();
380

    
381
  if ($validators) {
382
    $results = db_query('SELECT DISTINCT type FROM {webform_component} WHERE nid = :nid', array('nid' => $nid));
383
    $types = array();
384
    while ($item = $results->fetch()) {
385
      $types[] = $item->type;
386
    }
387

    
388
    $output = '<h3>' . t('Add a validation rule') . '</h3>';
389
    $output .= '<dl>';
390
    foreach ($validators as $validator_key => $validator_info) {
391
      $validator_types = webform_validation_valid_component_types($validator_key);
392
      $title = $validator_info['name'];
393
      if (array_intersect($types, $validator_types)) {
394
        $url = 'node/' . $nid . '/webform/validation/add/' . $validator_key;
395
        $title = l($title, $url, array('query' => drupal_get_destination()));
396
        $component_list_postfix = '';
397
      }
398
      else {
399
        $component_list_postfix = '; ' . t('none present in this form');
400
      }
401
      $item = '<dt>' . $title . '</dt>';
402
      $item .= '<dd>';
403
      $item .= $validator_info['description'];
404
      $item .= ' ' . t('Works with: @component_types.', array('@component_types' => implode(', ', $validator_types) . $component_list_postfix)) . '</dd>';
405
      $output .= $item;
406
    }
407
    $output .= '</dl>';
408
  }
409
  return $output;
410
}
411

    
412
/**
413
 * Implements hook_webform_validation().
414
 */
415
function webform_validation_webform_validation($type, $op, $data) {
416
  if ($type == 'rule' && in_array($op, array('add', 'edit'))) {
417
    if (module_exists('i18n_string') && isset($data['error_message'])) {
418
      i18n_string_update('webform_validation:error_message:' . $data['ruleid'] . ':message', $data['error_message']);
419
    }
420
  }
421
}
422

    
423
/**
424
 * Implements hook_node_insert().
425
 */
426
function webform_validation_node_insert($node) {
427
  if (module_exists('clone') && in_array($node->type, webform_variable_get('webform_node_types'))) {
428
    webform_validation_node_clone($node);
429
  }
430
}
431

    
432
/**
433
 * Implements hook_node_delete().
434
 */
435
function webform_validation_node_delete($node) {
436
  $rules = webform_validation_get_node_rules($node->nid);
437
  if ($rules) {
438
    $transaction = db_transaction();
439
    foreach (array_keys($rules) as $ruleid) {
440
      webform_dynamic_delete_rule($ruleid);
441
    }
442
  }
443
}
444

    
445
/**
446
 * Adds support for node_clone module.
447
 */
448
function webform_validation_node_clone($node) {
449
  if (!in_array($node->type, webform_variable_get('webform_node_types'))) {
450
    return;
451
  }
452
  if (isset($node->clone_from_original_nid)) {
453
    $original_nid = $node->clone_from_original_nid;
454
    // Get existing rules for original node.
455
    $rules = webform_validation_get_node_rules($original_nid);
456
    if ($rules) {
457
      foreach ($rules as $orig_ruleid => $rule) {
458
        unset($rule['ruleid']);
459
        $rule['action'] = 'add';
460
        // Attach existing rules to new node.
461
        $rule['nid'] = $node->nid;
462
        $rule['rule_components'] = $rule['components'];
463
        webform_validation_rule_save($rule);
464
      }
465
    }
466
  }
467
}
468

    
469
/**
470
 * Save a validation rule.
471
 *
472
 * Data comes from the admin form or nodeapi function in case of node clone.
473
 *
474
 * @param array $values
475
 *   An associative array containing:
476
 *   - action: "add" or "edit".
477
 *   - ruleid: ID of the rule to edit. Do not set for "add".
478
 *   - nid: Node ID of the Webform.
479
 *   - validator: Machine name of the validator used by this validation rule.
480
 *   - rulename: Human-readable name for this validation rule.
481
 *   - rule_components: An array in which the keys and the values are the cid's
482
 *     of the Webform components that this rule applies to.
483
 *
484
 * @return int
485
 *   The $ruleid of the rule added or edited.
486
 */
487
function webform_validation_rule_save(array $values) {
488
  if ($values['action'] === 'add') {
489
    $primary_keys = array();
490
  }
491
  elseif ($values['action'] === 'edit') {
492
    $primary_keys = array('ruleid');
493
  }
494
  else {
495
    return FALSE;
496
  }
497

    
498
  $transaction = db_transaction();
499

    
500
  drupal_write_record('webform_validation_rule', $values, $primary_keys);
501

    
502
  // Delete existing component records for this ruleid.
503
  if ($values['action'] === 'edit') {
504
    db_delete('webform_validation_rule_components')
505
      ->condition('ruleid', $values['ruleid'])
506
      ->execute();
507
  }
508

    
509
  $components = array_filter($values['rule_components']);
510
  if ($values['ruleid'] && $components) {
511
    webform_validation_save_rule_components($values['ruleid'], $components);
512
    module_invoke_all('webform_validation', 'rule', $values['action'], $values);
513
  }
514

    
515
  return $values['ruleid'];
516
}
517

    
518
/**
519
 * Save components attached to a specific rule.
520
 *
521
 * @param int $ruleid
522
 *   The ruleid of the rule being saved.
523
 * @param array $components
524
 *   An array in which the keys are the cid's of the components attached to the
525
 *   rule.
526
 *
527
 * @return array
528
 *   An array of the return statuses for each query keyed by cid.
529
 */
530
function webform_validation_save_rule_components($ruleid, array $components) {
531
  $return_status = array();
532
  foreach ($components as $cid => $component) {
533
    $return_status[$cid] = db_merge('webform_validation_rule_components')
534
      ->key(array(
535
        'ruleid' => $ruleid,
536
        'cid' => $cid,
537
      ))
538
      ->fields(array(
539
        'ruleid' => $ruleid,
540
        'cid' => $cid,
541
      ))
542
      ->execute();
543
  }
544
  return $return_status;
545
}
546

    
547
/**
548
 * Given a webform node, get the component type based on a given component key.
549
 */
550
function _webform_validation_get_component_type($node, $component_key) {
551
  if ($node->webform['components']) {
552
    foreach ($node->webform['components'] as $component) {
553
      if ($component['form_key'] == $component_key) {
554
        return $component['type'];
555
      }
556
    }
557
  }
558
  return FALSE;
559
}
560

    
561
/**
562
 * Get all webform components that are defined as a group.
563
 */
564
function _webform_validation_get_group_types() {
565
  $types = array();
566
  foreach (webform_components() as $name => $component) {
567
    if (isset($component['features']['group']) && $component['features']['group']) {
568
      $types[] = $name;
569
    }
570
  }
571
  return $types;
572
}
573

    
574
/**
575
 * Implements hook_webform_validator_alter().
576
 */
577
function webform_validation_webform_validator_alter(&$validators) {
578
  // Add support for the Select (or Other) module.
579
  if (module_exists('select_or_other')) {
580
    // If this module exists, all select components can now except user input.
581
    // Thus we provide those components the same rules as a textfield.
582
    if ($validators) {
583
      foreach ($validators as $validator_name => $validator_info) {
584
        if (in_array('textfield', $validator_info['component_types'])) {
585
          $validators[$validator_name]['component_types'][] = 'select';
586
        }
587
        $validators[$validator_name]['component_types'] = array_unique($validators[$validator_name]['component_types']);
588
      }
589
    }
590
  }
591
}
592

    
593
/**
594
 * Implements hook_uuid_node_features_export_alter().
595
 */
596
function webform_validation_uuid_node_features_export_alter(&$data, $node, $module) {
597
  $nid = entity_get_id_by_uuid('node', array($node->uuid));
598
  $nid = reset($nid);
599
  if (webform_validation_get_node_rules($nid)) {
600
    $data['dependencies']['webform_validation'] = 'webform_validation';
601
  }
602
}
603

    
604
/**
605
 * Implements hook_uuid_node_features_export_render_alter().
606
 */
607
function webform_validation_uuid_node_features_export_render_alter(&$export, $node, $module) {
608
  if (!empty($node->webform)) {
609
    $rules = webform_validation_get_node_rules_assoc($node->nid);
610
    foreach ($rules as &$rule) {
611
      unset($rule['nid']);
612
      unset($rule['ruleid']);
613
    }
614
    $export->webform['validation'] = $rules;
615
  }
616
}
617

    
618
/**
619
 * Implements hook_entity_uuid_save().
620
 */
621
function webform_validation_entity_uuid_save($node, $entity_type) {
622
  if ($entity_type == 'node') {
623
    if (isset($node->webform['validation'])) {
624
      $rules = $node->webform['validation'];
625
      $orig_rules = webform_validation_get_node_rules_assoc($node->nid);
626
      $transaction = db_transaction();
627
      // Delete obsolete rules.
628
      $delete = array_diff_key($orig_rules, $rules);
629
      foreach ($delete as $rule) {
630
        webform_dynamic_delete_rule($rule['ruleid']);
631
      }
632
      // Add new rules.
633
      $new = array_diff_key($rules, $orig_rules);
634
      foreach ($new as $rule) {
635
        $rule['action'] = 'add';
636
        $rule['nid'] = $node->nid;
637
        $rule['rule_components'] = $rule['components'];
638
        webform_validation_rule_save($rule);
639
      }
640
      // Update existing rules.
641
      $existing = array_diff_key($rules, $new + $delete);
642
      foreach ($existing as $name => $rule) {
643
        $orig_rule = $orig_rules[$name];
644
        $rule['nid'] = $orig_rule['nid'];
645
        $rule['ruleid'] = $orig_rule['ruleid'];
646
        if ($rule != $orig_rule) {
647
          $rule['action'] = 'edit';
648
          $rule['rule_components'] = $rule['components'];
649
          webform_validation_rule_save($rule);
650
        }
651
      }
652
    }
653
  }
654
}
655

    
656
/**
657
 * Get a reference to a specific webform element.
658
 *
659
 * (For a given webform_validation rule component, and a given Drupal webform
660
 * form, get a reference to the webform element represented by the rule
661
 * component; return the correct element regardless of how deeply it's nested
662
 * in webform fieldsets or other wrappers.)
663
 *
664
 * @param array $component
665
 *   Webform validation rule component.
666
 * @param array $form
667
 *   Drupal webform form.
668
 *
669
 * @return array
670
 *   Reference to the webform element represented by the rule component.
671
 */
672
function &_webform_validation_get_webform_element(array $component, array &$form) {
673
  // Define an array of ancestors, beginning with the component itself.
674
  $component_ancestors = array($component['form_key']);
675
  // Define the parent-id, starting with the parent-id of the component itself,
676
  // if any.
677
  $pid = $component['pid'];
678
  // Look into $form['#node']->webform['components'][$pid] to get any parent
679
  // of the component, and continue working up the family tree until there is
680
  // no more parent-id.
681
  while ($pid) {
682
    $parent = $form['#node']->webform['components'][$pid];
683
    // Prepend the parent form_key to the array of ancestors. This causes the
684
    // array of ancestors to be ordered from ancestor to descendant.
685
    array_unshift($component_ancestors, $parent['form_key']);
686
    // Note this parent's parent-id, if any.
687
    $pid = $parent['pid'];
688
  }
689
  // $component_ancestors now contains the ordered ancestry. Cycle through it to
690
  // get the correct member of $form['submitted']. Assign by reference so that
691
  // we have a good reference to $webform_element to return.
692
  $webform_element = &$form['submitted'];
693
  foreach ($component_ancestors as $ancestor) {
694
    $webform_element = &$webform_element[$ancestor];
695
  }
696
  return $webform_element;
697
}