Completed
Push — update/subscriptions-block ( 70d499...11a394 )
by Jeremy
07:42
created

Jetpack_Cxn_Tests::test__full_sync_health()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 0
dl 0
loc 64
rs 8.4743
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Collection of tests to run on the Jetpack connection locally.
4
 *
5
 * @package Jetpack
6
 */
7
8
use Automattic\Jetpack\Connection\Client;
9
use Automattic\Jetpack\Status;
10
use Automattic\Jetpack\Connection\Utils as Connection_Utils;
11
use Automattic\Jetpack\Sync\Modules;
12
use Automattic\Jetpack\Sync\Settings as Sync_Settings;
13
use Automattic\Jetpack\Sync\Health as Sync_Health;
14
use Automattic\Jetpack\Sync\Sender as Sync_Sender;
15
use Automattic\Jetpack\Redirect;
16
17
/**
18
 * Class Jetpack_Cxn_Tests contains all of the actual tests.
19
 */
20
class Jetpack_Cxn_Tests extends Jetpack_Cxn_Test_Base {
21
22
	/**
23
	 * Jetpack_Cxn_Tests constructor.
24
	 */
25
	public function __construct() {
26
		parent::__construct();
27
28
		$methods = get_class_methods( 'Jetpack_Cxn_Tests' );
29
30
		foreach ( $methods as $method ) {
31
			if ( false === strpos( $method, 'test__' ) ) {
32
				continue;
33
			}
34
			$this->add_test( array( $this, $method ), $method, 'direct' );
35
		}
36
37
		/**
38
		 * Fires after loading default Jetpack Connection tests.
39
		 *
40
		 * @since 7.1.0
41
		 * @since 8.3.0 Passes the Jetpack_Cxn_Tests instance.
42
		 */
43
		do_action( 'jetpack_connection_tests_loaded', $this );
44
45
		/**
46
		 * Determines if the WP.com testing suite should be included.
47
		 *
48
		 * @since 7.1.0
49
		 * @since 8.1.0 Default false.
50
		 *
51
		 * @param bool $run_test To run the WP.com testing suite. Default false.
52
		 */
53
		if ( apply_filters( 'jetpack_debugger_run_self_test', false ) ) {
54
			/**
55
			 * Intentionally added last as it checks for an existing failure state before attempting.
56
			 * Generally, any failed location condition would result in the WP.com check to fail too, so
57
			 * we will skip it to avoid confusing error messages.
58
			 *
59
			 * Note: This really should be an 'async' test.
60
			 */
61
			$this->add_test( array( $this, 'last__wpcom_self_test' ), 'test__wpcom_self_test', 'direct' );
62
		}
63
	}
64
65
	/**
66
	 * Helper function to look up the expected master user and return the local WP_User.
67
	 *
68
	 * @return WP_User Jetpack's expected master user.
69
	 */
70
	protected function helper_retrieve_local_master_user() {
71
		$master_user = Jetpack_Options::get_option( 'master_user' );
72
		return new WP_User( $master_user );
73
	}
74
75
	/**
76
	 * Is Jetpack even connected and supposed to be talking to WP.com?
77
	 */
78
	protected function helper_is_jetpack_connected() {
79
		return ( Jetpack::is_active() && ! ( new Status() )->is_development_mode() );
80
	}
81
82
	/**
83
	 * Returns a support url based on development mode.
84
	 */
85
	protected function helper_get_support_url() {
86
		return Jetpack::is_development_version()
87
			? Redirect::get_url( 'jetpack-contact-support-beta-group' )
88
			: Redirect::get_url( 'jetpack-contact-support' );
89
	}
90
91
	/**
92
	 * Gets translated support text.
93
	 */
94
	protected function helper_get_support_text() {
95
		return __( 'Please contact Jetpack support.', 'jetpack' );
96
	}
97
98
	/**
99
	 * Gets translated text to enable outbound requests.
100
	 *
101
	 * @param string $protocol Either 'HTTP' or 'HTTPS'.
102
	 *
103
	 * @return string The translated text.
104
	 */
105
	protected function helper_enable_outbound_requests( $protocol ) {
106
		return sprintf(
107
			/* translators: %1$s - request protocol, either http or https */
108
			__(
109
				'Your server did not successfully connect to the Jetpack server using %1$s
110
				Please ask your hosting provider to confirm your server can make outbound requests to jetpack.com.',
111
				'jetpack'
112
			),
113
			$protocol
114
		);
115
	}
116
117
	/**
118
	 * Returns 30 for use with a filter.
119
	 *
120
	 * To allow time for WP.com to run upstream testing, this function exists to increase the http_request_timeout value
121
	 * to 30.
122
	 *
123
	 * @return int 30
124
	 */
125
	public static function increase_timeout() {
126
		return 30; // seconds.
127
	}
128
129
	/**
130
	 * Test if Jetpack is connected.
131
	 */
132
	protected function test__check_if_connected() {
133
		$name = __FUNCTION__;
134
		if ( $this->helper_is_jetpack_connected() ) {
135
			$result = self::passing_test(
136
				array(
137
					'name'             => $name,
138
					'label'            => __( 'Your site is connected to Jetpack', 'jetpack' ),
139
					'long_description' => sprintf(
140
						'<p>%1$s</p>' .
141
						'<p><span class="dashicons pass"><span class="screen-reader-text">%2$s</span></span> %3$s</p>',
142
						__( 'A healthy connection ensures Jetpack essential services are provided to your WordPress site, such as Stats and Site Security.', 'jetpack' ),
143
						/* translators: Screen reader text indicating a test has passed */
144
						__( 'Passed', 'jetpack' ),
145
						__( 'Your site is connected to Jetpack.', 'jetpack' )
146
					),
147
				)
148
			);
149
		} elseif ( ( new Status() )->is_development_mode() ) {
150
			$result = self::skipped_test(
151
				array(
152
					'name'              => $name,
153
					'short_description' => __( 'Jetpack is in Development Mode:', 'jetpack' ) . ' ' . Jetpack::development_mode_trigger_text(),
154
				)
155
			);
156
		} else {
157
			$result = self::failing_test(
158
				array(
159
					'name'             => $name,
160
					'label'            => __( 'Your site is not connected to Jetpack', 'jetpack' ),
161
					'action'           => admin_url( 'admin.php?page=jetpack#/dashboard' ),
162
					'action_label'     => __( 'Reconnect your site now', 'jetpack' ),
163
					'long_description' => sprintf(
164
						'<p>%1$s</p>' .
165
						'<p><span class="dashicons fail"><span class="screen-reader-text">%2$s</span></span> %3$s<strong> %4$s</strong></p>',
166
						__( 'A healthy connection ensures Jetpack essential services are provided to your WordPress site, such as Stats and Site Security.', 'jetpack' ),
167
						/* translators: screen reader text indicating a test failed */
168
						__( 'Error', 'jetpack' ),
169
						__( 'Your site is not connected to Jetpack.', 'jetpack' ),
170
						__( 'We recommend reconnecting Jetpack.', 'jetpack' )
171
					),
172
				)
173
			);
174
		}
175
176
		return $result;
177
	}
178
179
	/**
180
	 * Test that the master user still exists on this site.
181
	 *
182
	 * @return array Test results.
183
	 */
184
	protected function test__master_user_exists_on_site() {
185
		$name = __FUNCTION__;
186 View Code Duplication
		if ( ! $this->helper_is_jetpack_connected() ) {
187
			return self::skipped_test(
188
				array(
189
					'name'              => $name,
190
					'short_description' => __( 'Jetpack is not connected. No master user to check.', 'jetpack' ),
191
				)
192
			);
193
		}
194
		$local_user = $this->helper_retrieve_local_master_user();
195
196
		if ( $local_user->exists() ) {
197
			$result = self::passing_test( array( 'name' => $name ) );
198 View Code Duplication
		} else {
199
			$result = self::failing_test(
200
				array(
201
					'name'              => $name,
202
					'short_description' => __( 'The user who setup the Jetpack connection no longer exists on this site.', 'jetpack' ),
203
					'action_label'      => __( 'Please disconnect and reconnect Jetpack.', 'jetpack' ),
204
					'action'            => Redirect::get_url( 'jetpack-support-reconnecting-reinstalling-jetpack' ),
205
				)
206
			);
207
		}
208
209
		return $result;
210
	}
211
212
	/**
213
	 * Test that the master user has the manage options capability (e.g. is an admin).
214
	 *
215
	 * Generic calls from WP.com execute on Jetpack as the master user. If it isn't an admin, random things will fail.
216
	 *
217
	 * @return array Test results.
218
	 */
219
	protected function test__master_user_can_manage_options() {
220
		$name = __FUNCTION__;
221 View Code Duplication
		if ( ! $this->helper_is_jetpack_connected() ) {
222
			return self::skipped_test(
223
				array(
224
					'name'              => $name,
225
					'short_description' => __( 'Jetpack is not connected.', 'jetpack' ),
226
				)
227
			);
228
		}
229
		$master_user = $this->helper_retrieve_local_master_user();
230
231
		if ( user_can( $master_user, 'manage_options' ) ) {
232
			$result = self::passing_test( array( 'name' => $name ) );
233 View Code Duplication
		} else {
234
			$result = self::failing_test(
235
				array(
236
					'name'              => $name,
237
					/* translators: a WordPress username */
238
					'short_description' => sprintf( __( 'The user (%s) who setup the Jetpack connection is not an administrator.', 'jetpack' ), $master_user->user_login ),
239
					'action_label'      => __( 'Either upgrade the user or disconnect and reconnect Jetpack.', 'jetpack' ),
240
					'action'            => Redirect::get_url( 'jetpack-support-reconnecting-reinstalling-jetpack' ),
241
				)
242
			);
243
		}
244
245
		return $result;
246
	}
247
248
	/**
249
	 * Test that the PHP's XML library is installed.
250
	 *
251
	 * While it should be installed by default, increasingly in PHP 7, some OSes require an additional php-xml package.
252
	 *
253
	 * @return array Test results.
254
	 */
255
	protected function test__xml_parser_available() {
256
		$name = __FUNCTION__;
257
		if ( function_exists( 'xml_parser_create' ) ) {
258
			$result = self::passing_test( array( 'name' => $name ) );
259
		} else {
260
			$result = self::failing_test(
261
				array(
262
					'name'              => $name,
263
					'label'             => __( 'PHP XML manipulation libraries are not available.', 'jetpack' ),
264
					'short_description' => __( 'Please ask your hosting provider to refer to our server requirements and enable PHP\'s XML module.', 'jetpack' ),
265
					'action_label'      => __( 'View our server requirements', 'jetpack' ),
266
					'action'            => Redirect::get_url( 'jetpack-support-server-requirements' ),
267
				)
268
			);
269
		}
270
		return $result;
271
	}
272
273
	/**
274
	 * Test that the server is able to send an outbound http communication.
275
	 *
276
	 * @return array Test results.
277
	 */
278 View Code Duplication
	protected function test__outbound_http() {
279
		$name    = __FUNCTION__;
280
		$request = wp_remote_get( preg_replace( '/^https:/', 'http:', JETPACK__API_BASE ) . 'test/1/' );
281
		$code    = wp_remote_retrieve_response_code( $request );
282
283
		if ( 200 === intval( $code ) ) {
284
			$result = self::passing_test( array( 'name' => $name ) );
285
		} else {
286
			$result = self::failing_test(
287
				array(
288
					'name'              => $name,
289
					'short_description' => $this->helper_enable_outbound_requests( 'HTTP' ),
290
				)
291
			);
292
		}
293
294
		return $result;
295
	}
296
297
	/**
298
	 * Test that the server is able to send an outbound https communication.
299
	 *
300
	 * @return array Test results.
301
	 */
302 View Code Duplication
	protected function test__outbound_https() {
303
		$name    = __FUNCTION__;
304
		$request = wp_remote_get( preg_replace( '/^http:/', 'https:', JETPACK__API_BASE ) . 'test/1/' );
305
		$code    = wp_remote_retrieve_response_code( $request );
306
307
		if ( 200 === intval( $code ) ) {
308
			$result = self::passing_test( array( 'name' => $name ) );
309
		} else {
310
			$result = self::failing_test(
311
				array(
312
					'name'              => $name,
313
					'short_description' => $this->helper_enable_outbound_requests( 'HTTPS' ),
314
				)
315
			);
316
		}
317
318
		return $result;
319
	}
320
321
	/**
322
	 * Check for an IDC.
323
	 *
324
	 * @return array Test results.
325
	 */
326
	protected function test__identity_crisis() {
327
		$name = __FUNCTION__;
328 View Code Duplication
		if ( ! $this->helper_is_jetpack_connected() ) {
329
			return self::skipped_test(
330
				array(
331
					'name'              => $name,
332
					'short_description' => __( 'Jetpack is not connected.', 'jetpack' ),
333
				)
334
			);
335
		}
336
		$identity_crisis = Jetpack::check_identity_crisis();
337
338
		if ( ! $identity_crisis ) {
339
			$result = self::passing_test( array( 'name' => $name ) );
340
		} else {
341
			$result = self::failing_test(
342
				array(
343
					'name'              => $name,
344
					'short_description' => sprintf(
345
						/* translators: Two URLs. The first is the locally-recorded value, the second is the value as recorded on WP.com. */
346
						__( 'Your url is set as `%1$s`, but your WordPress.com connection lists it as `%2$s`!', 'jetpack' ),
347
						$identity_crisis['home'],
348
						$identity_crisis['wpcom_home']
349
					),
350
					'action_label'      => $this->helper_get_support_text(),
351
					'action'            => $this->helper_get_support_url(),
352
				)
353
			);
354
		}
355
		return $result;
356
	}
357
358
	/**
359
	 * Tests connection status against wp.com's test-connection endpoint.
360
	 *
361
	 * @todo: Compare with the wpcom_self_test. We only need one of these.
362
	 *
363
	 * @return array Test results.
364
	 */
365
	protected function test__wpcom_connection_test() {
366
		$name = __FUNCTION__;
367
368
		$status = new Status();
369 View Code Duplication
		if ( ! Jetpack::is_active() || $status->is_development_mode() || $status->is_staging_site() || ! $this->pass ) {
370
			return self::skipped_test( array( 'name' => $name ) );
371
		}
372
373
		add_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ) );
