Completed
Push — add/debugger-reconnect-url ( 169d13...7b2e84 )
by
unknown
08:56
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\Connection\Manager as Connection_Manager;
10
use Automattic\Jetpack\Status;
11
use Automattic\Jetpack\Connection\Utils as Connection_Utils;
12
use Automattic\Jetpack\Sync\Modules;
13
use Automattic\Jetpack\Sync\Settings as Sync_Settings;
14
use Automattic\Jetpack\Sync\Health as Sync_Health;
15
use Automattic\Jetpack\Sync\Sender as Sync_Sender;
16
use Automattic\Jetpack\Redirect;
17
18
/**
19
 * Class Jetpack_Cxn_Tests contains all of the actual tests.
20
 */
21
class Jetpack_Cxn_Tests extends Jetpack_Cxn_Test_Base {
22
23
	/**
24
	 * Jetpack_Cxn_Tests constructor.
25
	 */
26
	public function __construct() {
27
		parent::__construct();
28
29
		$methods = get_class_methods( 'Jetpack_Cxn_Tests' );
30
31
		foreach ( $methods as $method ) {
32
			if ( false === strpos( $method, 'test__' ) ) {
33
				continue;
34
			}
35
			$this->add_test( array( $this, $method ), $method, 'direct' );
36
		}
37
38
		/**
39
		 * Fires after loading default Jetpack Connection tests.
40
		 *
41
		 * @since 7.1.0
42
		 * @since 8.3.0 Passes the Jetpack_Cxn_Tests instance.
43
		 */
44
		do_action( 'jetpack_connection_tests_loaded', $this );
45
46
		/**
47
		 * Determines if the WP.com testing suite should be included.
48
		 *
49
		 * @since 7.1.0
50
		 * @since 8.1.0 Default false.
51
		 *
52
		 * @param bool $run_test To run the WP.com testing suite. Default false.
53
		 */
54
		if ( apply_filters( 'jetpack_debugger_run_self_test', false ) ) {
55
			/**
56
			 * Intentionally added last as it checks for an existing failure state before attempting.
57
			 * Generally, any failed location condition would result in the WP.com check to fail too, so
58
			 * we will skip it to avoid confusing error messages.
59
			 *
60
			 * Note: This really should be an 'async' test.
61
			 */
62
			$this->add_test( array( $this, 'last__wpcom_self_test' ), 'test__wpcom_self_test', 'direct' );
63
		}
64
	}
65
66
	/**
67
	 * Helper function to look up the expected master user and return the local WP_User.
68
	 *
69
	 * @return WP_User Jetpack's expected master user.
70
	 */
71
	protected function helper_retrieve_local_master_user() {
72
		$master_user = Jetpack_Options::get_option( 'master_user' );
73
		return new WP_User( $master_user );
74
	}
75
76
	/**
77
	 * Is Jetpack even connected and supposed to be talking to WP.com?
78
	 */
79
	protected function helper_is_jetpack_connected() {
80
		return Jetpack::is_active() && ! ( new Status() )->is_offline_mode();
81
	}
82
83
	/**
84
	 * Retrieve the `blog_token` if it exists.
85
	 *
86
	 * @return object|false
87
	 */
88
	protected function helper_get_blog_token() {
89
		return Jetpack::connection()->get_access_token();
90
	}
91
92
	/**
93
	 * Returns a support url based on using a development version.
94
	 */
95
	protected function helper_get_support_url() {
96
		return Jetpack::is_development_version()
97
			? Redirect::get_url( 'jetpack-contact-support-beta-group' )
98
			: Redirect::get_url( 'jetpack-contact-support' );
99
	}
100
101
	/**
102
	 * Returns the url to reconnect Jetpack.
103
	 *
104
	 * @return string The reconnect url.
105
	 */
106
	protected static function helper_get_reconnect_url() {
107
		return admin_url( 'admin.php?page=jetpack#/reconnect' );
108
	}
109
110
	/**
111
	 * Gets translated support text.
112
	 */
113
	protected function helper_get_support_text() {
114
		return __( 'Please contact Jetpack support.', 'jetpack' );
115
	}
116
117
	/**
118
	 * Returns the translated text to reconnect Jetpack.
119
	 *
120
	 * @return string The translated reconnect text.
121
	 */
122
	protected static function helper_get_reconnect_text() {
123
		return __( 'Reconnect Jetpack now', 'jetpack' );
124
	}
125
126
	/**
127
	 * Returns the translated text for failing tests due to timeouts.
128
	 *
129
	 * @return string The translated timeout text.
130
	 */
131
	protected static function helper_get_timeout_text() {
132
		return __( 'The test timed out which may sometimes indicate a failure or may be a false failure. Please relaunch tests.', 'jetpack' );
133
	}
134
135
	/**
136
	 * Gets translated reconnect long description.
137
	 *
138
	 * @param string $connection_error The connection specific error.
139
	 * @param string $recommendation The recommendation for resolving the connection error.
140
	 *
141
	 * @return string The translated long description for reconnection recommendations.
142
	 */
143
	protected static function helper_get_reconnect_long_description( $connection_error, $recommendation ) {
144
145
		return sprintf(
146
			'<p>%1$s</p>' .
147
			'<p><span class="dashicons fail"><span class="screen-reader-text">%2$s</span></span> %3$s</p><p><strong>%4$s</strong></p>',
148
			__( 'A healthy connection ensures Jetpack essential services are provided to your WordPress site, such as Stats and Site Security.', 'jetpack' ),
149
			/* translators: screen reader text indicating a test failed */
150
			__( 'Error', 'jetpack' ),
151
			$connection_error,
152
			$recommendation
153
		);
154
	}
155
156
	/**
157
	 * Helper function to return consistent responses for a connection failing test.
158
	 *
159
	 * @param string $name The raw method name that runs the test. Default unnamed_test.
160
	 * @param string $connection_error The connection specific error. Default 'Your site is not connected to Jetpack.'.
161
	 * @param string $recommendation The recommendation for resolving the connection error. Default 'We recommend reconnecting Jetpack.'.
162
	 *
163
	 * @return array Test results.
164
	 */
165
	public static function connection_failing_test( $name, $connection_error = '', $recommendation = '' ) {
166
		$connection_error = empty( $connection_error ) ? __( 'Your site is not connected to Jetpack.', 'jetpack' ) : $connection_error;
167
		$recommendation   = empty( $recommendation ) ? __( 'We recommend reconnecting Jetpack.', 'jetpack' ) : $recommendation;
168
169
		$args = array(
170
			'name'              => $name,
171
			'short_description' => $connection_error,
172
			'action'            => self::helper_get_reconnect_url(),
173
			'action_label'      => self::helper_get_reconnect_text(),
174
			'long_description'  => self::helper_get_reconnect_long_description( $connection_error, $recommendation ),
175
		);
176
177
		return self::failing_test( $args );
178
	}
179
180
	/**
181
	 * Gets translated text to enable outbound requests.
182
	 *
183
	 * @param string $protocol Either 'HTTP' or 'HTTPS'.
184
	 *
185
	 * @return string The translated text.
186
	 */
187
	protected function helper_enable_outbound_requests( $protocol ) {
188
		return sprintf(
189
			/* translators: %1$s - request protocol, either http or https */
190
			__(
191
				'Your server did not successfully connect to the Jetpack server using %1$s
192
				Please ask your hosting provider to confirm your server can make outbound requests to jetpack.com.',
193
				'jetpack'
194
			),
195
			$protocol
196
		);
197
	}
198
199
	/**
200
	 * Returns 30 for use with a filter.
201
	 *
202
	 * To allow time for WP.com to run upstream testing, this function exists to increase the http_request_timeout value
203
	 * to 30.
204
	 *
205
	 * @return int 30
206
	 */
207
	public static function increase_timeout() {
208
		return 30; // seconds.
209
	}
210
211
	/**
212
	 * The test verifies the blog token exists.
213
	 *
214
	 * @return array
215
	 */
216 View Code Duplication
	protected function test__blog_token_if_exists() {
217
		$name = __FUNCTION__;
218
219
		if ( ! $this->helper_is_jetpack_connected() ) {
220
			return self::skipped_test(
221
				array(
222
					'name'              => $name,
223
					'short_description' => __( 'Jetpack is not connected. No blog token to check.', 'jetpack' ),
224
				)
225
			);
226
		}
227
		$blog_token = $this->helper_get_blog_token();
228
229
		if ( $blog_token ) {
230
			$result = self::passing_test( array( 'name' => $name ) );
231
		} else {
232
			$connection_error = __( 'Blog token is missing.', 'jetpack' );
233
234
			$result = self::connection_failing_test( $name, $connection_error );
235
		}
236
237
		return $result;
238
	}
239
240
	/**
241
	 * Test if Jetpack is connected.
242
	 */
243
	protected function test__check_if_connected() {
244
		$name = __FUNCTION__;
245
246
		if ( ! $this->helper_get_blog_token() ) {
247
			return self::skipped_test(
248
				array(
249
					'name'              => $name,
250
					'short_description' => __( 'Blog token is missing.', 'jetpack' ),
251
				)
252
			);
253
		}
254
255
		if ( $this->helper_is_jetpack_connected() ) {
256
			$result = self::passing_test(
257
				array(
258
					'name'             => $name,
259
					'label'            => __( 'Your site is connected to Jetpack', 'jetpack' ),
260
					'long_description' => sprintf(
261
						'<p>%1$s</p>' .
262
						'<p><span class="dashicons pass"><span class="screen-reader-text">%2$s</span></span> %3$s</p>',
263
						__( 'A healthy connection ensures Jetpack essential services are provided to your WordPress site, such as Stats and Site Security.', 'jetpack' ),
264
						/* translators: Screen reader text indicating a test has passed */
265
						__( 'Passed', 'jetpack' ),
266
						__( 'Your site is connected to Jetpack.', 'jetpack' )
267
					),
268
				)
269
			);
270
		} elseif ( ( new Status() )->is_offline_mode() ) {
271
			$result = self::skipped_test(
272
				array(
273
					'name'              => $name,
274
					'short_description' => __( 'Jetpack is in Offline Mode:', 'jetpack' ) . ' ' . Jetpack::development_mode_trigger_text(),
275
				)
276
			);
277
		} else {
278
			$connection_error = __( 'Your site is not connected to Jetpack', 'jetpack' );
279
280
			$result = self::connection_failing_test( $name, $connection_error );
281
		}
282
283
		return $result;
284
	}
285
286
	/**
287
	 * Test that the master user still exists on this site.
288
	 *
289
	 * @return array Test results.
290
	 */
291 View Code Duplication
	protected function test__master_user_exists_on_site() {
292
		$name = __FUNCTION__;
293
		if ( ! $this->helper_is_jetpack_connected() ) {
294
			return self::skipped_test(
295
				array(
296
					'name'              => $name,
297
					'short_description' => __( 'Jetpack is not connected. No master user to check.', 'jetpack' ),
298
				)
299
			);
300
		}
301
		$local_user = $this->helper_retrieve_local_master_user();
302
303
		if ( $local_user->exists() ) {
304
			$result = self::passing_test( array( 'name' => $name ) );
305
		} else {
306
			$connection_error = __( 'The user who setup the Jetpack connection no longer exists on this site.', 'jetpack' );
307
308
			$result = self::connection_failing_test( $name, $connection_error );
309
		}
310
311
		return $result;
312
	}
313
314
	/**
315
	 * Test that the master user has the manage options capability (e.g. is an admin).
316
	 *
317
	 * Generic calls from WP.com execute on Jetpack as the master user. If it isn't an admin, random things will fail.
318
	 *
319
	 * @return array Test results.
320
	 */
321
	protected function test__master_user_can_manage_options() {
322
		$name = __FUNCTION__;
323
		if ( ! $this->helper_is_jetpack_connected() ) {
324
			return self::skipped_test(
325
				array(
326
					'name'              => $name,
327
					'short_description' => __( 'Jetpack is not connected.', 'jetpack' ),
328
				)
329
			);
330
		}
331
		$master_user = $this->helper_retrieve_local_master_user();
332
333
		if ( user_can( $master_user, 'manage_options' ) ) {
334
			$result = self::passing_test( array( 'name' => $name ) );
335
		} else {
336
			/* translators: a WordPress username */
337
			$connection_error = sprintf( __( 'The user (%s) who setup the Jetpack connection is not an administrator.', 'jetpack' ), $master_user->user_login );
338
			/* translators: a WordPress username */
339
			$recommendation = sprintf( __( 'We recommend either upgrading the user (%s) or reconnecting Jetpack.', 'jetpack' ), $master_user->user_login );
340
341
			$result = self::connection_failing_test( $name, $connection_error, $recommendation );
342
		}
343
344
		return $result;
345
	}
346
347
	/**
348
	 * Test that the PHP's XML library is installed.
349
	 *
350
	 * While it should be installed by default, increasingly in PHP 7, some OSes require an additional php-xml package.
351
	 *
352
	 * @return array Test results.
353
	 */
354
	protected function test__xml_parser_available() {
355
		$name = __FUNCTION__;
356
		if ( function_exists( 'xml_parser_create' ) ) {
357
			$result = self::passing_test( array( 'name' => $name ) );
358
		} else {
359
			$result = self::failing_test(
360
				array(
361
					'name'              => $name,
362
					'label'             => __( 'PHP XML manipulation libraries are not available.', 'jetpack' ),
363
					'short_description' => __( 'Please ask your hosting provider to refer to our server requirements and enable PHP\'s XML module.', 'jetpack' ),
364
					'action_label'      => __( 'View our server requirements', 'jetpack' ),
365
					'action'            => Redirect::get_url( 'jetpack-support-server-requirements' ),
366
				)
367
			);
368
		}
369
		return $result;
370
	}
371
372
	/**
373
	 * Test that the server is able to send an outbound http communication.
374
	 *
375
	 * @return array Test results.
376
	 */
377 View Code Duplication
	protected function test__outbound_http() {
378
		$name    = __FUNCTION__;
379
		$request = wp_remote_get( preg_replace( '/^https:/', 'http:', JETPACK__API_BASE ) . 'test/1/' );
380
		$code    = wp_remote_retrieve_response_code( $request );
381
382
		if ( 200 === intval( $code ) ) {
383
			$result = self::passing_test( array( 'name' => $name ) );
384
		} else {
385
			$result = self::failing_test(
386
				array(
387
					'name'              => $name,
388
					'short_description' => $this->helper_enable_outbound_requests( 'HTTP' ),
389
				)
390
			);
391
		}
392
393
		return $result;
394
	}
395
396
	/**
397
	 * Test that the server is able to send an outbound https communication.
398
	 *
399
	 * @return array Test results.
400
	 */
401 View Code Duplication
	protected function test__outbound_https() {
402
		$name    = __FUNCTION__;
403
		$request = wp_remote_get( preg_replace( '/^http:/', 'https:', JETPACK__API_BASE ) . 'test/1/' );
404
		$code    = wp_remote_retrieve_response_code( $request );
405
406
		if ( 200 === intval( $code ) ) {
407
			$result = self::passing_test( array( 'name' => $name ) );
408
		} else {
409
			$result = self::failing_test(
410
				array(
411
					'name'              => $name,
412
					'short_description' => $this->helper_enable_outbound_requests( 'HTTPS' ),
413
				)
414
			);
415
		}
416
417
		return $result;
418
	}
419
420
	/**
421
	 * Check for an IDC.
422
	 *
423
	 * @return array Test results.
424
	 */
425
	protected function test__identity_crisis() {
426
		$name = __FUNCTION__;
427
		if ( ! $this->helper_is_jetpack_connected() ) {
428
			return self::skipped_test(
429
				array(
430
					'name'              => $name,
431
					'short_description' => __( 'Jetpack is not connected.', 'jetpack' ),
432
				)
433
			);
434
		}
435
		$identity_crisis = Jetpack::check_identity_crisis();
436
437
		if ( ! $identity_crisis ) {
438
			$result = self::passing_test( array( 'name' => $name ) );
439
		} else {
440
			$result = self::failing_test(
441
				array(
442
					'name'              => $name,
443
					'short_description' => sprintf(
444
						/* translators: Two URLs. The first is the locally-recorded value, the second is the value as recorded on WP.com. */
445
						__( 'Your url is set as `%1$s`, but your WordPress.com connection lists it as `%2$s`!', 'jetpack' ),
446
						$identity_crisis['home'],
447
						$identity_crisis['wpcom_home']
448
					),
449
					'action_label'      => $this->helper_get_support_text(),
450
					'action'            => $this->helper_get_support_url(),
451
				)
452
			);
453
		}
454
		return $result;
455
	}
456
457
	/**
458
	 * Tests blog and current user's token against wp.com's check-token-health endpoint.
459
	 *
460
	 * @since 9.0.0
461
	 *
462
	 * @return array Test results.
463
	 */
464
	protected function test__connection_token_health() {
465
		$name = __FUNCTION__;
466
467
		$m                = new Connection_Manager();
468
		$validated_tokens = $m->validate_tokens( get_current_user_id() ? get_current_user_id() : $m->get_connection_owner_id() );
469
470
		if ( ! is_array( $validated_tokens ) || count( array_diff_key( array_flip( array( 'blog_token', 'user_token' ) ), $validated_tokens ) ) ) {
471
			return self::skipped_test(
472
				array(
473
					'name'              => $name,
474
					'short_description' => __( 'Token health check failed to validate tokens.', 'jetpack' ),
475
				)
476
			);
477
		}
478
479
		$invalid_tokens_exist = false;
480
		foreach ( $validated_tokens as $validated_token ) {
481
			if ( ! $validated_token['is_healthy'] ) {
482
				$invalid_tokens_exist = true;
483
				break;
484
			}
485
		}
486
487
		if ( false === $invalid_tokens_exist ) {
488
			return self::passing_test( array( 'name' => $name ) );
489
		}
490
491
		$connection_error = __( 'Invalid Jetpack connection tokens.', 'jetpack' );
492
493
		return self::connection_failing_test( $name, $connection_error );
494
	}
495
496
	/**
497
	 * Tests connection status against wp.com's test-connection endpoint.
498
	 *
499
	 * @todo: Compare with the wpcom_self_test. We only need one of these.
500
	 *
501
	 * @return array Test results.
502
	 */
503
	protected function test__wpcom_connection_test() {
504
		$name = __FUNCTION__;
505
506
		$status = new Status();
507 View Code Duplication
		if ( ! Jetpack::is_active() || $status->is_offline_mode() || $status->is_staging_site() || ! $this->pass ) {
508
			return self::skipped_test( array( 'name' => $name ) );
509
		}
510
511
		add_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ) );
