Completed
Push — renovate/copy-webpack-plugin-5... ( 5b2e7f...8235b4 )
by
unknown
52:22 queued 45:34
created

Jetpack_Plugin_Search   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 516
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 516
rs 3.28
c 0
b 0
f 0
wmc 64
lcom 1
cbo 6

21 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 10 2
A __construct() 0 3 1
A start() 0 8 4
A plugin_details() 0 5 2
A register_endpoints() 0 15 1
A can_request() 0 3 1
A is_hint_id() 0 5 2
A dismiss() 0 5 2
A get_dismissed_hints() 0 6 3
A add_to_dismissed_hints() 0 3 1
A should_display_hint() 0 14 6
A load_plugins_search_script() 0 30 1
A get_jetpack_plugin_data() 0 21 3
A get_extra_features() 0 14 1
B inject_jetpack_module_suggestion() 0 87 8
A filter_cards() 0 6 2
A sanitize_search_term() 0 11 1
A by_sorting_option() 0 3 1
A get_upgrade_url() 0 7 2
A get_configure_url() 0 19 6
C insert_module_related_links() 0 76 14

How to fix   Complexity   

Complex Class

Complex classes like Jetpack_Plugin_Search often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Plugin_Search, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Disable direct access and execution.
4
 */
5
if ( ! defined( 'ABSPATH' ) ) {
6
	exit;
7
}
8
9
10
if (
11
	is_admin() &&
12
	Jetpack::is_active() &&
13
	/** This filter is documented in _inc/lib/admin-pages/class.jetpack-react-page.php */
14
	apply_filters( 'jetpack_show_promotions', true ) &&
15
	jetpack_is_psh_active()
16
) {
17
	Jetpack_Plugin_Search::init();
18
}
19
20
// Register endpoints when WP REST API is initialized.
21
add_action( 'rest_api_init', array( 'Jetpack_Plugin_Search', 'register_endpoints' ) );
22
23
/**
24
 * Class that includes cards in the plugin search results when users enter terms that match some Jetpack feature.
25
 * Card can be dismissed and includes a title, description, button to enable the feature and a link for more information.
26
 *
27
 * @since 7.1.0
28
 */