374
		$response = Client::wpcom_json_api_request_as_blog(
375
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
376
			Client::WPCOM_JSON_API_VERSION
377
		);
378
		remove_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ) );
379
380 View Code Duplication
		if ( is_wp_error( $response ) ) {
381
			return self::failing_test(
382
				array(
383
					'name'              => $name,
384
					/* translators: %1$s is the error code, %2$s is the error message */
385
					'short_description' => sprintf( __( 'Connection test failed (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() ),
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
386
					'action_label'      => $this->helper_get_support_text(),
387
					'action'            => $this->helper_get_support_url(),
388
				)
389
			);
390
		}
391
392
		$body = wp_remote_retrieve_body( $response );
393 View Code Duplication
		if ( ! $body ) {
394
			return self::failing_test(
395
				array(
396
					'name'              => $name,
397
					'short_description' => __( 'Connection test failed (empty response body)', 'jetpack' ) . wp_remote_retrieve_response_code( $response ),
398
					'action_label'      => $this->helper_get_support_text(),
399
					'action'            => $this->helper_get_support_url(),
400
				)
401
			);
402
		}
403
404 View Code Duplication
		if ( 404 === wp_remote_retrieve_response_code( $response ) ) {
405
			return self::skipped_test(
406
				array(
407
					'name'              => $name,
408
					'short_description' => __( 'The WordPress.com API returned a 404 error.', 'jetpack' ),
409
				)
410
			);
411
		}
412
413
		$result       = json_decode( $body );
414
		$is_connected = (bool) $result->connected;
415
		$message      = $result->message . ': ' . wp_remote_retrieve_response_code( $response );
416
417
		if ( $is_connected ) {
418
			return self::passing_test( array( 'name' => $name ) );
419
		} else {
420
			return self::failing_test(
421
				array(
422
					'name'              => $name,
423
					'short_description' => $message,
424
					'action_label'      => $this->helper_get_support_text(),
425
					'action'            => $this->helper_get_support_url(),
426
				)
427
			);
428
		}
429
	}
430
431
	/**
432
	 * Tests the port number to ensure it is an expected value.
433
	 *
434
	 * We expect that sites on be on one of:
435
	 * port 80,
436
	 * port 443 (https sites only),
437
	 * the value of JETPACK_SIGNATURE__HTTP_PORT,
438
	 * unless the site is intentionally on a different port (e.g. example.com:8080 is the site's URL).
439
	 *
440
	 * If the value isn't one of those and the site's URL doesn't include a port, then the signature verification will fail.
441
	 *
442
	 * This happens most commonly on sites with reverse proxies, so the edge (e.g. Varnish) is running on 80/443, but nginx
443
	 * or Apache is responding internally on a different port (e.g. 81).
444
	 *
445
	 * @return array Test results
446
	 */
447
	protected function test__server_port_value() {
448
		$name = __FUNCTION__;
449
		if ( ! isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) && ! isset( $_SERVER['SERVER_PORT'] ) ) {
450
			return self::skipped_test(
451
				array(
452
					'name'              => $name,
453
					'short_description' => __( 'The server port values are not defined. This is most common when running PHP via a CLI.', 'jetpack' ),
454
				)
455
			);
456
		}
457
		$site_port   = wp_parse_url( home_url(), PHP_URL_PORT );
0 ignored issues
show
Unused Code introduced by
The call to wp_parse_url() has too many arguments starting with PHP_URL_PORT.

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...
458
		$server_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? (int) $_SERVER['HTTP_X_FORWARDED_PORT'] : (int) $_SERVER['SERVER_PORT'];
459
		$http_ports  = array( 80 );
460
		$https_ports = array( 80, 443 );
461
462
		if ( defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ) {
463
			$http_ports[] = JETPACK_SIGNATURE__HTTP_PORT;
464
		}
465
466
		if ( defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ) {
467
			$https_ports[] = JETPACK_SIGNATURE__HTTPS_PORT;
468
		}
469
470
		if ( $site_port ) {
471
			return self::skipped_test( array( 'name' => $name ) ); // Not currently testing for this situation.
472
		}
473
474
		if ( is_ssl() && in_array( $server_port, $https_ports, true ) ) {
475
			return self::passing_test( array( 'name' => $name ) );
476
		} elseif ( in_array( $server_port, $http_ports, true ) ) {
477
			return self::passing_test( array( 'name' => $name ) );
478
		} else {
479
			if ( is_ssl() ) {
480
				$needed_constant = 'JETPACK_SIGNATURE__HTTPS_PORT';
481
			} else {
482
				$needed_constant = 'JETPACK_SIGNATURE__HTTP_PORT';
483
			}
484
			return self::failing_test(
485
				array(
486
					'name'              => $name,
487
					'short_description' => sprintf(
488
						/* translators: %1$s - a PHP code snippet */
489
						__(
490
							'The server port value is unexpected.
491
						Try adding the following to your wp-config.php file: %1$s',
492
							'jetpack'
493
						),
494
						"define( '$needed_constant', $server_port )"
495
					),
496
				)
497
			);
498
		}
499
	}
