fix(badges): keep the medal catalog from being cast to a string on save

user_medal_args was declared as type=json without sanitize_type, so
sanitize_field_value() fell through to sanitize_text_field((string) $value)
and stored the literal string "Array". validate() has no json shape check,
so the save reported success.

Consequence: parse_medal_catalog() json_decoded "Array" into an empty
catalog, the dictionary came back empty, and user_context() dropped every
owned badge — the front end showed "获取徽章" and the grant modal listed
nothing, hiding both the newly added and the pre-existing badges.

Add the missing sanitize_type to match its sibling level/integral fields,
and guard sanitize_field_value() so structured container types can never
reach the scalar fallback again. Mirrors the declaration in the Vue schema.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
XingandClaude Opus 5 committed 2026-11-01 15:47:09 +08:00
1 parent 5c6b422866
commit 4e62b325d2
3 files changed
+12

No files matched your search

+7
View File
@@ -857,6 +857,13 @@ class SchemaProvider {
return esc_url_raw((string) $value);
}
// Structured container types must never reach the scalar fallback below:
// casting a nested payload to string silently destroys it ("Array") and
// validate() has no json/array shape check to catch the loss.
if (in_array($type, ['json', 'repeater', 'sortable', 'group', 'fieldset', 'accordion'], true)) {
return is_array($value) ? $value : [];
}
return sanitize_text_field((string) $value);
}