Skip to content

feat(wpforms): add single/double opt-in setting to WPForms integration#823

Open
faisalahammad wants to merge 1 commit intoibericode:mainfrom
faisalahammad:feature/issue-710-wpforms-double-optin
Open

feat(wpforms): add single/double opt-in setting to WPForms integration#823
faisalahammad wants to merge 1 commit intoibericode:mainfrom
faisalahammad:feature/issue-710-wpforms-double-optin

Conversation

@faisalahammad
Copy link
Contributor

@faisalahammad faisalahammad commented Feb 17, 2026

🎯 Summary

Adds a per-field "Double opt-in?" setting to the WPForms Mailchimp field, allowing administrators to toggle between single and double opt-in on a per-form basis — matching the existing Gravity Forms integration pattern.

📋 Issue Reference

Fixes #710

🔍 Problem Description

Current Behavior

The WPForms integration always uses double opt-in when subscribing users to Mailchimp. There is no way for site administrators to switch to single opt-in, forcing all subscribers to go through the confirmation email flow regardless of the use case.

Expected Behavior

Administrators should be able to choose between single opt-in and double opt-in on a per-form (per-field) basis, similar to how the Gravity Forms and Ninja Forms integrations already work.

Root Cause

The WPForms integration's subscribe_from_wpforms() method directly calls $this->subscribe() without ever modifying $this->options['double_optin'], which defaults to 1 (enabled) in the base MC4WP_Integration class. Additionally, the WPForms field builder UI (MC4WP_WPForms_Field) does not expose any setting for double opt-in, so the value is never stored in the form data.

✨ Solution Overview

Approach Taken

Per-field setting in the WPForms builder — following the same pattern as the existing Gravity Forms integration:

  1. Add a "Double opt-in?" Yes/No <select> dropdown to the WPForms Mailchimp field's builder options
  2. Read the per-field mailchimp_double_optin value during form submission
  3. Apply it to $this->options['double_optin'] before calling subscribe()
  4. Restore original options afterward to prevent side effects

Why This Approach

  • Consistency: Matches the Gravity Forms integration pattern (see integrations/gravity-forms/class-gravity-forms.php), so the codebase stays uniform
  • Per-field control: Since WPForms can have multiple Mailchimp fields (one per form), the setting is stored at the field level — not globally in the integration settings page
  • Backward compatible: Defaults to '1' (double opt-in enabled), so existing forms continue working identically without any migration
  • Minimal changes: Only 2 files modified, ~50 lines of new production code

Alternatives Considered

  1. Global setting on the integration admin page: Would require adding 'double_optin' to get_ui_elements(). Rejected because WPForms stores list selection per-field (not globally), so double opt-in should follow the same pattern for consistency. A global setting also wouldn't support different opt-in modes across different forms.
  2. Filter-based approach: Adding a mc4wp_integration_wpforms_double_optin filter hook. Rejected because it requires custom code from the user — the whole point of issue WP Forms integration single / double opt in setting #710 is providing a UI setting that non-developers can use.

🔧 Changes Made

Files Modified

File Changes
integrations/wpforms/class-field.php Added "Double opt-in?" select dropdown in the WPForms field builder
integrations/wpforms/class-wpforms.php Read per-field mailchimp_double_optin setting and apply before subscribing
tests/WPFormsDoubleOptinTest.php [NEW] 5 unit tests for the feature

Detailed Changes

1. WPForms Field Builder UI — class-field.php

Added a new field_option_double_optin() method and wired it into the field options panel:

// Called in field_options() between list selector and checkbox choices
$this->field_option_double_optin($field);
/**
 * Renders the "Double opt-in?" select field option in the form builder.
 *
 * @since 4.9
 * @param array $field Field data.
 */
private function field_option_double_optin($field)
{
    $tooltip      = __('Select "yes" if you want people to confirm their email address before being subscribed (recommended).', 'mailchimp-for-wp');
    $option_label = $this->field_element(
        'label',
        $field,
        [
            'slug'    => 'mailchimp-double-optin',
            'value'   => __('Double opt-in?', 'mailchimp-for-wp'),
            'tooltip' => $tooltip,
        ],
        false
    );

    $current_value = isset($field['mailchimp_double_optin']) ? $field['mailchimp_double_optin'] : '1';
    $option_select = sprintf('<select name="fields[%s][mailchimp_double_optin]" ...>', $field['id'], ...);
    $option_select .= sprintf('<option value="1" %s>%s</option>', selected('1', $current_value, false), __('Yes', 'mailchimp-for-wp'));
    $option_select .= sprintf('<option value="0" %s>%s</option>', selected('0', $current_value, false), __('No', 'mailchimp-for-wp'));
    $option_select .= '</select>';

    $output = $this->field_element('row', $field, [
        'slug'    => 'mailchimp-double-optin',
        'content' => $option_label . $option_select,
    ]);
}

Why This Works:

  • Uses the same field_element() API as the existing list selector and checkbox options, ensuring consistent rendering
  • Stores the value in $field['mailchimp_double_optin'] which is saved as part of the WPForms form data
  • Defaults to '1' (Yes) so existing forms are unaffected

2. Integration Logic — class-wpforms.php

Before:

