Skip to content

Commit e799a09

Browse files
kangzjmatticbot
authored andcommitted
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@689e46f
1 parent b131158 commit e799a09

39 files changed

Lines changed: 758 additions & 56 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ This is an alpha version! The changes listed here are not final.
3232
- Search blocks: Extend the chips display style option to the Date and Product Attribute filter blocks.
3333
- Search Blocks: foundation data-plane primitives for the upcoming WC product filter blocks. Adds `actions.setPriceRange(min, max)`, extends `actions.clearFilters` to also clear the price range, and folds `priceRange` into `state.hasActiveFilters` so the active-filters wrapper stays visible on a price-only deep link.
3434
- Search Blocks: Render highlighted content snippet under the result title in the expanded card layout.
35+
- Search Blocks: whitelist supported taxonomies in the Custom Taxonomy filter; the jetpack_search_custom_taxonomy_map filter now also mirrors mapped taxonomies onto reserved jetpack-search-tagN slot taxonomies at write time so Jetpack Search can index them, and routes both the aggregation key and field through the slot at query time.
3536
- Search dashboard: new feature-selection UI gated behind the jetpack_search_blocks_enabled filter.
3637

3738
### Changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => 'd47dddf555701219ebaf');
1+
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-primitives'), 'version' => 'dc81e6515e103111830c');

build/search-blocks-editor/register-blocks.js

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '9041ddc1c773b3d264b2', 'type' => 'module');
1+
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '9a31bde8887cc70153f3', 'type' => 'module');

build/search-blocks/active-filters.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '6317598e9ca41cb09993', 'type' => 'module');
1+
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '6eafdd57a6eaba923302', 'type' => 'module');

build/search-blocks/clear-filters.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => 'c34de3e68a142e2bbc51', 'type' => 'module');
1+
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '252f9d9d27f230ed50f1', 'type' => 'module');

build/search-blocks/filter-checkbox.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '579b2e58eba3275506b3', 'type' => 'module');
1+
<?php return array('dependencies' => array('@wordpress/interactivity'), 'version' => '1227c6eaebf69770966b', 'type' => 'module');

0 commit comments

Comments
 (0)