29
class Jetpack_Plugin_Search {
30
31
	static $slug = 'jetpack-plugin-search';
32
33
	public static function init() {
34
		static $instance = null;
35
36
		if ( ! $instance ) {
37
			jetpack_require_lib( 'tracks/client' );
38
			$instance = new Jetpack_Plugin_Search();
39
		}
40
41
		return $instance;
42
	}
43
44
	public function __construct() {
45
		add_action( 'current_screen', array( $this, 'start' ) );
46
	}
47
48
	/**
49
	 * Add actions and filters only if this is the plugin installation screen and it's the first page.
50
	 *
51
	 * @param object $screen
52
	 *
53
	 * @since 7.1.0
54
	 */
55
	public function start( $screen ) {
56
		if ( 'plugin-install' === $screen->base && ( ! isset( $_GET['paged'] ) || 1 == $_GET['paged'] ) ) {
57
			add_action( 'admin_enqueue_scripts', array( $this, 'load_plugins_search_script' ) );
58
			add_filter( 'plugins_api_result', array( $this, 'inject_jetpack_module_suggestion' ), 10, 3 );
59
			add_filter( 'self_admin_url', array( $this, 'plugin_details' ) );
60
			add_filter( 'plugin_install_action_links', array( $this, 'insert_module_related_links' ), 10, 2 );
61
		}
62
	}
63
64
	/**
65
	 * Modify URL used to fetch to plugin information so it pulls Jetpack plugin page.
66
	 *
67
	 * @param string $url URL to load in dialog pulling the plugin page from wporg.
68
	 *
69
	 * @since 7.1.0
70
	 *
71
	 * @return string The URL with 'jetpack' instead of 'jetpack-plugin-search'.
72
	 */
73
	public function plugin_details( $url ) {
74
		return false !== stripos( $url, 'tab=plugin-information&amp;plugin=' . self::$slug )
75
			? 'plugin-install.php?tab=plugin-information&amp;plugin=jetpack&amp;TB_iframe=true&amp;width=600&amp;height=550'
76
			: $url;
77
	}
78
79
	/**
80
	 * Register REST API endpoints.
81
	 *
82
	 * @since 7.1.0
83
	 */
84
	public static function register_endpoints() {
85
		register_rest_route( 'jetpack/v4', '/hints', array(
86
			'methods' => WP_REST_Server::EDITABLE,
87
			'callback' => __CLASS__ . '::dismiss',
88
			'permission_callback' => __CLASS__ . '::can_request',
89
			'args' => array(
90
				'hint' => array(
91
					'default'           => '',
92
					'type'              => 'string',
93
					'required'          => true,
94
					'validate_callback' => __CLASS__ . '::is_hint_id',
95
				),
96
			)
97
		) );
98
	}
99
100
	/**
101
	 * A WordPress REST API permission callback method that accepts a request object and
102
	 * decides if the current user has enough privileges to act.
103
	 *
104
	 * @since 7.1.0
105
	 *
106
	 * @return bool does a current user have enough privileges.
107
	 */
108
	public static function can_request() {
109
		return current_user_can( 'jetpack_admin_page' );
110
	}
111
112
	/**
113
	 * Validates that the ID of the hint to dismiss is a string.
114
	 *
115
	 * @since 7.1.0
116
	 *
117
	 * @param string|bool $value Value to check.
118
	 * @param WP_REST_Request $request The request sent to the WP REST API.
119
	 * @param string $param Name of the parameter passed to endpoint holding $value.
120
	 *
121
	 * @return bool|WP_Error
122
	 */
123
	public static function is_hint_id( $value, $request, $param ) {
124
		return in_array( $value, Jetpack::get_available_modules(), true )
125
			? true
126
			: new WP_Error( 'invalid_param', sprintf( esc_html__( '%s must be an alphanumeric string.', 'jetpack' ), $param ) );
127
	}
128
129
	/**
130
	 * A WordPress REST API callback method that accepts a request object and decides what to do with it.
131
	 *
132
	 * @param WP_REST_Request $request {
133
	 *     Array of parameters received by request.
134
	 *
135
	 *     @type string $hint Slug of card to dismiss.
136
	 * }
137
	 *
138
	 * @since 7.1.0
139
	 *
140
	 * @return bool|array|WP_Error a resulting value or object, or an error.
141
	 */
142
	public static function dismiss( WP_REST_Request $request ) {
143
		return self::add_to_dismissed_hints( $request['hint'] )
144
			? rest_ensure_response( array( 'code' => 'success' ) )
145
			: new WP_Error( 'not_dismissed', esc_html__( 'The card could not be dismissed', 'jetpack' ), array( 'status' => 400 ) );
146
	}
147
148
	/**
149
	 * Returns a list of previously dismissed hints.
150
	 *
151
	 * @since 7.1.0
152
	 *
153
	 * @return array List of dismissed hints.
154
	 */
155
	protected static function get_dismissed_hints() {
156
		$dismissed_hints = Jetpack_Options::get_option( 'dismissed_hints' );
157
		return isset( $dismissed_hints ) && is_array( $dismissed_hints )
158
			? $dismissed_hints
159
			: array();
160
	}
161
162
	/**
163
	 * Save the hint in the list of dismissed hints.
164
	 *
165
	 * @since 7.1.0
166
	 *
167
	 * @param string $hint The hint id, which is a Jetpack module slug.
168
	 *
169
	 * @return bool Whether the card was added to the list and hence dismissed.
170
	 */
171
	protected static function add_to_dismissed_hints( $hint ) {
172
		return Jetpack_Options::update_option( 'dismissed_hints', array_merge( self::get_dismissed_hints(), array( $hint ) ) );
173
	}
174
175
	/**
176
	 * Checks that the module slug passed should be displayed.
177
	 *
178
	 * A feature hint will be displayed if it has not been dismissed before or if 2 or fewer other hints have been dismissed.
179
	 *
180
	 * @since 7.2.1
181
	 *
182
	 * @param string $hint The hint id, which is a Jetpack module slug.
183
	 *
184
	 * @return bool True if $hint should be displayed.
185
	 */
186
	protected function should_display_hint( $hint ) {
187
		$dismissed_hints = $this->get_dismissed_hints();
188
		// If more than 2 hints have been dismissed, then show no more.
189
		if ( 2 < count( $dismissed_hints ) ) {
190
			return false;
191
		}
192
193
		$plan = Jetpack_Plan::get();
194
		if ( isset( $plan['class'] ) && ( 'free' === $plan['class'] || 'personal' === $plan['class'] ) && 'vaultpress' === $hint ) {
195
			return false;
196
		}
197
198
		return ! in_array( $hint, $dismissed_hints, true );
199
	}
200
201
	public function load_plugins_search_script() {
202
		wp_enqueue_script( self::$slug, plugins_url( 'modules/plugin-search/plugin-search.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION, true );
203
		wp_localize_script(
204
			self::$slug,
205
			'jetpackPluginSearch',
206
			array(
207
				'nonce'          => wp_create_nonce( 'wp_rest' ),
208
				'base_rest_url'  => rest_url( '/jetpack/v4' ),
209
				'poweredBy'      => esc_html__( 'by Jetpack (installed)', 'jetpack' ),
210
				'manageSettings' => esc_html__( 'Configure', 'jetpack' ),
211
				'activateModule' => esc_html__( 'Activate Module', 'jetpack' ),
212
				'getStarted'     => esc_html__( 'Get started', 'jetpack' ),
213
				'activated'      => esc_html__( 'Activated', 'jetpack' ),
214
				'activating'     => esc_html__( 'Activating', 'jetpack' ),
215
				'logo'           => 'https://ps.w.org/jetpack/assets/icon.svg?rev=1791404',
216
				'legend'         => esc_html__(
217
					'This suggestion was made by Jetpack, the security and performance plugin already installed on your site.',
218
					'jetpack'
219
				),
220
				'supportText'    => esc_html__(
221
					'Learn more about these suggestions.',
222
					'jetpack'
223
				),
224
				'supportLink'    => 'https://jetpack.com/redirect/?source=plugin-hint-learn-support',
225
				'hideText'       => esc_html__( 'Hide this suggestion', 'jetpack' ),
226
			)
227
		);
228
229
		wp_enqueue_style( self::$slug, plugins_url( 'modules/plugin-search/plugin-search.css', JETPACK__PLUGIN_FILE ) );
230
	}
231
232
	/**
233
	 * Get the plugin repo's data for Jetpack to populate the fields with.
234
	 *
235
	 * @return array|mixed|object|WP_Error
236
	 */
237
	public static function get_jetpack_plugin_data() {
238
		$data = get_transient( 'jetpack_plugin_data' );
239
240
		if ( false === $data || is_wp_error( $data ) ) {
241
			include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
242
			$data = plugins_api( 'plugin_information', array(
243
				'slug' => 'jetpack',
244
				'is_ssl' => is_ssl(),
245
				'fields' => array(
246
					'banners' => true,
247
					'reviews' => true,
248
					'active_installs' => true,
249
					'versions' => false,
250
					'sections' => false,
251
				),
252
			) );
253
			set_transient( 'jetpack_plugin_data', $data, DAY_IN_SECONDS );
254
		}
255
256
		return $data;
257
	}
258
259
	/**
260
	 * Create a list with additional features for those we don't have a module, like Akismet.
261
	 *
262
	 * @since 7.1.0
263
	 *
264
	 * @return array List of features.
265
	 */
266
	public function get_extra_features() {
267
		return array(
268
			'akismet' => array(
269
				'name' => 'Akismet',
270
				'search_terms' => 'akismet, anti-spam, antispam, comments, spam, spam protection, form spam, captcha, no captcha, nocaptcha, recaptcha, phising, google',
271
				'short_description' => esc_html__( 'Keep your visitors and search engines happy by stopping comment and contact form spam with Akismet.', 'jetpack' ),
272
				'requires_connection' => true,
273
				'module' => 'akismet',
274
				'sort' => '16',
275
				'learn_more_button' => 'https://jetpack.com/features/security/spam-filtering/',
276
				'configure_url' => admin_url( 'admin.php?page=akismet-key-config' ),
277
			),
278
		);
279
	}
280
281
	/**
282
	 * Intercept the plugins API response and add in an appropriate card for Jetpack
283
	 */
284
	public function inject_jetpack_module_suggestion( $result, $action, $args ) {
285
		// Looks like a search query; it's matching time
286
		if ( ! empty( $args->search ) ) {
287
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php';
288
			$jetpack_modules_list = array_intersect_key(
289
				array_merge( $this->get_extra_features(), Jetpack_Admin::init()->get_modules() ),
290
				array_flip( array(
291
					'contact-form',
292
					'lazy-images',
293
					'monitor',
294
					'photon',
295
					'photon-cdn',
296
					'protect',
297
					'publicize',
298
					'related-posts',
299
					'sharedaddy',
300
					'akismet',
301
					'vaultpress',
302
					'videopress',
303
					'search',
304
				) )
305
			);
306
			uasort( $jetpack_modules_list, array( $this, 'by_sorting_option' ) );
307
308
			// Record event when user searches for a term over 3 chars (less than 3 is not very useful.)
309
			if ( strlen( $args->search ) >= 3 ) {
310
				JetpackTracking::record_user_event( 'wpa_plugin_search_term', array( 'search_term' => $args->search ) );
311
			}
312
313
			// Lowercase, trim, remove punctuation/special chars, decode url, remove 'jetpack'
314
			$normalized_term = $this->sanitize_search_term( $args->search );
315
316
			$matching_module = null;
317
318
			// Try to match a passed search term with module's search terms
319
			foreach ( $jetpack_modules_list as $module_slug => $module_opts ) {
320
				/*
321
				* Does the site's current plan support the feature?
322
				* We don't use Jetpack_Plan::supports() here because
323
				* that check always returns Akismet as supported,
324
				* since Akismet has a free version.
325
				*/
326
				$current_plan         = Jetpack_Plan::get();
327
				$is_supported_by_plan = in_array( $module_slug, $current_plan['supports'], true );
328
329
				if (
330
					false !== stripos( $module_opts['search_terms'] . ', ' . $module_opts['name'], $normalized_term )
331
					&& $is_supported_by_plan
332
				) {
333
					$matching_module = $module_slug;
334
					break;
335
				}
336
			}
337
338
			if ( isset( $matching_module ) && $this->should_display_hint( $matching_module ) ) {
339
				// Record event when a matching feature is found
340
				JetpackTracking::record_user_event( 'wpa_plugin_search_match_found', array( 'feature' => $matching_module ) );
341
342
				$inject = (array) self::get_jetpack_plugin_data();
343
				$image_url = plugins_url( 'modules/plugin-search/psh', JETPACK__PLUGIN_FILE );
344
				$overrides = array(
345
					'plugin-search' => true, // Helps to determine if that an injected card.
346
					'name' => sprintf(       // Supplement name/description so that they clearly indicate this was added.
347
						esc_html_x( 'Jetpack: %s', 'Jetpack: Module Name', 'jetpack' ),
348
						$jetpack_modules_list[ $matching_module ]['name']
349
					),
350
					'short_description' => $jetpack_modules_list[ $matching_module ]['short_description'],
351
					'requires_connection' => (bool) $jetpack_modules_list[ $matching_module ]['requires_connection'],
352
					'slug'    => self::$slug,
353
					'version' => JETPACK__VERSION,
354
					'icons' => array(
355
						'1x'  => "$image_url-128.png",
356
						'2x'  => "$image_url-256.png",
357
						'svg' => "$image_url.svg",
358
					),
359
				);
360
361
				// Splice in the base module data
362
				$inject = array_merge( $inject, $jetpack_modules_list[ $matching_module ], $overrides );
363
364
				// Add it to the top of the list
365
				$result->plugins = array_filter( $result->plugins, array( $this, 'filter_cards' ) );
366
				array_unshift( $result->plugins, $inject );
367
			}
368
		}
369
		return $result;
370
	}
371
372
	/**
373
	 * Remove cards for Jetpack plugins since we don't want duplicates.
374
	 *
375
	 * @since 7.1.0
376
	 * @since 7.2.0 Only remove Jetpack.
377
	 *
378
	 * @param array|object $plugin
379
	 *
380
	 * @return bool
381
	 */
382
	function filter_cards( $plugin ) {
383
		// Take in account that before WordPress 5.1, the list of plugins is an array of objects.
384
		// With WordPress 5.1 the list of plugins is an array of arrays.
385
		$slug = is_array( $plugin ) ? $plugin['slug'] : $plugin->slug;
386
		return ! in_array( $slug, array( 'jetpack' ), true );
387
	}
388
389
	/**
390
	 * Take a raw search query and return something a bit more standardized and
391
	 * easy to work with.
392
	 *
393
	 * @param  String $term The raw search term
394
	 * @return String A simplified/sanitized version.
395
	 */
396
	private function sanitize_search_term( $term ) {
397
		$term = strtolower( urldecode( $term ) );
398
399
		// remove non-alpha/space chars.
400
		$term = preg_replace( '/[^a-z ]/', '', $term );
401
402
		// remove strings that don't help matches.
403
		$term = trim( str_replace( array( 'jetpack', 'jp', 'free', 'wordpress' ), '', $term ) );
404
405
		return $term;
406
	}
407
408
	/**
409
	 * Callback function to sort the array of modules by the sort option.
410
	 */
411
	private function by_sorting_option( $m1, $m2 ) {
412
		return $m1['sort'] - $m2['sort'];
413
	}
414
415
	/**
416
	 * Builds a URL to purchase and upgrade inserting the site fragment and the affiliate code if it exists.
417
	 *
418
	 * @param string $feature Module slug (or forged one for extra features).
419
	 *
420
	 * @since 7.1.0
421
	 *
422
	 * @return string URL to upgrade.
423
	 */
424
	private function get_upgrade_url( $feature ) {
425
		$site_raw_url = Jetpack::build_raw_urls( get_home_url() );
426
		$affiliateCode = Jetpack_Affiliate::init()->get_affiliate_code();
427
		$user = wp_get_current_user()->ID;
428
		return "https://jetpack.com/redirect/?source=plugin-hint-upgrade-$feature&site=$site_raw_url&u=$user" .
429
		       ( $affiliateCode ? "&aff=$affiliateCode" : '' );
430
	}
431
432
	/**
433
	 * Modify the URL to the feature settings, for example Publicize.
434
	 * Sharing is included here because while we still have a page in WP Admin,
435
	 * we prefer to send users to Calypso.
436
	 *
437
	 * @param string $feature
438
	 * @param string $configure_url
439
	 *
440
	 * @return string
441
	 * @since 7.1.0
442
	 *
443
	 */
444
	private function get_configure_url( $feature, $configure_url ) {
445
		$siteFragment = Jetpack::build_raw_urls( get_home_url() );
446
		switch ( $feature ) {
447
			case 'sharing':
448
			case 'publicize':
449
				$configure_url = "https://wordpress.com/sharing/$siteFragment";
450
				break;
451
			case 'seo-tools':
452
				$configure_url = "https://wordpress.com/settings/traffic/$siteFragment#seo";
453
				break;
454
			case 'google-analytics':
455
				$configure_url = "https://wordpress.com/settings/traffic/$siteFragment#analytics";
456
				break;
457
			case 'wordads':
458
				$configure_url = "https://wordpress.com/ads/settings/$siteFragment";
459
				break;
460
		}
461
		return $configure_url;
462
	}
463
464
	/**
465
	 * Put some more appropriate links on our custom result cards.
466
	 */
467
	public function insert_module_related_links( $links, $plugin ) {
468
		if ( self::$slug !== $plugin['slug'] ) {
469
			return $links;
470
		}
471
472
		// By the time this filter is applied, self_admin_url was already applied and we don't need it anymore.
473
		remove_filter( 'self_admin_url', array( $this, 'plugin_details' ) );
474
475
		$links = array();
476
477
		if ( 'akismet' === $plugin['module'] || 'vaultpress' === $plugin['module'] ) {
478
			$links['jp_get_started'] = '<a
479
				id="plugin-select-settings"
480
				class="jetpack-plugin-search__primary jetpack-plugin-search__get-started button"
481
				href="https://jetpack.com/redirect/?source=plugin-hint-learn-' . $plugin['module'] . '"
482
				data-module="' . esc_attr( $plugin['module'] ) . '"
483
				data-track="get_started"
484
				>' . esc_html__( 'Get started', 'jetpack' ) . '</a>';
485
			// Jetpack installed, active, feature not enabled; prompt to enable.
486
		} elseif (
487
			current_user_can( 'jetpack_activate_modules' ) &&
488
			! Jetpack::is_module_active( $plugin['module'] ) &&
489
			Jetpack_Plan::supports( $plugin['module'] )
490
		) {
491
			$links[] = '<button
492
					id="plugin-select-activate"
493
					class="jetpack-plugin-search__primary button"
494
					data-module="' . esc_attr( $plugin['module'] ) . '"
495
					data-configure-url="' . esc_url( $this->get_configure_url( $plugin['module'], $plugin['configure_url'] ) ) . '"
496
					> ' . esc_html__( 'Enable', 'jetpack' ) . '</button>';
497
498
			// Jetpack installed, active, feature enabled; link to settings.
499
		} elseif (
500
			! empty( $plugin['configure_url'] ) &&
501
			current_user_can( 'jetpack_configure_modules' ) &&
502
			Jetpack::is_module_active( $plugin['module'] ) &&
503
			/** This filter is documented in class.jetpack-admin.php */
504
			apply_filters( 'jetpack_module_configurable_' . $plugin['module'], false )
505
		) {
506
			$links[] = '<a
507
				id="plugin-select-settings"
508
				class="jetpack-plugin-search__primary button jetpack-plugin-search__configure"
509
				href="' . esc_url( $this->get_configure_url( $plugin['module'], $plugin['configure_url'] ) ) . '"
510
				data-module="' . esc_attr( $plugin['module'] ) . '"
511
				data-track="configure"
512
				>' . esc_html__( 'Configure', 'jetpack' ) . '</a>';
513
			// Module is active, doesn't have options to configure
514
		} elseif ( Jetpack::is_module_active( $plugin['module'] ) ) {
515
			$links['jp_get_started'] = '<a
516
				id="plugin-select-settings"
517
				class="jetpack-plugin-search__primary jetpack-plugin-search__get-started button"
518
				href="https://jetpack.com/redirect/?source=plugin-hint-learn-' . $plugin['module'] . '"
519
				data-module="' . esc_attr( $plugin['module'] ) . '"
520
				data-track="get_started"
521
				>' . esc_html__( 'Get started', 'jetpack' ) . '</a>';
522
		}
523
524
		// Add link pointing to a relevant doc page in jetpack.com only if the Get started button isn't displayed.
525
		if ( ! empty( $plugin['learn_more_button'] ) && ! isset( $links['jp_get_started'] ) ) {
526
			$links[] = '<a
527
				class="jetpack-plugin-search__learn-more"
528
				href="' . esc_url( $plugin['learn_more_button'] ) . '"
529
				target="_blank"
530
				data-module="' . esc_attr( $plugin['module'] ) . '"
531
				data-track="learn_more"
532
				>' . esc_html__( 'Learn more', 'jetpack' ) . '</a>';
533
		}
534
535
		// Dismiss link
536
		$links[] = '<a
537
			class="jetpack-plugin-search__dismiss"
538
			data-module="' . esc_attr( $plugin['module'] ) . '"
539
			>' . esc_html__( 'Hide this suggestion', 'jetpack' ) . '</a>';
540
541
		return $links;
542
	}
543
544
}
545
546
/**
547
 * Master control that checks if Plugin search hints is active.
548
 *
549
 * @since 7.1.1
550
 *
551
 * @return bool True if PSH is active.
552
 */
553
function jetpack_is_psh_active() {
554
	// false means unset, 1 means active, 0 means inactive.
555
	$status = get_transient( 'jetpack_psh_status' );
556
557
	if ( false === $status ) {
558
		$error = false;
559
		$status = jetpack_get_remote_is_psh_active( $error );
560
		set_transient(
561
			'jetpack_psh_status',
562
			// Cache as int
563
			(int) $status,
564
			// If there was an error, still cache but for a shorter time
565
			( $error ? 5 : 15 ) * MINUTE_IN_SECONDS
566
		);
567
	}
568
569
	return (bool) $status;
570
}
571
572
/**
573
 * Makes remote request to determine if Plugin search hints is active.
574
 *
575
 * @since 7.1.1
576
 * @internal
577
 *
578
 * @param bool &$error Did the remote request result in an error?
579
 * @return bool True if PSH is active.
580
 */
581
function jetpack_get_remote_is_psh_active( &$error ) {
582
	$response = wp_remote_get( 'https://jetpack.com/psh-status/' );
583
	if ( is_wp_error( $response ) ) {
584
		$error = true;
585
		return true;
586
	}
587
588
	$body = wp_remote_retrieve_body( $response );
589
	if ( empty( $body ) ) {
590
		$error = true;
591
		return true;
592
	}
593
594
	$json = json_decode( $body );
595
	if ( ! isset( $json->active ) ) {
596
		$error = true;
597
		return true;
598
	}
599
600
	$error = false;
601
	return (bool) $json->active;
602
}
603