View source
<?php
namespace Drupal\webform\Plugin;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Link;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Render\Element;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\webform\Cache\WebformBubbleableMetadata;
use Drupal\webform\Element\WebformCompositeFormElementTrait;
use Drupal\webform\Element\WebformHtmlEditor;
use Drupal\webform\Element\WebformMessage;
use Drupal\webform\Entity\WebformOptions;
use Drupal\webform\EntityStorage\WebformEntityStorageTrait;
use Drupal\webform\Plugin\WebformElement\Checkbox;
use Drupal\webform\Plugin\WebformElement\Checkboxes;
use Drupal\webform\Plugin\WebformElement\ContainerBase;
use Drupal\webform\Plugin\WebformElement\Details;
use Drupal\webform\Plugin\WebformElement\WebformCompositeBase;
use Drupal\webform\Twig\WebformTwigExtension;
use Drupal\webform\Utility\WebformArrayHelper;
use Drupal\webform\Utility\WebformDialogHelper;
use Drupal\webform\Utility\WebformElementHelper;
use Drupal\webform\Utility\WebformFormHelper;
use Drupal\webform\Utility\WebformHtmlHelper;
use Drupal\webform\Utility\WebformOptionsHelper;
use Drupal\webform\Utility\WebformReflectionHelper;
use Drupal\webform\WebformInterface;
use Drupal\webform\WebformSubmissionConditionsValidator;
use Drupal\webform\WebformSubmissionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class WebformElementBase extends PluginBase implements WebformElementInterface, TrustedCallbackInterface {
use StringTranslationTrait;
use MessengerTrait;
use WebformCompositeFormElementTrait;
use WebformEntityInjectionTrait;
use WebformEntityStorageTrait;
protected $logger;
protected $configFactory;
protected $moduleHandler;
protected $currentUser;
protected $entityTypeManager;
protected $elementInfo;
protected $elementManager;
protected $tokenManager;
protected $librariesManager;
protected $defaultProperties;
protected $translatableProperties;
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = new static($configuration, $plugin_id, $plugin_definition);
$instance->logger = $container
->get('logger.factory')
->get('webform');
$instance->configFactory = $container
->get('config.factory');
$instance->moduleHandler = $container
->get('module_handler');
$instance->currentUser = $container
->get('current_user');
$instance->entityTypeManager = $container
->get('entity_type.manager');
$instance->elementInfo = $container
->get('plugin.manager.element_info');
$instance->elementManager = $container
->get('plugin.manager.webform.element');
$instance->tokenManager = $container
->get('webform.token_manager');
$instance->librariesManager = $container
->get('webform.libraries_manager');
return $instance;
}
protected function defineDefaultProperties() {
$properties = [
'title' => '',
'default_value' => '',
'help' => '',
'help_title' => '',
'description' => '',
'more' => '',
'more_title' => '',
'title_display' => '',
'description_display' => '',
'help_display' => '',
'field_prefix' => '',
'field_suffix' => '',
'disabled' => FALSE,
'required' => FALSE,
'required_error' => '',
'wrapper_attributes' => [],
'label_attributes' => [],
'attributes' => [],
'format' => $this
->getItemDefaultFormat(),
'format_html' => '',
'format_text' => '',
'format_items' => $this
->getItemsDefaultFormat(),
'format_items_html' => '',
'format_items_text' => '',
'format_attributes' => [],
];
if (!$this
->isComposite()) {
$properties += [
'unique' => FALSE,
'unique_user' => FALSE,
'unique_entity' => FALSE,
'unique_error' => '',
];
}
$properties += $this
->defineDefaultBaseProperties();
return $properties;
}
protected function defineDefaultMultipleProperties() {
return [
'multiple' => FALSE,
'multiple__header_label' => '',
'multiple__min_items' => NULL,
'multiple__empty_items' => 1,
'multiple__add_more' => TRUE,
'multiple__add_more_items' => 1,
'multiple__add_more_button_label' => (string) $this
->t('Add'),
'multiple__add_more_input' => TRUE,
'multiple__add_more_input_label' => (string) $this
->t('more items'),
'multiple__item_label' => (string) $this
->t('item'),
'multiple__no_items_message' => (string) $this
->t('No items entered. Please add items below.'),
'multiple__sorting' => TRUE,
'multiple__operations' => TRUE,
'multiple__add' => TRUE,
'multiple__remove' => TRUE,
];
}
protected function defineDefaultBaseProperties() {
return [
'admin_title' => '',
'admin_notes' => '',
'prepopulate' => FALSE,
'private' => FALSE,
'access' => TRUE,
'flex' => 1,
'states' => [],
'states_clear' => TRUE,
'access_create_roles' => [
'anonymous',
'authenticated',
],
'access_create_users' => [],
'access_create_permissions' => [],
'access_update_roles' => [
'anonymous',
'authenticated',
],
'access_update_users' => [],
'access_update_permissions' => [],
'access_view_roles' => [
'anonymous',
'authenticated',
],
'access_view_users' => [],
'access_view_permissions' => [],
];
}
protected function defineTranslatableProperties() {
return [
'title',
'label',
'help',
'help_title',
'more',
'more_title',
'description',
'field_prefix',
'field_suffix',
'required_error',
'unique_error',
'admin_title',
'admin_notes',
'placeholder',
'markup',
'test',
'header_label',
'add_more_button_label',
'add_more_input_label',
'no_items_message',
];
}
public function getDefaultProperties() {
if (!isset($this->defaultProperties)) {
$properties = $this
->defineDefaultProperties();
$definition = $this
->getPluginDefinition();
\Drupal::moduleHandler()
->alter('webform_element_default_properties', $properties, $definition);
$this->defaultProperties = $properties;
}
return $this->defaultProperties;
}
public function getTranslatableProperties() {
if (!isset($this->translatableProperties)) {
$properties = $this
->defineTranslatableProperties();
$definition = $this
->getPluginDefinition();
\Drupal::moduleHandler()
->alter('webform_element_translatable_properties', $properties, $definition);
$this->translatableProperties = array_unique($properties);
}
return $this->translatableProperties;
}
public function hasProperty($property_name) {
$default_properties = $this
->getDefaultProperties();
return array_key_exists($property_name, $default_properties);
}
public function getDefaultProperty($property_name) {
$default_properties = $this
->getDefaultProperties();
return array_key_exists($property_name, $default_properties) ? $default_properties[$property_name] : NULL;
}
public function getElementProperty(array $element, $property_name) {
return isset($element["#{$property_name}"]) ? $element["#{$property_name}"] : $this
->getDefaultProperty($property_name);
}
protected function setElementDefaultCallback(array &$element, $callback_name) {
$callback_name = $callback_name[0] !== '#' ? '#' . $callback_name : $callback_name;
$callback_value = $this
->getElementInfoDefaultProperty($element, $callback_name) ?: [];
if (!empty($element[$callback_name])) {
$element[$callback_name] = array_merge($callback_value, $element[$callback_name]);
}
else {
$element[$callback_name] = $callback_value;
}
}
protected function getElementInfoDefaultProperty(array $element, $property_name) {
if (!isset($element['#type'])) {
return NULL;
}
$property_name = $property_name[0] !== '#' ? '#' . $property_name : $property_name;
$type = $element['#type'];
return $this->elementInfo
->getInfoProperty($type, $property_name, NULL) ?: $this->elementInfo
->getInfoProperty("webform_{$type}", $property_name, NULL);
}
public function getFormElementClassDefinition() {
$definition = $this->elementInfo
->getDefinition($this
->getBaseId());
return $definition['class'];
}
public function getPluginApiUrl() {
return !empty($this->pluginDefinition['api']) ? Url::fromUri($this->pluginDefinition['api']) : NULL;
}
public function getPluginApiLink() {
$api_url = $this
->getPluginApiUrl();
return $api_url ? Link::fromTextAndUrl($this
->getPluginLabel(), $api_url) : NULL;
}
public function getPluginLabel() {
return $this->pluginDefinition['label'];
}
public function getPluginDescription() {
return $this->pluginDefinition['description'];
}
public function getPluginCategory() {
return $this->pluginDefinition['category'] ?: $this
->t('Other elements');
}
public function getTypeName() {
return $this->pluginDefinition['id'];
}
public function getDefaultKey() {
return isset($this->pluginDefinition['default_key']) ? $this->pluginDefinition['default_key'] : NULL;
}
public function isInput(array $element) {
return !empty($element['#type']) ? TRUE : FALSE;
}
public function hasWrapper(array $element) {
return $this
->hasProperty('wrapper_attributes');
}
public function isContainer(array $element) {
return $this
->isInput($element) ? FALSE : TRUE;
}
public function isRoot() {
return FALSE;
}
public function hasManagedFiles(array $element) {
return FALSE;
}
public function supportsMultipleValues() {
return $this
->hasProperty('multiple');
}
public function hasMultipleWrapper() {
return TRUE;
}
public function hasMultipleValues(array $element) {
if ($this
->hasProperty('multiple')) {
if (isset($element['#multiple'])) {
return $element['#multiple'];
}
else {
$default_property = $this
->getDefaultProperties();
return $default_property['multiple'];
}
}
else {
return FALSE;
}
}
public function isMultiline(array $element) {
$item_format = $this
->getItemFormat($element);
$items_format = $this
->getItemsFormat($element);
if ($this
->hasMultipleValues($element) && in_array($items_format, [
'ol',
'ul',
'hr',
'custom',
])) {
return TRUE;
}
if ($items_format === 'custom' && isset($element['#format_items_html']) && WebformHtmlHelper::hasBlockTags($element['#format_items_html'])) {
return TRUE;
}
if ($item_format === 'custom' && isset($element['#format_html']) && WebformHtmlHelper::hasBlockTags($element['#format_html'])) {
return TRUE;
}
return $this->pluginDefinition['multiline'];
}
public function isComposite() {
return $this->pluginDefinition['composite'];
}
public function isHidden() {
return $this->pluginDefinition['hidden'];
}
public function isExcluded() {
return $this->configFactory
->get('webform.settings')
->get('element.excluded_elements.' . $this->pluginDefinition['id']) ? TRUE : FALSE;
}
public function isEnabled() {
return !$this
->isExcluded();
}
public function isDisabled() {
return !$this
->isEnabled();
}
public function getInfo() {
return $this->elementInfo
->getInfo($this
->getBaseId());
}
public function getRelatedTypes(array $element) {
$types = [];
$parent_classes = WebformReflectionHelper::getParentClasses($this, 'WebformElementBase');
$plugin_id = $this
->getPluginId();
$is_container = $this
->isContainer($element);
$has_multiple_values = $this
->hasMultipleValues($element);
$is_multiline = $this
->isMultiline($element);
$elements = $this->elementManager
->getInstances();
foreach ($elements as $element_name => $element_instance) {
if ($plugin_id === $element_instance
->getPluginId()) {
continue;
}
if ($element_instance
->isDisabled() || $element_instance
->isHidden()) {
continue;
}
$element_instance_parent_classes = WebformReflectionHelper::getParentClasses($element_instance, 'WebformElementBase');
if ($parent_classes[1] !== $element_instance_parent_classes[1]) {
continue;
}
if ($is_container !== $element_instance
->isContainer($element)) {
continue;
}
if ($has_multiple_values !== $element_instance
->hasMultipleValues($element)) {
continue;
}
if ($is_multiline !== $element_instance
->isMultiline($element)) {
continue;
}
$types[$element_name] = $element_instance
->getPluginLabel();
}
asort($types);
return $types;
}
public function initialize(array &$element) {
if (isset($element['#options'])) {
$element['#options'] = WebformOptions::getElementOptions($element);
}
if (!empty($element['#title']) && empty($element['#admin_title'])) {
$element['#admin_title'] = strip_tags($element['#title']);
}
}
public function prepare(array &$element, WebformSubmissionInterface $webform_submission = NULL) {
$attributes_property = $this
->hasWrapper($element) ? '#wrapper_attributes' : '#attributes';
if ($webform_submission) {
$element['#webform'] = $webform_submission
->getWebform()
->id();
$element['#webform_submission'] = $webform_submission
->id();
if ($this
->isDisabled()) {
if ($webform_submission
->getWebform()
->access('edit')) {
$this
->displayDisabledWarning($element);
}
$element['#access'] = FALSE;
}
$operation = $webform_submission
->isCompleted() ? 'update' : 'create';
$this
->setEntities($webform_submission);
$element['#access'] = $this
->checkAccessRules($operation, $element);
}
$element['#webform_element'] = TRUE;
$allowed_tags = $this->configFactory
->get('webform.settings')
->get('element.allowed_tags');
switch ($allowed_tags) {
case 'admin':
$element['#allowed_tags'] = Xss::getAdminTagList();
break;
case 'html':
$element['#allowed_tags'] = Xss::getHtmlTagList();
break;
default:
$element['#allowed_tags'] = preg_split('/ +/', $allowed_tags);
break;
}
if (isset($element['#autocomplete'])) {
$element['#attributes']['autocomplete'] = $element['#autocomplete'];
}
if (isset($element['#title_display']) && $element['#title_display'] === 'inline') {
$element['#_title_display'] = $element['#title_display'];
unset($element['#title_display']);
$element['#wrapper_attributes']['class'][] = 'webform-element--title-inline';
}
$default_description_display = $this->configFactory
->get('webform.settings')
->get('element.default_description_display');
if ($default_description_display && !isset($element['#description_display']) && $this
->hasProperty('description_display')) {
$element['#description_display'] = $default_description_display;
}
if (isset($element['#description_display']) && $element['#description_display'] === 'tooltip') {
$element['#description_display'] = 'invisible';
$element[$attributes_property]['class'][] = 'js-webform-tooltip-element';
$element[$attributes_property]['class'][] = 'webform-tooltip-element';
$element['#attached']['library'][] = 'webform/webform.tooltip';
}
if (!empty($element['#field_prefix'])) {
$element[$attributes_property]['class'][] = 'webform-has-field-prefix';
}
if (!empty($element['#field_suffix'])) {
$element[$attributes_property]['class'][] = 'webform-has-field-suffix';
}
if (isset($element['#states_clear']) && $element['#states_clear'] === FALSE) {
$element[$attributes_property]['data-webform-states-no-clear'] = TRUE;
}
$this
->setElementDefaultCallback($element, 'element_validate');
$this
->prepareElementValidateCallbacks($element, $webform_submission);
if ($this
->isInput($element)) {
if (!empty($element['#readonly'])) {
$element['#attributes']['readonly'] = 'readonly';
if ($this
->hasProperty('wrapper_attributes')) {
$element['#wrapper_attributes']['class'][] = 'webform-readonly';
}
}
if (!empty($element['#required_error'])) {
$element['#attributes']['data-webform-required-error'] = WebformHtmlHelper::toPlainText($element['#required_error']);
$element['#required_error'] = WebformHtmlHelper::toHtmlMarkup($element['#required_error']);
}
}
if ($webform_submission) {
$this
->replaceTokens($element, $webform_submission);
}
$markup_properties = [
'#description',
'#help',
'#more',
'#multiple__no_items_message',
];
foreach ($markup_properties as $markup_property) {
if (!empty($element[$markup_property]) && !is_array($element[$markup_property])) {
$element[$markup_property] = WebformHtmlEditor::checkMarkup($element[$markup_property]);
}
}
}
public function finalize(array &$element, WebformSubmissionInterface $webform_submission = NULL) {
$this
->setElementDefaultCallback($element, 'pre_render');
$this
->prepareElementPreRenderCallbacks($element, $webform_submission);
$this
->prepareCompositeFormElement($element);
$this
->prepareMultipleWrapper($element);
$this
->prepareWrapper($element);
$this
->setElementDefaultCallback($element, 'after_build');
$element['#after_build'][] = [
get_class($this),
'hiddenElementAfterBuild',
];
}
public function alterForm(array &$element, array &$form, FormStateInterface $form_state) {
}
public static function hiddenElementAfterBuild(array $element, FormStateInterface $form_state) {
if (!isset($element['#access']) || $element['#access']) {
return $element;
}
$element['#required'] = FALSE;
return WebformElementHelper::setElementValidate($element);
}
public function checkAccessRules($operation, array $element, AccountInterface $account = NULL) {
if (isset($element['#access']) && $element['#access'] === FALSE) {
return FALSE;
}
$account = $account ?: $this->currentUser;
$webform = $this
->getWebform();
$webform_submission = $this
->getWebformSubmission();
if (!$webform) {
throw new \Exception("Webform entity is required to check and element's access (rules).");
}
if (!empty($element['#private']) && !$webform
->access('submission_view_any', $account)) {
return FALSE;
}
$access_result = $this
->checkAccessRule($element, $operation, $account) ? AccessResult::allowed() : AccessResult::neutral();
$handler_result = $webform
->invokeHandlers('accessElement', $element, $operation, $account, $webform_submission);
$access_result = $access_result
->orIf($handler_result);
$context = [
'webform' => $webform,
'webform_submission' => $webform_submission,
];
$modules = \Drupal::moduleHandler()
->getImplementations('webform_element_access');
foreach ($modules as $module) {
$hook = $module . '_webform_element_access';
$hook_result = $hook($operation, $element, $account, $context);
$access_result = $access_result
->orIf($hook_result);
}
return $access_result
->isAllowed();
}
protected function checkAccessRule(array $element, $operation, AccountInterface $account) {
if (!isset($element['#access_' . $operation . '_roles']) && !isset($element['#access_' . $operation . '_users']) && !isset($element['#access_' . $operation . '_permissions'])) {
return TRUE;
}
if (!isset($element['#access_' . $operation . '_roles'])) {
$element['#access_' . $operation . '_roles'] = $this
->getDefaultProperty('access_' . $operation . '_roles') ?: [];
}
if (array_intersect($element['#access_' . $operation . '_roles'], $account
->getRoles())) {
return TRUE;
}
if (isset($element['#access_' . $operation . '_users']) && in_array($account
->id(), $element['#access_' . $operation . '_users'])) {
return TRUE;
}
if (isset($element['#access_' . $operation . '_permissions'])) {
foreach ($element['#access_' . $operation . '_permissions'] as $permission) {
if ($account
->hasPermission($permission)) {
return TRUE;
}
}
}
return FALSE;
}
public function replaceTokens(array &$element, EntityInterface $entity = NULL) {
$bubbleable_metadata = new WebformBubbleableMetadata();
foreach ($element as $key => $value) {
if (Element::child($key)) {
continue;
}
if (in_array($key, [
'#template',
'#format_html',
'#format_text',
'format_items_html',
'format_items_text',
])) {
continue;
}
$element[$key] = $this->tokenManager
->replace($value, $entity, [], [], $bubbleable_metadata);
}
$bubbleable_metadata
->appendTo($element);
}
protected function prepareElementValidateCallbacks(array &$element, WebformSubmissionInterface $webform_submission = NULL) {
if (!$this
->isInput($element)) {
return;
}
if (isset($element['#minlength'])) {
$element['#element_validate'][] = [
get_class($this),
'validateMinlength',
];
}
if (isset($element['#multiple']) && $element['#multiple'] > 1) {
$element['#element_validate'][] = [
get_class($this),
'validateMultiple',
];
}
if (isset($element['#unique']) && $webform_submission) {
$element['#element_validate'][] = [
get_class($this),
'validateUnique',
];
}
}
protected function prepareElementPreRenderCallbacks(array &$element, WebformSubmissionInterface $webform_submission = NULL) {
}
protected function prepareCompositeFormElement(array &$element) {
if (empty($element['#pre_render'])) {
return;
}
foreach ($element['#pre_render'] as $index => $pre_render) {
if (is_array($pre_render) && $pre_render[1] === 'preRenderCompositeFormElement') {
$element['#pre_render'][$index] = [
get_called_class(),
'preRenderWebformCompositeFormElement',
];
}
}
}
protected function prepareWrapper(array &$element) {
$has_states_wrapper = $this->pluginDefinition['states_wrapper'];
$has_flexbox_wrapper = !empty($element['#webform_parent_flexbox']);
if (!$has_states_wrapper && !$has_flexbox_wrapper) {
return;
}
$class = get_class($this);
if ($has_states_wrapper) {
$element['#pre_render'][] = [
$class,
'preRenderFixStatesWrapper',
];
}
if ($has_flexbox_wrapper) {
$element['#pre_render'][] = [
$class,
'preRenderFixFlexboxWrapper',
];
}
}
public static function preRenderFixStatesWrapper(array $element) {
if (isset($element['#webform_wrapper']) && $element['#webform_wrapper'] === FALSE) {
return $element;
}
WebformElementHelper::fixStatesWrapper($element);
return $element;
}
public static function preRenderFixFlexboxWrapper(array $element) {
if (isset($element['#webform_wrapper']) && $element['#webform_wrapper'] === FALSE) {
return $element;
}
$flex = isset($element['#flex']) ? $element['#flex'] : 1;
$element += [
'#prefix' => '',
'#suffix' => '',
];
$element['#prefix'] = '<div class="webform-flex webform-flex--' . $flex . '"><div class="webform-flex--container">' . $element['#prefix'];
$element['#suffix'] = $element['#suffix'] . '</div></div>';
return $element;
}
protected function prepareMultipleWrapper(array &$element) {
if (!$this
->hasMultipleValues($element) || !$this
->hasMultipleWrapper() || empty($element['#multiple'])) {
return;
}
$element['#element'] = $element;
$element['#element'] = array_diff_key($element['#element'], array_flip([
'#access',
'#default_value',
'#description',
'#description_display',
'#required',
'#required_error',
'#states',
'#wrapper_attributes',
'#prefix',
'#suffix',
'#element',
'#tags',
'#multiple',
]));
if (!empty($element['#states'])) {
$element['#element']['#_webform_states'] = $element['#states'];
}
$element['#element']['#title_display'] = 'invisible';
$element['#element']['#after_build'][] = [
get_class($this),
'hiddenElementAfterBuild',
];
$element['#label_attributes']['webform-remove-for-attribute'] = TRUE;
$element['#type'] = 'webform_multiple';
$element['#webform_multiple'] = TRUE;
if ($element['#multiple'] > 1) {
$element['#cardinality'] = $element['#multiple'];
}
$multiple_properties = $this
->defineDefaultMultipleProperties();
foreach ($multiple_properties as $multiple_property => $multiple_value) {
if (strpos($multiple_property, 'multiple__') === 0) {
$property_name = str_replace('multiple__', '', $multiple_property);
$element["#{$property_name}"] = isset($element["#{$multiple_property}"]) ? $element["#{$multiple_property}"] : $multiple_value;
}
}
if (!empty($element['#multiple__header_label'])) {
$element['#header'] = $element['#multiple__header_label'];
}
$element = array_diff_key($element, array_flip([
'#attributes',
'#field_prefix',
'#field_suffix',
'#pattern',
'#placeholder',
'#maxlength',
'#element_validate',
'#pre_render',
]));
if (isset($element['#unique'])) {
$element['#element_validate'][] = [
get_class($this),
'validateUniqueMultiple',
];
}
}
public function displayDisabledWarning(array $element) {
$t_args = [
'%title' => $this
->getLabel($element),
'%type' => $this
->getPluginLabel(),
':href' => Url::fromRoute('webform.config.elements')
->toString(),
];
if ($this->currentUser
->hasPermission('administer webform')) {
$message = $this
->t('%title is a %type element, which has been disabled and will not be rendered. Go to the <a href=":href">admin settings</a> page to enable this element.', $t_args);
}
else {
$message = $this
->t('%title is a %type element, which has been disabled and will not be rendered. Please contact a site administrator.', $t_args);
}
$this
->messenger()
->addWarning($message);
$context = [
'@title' => $this
->getLabel($element),
'@type' => $this
->getPluginLabel(),
'link' => Link::fromTextAndUrl($this
->t('Edit'), Url::fromRoute('<current>'))
->toString(),
];
$this->logger
->notice("'@title' is a '@type' element, which has been disabled and will not be rendered.", $context);
}
public function setDefaultValue(array &$element) {
}
public function getLabel(array $element) {
return !empty($element['#title']) ? $element['#title'] : $element['#webform_key'];
}
public function getAdminLabel(array $element) {
$element += [
'#admin_title' => '',
'#title' => '',
'#webform_key' => '',
];
return $element['#admin_title'] ?: $element['#title'] ?: $element['#webform_key'];
}
public function getKey(array $element) {
return $element['#webform_key'];
}
public function buildHtml(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
return $this
->build('html', $element, $webform_submission, $options);
}
public function buildText(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
return $this
->build('text', $element, $webform_submission, $options);
}
protected function build($format, array &$element, WebformSubmissionInterface $webform_submission, array $options = []) {
$options['multiline'] = $this
->isMultiline($element);
$format_function = 'format' . ucfirst($format);
$value = $this
->{$format_function}($element, $webform_submission, $options);
if ($value === '' || is_array($value) && $value === []) {
if ($this
->isEmptyExcluded($element, $options)) {
return NULL;
}
else {
$value = $this->configFactory
->get('webform.settings')
->get('element.empty_message');
}
}
if (is_string($value)) {
$value = [
'#' . ($format === 'text' ? 'plain_text' : 'markup') => $value,
];
}
return [
'#theme' => 'webform_element_base_' . $format,
'#element' => $element,
'#value' => $value,
'#webform_submission' => $webform_submission,
'#options' => $options,
];
}
public function formatHtml(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
return $this
->format('Html', $element, $webform_submission, $options);
}
public function formatText(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
return $this
->format('Text', $element, $webform_submission, $options);
}
protected function format($type, array &$element, WebformSubmissionInterface $webform_submission, array $options = []) {
if (!$this
->hasValue($element, $webform_submission, $options) && !$this
->isContainer($element)) {
return '';
}
$item_function = 'format' . $type . 'Item';
$items_function = 'format' . $type . 'Items';
if ($this
->hasMultipleValues($element)) {
if (isset($options['delta'])) {
return $this
->{$item_function}($element, $webform_submission, $options);
}
elseif ($this
->getItemsFormat($element) === 'custom' && !empty($element['#format_items_' . strtolower($type)])) {
return $this
->formatCustomItems($type, $element, $webform_submission, $options);
}
else {
return $this
->{$items_function}($element, $webform_submission, $options);
}
}
else {
if ($this
->getItemFormat($element) === 'custom' && !empty($element['#format_' . strtolower($type)])) {
return $this
->formatCustomItem($type, $element, $webform_submission, $options);
}
else {
return $this
->{$item_function}($element, $webform_submission, $options);
}
}
}
protected function formatCustomItems($type, array &$element, WebformSubmissionInterface $webform_submission, array $options = [], array $context = []) {
$name = strtolower($type);
$value = $this
->getValue($element, $webform_submission, $options);
$items = [];
$item_function = 'format' . $type . 'Item';
foreach (array_keys($value) as $delta) {
$items[] = $this
->{$item_function}($element, $webform_submission, [
'delta' => $delta,
] + $options);
}
$template = trim($element['#format_items_' . $name]);
$options += [
'context' => [],
];
$context += [
'value' => $value,
'items' => $items,
];
return WebformTwigExtension::buildTwigTemplate($webform_submission, $template, $options, $context);
}
protected function formatHtmlItems(array &$element, WebformSubmissionInterface $webform_submission, array $options = []) {
$value = $this
->getValue($element, $webform_submission, $options);
$items = [];
foreach (array_keys($value) as $delta) {
$item = $this
->formatHtmlItem($element, $webform_submission, [
'delta' => $delta,
] + $options);
if ($item) {
$items[] = $item;
}
}
if (empty($items)) {
return [];
}
$format = $this
->getItemsFormat($element);
switch ($format) {
case 'ol':
case 'ul':
return [
'#theme' => 'item_list',
'#items' => $items,
'#list_type' => $format,
];
case 'and':
$total = count($items);
if ($total === 1) {
$item = current($items);
return is_array($item) ? $item : [
'#markup' => $item,
];
}
$build = [];
foreach ($items as $index => &$item) {
$build[] = is_array($item) ? $item : [
'#markup' => $item,
];
if ($total === 2 && $index === 0) {
$build[] = [
'#markup' => ' ' . $this
->t('and') . ' ',
];
}
elseif ($index !== $total - 1) {
if ($index === $total - 2) {
$build[] = [
'#markup' => ', ' . $this
->t('and') . ' ',
];
}
else {
$build[] = [
'#markup' => ', ',
];
}
}
}
return $build;
default:
case 'br':
case 'semicolon':
case 'comma':
case 'space':
case 'hr':
$delimiters = [
'hr' => '<hr class="webform-horizontal-rule" />',
'br' => '<br />',
'semicolon' => '; ',
'comma' => ', ',
'space' => ' ',
];
$delimiter = isset($delimiters[$format]) ? $delimiters[$format] : $format;
$total = count($items);
$build = [];
foreach ($items as $index => &$item) {
$build[] = is_array($item) ? $item : [
'#markup' => $item,
];
if ($index !== $total - 1) {
$build[] = [
'#markup' => $delimiter,
];
}
}
return $build;
}
}
protected function formatTextItems(array &$element, WebformSubmissionInterface $webform_submission, array $options = []) {
$value = $this
->getValue($element, $webform_submission, $options);
$items = [];
foreach (array_keys($value) as $delta) {
$item = $this
->formatTextItem($element, $webform_submission, [
'delta' => $delta,
] + $options);
if ($item) {
$items[] = $item;
}
}
if (empty($items)) {
return '';
}
$format = $this
->getItemsFormat($element);
switch ($format) {
case 'ol':
$list = [];
$index = 1;
foreach ($items as $item) {
$prefix = $index++ . '. ';
$list[] = $prefix . str_replace(PHP_EOL, PHP_EOL . str_repeat(' ', strlen($prefix)), $item);
}
return implode(PHP_EOL, $list);
case 'ul':
$list = [];
foreach ($items as $index => $item) {
$list[] = '- ' . str_replace(PHP_EOL, PHP_EOL . ' ', $item);
}
return implode(PHP_EOL, $list);
case 'and':
return WebformArrayHelper::toString($items);
default:
case 'br':
case 'semicolon':
case 'comma':
case 'space':
case 'hr':
$delimiters = [
'hr' => PHP_EOL . '---' . PHP_EOL,
'br' => PHP_EOL,
'semicolon' => '; ',
'comma' => ', ',
'space' => ' ',
];
$delimiter = isset($delimiters[$format]) ? $delimiters[$format] : $format;
return implode($delimiter, $items);
}
}
protected function formatCustomItem($type, array &$element, WebformSubmissionInterface $webform_submission, array $options = [], array $context = []) {
$name = strtolower($type);
$template = trim($element['#format_' . $name]);
$context['value'] = $this
->getValue($element, $webform_submission, $options);
$context['item'] = [];
if (preg_match_all("/item(?:\\[['\"]|\\.)([a-zA-Z0-9-_:]+)/", $template, $matches)) {
$formats = array_unique($matches[1]);
$item_function = 'format' . $type . 'Item';
foreach ($formats as $format) {
$context['item'][$format] = $this
->{$item_function}([
'#format' => $format,
] + $element, $webform_submission, $options);
}
}
if ($type === 'Text') {
return WebformTwigExtension::renderTwigTemplate($webform_submission, $template, $options, $context);
}
else {
return WebformTwigExtension::buildTwigTemplate($webform_submission, $template, $options, $context);
}
}
protected function formatHtmlItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
$format = $this
->getItemFormat($element);
$value = $this
->formatTextItem($element, $webform_submission, [
'prefixing' => FALSE,
] + $options);
if ($format === 'raw') {
return $value;
}
$build = [
'#plain_text' => $value,
];
$options += [
'prefixing' => TRUE,
];
if ($options['prefixing']) {
if (isset($element['#field_prefix'])) {
$build['#prefix'] = $element['#field_prefix'];
}
if (isset($element['#field_suffix'])) {
$build['#suffix'] = $element['#field_suffix'];
}
}
return $build;
}
protected function formatTextItem(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
$value = $this
->getValue($element, $webform_submission, $options);
$format = $this
->getItemFormat($element);
if ($format === 'raw') {
return $value;
}
$options += [
'prefixing' => TRUE,
];
if ($options['prefixing']) {
if (isset($element['#field_prefix'])) {
$value = strip_tags($element['#field_prefix']) . $value;
}
if (isset($element['#field_suffix'])) {
$value .= strip_tags($element['#field_suffix']);
}
}
return $value;
}
public function hasValue(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
$value = $this
->getValue($element, $webform_submission, $options);
return $value === '' || $value === NULL || is_array($value) && empty($value) ? FALSE : TRUE;
}
public function getValue(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
if (!isset($element['#webform_key']) && isset($element['#value'])) {
return $element['#value'];
}
$webform_key = isset($options['webform_key']) ? $options['webform_key'] : $element['#webform_key'];
$value = $webform_submission
->getElementData($webform_key);
if ($value === NULL && isset($element['#default_value'])) {
$value = $element['#default_value'];
}
if (is_array($value)) {
if (isset($options['delta'])) {
$value = isset($value[$options['delta']]) ? $value[$options['delta']] : NULL;
}
if ($value && isset($options['composite_key'])) {
$value = isset($value[$options['composite_key']]) ? $value[$options['composite_key']] : NULL;
}
}
return $value;
}
public function getRawValue(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
$element['#format'] = 'raw';
return $this
->getValue($element, $webform_submission, $options);
}
public function getItemFormats() {
return [
'value' => $this
->t('Value'),
'raw' => $this
->t('Raw value'),
];
}
public function getItemDefaultFormat() {
return 'value';
}
public function getItemFormat(array $element) {
if (isset($element['#format'])) {
return $element['#format'];
}
elseif ($default_format = $this->configFactory
->get('webform.settings')
->get('format.' . $this
->getPluginId() . '.item')) {
return $default_format;
}
else {
return $this
->getItemDefaultFormat();
}
}
public function getItemsDefaultFormat() {
return 'ul';
}
public function getItemsFormats() {
return [
'comma' => $this
->t('Comma'),
'semicolon' => $this
->t('Semicolon'),
'and' => $this
->t('And'),
'ol' => $this
->t('Ordered list'),
'ul' => $this
->t('Unordered list'),
];
}
public function getItemsFormat(array $element) {
if (isset($element['#format_items'])) {
return $element['#format_items'];
}
elseif ($default_format = $this->configFactory
->get('webform.settings')
->get('format.' . $this
->getPluginId() . '.items')) {
return $default_format;
}
else {
return $this
->getItemsDefaultFormat();
}
}
public function preview() {
return [
'#type' => $this
->getTypeName(),
'#title' => $this
->getPluginLabel(),
];
}
public function getTestValues(array $element, WebformInterface $webform, array $options = []) {
return FALSE;
}
public function getTableColumn(array $element) {
$key = $element['#webform_key'];
return [
'element__' . $key => [
'title' => $this
->getAdminLabel($element),
'sort' => TRUE,
'key' => $key,
'property_name' => NULL,
'element' => $element,
'plugin' => $this,
],
];
}
public function formatTableColumn(array $element, WebformSubmissionInterface $webform_submission, array $options = []) {
return $this
->formatHtml($element, $webform_submission);
}
public function isEmptyExcluded(array $element, array $options) {
$options += [
'exclude_empty' => TRUE,
];
return !empty($options['exclude_empty']);
}
public function getExportDefaultOptions() {
return [];
}
public function buildExportOptionsForm(array &$form, FormStateInterface $form_state, array $export_options) {
}
public function buildExportHeader(array $element, array $export_options) {
if ($export_options['header_format'] === 'label') {
return [
$this
->getAdminLabel($element),
];
}
else {
return [
$element['#webform_key'],
];
}
}
protected function prefixExportHeader(array $header, array $element, array $export_options) {
if (empty($export_options['header_prefix'])) {
return $header;
}
if ($export_options['header_format'] === 'label') {
$prefix = $this
->getAdminLabel($element) . $export_options['header_prefix_label_delimiter'];
}
else {
$prefix = $this
->getKey($element) . $export_options['header_prefix_key_delimiter'];
}
foreach ($header as $index => $column) {
$header[$index] = $prefix . $column;
}
return $header;
}
public function buildExportRecord(array $element, WebformSubmissionInterface $webform_submission, array $export_options) {
$element['#format_items'] = $export_options['multiple_delimiter'];
return [
$this
->formatText($element, $webform_submission, $export_options),
];
}
public static function validateMinlength(&$element, FormStateInterface &$form_state) {
if (!isset($element['#minlength'])) {
return;
}
if (!empty($element['#value']) && mb_strlen($element['#value']) < $element['#minlength']) {
$t_args = [
'%name' => empty($element['#title']) ? $element['#parents'][0] : $element['#title'],
'%min' => $element['#minlength'],
'%length' => mb_strlen($element['#value']),
];
$form_state
->setError($element, t('%name cannot be less than %min characters but is currently %length characters long.', $t_args));
}
}
public static function validateUnique(array &$element, FormStateInterface $form_state) {
if (!isset($element['#unique'])) {
return;
}
$element_manager = \Drupal::service('plugin.manager.webform.element');
$name = $element['#webform_key'];
$value = NestedArray::getValue($form_state
->getValues(), $element['#parents']);
$element_plugin = $element_manager
->getElementInstance($element);
if ($element_plugin
->isComposite()) {
return;
}
if ($value === '' || $value === NULL || is_array($value) && empty($value)) {
return;
}
$form_object = $form_state
->getFormObject();
$webform_submission = $form_object
->getEntity();
$webform = $webform_submission
->getWebform();
$query = \Drupal::database()
->select('webform_submission', 'ws');
$query
->leftJoin('webform_submission_data', 'wsd', 'ws.sid = wsd.sid');
$query
->fields('wsd', [
'value',
]);
$query
->condition('wsd.webform_id', $webform
->id());
$query
->condition('wsd.name', $name);
$query
->condition('wsd.value', (array) $value, 'IN');
if (!empty($element['#unique_user'])) {
$query
->condition('ws.uid', $webform_submission
->getOwnerId());
}
if (!empty($element['#unique_entity'])) {
if ($source_entity = $webform_submission
->getSourceEntity()) {
$query
->condition('ws.entity_type', $source_entity
->getEntityTypeId());
$query
->condition('ws.entity_id', $source_entity
->id());
}
else {
$query
->isNull('ws.entity_type');
$query
->isNull('ws.entity_id');
}
}
if ($sid = $webform_submission
->id()) {
$query
->condition('ws.sid', $sid, '<>');
}
$query
->range(0, 1);
$duplicate_value = $query
->execute()
->fetchField();
if ($duplicate_value === FALSE || $duplicate_value === '') {
return;
}
if (isset($element['#unique_error'])) {
$form_state
->setError($element, WebformHtmlHelper::toHtmlMarkup($element['#unique_error']));
}
elseif (isset($element['#title'])) {
if (isset($element['#options'])) {
$duplicate_value = WebformOptionsHelper::getOptionText($duplicate_value, $element['#options'], TRUE);
}
$t_args = [
'%name' => empty($element['#title']) ? $element['#parents'][0] : $element['#title'],
'%value' => $duplicate_value,
];
$form_state
->setError($element, t('The value %value has already been submitted once for the %name element. You may have already submitted this webform, or you need to use a different value.', $t_args));
}
else {
$form_state
->setError($element);
}
}
public static function validateUniqueMultiple(array &$element, FormStateInterface $form_state) {
if (!isset($element['#unique'])) {
return;
}
$name = $element['#name'];
$value = NestedArray::getValue($form_state
->getValues(), $element['#parents']);
if (empty($value)) {
return;
}
if (count($value) !== count(array_unique($value))) {
$duplicates = WebformArrayHelper::getDuplicates($value);
if (isset($element['#unique_error'])) {
$form_state
->setError($element, WebformHtmlHelper::toHtmlMarkup($element['#unique_error']));
}
elseif (isset($element['#title'])) {
$t_args = [
'%name' => empty($element['#title']) ? $name : $element['#title'],
'%value' => reset($duplicates),
];
$form_state
->setError($element, t('The value %value has already been submitted once for the %name element. You may have already submitted this webform, or you need to use a different value.', $t_args));
}
else {
$form_state
->setError($element);
}
}
}
public static function validateMultiple(array &$element, FormStateInterface $form_state) {
if (!isset($element['#multiple'])) {
return;
}
$values = NestedArray::getValue($form_state
->getValues(), $element['#parents']);
if (empty($values) || !is_array($values)) {
return;
}
if (count($values) > $element['#multiple']) {
if (isset($element['#multiple_error'])) {
$form_state
->setError($element, $element['#multiple_error']);
}
elseif (isset($element['#title'])) {
$t_args = [
'%name' => empty($element['#title']) ? $element['#parents'][0] : $element['#title'],
'@count' => $element['#multiple'],
];
$form_state
->setError($element, t('%name: this element cannot hold more than @count values.', $t_args));
}
else {
$form_state
->setError($element);
}
}
}
public function getElementStateOptions() {
$visibility_optgroup = (string) $this
->t('Visibility');
$state_optgroup = (string) $this
->t('State');
$validation_optgroup = (string) $this
->t('Validation');
$value_optgroup = (string) $this
->t('Value');
$states = [];
$states += [
$visibility_optgroup => [
'visible' => $this
->t('Visible'),
'invisible' => $this
->t('Hidden'),
'visible-slide' => $this
->t('Visible (Slide)'),
'invisible-slide' => $this
->t('Hidden (Slide)'),
],
$state_optgroup => [
'enabled' => $this
->t('Enabled'),
'disabled' => $this
->t('Disabled'),
],
$validation_optgroup => [
'required' => $this
->t('Required'),
'optional' => $this
->t('Optional'),
],
];
if ($this
->hasProperty('readonly') || $this
->isContainer([
'#type' => $this
->getPluginId(),
])) {
$states[$state_optgroup] += [
'readwrite' => $this
->t('Read/write'),
'readonly' => $this
->t('Read-only'),
];
}
if ($this instanceof Checkbox || $this instanceof Checkboxes) {
$states[$value_optgroup] = [
'checked' => $this
->t('Checked'),
'unchecked' => $this
->t('Unchecked'),
];
}
if ($this instanceof Details) {
$states[$state_optgroup] += [
'expanded' => $this
->t('Expanded'),
'collapsed' => $this
->t('Collapsed'),
];
}
return $states;
}
public function getElementSelectorOptions(array $element) {
if ($this
->hasMultipleValues($element) && $this
->hasMultipleWrapper()) {
return [];
}
$title = $this
->getAdminLabel($element) . ' [' . $this
->getPluginLabel() . ']';
$name = $element['#webform_key'];
if ($inputs = $this
->getElementSelectorInputsOptions($element)) {
$selectors = [];
foreach ($inputs as $input_name => $input_title) {
$selectors[":input[name=\"{$name}[{$input_name}]\"]"] = $input_title;
}
return [
$title => $selectors,
];
}
else {
return [
":input[name=\"{$name}\"]" => $title,
];
}
}
public function getElementSelectorSourceValues(array $element) {
return [];
}
protected function getElementSelectorInputsOptions(array $element) {
return [];
}
public function getElementSelectorInputValue($selector, $trigger, array $element, WebformSubmissionInterface $webform_submission) {
if ($this
->isComposite()) {
$input_name = WebformSubmissionConditionsValidator::getSelectorInputName($selector);
$composite_key = WebformSubmissionConditionsValidator::getInputNameAsArray($input_name, 1);
if ($composite_key) {
return $this
->getRawValue($element, $webform_submission, [
'composite_key' => $composite_key,
]);
}
else {
return NULL;
}
}
else {
return $this
->getRawValue($element, $webform_submission);
}
}
public function preCreate(array &$element, array &$values) {
}
public function postCreate(array &$element, WebformSubmissionInterface $webform_submission) {
}
public function postLoad(array &$element, WebformSubmissionInterface $webform_submission) {
}
public function preDelete(array &$element, WebformSubmissionInterface $webform_submission) {
}
public function postDelete(array &$element, WebformSubmissionInterface $webform_submission) {
}
public function preSave(array &$element, WebformSubmissionInterface $webform_submission) {
}
public function postSave(array &$element, WebformSubmissionInterface $webform_submission, $update = TRUE) {
}
public function getOffCanvasWidth() {
return WebformDialogHelper::DIALOG_NARROW;
}
public function form(array $form, FormStateInterface $form_state) {
$form_object = $form_state
->getFormObject();
$webform = $form_object
->getWebform();
$element_properties = $form_state
->get('element_properties');
$form['element'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Element settings'),
'#access' => TRUE,
'#weight' => -50,
];
$form['element']['title'] = [
'#type' => 'textfield',
'#title' => $this
->t('Title'),
'#maxlength' => NULL,
'#description' => $this
->t('This is used as a descriptive label when displaying this webform element.'),
'#required' => TRUE,
'#attributes' => [
'autofocus' => 'autofocus',
],
];
$form['element']['value'] = [
'#type' => 'textarea',
'#title' => $this
->t('Value'),
'#description' => $this
->t('The value of the webform element.'),
];
$form['element']['multiple'] = [
'#title' => $this
->t('Allowed number of values'),
'#type' => 'webform_element_multiple',
];
$form['element']['multiple_error'] = [
'#type' => 'textfield',
'#title' => $this
->t('Custom allowed number of values error message'),
'#description' => $this
->t('If set, this message will be used when an element\'s allowed number of values is exceeded, instead of the default "@message" message.', [
'@message' => $this
->t('%name: this element cannot hold more than @count values.'),
]),
'#states' => [
'visible' => [
':input[name="properties[multiple][container][cardinality]"]' => [
'value' => 'number',
],
':input[name="properties[multiple][container][cardinality_number]"]' => [
'!value' => 1,
],
],
],
];
$form['element_description'] = [
'#type' => 'details',
'#title' => $this
->t('Element description/help/more'),
];
$form['element_description']['description'] = [
'#type' => 'webform_html_editor',
'#title' => $this
->t('Description'),
'#description' => $this
->t('A short description of the element used as help for the user when they use the webform.'),
];
$form['element_description']['help'] = [
'#type' => 'details',
'#title' => $this
->t('Help'),
'#description' => $this
->t("Displays a Help tooltip after the element's title."),
'#states' => [
'invisible' => [
[
':input[name="properties[title_display]"]' => [
'value' => 'invisible',
],
],
],
],
];
$form['element_description']['help']['help_title'] = [
'#type' => 'textfield',
'#title' => $this
->t('Help title'),
'#description' => $this
->t("The text displayed in Help tooltip after the element's title.") . '<br /><br />' . $this
->t("Defaults to the element's title"),
];
$form['element_description']['help']['help'] = [
'#type' => 'webform_html_editor',
'#title' => $this
->t('Help text'),
'#description' => $this
->t("The text displayed in Help tooltip after the element's title."),
];
$form['element_description']['more'] = [
'#type' => 'details',
'#title' => $this
->t('More'),
'#description' => $this
->t("Displays a read more hide/show widget below the element's description."),
];
$form['element_description']['more']['more_title'] = [
'#type' => 'textfield',
'#title' => $this
->t('More title'),
'#description' => $this
->t('The click-able label used to open and close more text.') . '<br /><br />' . $this
->t('Defaults to: %value', [
'%value' => $this->configFactory
->get('webform.settings')
->get('element.default_more_title'),
]),
];
$form['element_description']['more']['more'] = [
'#type' => 'webform_html_editor',
'#title' => $this
->t('More text'),
'#description' => $this
->t('A long description of the element that provides form additional information which can opened and closed.'),
];
$form['form'] = [
'#type' => 'details',
'#title' => $this
->t('Form display'),
];
$form['form']['display_container'] = $this
->getFormInlineContainer();
$form['form']['display_container']['title_display'] = [
'#type' => 'select',
'#title' => $this
->t('Title display'),
'#empty_option' => $this
->t('- Default -'),
'#options' => [
'before' => $this
->t('Before'),
'after' => $this
->t('After'),
'inline' => $this
->t('Inline'),
'invisible' => $this
->t('Invisible'),
'none' => $this
->t('None'),
],
'#description' => $this
->t('Determines the placement of the title.'),
];
if ($this
->hasCompositeFormElementWrapper()) {
unset($form['form']['display_container']['title_display']['#options']['after']);
}
$form['form']['display_container']['description_display'] = [
'#type' => 'select',
'#title' => $this
->t('Description display'),
'#empty_option' => $this
->t('- Default -'),
'#options' => [
'before' => $this
->t('Before'),
'after' => $this
->t('After'),
'invisible' => $this
->t('Invisible'),
'tooltip' => $this
->t('Tooltip'),
],
'#description' => $this
->t('Determines the placement of the description.'),
];
$form['form']['display_container']['help_display'] = [
'#type' => 'select',
'#title' => $this
->t('Help display'),
'#empty_option' => $this
->t('- Default -'),
'#options' => [
'title_before' => $this
->t('Before title'),
'title_after' => $this
->t('After title'),
'element_before' => $this
->t('Before element'),
'element_after' => $this
->t('After element'),
],
'#description' => $this
->t('Determines the placement of the Help tooltip.'),
];
if ($this
->hasProperty('title_display')) {
$form['form']['title_display_message'] = [
'#type' => 'webform_message',
'#message_type' => 'warning',
'#message_message' => $this
->t("Please note: Settings the element's title display to 'none' means the title will not be rendered or accessible to screenreaders"),
'#message_close' => TRUE,
'#message_storage' => WebformMessage::STORAGE_LOCAL,
'#access' => TRUE,
'#states' => [
'visible' => [
':input[name="properties[title_display]"]' => [
'value' => 'none',
],
],
],
];
}
if ($this
->isComposite()) {
unset($form['form']['display_container']['title_display']['#options']['inline']);
unset($form['form']['display_container']['description_display']['#options']['tooltip']);
}
$element_types = [
'webform_codemirror',
'webform_email_confirm',
'webform_htmleditor',
'webform_mapping',
'webform_signature',
];
if (in_array($this
->getPluginId(), $element_types)) {
unset($form['form']['display_container']['title_display']['#options']['inline']);
}
$element_types = [
'fieldset',
'details',
'webform_codemirror',
'webform_email_confirm',
'webform_htmleditor',
'webform_image_select',
'webform_likert',
'webform_mapping',
'webform_signature',
];
if (in_array($this
->getPluginId(), $element_types)) {
unset($form['form']['display_container']['title_display']['#options']['inline']);
}
$form['form']['field_container'] = $this
->getFormInlineContainer();
$form['form']['field_container']['field_prefix'] = [
'#type' => 'textfield',
'#title' => $this
->t('Field prefix'),
'#description' => $this
->t('Text or code that is placed directly in front of the input. This can be used to prefix an input with a constant string. Examples: $, #, -.'),
'#size' => 10,
];
$form['form']['field_container']['field_suffix'] = [
'#type' => 'textfield',
'#title' => $this
->t('Field suffix'),
'#description' => $this
->t('Text or code that is placed directly after the input. This can be used to add a unit to an input. Examples: lb, kg, %.'),
'#size' => 10,
];
$form['form']['length_container'] = $this
->getFormInlineContainer();
$form['form']['length_container']['minlength'] = [
'#type' => 'number',
'#title' => $this
->t('Minlength'),
'#description' => $this
->t('The element may still be empty unless it is required.'),
'#min' => 1,
'#size' => 4,
];
$form['form']['length_container']['maxlength'] = [
'#type' => 'number',
'#title' => $this
->t('Maxlength'),
'#description' => $this
->t('Leaving blank will use the default maxlength.'),
'#min' => 1,
'#size' => 4,
];
$form['form']['size_container'] = $this
->getFormInlineContainer();
$form['form']['size_container']['size'] = [
'#type' => 'number',
'#title' => $this
->t('Size'),
'#description' => $this
->t('Leaving blank will use the default size.'),
'#min' => 1,
'#size' => 4,
];
$form['form']['size_container']['rows'] = [
'#type' => 'number',
'#title' => $this
->t('Rows'),
'#description' => $this
->t('Leaving blank will use the default rows.'),
'#min' => 1,
'#size' => 4,
];
$form['form']['placeholder'] = [
'#type' => 'textfield',
'#title' => $this
->t('Placeholder'),
'#description' => $this
->t('The placeholder will be shown in the element until the user starts entering a value.'),
];
$form['form']['autocomplete'] = [
'#type' => 'webform_select_other',
'#title' => $this
->t('Autocomplete'),
'#description' => $this
->t("Setting autocomplete to off will disable autocompletion for this element. Select 'Autofill' to use semantic attribute values for collecting certain types of user information."),
'#options' => [
'on' => $this
->t('On'),
'off' => $this
->t('Off'),
],
'#other__type' => 'select',
'#other__option_label' => $this
->t('Autofill…'),
'#other__title' => $this
->t('Autocomplete autofill'),
'#other__description' => $this
->t("Browsers sometimes have features for helping users fill forms in, for example prefilling the user's address based on earlier user input. The autocomplete (autofill) attribute can be used to hint to the user agent how to, or indeed whether to, provide such a feature."),
'#other__options' => [
(string) $this
->t('Biographical attributes') => [
"name" => $this
->t('Full name'),
"honorific-prefix" => $this
->t('Honorific prefix'),
"given-name" => $this
->t('Given name'),
"additional-name" => $this
->t('Additional names'),
"family-name" => $this
->t('Family name'),
"honorific-suffix" => $this
->t('Honorific suffix'),
"nickname" => $this
->t('Nickname'),
"username" => $this
->t('Username'),
"new-password" => $this
->t('New password'),
"current-password" => $this
->t('Current password'),
"organization-title" => $this
->t('Organization job title'),
"organization" => $this
->t('Organization name'),
"language" => $this
->t('Preferred language'),
"bday" => $this
->t('Birthday'),
"bday-day" => $this
->t('Birthday day'),
"bday-month" => $this
->t('Birthday month'),
"bday-year" => $this
->t('Birthday year'),
"sex" => $this
->t('Sex'),
"url" => $this
->t('Contact URL'),
"photo" => $this
->t('Contact photo'),
"email" => $this
->t('Email'),
"impp" => $this
->t('Instant messaging URL'),
],
(string) $this
->t('Address attributes') => [
"street-address" => $this
->t('Street address (multiline)'),
"address-line1" => $this
->t('Address line 1'),
"address-line2" => $this
->t('Address line 2'),
"address-line3" => $this
->t('Address line 3'),
"address-level1" => $this
->t('Address level 1'),
"address-level2" => $this
->t('Address level 2'),
"address-level3" => $this
->t('Address level 3'),
"address-level4" => $this
->t('Address level 4'),
"country" => $this
->t('Country code'),
"country-name" => $this
->t('Country name'),
"postal-code" => $this
->t('Postal code / Zip code'),
],
(string) $this
->t('Telephone attributes') => [
"tel" => $this
->t('Telephone'),
"home tel" => $this
->t('Telephone - home'),
"work tel" => $this
->t('Telephone - work'),
"work tel-extension" => $this
->t('Telephone - work extension'),
"mobile tel" => $this
->t('Telephone - mobile'),
"fax tel" => $this
->t('Telephone - fax'),
"pager tel" => $this
->t('Telephone - pager'),
"tel-country-code" => $this
->t('Telephone country code'),
"tel-national" => $this
->t('Telephone national code'),
"tel-area-code" => $this
->t('Telephone area code'),
"tel-local" => $this
->t('Telephone local number'),
"tel-local-prefix" => $this
->t('Telephone local prefix'),
"tel-local-suffix" => $this
->t('Telephone local suffix'),
"tel-extension" => $this
->t('Telephone extension'),
],
(string) $this
->t('Commerce attributes') => [
"cc-name" => $this
->t('Name on card'),
"cc-given-name" => $this
->t('Given name on card'),
"cc-additional-name" => $this
->t('Additional names on card'),
"cc-family-name" => $this
->t('Family name on card'),
"cc-number" => $this
->t('Card number'),
"cc-exp" => $this
->t('Card expiry date'),
"cc-exp-month" => $this
->t('Card expiry month'),
"cc-exp-year" => $this
->t('Card expiry year'),
"cc-csc" => $this
->t('Card Security Code'),
"cc-type" => $this
->t('Card type'),
"transaction-currency" => $this
->t('Transaction currency'),
"transaction-amount" => $this
->t('Transaction amount'),
],
],
];
$form['form']['disabled'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Disabled'),
'#description' => $this
->t('Make this element non-editable with the user entered (e.g. via developer tools) value <strong>ignored</strong>. Useful for displaying default value. Changeable via JavaScript.'),
'#return_value' => TRUE,
'#weight' => 50,
];
$form['form']['readonly'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Readonly'),
'#description' => $this
->t('Make this element non-editable with the user entered (e.g. via developer tools) value <strong>submitted</strong>. Useful for displaying default value. Changeable via JavaScript.'),
'#return_value' => TRUE,
'#weight' => 50,
];
$form['form']['prepopulate'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Prepopulate'),
'#description' => $this
->t('Allow element to be populated using query string parameters.'),
'#return_value' => TRUE,
'#weight' => 50,
];
if ($webform
->getSetting('form_prepopulate') && $this
->hasProperty('prepopulate')) {
$form['form']['prepopulate_disabled'] = [
'#description' => $this
->t('Prepopulation is enabled for all form elements.'),
'#value' => TRUE,
'#disabled' => TRUE,
'#access' => TRUE,
] + $form['form']['prepopulate'];
$form['form']['prepopulate']['#value'] = FALSE;
$form['form']['prepopulate']['#access'] = FALSE;
}
$form['form']['open'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Open'),
'#description' => $this
->t('Contents should be visible (open) to the user.'),
'#return_value' => TRUE,
'#weight' => 50,
];
$form['options'] = [];
$form['options_other'] = [];
$form['validation'] = [
'#type' => 'details',
'#title' => $this
->t('Form validation'),
];
$error_messages = [
'required_error',
'unique_error',
'pattern_error',
];
$validation_html_message_states = [];
foreach ($error_messages as $error_message) {
if ($this
->hasProperty($error_message)) {
if ($validation_html_message_states) {
$validation_html_message_states[] = 'or';
}
$validation_html_message_states[] = [
':input[name="properties[' . $error_message . ']"]' => [
'value' => [
'pattern' => '(<[a-z][^>]*>|&(?:[a-z]+|#\\d+);)',
],
],
];
}
}
if ($validation_html_message_states) {
$form['validation']['html_message'] = [
'#type' => 'webform_message',
'#message_message' => $this
->t('Validation error message contains HTML markup. HTML markup can not be display via HTML5 clientside validation and will be removed.'),
'#message_type' => 'warning',
'#states' => [
'visible' => $validation_html_message_states,
],
'#access' => TRUE,
];
}
$form['validation']['required_container'] = [
'#type' => 'container',
];
$form['validation']['required_container']['required'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Required'),
'#description' => $this
->t('Check this option if the user must enter a value.'),
'#return_value' => TRUE,
];
$form['validation']['required_container']['required_error'] = [
'#type' => 'textfield',
'#title' => $this
->t('Required message'),
'#description' => $this
->t('If set, this message will be used when a required webform element is empty, instead of the default "Field x is required." message.'),
'#states' => [
'visible' => [
':input[name="properties[required]"]' => [
'checked' => TRUE,
],
],
],
];
$form['validation']['unique_container'] = $this
->getFormInlineContainer();
$form['validation']['unique_container']['unique'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Unique'),
'#description' => $this
->t('Check that all entered values for this element are unique.'),
'#return_value' => TRUE,
];
$form['validation']['unique_container']['unique_entity'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Unique per entity'),
'#description' => $this
->t('Check that entered values for this element is unique for the current source entity.'),
'#return_value' => TRUE,
'#states' => [
'visible' => [
[
'input[name="properties[unique]"]' => [
'checked' => TRUE,
],
],
],
],
];
$form['validation']['unique_container']['unique_user'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Unique per user'),
'#description' => $this
->t('Check that entered values for this element are unique for the current user.'),
'#return_value' => TRUE,
'#states' => [
'visible' => [
[
'input[name="properties[unique]"]' => [
'checked' => TRUE,
],
],
],
],
];
$form['validation']['unique_error'] = [
'#type' => 'textfield',
'#title' => $this
->t('Unique message'),
'#description' => $this
->t('If set, this message will be used when an element\'s value are not unique, instead of the default "@message" message.', [
'@message' => $this
->t('The value %value has already been submitted once for the %name element. You may have already submitted this webform, or you need to use a different value.'),
]),
'#states' => [
'visible' => [
[
':input[name="properties[unique]"]' => [
'checked' => TRUE,
],
],
],
],
];
$form['flex'] = [
'#type' => 'details',
'#title' => $this
->t('Flexbox item'),
'#description' => $this
->t('Learn more about using <a href=":href">flexbox layouts</a>.', [
':href' => 'https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox',
]),
];
$flex_range = range(0, 12);
$form['flex']['flex'] = [
'#type' => 'select',
'#title' => $this
->t('Flex'),
'#description' => $this
->t('The flex property specifies the length of the item, relative to the rest of the flexible items inside the same container.') . '<br /><br />' . $this
->t('Defaults to: %value', [
'%value' => 1,
]),
'#options' => [
0 => $this
->t('0 (none)'),
] + array_combine($flex_range, $flex_range),
];
$form['conditional_logic'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Conditional logic'),
];
$form['conditional_logic']['states'] = [
'#type' => 'webform_element_states',
'#state_options' => $this
->getElementStateOptions(),
'#selector_options' => $webform
->getElementsSelectorOptions(),
'#selector_sources' => $webform
->getElementsSelectorSourceValues(),
'#disabled_message' => TRUE,
];
$form['conditional_logic']['states_clear'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Clear value(s) when hidden'),
'#return_value' => TRUE,
'#description' => $this instanceof ContainerBase ? $this
->t("When this container is hidden all this container's subelement values will be cleared.") : $this
->t("When this element is hidden, this element's value will be cleared."),
];
if ($this
->hasProperty('states') && $this
->hasProperty('required')) {
$form['conditional_logic']['states_required_message'] = [
'#type' => 'webform_message',
'#message_type' => 'warning',
'#message_message' => $this
->t('Please note when an element is hidden it will not be required.'),
'#access' => TRUE,
'#states' => [
'visible' => [
':input[name="properties[required]"]' => [
'checked' => TRUE,
],
],
],
];
}
$form['default'] = [
'#type' => 'details',
'#title' => $this
->t('Default value'),
];
if ($this
->isComposite()) {
$form['default']['default_value'] = [
'#type' => 'webform_codemirror',
'#mode' => 'yaml',
'#title' => $this
->t('Default value'),
];
}
else {
$form['default']['default_value'] = [
'#type' => 'textfield',
'#title' => $this
->t('Default value'),
'#maxlength' => NULL,
];
}
$form['default']['default_value']['#description'] = $this
->t('The default value of the webform element.');
if ($this
->hasProperty('multiple')) {
$form['default']['default_value']['#description'] .= ' ' . $this
->t('For multiple options, use commas to separate multiple defaults.');
}
$form['multiple'] = [
'#type' => 'details',
'#title' => $this
->t('Multiple settings'),
'#states' => [
'invisible' => [
':input[name="properties[multiple][container][cardinality]"]' => [
'!value' => -1,
],
':input[name="properties[multiple][container][cardinality_number]"]' => [
'value' => 1,
],
],
],
'#attributes' => [
'data-webform-states-no-clear' => TRUE,
],
];
$form['multiple']['multiple__header'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Display elements in table columns'),
'#description' => $this
->t("If checked, the composite sub-element titles will be displayed as the table header labels."),
'#return_value' => TRUE,
];
$form['multiple']['multiple__header_label'] = [
'#type' => 'textfield',
'#title' => $this
->t('Table header label'),
'#description' => $this
->t('This is used as the table header for this webform element when displaying multiple values.'),
];
$form['multiple']['multiple__item_label'] = [
'#type' => 'textfield',
'#title' => $this
->t('Item label'),
'#description' => $this
->t('This is used by the add/remove (+/-) icons.'),
];
$form['multiple']['multiple__no_items_message'] = [
'#type' => 'webform_html_editor',
'#title' => $this
->t('No items message'),
'#description' => $this
->t('This is used when there are no items entered.'),
];
$form['multiple']['multiple__min_items'] = [
'#type' => 'number',
'#title' => $this
->t('Minimum amount of items'),
'#description' => $this
->t('Minimum items defaults to 0 for optional elements and 1 for required elements.'),
'#min' => 0,
'#max' => 20,
];
$form['multiple']['multiple__empty_items'] = [
'#type' => 'number',
'#title' => $this
->t('Number of empty items'),
'#required' => TRUE,
'#min' => 0,
'#max' => 20,
];
$form['multiple']['multiple__sorting'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Allow users to sort elements'),
'#description' => $this
->t('If unchecked, the elements will no longer be sortable.'),
'#return_value' => TRUE,
];
$form['multiple']['multiple__operations'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Allow users to add/remove elements'),
'#description' => $this
->t('If unchecked, the add/remove (+/x) buttons will be removed from each table row.'),
'#return_value' => TRUE,
];
$form['multiple']['multiple__add'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Show add element button'),
'#description' => $this
->t('If unchecked, the add button will be removed from each table row.'),
'#return_value' => TRUE,
'#states' => [
'visible' => [
':input[name="properties[multiple__operations]"]' => [
'checked' => TRUE,
],
],
],
];
$form['multiple']['multiple__remove'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Show remove element button'),
'#description' => $this
->t('If unchecked, the remove button will be removed from each table row.'),
'#return_value' => TRUE,
'#states' => [
'visible' => [
':input[name="properties[multiple__operations]"]' => [
'checked' => TRUE,
],
],
],
];
$form['multiple']['multiple__add_more'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Allow users to add more items'),
'#description' => $this
->t('If checked, an add more input will be added below the multiple values.'),
'#return_value' => TRUE,
];
$form['multiple']['multiple__add_more_container'] = [
'#type' => 'container',
'#states' => [
'visible' => [
':input[name="properties[multiple__add_more]"]' => [
'checked' => TRUE,
],
],
],
];
$form['multiple']['multiple__add_more_container']['multiple__add_more_input'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Allow users to input the number of items to be added'),
'#description' => $this
->t('If checked, users will be able to input the number of items to be added.'),
'#return_value' => TRUE,
];
$form['multiple']['multiple__add_more_container']['multiple__add_more_button_label'] = [
'#type' => 'textfield',
'#title' => $this
->t('Add more button label'),
'#description' => $this
->t('This is used as the add more items button label for this webform element when displaying multiple values.'),
];
$form['multiple']['multiple__add_more_container']['multiple__add_more_input_label'] = [
'#type' => 'textfield',
'#title' => $this
->t('Add more input label'),
'#description' => $this
->t('This is used as the add more items input label for this webform element when displaying multiple values.'),
'#states' => [
'visible' => [
':input[name="properties[multiple__add_more_input]"]' => [
'checked' => TRUE,
],
],
],
];
$form['multiple']['multiple__add_more_container']['multiple__add_more_items'] = [
'#type' => 'number',
'#title' => $this
->t('Number of add more items'),
'#required' => TRUE,
'#min' => 1,
'#max' => 20,
];
$form['wrapper_attributes'] = [
'#type' => 'details',
'#title' => $this
->hasProperty('wrapper_type') ? $this
->t('Wrapper type and attributes') : $this
->t('Wrapper attributes'),
];
$form['wrapper_attributes']['wrapper_type'] = [
'#type' => 'select',
'#title' => $this
->t('Wrapper type'),
'#options' => [
'fieldset' => $this
->t('Fieldset'),
'form_element' => $this
->t('Form element'),
'container' => $this
->t('Container'),
],
'#description' => '<b>' . $this
->t('Fieldset') . ':</b> ' . $this
->t('Wraps inputs in a fieldset.') . ' <strong>' . $this
->t('Recommended') . '</strong>' . '<br/><br/><b>' . $this
->t('Form element') . ':</b> ' . $this
->t('Wraps inputs in a basic form element with title and description.') . '<br/><br/><b>' . $this
->t('Container') . ':</b> ' . $this
->t('Wraps inputs in a basic div with no title or description.'),
];
if ($this
->hasProperty('wrapper_type')) {
$form['element_description']['#states'] = [
'!visible' => [
':input[name="properties[wrapper_type]"]' => [
'value' => 'container',
],
],
];
$form['form']['display_container']['#states'] = [
'!visible' => [
':input[name="properties[wrapper_type]"]' => [
'value' => 'container',
],
],
];
$form['form']['field_container']['#states'] = [
'!visible' => [
':input[name="properties[wrapper_type]"]' => [
'value' => 'container',
],
],
];
}
$form['wrapper_attributes']['wrapper_attributes'] = [
'#type' => 'webform_element_attributes',
'#title' => $this
->t('Wrapper'),
'#class__description' => $this
->t("Apply classes to the element's wrapper around both the field and its label. Select 'custom…' to enter custom classes."),
'#style__description' => $this
->t("Apply custom styles to the element's wrapper around both the field and its label."),
'#attributes__description' => $this
->t("Enter additional attributes to be added to the element's wrapper."),
'#classes' => $this->configFactory
->get('webform.settings')
->get('element.wrapper_classes'),
];
$form['element_attributes'] = [
'#type' => 'details',
'#title' => $this
->t('Element attributes'),
];
$form['element_attributes']['attributes'] = [
'#type' => 'webform_element_attributes',
'#title' => $this
->t('Element'),
'#classes' => $this->configFactory
->get('webform.settings')
->get('element.classes'),
];
$form['label_attributes'] = [
'#type' => 'details',
'#title' => $this
->t('Label attributes'),
];
$form['label_attributes']['label_attributes'] = [
'#type' => 'webform_element_attributes',
'#title' => $this
->t('Label'),
'#class__description' => $this
->t("Apply classes to the element's label."),
'#style__description' => $this
->t("Apply custom styles to the element's label."),
'#attributes__description' => $this
->t("Enter additional attributes to be added to the element's label."),
];
if ($this
->hasProperty('wrapper_type')) {
$form['label_attributes']['#states'] = [
'visible' => [
':input[name="properties[wrapper_type]"]' => [
'value' => 'form_element',
],
],
];
}
$form['summary_attributes'] = [
'#type' => 'details',
'#title' => $this
->t('Summary attributes'),
];
$form['summary_attributes']['summary_attributes'] = [
'#type' => 'webform_element_attributes',
'#title' => $this
->t('Summary'),
'#class__description' => $this
->t("Apply classes to the details' summary around both the field and its label."),
'#style__description' => $this
->t("Apply custom styles to the details' summary."),
'#attributes__description' => $this
->t("Enter additional attributes to be added to the details' summary."),
];
$form['title_attributes'] = [
'#type' => 'details',
'#title' => $this
->t('Title attributes'),
];
$form['title_attributes']['title_attributes'] = [
'#type' => 'webform_element_attributes',
'#title' => $this
->t('Title'),
'#class__description' => $this
->t("Apply classes to the title tag."),
'#style__description' => $this
->t("Apply custom styles to the title tag."),
'#attributes__description' => $this
->t("Enter additional attributes to be added to the title tag."),
];
$has_edit_twig_access = WebformTwigExtension::hasEditTwigAccess();
$form['display'] = [
'#type' => 'details',
'#title' => $this
->t('Submission display'),
];
$form['display']['item'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Single item'),
];
$form['display']['item']['format'] = [
'#type' => 'select',
'#title' => $this
->t('Item format'),
'#description' => $this
->t('Select how a single value is displayed.'),
'#options' => WebformOptionsHelper::appendValueToText($this
->getItemFormats()),
];
$format = isset($element_properties['format']) ? $element_properties['format'] : NULL;
$format_custom = $has_edit_twig_access || $format === 'custom';
if ($format_custom) {
$form['display']['item']['format']['#options'] += [
'custom' => $this
->t('Custom…'),
];
}
$format_custom_states = [
'visible' => [
':input[name="properties[format]"]' => [
'value' => 'custom',
],
],
'required' => [
':input[name="properties[format]"]' => [
'value' => 'custom',
],
],
];
$form['display']['item']['format_html'] = [
'#type' => 'webform_codemirror',
'#mode' => 'twig',
'#title' => $this
->t('Item format custom HTML'),
'#description' => $this
->t('The HTML to display for a single element value. You may include HTML or <a href=":href">Twig</a>. You may enter data from the submission as per the "variables" below.', [
':href' => 'http://twig.sensiolabs.org/documentation',
]),
'#states' => $format_custom_states,
'#access' => $format_custom,
];
$form['display']['item']['format_text'] = [
'#type' => 'webform_codemirror',
'#mode' => 'twig',
'#title' => $this
->t('Item format custom Text'),
'#description' => $this
->t('The text to display for a single element value. You may include <a href=":href">Twig</a>. You may enter data from the submission as per the "variables" below.', [
':href' => 'http://twig.sensiolabs.org/documentation',
]),
'#states' => $format_custom_states,
'#access' => $format_custom,
];
if ($has_edit_twig_access) {
$twig_variables = $this instanceof ContainerBase ? [
'children' => '{{ children }}',
] : [
'value' => '{{ value }}',
];
if ($this instanceof WebformCompositeBase) {
$composite_elements = $this
->getCompositeElements();
foreach ($composite_elements as $composite_key => $composite_element) {
$twig_variables["element.{$composite_key}"] = "{{ element.{$composite_key} }}";
}
}
$formats = $this
->getItemFormats();
foreach ($formats as $format_name => $format) {
if (is_array($format)) {
foreach ($format as $sub_format_name => $sub_format) {
$twig_variables["item['{$sub_format_name}']"] = "{{ item['{$sub_format_name}'] }}";
}
}
else {
$twig_variables["item.{$format_name}"] = "{{ item.{$format_name} }}";
}
}
$form['display']['item']['twig'] = WebformTwigExtension::buildTwigHelp($twig_variables);
$form['display']['item']['twig']['#states'] = $format_custom_states;
WebformElementHelper::setPropertyRecursive($form['display']['item']['twig'], '#access', TRUE);
}
$form['display']['items'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Multiple items'),
'#states' => [
'visible' => [
[
':input[name="properties[multiple][container][cardinality]"]' => [
'value' => '-1',
],
],
'or',
[
':input[name="properties[multiple][container][cardinality_number]"]' => [
'!value' => 1,
],
],
],
],
];
$form['display']['items']['format_items'] = [
'#type' => 'select',
'#title' => $this
->t('Items format'),
'#description' => $this
->t('Select how multiple values are displayed.'),
'#options' => WebformOptionsHelper::appendValueToText($this
->getItemsFormats()),
];
$format_items = isset($element_properties['format_items']) ? $element_properties['format_items'] : NULL;
$format_items_custom = $has_edit_twig_access || $format_items === 'custom';
if ($format_items_custom) {
$form['display']['items']['format_items']['#options'] += [
'custom' => $this
->t('Custom…'),
];
}
$format_items_custom_states = [
'visible' => [
':input[name="properties[format_items]"]' => [
'value' => 'custom',
],
],
'required' => [
':input[name="properties[format_items]"]' => [
'value' => 'custom',
],
],
];
$form['display']['items']['format_items_html'] = [
'#type' => 'webform_codemirror',
'#mode' => 'twig',
'#title' => $this
->t('Items format custom HTML'),
'#description' => $this
->t('The HTML to display for multiple element values. You may include HTML or <a href=":href">Twig</a>. You may enter data from the submission as per the "variables" below.', [
':href' => 'http://twig.sensiolabs.org/documentation',
]),
'#states' => $format_items_custom_states,
'#access' => $format_items_custom,
];
$form['display']['items']['format_items_text'] = [
'#type' => 'webform_codemirror',
'#mode' => 'twig',
'#title' => $this
->t('Items format custom Text'),
'#description' => $this
->t('The text to display for multiple element values. You may include <a href=":href">Twig</a>. You may enter data from the submission as per the "variables" below.', [
':href' => 'http://twig.sensiolabs.org/documentation',
]),
'#states' => $format_items_custom_states,
'#access' => $format_items_custom,
];
if ($format_items_custom) {
$twig_variables = [
'{{ value }}',
'{{ items }}',
];
$form['display']['items']['twig'] = WebformTwigExtension::buildTwigHelp($twig_variables);
$form['display']['items']['twig']['#states'] = $format_items_custom_states;
WebformElementHelper::setPropertyRecursive($form['display']['items']['twig'], '#access', TRUE);
}
$form['display']['format_attributes'] = [
'#type' => 'details',
'#title' => $this
->t('Display wrapper attributes'),
];
$form['display']['format_attributes']['format_attributes'] = [
'#type' => 'webform_element_attributes',
'#title' => $this
->t('Display'),
'#class__description' => $this
->t("Apply classes to the element's display wrapper. Select 'custom…' to enter custom classes."),
'#style__description' => $this
->t("Apply custom styles to the element's display wrapper."),
'#attributes__description' => $this
->t("Enter additional attributes to be added to the element's display wrapper."),
'#classes' => $this->configFactory
->get('webform.settings')
->get('element.wrapper_classes'),
];
$form['admin'] = [
'#type' => 'details',
'#title' => $this
->t('Administration'),
];
$form['admin']['private'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Private'),
'#description' => $this
->t('Private elements are shown only to users with results access.'),
'#weight' => 50,
'#return_value' => TRUE,
];
$form['admin']['admin_title'] = [
'#type' => 'textfield',
'#title' => $this
->t('Admin title'),
'#description' => $this
->t('The admin title will always be displayed when managing elements and viewing & downloading submissions.') . '<br/>' . $this
->t("If an element's title is hidden, the element's admin title will be displayed when viewing a submission."),
];
$form['admin']['admin_notes'] = [
'#type' => 'webform_html_editor',
'#title' => $this
->t('Admin notes/comments'),
'#description' => $this
->t("Admin notes/comments are display next to the element title in the form builder and visible in the form's YAML source"),
];
$operations = [
'create' => [
'#title' => $this
->t('Create submission'),
'#description' => $this
->t('Select roles and users that should be able to populate this element when creating a new submission.'),
'#open' => TRUE,
],
'update' => [
'#title' => $this
->t('Update submission'),
'#description' => $this
->t('Select roles and users that should be able to update this element when updating an existing submission.'),
'#open' => FALSE,
],
'view' => [
'#title' => $this
->t('View submission'),
'#description' => $this
->t('Select roles and users that should be able to view this element when viewing a submission.'),
'#open' => FALSE,
],
];
$form['access'] = [
'#type' => 'container',
'#attributes' => [
'data-webform-states-no-clear' => TRUE,
],
];
if (!$this->currentUser
->hasPermission('administer webform') && !$this->currentUser
->hasPermission('administer webform element access')) {
$form['access']['#access'] = FALSE;
}
foreach ($operations as $operation => $operation_element) {
$form['access']['access_' . $operation] = $operation_element + [
'#type' => 'details',
'#states' => [
'visible' => [
':input[name="properties[access]"]' => [
'checked' => TRUE,
],
],
],
];
$form['access']['access_' . $operation]['access_' . $operation . '_roles'] = [
'#type' => 'webform_roles',
'#title' => $this
->t('Roles'),
];
$form['access']['access_' . $operation]['access_' . $operation . '_users'] = [
'#type' => 'webform_users',
'#title' => $this
->t('Users'),
];
$form['access']['access_' . $operation]['access_' . $operation . '_permissions'] = [
'#type' => 'webform_permissions',
'#title' => $this
->t('Permissions'),
'#multiple' => TRUE,
'#select2' => TRUE,
];
}
$form['access']['access'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Display element'),
'#description' => $this
->t('If unchecked, the element is never displayed. The element will only be visible within the form builder and hidden from all other displays including submission details, results, and download.'),
'#weight' => 50,
'#return_value' => TRUE,
];
if (!$form_object
->isNew() && $this
->hasProperty('multiple')) {
$element_key = $form_object
->getKey();
if ($this
->getSubmissionStorage()
->hasSubmissionValue($webform, $element_key)) {
$form['element']['multiple']['#disabled'] = TRUE;
$form['element']['multiple']['#description'] = '<em>' . $this
->t('There is data for this element in the database. This setting can no longer be changed.') . '</em>';
}
}
if (strpos($this->pluginId, 'password') !== FALSE && !$webform
->getSetting('results_disabled')) {
$form['element']['password_message'] = [
'#type' => 'webform_message',
'#message_type' => 'warning',
'#message_message' => $this
->t('Webform submissions store passwords as plain text.') . ' ' . $this
->t('<a href=":href">Encryption</a> should be enabled for this element.', [
':href' => 'https://www.drupal.org/project/webform_encrypt',
]),
'#access' => TRUE,
'#weight' => -100,
'#message_close' => TRUE,
'#message_storage' => WebformMessage::STORAGE_SESSION,
];
}
return $form;
}
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$element = $form_state
->get('element');
if (is_null($element)) {
throw new \Exception('Element must be defined in the $form_state.');
}
$default_properties = $this
->getDefaultProperties();
$element_properties = WebformArrayHelper::removePrefix($form_state
->get('element')) + $default_properties;
$form_state
->set('default_properties', $default_properties);
$form_state
->set('element_properties', $element_properties);
$form = $this
->form($form, $form_state);
\Drupal::moduleHandler()
->alter('webform_element_configuration_form', $form, $form_state);
$element_properties = $form_state
->get('element_properties');
$custom_properties = $element_properties;
$this
->setConfigurationFormDefaultValueRecursive($form, $custom_properties);
foreach ($form as &$element) {
if (is_array($element) && !isset($element['#weight']) && isset($element['#type']) && $element['#type'] === 'fieldset') {
$element['#weight'] = -20;
}
}
if (isset($custom_properties['type'])) {
$form['type'] = [
'#type' => 'value',
'#value' => $custom_properties['type'],
'#parents' => [
'properties',
'type',
],
];
unset($custom_properties['type']);
}
$form['custom'] = [
'#type' => 'details',
'#title' => $this
->t('Custom settings'),
'#open' => $custom_properties ? TRUE : FALSE,
'#access' => $this->currentUser
->hasPermission('edit webform source'),
];
if ($api_url = $this
->getPluginApiUrl()) {
$t_args = [
':href' => $api_url
->toString(),
'%label' => $this
->getPluginLabel(),
];
$form['custom']['#description'] = $this
->t('Read the %label element\'s <a href=":href">API documentation</a>.', $t_args);
}
$form['custom']['properties'] = [
'#type' => 'webform_codemirror',
'#mode' => 'yaml',
'#title' => $this
->t('Custom properties'),
'#description' => $this
->t('Properties do not have to be prepended with a hash (#) character, the hash character will be automatically added to the custom properties.') . '<br /><br />' . $this
->t('These properties and callbacks are not allowed: @properties', [
'@properties' => WebformArrayHelper::toString(WebformArrayHelper::addPrefix(WebformElementHelper::$ignoredProperties)),
]),
'#default_value' => $custom_properties,
'#parents' => [
'properties',
'custom',
],
];
$this->tokenManager
->elementValidate($form);
$form_state
->set('custom_properties', $custom_properties);
return $this
->buildConfigurationFormTabs($form, $form_state);
}
protected function getFormInlineContainer() {
$help_enabled = $this->configFactory
->get('webform.settings')
->get('ui.description_help');
return [
'#type' => 'container',
'#attributes' => $help_enabled ? [
'class' => [
'form--inline',
'clearfix',
'webform-ui-element-form-inline--input',
],
] : [],
];
}
protected function buildConfigurationFormTabs(array $form, FormStateInterface $form_state) {
$tabs = [
'conditions' => [
'title' => $this
->t('Conditions'),
'elements' => [
'conditional_logic',
],
'weight' => 10,
],
'advanced' => [
'title' => $this
->t('Advanced'),
'elements' => [
'default',
'multiple',
'wrapper_attributes',
'element_attributes',
'label_attributes',
'summary_attributes',
'title_attributes',
'display',
'admin',
'options_properties',
'custom',
],
'weight' => 20,
],
'access' => [
'title' => $this
->t('Access'),
'elements' => [
'access',
],
'weight' => 30,
],
];
return WebformFormHelper::buildTabs($form, $tabs, $form_state
->get('active_tab'));
}
protected function setConfigurationFormDefaultValueRecursive(array &$form, array &$element_properties) {
$has_input = FALSE;
foreach ($form as $property_name => &$property_element) {
if (WebformElementHelper::property($property_name)) {
continue;
}
if (!empty($property_element['#tree']) && $property_name === 'selection_settings') {
unset($element_properties[$property_name]);
$property_element['#parents'] = [
'properties',
$property_name,
];
$has_input = TRUE;
continue;
}
$is_input = $this->elementManager
->getElementInstance($property_element)
->isInput($property_element);
if ($is_input) {
if (array_key_exists($property_name, $element_properties)) {
$this
->setConfigurationFormDefaultValue($form, $element_properties, $property_element, $property_name);
$has_input = TRUE;
}
elseif (empty($form[$property_name]['#access'])) {
unset($form[$property_name]);
}
}
else {
$container_has_input = $this
->setConfigurationFormDefaultValueRecursive($property_element, $element_properties);
if ($container_has_input) {
$has_input = TRUE;
}
elseif (empty($form[$property_name]['#access'])) {
unset($form[$property_name]);
}
}
}
return $has_input;
}
protected function setConfigurationFormDefaultValue(array &$form, array &$element_properties, array &$property_element, $property_name) {
$default_value = $element_properties[$property_name];
$type = isset($property_element['#type']) ? $property_element['#type'] : NULL;
switch ($type) {
case 'entity_autocomplete':
$target_type = $property_element['#target_type'];
$target_storage = $this
->getEntityStorage($target_type);
if (!empty($property_element['#tags'])) {
$property_element['#default_value'] = $default_value ? $target_storage
->loadMultiple($default_value) : [];
}
else {
$property_element['#default_value'] = $default_value ? $target_storage
->load($default_value) : NULL;
}
break;
case 'radios':
case 'select':
if (!is_array($default_value) && isset($property_element['#options'])) {
$flattened_options = OptGroup::flattenOptions($property_element['#options']);
if (!isset($flattened_options[$default_value])) {
$default_value = NULL;
}
}
$property_element['#default_value'] = $default_value;
break;
default:
if (is_array($default_value) && $property_name === 'default_value' && !$this
->isComposite()) {
$property_element['#default_value'] = implode(', ', $default_value);
}
elseif (is_bool($default_value) && $property_name === 'default_value') {
$property_element['#default_value'] = $default_value ? 1 : 0;
}
elseif (is_null($default_value) && $property_name === 'default_value') {
$property_element['#default_value'] = (string) $default_value;
}
else {
$property_element['#default_value'] = $default_value;
}
break;
}
$property_element['#parents'] = [
'properties',
$property_name,
];
unset($element_properties[$property_name]);
}
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
$properties = $this
->getConfigurationFormProperties($form, $form_state);
$ignored_properties = WebformElementHelper::getIgnoredProperties($properties);
foreach ($ignored_properties as $ignored_property => $ignored_message) {
if ($ignored_property !== $ignored_message) {
unset($ignored_properties[$ignored_property]);
$form_state
->setErrorByName('custom', $ignored_message);
}
}
if ($ignored_properties) {
$t_args = [
'@properties' => WebformArrayHelper::toString($ignored_properties),
];
$form_state
->setErrorByName('custom', $this
->t('Element contains ignored/unsupported properties: @properties', $t_args));
}
}
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
}
public function getConfigurationFormProperties(array &$form, FormStateInterface $form_state) {
$element_properties = $form_state
->getValues();
$default_properties = $form_state
->get('default_properties');
if (isset($element_properties['custom'])) {
if (is_array($element_properties['custom'])) {
$element_properties += $element_properties['custom'];
}
unset($element_properties['custom']);
}
WebformArrayHelper::removePrefix($element_properties);
$element = WebformArrayHelper::addPrefix($element_properties);
foreach ($element_properties as $property_name => $property_value) {
if (!array_key_exists($property_name, $default_properties)) {
continue;
}
$this
->getConfigurationFormProperty($element_properties, $property_name, $property_value, $element);
switch ($property_name) {
case 'multiple':
if ($default_properties[$property_name] === $element_properties[$property_name]) {
unset($element_properties[$property_name]);
}
break;
default:
if ($default_properties[$property_name] == $element_properties[$property_name]) {
unset($element_properties[$property_name]);
}
if (isset($element_properties[$property_name])) {
if (is_bool($default_properties[$property_name])) {
$element_properties[$property_name] = (bool) $element_properties[$property_name];
}
elseif (is_null($default_properties[$property_name]) || is_numeric($default_properties[$property_name])) {
$value = $element_properties[$property_name];
$cast_value = $value == (int) $value ? (int) $value : (double) $value;
if ($value == $cast_value) {
$element_properties[$property_name] = $cast_value;
}
}
}
break;
}
}
if (isset($element_properties['type'])) {
$element_properties = [
'type' => $element_properties['type'],
] + $element_properties;
}
return WebformArrayHelper::addPrefix($element_properties);
}
protected function getConfigurationFormProperty(array &$properties, $property_name, $property_value, array $element) {
if ($property_name === 'default_value' && is_string($property_value) && $property_value && $this
->hasMultipleValues($element)) {
$properties[$property_name] = preg_split('/\\s*,\\s*/', $property_value);
}
}
protected function hasCompositeFormElementWrapper() {
$callbacks = $this->elementInfo
->getInfoProperty($this
->getPluginId(), '#pre_render') ?: [];
foreach ($callbacks as $callback) {
if (is_array($callback) && in_array($callback[1], [
'preRenderCompositeFormElement',
'preRenderWebformCompositeFormElement',
])) {
return TRUE;
}
}
return FALSE;
}
public static function trustedCallbacks() {
return [
'preRenderFixStatesWrapper',
'preRenderFixFlexboxWrapper',
'preRenderWebformCompositeFormElement',
];
}
}