Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
novicell/dds_sendgrid / modules / custom_forms / src / Element / ConditionalSendgridTemplate.php
Size: Mime:
<?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'),
    ];
  }
}