Commit 45cd9007 authored by Jacob Rockowitz's avatar Jacob Rockowitz Committed by Jacob Rockowitz
Browse files

Issue #2932117 by jrockowitz: Regular Expressions for Conditionals logic

parent 8005c80f
Loading
Loading
Loading
Loading
+31 −0
Changes for js/webform.states.js: 31 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -20,6 +20,37 @@
    return (typeof this.data(data) !== 'undefined');
  };

  /**
   * Adds pattern, less than, and greater than support to #state API.
   *
   * @type {Drupal~behavior}
   *
   * @see http://drupalsun.com/julia-evans/2012/03/09/extending-form-api-states-regular-expressions
   */
  Drupal.behaviors.webformStatesComparisions = {
    attach: function (context, settings) {
      if (Drupal.states) {
        Drupal.states.Dependent.comparisons.Object = function (reference, value) {
          if ('pattern' in reference) {
            return (new RegExp(reference.pattern)).test(value);
          }
          else if ('!pattern' in reference) {
            return !((new RegExp(reference['!pattern'])).test(value));
          }
          else if ('less' in reference) {
            return (value !== '' && reference.less > value);
          }
          else if ('greater' in reference) {
            return (value !== '' && reference.greater < value);
          }
          else {
            return reference.indexOf(value) !== false;
          }
        }
      }
    }
  };

  var $document = $(document);

  // Issue #2860529: Conditional required File upload field don't work.
+87 −23
Changes for src/Element/WebformElementStates.php: 87 added lines, 23 removed lines.
Original line number Diff line number Diff line
@@ -297,11 +297,31 @@ class WebformElementStates extends FormElement {
          [$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' => ['webform-states-table--value']],
      '#parents' => [$element_name, 'states', $row_index , 'value'],
    ];
    $row['condition']['pattern'] = [
      '#type' => 'container',
      'description' => ['#markup' => t('Enter a <a href=":href">regular expression</a>')],
      '#states' => [
        'visible' => [
          [$trigger_selector => ['value' => 'pattern']],
          'or',
          [$trigger_selector => ['value' => '!pattern']],
        ],
      ],
    ];

    $row['operations'] = static::buildOperations($table_id, $row_index, $ajax_settings);
    return $row;
  }