500
501
	/**
502
	 * Full Sync Health Test.
503
	 *
504
	 * Sync Disabled: Results in a skipped test
505
	 * Not In Progress : Results in a skipped test
506
	 * In Progress: Results in skipped test w/ status in CLI
507
	 */
508
	protected function test__full_sync_health() {
509
510
		$name = __FUNCTION__;
511
512
		if ( ! $this->helper_is_jetpack_connected() ) {
513
			// If the site is not connected, there is no point in testing Sync health.
514
			return self::skipped_test(
515
				array(
516
					'name'                => $name,
517
					'show_in_site_health' => false,
518
				)
519
			);
520
		}
521
522
		// Sync is enabled.
523
		if ( Sync_Settings::is_sync_enabled() ) {
524
525
			// Get Full Sync Progress.
526
			$full_sync_module = Modules::get_module( 'full-sync' );
527
			$progress_percent = $full_sync_module ? $full_sync_module->get_sync_progress_percentage() : null;
528
529
			// Full Sync in Progress.
530
			if ( $progress_percent ) {
531
532
				return self::informational_test(
533
					array(
534
						'name'              => $name,
535
						'label'             => __( 'Jetpack is performing a full sync of your site', 'jetpack' ),
536
						'severity'          => 'recommended',
537
						/* translators: placeholder is a percentage number. */
538
						'short_description' => sprintf( __( 'Jetpack is performing a full sync of your site. Current Progress: %1$d %%', 'jetpack' ), $progress_percent ),
539
						'long_description'  => sprintf(
540
							'<p>%1$s</p><p><span class="dashicons dashicons-update"><span class="screen-reader-text">%2$s</span></span> %3$s</p><div class="jetpack-sync-progress-ui"><div class="jetpack-sync-progress-label"></div><div class="jetpack-sync-progress-bar"></div></div>',
541
							__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' ), /* translators: screen reader text indicating data is updating. */
542
							__( 'Updating', 'jetpack' ),
543
							__( 'Jetpack is currently performing a full sync of your site data.', 'jetpack' )
544
						),
545
					)
546
				);
547
548
			} else {
549
550
				// no Full Sync in Progress.
551
				return self::skipped_test(
552
					array(
553
						'name'                => $name,
554
						'show_in_site_health' => false,
555
					)
556
				);
557
558
			}
559
		} else {
560
561
			// If sync is not enabled no Full Sync can occur.
562
			return self::skipped_test(
563
				array(
564
					'name'                => $name,
565
					'show_in_site_health' => false,
566
				)
567
			);
568
569
		}
570
571
	}