512
		$response = Client::wpcom_json_api_request_as_blog(
513
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
514
			Client::WPCOM_JSON_API_VERSION
515
		);
516
		remove_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ) );
517
518
		if ( is_wp_error( $response ) ) {
519
			if ( false !== strpos( $response->get_error_message(), 'cURL error 28' ) ) { // Timeout.
0 ignored issues
show
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...
520
				$result = self::skipped_test(
521
					array(
522
						'name'              => $name,
523
						'short_description' => self::helper_get_timeout_text(),
524
					)
525
				);
526
			} else {
527
				/* translators: %1$s is the error code, %2$s is the error message */
528
				$message = 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...
529
530
				$result = self::connection_failing_test( $name, $message );
531
			}
532
533
			return $result;
534
		}
535
536
		$body = wp_remote_retrieve_body( $response );
537
		if ( ! $body ) {
538
			return self::failing_test(
539
				array(
540
					'name'              => $name,
541
					'short_description' => __( 'Connection test failed (empty response body)', 'jetpack' ) . wp_remote_retrieve_response_code( $response ),
542
					'action_label'      => $this->helper_get_support_text(),
543
					'action'            => $this->helper_get_support_url(),
544
				)
545
			);
546
		}
547
548
		if ( 404 === wp_remote_retrieve_response_code( $response ) ) {
549
			return self::skipped_test(
550
				array(
551
					'name'              => $name,
552
					'short_description' => __( 'The WordPress.com API returned a 404 error.', 'jetpack' ),
553
				)
554
			);
555
		}
