View source
<?php
namespace Drupal\webform\Element;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElement;
use Drupal\webform\Utility\WebformElementHelper;
use Drupal\webform\Utility\WebformOptionsHelper;
abstract class WebformOtherBase extends FormElement {
use WebformCompositeFormElementTrait;
const OTHER_OPTION = '_other_';
protected static $type;
protected static $properties = [
'#title',
'#required',
'#required_error',
'#options',
'#options_display',
'#options_randomize',
'#options_description_display',
'#options__properties',
'#default_value',
'#attributes',
];
protected static $otherProperties = [
'#required_error',
];
public function getInfo() {
$class = get_class($this);
return [
'#input' => TRUE,
'#process' => [
[
$class,
'processWebformOther',
],
[
$class,
'processAjaxForm',
],
],
'#pre_render' => [
[
$class,
'preRenderWebformCompositeFormElement',
],
],
'#options' => [],
'#other__option_delimiter' => ', ',
'#states' => [],
'#markup' => '',
];
}
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
$type = str_replace('webform_', '', static::$type);
if ($input === FALSE) {
$value = static::convertDefaultValueToElementValue($element);
$element[$type]['#default_value'] = $value[$type];
if ($value['other'] !== NULL) {
$element['other']['#default_value'] = $value['other'];
}
return $value;
}
return NULL;
}
public static function processWebformOther(&$element, FormStateInterface $form_state, &$complete_form) {
$type = str_replace('webform_', '', static::$type);
$properties = static::$properties;
$element['#tree'] = TRUE;
$element[$type]['#type'] = static::$type;
$element[$type]['#webform_element'] = TRUE;
$element[$type]['#webform_other'] = TRUE;
$element[$type] += array_intersect_key($element, array_combine($properties, $properties));
$element[$type]['#title_display'] = 'invisible';
if (!isset($element[$type]['#options'][static::OTHER_OPTION])) {
$element[$type]['#options'][static::OTHER_OPTION] = !empty($element['#other__option_label']) ? $element['#other__option_label'] : t('Other…');
}
$element[$type]['#error_no_message'] = TRUE;
$element += [
'other' => [],
];
foreach ($element as $key => $value) {
if (strpos($key, '#other__') === 0) {
$other_key = str_replace('#other__', '#', $key);
if (!isset($element['other'][$other_key])) {
$element['other'][$other_key] = $value;
}
}
}
$element['other'] += [
'#type' => 'textfield',
'#webform_element' => TRUE,
'#placeholder' => t('Enter other…'),
];
if (!isset($element['other']['#title'])) {
$element['other'] += [
'#title' => $element['other']['#placeholder'],
'#title_display' => 'invisible',
];
}
$element['other'] += array_intersect_key($element, array_combine(static::$otherProperties, static::$otherProperties));
$element['other']['#wrapper_attributes']['class'][] = "js-webform-{$type}-other-input";
$element['other']['#wrapper_attributes']['class'][] = "webform-{$type}-other-input";
if ($element['other']['#type'] === 'datetime') {
$element['other']['#prefix'] = '<div class="' . implode(' ', $element['other']['#wrapper_attributes']['class']) . '">';
$element['other']['#suffix'] = '</div>';
unset($element['other']['#wrapper_attributes']['class']);
}
if (isset($element['#parents'])) {
$element[$type]['#parents'] = array_merge($element['#parents'], [
$type,
]);
$element['other']['#parents'] = array_merge($element['#parents'], [
'other',
]);
}
if (\Drupal::moduleHandler()
->moduleExists('clientside_validation') && isset($element['other']['#required_error'])) {
$element['other']['#attributes']['data-msg-required'] = $element['other']['#required_error'];
}
$element_manager = \Drupal::service('plugin.manager.webform.element');
$element_manager
->buildElement($element[$type], $complete_form, $form_state);
$element_manager
->buildElement($element['other'], $complete_form, $form_state);
$element[$type]['#pre_render'] = [];
$is_form_element_wrapper = isset($element['#wrapper_type']) && $element['#wrapper_type'] === 'form_element';
$wrapper_attributes = $is_form_element_wrapper ? '#wrapper_attributes' : '#attributes';
$element[$wrapper_attributes]['class'][] = "js-webform-{$type}-other";
$element[$wrapper_attributes]['class'][] = "webform-{$type}-other";
$element['#attributes']['id'] = $element['#id'];
$element['#label_attributes']['webform-remove-for-attribute'] = TRUE;
unset($element['#options']);
$element += [
'#element_validate' => [],
];
array_unshift($element['#element_validate'], [
get_called_class(),
'validateWebformOther',
]);
$element['#attached']['library'][] = 'webform/webform.element.other';
webform_process_states($element, '#wrapper_attributes');
return $element;
}
public static function validateWebformOther(&$element, FormStateInterface $form_state, &$complete_form) {
$type = static::getElementType();
$is_multiple = static::isMultiple($element);
$value = NestedArray::getValue($form_state
->getValues(), $element['#parents']);
$return_value = static::processValue($element, $value);
if ($is_multiple) {
$is_empty = empty($return_value) ? TRUE : FALSE;
}
else {
$is_empty = $return_value === '' || $return_value === NULL ? TRUE : FALSE;
}
$element_value = (array) $value[$type];
$other_value = $value['other'];
if ($element_value) {
$element_value = array_filter($element_value);
$element_value = array_combine($element_value, $element_value);
}
$other_is_empty = isset($element_value[static::OTHER_OPTION]) && $other_value === '';
if (Element::isVisibleElement($element)) {
$required_error_title = isset($element['#title']) ? $element['#title'] : NULL;
if ($other_is_empty) {
WebformElementHelper::setRequiredError($element['other'], $form_state, $required_error_title);
}
elseif ($element['#required'] && $is_empty) {
WebformElementHelper::setRequiredError($element, $form_state, $required_error_title);
$element['other']['#error_no_message'] = TRUE;
}
}
$form_state
->setValueForElement($element[$type], NULL);
$form_state
->setValueForElement($element['other'], NULL);
$element['#value'] = $return_value;
$form_state
->setValueForElement($element, $return_value);
}
public static function processValue(array $element, array $value) {
$type = static::getElementType();
$element_value = $value[$type];
$other_value = $value['other'];
if (static::isMultiple($element)) {
$return_value = array_filter($element_value);
$return_value = array_combine($return_value, $return_value);
if (isset($return_value[static::OTHER_OPTION])) {
unset($return_value[static::OTHER_OPTION]);
if ($other_value !== '') {
$return_value += [
$other_value => $other_value,
];
}
}
return $return_value;
}
else {
return $element_value === static::OTHER_OPTION ? $other_value : $element_value;
}
}
public static function getElementType() {
return str_replace('webform_', '', static::$type);
}
protected static function isMultiple(array $element) {
return !empty($element['#multiple']) || static::$type === 'checkboxes' ? TRUE : FALSE;
}
protected static function convertDefaultValueToElementValue(array $element) {
$type = str_replace('webform_', '', static::$type);
$default_value = isset($element['#default_value']) && $element['#default_value'] !== '' ? $element['#default_value'] : NULL;
if (static::isMultiple($element)) {
if (!is_array($default_value)) {
return [
$type => [],
'other' => NULL,
];
}
$default_options = array_combine($default_value, $default_value);
$flattened_options = OptGroup::flattenOptions($element['#options']);
if ($other_options = array_diff_key($default_options, $flattened_options)) {
return [
$type => array_diff_key($default_options, $other_options) + [
static::OTHER_OPTION => static::OTHER_OPTION,
],
'other' => implode($element['#other__option_delimiter'], $other_options),
];
}
return [
$type => $default_options,
'other' => NULL,
];
}
else {
if ($default_value !== NULL && !WebformOptionsHelper::hasOption($default_value, $element['#options'])) {
return [
$type => static::OTHER_OPTION,
'other' => $default_value,
];
}
return [
$type => $default_value,
'other' => NULL,
];
}
}
}