Completed
Push — add/connection_plugins_active_... ( 2ac671...32c5d9 )
by
unknown
104:49 queued 97:47
created

packages/sync/src/class-actions.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * A class that defines syncable actions for Jetpack.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync;
9
10
use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
11
use Automattic\Jetpack\Constants;
12
use Automattic\Jetpack\Status;
13
use Automattic\Jetpack\Sync\Health;
14
use Automattic\Jetpack\Sync\Modules;
15
16
/**
17
 * The role of this class is to hook the Sync subsystem into WordPress - when to listen for actions,
18
 * when to send, when to perform a full sync, etc.
19
 *
20
 * It also binds the action to send data to WPCOM to Jetpack's XMLRPC client object.
21
 */
22
class Actions {
23
	/**
24
	 * A variable to hold a sync sender object.
25
	 *
26
	 * @access public
27
	 * @static
28
	 *
29
	 * @var Automattic\Jetpack\Sync\Sender
30
	 */
31
	public static $sender = null;
32
33
	/**
34
	 * A variable to hold a sync listener object.
35
	 *
36
	 * @access public
37
	 * @static
38
	 *
39
	 * @var Automattic\Jetpack\Sync\Listener
40
	 */
41
	public static $listener = null;
42
43
	/**
44
	 * Name of the sync cron schedule.
45
	 *
46
	 * @access public
47
	 *
48
	 * @var string
49
	 */
50
	const DEFAULT_SYNC_CRON_INTERVAL_NAME = 'jetpack_sync_interval';
51
52
	/**
53
	 * Interval between the last and the next sync cron action.
54
	 *
55
	 * @access public
56
	 *
57
	 * @var int
58
	 */
59
	const DEFAULT_SYNC_CRON_INTERVAL_VALUE = 300; // 5 * MINUTE_IN_SECONDS;
60
61
	/**
62
	 * Initialize Sync for cron jobs, set up listeners for WordPress Actions,
63
	 * and set up a shut-down action for sending actions to WordPress.com
64
	 *
65
	 * @access public
66
	 * @static
67
	 */
68
	public static function init() {
69
		// Everything below this point should only happen if we're a valid sync site.
70
		if ( ! self::sync_allowed() ) {
71
			return;
72
		}
73
74
		if ( self::sync_via_cron_allowed() ) {
75
			self::init_sync_cron_jobs();
76
		} elseif ( wp_next_scheduled( 'jetpack_sync_cron' ) ) {
77
			self::clear_sync_cron_jobs();
78
		}
79
		// When importing via cron, do not sync.
80
		add_action( 'wp_cron_importer_hook', array( __CLASS__, 'set_is_importing_true' ), 1 );
81
82
		// Sync connected user role changes to WordPress.com.
83
		Users::init();
84
85
		// Publicize filter to prevent publicizing blacklisted post types.
86
		add_filter( 'publicize_should_publicize_published_post', array( __CLASS__, 'prevent_publicize_blacklisted_posts' ), 10, 2 );
87
88
		/**
89
		 * Fires on every request before default loading sync listener code.
90
		 * Return false to not load sync listener code that monitors common
91
		 * WP actions to be serialized.
92
		 *
93
		 * By default this returns true for cron jobs, non-GET-requests, or requests where the
94
		 * user is logged-in.
95
		 *
96
		 * @since 4.2.0
97
		 *
98
		 * @param bool should we load sync listener code for this request
99
		 */
100
		if ( apply_filters( 'jetpack_sync_listener_should_load', true ) ) {
101
			self::initialize_listener();
102
		}
103
104
		add_action( 'init', array( __CLASS__, 'add_sender_shutdown' ), 90 );
105
	}
106
107
	/**
108
	 * Prepares sync to send actions on shutdown for the current request.
109
	 *
110
	 * @access public
111
	 * @static
112
	 */
113
	public static function add_sender_shutdown() {
114
		/**
115
		 * Fires on every request before default loading sync sender code.
116
		 * Return false to not load sync sender code that serializes pending
117
		 * data and sends it to WPCOM for processing.
118
		 *
119
		 * By default this returns true for cron jobs, POST requests, admin requests, or requests
120
		 * by users who can manage_options.
121
		 *
122
		 * @since 4.2.0
123
		 *
124
		 * @param bool should we load sync sender code for this request
125
		 */
126
		if ( apply_filters(
127
			'jetpack_sync_sender_should_load',
128
			self::should_initialize_sender()
129
		) ) {
130
			self::initialize_sender();
131
			add_action( 'shutdown', array( self::$sender, 'do_sync' ) );
132
			add_action( 'shutdown', array( self::$sender, 'do_full_sync' ), 9999 );
133
		}
134
	}
135
136
	/**
137
	 * Decides if the sender should run on shutdown for this request.
138
	 *
139
	 * @access public
140
	 * @static
141
	 *
142
	 * @return bool
143
	 */
144
	public static function should_initialize_sender() {
145
		if ( Constants::is_true( 'DOING_CRON' ) ) {
146
			return self::sync_via_cron_allowed();
147
		}
148
149
		if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
150
			return true;
151
		}
152
153
		if ( current_user_can( 'manage_options' ) ) {
154
			return true;
155
		}
156
157
		if ( is_admin() ) {
158
			return true;
159
		}
160
161
		if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
162
			return true;
163
		}
