Completed
Push — add/cli-clean ( c51287...a1b904 )
by
unknown
30:43 queued 20:07
created

Jetpack_Instant_Search::remove_wp_migrated_widgets()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 4
nop 1
dl 0
loc 24
rs 9.2248
c 0
b 0
f 0
1
<?php
2
/**
3
 * Jetpack Search: Instant Front-End Search and Filtering
4
 *
5
 * @since 8.3.0
6
 * @package automattic/jetpack
7
 */
8
9
/**
10
 * Class to load Instant Search experience on the site.
11
 *
12
 * @since 8.3.0
13
 */
14
class Jetpack_Instant_Search extends Jetpack_Search {
15
16
	/**
17
	 * Loads the php for this version of search
18
	 *
19
	 * @since 8.3.0
20
	 */
21
	public function load_php() {
22
		$this->base_load_php();
23
24
		if ( class_exists( 'WP_Customize_Manager' ) ) {
25
			require_once __DIR__ . '/class-jetpack-search-customize.php';
26
			new Jetpack_Search_Customize();
27
		}
28
	}
29
30
	/**
31
	 * Setup the various hooks needed for the plugin to take over search duties.
32
	 *
33
	 * @since 5.0.0
34
	 */
35 View Code Duplication
	public function init_hooks() {
36
		if ( ! is_admin() ) {
37
			add_filter( 'posts_pre_query', array( $this, 'filter__posts_pre_query' ), 10, 2 );
38
			add_action( 'parse_query', array( $this, 'action__parse_query' ), 10, 1 );
39
40
			add_action( 'init', array( $this, 'set_filters_from_widgets' ) );
41
42
			add_action( 'wp_enqueue_scripts', array( $this, 'load_assets' ) );
43
			add_action( 'wp_footer', array( $this, 'print_instant_search_sidebar' ) );
44
		} else {
45
			add_action( 'update_option', array( $this, 'track_widget_updates' ), 10, 3 );
46
		}
47
48
		add_action( 'widgets_init', array( $this, 'register_jetpack_instant_sidebar' ) );
49
		add_action( 'jetpack_deactivate_module_search', array( $this, 'move_search_widgets_to_inactive' ) );
50
	}
51
52
	/**
53
	 * Loads assets for Jetpack Instant Search Prototype featuring Search As You Type experience.
54
	 */
55
	public function load_assets() {
56
		$this->load_assets_with_parameters( '', JETPACK__PLUGIN_FILE );
57
	}
58
59
	/**
60
	 * Loads assets according to parameters provided.
61
	 *
62
	 * @param string $path_prefix - Prefix for assets' relative paths.
63
	 * @param string $plugin_base_path - Base path for use in plugins_url.
64
	 */
65
	public function load_assets_with_parameters( $path_prefix, $plugin_base_path ) {
66
		$polyfill_relative_path = $path_prefix . '_inc/build/instant-search/jp-search-ie11-polyfill-loader.bundle.js';
67
		$script_relative_path   = $path_prefix . '_inc/build/instant-search/jp-search-main.bundle.js';
68
69
		if (
70
			! file_exists( JETPACK__PLUGIN_DIR . $polyfill_relative_path ) ||
71
			! file_exists( JETPACK__PLUGIN_DIR . $script_relative_path )
72
		) {
73
			return;
74
		}
75
76
		$polyfill_version = Jetpack_Search_Helpers::get_asset_version( $polyfill_relative_path );
77
		$polyfill_path    = plugins_url( $polyfill_relative_path, $plugin_base_path );
78
		wp_enqueue_script( 'jetpack-instant-search-ie11', $polyfill_path, array(), $polyfill_version, true );
79
		$polyfill_payload_path = plugins_url(
80
			$path_prefix . '_inc/build/instant-search/jp-search-ie11-polyfill-payload.bundle.js',
81
			$plugin_base_path
82
		);
83
		$this->inject_polyfill_js_options( $polyfill_payload_path );
84
85
		$script_version = Jetpack_Search_Helpers::get_asset_version( $script_relative_path );
86
		$script_path    = plugins_url( $script_relative_path, $plugin_base_path );
87
		wp_enqueue_script( 'jetpack-instant-search', $script_path, array(), $script_version, true );
88
		wp_set_script_translations( 'jetpack-instant-search', 'jetpack' );
89
		$this->load_and_initialize_tracks();
90
		$this->inject_javascript_options();
91
	}
92
93
	/**
94
	 * Passes all options to the JS app.
95
	 */
96
	protected function inject_javascript_options() {
97
		$widget_options = Jetpack_Search_Helpers::get_widgets_from_option();
98
		if ( is_array( $widget_options ) ) {
99
			$widget_options = end( $widget_options );
0 ignored issues
show
Unused Code introduced by
$widget_options is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
100
		}
101
102
		$overlay_widget_ids      = is_active_sidebar( 'jetpack-instant-search-sidebar' ) ?
103
			wp_get_sidebars_widgets()['jetpack-instant-search-sidebar'] : array();
104
		$filters                 = Jetpack_Search_Helpers::get_filters_from_widgets();
105
		$widgets                 = array();
106
		$widgets_outside_overlay = array();
107
		foreach ( $filters as $key => &$filter ) {
108
			$filter['filter_id'] = $key;
109
110
			if ( in_array( $filter['widget_id'], $overlay_widget_ids, true ) ) {
111 View Code Duplication
				if ( ! isset( $widgets[ $filter['widget_id'] ] ) ) {
112
					$widgets[ $filter['widget_id'] ]['filters']   = array();
113
					$widgets[ $filter['widget_id'] ]['widget_id'] = $filter['widget_id'];
114
				}
115
				$widgets[ $filter['widget_id'] ]['filters'][] = $filter;
116
			} else {
117 View Code Duplication
				if ( ! isset( $widgets_outside_overlay[ $filter['widget_id'] ] ) ) {
118
					$widgets_outside_overlay[ $filter['widget_id'] ]['filters']   = array();
119
					$widgets_outside_overlay[ $filter['widget_id'] ]['widget_id'] = $filter['widget_id'];
120
				}
121
				$widgets_outside_overlay[ $filter['widget_id'] ]['filters'][] = $filter;
122
			}
123
		}
124
		unset( $filter );
125
126
		$has_non_search_widgets = false;
127
		foreach ( $overlay_widget_ids as $overlay_widget_id ) {
128
			if ( strpos( $overlay_widget_id, Jetpack_Search_Helpers::FILTER_WIDGET_BASE ) === false ) {
129
				$has_non_search_widgets = true;
130
				break;
131
			}
132
		}
133
134
		$post_type_objs   = get_post_types( array( 'exclude_from_search' => false ), 'objects' );
135
		$post_type_labels = array();
136
		foreach ( $post_type_objs as $key => $obj ) {
137
			$post_type_labels[ $key ] = array(
138
				'singular_name' => $obj->labels->singular_name,
139
				'name'          => $obj->labels->name,
140
			);
141
		}
142
143
		$prefix         = Jetpack_Search_Options::OPTION_PREFIX;
144
		$posts_per_page = (int) get_option( 'posts_per_page' );
145
		if ( ( $posts_per_page > 20 ) || ( $posts_per_page <= 0 ) ) {
146
			$posts_per_page = 20;
147
		}
148
149
		$excluded_post_types   = get_option( $prefix . 'excluded_post_types' ) ? explode( ',', get_option( $prefix . 'excluded_post_types', '' ) ) : array();
150
		$post_types            = array_values(
151
			get_post_types(
152
				array(
153
					'exclude_from_search' => false,
154
					'public'              => true,
155
				)
156
			)
157
		);
158
		$unexcluded_post_types = array_diff( $post_types, $excluded_post_types );
159
		// NOTE: If all post types are being excluded, ignore the option value.
160
		if ( count( $unexcluded_post_types ) === 0 ) {
161
			$excluded_post_types = array();
162
		}
163
164
		$is_wpcom                  = defined( 'IS_WPCOM' ) && IS_WPCOM;
165
		$is_private_site           = '-1' === get_option( 'blog_public' );
166
		$is_jetpack_photon_enabled = method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'photon' );
167
168
		$options = array(
169
			'overlayOptions'        => array(
170
				'colorTheme'      => get_option( $prefix . 'color_theme', 'light' ),
171
				'enableInfScroll' => get_option( $prefix . 'inf_scroll', '1' ) === '1',
172
				'enableSort'      => get_option( $prefix . 'enable_sort', '1' ) === '1',
173
				'highlightColor'  => get_option( $prefix . 'highlight_color', '#FFC' ),
174
				'overlayTrigger'  => get_option( $prefix . 'overlay_trigger', 'immediate' ),
175
				'resultFormat'    => get_option( $prefix . 'result_format', Jetpack_Search_Options::RESULT_FORMAT_MINIMAL ),
176
				'showPoweredBy'   => get_option( $prefix . 'show_powered_by', '1' ) === '1',
177
			),
178
179
			// core config.
180
			'homeUrl'               => home_url(),
181
			'locale'                => str_replace( '_', '-', Jetpack_Search_Helpers::is_valid_locale( get_locale() ) ? get_locale() : 'en_US' ),
182
			'postsPerPage'          => $posts_per_page,
183
			'siteId'                => $this->jetpack_blog_id,
184
			'postTypes'             => $post_type_labels,
185
			'webpackPublicPath'     => plugins_url( '_inc/build/instant-search/', JETPACK__PLUGIN_FILE ),
186
			'isPhotonEnabled'       => ( $is_wpcom || $is_jetpack_photon_enabled ) && ! $is_private_site,
187
188
			// config values related to private site support.
189
			'apiRoot'               => esc_url_raw( rest_url() ),
190
			'apiNonce'              => wp_create_nonce( 'wp_rest' ),
191
			'isPrivateSite'         => $is_private_site,
192
			'isWpcom'               => $is_wpcom,
193
194
			// search options.
195
			'defaultSort'           => get_option( $prefix . 'default_sort', 'relevance' ),
196
			'excludedPostTypes'     => $excluded_post_types,
197
198
			// widget info.
199
			'hasOverlayWidgets'     => count( $overlay_widget_ids ) > 0,
200
			'widgets'               => array_values( $widgets ),
201
			'widgetsOutsideOverlay' => array_values( $widgets_outside_overlay ),
202
			'hasNonSearchWidgets'   => $has_non_search_widgets,
203
		);
204
205
		/**
206
		 * Customize Instant Search Options.
207
		 *
208
		 * @module search
209
		 *
210
		 * @since 7.7.0
211
		 *
212
		 * @param array $options Array of parameters used in Instant Search queries.
213
		 */
214
		$options = apply_filters( 'jetpack_instant_search_options', $options );
215
216
		// Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
217
		wp_add_inline_script( 'jetpack-instant-search', 'var JetpackInstantSearchOptions=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $options ) ) . '"));', 'before' );
218
	}
219
220
	/**
221
	 * Passes options to the polyfill loader script.
222
	 *
223
	 * @param string $polyfill_payload_path - Absolute path to the IE11 polyfill payload.
224
	 */
225
	protected function inject_polyfill_js_options( $polyfill_payload_path ) {
226
		wp_add_inline_script( 'jetpack-instant-search-ie11', 'var JetpackInstantSearchIe11PolyfillPath=decodeURIComponent("' . rawurlencode( $polyfill_payload_path ) . '");', 'before' );
227
	}
228
229
	/**
230
	 * Registers a widget sidebar for Instant Search.
231
	 */
232
	public function register_jetpack_instant_sidebar() {
233
		$args = array(
234
			'name'          => __( 'Jetpack Search Sidebar', 'jetpack' ),
235
			'id'            => 'jetpack-instant-search-sidebar',
236
			'description'   => __( 'Customize the sidebar inside the Jetpack Search overlay', 'jetpack' ),
237
			'class'         => '',
238
			'before_widget' => '<div id="%1$s" class="widget %2$s">',
239
			'after_widget'  => '</div>',
240
			'before_title'  => '<h2 class="widgettitle">',
241
			'after_title'   => '</h2>',
242
		);
243
		register_sidebar( $args );
244
	}
245
246
	/**
247
	 * Prints Instant Search sidebar.
248
	 */
249
	public function print_instant_search_sidebar() {
250
		?>
251
		<div class="jetpack-instant-search__widget-area" style="display: none">
252
			<?php if ( is_active_sidebar( 'jetpack-instant-search-sidebar' ) ) { ?>
253
				<?php dynamic_sidebar( 'jetpack-instant-search-sidebar' ); ?>
254
			<?php } ?>
255
		</div>
256
		<?php
257
	}
258
259
	/**
260
	 * Loads scripts for Tracks analytics library
261
	 */
262
	public function load_and_initialize_tracks() {
263
		wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
264
	}
265
266
	/**
267
	 * Bypass the normal Search query since we will run it with instant search.
268
	 *
269
	 * @since 8.3.0
270
	 *
271
	 * @param array    $posts Current array of posts (still pre-query).
272
	 * @param WP_Query $query The WP_Query being filtered.
273
	 *
274
	 * @return array Array of matching posts.
275
	 */
276
	public function filter__posts_pre_query( $posts, $query ) {
277
		if ( ! $this->should_handle_query( $query ) ) {
278
			// Intentionally not adding the 'jetpack_search_abort' action since this should fire for every request except for search.
279
			return $posts;
280
		}
281
282
		/**
283
		 * Bypass the main query and return dummy data
284
		 *  WP Core doesn't call the set_found_posts and its filters when filtering
285
		 *  posts_pre_query like we do, so need to do these manually.
286
		 */
287
		$query->found_posts   = 1;
288
		$query->max_num_pages = 1;
289
290
		return array();
291
	}
292
293
	/**
294
	 * Run the aggregations API query for any filtering
295
	 *
296
	 * @since 8.3.0
297
	 */
298
	public function action__parse_query() {
299
		if ( ! empty( $this->search_result ) ) {
300
			return;
301
		}
302
303
		if ( is_admin() ) {
304
			return;
305
		}
306
307
		if ( empty( $this->aggregations ) ) {
308
			return;
309
		}
310
311
		jetpack_require_lib( 'jetpack-wpes-query-builder/jetpack-wpes-query-builder' );
312
313
		$builder = new Jetpack_WPES_Query_Builder();
314
		$this->add_aggregations_to_es_query_builder( $this->aggregations, $builder );
315
		$this->search_result = $this->instant_api(
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->instant_api(array...ze' => 0, 'from' => 0)) of type object is incompatible with the declared type array of property $search_result.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
316
			array(
317
				'aggregations' => $builder->build_aggregation(),
318
				'size'         => 0,
319
				'from'         => 0,
320
			)
321
		);
322
	}
323
324
	/**
325
	 * Run an instant search on the WordPress.com public API.
326
	 *
327
	 * @since 8.3.0
328
	 *
329
	 * @param array $args Args conforming to the WP.com v1.3/sites/<blog_id>/search endpoint.
330
	 *
331
	 * @return object|WP_Error The response from the public API, or a WP_Error.
332
	 */
333
	public function instant_api( array $args ) {
334
		global $wp_version;
335
		$start_time = microtime( true );
336
337
		// Cache locally to avoid remote request slowing the page.
338
		$transient_name = 'jetpack_instant_search_cache_' . md5( wp_json_encode( $args ) );
339
		$cache          = get_transient( $transient_name );
340
		if ( false !== $cache ) {
341
			return $cache;
342
		}
343
344
		$service_url = add_query_arg(
345
			$args,
346
			sprintf(
347
				'https://public-api.wordpress.com/rest/v1.3/sites/%d/search',
348
				$this->jetpack_blog_id
349
			)
350
		);
351
352
		$request_args = array(
353
			'timeout'    => 10,
354
			'user-agent' => "WordPress/{$wp_version} | Jetpack/" . constant( 'JETPACK__VERSION' ),
355
		);
356
357
		$request  = wp_remote_get( esc_url_raw( $service_url ), $request_args );
358
		$end_time = microtime( true );
359
360
		if ( is_wp_error( $request ) ) {
361
			return $request;
362
		}
363
364
		$response_code = wp_remote_retrieve_response_code( $request );
365
		$response      = json_decode( wp_remote_retrieve_body( $request ), true );
366
367 View Code Duplication
		if ( ! $response_code || $response_code < 200 || $response_code >= 300 ) {
368
			/**
369
			 * Fires after a search query request has failed
370
			 *
371
			 * @module search
372
			 *
373
			 * @since  5.6.0
374
			 *
375
			 * @param array Array containing the response code and response from the failed search query
376
			 */
377
			do_action(
378
				'failed_jetpack_search_query',
379
				array(
380
					'response_code' => $response_code,
381
					'json'          => $response,
382
				)
383
			);
384
385
			return new WP_Error( 'invalid_search_api_response', 'Invalid response from API - ' . $response_code );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_search_api_response'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
386
		}
387
388
		$took = is_array( $response ) && ! empty( $response['took'] )
389
			? $response['took']
390
			: null;
391
392
		$query = array(
393
			'args'          => $args,
394
			'response'      => $response,
395
			'response_code' => $response_code,
396
			'elapsed_time'  => ( $end_time - $start_time ) * 1000, // Convert from float seconds to ms.
397
			'es_time'       => $took,
398
			'url'           => $service_url,
399
		);
400
401
		/**
402
		 * Fires after a search request has been performed.
403
		 *
404
		 * Includes the following info in the $query parameter:
405
		 *
406
		 * array args Array of Elasticsearch arguments for the search
407
		 * array response Raw API response, JSON decoded
408
		 * int response_code HTTP response code of the request
409
		 * float elapsed_time Roundtrip time of the search request, in milliseconds
410
		 * float es_time Amount of time Elasticsearch spent running the request, in milliseconds
411
		 * string url API url that was queried
412
		 *
413
		 * @module search
414
		 *
415
		 * @since  5.0.0
416
		 * @since  5.8.0 This action now fires on all queries instead of just successful queries.
417
		 *
418
		 * @param array $query Array of information about the query performed
419
		 */
420
		do_action( 'did_jetpack_search_query', $query );
421
422
		// Update local cache.
423
		set_transient( $transient_name, $response, 1 * HOUR_IN_SECONDS );
424
425
		return $response;
426
	}
427
428
	/**
429
	 * Get the raw Aggregation results from the Elasticsearch response.
430
	 *
431
	 * @since  8.4.0
432
	 *
433
	 * @return array Array of Aggregations performed on the search.
434
	 */
435
	public function get_search_aggregations_results() {
436
		if ( empty( $this->search_result ) || is_wp_error( $this->search_result ) || ! isset( $this->search_result['aggregations'] ) ) {
437
			return array();
438
		}
439
440
		return $this->search_result['aggregations'];
441
	}
442
443
	/**
444
	 * Automatically configure necessary settings for instant search
445
	 *
446
	 * @since  8.3.0
447
	 */
448
	public function auto_config_search() {
449
		if ( ! current_user_can( 'edit_theme_options' ) ) {
450
			return;
451
		}
452
453
		// Set default result format to "expanded".
454
		update_option( Jetpack_Search_Options::OPTION_PREFIX . 'result_format', Jetpack_Search_Options::RESULT_FORMAT_EXPANDED );
455
456
		$this->auto_config_excluded_post_types();
457
		$this->auto_config_overlay_sidebar_widgets();
458
		$this->auto_config_woo_result_format();
459
	}
460
461
	/**
462
	 * Automatically copy configured search widgets into the overlay sidebar
463
	 *
464
	 * @since  8.8.0
465
	 */
466
	public function auto_config_overlay_sidebar_widgets() {
467
		global $wp_registered_sidebars;
468
		$sidebars = get_option( 'sidebars_widgets', array() );
469
		$slug     = Jetpack_Search_Helpers::FILTER_WIDGET_BASE;
470
471
		if ( isset( $sidebars['jetpack-instant-search-sidebar'] ) ) {
472
			foreach ( (array) $sidebars['jetpack-instant-search-sidebar'] as $widget_id ) {
473
				if ( 0 === strpos( $widget_id, $slug ) ) {
474
					// Already configured.
475
					return;
476
				}
477
			}
478
		}
479
480
		$has_sidebar           = isset( $wp_registered_sidebars['sidebar-1'] );
481
		$sidebar_id            = false;
482
		$sidebar_searchbox_idx = false;
483
		if ( $has_sidebar ) {
484
			if ( empty( $sidebars['sidebar-1'] ) ) {
485
				// Adding to an empty sidebar is generally a bad idea.
486
				$has_sidebar = false;
487
			}
488
			foreach ( (array) $sidebars['sidebar-1'] as $idx => $widget_id ) {
489
				if ( 0 === strpos( $widget_id, 'search-' ) ) {
490
					$sidebar_searchbox_idx = $idx;
491
				}
492
				if ( 0 === strpos( $widget_id, $slug ) ) {
493
					$sidebar_id = (int) str_replace( Jetpack_Search_Helpers::FILTER_WIDGET_BASE . '-', '', $widget_id );
494
					break;
495
				}
496
			}
497
		}
498
499
		$next_id         = 1;
500
		$widget_opt_name = Jetpack_Search_Helpers::get_widget_option_name();
501
		$widget_options  = get_option( $widget_opt_name, array() );
502
		foreach ( $widget_options as $id => $w ) {
503
			if ( $id >= $next_id ) {
504
				$next_id = $id + 1;
505
			}
506
		}
507
508
		// Copy sidebar settings to overlay.
509
		if ( ( false !== $sidebar_id ) && isset( $widget_options[ $sidebar_id ] ) ) {
510
			$widget_options[ $next_id ] = $widget_options[ $sidebar_id ];
511
			update_option( $widget_opt_name, $widget_options );
512
513
			if ( ! isset( $sidebars['jetpack-instant-search-sidebar'] ) ) {
514
				$sidebars['jetpack-instant-search-sidebar'] = array();
515
			}
516
			array_unshift( $sidebars['jetpack-instant-search-sidebar'], Jetpack_Search_Helpers::build_widget_id( $next_id ) );
517
			update_option( 'sidebars_widgets', $sidebars );
518
519
			return;
520
		}
521
522
		// Configure overlay and sidebar (if it exists).
523
		$preconfig_opts = $this->get_preconfig_widget_options();
524
		if ( ! isset( $sidebars['jetpack-instant-search-sidebar'] ) ) {
525
			$sidebars['jetpack-instant-search-sidebar'] = array();
526
		}
527
		if ( $has_sidebar ) {
528
			$widget_options[ $next_id ] = $preconfig_opts;
529
			if ( false !== $sidebar_searchbox_idx ) {
530
				// Replace Core search box.
531
				$sidebars['sidebar-1'][ $sidebar_searchbox_idx ] = Jetpack_Search_Helpers::build_widget_id( $next_id );
532
			} else {
533
				// Add to top.
534
				array_unshift( $sidebars['sidebar-1'], Jetpack_Search_Helpers::build_widget_id( $next_id ) );
535
			}
536
			$next_id++;
537
		}
538
		$widget_options[ $next_id ] = $preconfig_opts;
539
		array_unshift( $sidebars['jetpack-instant-search-sidebar'], Jetpack_Search_Helpers::build_widget_id( $next_id ) );
540
541
		update_option( $widget_opt_name, $widget_options );
542
		update_option( 'sidebars_widgets', $sidebars );
543
	}
544
545
	/**
546
	 * Autoconfig search by adding filter widgets
547
	 *
548
	 * @since  8.4.0
549
	 *
550
	 * @return array Array of config settings for search widget.
551
	 */
552
	protected function get_preconfig_widget_options() {
553
		$settings = array(
554
			'title'   => '',
555
			'filters' => array(),
556
		);
557
558
		$post_types = get_post_types(
559
			array(
560
				'public'   => true,
561
				'_builtin' => false,
562
			)
563
		);
564
565
		if ( ! empty( $post_types ) ) {
566
			$settings['filters'][] = array(
567
				'name'  => '',
568
				'type'  => 'post_type',
569
				'count' => 5,
570
			);
571
		}
572
573
		// Grab a maximum of 3 taxonomies.
574
		$taxonomies = array_slice(
575
			get_taxonomies(
576
				array(
577
					'public'   => true,
578
					'_builtin' => false,
579
				)
580
			),
581
			0,
582
			3
583
		);
584
585
		foreach ( $taxonomies as $t ) {
586
			$settings['filters'][] = array(
587
				'name'     => '',
588
				'type'     => 'taxonomy',
589
				'taxonomy' => $t,
590
				'count'    => 5,
591
			);
592
		}
593
594
		$settings['filters'][] = array(
595
			'name'     => '',
596
			'type'     => 'taxonomy',
597
			'taxonomy' => 'category',
598
			'count'    => 5,
599
		);
600
601
		$settings['filters'][] = array(
602
			'name'     => '',
603
			'type'     => 'taxonomy',
604
			'taxonomy' => 'post_tag',
605
			'count'    => 5,
606
		);
607
608
		$settings['filters'][] = array(
609
			'name'     => '',
610
			'type'     => 'date_histogram',
611
			'count'    => 5,
612
			'field'    => 'post_date',
613
			'interval' => 'year',
614
		);
615
616
		return $settings;
617
	}
618
619
	/**
620
	 * Automatically configure post types to exclude from one of the search widgets
621
	 *
622
	 * @since  8.8.0
623
	 */
624
	public function auto_config_excluded_post_types() {
625
		$post_types         = get_post_types(
626
			array(
627
				'exclude_from_search' => false,
628
				'public'              => true,
629
			)
630
		);
631
		$enabled_post_types = array();
632
		$widget_options     = get_option( Jetpack_Search_Helpers::get_widget_option_name(), array() );
633
634
		// Prior to Jetpack 8.8, post types were enabled via Jetpack Search widgets rather than disabled via the Customizer.
635
		// To continue supporting post types set up in the old way, we iterate through each Jetpack Search
636
		// widget configuration and append each enabled post type to $enabled_post_types.
637
		foreach ( $widget_options as $widget_option ) {
638
			if ( isset( $widget_option['post_types'] ) && is_array( $widget_option['post_types'] ) ) {
639
				foreach ( $widget_option['post_types'] as $enabled_post_type ) {
640
					$enabled_post_types[ $enabled_post_type ] = $enabled_post_type;
641
				}
642
			}
643
		}
644
645
		if ( ! empty( $enabled_post_types ) ) {
646
			$post_types_to_disable = array_diff( $post_types, $enabled_post_types );
647
			update_option( Jetpack_Search_Options::OPTION_PREFIX . 'excluded_post_types', join( ',', $post_types_to_disable ) );
648
		}
649
	}
650
651
	/**
652
	 * Automatically set result format to 'product' if WooCommerce is installed
653
	 *
654
	 * @since  9.6.0
655
	 */
656
	public function auto_config_woo_result_format() {
657
		if ( ! method_exists( 'Jetpack', 'get_active_plugins' ) ) {
658
			return false;
659
		}
660
661
		// Check if WooCommerce plugin is active (based on https://docs.woocommerce.com/document/create-a-plugin/).
662
		if ( ! in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', Jetpack::get_active_plugins() ), true ) ) {
663
			return false;
664
		}
665
666
		update_option( Jetpack_Search_Options::OPTION_PREFIX . 'result_format', Jetpack_Search_Options::RESULT_FORMAT_PRODUCT );
667
	}
668
}
669