Repository URL to install this package:
|
Version:
1.1.1 ▾
|
<?php
namespace Drupal\dds_sendgrid_custom_forms\Element;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\OptGroup;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\Serialization\Yaml;
/**
* Class ConditionalSendgridTemplate
*
* @package Drupal\dds_sendgrid_custom_forms\Element
*
* @FormElement("conditional_sendgrid_template")
*/
class ConditionalSendgridTemplate extends FormElement {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return [
'#input' => TRUE,
'#selector_options' => [],
'#empty_states' => 3,
'#process' => [
[$class, 'processConditionalElement'],
],
'#theme_wrappers' => ['form_element'],
'#multiple' => TRUE,
];
}
/**
* {@inheritdoc}
*/
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input === FALSE) {
if (isset($element['#default_value'])) {
if (is_string($element['#default_value'])) {
$default_value = Yaml::decode($element['#default_value']);
}
else {
$default_value = $element['#default_value'] ?: [];
}
return $default_value;
}
else {
return [];
}
}
elseif (is_array($input) && isset($input['templates'])) {
return is_string($input['templates']) ? Yaml::decode($input['templates']) : $input['templates'];
}
else {
return [];
}
}
/**
* @param $element
* @param \Drupal\Core\Form\FormStateInterface $form_state
* @param $complete_form
*
* @return array
*/
public static function processConditionalElement(&$element, FormStateInterface $form_state, &$complete_form) {
$element['#attached']['library'][] = 'dds_sendgrid_custom_forms/conditional-template-ui';
$element += [
'#trigger_options' => static::getTriggerOptions(),
];
$element['#selector_options_flattened'] = OptGroup::flattenOptions($element['#selector_options']);
$element['#tree'] = TRUE;
$table_id = implode('_', $element['#parents']) . '_wrapper';
$ajax_settings = [
'callback' => [static::class, 'ajaxCallback'],
'wrapper' => $table_id,
'progress' => ['type' => 'none'],
];
$element['add_template'] = [
'#type' => 'submit',
'#value' => t('Add template'),
'#limit_validation_errors' => [],
'#submit' => [[static::class, 'addSubmit']],
'#ajax' => $ajax_settings,
'#name' => $table_id . '_add_template',
'#action_type' => 'add-template',
'#attributes' => [
'class' => [
'add-template-button',
]
],
];
$storage_key = 'element_templates__' . $element['#name'] . '__number_of_rows';
if ($form_state->get($storage_key) === NULL) {
if (empty($element['#default_value']) || !is_array($element['#default_value'])) {
$number_of_rows = 1;
}
else {
$number_of_rows = count($element['#default_value']);
}
$form_state->set($storage_key, $number_of_rows);
}
$number_of_rows = $form_state->get($storage_key);
if ($form_state->isRebuilding()) {
$templates = $element['#value'];
}
else {
$templates = $element['#default_value'] ?? [];
}
// Build template rows.
$row_index = 0;
$rows = [];
foreach ($templates as $template_settings) {
$rows[$row_index] = self::buildTemplateRow($element, $template_settings, $table_id, $row_index, $form_state, $ajax_settings);
$row_index++;
}
// Generator empty template with conditions rows.
if ($row_index < $number_of_rows) {
while ($row_index < $number_of_rows) {
$rows[$row_index] = self::buildTemplateRow($element, [], $table_id, $row_index, $form_state, $ajax_settings);
$row_index++;
}
}
// Add wrapper to the element.
$element += ['#prefix' => '', '#suffix' => ''];
$element['#prefix'] = '<div id="' . $table_id . '" class="element-templates">' . $element['#prefix'];
$element['#suffix'] .= '</div>';
// Build table.
$element['templates'] = [
'#type' => 'container',
'#attributes' => ['class' => ['templates-container']],
] + $rows;
return $element;
}
/**
* Build template row.
*
* @param array $element
* The element.
* @param array $template
* The template.
* @param string $table_id
* The element's table id.
* @param int $row_index
* The row index.
* @param array $ajax_settings
* An array containing Ajax callback settings.
*
* @return array
* A render array containing a template table row.
*/
protected static function buildTemplateRow(array $element, array $template, $table_id, $row_index, FormStateInterface $form_state, array $ajax_settings) {
$template += ['template' => '', 'operator' => 'and', 'condition' => []];
$element_name = $element['#name'];
$template_selector = ":input[name=\"{$element_name}[templates][{$row_index}][template]\"]";
$row = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => [
'class' => ['element-templates--template'],
],
];
$row['template'] = [
'#type' => 'sendgrid_template_select',
'#title' => t('Template'),
'#title_display' => 'invisible',
'#placeholder' => t('Template'),
'#default_value' => $template['template'] ?? '',
'#empty_option' => '- ' . t('Select template') . ' -',
'#parents' => [$element_name, 'templates', $row_index , 'template'],
'#wrapper_attributes' => ['class' => ['element-templates--template--template']],
'#field_prefix' => t('Use'),
'#error_no_message' => TRUE,
];
if ($element['#multiple']) {
$row['operations'] = self::buildOperations($table_id, $row_index, $ajax_settings);
} else {
$row['operations'] = [];
}
$row['break'] = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => [
'class' => ['element-templates--template--break'],
]
];
$row['operator'] = [
'#type' => 'select',
'#title' => t('Operator'),
'#title_display' => 'invisible',
'#options' => [
'and' => t('All'),
'or' => t('Any'),
'xor' => t('One'),
],
'#attributes' => [
'title' => t('The operator used when validating multiple conditions.'),
],
'#default_value' => $template['operator'] ?? 'and',
'#parents' => [$element_name, 'templates', $row_index , 'operator'],
'#field_prefix' => t('If'),
'#field_suffix' => t('of the following is met:'),
'#wrapper_attributes' => ['class' => ['element-templates--template--operator']],
'#error_no_message' => TRUE,
];
$row['add_condition'] = [
'#type' => 'submit',
'#value' => t('Add condition'),
'#limit_validation_errors' => [],
'#submit' => [[static::class, 'addSubmit']],
'#ajax' => $ajax_settings,
'#name' => $table_id . '_template_'. $row_index . '_add_condition',
'#action_type' => 'add-condition',
'#row_index' => $row_index,
'#attributes' => [
'class' => [
'add-condition-button',
]
],
];
// Conditions
$storage_key = 'element_templates__' . $element['#name'] . '__template_' . $row_index . '__conditions__number_of_rows';
if ($form_state->get($storage_key) === NULL) {
if (empty($template['condition']) || !is_array($template['condition'])) {
$number_of_rows = 1;
}
else {
$number_of_rows = count($template['condition']);
}
$form_state->set($storage_key, $number_of_rows);
}
$number_of_rows = $form_state->get($storage_key);
$condition_count = 0;
foreach ($template['condition'] as $condition) {
$row[$condition_count] = self::buildConditionRow($element, $condition, $table_id, $row_index, $condition_count, $ajax_settings);
$condition_count++;
}
// Generator empty template with conditions rows.
if ($condition_count < $number_of_rows) {
while ($condition_count < $number_of_rows) {
$row[$condition_count] = self::buildConditionRow($element, [], $table_id, $row_index, $condition_count, $ajax_settings);
$condition_count++;
}
}
return $row;
}
/**
* Build condition row.
*
* @param array $element
* The element.
* @param array $condition
* The condition.
* @param string $table_id
* The element's table id.
* @param $template_index
* The template index.
* @param int $row_index
* The row index.
* @param array $ajax_settings
* An array containing Ajax callback settings.
*
* @return array
* A render array containing a condition table row.
*/
protected static function buildConditionRow(array $element, array $condition, $table_id, $template_index, $row_index, array $ajax_settings) {
$element_name = $element['#name'];
$row['condition'] = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => [
'class' => ['element-template-conditions--condition'],
],
];
$trigger_selector = ":input[name=\"{$element_name}[templates][{$template_index}][condition][{$row_index}][trigger]\"]";
$row['condition']['selector'] = [
'#type' => 'select',
'#title' => t('Selector'),
'#title_display' => 'invisible',
'#options' => $element['#selector_options'],
'#wrapper_attributes' => ['class' => ['element-template-conditions--condition--selector']],
'#default_value' => $condition['selector'] ?? '',
'#empty_option' => t('- Select field -'),
'#parents' => [$element_name, 'templates', $template_index, 'condition', $row_index , 'selector'],
'#error_no_message' => TRUE,
'#attributes' => [
'title' => t('The field or group to check the condition against.')
],
];
if (!empty($condition['selector']) && !isset($element['#selector_options_flattened'][$condition['selector']])) {
$row['condition']['selector']['#options'][$condition['selector']] = $condition['selector'];
}
$row['condition']['trigger'] = [
'#type' => 'select',
'#title' => t('Trigger'),
'#title_display' => 'invisible',
'#options' => $element['#trigger_options'],
'#default_value' => $condition['trigger'] ?? '',
'#empty_option' => t('- Select condition -'),
'#parents' => [$element_name, 'templates', $template_index, 'condition', $row_index , 'trigger'],
'#wrapper_attributes' => ['class' => ['element-template-conditions--condition--trigger']],
'#error_no_message' => TRUE,
'#attributes' => [
'title' => t('The condition the selected field or group must meet.')
],
];
$row['condition']['value'] = [
'#type' => 'textfield',
'#title' => t('Value'),
'#title_display' => 'invisible',
'#size' => 25,
'#default_value' => $condition['value'] ?? '',
'#placeholder' => t('Enter value…'),
'#states' => [
'visible' => [
[$trigger_selector => ['value' => 'value']],
'or',
[$trigger_selector => ['value' => '!value']],
'or',
[$trigger_selector => ['value' => 'pattern']],
'or',
[$trigger_selector => ['value' => '!pattern']],
'or',
[$trigger_selector => ['value' => 'greater']],
'or',
[$trigger_selector => ['value' => 'less']],
],
],
'#wrapper_attributes' => ['class' => ['element-template-conditions--condition--value']],
'#parents' => [$element_name, 'templates', $template_index, 'condition', $row_index , 'value'],
'#error_no_message' => TRUE,
];
if ($element['#multiple']) {
$row['condition']['operations'] = self::buildConditionOperations($table_id, $template_index, $row_index, $ajax_settings);
}
return $row;
}
/**
* Build a template's operations.
*
* @param string $table_id
* The option element's table id.
* @param int $row_index
* The option's row index.
* @param array $ajax_settings
* An array containing Ajax callback settings.
*
* @return array
* A render array containing template operations.
*/
protected static function buildOperations($table_id, $row_index, array $ajax_settings) {
$operations = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => ['class' => ['element-templates--template--operations']],
];
$operations['remove'] = [
'#type' => 'image_button',
'#title' => t('Remove this template.'),
'#src' => drupal_get_path('module', 'dds_sendgrid_custom_forms') . '/images/ffffff/minus.svg',
'#limit_validation_errors' => [],
'#submit' => [[static::class, 'removeTemplateSubmit']],
'#attributes' => ['class' => ['element-templates--template--operation', 'element-templates--template--operation-remove']],
'#ajax' => $ajax_settings,
'#row_index' => $row_index,
'#action_type' => 'remove-template',
'#name' => $table_id . '_remove_' . $row_index,
];
return $operations;
}
/**
* Build a template's operations.
*
* @param string $table_id
* The option element's table id.
* @param int $row_index
* The option's row index.
* @param array $ajax_settings
* An array containing Ajax callback settings.
*
* @return array
* A render array containing template operations.
*/
protected static function buildConditionOperations($table_id, $row_index, $condition_index, array $ajax_settings) {
$operations = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => ['class' => ['element-template-conditions--condition--operations']],
];
$operations['remove'] = [
'#type' => 'image_button',
'#title' => t('Remove this condition.'),
'#src' => drupal_get_path('module', 'dds_sendgrid_custom_forms') . '/images/ffffff/minus.svg',
'#limit_validation_errors' => [],
'#submit' => [[static::class, 'removeConditionSubmit']],
'#attributes' => ['class' => ['element-template-conditions--condition--operation', 'element-template-conditions--condition--operation-remove']],
'#ajax' => $ajax_settings,
'#row_index' => $row_index,
'#action_type' => 'remove-condition',
'#condition_index' => $condition_index,
'#name' => $table_id . '_remove_' . $row_index . '_remove_condition_' . $condition_index,
];
return $operations;
}
/**
* Form submission handler for adding another template.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public static function addSubmit(array &$form, FormStateInterface $form_state) {
// Get the templates element by going up one level.
$button = $form_state->getTriggeringElement();
if ($button['#action_type'] === 'add-condition') {
$parent_length = -3;
} else if ($button['#action_type'] === 'add-template') {
$parent_length = -1;
}
$element =& NestedArray::getValue($form, array_slice($button['#array_parents'], 0, $parent_length));
// The $row_index is not sequential so we need to rebuild the value instead
// of just using an array_slice().
$row_index = $button['#row_index'];
$condition_index = $button['#condition_index'];
$values = [];
if ($button['#action_type'] === 'add-condition') {
foreach ($element['#value'] as $index => $value) {
if ($index == $row_index) {
$value['condition'][] = ['selector' => '', 'trigger' => ''];
}
$values[] = $value;
}
} else if ($button['#action_type'] === 'add-template') {
foreach ($element['#value'] as $index => $value) {
$values[] = $value;
}
$values[] = ['template' => '', 'operator' => 'and', 'condition' => [['selector' => '', 'trigger' => '', 'value' => '']]];
}
// Reset values.
$values = array_values($values);
// Set values.
$form_state->setValueForElement($element['templates'], $values);
NestedArray::setValue($form_state->getUserInput(), $element['templates']['#parents'], $values);
if ($button['#action_type'] === 'add-condition') {
foreach ($values as $index => $value) {
$form_state->set('element_templates__' . $element['#name'] . '__template_' . $index . '__conditions__number_of_rows', count($value['condition']));
}
} else {
// Update the number of rows.
$form_state->set('element_templates__' . $element['#name'] . '__number_of_rows', count($values));
}
// Rebuild the form.
$form_state->setRebuild();
}
/**
* Form submission handler for removing a template.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public static function removeTemplateSubmit(array &$form, FormStateInterface $form_state) {
$button = $form_state->getTriggeringElement();
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -4));
$row_index = $button['#row_index'];
$values = $element['#value'];
if (isset($values[$row_index])) {
// Remove template.
do {
unset($values[$row_index]);
$row_index++;
} while (isset($values[$row_index]) && !isset($values[$row_index]));
}
// Reset values.
$values = array_values($values);
// Set values.
$form_state->setValueForElement($element['templates'], $values);
NestedArray::setValue($form_state->getUserInput(), $element['templates']['#parents'], $values);
// Update the number of rows.
$form_state->set('element_templates__' . $element['#name'] . '__number_of_rows', count($values));
// Rebuild the form.
$form_state->setRebuild();
}
/**
* Form submission handler for removing a condition.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public static function removeConditionSubmit(array &$form, FormStateInterface $form_state) {
$button = $form_state->getTriggeringElement();
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -6));
$row_index = $button['#row_index'];
$condition_index = $button['#condition_index'];
$values = $element['#value'];
if (isset($values[$row_index]['condition'][$condition_index])) {
// Remove condition.
do {
unset($values[$row_index]['condition'][$condition_index]);
$condition_index++;
} while (isset($values[$row_index]['condition'][$condition_index]) && !isset($values[$row_index]['condition'][$condition_index]));
}
// Reset values.
$values[$row_index]['condition'] = array_values($values[$row_index]['condition']);
// Set values.
$form_state->setValueForElement($element['templates'], $values);
NestedArray::setValue($form_state->getUserInput(), $element['templates']['#parents'], $values);
// Update the number of rows.
$form_state->set('element_templates__' . $element['#name'] . '__template_' . $row_index . '__conditions__number_of_rows', count($values[$row_index]['condition']));
// Rebuild the form.
$form_state->setRebuild();
}
/**
* Ajax callback the returns the templates table.
*/
public static function ajaxCallback(array &$form, FormStateInterface $form_state) {
$button = $form_state->getTriggeringElement();
$parent_length = 0;
if ($button['#action_type'] === 'add-condition') {
$parent_length = -3;
} else if ($button['#action_type'] === 'remove-condition') {
$parent_length = -6;
} else if ($button['#action_type'] === 'add-template') {
$parent_length = -1;
} else if ($button['#action_type'] === 'remove-template') {
$parent_length = -4;
}
$element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, $parent_length));
return $element;
}
/**
* Get an associative array of translated trigger options.
*
* @return array
* An associative array of translated trigger options.
*/
public static function getTriggerOptions() {
return [
'empty' => t('Empty'),
'filled' => t('Filled'),
'checked' => t('Checked'),
'unchecked' => t('Unchecked'),
'value' => t('Value is'),
'!value' => t('Value is not'),
'pattern' => t('Pattern'),
'!pattern' => t('Not Pattern'),
'less' => t('Less than'),
'greater' => t('Greater than'),
];
}
}