164
165
		if ( Constants::get_constant( 'WP_CLI' ) ) {
166
			return true;
167
		}
168
169
		return false;
170
	}
171
172
	/**
173
	 * Decides if the sender should run on shutdown when actions are queued.
174
	 *
175
	 * @access public
176
	 * @static
177
	 *
178
	 * @return bool
179
	 */
180
	public static function should_initialize_sender_enqueue() {
181
		if ( Constants::is_true( 'DOING_CRON' ) ) {
182
			return self::sync_via_cron_allowed();
183
		}
184
185
		return true;
186
	}
187
188
	/**
189
	 * Decides if sync should run at all during this request.
190
	 *
191
	 * @access public
192
	 * @static
193
	 *
194
	 * @return bool
195
	 */
196
	public static function sync_allowed() {
197
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
198
			return false;
199
		}
200
201
		if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
202
			return true;
203
		}
204
205
		if ( ! Settings::is_sync_enabled() ) {
206
			return false;
207
		}
208
209
		if ( ( new Status() )->is_development_mode() ) {
210
			return false;
211
		}
212
213
		if ( ( new Status() )->is_staging_site() ) {
214
			return false;
215
		}
216
217
		$connection = new Jetpack_Connection();
218
		if ( ! $connection->is_active() ) {
219
			if ( ! doing_action( 'jetpack_user_authorized' ) ) {
220
				return false;
221
			}
222
		}
223
224
		return true;
225
	}
226
227
	/**
228
	 * Determines if syncing during a cron job is allowed.
229
	 *
230
	 * @access public
231
	 * @static
232
	 *
233
	 * @return bool|int
234
	 */
235
	public static function sync_via_cron_allowed() {
236
		return ( Settings::get_setting( 'sync_via_cron' ) );
237
	}
238
239
	/**
240
	 * Decides if the given post should be Publicized based on its type.
241
	 *
242
	 * @access public
243
	 * @static
244
	 *
245
	 * @param bool     $should_publicize  Publicize status prior to this filter running.
246
	 * @param \WP_Post $post              The post to test for Publicizability.
247
	 * @return bool
248
	 */
249
	public static function prevent_publicize_blacklisted_posts( $should_publicize, $post ) {
250
		if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
251
			return false;
252
		}
253
254
		return $should_publicize;
255
	}
256
257
	/**
258
	 * Set an importing flag to `true` in sync settings.
259
	 *
260
	 * @access public
261
	 * @static
262
	 */
263
	public static function set_is_importing_true() {
264
		Settings::set_importing( true );
265
	}
