fix(schema): accept readonly and disabled where ACF renders them - #38
Conversation
ACF Pro passes `readonly` and `disabled` into the rendered input for ten field types, but no schema declared either key and `field-item.schema.json` sets `unevaluatedProperties: false`. A field group using ACF's own read-only inputs therefore failed validation, and the package has no local ignore to fall back on -- so the consuming project had no lint-clean option short of removing configuration that works. Verified against ACF Pro 6.x rather than the documentation, which does not list either setting: text class-acf-field-text.php:71 textarea class-acf-field-textarea.php:59 number class-acf-field-number.php:61 range class-acf-field-range.php:60 email class-acf-field-email.php:58 url class-acf-field-url.php:52 select class-acf-field-select.php:283-287 date_picker class-acf-field-date-picker.php:128 date_time_picker class-acf-field-date-time-picker.php:141 time_picker class-acf-field-time-picker.php:78 Added to those ten refs only, NOT to `refs/field.schema.json`. The base schema would accept the keys on `image`, `repeater`, `true_false` and every other type, where ACF silently ignores them -- exactly the dead configuration this package reports elsewhere (the WPML repeater message says so in as many words). `enum: [0, 1]` matches `required` and `allow_in_bindings` in the base schema; ACF truthy-checks the value and serialises 0/1. Both copies edited, template and distribution, per the source-of-truth rule in AGENTS.md. No new field type, so `FIELD_TYPE_ORDER` and the generated root schemas are untouched. Fixtures cover both halves of the decision: a valid group carrying `readonly`/`disabled` on text, number, select and date_picker, and an invalid one putting `readonly` on an image. The second is the load-bearing one -- without it a later move of these keys into the base schema would pass every test. Checked by negative control that the invalid fixture fails ONLY on this key: removing `readonly` from it makes the same document validate. Found while linting a real project (fellows), whose `flat` field group marks two import-owned fields read-only; that file validates against the patched schemas. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GJnbedTAFFLZX5Z8MEnTB (petr@pari.cz)
Two independent reviews of the first commit. Both findings below were verified against the ACF source before acting. PASSWORD WAS MISSING. `class-acf-field-password.php:52` delegates its whole render to the text field, which is where `readonly`/`disabled` are passed into the input -- so password honours both and the schema still rejected them. The first commit's evidence was gathered by grepping each field class for the literal `'readonly'`, which cannot see a type that inherits the behaviour. Checked the whole directory for that pattern afterwards: `password` is the only delegating type, so the blind spot cost exactly one. Control run rather than assumed: a password field with `readonly: 1` validates against the patched schemas and fails against the previous commit's, with the error naming the key. FIXTURE now covers all eleven accepted types instead of four. The previous fixture would have stayed green with password missing, which is how the gap survived the first round. Invalid fixture renamed `readonly-on-image` -> `image-readonly-prop`, to match the `<type>-<problem>` convention every sibling uses (`image-return-format-url`, `file-unknown-prop`, `button_group-bad-layout`). CHANGELOG now cites the ACF source behind the type list and names the downstream project, as the 0.7.3 and 0.7.5 entries do, and warns that the list is a claim about ACF internals that needs re-checking on upgrades. CONSIDERED AND NOT CHANGED, both raised as findings: - `readonly` on `select` is inert in the browser: ACF sets the attribute (`class-acf-field-select.php:283`) but HTML ignores `readonly` on `<select>`, and Select2 reads `disabled`. Kept, because the criterion here is what ACF writes into the input, not what the browser then does with it. Modelling browser semantics would be a different and much larger promise. - `enum: [0, 1]` rejects `readonly: true`. Kept for consistency with `required` and `allow_in_bindings` in `refs/field.schema.json`, which use the same shape; acf.json is an export format and ACF's admin serialises 0/1. Worth revisiting only if ACF is shown to export a boolean. - `disabled` on `radio`/`checkbox` is a list of choice values, not a flag (`class-acf-field-checkbox.php:265,310`, `class-acf-field-radio.php:158`). Real, and deliberately out of scope: it needs its own array shape, and folding a second shape into this change would bundle two decisions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GJnbedTAFFLZX5Z8MEnTB (petr@pari.cz)
|
Agent review record — two independent reviewers on Fixed in
|
From-Project
Found while running
composer lint:acf-jsonon fellows, whoseflatfield group marks two import-owned fields (better_id,better_roomtype_id) read-only so editors cannot desync them from the CSV import that owns them:Why
ACF Pro supports
readonlyanddisabledon ten field types and passes both into the rendered input. No schema declared either key, andfield-item.schema.jsonsetsunevaluatedProperties: false— so a field group using ACF's own read-only inputs fails validation. Per the consuming doctrine there is no local ignore and no per-project override, so the project's only lint-clean option was to delete configuration that works.Verified against ACF Pro 6.x source rather than the documentation, which lists neither setting:
textclass-acf-field-text.php:71textareaclass-acf-field-textarea.php:59numberclass-acf-field-number.php:61rangeclass-acf-field-range.php:60emailclass-acf-field-email.php:58urlclass-acf-field-url.php:52selectclass-acf-field-select.php:283-287date_pickerclass-acf-field-date-picker.php:128date_time_pickerclass-acf-field-date-time-picker.php:141time_pickerclass-acf-field-time-picker.php:78What
Adds to those ten per-type refs, in both
src/templates/refs/andschemas/refs/per the source-of-truth rule inAGENTS.md:Per type, deliberately not in
refs/field.schema.json. The base schema would accept both keys onimage,repeater,true_falseand everything else, where ACF silently ignores them — the dead configuration this package already reports elsewhere (the WPML repeater message calls it that by name).enum: [0, 1]matchesrequiredandallow_in_bindingsin the base schema; ACF truthy-checks the value and serialises0/1.No new field type, so
FIELD_TYPE_ORDERand the generated root schemas are untouched.Tests
Two fixtures, one per half of the decision:
valid/starter_theme/readonly-inputs—readonly/disabledontext,number,select,date_picker.invalid/readonly-on-image—readonlyon an image. This is the load-bearing one: without it, a later move of these keys into the base schema would pass the whole suite.Checked by negative control that the invalid fixture fails only on this key — removing
readonlyfrom that same document makes it validate:composer checkgreen locally: PHPUnit 205 passed / 1 skipped, PHPStan level 8 clean. The real fellowsflat/acf.jsonvalidates against the patched schemas.Not proposed
Loosening
unevaluatedPropertiesonfield-item.schema.json. It is what makes a typo'd key an error, and trading that for this would be a bad exchange.Draft — opening for review rather than merge.
🤖 Generated with Claude Code
https://claude.ai/code/session_012GJnbedTAFFLZX5Z8MEnTB