You are here

public static function WebformElementStates::processWebformStates in Webform 6.x

Same name and namespace in other branches
  1. 8.5 src/Element/WebformElementStates.php \Drupal\webform\Element\WebformElementStates::processWebformStates()

Expand an email confirm field into two HTML5 email elements.

File

src/Element/WebformElementStates.php, line 70

Class

WebformElementStates
Provides a webform element to edit an element's #states.

Namespace

Drupal\webform\Element

Code

public static function processWebformStates(&$element, FormStateInterface $form_state, &$complete_form) {

  // Define default #state_options and #trigger_options.
  // There are also defined by \Drupal\webform\Plugin\WebformElementBase::form.
  $element += [
    '#state_options' => static::getStateOptions(),
    '#trigger_options' => static::getTriggerOptions(),
  ];
  $element['#state_options_flattened'] = OptGroup::flattenOptions($element['#state_options']);
  $element['#selector_options_flattened'] = OptGroup::flattenOptions($element['#selector_options']);
  $element['#tree'] = TRUE;
  $edit_source = $form_state
    ->get(static::getStorageKey($element, 'edit_source'));

  // Add validate callback that extracts the associative array of states.
  $element += [
    '#element_validate' => [],
  ];
  array_unshift($element['#element_validate'], [
    get_called_class(),
    'validateWebformElementStates',
  ]);

  // For customized #states display a CodeMirror YAML editor.
  $warning_message = static::isDefaultValueCustomizedFormApiStates($element);
  if ($warning_message || $edit_source) {
    if ($warning_message) {
      $warning_message .= ' ' . t('Form API #states must be manually entered.');
      $element['warning_messages'] = [
        '#type' => 'webform_message',
        '#message_type' => 'warning',
        '#message_message' => $warning_message,
      ];
    }
    if ($edit_source) {
      $element['edit_source_message'] = [
        '#type' => 'webform_message',
        '#message_message' => t('Creating custom conditional logic (Form API #states) with nested conditions or custom selectors will disable the conditional logic builder. This will require that Form API #states be manually entered.'),
        '#message_type' => 'info',
        '#message_close' => TRUE,
        '#message_storage' => WebformMessage::STORAGE_SESSION,
      ];
    }
    $element['states'] = [
      '#type' => 'webform_codemirror',
      '#title' => t('Conditional Logic (YAML)'),
      '#title_display' => 'invisible',
      '#mode' => 'yaml',
      '#default_value' => WebformYaml::encode($element['#default_value']),
      '#description' => t('Learn more about Drupal\'s <a href=":href">Form API #states</a>.', [
        ':href' => 'https://www.lullabot.com/articles/form-api-states',
      ]),
      '#webform_element' => TRUE,
      '#more_title' => t('Help'),
      '#more' => static::buildSourceHelp($element),
    ];
    return $element;
  }
  $table_id = implode('_', $element['#parents']) . '_table';

  // Store the number of rows.
  $storage_key = static::getStorageKey($element, 'number_of_rows');
  if ($form_state
    ->get($storage_key) === NULL) {
    if (empty($element['#default_value']) || !is_array($element['#default_value'])) {
      $number_of_rows = 2;
    }
    else {
      $number_of_rows = count($element['#default_value']);
    }
    $form_state
      ->set($storage_key, $number_of_rows);
  }
  $number_of_rows = $form_state
    ->get($storage_key);

  // DEBUG: Disable Ajax callback by commenting out the below callback and
  // wrapper.
  $ajax_settings = [
    'callback' => [
      get_called_class(),
      'ajaxCallback',
    ],
    'wrapper' => $table_id,
    'progress' => [
      'type' => 'none',
    ],
  ];

  // Build header.
  $header = [
    [
      'data' => t('State'),
      'width' => '25%',
    ],
    [
      'data' => t('Element'),
      'width' => '50%',
    ],
    [
      'data' => t('Trigger/Value'),
      'width' => '25%',
    ],
    [
      'data' => WebformAccessibilityHelper::buildVisuallyHidden(t('Operations')),
    ],
  ];

  // Get states and number of rows.
  if ($form_state
    ->isRebuilding()) {
    $states = $element['#value'];
  }
  else {
    $states = isset($element['#default_value']) ? static::convertFormApiStatesToStatesArray($element['#default_value']) : [];
  }

  // Track state row indexes for disable/enabled warning message.
  $state_row_indexes = [];

  // Build state and conditions rows.
  $row_index = 0;
  $rows = [];
  foreach ($states as $state_settings) {
    $rows[$row_index] = static::buildStateRow($element, $state_settings, $table_id, $row_index, $ajax_settings);
    $state_row_indexes[] = $row_index;
    $row_index++;
    foreach ($state_settings['conditions'] as $condition) {
      $rows[$row_index] = static::buildConditionRow($element, $condition, $table_id, $row_index, $ajax_settings);
      $row_index++;
    }
  }

  // Generator empty state with conditions rows.
  if ($row_index < $number_of_rows) {
    $rows[$row_index] = static::buildStateRow($element, [], $table_id, $row_index, $ajax_settings);
    $state_row_indexes[] = $row_index;
    $row_index++;
    while ($row_index < $number_of_rows) {
      $rows[$row_index] = static::buildConditionRow($element, [], $table_id, $row_index, $ajax_settings);
      $row_index++;
    }
  }

  // Add wrapper to the element.
  $element += [
    '#prefix' => '',
    '#suffix' => '',
  ];
  $element['#prefix'] = '<div id="' . $table_id . '">' . $element['#prefix'];
  $element['#suffix'] .= '</div>';

  // Build table.
  $element['states'] = [
    '#type' => 'table',
    '#header' => $header,
    '#attributes' => [
      'class' => [
        'webform-states-table',
      ],
    ],
  ] + $rows;
  $element['actions'] = [
    '#type' => 'container',
  ];

  // Build add state action.
  if ($element['#multiple']) {
    $element['actions']['add'] = [
      '#type' => 'submit',
      '#value' => t('Add another state'),
      '#limit_validation_errors' => [],
      '#submit' => [
        [
          get_called_class(),
          'addStateSubmit',
        ],
      ],
      '#ajax' => $ajax_settings,
      '#name' => $table_id . '_add',
    ];
  }

  // Edit source.
  if (\Drupal::currentUser()
    ->hasPermission('edit webform source')) {
    $element['actions']['source'] = [
      '#type' => 'submit',
      '#value' => t('Edit source'),
      '#limit_validation_errors' => [],
      '#submit' => [
        [
          get_called_class(),
          'editSourceSubmit',
        ],
      ],
      '#ajax' => $ajax_settings,
      '#attributes' => [
        'class' => [
          'button',
          'button--danger',
        ],
      ],
      '#name' => $table_id . '_source',
    ];
  }

  // Display a warning message when any state is set to disabled or enabled.
  if (!empty($element['#disabled_message'])) {
    $total_state_row_indexes = count($state_row_indexes);
    $triggers = [];
    foreach ($state_row_indexes as $index => $row_index) {
      $id = Html::getId('edit-' . implode('-', $element['#parents']) . '-states-' . $row_index . '-state');
      $triggers[] = [
        ':input[data-drupal-selector="' . $id . '"]' => [
          'value' => [
            'pattern' => '^(disabled|enabled)$',
          ],
        ],
      ];
      if ($index + 1 < $total_state_row_indexes) {
        $triggers[] = 'or';
      }
    }
    if ($triggers) {
      $element['disabled_message'] = [
        '#type' => 'webform_message',
        '#message_message' => t('<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-disabled">Disabled</a> elements do not submit data back to the server and the element\'s server-side default or current value will be preserved and saved to the database.'),
        '#message_type' => 'warning',
        '#states' => [
          'visible' => $triggers,
        ],
      ];
    }
  }
  $element['#attached']['library'][] = 'webform/webform.element.states';

  // Convert #options to jQuery autocomplete source format.
  // @see http://api.jqueryui.com/autocomplete/#option-source
  $selectors = [];
  $sources = [];
  if ($element['#selector_sources']) {
    foreach ($element['#selector_sources'] as $selector => $values) {
      $sources_key = Crypt::hashBase64(serialize($values));
      $selectors[$selector] = $sources_key;
      if (!isset($sources[$sources_key])) {
        foreach ($values as $key => $value) {
          $sources[$sources_key][] = [
            'label' => (string) $value . ($value !== $key ? ' (' . $key . ')' : ''),
            'value' => (string) $key,
          ];
        }
      }
    }
  }
  $element['#attached']['drupalSettings']['webformElementStates'] = [
    'selectors' => $selectors,
    'sources' => $sources,
  ];
  return $element;
}