266
267
	/**
268
	 * Sends data to WordPress.com via an XMLRPC request.
269
	 *
270
	 * @access public
271
	 * @static
272
	 *
273
	 * @param object $data                   Data relating to a sync action.
274
	 * @param string $codec_name             The name of the codec that encodes the data.
275
	 * @param float  $sent_timestamp         Current server time so we can compensate for clock differences.
276
	 * @param string $queue_id               The queue the action belongs to, sync or full_sync.
277
	 * @param float  $checkout_duration      Time spent retrieving queue items from the DB.
278
	 * @param float  $preprocess_duration    Time spent converting queue items into data to send.
279
	 * @param int    $queue_size             The size of the sync queue at the time of processing.
280
	 * @param string $buffer_id              The ID of the Queue buffer checked out for processing.
0 ignored issues
show
Should the type for parameter $buffer_id not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
281
	 * @return Jetpack_Error|mixed|WP_Error  The result of the sending request.
282
	 */
283
	public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size = null, $buffer_id = null ) {
284
		$query_args = array(
285
			'sync'       => '1',             // Add an extra parameter to the URL so we can tell it's a sync action.
286
			'codec'      => $codec_name,
287
			'timestamp'  => $sent_timestamp,
288
			'queue'      => $queue_id,
289
			'home'       => Functions::home_url(),  // Send home url option to check for Identity Crisis server-side.
290
			'siteurl'    => Functions::site_url(),  // Send siteurl option to check for Identity Crisis server-side.
291
			'cd'         => sprintf( '%.4f', $checkout_duration ),
292
			'pd'         => sprintf( '%.4f', $preprocess_duration ),
293
			'queue_size' => $queue_size,
294
			'buffer_id'  => $buffer_id,
295
		);
296
297
		// Has the site opted in to IDC mitigation?
298
		if ( \Jetpack::sync_idc_optin() ) {
299
			$query_args['idc'] = true;
300
		}
301
302
		if ( \Jetpack_Options::get_option( 'migrate_for_idc', false ) ) {
303
			$query_args['migrate_for_idc'] = true;
304
		}
305
306
		$query_args['timeout'] = Settings::is_doing_cron() ? 30 : 15;
307
308
		/**
309
		 * Filters query parameters appended to the Sync request URL sent to WordPress.com.
310
		 *
311
		 * @since 4.7.0
312
		 *
313
		 * @param array $query_args associative array of query parameters.
314
		 */
315
		$query_args = apply_filters( 'jetpack_sync_send_data_query_args', $query_args );
316
317
		$connection = new Jetpack_Connection();
318
		$url        = add_query_arg( $query_args, $connection->xmlrpc_api_url() );
319
320
		// If we're currently updating to Jetpack 7.7, the IXR client may be missing briefly
321
		// because since 7.7 it's being autoloaded with Composer.
322
		if ( ! class_exists( '\\Jetpack_IXR_Client' ) ) {
323
			return new \WP_Error(
324
				'ixr_client_missing',
325
				esc_html__( 'Sync has been aborted because the IXR client is missing.', 'jetpack' )
326
			);
327
		}
328
329
		$rpc = new \Jetpack_IXR_Client(
330
			array(
331
				'url'     => $url,
332
				'user_id' => JETPACK_MASTER_USER,
333
				'timeout' => $query_args['timeout'],
334
			)
335
		);
336
337
		$result = $rpc->query( 'jetpack.syncActions', $data );
338
339
		if ( ! $result ) {
340
			return $rpc->get_jetpack_error();
341
		}
342
343
		$response = $rpc->getResponse();
344
345
		// Check if WordPress.com IDC mitigation blocked the sync request.
346
		if ( is_array( $response ) && isset( $response['error_code'] ) ) {
347
			$error_code              = $response['error_code'];
348
			$allowed_idc_error_codes = array(
349
				'jetpack_url_mismatch',
350
				'jetpack_home_url_mismatch',
351
				'jetpack_site_url_mismatch',
352
			);
353
354
			if ( in_array( $error_code, $allowed_idc_error_codes, true ) ) {
355
				\Jetpack_Options::update_option(
356
					'sync_error_idc',
357
					\Jetpack::get_sync_error_idc_option( $response )
358
				);
359
			}
360
361
			return new \WP_Error(
362
				'sync_error_idc',
363
				esc_html__( 'Sync has been blocked from WordPress.com because it would cause an identity crisis', 'jetpack' )
364
			);
365
		}
366
367
		return $response;
368
	}
