This topic contains 0 reply, has 1 voice.
Last updated by enrikB 3 months, 2 weeks ago.
Assisted by: Andreas W..
| Author | Posts |
|---|---|
| January 20, 2026 at 10:23 am #17745347 | |
|
enrikB |
## Summary When using **ACF Options Pages** with **WPML ACF Multilingual (ACFML)** and setting fields to **“Copy once”** (`wpml_cf_preferences = 3`), the admin UI can incorrectly revert translated values to the default language on page reload. Two related issues are observed: 1) **Boolean (`true_false`) cannot persist `0`** in secondary languages on admin reload (it snaps back to default `1`). Frontend output is correct (until a subsequent save, when the value from the default language gets saved); the issues are isolated to **admin options page rendering** and **save-time handling**. ## Environment / Context - WordPress 6.9 - ACF Options Page fields stored in `wp_options` as: ## Expected Behavior ### For “Copy once” on options pages (general) In other words: **“Copy once” should be “copy initially, then allow independent editing including clearing.”** ## Actual Behavior (Reproduction) ### Issue A — `true_false` cannot persist `0` in secondary language ### Issue B — WYSIWYG/text cleared value reappears after reload (after later saves) #### Evidence from DB (`wp_options`, here table prefix `tcoc_`) After clearing content and saving (first time): After modifying any other field later and saving again (content still empty): Once removed, admin reload shows the default language value again (fallback), because the translated value is treated as “not set”. ## Root Cause Analysis (Plugin Code) ### Render-time fallback uses `empty()`, treating legitimate values as “not set” ```php if ( empty( $field['value'] ) && $this->is_field_on_translated_options_page( $post_id ) ) { **Problem:** `empty()` returns true for valid, intentionally-set values like: This causes ACFML to interpret “saved but empty/false” as “not set yet” and fallback to default language for display. This directly explains Issue A (`true_false` value `0`) and contributes to Issue B when empty value persists. ### Save-time filter may convert empty string to `null` on subsequent saves, causing option deletion ```php And: ```php **Observed consequence:** On a later save when the field was already empty, acfml/classes/class-wpml-acf-custom-fields-sync.php:53:1-56:2 can return false (because `empty('')` is true), so `''` becomes `null`. For ACF options, `null` typically results in deletion of the option key. This matches the DB evidence where the `options_fr_info_banner_content` row disappears after a later save, re-triggering fallback. ## Proposed Fixes (Plugin-level) ## Fix 1 (Render): Replace `empty()` check with “has value / key exists” logic **Goal:** Only fallback to default language when the translated value has truly never been set before (as in **copy once**), not when it’s a valid empty/false value (it's been already copied once, user has intentionally cleared it and wants it to be an empty value or 0). ### Suggested approach ### Minimal change (safer) ```diff **Pros:** **Cons:** ### Better fix (options-page key existence) - Value option key: `{$post_id}_{$field['name']}` If the key exists, do not fallback even if the value is empty. Pseudo: ```php $sentinel = '__acfml_notset__'; if ( ! $hasTranslatedKey && $this->is_field_on_translated_options_page( $post_id ) ) { This yields correct Copy once semantics: “fallback only when not created yet”. ## Fix 2 (Save): Do not convert “intentionally cleared” translated values to `null` on options pages **Goal:** Prevent deletion of `options_{lang}_{field}` and `_options_{lang}_{field}` rows when user cleared the field and later saves again. The current clean_empty_values_for_copy_once_field acfml/classes/class-wpml-acf-custom-fields-sync.php:23:1-51:2 was designed for posts/postmeta (and early post creation). On options pages, deletion causes Copy once fallback loops. ### Proposed adjustment - If `$post_id` is an options page ID (string, e.g. `options_fr`) and the option key already exists, **do not set value to null**. Pseudo: ```php if ( $isOptions && '' === $value && WPML_COPY_ONCE_CUSTOM_FIELD === $field['wpml_cf_preferences'] ) { if ( $exists ) { This keeps the empty translated value stable across subsequent saves and prevents accidental “unset” state. ## Fix 3 (Optional): Make value_has_been_emptied classes/class-wpml-acf-custom-fields-sync.php:53:1-56:2 robust against “already empty” history ```php This treats previous empty as false, which is not aligned with “emptied intentionally” history. If you want to track “user emptied it” as a state, do not rely on `empty()`. Use `array_key_exists`: ```php (or track per-field state more explicitly). This is likely more invasive, so Fix 2 is the more localized change. ## Why are workarounds currently needed Because plugin behavior currently: A plugin-level fix would remove the need for interception of ACF rendering and saving, which is currently needed for a logical "copy once" functioning. |
The topic ‘[Closed] ACFML Bug Report: “Copy once” fields on Options Pages (admin reload issues with falsy/empty valu…’ is closed to new replies.