Completed
Push — add/jetpack-debugger-token-hea... ( d9f7f2 )
by
unknown
57:02 queued 47:40
created

Jetpack_Cxn_Tests::test__blog_token_health()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 56

Duplication

Lines 14
Ratio 25 %

Importance

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