Skip to main content

[Bug] "Select Editor" choice (design_template) not persisted for existing campaigns

# "Select Editor" choice (design_template) not persisted for existing campaigns β€” stale `settings.template_config.design_template` overrides the user's selection on save

## Environment
- FluentCRM: 3.1.6
- FluentCRM Pro / FluentCampaign: 3.1.6
- WordPress: 7.0
- WooCommerce: 10.8.1
- PHP: 8.3
- Database: MariaDB 11.4

## Summary

In the campaign editor, switching the editor via "Select Editor" (Default/Gutenberg β†’ Classic Editor / Raw HTML) and saving does not persist for an existing campaign: after save + reload the campaign opens again in the previous editor (typically Default/Gutenberg).

Asymmetry observed:
- Switching to a Gutenberg-based template (simple/plain/classic) β†’ persists.
- Switching to `raw_classic` / `raw_html` (`use_gutenberg=false`) β†’ does not persist.

## Root cause (diagnosed via network capture + source)

On save, the request to `POST /wp-json/fluent-crm/v2/campaigns/update-single-campaign` does carry the correct user choice as a top-level field:

design_template: "raw_classic" Β  Β  Β  Β  Β  Β // correct β€” the user's Select Editor choicesettings.template_config.design_template: "simple" Β  // STALE

But the server (`CampaignController::updateSingleCampaignSimulate`, ~L352–360) lets the nested value override the top-level one:

$updateData = Arr::only($data, [..., 'design_template']); Β  // = 'raw_classic' (correct)
if (!empty($data['settings']['template_config']['design_template'])) {
Β  Β  $updateData['design_template'] = $data['settings']['template_config']['design_template']; // = 'simple' β†’ reverts
}

Because `raw_classic` / `raw_html` use an empty `config` (see `Helper::getEmailDesignTemplates()`), the `template_config.design_template` is never refreshed when switching to them, so the stale `simple` survives and overrides the user's choice β†’ the column is saved as `simple` β†’ reload opens Gutenberg.

(The campaign editor loads the editor based on the `design_template` column, which is why the stale-override reverts the UI.)

## Reproduction

  1. Open an existing campaign that is in Default (Gutenberg) mode.
  2. Select Editor β†’ Classic Editor. Save (toast: success).
  3. Reload / reopen the campaign.
  4. Observe: it opens in Default (Gutenberg) again. DB shows `design_template = 'simple'` and `settings.template_config.design_template = 'simple'`, even though the save request's top-level `design_template` was `'raw_classic'`.

## Impact

Users cannot reliably switch an existing campaign to the Classic / Raw HTML editor; the choice silently reverts. (Choosing the editor at campaign creation does persist β€” only post-creation switching is affected.)

## Expected

On save, the user's explicit top-level `design_template` (the Select Editor choice) should win, and `settings.template_config.design_template` should be synced to it β€” so the chosen editor mode persists in either direction (mode-agnostic), regardless of whether the target mode has an empty config.

## Our mitigation (site-side, until fixed)

A small mu-plugin hooks `fluent_crm/campaign_data_updated` and, when the saved `design_template` differs from the request's top-level `design_template`, restores the user's choice and syncs `template_config`. It is mode-agnostic and self-deactivating (no-op once the core override is fixed).

## Attachments
- Captured save payload (top-level `design_template=raw_classic`, `template_config.design_template=simple`).
- Before/after DB values.