369
370
	/**
371
	 * Kicks off the initial sync.
372
	 *
373
	 * @access public
374
	 * @static
375
	 *
376
	 * @return bool|null False if sync is not allowed.
377
	 */
378
	public static function do_initial_sync() {
379
		// Lets not sync if we are not suppose to.
380
		if ( ! self::sync_allowed() ) {
381
			return false;
382
		}
383
384
		// Don't start new sync if a full sync is in process.
385
		$full_sync_module = Modules::get_module( 'full-sync' );
386
		if ( $full_sync_module && $full_sync_module->is_started() && ! $full_sync_module->is_finished() ) {
387
			return false;
388
		}
389
390
		$initial_sync_config = array(
391
			'options'   => true,
392
			'functions' => true,
393
			'constants' => true,
394
			'users'     => array( get_current_user_id() ),
395
		);
396
397
		if ( is_multisite() ) {
398
			$initial_sync_config['network_options'] = true;
399
		}
400
401
		self::do_full_sync( $initial_sync_config );
402
	}
403
404
	/**
405
	 * Kicks off a full sync.
406
	 *
407
	 * @access public
408
	 * @static
409
	 *
410
	 * @param array $modules  The sync modules should be included in this full sync. All will be included if null.
411
	 * @return bool           True if full sync was successfully started.
412
	 */
413
	public static function do_full_sync( $modules = null ) {
414
		if ( ! self::sync_allowed() ) {
415
			return false;
416
		}
417
418
		$full_sync_module = Modules::get_module( 'full-sync' );
419
420
		if ( ! $full_sync_module ) {
421
			return false;
422
		}
423
424
		self::initialize_listener();
425
426
		$full_sync_module->start( $modules );
427
428
		return true;
429
	}
430
431
	/**
432
	 * Adds a cron schedule for regular syncing via cron, unless the schedule already exists.
433
	 *
434
	 * @access public
435
	 * @static
436
	 *
437
	 * @param array $schedules  The list of WordPress cron schedules prior to this filter.
438
	 * @return array            A list of WordPress cron schedules with the Jetpack sync interval added.
439
	 */
440
	public static function jetpack_cron_schedule( $schedules ) {
441
		if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
442
			$minutes = intval( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
443
			$display = ( 1 === $minutes ) ?
444
				__( 'Every minute', 'jetpack' ) :
445
				/* translators: %d is an integer indicating the number of minutes. */
446
				sprintf( __( 'Every %d minutes', 'jetpack' ), $minutes );
447
			$schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
448
				'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
449
				'display'  => $display,
450
			);
451
		}
452
		return $schedules;
453
	}
454
455
	/**
456
	 * Starts an incremental sync via cron.
457
	 *
458
	 * @access public
459
	 * @static
460
	 */
461
	public static function do_cron_sync() {
462
		self::do_cron_sync_by_type( 'sync' );
463
	}
464
465
	/**
466
	 * Starts a full sync via cron.
467
	 *
468
	 * @access public
469
	 * @static
470
	 */
471
	public static function do_cron_full_sync() {
472
		self::do_cron_sync_by_type( 'full_sync' );
473
	}
474
475
	/**
476
	 * Try to send actions until we run out of things to send,
477
	 * or have to wait more than 15s before sending again,
478
	 * or we hit a lock or some other sending issue
479
	 *
480
	 * @access public
481
	 * @static
482
	 *
483
	 * @param string $type Sync type. Can be `sync` or `full_sync`.
484
	 */
