View source
<?php
namespace Drupal\webform_ui;
use Drupal\Core\Entity\BundleEntityFormBase;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Render\Markup;
use Drupal\Core\Serialization\Yaml;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\webform\Element\WebformElementStates;
use Drupal\webform\Form\WebformEntityAjaxFormTrait;
use Drupal\webform\Plugin\WebformElement\WebformElement;
use Drupal\webform\Plugin\WebformElement\WebformTable;
use Drupal\webform\Utility\WebformDialogHelper;
use Drupal\webform\Utility\WebformElementHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;
class WebformUiEntityElementsForm extends BundleEntityFormBase {
use WebformEntityAjaxFormTrait;
protected $requiredStates = [
'required' => 'required',
'!required' => '!required',
'optional' => 'optional',
'!optional' => '!optional',
];
protected $renderer;
protected $elementInfo;
protected $elementManager;
protected $elementsValidator;
protected $tokenManager;
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
$instance->renderer = $container
->get('renderer');
$instance->elementInfo = $container
->get('plugin.manager.element_info');
$instance->elementManager = $container
->get('plugin.manager.webform.element');
$instance->elementsValidator = $container
->get('webform.elements_validator');
return $instance;
}
public function buildForm(array $form, FormStateInterface $form_state) {
$webform = $this
->getEntity();
$header = $this
->getTableHeader();
$elements = $this
->getOrderableElements();
$delta = count($elements);
$parent_options = $this
->getParentOptions($elements);
$rows = [];
foreach ($elements as $element) {
$rows[$element['#webform_key']] = $this
->getElementRow($element, $delta, $parent_options);
}
$form['webform_ui_elements'] = [
'#type' => 'table',
'#header' => $header,
'#empty' => $this
->t('Please add elements to this webform.'),
'#attributes' => [
'class' => [
'webform-ui-elements-table',
],
],
'#tabledrag' => [
[
'action' => 'match',
'relationship' => 'parent',
'group' => 'row-parent-key',
'source' => 'row-key',
'hidden' => TRUE,
'limit' => FALSE,
],
[
'action' => 'order',
'relationship' => 'sibling',
'group' => 'row-weight',
],
],
] + $rows;
if ($rows && !$webform
->hasActions()) {
$form['webform_ui_elements'] += [
'webform_actions_default' => $this
->getCustomizeActionsRow(),
];
}
WebformDialogHelper::attachLibraries($form);
$form['#attached']['library'][] = 'webform/webform.admin.tabledrag';
$form['#attached']['library'][] = 'webform_ui/webform_ui';
$form = parent::buildForm($form, $form_state);
return $this
->buildAjaxForm($form, $form_state);
}
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
$webform = $this
->getEntity();
$elements_flattened = $webform
->getElementsDecodedAndFlattened();
$webform_ui_elements = $form_state
->getValue('webform_ui_elements') ?: [];
uasort($webform_ui_elements, [
'Drupal\\Component\\Utility\\SortArray',
'sortByWeightElement',
]);
if (array_diff_key($webform_ui_elements, $elements_flattened)) {
$form_state
->setError($form['webform_ui_elements'], $this
->t('The elements have been unexpectedly altered. Please try again'));
}
foreach ($webform_ui_elements as $key => $table_element) {
if ($parent_key = $table_element['parent_key']) {
if (!isset($elements_flattened[$parent_key])) {
$form_state
->setError($form['webform_ui_elements'][$key]['parent']['parent_key'], $this
->t('Parent %parent_key does not exist.', [
'%parent_key' => $parent_key,
]));
continue;
}
$parent_keys = [
$key,
];
$current_parent_key = $parent_key;
while ($current_parent_key) {
if (in_array($current_parent_key, $parent_keys)) {
$form_state
->setError($form['webform_ui_elements'][$key]['parent']['parent_key'], $this
->t('Parent %parent_key key is not valid.', [
'%parent_key' => $parent_key,
]));
break;
}
$parent_keys[] = $current_parent_key;
$current_parent_key = isset($webform_ui_elements[$current_parent_key]['parent_key']) ? $webform_ui_elements[$current_parent_key]['parent_key'] : NULL;
}
}
$is_conditionally_required = isset($elements_flattened[$key]['#states']) && array_intersect_key($this->requiredStates, $elements_flattened[$key]['#states']);
if ($is_conditionally_required) {
unset($elements_flattened[$key]['#required']);
}
elseif (isset($webform_ui_elements[$key]['required'])) {
if (empty($webform_ui_elements[$key]['required'])) {
unset($elements_flattened[$key]['#required']);
}
else {
$elements_flattened[$key]['#required'] = TRUE;
}
}
$webform_ui_elements[$parent_key]['children'][$key] = $key;
}
if ($form_state
->hasAnyErrors()) {
return;
}
$elements_updated = [];
$elements_original = Yaml::decode($webform
->get('elements')) ?: [];
foreach ($elements_original as $key => $value) {
if (WebformElementHelper::property($key)) {
$elements_updated[$key] = $value;
}
}
$this
->buildUpdatedElementsRecursive($elements_updated, '', $webform_ui_elements, $elements_flattened);
$webform
->setUpdating()
->setElements($elements_updated);
$validate_options = [
'required' => TRUE,
'yaml' => FALSE,
'array' => FALSE,
'names' => FALSE,
'properties' => FALSE,
'submissions' => FALSE,
'hierarchy' => TRUE,
'rendering' => TRUE,
];
if ($this->elementsValidator
->validate($webform, $validate_options)) {
$form_state
->setErrorByName(NULL, $this
->t('There has been error validating the elements.'));
}
}
public function save(array $form, FormStateInterface $form_state) {
$webform = $this
->getEntity();
$webform
->save();
$context = [
'@label' => $webform
->label(),
'link' => $webform
->toLink($this
->t('Edit'), 'edit-form')
->toString(),
];
$t_args = [
'%label' => $webform
->label(),
];
$this
->logger('webform')
->notice('Webform @label elements saved.', $context);
$this
->messenger()
->addStatus($this
->t('Webform %label elements saved.', $t_args));
}
protected function actionsElement(array $form, FormStateInterface $form_state) {
$form = parent::actionsElement($form, $form_state);
$form['submit']['#value'] = $this
->t('Save elements');
unset($form['delete']);
return $form;
}
protected function buildUpdatedElementsRecursive(array &$elements, $key, array $webform_ui_elements, array $elements_flattened) {
if (!isset($webform_ui_elements[$key]['children'])) {
return;
}
foreach ($webform_ui_elements[$key]['children'] as $key) {
$elements[$key] = $elements_flattened[$key];
$this
->buildUpdatedElementsRecursive($elements[$key], $key, $webform_ui_elements, $elements_flattened);
}
}
protected function getOrderableElements() {
$webform = $this
->getEntity();
$elements = $webform
->getElementsInitializedAndFlattened();
$weights = [];
foreach ($elements as $element_key => &$element) {
$parent_key = $element['#webform_parent_key'];
if (!isset($weights[$parent_key])) {
$element['#weight'] = $weights[$parent_key] = 0;
}
else {
$element['#weight'] = ++$weights[$parent_key];
}
if (empty($element['#type'])) {
if (isset($element['#theme'])) {
$element['#type'] = $element['#theme'];
}
else {
$element['#type'] = '';
}
}
if (empty($element['#title'])) {
if (!empty($element['#markup'])) {
$element['#title'] = Markup::create(Unicode::truncate(strip_tags($element['#markup']), 100, TRUE, TRUE));
}
else {
$element['#title'] = '[' . $element_key . ']';
}
}
}
return $elements;
}
protected function getTableHeader() {
$webform = $this
->getEntity();
$header = [];
$header['title'] = $this
->t('Title');
if ($webform
->hasContainer()) {
$header['add'] = [
'data' => '',
'class' => [
'webform-ui-element-operations',
],
];
}
$header['key'] = [
'data' => $this
->t('Key'),
'class' => [
RESPONSIVE_PRIORITY_LOW,
],
];
$header['type'] = [
'data' => $this
->t('Type'),
'class' => [
RESPONSIVE_PRIORITY_LOW,
],
];
if ($webform
->hasFlexboxLayout()) {
$header['flex'] = [
'data' => $this
->t('Flex'),
'class' => [
RESPONSIVE_PRIORITY_LOW,
],
];
}
if ($webform
->hasConditions()) {
$header['conditions'] = [
'data' => $this
->t('Conditional'),
'class' => [
RESPONSIVE_PRIORITY_LOW,
],
];
}
$header['required'] = [
'data' => $this
->t('Required'),
'class' => [
'webform-ui-element-required',
RESPONSIVE_PRIORITY_LOW,
],
];
$header['weight'] = [
'data' => $this
->t('Weight'),
'class' => [
'webform-tabledrag-hide',
],
];
$header['parent'] = [
'data' => $this
->t('Parent'),
'class' => [
'webform-tabledrag-hide',
],
];
$header['operations'] = [
'data' => $this
->t('Operations'),
'class' => [
'webform-ui-element-operations',
],
];
return $header;
}
protected function getParentOptions(array $elements) {
$options = [];
foreach ($elements as $key => $element) {
$plugin_id = $this->elementManager
->getElementPluginId($element);
$webform_element = $this->elementManager
->createInstance($plugin_id);
if ($webform_element
->isContainer($element)) {
$options[$key] = $element['#admin_title'] ?: $element['#title'];
}
}
return $options;
}
protected function getElementRow(array $element, $delta, array $parent_options) {
$webform = $this
->getEntity();
$row = [];
$element_state_options = OptGroup::flattenOptions(WebformElementStates::getStateOptions());
$key = $element['#webform_key'];
$title = $element['#admin_title'] ?: $element['#title'];
$title = is_array($title) ? $this->renderer
->render($title) : $title;
$plugin_id = $this->elementManager
->getElementPluginId($element);
$webform_element = $this->elementManager
->createInstance($plugin_id);
$offcanvas_dialog_attributes = WebformDialogHelper::getOffCanvasDialogAttributes($webform_element
->getOffCanvasWidth());
$is_container = $webform_element
->isContainer($element);
$is_root = $webform_element
->isRoot();
$is_element_disabled = $webform_element
->isDisabled();
$is_access_disabled = !Element::isVisibleElement($element);
if ($is_element_disabled) {
$webform_element
->displayDisabledWarning($element);
}
$row_class = [
'draggable',
];
if ($is_root) {
$row_class[] = 'tabledrag-root';
$row_class[] = 'webform-ui-element-root';
}
if (!$is_container) {
$row_class[] = 'tabledrag-leaf';
}
if ($is_container) {
$row_class[] = 'webform-ui-element-container';
}
if (!empty($element['#type'])) {
$row_class[] = 'webform-ui-element-type-' . $element['#type'];
}
else {
$row_class[] = 'webform-ui-element-container';
}
if ($is_element_disabled || $is_access_disabled) {
$row_class[] = 'webform-ui-element-disabled';
}
$row['#attributes']['data-webform-key'] = $element['#webform_key'];
$row['#attributes']['data-webform-type'] = isset($element['#type']) ? $element['#type'] : '';
$row['#attributes']['class'] = $row_class;
$indentation = NULL;
if ($element['#webform_depth']) {
$indentation = [
'#theme' => 'indentation',
'#size' => $element['#webform_depth'],
];
}
$row['title'] = [
'link' => [
'#type' => 'link',
'#title' => $element['#admin_title'] ?: $element['#title'],
'#url' => new Url('entity.webform_ui.element.edit_form', [
'webform' => $webform
->id(),
'key' => $key,
]),
'#attributes' => $offcanvas_dialog_attributes,
'#prefix' => !empty($indentation) ? $this->renderer
->renderPlain($indentation) : '',
],
];
if (!empty($element['#admin_notes'])) {
$row['title']['notes'] = [
'#type' => 'webform_help',
'#help_title' => $element['#admin_title'] ?: $element['#title'],
'#help' => $element['#admin_notes'],
'#weight' => 100,
];
}
if ($webform
->hasContainer()) {
if ($is_container) {
$route_parameters = [
'webform' => $webform
->id(),
];
$route_options = [
'query' => [
'parent' => $key,
],
];
if ($webform_element instanceof WebformTable) {
$route_parameters['type'] = 'webform_table_row';
$row['add'] = [
'#type' => 'link',
'#title' => $this
->t('Add <span>row</span>'),
'#url' => new Url('entity.webform_ui.element.add_form', $route_parameters, $route_options),
'#attributes' => WebformDialogHelper::getOffCanvasDialogAttributes(WebformDialogHelper::DIALOG_NORMAL, [
'button',
'button-action',
'button--primary',
'button--small',
]),
];
}
else {
$row['add'] = [
'#type' => 'link',
'#title' => $this
->t('Add <span>element</span>'),
'#url' => new Url('entity.webform_ui.element', $route_parameters, $route_options),
'#attributes' => WebformDialogHelper::getModalDialogAttributes(WebformDialogHelper::DIALOG_NORMAL, [
'button',
'button-action',
'button--primary',
'button--small',
]),
];
}
}
else {
$row['add'] = [
'#markup' => '',
];
}
}
$row['name'] = [
'#markup' => $element['#webform_key'],
];
$type = $webform_element
->getPluginLabel();
if ($webform_element instanceof WebformElement) {
if (!empty($element['#type'])) {
$type = '[' . $element['#type'] . ']';
}
elseif (isset($element['#theme'])) {
$type = '[' . $element['#theme'] . ']';
}
}
$row['type'] = [
'#markup' => $type,
];
if ($webform
->hasFlexboxLayout()) {
$row['flex'] = [
'#markup' => empty($element['#flex']) ? 1 : $element['#flex'],
];
}
$is_conditionally_required = FALSE;
if ($webform
->hasConditions()) {
$states = [];
if (!empty($element['#states'])) {
$states = array_intersect_key($element_state_options, $element['#states']);
$is_conditionally_required = array_intersect_key($this->requiredStates, $element['#states']);
}
$row['conditional'] = [
'#type' => 'link',
'#title' => implode('; ', $states),
'#url' => new Url('entity.webform_ui.element.edit_form', [
'webform' => $webform
->id(),
'key' => $key,
]),
'#attributes' => $offcanvas_dialog_attributes + [
'data-hash' => 'webform-tab--conditions',
'title' => $this
->t('Edit @states conditional', [
'@states' => implode('; ', $states),
]),
'aria-label' => $this
->t('Edit @states conditional', [
'@states' => implode('; ', $states),
]),
],
];
}
if ($webform_element
->hasProperty('required')) {
$row['required'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Required for @title', [
'@title' => $title,
]),
'#title_display' => 'invisible',
'#default_value' => empty($element['#required']) ? FALSE : TRUE,
];
if ($is_conditionally_required) {
$row['required']['#default_value'] = TRUE;
$row['required']['#disabled'] = TRUE;
}
}
else {
$row['required'] = [
'#markup' => '',
];
}
$row['weight'] = [
'#type' => 'weight',
'#title' => $this
->t('Weight for @title', [
'@title' => $title,
]),
'#title_display' => 'invisible',
'#default_value' => $element['#weight'],
'#wrapper_attributes' => [
'class' => [
'webform-tabledrag-hide',
],
],
'#attributes' => [
'class' => [
'row-weight',
],
],
'#delta' => $delta,
];
$row['parent'] = [
'#wrapper_attributes' => [
'class' => [
'webform-tabledrag-hide',
],
],
];
$row['parent']['key'] = [
'#parents' => [
'webform_ui_elements',
$key,
'key',
],
'#type' => 'hidden',
'#value' => $key,
'#attributes' => [
'class' => [
'row-key',
],
],
];
if ($parent_options) {
$row['parent']['parent_key'] = [
'#parents' => [
'webform_ui_elements',
$key,
'parent_key',
],
'#type' => 'select',
'#options' => $parent_options,
'#empty_option' => '',
'#title' => $this
->t('Parent element @title', [
'@title' => $title,
]),
'#title_display' => 'invisible',
'#default_value' => $element['#webform_parent_key'],
'#attributes' => [
'class' => [
'row-parent-key',
],
],
];
}
else {
$row['parent']['parent_key'] = [
'#parents' => [
'webform_ui_elements',
$key,
'parent_key',
],
'#type' => 'hidden',
'#default_value' => '',
'#attributes' => [
'class' => [
'row-parent-key',
],
],
];
}
$row['operations'] = [
'#type' => 'operations',
'#prefix' => '<div class="webform-dropbutton">',
'#suffix' => '</div>',
];
$row['operations']['#links']['edit'] = [
'title' => $this
->t('Edit'),
'url' => new Url('entity.webform_ui.element.edit_form', [
'webform' => $webform
->id(),
'key' => $key,
]),
'attributes' => $offcanvas_dialog_attributes,
];
if ($webform_element
->getPluginId() === 'processed_text' && !WebformDialogHelper::useOffCanvas()) {
unset($row['operations']['#links']['edit']['attributes']);
}
if (!$is_container) {
$row['operations']['#links']['duplicate'] = [
'title' => $this
->t('Duplicate'),
'url' => new Url('entity.webform_ui.element.duplicate_form', [
'webform' => $webform
->id(),
'key' => $key,
]),
'attributes' => $offcanvas_dialog_attributes,
];
}
$row['operations']['#links']['delete'] = [
'title' => $this
->t('Delete'),
'url' => new Url('entity.webform_ui.element.delete_form', [
'webform' => $webform
->id(),
'key' => $key,
]),
'attributes' => WebformDialogHelper::getModalDialogAttributes(WebformDialogHelper::DIALOG_NARROW),
];
return $row;
}
protected function getCustomizeActionsRow() {
$webform = $this
->getEntity();
$row = [];
$row['#attributes']['class'] = [
'webform-ui-element-type-webform_actions',
];
$row['title'] = [
'#type' => 'link',
'#title' => $this
->t('Submit button(s)'),
'#url' => new Url('entity.webform_ui.element.add_form', [
'webform' => $webform
->id(),
'type' => 'webform_actions',
], [
'query' => [
'key' => 'actions',
],
]),
'#attributes' => WebformDialogHelper::getOffCanvasDialogAttributes(),
];
if ($webform
->hasContainer()) {
$row['add'] = [
'#markup' => '',
];
}
$row['name'] = [
'#markup' => 'actions',
];
$row['type'] = [
'#markup' => $this
->t('Submit button(s)'),
];
if ($webform
->hasFlexboxLayout()) {
$row['flex'] = [
'#markup' => 1,
];
}
if ($webform
->hasConditions()) {
$row['conditions'] = [
'#markup' => '',
];
}
$row['required'] = [
'#markup' => '',
];
$row['weight'] = [
'#markup' => '',
'#wrapper_attributes' => [
'class' => [
'webform-tabledrag-hide',
],
],
];
$row['parent'] = [
'#markup' => '',
'#wrapper_attributes' => [
'class' => [
'webform-tabledrag-hide',
],
],
];
if ($this->elementManager
->isExcluded('webform_actions')) {
$row['operations'] = [
'#markup' => '',
];
}
else {
$row['operations'] = [
'#type' => 'operations',
'#prefix' => '<div class="webform-dropbutton">',
'#suffix' => '</div>',
];
$row['operations']['#links']['customize'] = [
'title' => $this
->t('Customize'),
'url' => new Url('entity.webform_ui.element.add_form', [
'webform' => $webform
->id(),
'type' => 'webform_actions',
]),
'attributes' => WebformDialogHelper::getOffCanvasDialogAttributes(),
];
}
return $row;
}
}