572
573
	/**
574
	 * Sync Health Tests.
575
	 *
576
	 * Disabled: Results in a failing test (recommended)
577
	 * Delayed: Results in failing test (recommended)
578
	 * Error: Results in failing test (critical)
579
	 */
580
	protected function test__sync_health() {
581
582
		$name = __FUNCTION__;
583
584
		if ( ! $this->helper_is_jetpack_connected() ) {
585
			// If the site is not connected, there is no point in testing Sync health.
586
			return self::skipped_test(
587
				array(
588
					'name'                => $name,
589
					'show_in_site_health' => false,
590
				)
591
			);
592
		}
593
594
		// Sync is enabled.
595
		if ( Sync_Settings::is_sync_enabled() ) {
596
597
			if ( Sync_Health::get_status() === Sync_Health::STATUS_OUT_OF_SYNC ) {
598
				/*
599
				 * Sync has experienced Data Loss.
600
				 */
601
				$description  = '<p>';
602
				$description .= esc_html__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' );
603
				$description .= '</p>';
604
				$description .= '<p>';
605
				$description .= sprintf(
606
					'<span class="dashicons fail"><span class="screen-reader-text">%1$s</span></span> ',
607
					esc_html__( 'Error', 'jetpack' )
608
				);
609
				$description .= wp_kses(
610
					__( 'Jetpack has detected an error while syncing your site. <strong>We recommend <a id="full_sync_request_link" href="#">a full sync</a> to align Jetpack with your site data.</strong>', 'jetpack' ),
611
					array(
612
						'a'      => array(
613
							'id'   => array(),
614
							'href' => array(),
615
						),
616
						'strong' => array(),
617
					)
618
				);
619
				$description .= '</p>';
620
621
				return self::failing_test(
622
					array(
623
						'name'              => $name,
624
						'label'             => __( 'Jetpack has detected an error syncing your site.', 'jetpack' ),
625
						'severity'          => 'critical',
626
						'action'            => Redirect::get_url( 'jetpack-contact-support' ),
627
						'action_label'      => __( 'Contact Jetpack Support', 'jetpack' ),
628
						'short_description' => __( 'Jetpack has detected an error while syncing your site. We recommend a full sync to align Jetpack with your site data.', 'jetpack' ),
629
						'long_description'  => $description,
630
					)
631
				);
632
633
			} else {
634
				// Get the Sync Queue.
635
				$sender     = Sync_Sender::get_instance();
636
				$sync_queue = $sender->get_sync_queue();
637
638
				// lag exceeds 5 minutes.
639
				if ( $sync_queue->lag() > 5 * MINUTE_IN_SECONDS ) {
640
641
					$description  = '<p>';
642
					$description .= esc_html__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' );
643
					$description .= '</p>';
644
					$description .= '<p>';
645
					$description .= sprintf(
646
						'<span class="dashicons dashicons-clock" style="color: orange;"><span class="screen-reader-text">%1$s</span></span> ',
647
						/* translators: name, used to describe a clock icon. */
648
						esc_html__( 'Clock', 'jetpack' )
649
					);
650
					$description .= wp_kses(
651
						sprintf(
652
							/* translators: placeholder is a number of minutes. */
653
							_n(
654
								'Jetpack has identified a delay while syncing individual content updates. Certain features might be slower than usual, but this is only temporary while sync catches up with recent changes to your site. <strong>We’re seeing a current delay of %1$d minute.</strong>',
655
								'Jetpack has identified a delay while syncing individual content updates. Certain features might be slower than usual, but this is only temporary while sync catches up with recent changes to your site. <strong>We’re seeing a current delay of %1$d minutes.</strong>',
656
								intval( $sync_queue->lag() / MINUTE_IN_SECONDS ),
657
								'jetpack'
658
							),
659
							number_format_i18n( $sync_queue->lag() / MINUTE_IN_SECONDS )
660
						),
661
						array( 'strong' => array() )
662
					);
663
					$description .= '</p>';
664
665
					return self::informational_test(
666
						array(
667
							'name'              => $name,
668
							'label'             => __( 'Jetpack is experiencing a delay syncing your site.', 'jetpack' ),
669
							'severity'          => 'recommended',
670
							'action'            => null,
671
							'action_label'      => null,
672
							'short_description' => __( 'Jetpack is experiencing a delay syncing your site.', 'jetpack' ),
673
							'long_description'  => $description,
674
						)
675
					);
676
677
				} else {
678
679
					// Sync is Healthy.
680
					return self::passing_test( array( 'name' => $name ) );
681
682
				}
683
			}
684
		} else {
685
			/*
686
			 * Sync is disabled.
687
			 */
688
689
			$description  = '<p>';
690
			$description .= esc_html__( 'The information synced by Jetpack ensures that Jetpack Search, Related Posts and other features are aligned with your site’s current content.', 'jetpack' );
691
			$description .= '</p>';
692
			$description .= '<p>';
693
			$description .= __( 'Developers may enable / disable syncing using the Sync Settings API.', 'jetpack' );
694
			$description .= '</p>';
695
			$description .= '<p>';
696
			$description .= sprintf(
697
				'<span class="dashicons fail"><span class="screen-reader-text">%1$s</span></span> ',
698
				esc_html__( 'Error', 'jetpack' )
699
			);
700
			$description .= wp_kses(
701
				__( 'Jetpack Sync has been disabled on your site. Without it, certain Jetpack features will not work. <strong>We recommend enabling Sync.</strong>', 'jetpack' ),
702
				array( 'strong' => array() )
703
			);
704
			$description .= '</p>';
705
706
			return self::failing_test(
707
				array(
708
					'name'              => $name,
709
					'label'             => __( 'Jetpack Sync has been disabled on your site.', 'jetpack' ),
710
					'severity'          => 'recommended',
711
					'action'            => 'https://github.com/Automattic/jetpack/blob/master/packages/sync/src/class-settings.php',
712
					'action_label'      => __( 'See Github for more on Sync Settings', 'jetpack' ),
713
					'short_description' => __( 'Jetpack Sync has been disabled on your site.', 'jetpack' ),
714
					'long_description'  => $description,
715
				)
716
			);
717
718
		}
719
720
	}
