View source
<?php
define('WFM_UNLIMITED', -1);
function wfm_form_webform_component_edit_form_alter(&$form, &$form_state) {
$component = $form_state['build_info']['args'][1];
if (!in_array($component['type'], _wfm_compatible_components())) {
return;
}
$form['wfm'] = array(
'#type' => 'fieldset',
'#title' => t('Multiple values'),
'#weight' => 10,
);
$form['wfm']['max_items'] = array(
'#type' => 'select',
'#title' => t('Maximum number of values'),
'#description' => t("The maximum number of values users can enter for this component. 'Unlimited' will allow users to add as many values as they like."),
'#options' => drupal_map_assoc(range(1, 10)) + array(
WFM_UNLIMITED => t('Unlimited'),
),
'#default_value' => _wfm_get_max_items($component),
);
$form['wfm']['min_items'] = array(
'#type' => 'select',
'#title' => t('Initial (fixed) number of values'),
'#options' => drupal_map_assoc(range(1, 10)),
'#default_value' => _wfm_get_min_items($component),
'#states' => array(
'invisible' => array(
':input[name="wfm[max_items]"]' => array(
'value' => 1,
),
),
),
);
$form['wfm']['add_more_text'] = array(
'#type' => 'textfield',
'#title' => t('"Add another" button text'),
'#default_value' => _wfm_get_add_text($component),
'#states' => array(
'invisible' => array(
':input[name="wfm[max_items]"]' => array(
'value' => 1,
),
),
),
);
}
function wfm_webform_component_presave(&$component) {
if (!isset($component['wfm']['max_items'])) {
return;
}
$max_items = $component['wfm']['max_items'];
$component['extra']['wfm_max_items'] = $max_items;
$min_items = $component['wfm']['min_items'];
if ($max_items != WFM_UNLIMITED && $min_items > $max_items) {
$min_items = $max_items;
}
$component['extra']['wfm_min_items'] = $min_items;
$component['extra']['wfm_add_more_text'] = $component['wfm']['add_more_text'];
}
function wfm_webform_submission_render_alter(&$renderable) {
$node = $renderable['#node'];
$submission = $renderable['#submission'];
$format = $renderable['#format'];
if ($format == 'html' || $format == 'text') {
$form_state = array();
foreach (element_children($renderable) as $form_key) {
_wfm_process_elements($renderable[$form_key], $form_state, $submission, TRUE, $format);
}
}
}
function wfm_form_webform_client_form_alter(&$form, &$form_state) {
$form_keys = element_children($form['submitted']);
foreach ($form_keys as $form_key) {
_wfm_process_elements($form['submitted'][$form_key], $form_state, $form['#submission']);
}
$key = array_search('webform_client_form_validate', $form['#validate']);
unset($form['#validate'][$key]);
$form['#validate'][] = 'wfm_validate';
}
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;
if (empty($component) || !_wfm_is_multiple($component)) {
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;
}
$max_items = _wfm_get_max_items($component);
$min_items = _wfm_get_min_items($component);
$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];
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;
}
}
}
}
$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);
if (!isset($element['#wfm_wrapper'])) {
$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,
);
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) {
$final = $item_count == $max_items;
if ($final) {
unset($element['add_more']);
}
elseif (!$display) {
$element['add_more']['#name'] = $element_name . '_ADD_' . $delta;
}
$wfm_parents = $element['#wfm_parents'];
$wfm_parents[] = $delta;
$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;
}
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;
}
$item =& $element[$delta];
$item['#name'] = $element_name . "[{$delta}]";
$item['#parents'] = $element['#parents'];
$item['#parents'][] = $delta;
$item['#wfm_parents'] = $wfm_parents;
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;
_wfm_process_elements($sub_item, $form_state, $submission, $display);
}
}
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>';
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,
);
$element['remove_' . $delta]['#suffix'] = '</div>';
unset($item['#suffix']);
}
$item_number++;
}
}
function wfm_add_more_submit(&$form, &$form_state) {
$button = $form_state['triggering_element'];
$element =& drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
$component = $element['#webform_component'];
$element_name = $element['#name'];
$element_deltas =& $form_state['wfm_deltas'][$element_name];
$item_count = count($element_deltas);
$next_delta = max(array_keys($element_deltas)) + 1;
$max_items = _wfm_get_max_items($component);
if ($max_items == WFM_UNLIMITED || $item_count < $max_items) {
$element_deltas[$next_delta] = $next_delta;
}
$form_state['rebuild'] = TRUE;
}
function wfm_remove_submit(&$form, &$form_state) {
$button = $form_state['triggering_element'];
list($element_name, $delta) = explode('_REMOVE_', $button['#name']);
unset($form_state['wfm_deltas'][$element_name][$delta]);
$form_state['rebuild'] = TRUE;
}
function wfm_js($form, $form_state) {
$button = $form_state['triggering_element'];
$element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1));
return $element;
}
function wfm_validate($form, &$form_state) {
$node = $form['#node'];
$components = $node->webform['components'];
if (isset($form_state['triggering_element']['#limit_validation_errors']) && !count($form_state['triggering_element']['#limit_validation_errors'])) {
return;
}
if (isset($form_state['values']['submitted'])) {
$original_submitted = $form_state['values']['submitted'];
_wfm_values_prepare($form_state, $components);
}
webform_client_form_validate($form, $form_state);
if (isset($original_submitted) && (form_get_errors() || !empty($form_state['rebuild']))) {
$form_state['values']['submitted'] = $original_submitted;
}
}
function _wfm_values_prepare(&$form_state, $components) {
$details = $form_state['values']['details'];
if (isset($form_state['values']['op'])) {
$op = $form_state['values']['op'];
}
form_state_values_clean($form_state);
$form_state['values']['details'] = $details;
if (isset($op)) {
$form_state['values']['op'] = $op;
}
$cids_by_key = array();
foreach ($components as $cid => $component) {
$cids_by_key[$component['form_key']] = $cid;
}
$component_ancestors = $form_state['wfm_ancestors'];
foreach ($components as $cid => $component) {
$form_key = $component['form_key'];
if (webform_component_feature($component['type'], 'group')) {
continue;
}
$is_multiple_recursive = _wfm_is_multiple_recursive($component, $components);
if (!$is_multiple_recursive) {
continue;
}
if (!isset($component_ancestors[$cid])) {
$child_element = $components[$cid];
$ancestors = array(
$child_element['form_key'],
);
$ancestors_key = $child_element['form_key'];
while ($child_element['pid'] != 0) {
$child_element = $components[$child_element['pid']];
array_unshift($ancestors, $child_element['form_key']);
$ancestors_key = $child_element['form_key'] . '.' . $ancestors_key;
}
array_unshift($ancestors, 'submitted');
$ancestors_key = 'submitted.' . $ancestors_key;
$component_ancestors[$cid][$ancestors_key] = $ancestors;
}
foreach ($component_ancestors[$cid] as $ancestors) {
array_shift($ancestors);
$value = drupal_array_get_nested_value($form_state['values']['submitted'], $ancestors, $set);
if (!$set) {
continue;
}
_wfm_array_unset_nested_value($form_state['values']['submitted'], $ancestors);
$value = _wfm_filter_recursive($value);
if (!$value) {
continue;
}
$value = (array) $value;
$ancestors_count = count($ancestors);
if ($ancestors_count > 1 && $is_multiple_recursive) {
$delta_prefix = '';
foreach ($ancestors as $key => $ancestor) {
if ($key < $ancestors_count) {
if ($key > 0) {
$delta_prefix .= is_int($ancestor) ? '#' : '|';
}
if (!is_int($ancestor)) {
$ancestor = $cids_by_key[$ancestor];
}
$delta_prefix .= $ancestor;
}
}
$value_prefixed = array();
foreach ($value as $delta => $sub_value) {
$new_delta = $delta_prefix . '#' . $delta;
$value_prefixed[$new_delta] = $sub_value;
}
$value = $value_prefixed;
}
$value_parents = array_filter($ancestors, 'is_string');
foreach ($value as $key => $sub_value) {
$sub_value_parents = $value_parents;
$sub_value_parents[] = $key;
drupal_array_set_nested_value($form_state['values']['submitted'], $sub_value_parents, $sub_value, TRUE);
}
}
}
$form_state['values']['submitted'] = _wfm_filter_recursive($form_state['values']['submitted']);
}
function wfm_webform_submission_create_alter(stdClass $submission, $node, $account, &$form_state) {
if ($submission->preview) {
_wfm_process_submission($submission);
}
}
function wfm_webform_submission_load(&$submissions) {
foreach ($submissions as $sid => $submission) {
_wfm_process_submission($submission);
}
}
function _wfm_process_submission(stdClass $submission) {
if (isset($submission->wfm_data)) {
return;
}
$node = node_load($submission->nid);
$components = $node->webform['components'];
$original_data = $submission->data;
$submission->wfm_data = array();
foreach ($submission->data as $cid => $data) {
if (!isset($components[$cid])) {
continue;
}
$submission->wfm_data[$cid] = _wfm_submission_data_expand($data);
}
_wfm_submission_data_expand_parents($submission->wfm_data, $original_data);
}
function _wfm_submission_data_expand(array $data) {
$output = array();
foreach ($data as $key => $value) {
if (strpos($key, '|') === FALSE && strpos($key, '#') === FALSE) {
$output[$key] = $value;
continue;
}
$key_parts = preg_split('/[#\\|]/', $key);
drupal_array_set_nested_value($output, $key_parts, $value, TRUE);
}
return $output;
}
function _wfm_submission_data_expand_parents(&$wfm_data, $data) {
foreach ($data as $cid => $value) {
foreach ($value as $key => $sub_value) {
if (strpos($key, '|') !== FALSE && strpos($key, '#') !== FALSE) {
$key_components = explode('|', $key);
foreach ($key_components as $part) {
if (strpos($part, '#') !== FALSE) {
list($pid, $delta) = explode('#', $part);
if (!isset($data[$pid]) && !isset($wfm_data[$pid][$delta])) {
$wfm_data[$pid][$delta] = $delta;
}
}
}
}
}
}
}
function _wfm_filter_recursive($array) {
if (!is_array($array)) {
return $array;
}
$filled = FALSE;
foreach ($array as $key => &$value) {
if (is_array($value)) {
$value = _wfm_filter_recursive($value);
}
if (!is_array($value) && ($value === '' || $value === NULL)) {
unset($array[$key]);
}
else {
$filled = TRUE;
}
}
return $filled ? $array : NULL;
}
function _wfm_component_generate($component, $display = FALSE, $value = NULL, $format = 'html') {
$static_cache =& drupal_static(__FUNCTION__, array());
$key = $component['cid'] . "_{$format}";
if (isset($static_cache[$key]) && $value === NULL) {
return $static_cache[$key];
}
if ($display) {
$element = webform_component_invoke($component['type'], 'display', $component, $value, $format);
}
else {
$element = webform_component_invoke($component['type'], 'render', $component, $value);
$element['#validated'] = TRUE;
$element['#webform_validated'] = FALSE;
}
$element['#webform_component'] = $component;
drupal_alter($display ? 'webform_component_display' : 'webform_component_render', $element, $component);
if ($value === NULL) {
$static_cache[$key] = $element;
}
return $element;
}
function _wfm_is_multiple(array $component) {
$max_items = _wfm_get_max_items($component);
return $max_items > 1 || $max_items == WFM_UNLIMITED;
}
function _wfm_is_multiple_recursive($component, $components) {
if ($component['pid'] != 0 && isset($components[$component['pid']])) {
$parent_component = $components[$component['pid']];
if (_wfm_is_multiple_recursive($parent_component, $components)) {
return TRUE;
}
}
return _wfm_is_multiple($component);
}
function _wfm_get_max_items(array $component) {
$max_items = 1;
if (isset($component['extra']['wfm_max_items'])) {
$max_items = (int) $component['extra']['wfm_max_items'];
}
return $max_items;
}
function _wfm_get_parent_keys(array $component, $components) {
$keys = array();
if (!empty($component['pid'])) {
$parent_component = $components[$component['pid']];
$keys = array_merge($keys, _wfm_get_parent_keys($parent_component, $components));
}
$keys[] = $component['form_key'];
return $keys;
}
function _wfm_get_add_text(array $component) {
$add_more_text = t('Add another item');
if (isset($component['extra']['wfm_add_more_text'])) {
$add_more_text = check_plain($component['extra']['wfm_add_more_text']);
}
return $add_more_text;
}
function _wfm_get_min_items(array $component) {
$min_items = 1;
if (isset($component['extra']['wfm_min_items'])) {
$min_items = (int) $component['extra']['wfm_min_items'];
}
return $min_items;
}
function _wfm_array_unset_nested_value(array &$array, array $parents, &$key_existed = NULL) {
$unset_key = array_pop($parents);
$ref =& drupal_array_get_nested_value($array, $parents, $key_existed);
if ($key_existed && is_array($ref) && array_key_exists($unset_key, $ref)) {
$key_existed = TRUE;
unset($ref[$unset_key]);
}
else {
$key_existed = FALSE;
}
}
function _wfm_compatible_components() {
$types = array(
'email',
'fieldset',
'name',
'number',
'textarea',
'textfield',
'time',
);
drupal_alter('wfm_compatible_components', $types);
return $types;
}