@@ -532,22 +552,14 @@ class WebformElementStates extends FormElement {

      foreach ($conditions as $condition_key => $condition_value) {
        if (is_string($condition_key)) {
          $states[$index]['conditions'][] = [
            'selector' => $condition_key,
            'trigger' => key($condition_value),
            'value' => reset($condition_value),
          ];
          $states[$index]['conditions'][] = static::getStatesArrayCondition( $condition_key, $condition_value);
        }
        elseif (is_string($condition_value)) {
          $states[$index]['operator'] = $condition_value;
        }
        else {
          foreach ($condition_value as $subcondition_key => $subcondition_value) {
            $states[$index]['conditions'][] = [
              'selector' => $subcondition_key,
              'trigger' => key($subcondition_value),
              'value' => reset($subcondition_value),
            ];
            $states[$index]['conditions'][] = static::getStatesArrayCondition($subcondition_key, $subcondition_value);
          }
        }
      }
@@ -556,6 +568,26 @@ class WebformElementStates extends FormElement {
    return $states;
  }

  /**
   * Get states array condition.
   *
   * @param string $selector
   *   The selector
   * @param array $condition
   *   The condition.
   *
   * @return array
   *   Associative array container selector, trigger, and value.
   */
  protected static function getStatesArrayCondition($selector, array $condition) {
    $trigger = key($condition);
    $value = reset($condition);
    if (is_array($value)) {
      return static::getStatesArrayCondition($selector, $value);
    }
    return ['selector' => $selector, 'trigger' => $trigger, 'value' => $value];
  }

  /**
   * Convert states array to Form API #states.
   *
@@ -568,26 +600,21 @@ class WebformElementStates extends FormElement {
  protected static function convertStatesArrayToFormApiStates(array $states_array = []) {
    $states = [];
    foreach ($states_array as $state_array) {
      if ($state = $state_array['state']) {
      $state = $state_array['state'];
      if (!$state) {
        continue;
      }

      $operator = $state_array['operator'];
      $conditions = $state_array['conditions'];
      if (count($conditions) === 1) {
        $condition = reset($conditions);
          $selector = $condition['selector'];
          $trigger = $condition['trigger'];
          if ($selector && $trigger) {
            $value = (in_array($trigger, ['value', '!value'])) ? $condition['value'] : TRUE;
          }
          else {
            $value = '';
          }
        extract(static::getFormApiStatesCondition($condition));
        $states[$state][$selector][$trigger] = $value;
      }
      else {
        foreach ($state_array['conditions'] as $index => $condition) {
            $selector = $condition['selector'];
            $trigger = $condition['trigger'];
            $value = (in_array($trigger, ['value', '!value'])) ? $condition['value'] : TRUE;
          extract(static::getFormApiStatesCondition($condition));
          if ($selector && $trigger) {
            if ($operator == 'or' || $operator == 'xor') {
              if ($index !== 0) {
@@ -608,10 +635,43 @@ class WebformElementStates extends FormElement {
        }
      }
    }
    }
    return $states;
  }

  /**
   * Get FAPI states array condition.
   *
   * @param array $condition
   *   The condition.
   *
   * @return array
   *   Associative array container selector, trigger, and value.
   */
  protected static function getFormApiStatesCondition(array $condition) {
    $selector = $condition['selector'];
    $trigger = $condition['trigger'];
    if ($selector && $trigger) {
      if (in_array($trigger, ['value', '!value'])) {
        $value = $condition['value'];
      }
      elseif (in_array($trigger, ['pattern', '!pattern', 'less', 'greater'])) {
        $value = [$trigger => $condition['value']];
        $trigger = 'value';
      }
      else {
        $value = TRUE;
      }
    }
    else {
      $value = '';
    }
    return [
      'selector' => $selector,
      'trigger' => $trigger,
      'value' => $value,
    ];
  }

  /**
   * Convert webform values to states array.
   *
@@ -766,6 +826,10 @@ class WebformElementStates extends FormElement {
      'collapsed' => t('Collapsed'),
      'value' => t('Value is'),
      '!value' => t('Value is not'),
      'pattern' => t('Pattern'),
      '!pattern' => t('Not Pattern'),
      'less' => t('Less than'),
      'greater' => t('Greater than'),
    ];
  }

+6 −1
Changes for src/Tests/Element/WebformElementStatesTest.php: 6 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -78,7 +78,12 @@ states_unsupported_nesting:
    - selector_03:
        value: 'Yes'
      selector_04:
        value: 'Yes'");
        value: 'Yes'
states_custom_condition:
  required:
    custom_selector:
      value:
        pattern: '[a-z0-9]+'");

    /**************************************************************************/
    // Rendering.
+24 −1
Changes for src/Tests/WebformSubmissionConditionsValidatorTest.php: 24 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ class WebformSubmissionConditionsValidatorTest extends WebformTestBase {
   *
   * @var array
   */
  protected static $testWebforms = ['test_form_states_server_required', 'test_form_states_server_wizard'];
  protected static $testWebforms = ['test_form_states_server_required', 'test_form_states_server_wizard', 'test_form_states_server_custom'];

  /**
   * {@inheritdoc}
@@ -287,6 +287,29 @@ class WebformSubmissionConditionsValidatorTest extends WebformTestBase {
    $this->assertRaw('state_province_b field is required.');
    $this->assertRaw('postal_code_b field is required.');
    $this->assertRaw('country_b field is required.');

    /**************************************************************************/
    // custom.
    /**************************************************************************/

    $webform = Webform::load('test_form_states_server_custom');

    // Check no #states required errors.
    $this->postSubmission($webform);
    $this->assertRaw('New submission added to Test: Form API #states custom pattern, less, and greater condition validation');

    $edit = [
      'trigger_pattern' => 'abc',
      'trigger_not_pattern' => 'ABC',
      'trigger_less' => 1,
      'trigger_greater' => 11,
    ];
    $this->postSubmission($webform, $edit);
    $this->assertNoRaw('New submission added to Test: Form API #states custom pattern, less, and greater condition validation');
    $this->assertRaw('dependent_pattern field is required.');
    $this->assertRaw('dependent_not_pattern field is required.');
    $this->assertRaw('dependent_less field is required.');
    $this->assertRaw('dependent_greater field is required.');
  }

  /**
+24 −0
Changes for src/WebformSubmissionConditionsValidator.php: 24 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -417,6 +417,17 @@ class WebformSubmissionConditionsValidator implements WebformSubmissionCondition
    $element_plugin = $this->elementManager->getElementInstance($element);
    $element_value = $element_plugin->getElementSelectorInputValue($selector, $trigger_state, $element, $webform_submission);

    // Process trigger sub state used for custom #states API validation.
    // @see Drupal.behaviors.webformStatesComparisions
    // @see http://drupalsun.com/julia-evans/2012/03/09/extending-form-api-states-regular-expressions
    if ($trigger_state == 'value' && is_array($trigger_value)) {
      $trigger_substate = key($trigger_value);
      if (in_array($trigger_substate, ['pattern', '!pattern', 'less', 'greater'])) {
        $trigger_state = $trigger_substate;
        $trigger_value = reset($trigger_value);
      }
    }

    // Process trigger state/negate.
    list($trigger_state, $trigger_negate) = $this->processState($trigger_state);

@@ -442,6 +453,19 @@ class WebformSubmissionConditionsValidator implements WebformSubmissionCondition
        }
        break;

      case 'pattern':
        // @see \Drupal\Core\Render\Element\FormElement::validatePattern
        $result = preg_match('{' . $trigger_value . '}', $element_value);
        break;

      case 'less':
        $result = ($element_value !== '' && $trigger_value > $element_value);
        break;

      case 'greater':
        $result = ($element_value !== '' && $trigger_value < $element_value);
        break;

      default:
        return NULL;
    }
Loading