485
	public static function do_cron_sync_by_type( $type ) {
486
		if ( ! self::sync_allowed() || ( 'sync' !== $type && 'full_sync' !== $type ) ) {
487
			return;
488
		}
489
490
		self::initialize_sender();
491
492
		$time_limit = Settings::get_setting( 'cron_sync_time_limit' );
493
		$start_time = time();
494
495
		do {
496
			$next_sync_time = self::$sender->get_next_sync_time( $type );
497
498
			if ( $next_sync_time ) {
499
				$delay = $next_sync_time - time() + 1;
500
				if ( $delay > 15 ) {
501
					break;
502
				} elseif ( $delay > 0 ) {
503
					sleep( $delay );
504
				}
505
			}
506
507
			$result = 'full_sync' === $type ? self::$sender->do_full_sync() : self::$sender->do_sync();
508
		} while ( $result && ! is_wp_error( $result ) && ( $start_time + $time_limit ) > time() );
509
	}
510
511
	/**
512
	 * Initialize the sync listener.
513
	 *
514
	 * @access public
515
	 * @static
516
	 */
517
	public static function initialize_listener() {
518
		self::$listener = Listener::get_instance();
519
	}
520
521
	/**
522
	 * Initializes the sync sender.
523
	 *
524
	 * @access public
525
	 * @static
526
	 */
527
	public static function initialize_sender() {
528
		self::$sender = Sender::get_instance();
529
		add_filter( 'jetpack_sync_send_data', array( __CLASS__, 'send_data' ), 10, 8 );
530
	}
531
532
	/**
533
	 * Initializes sync for WooCommerce.
534
	 *
535
	 * @access public
536
	 * @static
537
	 */
538
	public static function initialize_woocommerce() {
539
		if ( false === class_exists( 'WooCommerce' ) ) {
540
			return;
541
		}
542
		add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
543
	}
544
545
	/**
546
	 * Adds Woo's sync modules to existing modules for sending.
547
	 *
548
	 * @access public
549
	 * @static
550
	 *
551
	 * @param array $sync_modules The list of sync modules declared prior to this filter.
552
	 * @return array A list of sync modules that now includes Woo's modules.
553
	 */
554
	public static function add_woocommerce_sync_module( $sync_modules ) {
555
		$sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce';
556
		return $sync_modules;
557
	}
558
559
	/**
560
	 * Initializes sync for WP Super Cache.
561
	 *
562
	 * @access public
563
	 * @static
564
	 */
565
	public static function initialize_wp_super_cache() {
566
		if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
567
			return;
568
		}
569
		add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
570
	}
571
572
	/**
573
	 * Adds WP Super Cache's sync modules to existing modules for sending.
574
	 *
575
	 * @access public
576
	 * @static
577
	 *
578
	 * @param array $sync_modules The list of sync modules declared prior to this filer.
579
	 * @return array A list of sync modules that now includes WP Super Cache's modules.
580
	 */
581
	public static function add_wp_super_cache_sync_module( $sync_modules ) {
582
		$sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache';
583
		return $sync_modules;
584
	}
585
586
	/**
587
	 * Sanitizes the name of sync's cron schedule.
588
	 *
589
	 * @access public
590
	 * @static
591
	 *
592
	 * @param string $schedule The name of a WordPress cron schedule.
593
	 * @return string The sanitized name of sync's cron schedule.
594
	 */
595
	public static function sanitize_filtered_sync_cron_schedule( $schedule ) {
596
		$schedule  = sanitize_key( $schedule );
597
		$schedules = wp_get_schedules();
598
599
		// Make sure that the schedule has actually been registered using the `cron_intervals` filter.
600
		if ( isset( $schedules[ $schedule ] ) ) {
601
			return $schedule;
602
		}
603
604
		return self::DEFAULT_SYNC_CRON_INTERVAL_NAME;
605
	}
