PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-search/src/dashboard/class-initial-state.php +282 -15 13.3.3 → 16.3 View file →
@@ -46,9 +46,9 @@
46 46 *
47 47 * @return string - JS string.
48 48 */
49 49 public function render() {
50 - return 'var JETPACK_SEARCH_DASHBOARD_INITIAL_STATE=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $this->get_initial_state() ) ) . '"));';
50 + return 'var JETPACK_SEARCH_DASHBOARD_INITIAL_STATE=' . wp_json_encode( $this->get_initial_state(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';';
51 51 }
52 52
53 53 /**
54 54 * Get the initial state data.
@@ -57,28 +57,100 @@
57 57 */
58 58 public function get_initial_state() {
59 59 return array(
60 60 'siteData' => array(
61 - 'WP_API_root' => esc_url_raw( rest_url() ),
62 - 'wpcomOriginApiUrl' => $this->get_wp_api_root(),
63 - 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
64 - 'registrationNonce' => wp_create_nonce( 'jetpack-registration-nonce' ),
65 - 'purchaseToken' => $this->get_purchase_token(),
61 + 'WP_API_root' => esc_url_raw( rest_url() ),
62 + 'wpcomOriginApiUrl' => $this->get_wp_api_root(),
63 + 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
64 + 'registrationNonce' => wp_create_nonce( 'jetpack-registration-nonce' ),
65 + 'purchaseToken' => $this->get_purchase_token(),
66 66 /**
67 67 * Whether promotions are visible or not.
68 68 *
69 69 * @param bool $are_promotions_active Status of promotions visibility. True by default.
70 70 */
71 - 'showPromotions' => apply_filters( 'jetpack_show_promotions', true ),
72 - 'adminUrl' => esc_url( admin_url() ),
73 - 'blogId' => Jetpack_Options::get_option( 'id', 0 ),
74 - 'version' => Package::VERSION,
75 - 'calypsoSlug' => ( new Status() )->get_site_suffix(),
76 - 'title' => get_bloginfo( 'name' ),
77 - 'postTypes' => $this->get_post_types_with_labels(),
78 - 'isWpcom' => Helper::is_wpcom(),
71 + 'showPromotions' => apply_filters( 'jetpack_show_promotions', true ),
72 + 'adminUrl' => esc_url( admin_url() ),
73 + 'readerChatGuidelinesUrl' => $this->get_reader_chat_guidelines_url(),
74 + 'aiAgentAccessAvailable' => $this->is_ai_agent_access_available(),
75 + 'aiAgentAccessGuidelinesUrl' => $this->get_ai_agent_access_guidelines_url(),
76 + 'blogId' => Jetpack_Options::get_option( 'id', 0 ),
77 + 'version' => Package::VERSION,
78 + 'calypsoSlug' => ( new Status() )->get_site_suffix(),
79 + 'title' => get_bloginfo( 'name' ),
80 + 'postTypes' => $this->get_post_types_with_labels(),
81 + 'isWpcom' => Helper::is_wpcom(),
79 82 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
80 - 'isPlanJustUpgraded' => isset( $_GET['just_upgraded'] ) && wp_unslash( $_GET['just_upgraded'] ),
83 + 'isPlanJustUpgraded' => isset( $_GET['just_upgraded'] ) && wp_unslash( $_GET['just_upgraded'] ),
84 + /**
85 + * Whether the Jetpack Search 3.0 Interactivity API blocks are enabled.
86 + * Mirrors the `jetpack_search_blocks_enabled` server-side filter so the
87 + * dashboard React app can gate the new feature-selection UI on the
88 + * same flag the back end uses to register the blocks themselves.
89 + */
90 + 'searchBlocksEnabled' => (bool) apply_filters( 'jetpack_search_blocks_enabled', true ),
91 + /**
92 + * Whether the experimental blocks-powered Overlay search experience
93 + * is available in the Experience Selector. Mirrors the
94 + * `jetpack_search_overlay_block_template_enabled` server-side filter
95 + * so the dashboard React app can gate the new card on the same flag
96 + * the back end uses to enable the runtime swap. Defaults to true so
97 + * the Beta card ships to every site; operators that pin the filter
98 + * to false fall back to the original four-card selector.
99 + */
100 + 'blockOverlayEnabled' => (bool) apply_filters( 'jetpack_search_overlay_block_template_enabled', true ),
101 + /**
102 + * Editor affordances for the experimental blocks-powered overlay.
103 + * Surfaces in the new Overlay search card so admins can edit the
104 + * rendered template via the standard block editor on `post.php` —
105 + * works on both block and classic themes. URLs are null for any
106 + * visitor without `manage_options`; the action handlers also
107 + * enforce that capability server-side.
108 + */
109 + 'blockTemplateOverlay' => $this->get_block_template_overlay_config(),
110 + /**
111 + * Same as `blockTemplateOverlay` but for the WooCommerce product
112 + * variant of the overlay — the Overlay card surfaces a second
113 + * "Edit the product Search overlay" entry on Woo stores so the
114 + * product-search overlay template is customizable too.
115 + */
116 + 'productOverlayTemplate' => $this->get_product_overlay_template_config(),
117 + /**
118 + * Editor affordances for the classic-theme search-template
119 + * singleton CPT. The Embedded card surfaces these on
120 + * classic themes (which can't reach the Site Editor) so
121 + * admins still get an "Edit search template" entry — same
122 + * shape and same React link as `blockTemplateOverlay`.
123 + */
124 + 'searchTemplate' => $this->get_search_template_config(),
125 + /**
126 + * Same as `searchTemplate` but for the WooCommerce product
127 + * search shim — the `WooCommerceProductSearchControl`
128 + * surfaces this on classic themes so the "Edit the product
129 + * search template" link routes to `post.php` on the hidden
130 + * CPT instead of a useless Site Editor URL.
131 + */
132 + 'productSearchTemplate' => $this->get_product_search_template_config(),
133 + // Gates the WooCommerce Product Search control to stores.
134 + 'isWooCommerceActive' => Search_Blocks::woocommerce_blocks_enabled(),
135 + /**
136 + * Active theme stylesheet — used by the experience-selector to deep-link
137 + * the "Edit search template" action to the right Site Editor entry
138 + * (`?p=/wp_template/<stylesheet>//jetpack-search`).
139 + */
140 + 'activeThemeStylesheet' => get_stylesheet(),
141 + /**
142 + * Whether the active theme is a block theme. Embedded itself
143 + * works on every theme — block themes through the FSE
144 + * `search_template_hierarchy` route, classic themes through
145 + * the singleton-CPT shim — but the Embedded card's
146 + * customization affordances diverge: block themes get the
147 + * Site-Editor entry points ("Edit search template" / "Insert
148 + * pattern"), classic themes get the block-editor-on-a-hidden-
149 + * CPT path via `searchTemplate`. This flag is the dashboard's
150 + * branch selector for which to render.
151 + */
152 + 'themeSupportsBlocks' => wp_is_block_theme(),
81 153 ),
82 154 'userData' => array(
83 155 'currentUser' => $this->current_user_data(),
84 156 ),
@@ -84,8 +156,9 @@
84 156 ),
85 157 'jetpackSettings' => array(
86 158 'search' => $this->module_control->is_active(),
87 159 'instant_search_enabled' => $this->module_control->is_instant_search_enabled(),
160 + 'experience' => $this->module_control->get_experience(),
88 161 ),
89 162 'features' => array_map(
90 163 'sanitize_text_field',
91 164 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
@@ -104,8 +177,202 @@
104 177 return esc_url_raw( rest_url() );
105 178 }
106 179 // First party API prefix for WPCOM.
107 180 return esc_url_raw( site_url( '/wp-json/wpcom-origin/' ) );
181 + }
182 +
183 + /**
184 + * Get the Reader Chat guidelines admin page URL when it is registered.
185 + *
186 + * The guidelines page is controlled outside Jetpack. Returning an empty
187 + * URL lets the dashboard hide the link when that experiment is unavailable.
188 + *
189 + * @return string Guidelines admin URL, or an empty string when unavailable.
190 + */
191 + protected function get_reader_chat_guidelines_url() {
192 + return $this->get_guidelines_url();
193 + }
194 +
195 + /**
196 + * Get the AI Agent Access guidelines admin page URL when it is registered.
197 + *
198 + * The guidelines page is controlled outside Jetpack. Returning an empty
199 + * URL lets the dashboard hide the link when that page is unavailable.
200 + *
201 + * @return string Guidelines admin URL, or an empty string when unavailable.
202 + */
203 + protected function get_ai_agent_access_guidelines_url() {
204 + return $this->get_guidelines_url();
205 + }
206 +
207 + /**
208 + * Get the Guidelines admin page URL when it is registered.
209 + *
210 + * @return string Guidelines admin URL, or an empty string when unavailable.
211 + */
212 + protected function get_guidelines_url() {
213 + if ( ! function_exists( 'menu_page_url' ) ) {
214 + return '';
215 + }
216 +
217 + return esc_url_raw( menu_page_url( 'guidelines-wp-admin', false ) );
218 + }
219 +
220 + /**
221 + * Check whether the AI Agent Access toggle should be available.
222 + *
223 + * Private sites are not eligible because external AI agents cannot read
224 + * their public content.
225 + *
226 + * @return bool
227 + */
228 + protected function is_ai_agent_access_available() {
229 + return ! $this->is_private_site();
230 + }
231 +
232 + /**
233 + * Build the block-template overlay editor config exposed to the dashboard.
234 + *
235 + * `enabled` mirrors `Search_Blocks::is_block_template_overlay_enabled()`
236 + * — true only when the user is currently *on* the blocks Overlay arm.
237 + * The editor URLs, by contrast, are gated on the operator filter alone
238 + * so admins can edit the template from the Beta card even before
239 + * activating it (otherwise the React store, computed at page load
240 + * before any switch, would carry a null `editorUrl` and the link would
241 + * become a no-op once the card flipped to Active without a refresh).
242 + *
243 + * @return array{enabled: bool, editorUrl: string|null, postType: string|null, isCustomized: bool}
244 + */
245 + protected function get_block_template_overlay_config(): array {
246 + return $this->build_singleton_template_config(
247 + Search_Blocks::is_block_template_overlay_enabled(),
248 + Search_Blocks::is_block_template_overlay_filter_on() && current_user_can( 'manage_options' ),
249 + Overlay_Template::class
250 + );
251 + }
252 +
253 + /**
254 + * Build the product-overlay editor config exposed to the dashboard.
255 + * Counterpart of `get_block_template_overlay_config()` for the WooCommerce
256 + * product variant. `enabled` adds the override-on + WC check on top of the
257 + * overlay-arm gate — it signals the product overlay's front-end render path
258 + * is actually wired. `$can_edit` mirrors the init gate: the editable CPT
259 + * only registers on Woo stores with the overlay filter on (see
260 + * `Search_Blocks::init()`), so exposing the editor URL off Woo would produce
261 + * a link that silently does nothing.
262 + *
263 + * @return array{enabled: bool, editorUrl: string|null, postType: string|null, isCustomized: bool}
264 + */
265 + protected function get_product_overlay_template_config(): array {
266 + $wc_enabled = Search_Blocks::woocommerce_blocks_enabled();
267 + return $this->build_singleton_template_config(
268 + Search_Blocks::is_block_template_overlay_enabled()
269 + && $wc_enabled
270 + && Search_Blocks::woocommerce_search_template_override_enabled(),
271 + Search_Blocks::is_block_template_overlay_filter_on()
272 + && $wc_enabled
273 + && current_user_can( 'manage_options' ),
274 + Product_Overlay_Template::class
275 + );
276 + }
277 +
278 + /**
279 + * Build the classic-theme search-template editor config exposed to the
280 + * dashboard. Counterpart of `get_block_template_overlay_config()` —
281 + * same `{enabled, editorUrl, postType, isCustomized}` shape and the
282 + * same "expose URLs before activation" rule: admins can edit the
283 + * template from the Embedded card on any classic theme, even before
284 + * actually switching to Embedded.
285 + *
286 + * @return array{enabled: bool, editorUrl: string|null, postType: string|null, isCustomized: bool}
287 + */
288 + protected function get_search_template_config(): array {
289 + $is_classic = ! wp_is_block_theme();
290 + return $this->build_singleton_template_config(
291 + $is_classic && Module_Control::EXPERIENCE_EMBEDDED === $this->module_control->get_experience(),
292 + $is_classic && current_user_can( 'manage_options' ),
293 + Search_Template::class
294 + );
295 + }
296 +
297 + /**
298 + * Build the classic-theme product-search-template editor config exposed
299 + * to the dashboard. Counterpart of `get_search_template_config()` for the
300 + * WooCommerce product shim — same `{enabled, editorUrl, postType, isCustomized}`
301 + * shape. `enabled` adds the override-on check on top of the classic +
302 + * server-rendered-experience gate — it signals "the front-end render path
303 + * is actually wired", distinct from "the override toggle is on" in
304 + * `jetpackSettings.override_woocommerce_search_template`. `$can_edit`
305 + * mirrors the init gate: `Product_Search_Template::init()` (in
306 + * `Search_Blocks::init()`) registers `maybe_handle_editor_request` on
307 + * classic Embedded **and** classic Inline (both route the product shim),
308 + * so the editor URL surfaces for both — but not on a block theme, where
309 + * the Site Editor link is used instead.
310 + *
311 + * @return array{enabled: bool, editorUrl: string|null, postType: string|null, isCustomized: bool}
312 + */
313 + protected function get_product_search_template_config(): array {
314 + $experience = $this->module_control->get_experience();
315 + $is_classic = ! wp_is_block_theme();
316 + // Embedded and Inline both route the classic product shim, so both
317 + // expose the CPT editor.
318 + $is_classic_product_override = $is_classic
319 + && in_array(
320 + $experience,
321 + array( Module_Control::EXPERIENCE_EMBEDDED, Module_Control::EXPERIENCE_INLINE ),
322 + true
323 + );
324 + return $this->build_singleton_template_config(
325 + $is_classic_product_override && Search_Blocks::woocommerce_search_template_override_enabled(),
326 + $is_classic_product_override && current_user_can( 'manage_options' ),
327 + Product_Search_Template::class
328 + );
329 + }
330 +
331 + /**
332 + * Shared assembly for a {@see Singleton_Template_Cpt}-backed editor
333 + * config block. Both `blockTemplateOverlay` and `searchTemplate` ship
334 + * the same `{enabled, editorUrl, postType, isCustomized}` shape to
335 + * the React dashboard. The `$enabled` and `$can_edit` flags are
336 + * intentionally separate: the card lights up as "active" on
337 + * `$enabled`, but the URLs / `postType` surface as soon as `$can_edit`
338 + * is true so admins can pre-customize the template before activating
339 + * the experience — without forcing a page reload after the switch.
340 + *
341 + * `postType` (rather than a pre-built REST path) is what the dashboard
342 + * needs: the React "Restore default" handler builds the
343 + * `${wpcomOriginApiUrl}jetpack/v4/search/templates/<post_type>` URL
344 + * itself, which is how the request reaches the right route on wpcom
345 + * Simple sites (the local /wp-json/ surface there doesn't expose
346 + * Jetpack routes). The handler re-checks `manage_options` server-side,
347 + * so omitting the URLs / postType here just keeps the wire payload
348 + * tight for non-admins.
349 + *
350 + * @param bool $enabled Whether the CPT-backed route is currently live on the site.
351 + * @param bool $can_edit Whether to expose the editor URL + postType (operator-level gate + capability).
352 + * @param string $cpt_class Concrete `Singleton_Template_Cpt` subclass to query.
353 + * @return array{enabled: bool, editorUrl: string|null, postType: string|null, isCustomized: bool}
354 + */
355 + protected function build_singleton_template_config( bool $enabled, bool $can_edit, string $cpt_class ): array {
356 + return array(
357 + 'enabled' => $enabled,
358 + 'editorUrl' => $can_edit ? $cpt_class::get_editor_url() : null,
359 + 'postType' => $can_edit ? $cpt_class::POST_TYPE : null,
360 + 'isCustomized' => $can_edit && $cpt_class::is_customized(),
361 + );
362 + }
363 +
364 + /**
365 + * Check whether the current site is private.
366 + *
367 + * @return bool
368 + */
369 + protected function is_private_site() {
370 + if ( function_exists( 'is_private_blog' ) ) {
371 + return (bool) \is_private_blog();
372 + }
373 +
374 + return -1 === (int) get_option( 'blog_public', 1 );
108 375 }
109 376
110 377 /**
111 378 * Gather data about the current user.