721
722
	/**
723
	 * Calls to WP.com to run the connection diagnostic testing suite.
724
	 *
725
	 * Intentionally added last as it will be skipped if any local failed conditions exist.
726
	 *
727
	 * @since 7.1.0
728
	 * @since 7.9.0 Timeout waiting for a WP.com response no longer fails the test. Test is marked skipped instead.
729
	 *
730
	 * @return array Test results.
731
	 */
732
	protected function last__wpcom_self_test() {
733
		$name = 'test__wpcom_self_test';
734
735
		$status = new Status();
736 View Code Duplication
		if ( ! Jetpack::is_active() || $status->is_development_mode() || $status->is_staging_site() || ! $this->pass ) {
737
			return self::skipped_test( array( 'name' => $name ) );
738
		}
739
740
		$self_xml_rpc_url = site_url( 'xmlrpc.php' );
741
742
		$testsite_url = Connection_Utils::fix_url_for_bad_hosts( JETPACK__API_BASE . 'testsite/1/?url=' );
743
744
		// Using PHP_INT_MAX - 1 so that there is still a way to override this if needed and since it only impacts this one call.
745
		add_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ), PHP_INT_MAX - 1 );
746
747
		$response = wp_remote_get( $testsite_url . $self_xml_rpc_url );
748
749
		remove_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ), PHP_INT_MAX - 1 );
750
751
		if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
752
			return self::passing_test( array( 'name' => $name ) );
753
		} elseif ( is_wp_error( $response ) && false !== strpos( $response->get_error_message(), 'cURL error 28' ) ) { // Timeout.
754
			return self::skipped_test(
755
				array(
756
					'name'              => $name,
757
					'short_description' => __( 'The test timed out which may sometimes indicate a failure or may be a false failure.', 'jetpack' ),
758
				)
759
			);
760
		} else {
761
			return self::failing_test(
762
				array(
763
					'name'              => $name,
764
					'short_description' => sprintf(
765
						/* translators: %1$s - A debugging url */
766
						__( 'Jetpack.com detected an error on the WP.com Self Test. Visit the Jetpack Debug page for more info: %1$s, or contact support.', 'jetpack' ),
767
						Redirect::get_url( 'jetpack-support-debug', array( 'query' => 'url=' . rawurlencode( site_url() ) ) )
768
					),
769
					'action_label'      => $this->helper_get_support_text(),
770
					'action'            => $this->helper_get_support_url(),
771
				)
772
			);
773
		}
774
	}
775
}
776