Allow wpml_cf_preferences: 2 on image/gallery fields (Options Page house rule) - #29
Merged
Conversation
…e house rule)
field-image.schema.json and field-gallery.schema.json enforced an
unconditional const:1, curated against a post_type/block-context live
install. On an ACF Options Page, ACFML permanently locks a 1-flagged
("copy") field to its default-language value since there's no post
duplication to copy from — tailwind-base's wordpress/gutenberg.md and
wordpress/wpml.md both document 2 for every options-page value field,
image/gallery included. The schema had no location context to enforce
1-only-outside-options-pages, so both refs now accept enum [1, 2].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vq8VHGjkVG28SMVLCdu7B7
6 tasks
location-aware --wpml cross-check The enum: [1, 2] widening for image/gallery fixed the options-page false positive but opened a false negative in the far more common post/block context: an image/gallery field mistakenly set to wpml_cf_preferences: 2 now validates silently there, and translators lose per-language image swapping. Nothing caught this -- --wpml mode only checked presence, never value-against-location -- confirmed independently by both reviewers. Per both reviewers' converged recommendation: this is a linter concern, not a schema concern -- AcfLinter is the only place with both `fields` and `location` in view at once, so the schema stays context-neutral (per field-image.schema.json's own description) and the check lives in a new wpmlLocationValueFindings() gated behind the existing --wpml opt-in. Behaviour: - location resolves unambiguously to options_page-only -> requires 2 for every image/gallery field (root + nested via sub_fields/layouts) - location resolves unambiguously to post_type/block-only -> requires 1 - mixed location (targets both) is left alone -- no single correct value to demand without false-flagging a legitimate dual-context group New tests cover both directions of the value mismatch, the previously -fixed options-page-value-under-options-page-location case (regression guard), the mixed-location non-flag, and a nested repeater sub-field. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vq8VHGjkVG28SMVLCdu7B7
The classifier tracked only options_page vs post_type/block booleans, so any other ACF location param (taxonomy, nav_menu_item, user_form, attachment, widget, comment, page_template, ...) was invisible to it. A group whose location was `options_page OR taxonomy` collapsed to a pure options-page context and wrongly demanded the options-page value from fields that also render on a taxonomy term screen. This contradicted the method's own documented policy: a group genuinely targeting BOTH an options page and a post type is left alone (no single correct value to demand) -- the same must hold for any other unrecognized context mixed in, for the same reason. Now tracks whether any unrecognized param was seen and returns null (ambiguous, don't guess) whenever it coexists with a recognized context. Pure single-context classification (options_page-only, post_type/block-only) is unchanged and still flags correctly. Also documents why `operator` (e.g. `!=`) is deliberately ignored in this classifier: `post_type != page` still targets a post_type context, just narrowed by value -- negation doesn't change which context a param belongs to. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vq8VHGjkVG28SMVLCdu7B7
15b3ca6 introduced a single global $hasOther flag that made the classifier bail to null whenever ANY unrecognized param appeared anywhere in the location — including post-context qualifiers (page_template, post_status, …) paired with post_type in the SAME AND-group. That silently regressed the common real-world shape "field group scoped to a post type AND a specific page template": it went from correctly flagging to silently skipping. Replace the global flag with per-OR-group classification (classifyOrGroup()), enumerating all 24 declared location params into three buckets: primary (post_type/block, options_page), post-context qualifiers (page_template, post_template, post_status, post_format, post_category, post_taxonomy, post, page_type, page_parent, page, attachment — compatible with post_type_or_block, never competing for context), neutral (current_user, current_user_role — coexist with anything), and other (taxonomy, user_form, user_role, user, comment, widget, nav_menu, nav_menu_item — genuinely distinct contexts). classifyLocationContext() then unions group verdicts across OR-groups, returning null only when groups genuinely disagree or a group resolves to 'other'. Regression tests: post_type AND page_template / post_type AND post_status in a single OR-group now flag again. The 15b3ca6 fix's own intent (options_page OR taxonomy, post_type OR user_form as separate groups) stays silent — no regression there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J51xpeny2K3mm3LCFVGc2w
The [Unreleased] section documented only the enum widening (the first of four commits). The new location-aware --wpml check that the widening made necessary, and the two classifier fixes that followed, were missing -- including the behavioural widening to standalone qualifier groups, which will surface findings on files previously skipped.
parisek
marked this pull request as ready for review
July 25, 2026 08:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four commits. The first widens a schema enum; the next three build and then harden a new
--wpmllinter check that the widening made necessary.1.
487875e— allowwpml_cf_preferences: 2on image/gallery (Options Page house rule)field-image.schema.json/field-gallery.schema.jsonenforced an unconditionalconst: 1, curated against a live install in apost_type/blocklocation context.1-flagged ("copy") field to its default-language value — there's no post duplication to copy from.tailwind-base'swordpress/gutenberg.md§ Key Requirements andwordpress/wpml.md's gotcha table both document2for every value field on an options page, image/gallery included.location), so a conditional rule isn't expressible without a large restructuring. Both refs now acceptenum: [1, 2]— the same trade-off already made forfield-link/field-select/field-url.parisek/definition-kitPR fix: unify field validation across nesting, complete per-type stubs (closes #8) #10'sstore-locatorcorpus fixture.2.
43d40a4— new--wpmllocation-value cross-checkThe enum widening fixed the options-page false positive but opened a false negative in the far more common post/block context: an image/gallery field mistakenly set to
2now validated silently, and translators lose per-language image swapping.AcfLinteris the only place with bothfieldsandlocationin view at once, so the check lives there (wpmlLocationValueFindings(), gated behind--wpml) rather than in the schema, which stays context-neutral by design.3.
15b3ca6— heterogeneous OR groups misclassifiedclassifyLocationContext()tracked onlyoptions_pagevspost_type/blockbooleans; every other ACFparamwas invisible. A group located atoptions_page OR taxonomycollapsed to pure options-page context and demanded the options-page value from fields that also render on a term screen — contradicting the method's own documented policy of leaving genuinely dual-context groups alone.4.
5c1a0ce— per-OR-group classificationThe
15b3ca6fix used one global flag, which could not distinguish "two AND-rules in one OR-group" from "two separate OR-groups". That silencedpost_type AND page_template— one of the most common real ACF shapes — trading a false positive on a rare shape for a false negative on a common one.Classification now runs per OR-group and unions the verdicts. All 24 params from
location-rule.schema.jsonare assigned deliberately:post_type,blockoptions_pagepage_template,post_template,post_status,post_format,post_category,post_taxonomy,post,page_type,page_parent,page,attachmentcurrent_user,current_user_roletaxonomy,user_form,user_role,user,comment,widget,nav_menu,nav_menu_itemBehavioural widening worth calling out: besides fixing the AND-group regression, this also newly activates standalone qualifier groups. A group whose entire location is e.g.
[[page_template == consulting.php]], with nopost_typeat all, previously resolved to ambiguous and was skipped; it now resolves to post-context and is checked. This is intentional —page_templatecan only ever apply to a post/page screen — but it is broader than "an AND-group fix" and will surface findings on files that were silently skipped before.operatoris deliberately ignored:post_type != pagestill means a post-type context, so the demanded value is unchanged by negation. Documented in the classifier's docblock.Test plan
composer testgreen — 162 tests, 607 assertions (1 pre-existing skip:SnapshotTest, requires a live WP install).composer phpstangreen, no baseline or ignores added.src/templates/refs/(source of truth) andschemas/refs/(distribution mirror) edited identically —SchemaConsistencyTestpasses.--wpml --strict, read-only) againsteprukaz2025unchanged before/after5c1a0ce. Noted explicitly: that corpus is all single-context block locations, so it does not exercise this bug — the unit tests are the evidence, not the unchanged count.page_template + post_type,post_template + post_type,user_form + user_role, and a 6-grouppost_type/post_templatemix) all classify correctly, no false positives.[Unreleased]entry added.Known gap (non-blocking)
Regression coverage is thin relative to the 24-param table: only
page_templateandpost_statusare exercised as qualifier regressions, and noother-bucket param has a dedicated "mixed with post_type stays null" test. A future refactor could silently flip one of the 9 untested qualifier assignments without any test failing. Worth a follow-up; not a merge blocker.https://claude.ai/code/session_01J51xpeny2K3mm3LCFVGc2w