556
557
		$result       = json_decode( $body );
558
		$is_connected = ! empty( $result->connected );
559
		$message      = $result->message . ': ' . wp_remote_retrieve_response_code( $response );
560
561
		if ( $is_connected ) {
562
			return self::passing_test( array( 'name' => $name ) );
563
		} else {
564
			return self::connection_failing_test( $name, $message );
565
		}
566
	}
567
568
	/**
569
	 * Tests the port number to ensure it is an expected value.
570
	 *
571
	 * We expect that sites on be on one of:
572
	 * port 80,
573
	 * port 443 (https sites only),
574
	 * the value of JETPACK_SIGNATURE__HTTP_PORT,
575
	 * unless the site is intentionally on a different port (e.g. example.com:8080 is the site's URL).
576
	 *
577
	 * If the value isn't one of those and the site's URL doesn't include a port, then the signature verification will fail.
578
	 *
579
	 * This happens most commonly on sites with reverse proxies, so the edge (e.g. Varnish) is running on 80/443, but nginx
580
	 * or Apache is responding internally on a different port (e.g. 81).
581
	 *
582
	 * @return array Test results
583
	 */
584
	protected function test__server_port_value() {
585
		$name = __FUNCTION__;
586
		if ( ! isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) && ! isset( $_SERVER['SERVER_PORT'] ) ) {
587
			return self::skipped_test(
588
				array(
589
					'name'              => $name,
590
					'short_description' => __( 'The server port values are not defined. This is most common when running PHP via a CLI.', 'jetpack' ),
591
				)
592
			);
593
		}