606
607
	/**
608
	 * Allows offsetting of start times for sync cron jobs.
609
	 *
610
	 * @access public
611
	 * @static
612
	 *
613
	 * @param string $schedule The name of a cron schedule.
614
	 * @param string $hook     The hook that this method is responding to.
615
	 * @return int The offset for the sync cron schedule.
616
	 */
617
	public static function get_start_time_offset( $schedule = '', $hook = '' ) {
618
		$start_time_offset = is_multisite()
619
			? wp_rand( 0, ( 2 * self::DEFAULT_SYNC_CRON_INTERVAL_VALUE ) )
620
			: 0;
621
622
		/**
623
		 * Allows overriding the offset that the sync cron jobs will first run. This can be useful when scheduling
624
		 * cron jobs across multiple sites in a network.
625
		 *
626
		 * @since 4.5.0
627
		 *
628
		 * @param int    $start_time_offset
629
		 * @param string $hook
630
		 * @param string $schedule
631
		 */
632
		return intval(
633
			apply_filters(
634
				'jetpack_sync_cron_start_time_offset',
635
				$start_time_offset,
636
				$hook,
637
				$schedule
638
			)
639
		);
640
	}
641
642
	/**
643
	 * Decides if a sync cron should be scheduled.
644
	 *
645
	 * @access public
646
	 * @static
647
	 *
648
	 * @param string $schedule The name of a cron schedule.
649
	 * @param string $hook     The hook that this method is responding to.
650
	 */
651
	public static function maybe_schedule_sync_cron( $schedule, $hook ) {
652
		if ( ! $hook ) {
653
			return;
654
		}
655
		$schedule = self::sanitize_filtered_sync_cron_schedule( $schedule );
656
657
		$start_time = time() + self::get_start_time_offset( $schedule, $hook );
658
		if ( ! wp_next_scheduled( $hook ) ) {
659
			// Schedule a job to send pending queue items once a minute.
660
			wp_schedule_event( $start_time, $schedule, $hook );
661
		} elseif ( wp_get_schedule( $hook ) !== $schedule ) {
662
			// If the schedule has changed, update the schedule.
663
			wp_clear_scheduled_hook( $hook );
664
			wp_schedule_event( $start_time, $schedule, $hook );
665
		}
666
	}
667
668
	/**
669
	 * Clears Jetpack sync cron jobs.
670
	 *
671
	 * @access public
672
	 * @static
673
	 */
674
	public static function clear_sync_cron_jobs() {
675
		wp_clear_scheduled_hook( 'jetpack_sync_cron' );
676
		wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
677
	}
678
679
	/**
680
	 * Initializes Jetpack sync cron jobs.
681
	 *
682
	 * @access public
683
	 * @static
684
	 */
