Loading js/webform.states.js +31 −0 Changes for js/webform.states.js: 31 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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. Loading src/Element/WebformElementStates.php +87 −23 Changes for src/Element/WebformElementStates.php: 87 added lines, 23 removed lines. Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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); } } } Loading @@ -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. * Loading @@ -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) { Loading @@ -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. * Loading Loading @@ -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'), ]; } Loading src/Tests/Element/WebformElementStatesTest.php +6 −1 Changes for src/Tests/Element/WebformElementStatesTest.php: 6 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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. Loading src/Tests/WebformSubmissionConditionsValidatorTest.php +24 −1 Changes for src/Tests/WebformSubmissionConditionsValidatorTest.php: 24 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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} Loading Loading @@ -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.'); } /** Loading src/WebformSubmissionConditionsValidator.php +24 −0 Changes for src/WebformSubmissionConditionsValidator.php: 24 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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 Loading
js/webform.states.js +31 −0 Changes for js/webform.states.js: 31 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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. Loading
src/Element/WebformElementStates.php +87 −23 Changes for src/Element/WebformElementStates.php: 87 added lines, 23 removed lines. Original line number Diff line number Diff line Loading @@ -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; } Loading Loading @@ -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); } } } Loading @@ -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. * Loading @@ -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) { Loading @@ -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. * Loading Loading @@ -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'), ]; } Loading
src/Tests/Element/WebformElementStatesTest.php +6 −1 Changes for src/Tests/Element/WebformElementStatesTest.php: 6 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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. Loading
src/Tests/WebformSubmissionConditionsValidatorTest.php +24 −1 Changes for src/Tests/WebformSubmissionConditionsValidatorTest.php: 24 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -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} Loading Loading @@ -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.'); } /** Loading
src/WebformSubmissionConditionsValidator.php +24 −0 Changes for src/WebformSubmissionConditionsValidator.php: 24 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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