Commit e799a09
Search 3.0: whitelist supported taxonomies in Custom Taxonomy filter (RSM-2108) (#48684)
* Search 3.0: whitelist supported taxonomies in Custom Taxonomy filter (RSM-2108)
Previously the editor's Custom Taxonomy picker listed every public
custom taxonomy registered on the site, regardless of whether Jetpack
Search actually indexes it — so a site builder could pick a non-indexed
taxonomy and end up with a filter that silently returns zero results
on the front end.
Constrain the picker to the supported set, derived server-side from the
intersection of registered taxonomies with the Sync\Modules\Search
index allowlist. Add a `jetpack_search_custom_taxonomy_map` filter so
advanced users can route a site-specific taxonomy through one of the
reserved `jetpack-search-tagN` index slots (per
https://jetpack.com/support/search/frequently-asked-questions/#troubleshoot-custom-tax)
— the picker shows the slug with a "(mapped)" suffix, URLs and stored
attributes stay user-facing, and store/api.js rewrites only the inner
ES field path onto the slot at query time.
* Fix CI: collapse _doing_it_wrong calls to one line for replace-next-version-tag
tools/replace-next-version-tag.sh matches the _doing_it_wrong pattern
on a single line only — the multi-line layout left the token literal
in the built source and CI's 'Build all projects' rejected it. Extract
the sprintf message to a local $msg variable so the call itself fits
on one line, satisfying the script's regex while keeping the message
text readable.
* Address review: non-array filter notice, guard WC built-ins from slot redirect, gate get_taxonomies to public
- jetpack_search_custom_taxonomy_map filter returning a non-array now
fires _doing_it_wrong() so misconfiguration surfaces during dev
(matches the per-entry notice pattern).
- resolveFilterFields() now early-returns for product_cat / product_tag /
product_brand before consulting customTaxonomyMap so a stray map
entry can't silently redirect a WC built-in filter onto a slot.
- supported_custom_taxonomies() limits the registered-taxonomy probe
to public taxonomies, matching the editor's core.getTaxonomies()
visibility filter.
Coverage: PHP test for the non-array notice; JS test.each that pins
the WC built-in guard for all three product taxonomies.
* Refactor: resolve slot once server-side, drop customTaxonomyMap plumbing from JS API
Threading the global customTaxonomyMap through buildSearchUrl →
buildAggregations / buildFilterClause → resolveFilterFields was
plumbing-heavy and put global state in every JS helper's signature.
Move the resolution to where the filterConfig is BUILT: PHP
Filter_Checkbox::build_config() calls a new Search_Blocks::resolve_taxonomy_slot()
helper to pre-compute the ES field slug ('jetpack-search-tagN' for
mapped custom taxonomies, the slug itself for everything else) and
stores it on the filterConfig as 'effectiveSlug'. JS query builders
then just read config.effectiveSlug — they don't see the map at all.
Net effect:
- resolveFilterFields(), buildAggregations(), buildFilterClause(), and
buildSearchUrl() drop the customTaxonomyMap parameter entirely; their
signatures are back to the simple shape they had before this PR.
- The IA state seed no longer carries a global customTaxonomyMap. Each
filterConfig already carries its own resolution.
- Built-in taxonomies (category, post_tag, product_cat/tag/brand) are
anchored to their canonical fields server-side via the existing
BUILT_IN_CUSTOM_TAXONOMY_EXCLUSIONS list, so a stray map entry can
never silently redirect a built-in filter even at the PHP level.
- Editor side is unchanged: the picker still reads JetpackSearchBlocksConfig.customTaxonomyMap
to append the '(mapped)' suffix.
Tests:
- New PHP test pins resolve_taxonomy_slot() routing across mapped slugs,
unmapped slugs, the empty case, and every built-in slug.
- JS tests updated to pass effectiveSlug on the filterConfig instead of
a map argument; the integration test in buildSearchUrl pins the
end-to-end shape.
- Filter_Checkbox build_config test updated for the new key.
Verified live in atlas docker env: with the demo mu-plugin registering
genre and the slot map, state.filterConfigs.genre.effectiveSlug is
'jetpack-search-tag1', the fetch URL targets
taxonomy.jetpack-search-tag1.slug_slash_name, and aggregations[genre]
stays user-facing. state.customTaxonomyMap is no longer seeded.
* Trigger CI
* Search Blocks: auto-mirror mapped custom taxonomies into reserved slots
Builds on the query-side `jetpack_search_custom_taxonomy_map` filter to
close the indexing gap: when a site declares a slot mapping
(`'genre' => 'jetpack-search-tag1'`), the new
`Custom_Taxonomy_Slot_Mapping` class registers each in-use slot as a
private shadow taxonomy and mirrors term assignments / removals /
deletions onto it on `set_object_terms`, `deleted_term_relationships`,
and `delete_term`. Sync then ships the slot rows to the WPCOM
replicastore, where Jetpack Search's indexer picks them up.
The aggregation request now keys by the slot slug as well as the field
path — the WPCOM search proxy validates aggregation names against
indexable taxonomies, so `aggregations[genre]` against a non-indexed
slug silently returned nothing. `store/index.js` flips the response key
back to the user-facing `filterKey` before downstream consumers see it,
keeping URL params, `activeFilters` shape, and the active-filters pill
list untouched.
Default `apply_filters( 'jetpack_search_custom_taxonomy_map', array() )`
returns empty, so the whole feature is off by default — no slot
taxonomies registered, no auto-mirror, no query rewrite. The filter
doubles as the data declaration and the on/off switch.
Includes a `backfill()` helper for sites that turn on a mapping after
their posts were already tagged.
* Fix CI: suppress PhanAccessMethodInternal on get_terms() backfill call
The previous-WP-version Phan run flags `get_terms()` as `@internal` until
WP 6.9 fixes the stub annotation. Apply the same
PhanAccessMethodInternal / UnusedSuppression pair Sync uses at
class-replicastore.php:876 and modules/class-terms.php:346 so the
backfill helper compiles cleanly on both WP-version matrices.
* Address review: WP_Error guard, slug-based slot lookup, broader mirror tests
- `backfill()`: explicit `is_wp_error( $terms )` guard before
`wp_list_pluck()` so the error path is readable instead of relying
on the silent empty-array conversion. (Copilot #1, claude[bot] #1)
- `mirror_deletion()`: match the slot term by slug rather than name —
`get_term_by( 'name', ... )` is case-sensitive under non-default
collations, which would strand "Fantasy" → "fantasy" mappings.
(Copilot #2)
- Add gating tests for `mirror_removal` and `mirror_deletion` plus a
return-shape test for `backfill()` covering the empty-map and
unregistered-source-taxonomy paths. Term persistence is verified in
a live dev env; WorDBless can't carry `get_term_by()` results far
enough. (Copilot #3, #4)
- Add an end-to-end JS test pinning `actions.search()` → slot-keyed
response → `state.aggregations` remapped onto `filterKey`. The
individual remap helpers are already unit-tested; this anchors the
full round-trip so a refactor that drops `remapAggregationsToFilterKeys`
from the success path fails loud. (claude[bot] #2)
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Committed via a GitHub action: https://github.com/Automattic/jetpack/actions/runs/25703840814
Upstream-Ref: Automattic/jetpack@689e46f1 parent b131158 commit e799a09
39 files changed
Lines changed: 758 additions & 56 deletions
File tree
- build
- search-blocks-editor
- search-blocks
- store
- src/search-blocks
- blocks/filter-checkbox
- store
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
0 commit comments