public function subscribe_from_wpforms($checkbox_field_id, $fields, $form_data)
{
    // ... find email ...
    $mailchimp_list_id      = $form_data['fields'][ $checkbox_field_id ]['mailchimp_list'];
    $this->options['lists'] = [ $mailchimp_list_id ];
    // ❌ double_optin is never set — always defaults to 1

    if (! empty($email_address)) {
        return $this->subscribe([ 'EMAIL' => $email_address ], $form_data['id']);
    }
    // ❌ options are never restored
}

After:

public function subscribe_from_wpforms($checkbox_field_id, $fields, $form_data)
{
    // ... find email ...
    $field_config      = $form_data['fields'][ $checkbox_field_id ];
    $mailchimp_list_id = $field_config['mailchimp_list'];
    $double_optin      = isset($field_config['mailchimp_double_optin']) ? $field_config['mailchimp_double_optin'] : '1';

    // ✅ Override integration settings with per-field options
    $orig_options                  = $this->options;
    $this->options['lists']        = [ $mailchimp_list_id ];
    $this->options['double_optin'] = $double_optin;

    $result = false;
    if (! empty($email_address)) {
        $result = $this->subscribe([ 'EMAIL' => $email_address ], $form_data['id']);
    }

    // ✅ Restore original options to avoid side effects
    $this->options = $orig_options;
    return $result;
}

Why This Works:

  • The base MC4WP_Integration::subscribe() reads $this->options['double_optin'] to determine subscriber status ('pending' vs 'subscribed'). By setting this value before calling subscribe(), we control the opt-in behavior.
  • Restoring $this->options afterward prevents the per-field override from leaking into subsequent form processing — this is the same defensive pattern used by the Gravity Forms integration.
  • Defaults to '1' when mailchimp_double_optin is not set, ensuring backward compatibility with existing forms.

Impact:

  • ✅ Administrators can now choose single or double opt-in per WPForms form
  • ✅ Existing forms continue to use double opt-in (no behavior change)
  • ✅ Follows established patterns from the Gravity Forms integration

🧪 Testing Performed

Automated Testing

$ vendor/bin/phpunit
Welcome to the Mailchimp for WordPress Test Suite
PHPUnit 9.6.34 by Sebastian Bergmann and contributors.

...........................................................       59 / 59 (100%)

Time: 00:00.024, Memory: 6.00 MB

OK (59 tests, 185 assertions)

New Tests Createdtests/WPFormsDoubleOptinTest.php (5 tests, 10 assertions):

Test Description Result
test_default_double_optin Verifies double opt-in defaults to '1' when not set ✅ Pass
test_single_optin Verifies double_optin is set to '0' when configured ✅ Pass
test_options_restored_after_subscribe Verifies original options are restored after call ✅ Pass
test_listen_to_wpforms_triggers_subscription Verifies subscription triggers for checked checkbox ✅ Pass
test_listen_to_wpforms_skips_unchecked Verifies no subscription for unchecked checkbox ✅ Pass

Regression Testing

  • ✅ All 59 existing tests pass (185 assertions)
  • ✅ No existing test was modified

📊 Performance Impact

Negligible — The only addition is reading one extra field from the already-loaded $form_data array and one extra variable assignment. No additional database queries, API calls, or file I/O.

🔒 Security Considerations

  • Input handling: The mailchimp_double_optin value is read from $form_data['fields'] which is already sanitized by WPForms during form save. It's used as a truthy/falsy value in the base class (? 'pending' : 'subscribed'), not interpolated into SQL or HTML.
  • Output escaping: The <select> dropdown in the builder uses selected() (WordPress core function) for safe attribute output.
  • No new user input surfaces: The setting is only configurable by form editors (administrators), not front-end users.

🌍 Internationalization

  • ✅ All new strings wrapped in __() with text domain mailchimp-for-wp
  • ✅ Strings: "Double opt-in?", "Yes", "No", and tooltip text
  • ✅ Consistent with existing translation patterns in the codebase

⚠️ Breaking Changes

No breaking changes — Fully backward compatible. The mailchimp_double_optin field defaults to '1' when not present, maintaining identical behavior for all existing WPForms forms.

Screenshot / Screen recording

https://cln.sh/ylJ1Lk35

Field Settings Email List

✅ PR Checklist

  • Code follows WordPress Coding Standards
  • All functions have proper PHPDoc blocks
  • Input handled safely
  • Output escaped where applicable
  • Strings are translatable with correct text domain
  • No PHP warnings/errors
  • Backward compatible — defaults preserve existing behavior
  • All 59 tests pass successfully (185 assertions)
  • Self-reviewed for quality
  • Follows existing codebase patterns (Gravity Forms reference)

🤝 Additional Context

  • The Gravity Forms integration (integrations/gravity-forms/) was used as the primary reference for this implementation, as it already supports per-field double opt-in in the same way.
  • The Ninja Forms integration takes a slightly different approach (through its action-based architecture), but the end result is the same — per-form opt-in control.
  • This addresses a feature request that has been open since October 2020, with multiple community members requesting the same capability.

- Add 'Double opt-in?' select dropdown to the WPForms Mailchimp field builder
- Read per-field mailchimp_double_optin setting during subscription
- Restore original options after subscribing to avoid side effects
- Add unit tests covering default, single opt-in, and options restoration

Fixes ibericode#710
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WP Forms integration single / double opt in setting

1 participant