594
		$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...
595
		$server_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? (int) $_SERVER['HTTP_X_FORWARDED_PORT'] : (int) $_SERVER['SERVER_PORT'];
596
		$http_ports  = array( 80 );
597
		$https_ports = array( 80, 443 );
598
599
		if ( defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ) {
600
			$http_ports[] = JETPACK_SIGNATURE__HTTP_PORT;
601
		}
602
603
		if ( defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ) {
604
			$https_ports[] = JETPACK_SIGNATURE__HTTPS_PORT;
605
		}
606
607
		if ( $site_port ) {
608
			return self::skipped_test( array( 'name' => $name ) ); // Not currently testing for this situation.
609
		}
610
611
		if ( is_ssl() && in_array( $server_port, $https_ports, true ) ) {
612
			return self::passing_test( array( 'name' => $name ) );
613
		} elseif ( in_array( $server_port, $http_ports, true ) ) {
614
			return self::passing_test( array( 'name' => $name ) );
615
		} else {
616
			if ( is_ssl() ) {
617
				$needed_constant = 'JETPACK_SIGNATURE__HTTPS_PORT';
618
			} else {
619
				$needed_constant = 'JETPACK_SIGNATURE__HTTP_PORT';
620
			}
621
			return self::failing_test(
622
				array(
623
					'name'              => $name,
624
					'short_description' => sprintf(
625
						/* translators: %1$s - a PHP code snippet */
626
						__(
627
							'The server port value is unexpected.
628
						Try adding the following to your wp-config.php file: %1$s',
629
							'jetpack'
630
						),
631
						"define( '$needed_constant', $server_port )"
632
					),
633
				)
634
			);
635
		}
