Problem/Motivation

The "Webform navigation settings" fieldset added to a webform's third-party settings form (webformnavigation_webform_third_party_settings_form_alter()) includes two checkboxes, forward_navigation and prevent_next_validation, that are saved directly as webform third-party settings. Neither checkbox has an #element_validate callback, so Form API submits their raw values as-is — the string '1' when checked, and either '0' or nothing at all when unchecked, depending on the browser/JS state handling — rather than a real boolean. Over time this lets non-boolean values ('1'/'0' strings, or missing/NULL) get written into third_party_settings.webformnavigation.* on saved webform config entities.

This is compounded by the module's config schema (config/schema/webformnavigation.schema.yml) only declaring a type for forward_navigationprevent_next_validation, the additional_error_message setting, and the webform_navigation handler's debug setting have no schema entries at all. Because prevent_next_validation was never declared as boolean in schema, Drupal's config schema validation had no way to flag the bad values being stored for it in the first place, so the problem could go unnoticed.

Concretely, this means:

  • $webform->getThirdPartySetting('webformnavigation', 'prevent_next_validation') can return a string like '1' instead of TRUE, which breaks strict (===) boolean comparisons anywhere calling code assumes a real boolean.
  • drush config:export/config:import round-trips can produce spurious diffs once schema coverage is added, since existing saved config doesn't match the boolean type.
  • Config schema validation (drush config:status with strict schema checking, or ConfigSchemaChecker in tests) cannot catch bad data for prevent_next_validation today because there's no schema rule to check it against.

Steps to reproduce

  1. Install Webform + Webform Navigation 2.0.4.
  2. Edit a webform's third-party settings, check "Allow forward navigation" and/or "Prevent validation when the user presses the Next Page button", and save.
  3. Inspect the saved config (drush config:get webform.webform.<id> third_party_settings.webformnavigation, or via the UI's config export tab).
  4. The saved values for forward_navigation / prevent_next_validation are the raw submitted strings (e.g. '1'), not real YAML booleans (true).

Proposed resolution

  1. Add an #element_validate callback to both checkboxes in webformnavigation_webform_third_party_settings_form_alter() that casts the submitted value to a real boolean before it's stored:

    function webformnavigation_element_validate_boolean(array &$element, FormStateInterface
      $form_state) {
        $form_state->setValueForElement($element, (bool) $element['#value']);
      }

    ...and reference it from both the forward_navigation and prevent_next_validation checkbox definitions via '#element_validate' => ['webformnavigation_element_validate_boolean'].

  2. Complete config/schema/webformnavigation.schema.yml with entries for prevent_next_validation (boolean), additional_error_message (label), and the webform.handler.webform_navigation mapping's debug setting (boolean), so schema validation actually covers everything the module writes.

  3. Add an update hook that walks existing webform config entities and casts any non-boolean forward_navigation / prevent_next_validation values already on disk to real booleans, so sites upgrading don't carry forward corrupted data from before the fix. Using \Drupal\Core\Config\Entity\ConfigEntityUpdater keeps this batch-safe for sites with many webforms:

    function webformnavigation_update_8002(&$sandbox) {
        \Drupal::classResolver(\Drupal\Core\Config\Entity\ConfigEntityUpdater::class)
          ->update($sandbox, 'webform', function (\Drupal\webform\WebformInterface $webform): bool {
            $changed = FALSE;
            foreach (['forward_navigation', 'prevent_next_validation'] as $key) {
              $value = $webform->getThirdPartySetting('webformnavigation', $key);
              if ($value !== NULL && !is_bool($value)) {
                $webform->setThirdPartySetting('webformnavigation', $key, (bool) $value);
                $changed = TRUE;
              }
            }
            return $changed;
          });
      }

Remaining tasks

  1. We have a working patch against 2.0.4 implementing all three pieces above; will attach it here (or open an MR, whichever the maintainers prefer).
  2. No automated test coverage currently exercises this scenario; a kernel test asserting that forward_navigation/prevent_next_validation round-trip as real booleans after form submission, and that the update hook normalizes pre-existing bad data, would be worth adding.
  3. Needs review from a maintainer.

Data model changes

Sites upgrading to the fixed version may have existing webform config entities with non-boolean values stored under third_party_settings.webformnavigation.forward_navigation and/or third_party_settings.webformnavigation.prevent_next_validation (e.g. '1'/'0' strings instead of true/false). The new update hook (webformnavigation_update_8002() in the proposed patch) walks all webform config entities and normalizes any non-boolean value for these two keys to a real boolean, so existing sites don't fail config schema validation once the schema is completed.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

rymcveigh created an issue. See original summary.

rymcveigh’s picture

Issue summary: View changes

rymcveigh’s picture

Status: Active » Needs review

The code in the Merge Request is now ready for review.

  • rymcveigh committed 1693d904 on 2.0.x
    Issue #3612604 by rymcveigh: Cast third-party settings checkboxes to...
rymcveigh’s picture

Status: Needs review » Reviewed & tested by the community

I tested this on a production site. It is working.

rymcveigh’s picture

Status: Reviewed & tested by the community » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.