You are here

function _wfm_process_elements in Webform Multiple (WFM) 7

Process an element in the Webform to allow multiple values.

This is called recursively, throughout the form element tree.

Parameters

array &$element: The Form API element.

array &$form_state: The Form API form state.

stdClass $submission: The Webform submission object (when displaying or editing an existing submission), or an empty array.

bool $display: Whether the element should be themed for display (e.g. when viewing results), or as a form input. This is eventually passed on to _wfm_component_generate().

string $format: The display format, usually 'html' or 'text' ('html' by default). This is also passed on to _wfm_component_generate().

2 calls to _wfm_process_elements()
wfm_form_webform_client_form_alter in ./wfm.module
Implements hook_form_FORM_ID_alter().
wfm_webform_submission_render_alter in ./wfm.module
Implements hook_webform_submission_render_alter().

File

./wfm.module, line 128
Main module file for Webform Multiple (WFM).

Code

function _wfm_process_elements(&$element, &$form_state, $submission, $display = FALSE, $format = 'html') {
  if (!$element || !isset($element['#webform_component'])) {
    return;
  }
  $component = $element['#webform_component'];
  $form_key = $component['form_key'];
  $cid = $component['cid'];
  $element_name = isset($element['#name']) ? $element['#name'] : "submitted[{$form_key}]";
  $element_parents = isset($element['#parents']) ? $element['#parents'] : array(
    'submitted',
    $form_key,
  );
  $element_wfm_parents = isset($element['#wfm_parents']) ? $element['#wfm_parents'] : array(
    $cid,
  );
  $initial_weight = isset($element['#weight']) ? $element['#weight'] : 1;
  $form_state['wfm_ancestors'][$cid][implode('.', $element_parents)] = $element_parents;

  // Stop processing if this component does not accept multiple values.
  if (empty($component) || !_wfm_is_multiple($component)) {

    // Recurse through the element's children.
    foreach (element_children($element) as $key) {
      if (!$element[$key] || !isset($element[$key]['#webform_component'])) {
        continue;
      }
      $sub_element =& $element[$key];
      $sub_component = $sub_element['#webform_component'];
      $scid = $sub_element['#webform_component']['cid'];
      $sub_element['#name'] = $element_name . "[{$key}]";
      $sub_element['#parents'] = $element_parents;
      $sub_element['#parents'][] = $key;
      $sub_element['#wfm_parents'] = $element_wfm_parents;
      $sub_element['#wfm_parents'][] = $scid;
      if (empty($sub_component['children'])) {
        array_unshift($sub_element['#wfm_parents'], $scid);
      }
      _wfm_process_elements($sub_element, $form_state, $submission, $display, $format);
    }
    return;
  }

  // Get the current item count for this component.
  $max_items = _wfm_get_max_items($component);
  $min_items = _wfm_get_min_items($component);

  // The $delta keys for each component are saved in $form_state so they can be
  // modified by the add/remove callbacks.
  $new = FALSE;
  if (!isset($form_state['wfm_deltas'][$element_name])) {
    $new = TRUE;
    $form_state['wfm_deltas'][$element_name] = array();
  }
  $element_deltas =& $form_state['wfm_deltas'][$element_name];

  // Get the value and the item count from the submission, if there is one, and
  // if $form_state was not already populated.
  if ($submission && $new) {
    if (isset($submission->wfm_data[$cid]) && !empty($component['children'])) {
      $element_value = array_keys($submission->wfm_data[$cid]);
    }
    else {
      $element_value = drupal_array_get_nested_value($submission->wfm_data, $element_wfm_parents);
    }
    if (is_array($element_value)) {
      $deltas = array_keys($element_value);
      foreach ($deltas as $delta) {
        if (is_numeric($delta)) {
          $element_deltas[$delta] = $delta;
        }
      }
    }
  }

  // Pad the $delta keys if there aren't enough.
  $missing = $min_items - count($element_deltas);
  if ($missing > 0) {
    $last_delta = empty($element_deltas) ? -1 : max(array_keys($element_deltas));
    for ($i = 1; $i <= $missing; $i++) {
      $next_delta = $last_delta + $i;
      $element_deltas[$next_delta] = $next_delta;
    }
  }
  $item_count = count($element_deltas);

  // Replace the element with a parent container.
  if (!isset($element['#wfm_wrapper'])) {

    // Generate the ID that Webform would have given this element if it were a
    // single instance.
    //
    // This ID must remain unique so that the 'Add another item' buttons can
    // address the right AJAX wrapper (on multiple-instance descendants of
    // multiple-instance ancestors), but it must also be in the format:
    //   "webform-component-$form_key"
    // so that conditionals will work on the first instance. At the moment
    // there's no way to get conditionals to work on multiple instances without
    // breaking the HTML specification of unique ID attributes.
    //
    // Note that drupal_html_id() will always produce a unique ID, although
    // unfortunately it isn't fast.
    $wrapper_id = drupal_html_id('webform-component-' . str_replace('_', '-', $form_key));
    $element = array(
      '#type' => 'container',
      '#wfm_wrapper' => TRUE,
      '#webform_component' => $component,
      '#attributes' => array(
        'id' => $wrapper_id,
        'class' => array(
          'wfm-container',
        ),
      ),
      '#wrapper_id' => $wrapper_id,
      '#weight' => $initial_weight,
      '#name' => $element_name,
      '#parents' => $element_parents,
      '#wfm_parents' => $element_wfm_parents,
    );

    // Add the 'Add another item' button.
    if (!$display) {
      $element['add_more'] = array(
        '#type' => 'submit',
        '#value' => _wfm_get_add_text($component),
        '#weight' => $initial_weight + 0.5,
        '#limit_validation_errors' => array(
          array(
            'submitted',
            $form_key,
          ),
        ),
        '#submit' => array(
          'wfm_add_more_submit',
        ),
        '#attributes' => array(
          'class' => array(
            'wfm-add',
          ),
        ),
        '#ajax' => array(
          'callback' => 'wfm_js',
          'wrapper' => $wrapper_id,
        ),
      );
    }
  }
  $item_number = 1;
  foreach ($element_deltas as $delta) {

    // Remove the 'Add another item' button where appropriate.
    $final = $item_count == $max_items;
    if ($final) {
      unset($element['add_more']);
    }
    elseif (!$display) {
      $element['add_more']['#name'] = $element_name . '_ADD_' . $delta;
    }

    // Define the parents of the item.
    $wfm_parents = $element['#wfm_parents'];
    $wfm_parents[] = $delta;

    // Get the default value of the item.
    $default_value = NULL;
    if (!empty($element_value) && empty($component['children'])) {
      $default_value = drupal_array_get_nested_value($submission->wfm_data, $wfm_parents);
      $default_value = (array) $default_value;
    }

    // Generate any new form items needed. They are all added as children of the
    // parent $element, keyed by $delta.
    if (!isset($element[$delta])) {
      $new_element = _wfm_component_generate($component, $display, $default_value, $format);
      $new_element['#weight'] = $initial_weight + $item_number / 100;
      $element[$delta] = $new_element;
    }

    // Process new and existing form items.
    $item =& $element[$delta];
    $item['#name'] = $element_name . "[{$delta}]";
    $item['#parents'] = $element['#parents'];
    $item['#parents'][] = $delta;
    $item['#wfm_parents'] = $wfm_parents;

    // Add the item's children (e.g. if it's a fieldset).
    if (!empty($component['children'])) {
      foreach ($component['children'] as $scid => $sub_component) {
        $sub_component_key = $sub_component['form_key'];
        $sub_item_wfm_parents = $item['#wfm_parents'];
        $sub_item_wfm_parents[] = $scid;
        if (empty($sub_component['children'])) {
          array_unshift($sub_item_wfm_parents, $scid);
        }
        if (!isset($item[$sub_component_key])) {
          $sub_item_value = NULL;
          if ($submission && !_wfm_is_multiple($sub_component)) {
            $sub_item_value = drupal_array_get_nested_value($submission->wfm_data, $sub_item_wfm_parents, $exists);
            if ($exists) {
              $sub_item_value = (array) $sub_item_value;
            }
          }
          $item[$sub_component_key] = _wfm_component_generate($sub_component, $display, $sub_item_value, $format);
        }
        $sub_item =& $item[$sub_component_key];
        $sub_item['#name'] = $item['#name'] . "[{$sub_component_key}]";
        $sub_item['#parents'] = $item['#parents'];
        $sub_item['#parents'][] = $sub_component_key;
        $sub_item['#wfm_parents'] = $sub_item_wfm_parents;

        // Recursively process any multiple-value children.
        _wfm_process_elements($sub_item, $form_state, $submission, $display);
      }
    }

    // Append numbering to the form input's title.
    if ($item_count > 1) {
      $item['#title'] = t('@title (@num/@count)', array(
        '@title' => $item['#title'],
        '@num' => $item_number,
        '@count' => $item_count,
      ));
    }
    $item['#prefix'] = '<div class="wfm-item">';
    $item['#suffix'] = '</div>';

    // Add a 'Remove' button, to appear just after the form input.
    if ($min_items > 0 && $item_count > $min_items && !$display) {
      $element['remove_' . $delta] = array(
        '#type' => 'submit',
        '#value' => t('Remove'),
        '#limit_validation_errors' => array(),
        '#submit' => array(
          'wfm_remove_submit',
        ),
        '#attributes' => array(
          'class' => array(
            'wfm-remove',
          ),
          'title' => t('Remove "@title"', array(
            '@title' => $item['#title'],
          )),
        ),
        '#ajax' => array(
          'callback' => 'wfm_js',
          'wrapper' => $element['#wrapper_id'],
        ),
        '#weight' => $item['#weight'] + 0.001,
        '#name' => $element_name . '_REMOVE_' . $delta,
      );

      // Include the remove button within the 'wfm-item' div container.
      $element['remove_' . $delta]['#suffix'] = '</div>';
      unset($item['#suffix']);
    }
    $item_number++;
  }
}