685
	public static function init_sync_cron_jobs() {
686
		add_filter( 'cron_schedules', array( __CLASS__, 'jetpack_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
687
688
		add_action( 'jetpack_sync_cron', array( __CLASS__, 'do_cron_sync' ) );
689
		add_action( 'jetpack_sync_full_cron', array( __CLASS__, 'do_cron_full_sync' ) );
690
691
		/**
692
		 * Allows overriding of the default incremental sync cron schedule which defaults to once every 5 minutes.
693
		 *
694
		 * @since 4.3.2
695
		 *
696
		 * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
697
		 */
698
		$incremental_sync_cron_schedule = apply_filters( 'jetpack_sync_incremental_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
699
		self::maybe_schedule_sync_cron( $incremental_sync_cron_schedule, 'jetpack_sync_cron' );
700
701
		/**
702
		 * Allows overriding of the full sync cron schedule which defaults to once every 5 minutes.
703
		 *
704
		 * @since 4.3.2
705
		 *
706
		 * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
707
		 */
708
		$full_sync_cron_schedule = apply_filters( 'jetpack_sync_full_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
709
		self::maybe_schedule_sync_cron( $full_sync_cron_schedule, 'jetpack_sync_full_cron' );
710
	}
711
712
	/**
713
	 * Perform maintenance when a plugin upgrade occurs.
714
	 *
715
	 * @access public
716
	 * @static
717
	 *
718
	 * @param string $new_version New version of the plugin.
719
	 * @param string $old_version Old version of the plugin.
720
	 */
721
	public static function cleanup_on_upgrade( $new_version = null, $old_version = null ) {
722
		if ( wp_next_scheduled( 'jetpack_sync_send_db_checksum' ) ) {
723
			wp_clear_scheduled_hook( 'jetpack_sync_send_db_checksum' );
724
		}
725
726
		$is_new_sync_upgrade = version_compare( $old_version, '4.2', '>=' );
727
		if ( ! empty( $old_version ) && $is_new_sync_upgrade && version_compare( $old_version, '4.5', '<' ) ) {
728
			self::clear_sync_cron_jobs();
729
			Settings::update_settings(
730
				array(
731
					'render_filtered_content' => Defaults::$default_render_filtered_content,
732
				)
733
			);
734
		}
735
736
		Health::on_jetpack_upgraded();
737
	}
738
739
	/**
740
	 * Get syncing status for the given fields.
741
	 *
742
	 * @access public
743
	 * @static
744
	 *
745
	 * @param string|null $fields A comma-separated string of the fields to include in the array from the JSON response.
746
	 * @return array An associative array with the status report.
747
	 */
748
	public static function get_sync_status( $fields = null ) {
749
		self::initialize_sender();
750
751
		$sync_module = Modules::get_module( 'full-sync' );
752
		$queue       = self::$sender->get_sync_queue();
753
754
		// _get_cron_array can be false
755
		$cron_timestamps = ( _get_cron_array() ) ? array_keys( _get_cron_array() ) : array();
756
		$next_cron       = ( ! empty( $cron_timestamps ) ) ? $cron_timestamps[0] - time() : '';
757
758
		$checksums = array();
759
760
		if ( ! empty( $fields ) ) {
761
			$store         = new Replicastore();
762
			$fields_params = array_map( 'trim', explode( ',', $fields ) );
763
764
			if ( in_array( 'posts_checksum', $fields_params, true ) ) {
765
				$checksums['posts_checksum'] = $store->posts_checksum();
766
			}
767
			if ( in_array( 'comments_checksum', $fields_params, true ) ) {
768
				$checksums['comments_checksum'] = $store->comments_checksum();
769
			}
770
			if ( in_array( 'post_meta_checksum', $fields_params, true ) ) {
771
				$checksums['post_meta_checksum'] = $store->post_meta_checksum();
772
			}
773
			if ( in_array( 'comment_meta_checksum', $fields_params, true ) ) {
774
				$checksums['comment_meta_checksum'] = $store->comment_meta_checksum();
775
			}
776
		}
777
778
		$full_sync_status = ( $sync_module ) ? $sync_module->get_status() : array();
779
780
		$full_queue = self::$sender->get_full_sync_queue();
781
782
		$result = array_merge(
783
			$full_sync_status,
784
			$checksums,
785
			array(
786
				'cron_size'            => count( $cron_timestamps ),
787
				'next_cron'            => $next_cron,
788
				'queue_size'           => $queue->size(),
789
				'queue_lag'            => $queue->lag(),
790
				'queue_next_sync'      => ( self::$sender->get_next_sync_time( 'sync' ) - microtime( true ) ),
791
				'full_queue_next_sync' => ( self::$sender->get_next_sync_time( 'full_sync' ) - microtime( true ) ),
792
			)
793
		);
794
795
		// Verify $sync_module is not false.
796
		if ( ( $sync_module ) && false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
797
			$result['full_queue_size'] = $full_queue->size();
798
			$result['full_queue_lag']  = $full_queue->lag();
799
		}
800
		return $result;
801
	}
802
}
803