636
	}
637
638
	/**
639
	 * Full Sync Health Test.
640
	 *
641
	 * Sync Disabled: Results in a skipped test
642
	 * Not In Progress : Results in a skipped test
643
	 * In Progress: Results in skipped test w/ status in CLI
644
	 */
645
	protected function test__full_sync_health() {
646
647
		$name = __FUNCTION__;
648
649
		if ( ! $this->helper_is_jetpack_connected() ) {
650
			// If the site is not connected, there is no point in testing Sync health.
651
			return self::skipped_test(
652
				array(
653
					'name'                => $name,
654
					'show_in_site_health' => false,
655
				)
656
			);
657
		}
658
659
		// Sync is enabled.
660
		if ( Sync_Settings::is_sync_enabled() ) {
661
662
			// Get Full Sync Progress.
663
			$full_sync_module = Modules::get_module( 'full-sync' );
664
			$progress_percent = $full_sync_module ? $full_sync_module->get_sync_progress_percentage() : null;
665
666
			// Full Sync in Progress.
667
			if ( $progress_percent ) {
668
669
				return self::informational_test(
670
					array(
671
						'name'              => $name,
672
						'label'             => __( 'Jetpack is performing a full sync of your site', 'jetpack' ),
673
						'severity'          => 'recommended',
674
						/* translators: placeholder is a percentage number. */
675
						'short_description' => sprintf( __( 'Jetpack is performing a full sync of your site. Current Progress: %1$d %%', 'jetpack' ), $progress_percent ),
676
						'long_description'  => sprintf(
677
							'<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>',
678
							__( '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. */
679
							__( 'Updating', 'jetpack' ),
680
							__( 'Jetpack is currently performing a full sync of your site data.', 'jetpack' )
681
						),
682
					)
683
				);
684
685
			} else {
686
687
				// no Full Sync in Progress.
688
				return self::skipped_test(
689
					array(
690
						'name'                => $name,
691
						'show_in_site_health' => false,
692
					)
693
				);
694
695
			}
696
		} else {
697
698
			// If sync is not enabled no Full Sync can occur.
699
			return self::skipped_test(
700
				array(
701
					'name'                => $name,
702
					'show_in_site_health' => false,
703
				)
704
			);
705
706
		}
707
708
	}
709
710
	/**
711
	 * Sync Health Tests.
712
	 *
713
	 * Disabled: Results in a failing test (recommended)
714
	 * Delayed: Results in failing test (recommended)
715
	 * Error: Results in failing test (critical)
716
	 */
717
	protected function test__sync_health() {
718
719
		$name = __FUNCTION__;
720
721
		if ( ! $this->helper_is_jetpack_connected() ) {
722
			// If the site is not connected, there is no point in testing Sync health.
723
			return self::skipped_test(
724
				array(
725
					'name'                => $name,
726
					'show_in_site_health' => false,
727
				)
728
			);
729
		}
730
731
		// Sync is enabled.
732
		if ( Sync_Settings::is_sync_enabled() ) {
733
734
			if ( Sync_Health::get_status() === Sync_Health::STATUS_OUT_OF_SYNC ) {
735
				/*
736
				 * Sync has experienced Data Loss.
737
				 */
738
				$description  = '<p>';
739
				$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' );
740
				$description .= '</p>';
741
				$description .= '<p>';
742
				$description .= sprintf(
743
					'<span class="dashicons fail"><span class="screen-reader-text">%1$s</span></span> ',
744
					esc_html__( 'Error', 'jetpack' )
745
				);
746
				$description .= wp_kses(
747
					__( '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' ),
748
					array(
749
						'a'      => array(
750
							'id'   => array(),
751
							'href' => array(),
752
						),
753
						'strong' => array(),
754
					)
755
				);
756
				$description .= '</p>';
757
758
				return self::failing_test(
759
					array(
760
						'name'              => $name,
761
						'label'             => __( 'Jetpack has detected an error syncing your site.', 'jetpack' ),
762
						'severity'          => 'critical',
763
						'action'            => Redirect::get_url( 'jetpack-contact-support' ),
764
						'action_label'      => __( 'Contact Jetpack Support', 'jetpack' ),
765
						'short_description' => __( 'Jetpack has detected an error while syncing your site. We recommend a full sync to align Jetpack with your site data.', 'jetpack' ),
766
						'long_description'  => $description,
767
					)
768
				);
769
770
			} else {
771
				// Get the Sync Queue.
772
				$sender     = Sync_Sender::get_instance();
773
				$sync_queue = $sender->get_sync_queue();
774
775
				// lag exceeds 5 minutes.
776
				if ( $sync_queue->lag() > 5 * MINUTE_IN_SECONDS ) {
777
778
					$description  = '<p>';
779
					$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' );
780
					$description .= '</p>';
781
					$description .= '<p>';
782
					$description .= sprintf(
783
						'<span class="dashicons dashicons-clock" style="color: orange;"><span class="screen-reader-text">%1$s</span></span> ',
784
						/* translators: name, used to describe a clock icon. */
785
						esc_html__( 'Clock', 'jetpack' )
786
					);
787
					$description .= wp_kses(
788
						sprintf(
789
							/* translators: placeholder is a number of minutes. */
790
							_n(
791
								'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>',
792
								'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>',
793
								intval( $sync_queue->lag() / MINUTE_IN_SECONDS ),
794
								'jetpack'
795
							),
796
							number_format_i18n( $sync_queue->lag() / MINUTE_IN_SECONDS )
797
						),
798
						array( 'strong' => array() )
799
					);
800
					$description .= '</p>';
801
802
					return self::informational_test(
803
						array(
804
							'name'              => $name,
805
							'label'             => __( 'Jetpack is experiencing a delay syncing your site.', 'jetpack' ),
806
							'severity'          => 'recommended',
807
							'action'            => null,
808
							'action_label'      => null,
809
							'short_description' => __( 'Jetpack is experiencing a delay syncing your site.', 'jetpack' ),
810
							'long_description'  => $description,
811
						)
812
					);
813
814
				} else {
815
816
					// Sync is Healthy.
817
					return self::passing_test( array( 'name' => $name ) );
818
819
				}
820
			}
821
		} else {
822
			/*
823
			 * Sync is disabled.
824
			 */
825
826
			$description  = '<p>';
827
			$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' );
828
			$description .= '</p>';
829
			$description .= '<p>';
830
			$description .= __( 'Developers may enable / disable syncing using the Sync Settings API.', 'jetpack' );
831
			$description .= '</p>';
832
			$description .= '<p>';
833
			$description .= sprintf(
834
				'<span class="dashicons fail"><span class="screen-reader-text">%1$s</span></span> ',
835
				esc_html__( 'Error', 'jetpack' )
836
			);
837
			$description .= wp_kses(
838
				__( 'Jetpack Sync has been disabled on your site. Without it, certain Jetpack features will not work. <strong>We recommend enabling Sync.</strong>', 'jetpack' ),
839
				array( 'strong' => array() )
840
			);
841
			$description .= '</p>';
842
843
			return self::failing_test(
844
				array(
845
					'name'              => $name,
846
					'label'             => __( 'Jetpack Sync has been disabled on your site.', 'jetpack' ),
847
					'severity'          => 'recommended',
848
					'action'            => 'https://github.com/Automattic/jetpack/blob/master/packages/sync/src/class-settings.php',
849
					'action_label'      => __( 'See Github for more on Sync Settings', 'jetpack' ),
850
					'short_description' => __( 'Jetpack Sync has been disabled on your site.', 'jetpack' ),
851
					'long_description'  => $description,
852
				)
853
			);
854
855
		}
856
857
	}
858
859
	/**
860
	 * Calls to WP.com to run the connection diagnostic testing suite.
861
	 *
862
	 * Intentionally added last as it will be skipped if any local failed conditions exist.
863
	 *
864
	 * @since 7.1.0
865
	 * @since 7.9.0 Timeout waiting for a WP.com response no longer fails the test. Test is marked skipped instead.
866
	 *
867
	 * @return array Test results.
868
	 */
869
	protected function last__wpcom_self_test() {
870
		$name = 'test__wpcom_self_test';
871
872
		$status = new Status();
873 View Code Duplication
		if ( ! Jetpack::is_active() || $status->is_offline_mode() || $status->is_staging_site() || ! $this->pass ) {
874
			return self::skipped_test( array( 'name' => $name ) );
875
		}
876
877
		$self_xml_rpc_url = site_url( 'xmlrpc.php' );
878
879
		$testsite_url = Connection_Utils::fix_url_for_bad_hosts( JETPACK__API_BASE . 'testsite/1/?url=' );
880
881
		// 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.
882
		add_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ), PHP_INT_MAX - 1 );
883
884
		$response = wp_remote_get( $testsite_url . $self_xml_rpc_url );
885
886
		remove_filter( 'http_request_timeout', array( 'Jetpack_Cxn_Tests', 'increase_timeout' ), PHP_INT_MAX - 1 );
887
888
		if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
889
			$result = self::passing_test( array( 'name' => $name ) );
890
		} elseif ( is_wp_error( $response ) && false !== strpos( $response->get_error_message(), 'cURL error 28' ) ) { // Timeout.
891
			$result = self::skipped_test(
892
				array(
893
					'name'              => $name,
894
					'short_description' => self::helper_get_timeout_text(),
895
				)
896
			);
897
		} else {
898
			$result = self::failing_test(
899
				array(
900
					'name'              => $name,
901
					'short_description' => sprintf(
902
						/* translators: %1$s - A debugging url */
903
						__( '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' ),
904
						Redirect::get_url( 'jetpack-support-debug', array( 'query' => 'url=' . rawurlencode( site_url() ) ) )
905
					),
906
					'action_label'      => $this->helper_get_support_text(),
907
					'action'            => $this->helper_get_support_url(),
908
				)
909
			);
910
		}
911
912
		return $result;
913
	}
914
}
915