PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 2.7.5
Jetpack – WP Security, Backup, Speed, & Growth v2.7.5
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / class.jetpack-sync.php

class.jetpack-sync.php in Jetpack – WP Security, Backup, Speed, & Growth 2.7.5, at class.jetpack-sync.php

714 lines 22.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Request that a piece of data on this WordPress install be synced back to the
5 * Jetpack server for remote processing/notifications/etc
6 */
7 class Jetpack_Sync {
8 // What modules want to sync what content
9 var $sync_conditions = array( 'posts' => array(), 'comments' => array() );
10
11 // We keep track of all the options registered for sync so that we can sync them all if needed
12 var $sync_options = array();
13
14 // Keep trac of status transitions, which we wouldn't always know about on the Jetpack Servers but are important when deciding what to do with the sync.
15 var $post_transitions = array();
16 var $comment_transitions = array();
17
18 // Objects to sync
19 var $sync = array();
20
21 function __construct() {
22 // WP Cron action. Only used on upgrade
23 add_action( 'jetpack_sync_all_registered_options', array( $this, 'sync_all_registered_options' ) );
24 }
25
26 /* Static Methods for Modules */
27
28 /**
29 * @param string $file __FILE__
30 * @param array settings:
31 * post_types => array( post_type slugs ): The post types to sync. Default: post, page
32 * post_stati => array( post_status slugs ): The post stati to sync. Default: publish
33 */
34 static function sync_posts( $file, array $settings = null ) {
35 $jetpack = Jetpack::init();
36 $args = func_get_args();
37 return call_user_func_array( array( $jetpack->sync, 'posts' ), $args );
38 }
39
40 /**
41 * @param string $file __FILE__
42 * @param array settings:
43 * post_types => array( post_type slugs ): The post types to sync. Default: post, page
44 * post_stati => array( post_status slugs ): The post stati to sync. Default: publish
45 * comment_types => array( comment_type slugs ): The comment types to sync. Default: '', comment, trackback, pingback
46 * comment_stati => array( comment_status slugs ): The comment stati to sync. Default: approved
47 */
48 static function sync_comments( $file, array $settings = null ) {
49 $jetpack = Jetpack::init();
50 $args = func_get_args();
51 return call_user_func_array( array( $jetpack->sync, 'comments' ), $args );
52 }
53
54 /**
55 * @param string $file __FILE__
56 * @param string $option, Option name to sync
57 * @param string $option ...
58 */
59 static function sync_options( $file, $option /*, $option, ... */ ) {
60 $jetpack = Jetpack::init();
61 $args = func_get_args();
62 return call_user_func_array( array( $jetpack->sync, 'options' ), $args );
63 }
64
65 /* Internal Methods */
66
67 /**
68 * Create a sync object/request
69 *
70 * @param string $object Type of object to sync -- [ post | comment | option ]
71 * @param int $id Unique identifier
72 * @param array $settings
73 */
74 function register( $object, $id = false, array $settings = null ) {
75 // Since we've registered something for sync, hook it up to execute on shutdown if we haven't already
76 if ( !$this->sync ) {
77 ignore_user_abort( true );
78 add_action( 'shutdown', array( $this, 'sync' ), 9 ); // Right before async XML-RPC
79 }
80
81 $defaults = array(
82 'on_behalf_of' => array(), // What modules want this data
83 );
84 $settings = wp_parse_args( $settings, $defaults );
85
86 if ( !isset( $this->sync[$object] ) ) {
87 $this->sync[$object] = array();
88 }
89
90 // Store the settings for this object
91 if (
92 // First time for this object
93 !isset( $this->sync[$object][$id] )
94 ) {
95 // Easy: store the current settings
96 $this->sync[$object][$id] = $settings;
97 } else {
98 // Not as easy: we have to manually merge the settings from previous runs for this object with the settings for this run
99
100 $this->sync[$object][$id]['on_behalf_of'] = array_unique( array_merge( $this->sync[$object][$id]['on_behalf_of'], $settings['on_behalf_of'] ) );
101 }
102
103 $delete_prefix = 'delete_';
104 if ( 0 === strpos( $object, $delete_prefix ) ) {
105 $unset_object = substr( $object, strlen( $delete_prefix ) );
106 } else {
107 $unset_object = "{$delete_prefix}{$object}";
108 }
109
110 // Ensure post ... delete_post yields a delete operation
111 // Ensure delete_post ... post yields a sync post operation
112 // Ensure update_option() ... delete_option() ends up as a delete
113 // Ensure delete_option() ... update_option() ends up as an update
114 // Etc.
115 unset( $this->sync[$unset_object][$id] );
116
117 return true;
118 }
119
120 function get_common_sync_data() {
121 $available_modules = Jetpack::get_available_modules();
122 $active_modules = Jetpack::get_active_modules();
123 $modules = array();
124 foreach ( $available_modules as $available_module ) {
125 $modules[$available_module] = in_array( $available_module, $active_modules );
126 }
127 $modules['vaultpress'] = class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' );
128
129 $sync_data = array(
130 'modules' => $modules,
131 'version' => JETPACK__VERSION,
132 );
133
134 return $sync_data;
135 }
136
137 /**
138 * Set up all the data and queue it for the outgoing XML-RPC request
139 */
140 function sync() {
141 if ( !$this->sync ) {
142 return false;
143 }
144
145 $sync_data = $this->get_common_sync_data();
146
147 $wp_importing = defined( 'WP_IMPORTING' ) && WP_IMPORTING;
148
149 foreach ( $this->sync as $sync_operation_type => $sync_operations ) {
150 switch ( $sync_operation_type ) {
151 case 'post':
152 if ( $wp_importing ) {
153 break;
154 }
155
156 $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
157 $GLOBALS['post'] = null;
158 foreach ( $sync_operations as $post_id => $settings ) {
159 $sync_data['post'][$post_id] = $this->get_post( $post_id );
160 if ( isset( $this->post_transitions[$post_id] ) ) {
161 $sync_data['post'][$post_id]['transitions'] = $this->post_transitions[$post_id];
162 } else {
163 $sync_data['post'][$post_id]['transitions'] = array( false, false );
164 }
165 $sync_data['post'][$post_id]['on_behalf_of'] = $settings['on_behalf_of'];
166 }
167 $GLOBALS['post'] = $global_post;
168 unset( $global_post );
169 break;
170 case 'comment':
171 if ( $wp_importing ) {
172 break;
173 }
174
175 $global_comment = isset( $GLOBALS['comment'] ) ? $GLOBALS['comment'] : null;
176 unset( $GLOBALS['comment'] );
177 foreach ( $sync_operations as $comment_id => $settings ) {
178 $sync_data['comment'][$comment_id] = $this->get_comment( $comment_id );
179 if ( isset( $this->comment_transitions[$comment_id] ) ) {
180 $sync_data['comment'][$comment_id]['transitions'] = $this->comment_transitions[$comment_id];
181 } else {
182 $sync_data['comment'][$comment_id]['transitions'] = array( false, false );
183 }
184 $sync_data['comment'][$comment_id]['on_behalf_of'] = $settings['on_behalf_of'];
185 }
186 $GLOBALS['comment'] = $global_comment;
187 unset( $global_comment );
188 break;
189 case 'option' :
190 foreach ( $sync_operations as $option => $settings ) {
191 $sync_data['option'][$option] = array( 'value' => get_option( $option ) );
192 }
193 break;
194
195 case 'delete_post':
196 case 'delete_comment':
197 foreach ( $sync_operations as $object_id => $settings ) {
198 $sync_data[$sync_operation_type][$object_id] = array( 'on_behalf_of' => $settings['on_behalf_of'] );
199 }
200 break;
201 case 'delete_option' :
202 foreach ( $sync_operations as $object_id => $settings ) {
203 $sync_data[$sync_operation_type][$object_id] = true;
204 }
205 break;
206 }
207 }
208
209 Jetpack::xmlrpc_async_call( 'jetpack.syncContent', $sync_data );
210 }
211
212 /**
213 * Format and return content data from a direct xmlrpc request for it.
214 *
215 * @param array $content_ids: array( 'posts' => array of ids, 'comments' => array of ids, 'options' => array of options )
216 */
217 function get_content( $content_ids ) {
218 $sync_data = $this->get_common_sync_data();
219
220 if ( isset( $content_ids['posts'] ) ) {
221 foreach ( $content_ids['posts'] as $id ) {
222 $sync_data['post'][$id] = $this->get_post( $id );
223 }
224 }
225
226 if ( isset( $content_ids['comments'] ) ) {
227 foreach ( $content_ids['comments'] as $id ) {
228 $sync_data['comment'][$id] = $this->get_post( $id );
229 }
230 }
231
232 if ( isset( $content_ids['options'] ) ) {
233 foreach ( $content_ids['options'] as $option ) {
234 $sync_data['option'][$option] = array( 'value' => get_option( $option ) );
235 }
236 }
237
238 return $sync_data;
239 }
240
241 /**
242 * Helper method for registering a post for sync
243 *
244 * @param int $id wp_posts.ID
245 * @param array $settings Sync data
246 */
247 function register_post( $id, array $settings = null ) {
248 $id = (int) $id;
249 if ( !$id ) {
250 return false;
251 }
252
253 $post = get_post( $id );
254 if ( !$post ) {
255 return false;
256 }
257
258 $settings = wp_parse_args( $settings, array(
259 'on_behalf_of' => array(),
260 ) );
261
262 return $this->register( 'post', $id, $settings );
263 }
264
265 /**
266 * Helper method for registering a comment for sync
267 *
268 * @param int $id wp_comments.comment_ID
269 * @param array $settings Sync data
270 */
271 function register_comment( $id, array $settings = null ) {
272 $id = (int) $id;
273 if ( !$id ) {
274 return false;
275 }
276
277 $comment = get_comment( $id );
278 if ( !$comment || empty( $comment->comment_post_ID ) ) {
279 return false;
280 }
281
282 $post = get_post( $comment->comment_post_ID );
283 if ( !$post ) {
284 return false;
285 }
286
287 $settings = wp_parse_args( $settings, array(
288 'on_behalf_of' => array(),
289 ) );
290
291 return $this->register( 'comment', $id, $settings );
292 }
293
294 /* Posts Sync */
295
296 function posts( $file, array $settings = null ) {
297 $module_slug = Jetpack::get_module_slug( $file );
298
299 $defaults = array(
300 'post_types' => array( 'post', 'page' ),
301 'post_stati' => array( 'publish' ),
302 );
303
304 $this->sync_conditions['posts'][$module_slug] = wp_parse_args( $settings, $defaults );
305
306 add_action( 'transition_post_status', array( $this, 'transition_post_status_action' ), 10, 3 );
307 add_action( 'delete_post', array( $this, 'delete_post_action' ) );
308 }
309
310 function delete_post_action( $post_id ) {
311 $post = get_post( $post_id );
312 if ( !$post ) {
313 return $this->register( 'delete_post', (int) $post_id );
314 }
315
316 $this->transition_post_status_action( 'delete', $post->post_status, $post );
317 }
318
319 function transition_post_status_action( $new_status, $old_status, $post ) {
320 $sync = $this->get_post_sync_operation( $new_status, $old_status, $post, $this->sync_conditions['posts'] );
321 if ( !$sync ) {
322 // No module wants to sync this post
323 return false;
324 }
325
326 // Track post transitions
327 if ( isset( $this->post_transitions[$post->ID] ) ) {
328 // status changed more than once - keep tha most recent $new_status
329 $this->post_transitions[$post->ID][0] = $new_status;
330 } else {
331 $this->post_transitions[$post->ID] = array( $new_status, $old_status );
332 }
333
334 $operation = $sync['operation'];
335 unset( $sync['operation'] );
336
337 switch ( $operation ) {
338 case 'delete' :
339 return $this->register( 'delete_post', (int) $post->ID, $sync );
340 case 'submit' :
341 return $this->register_post( (int) $post->ID, $sync );
342 }
343 }
344
345 function get_post_sync_operation( $new_status, $old_status, $post, $module_conditions ) {
346 $delete_on_behalf_of = array();
347 $submit_on_behalf_of = array();
348 $delete_stati = array( 'delete' );
349
350 foreach ( $module_conditions as $module => $conditions ) {
351 if ( !in_array( $post->post_type, $conditions['post_types'] ) ) {
352 continue;
353 }
354
355 $deleted_post = in_array( $new_status, $delete_stati );
356
357 if ( $deleted_post ) {
358 $delete_on_behalf_of[] = $module;
359 } else {
360 clean_post_cache( $post->ID );
361 $new_status = get_post_status( $post->ID ); // Inherited status is resolved here
362 }
363
364 $old_status_in_stati = in_array( $old_status, $conditions['post_stati'] );
365 $new_status_in_stati = in_array( $new_status, $conditions['post_stati'] );
366
367 if ( $old_status_in_stati && !$new_status_in_stati ) {
368 // Jetpack no longer needs the post
369 if ( !$deleted_post ) {
370 $delete_on_behalf_of[] = $module;
371 } // else, we've already flagged it above
372 continue;
373 }
374
375 if ( !$new_status_in_stati ) {
376 continue;
377 }
378
379 // At this point, we know we want to sync the post, not delete it
380 $submit_on_behalf_of[] = $module;
381 }
382
383 if ( !empty( $submit_on_behalf_of ) ) {
384 return array( 'operation' => 'submit', 'on_behalf_of' => $submit_on_behalf_of );
385 }
386
387 if ( !empty( $delete_on_behalf_of ) ) {
388 return array( 'operation' => 'delete', 'on_behalf_of' => $delete_on_behalf_of );
389 }
390
391 return false;
392 }
393
394 /**
395 * Get a post and associated data in the standard JP format.
396 * Cannot be called statically
397 *
398 * @param int $id Post ID
399 * @return Array containing full post details
400 */
401 function get_post( $id ) {
402 $post_obj = get_post( $id );
403 if ( !$post_obj )
404 return false;
405
406 if ( is_callable( $post_obj, 'to_array' ) ) {
407 // WP >= 3.5
408 $post = $post_obj->to_array();
409 } else {
410 // WP < 3.5
411 $post = get_object_vars( $post_obj );
412 }
413
414 if ( 0 < strlen( $post['post_password'] ) ) {
415 $post['post_password'] = 'auto-' . wp_generate_password( 10, false ); // We don't want the real password. Just pass something random.
416 }
417
418 // local optimizations
419 unset(
420 $post['filter'],
421 $post['ancestors'],
422 $post['post_content_filtered'],
423 $post['to_ping'],
424 $post['pinged']
425 );
426
427 if ( $this->is_post_public( $post ) ) {
428 $post['post_is_public'] = Jetpack_Options::get_option( 'public' );
429 } else {
430 //obscure content
431 $post['post_content'] = '';
432 $post['post_excerpt'] = '';
433 $post['post_is_public'] = false;
434 }
435 $post_type_obj = get_post_type_object( $post['post_type'] );
436 $post['post_is_excluded_from_search'] = $post_type_obj->exclude_from_search;
437
438 $post['tax'] = array();
439 $taxonomies = get_object_taxonomies( $post_obj );
440 foreach ( $taxonomies as $taxonomy ) {
441 $terms = get_object_term_cache( $post_obj->ID, $taxonomy );
442 if ( empty( $terms ) )
443 $terms = wp_get_object_terms( $post_obj->ID, $taxonomy );
444 $term_names = array();
445 foreach ( $terms as $term ) {
446 $term_names[] = $term->name;
447 }
448 $post['tax'][$taxonomy] = $term_names;
449 }
450
451 $meta = get_post_meta( $post_obj->ID, false );
452 $post['meta'] = array();
453 foreach ( $meta as $key => $value ) {
454 $post['meta'][$key] = array_map( 'maybe_unserialize', $value );
455 }
456
457 $post['extra'] = array(
458 'author' => get_the_author_meta( 'display_name', $post_obj->post_author ),
459 'author_email' => get_the_author_meta( 'email', $post_obj->post_author ),
460 );
461
462 if ( $fid = get_post_thumbnail_id( $id ) ) {
463 $feature = wp_get_attachment_image_src( $fid, 'large' );
464 if ( !empty( $feature[0] ) )
465 $post['extra']['featured_image'] = $feature[0];
466 }
467
468 $post['permalink'] = get_permalink( $post_obj->ID );
469 $post['shortlink'] = wp_get_shortlink( $post_obj->ID );
470 return $post;
471 }
472
473 /**
474 * Decide whether a post/page/attachment is visible to the public.
475 *
476 * @param array $post
477 * @return bool
478 */
479 function is_post_public( $post ) {
480 if ( !is_array( $post ) ) {
481 $post = (array) $post;
482 }
483
484 if ( 0 < strlen( $post['post_password'] ) )
485 return false;
486 if ( ! in_array( $post['post_type'], get_post_types( array( 'public' => true ) ) ) )
487 return false;
488 $post_status = get_post_status( $post['ID'] ); // Inherited status is resolved here.
489 if ( ! in_array( $post_status, get_post_stati( array( 'public' => true ) ) ) )
490 return false;
491 return true;
492 }
493
494 /* Comments Sync */
495
496 function comments( $file, array $settings = null ) {
497 $module_slug = Jetpack::get_module_slug( $file );
498
499 $defaults = array(
500 'post_types' => array( 'post', 'page' ), // For what post types will we sync comments?
501 'post_stati' => array( 'publish' ), // For what post stati will we sync comments?
502 'comment_types' => array( '', 'comment', 'trackback', 'pingback' ), // What comment types will we sync?
503 'comment_stati' => array( 'approved' ), // What comment stati will we sync?
504 );
505
506 $settings = wp_parse_args( $settings, $defaults );
507
508 $this->sync_conditions['comments'][$module_slug] = $settings;
509
510 add_action( 'wp_insert_comment', array( $this, 'wp_insert_comment_action' ), 10, 2 );
511 add_action( 'transition_comment_status', array( $this, 'transition_comment_status_action' ), 10, 3 );
512 add_action( 'edit_comment', array( $this, 'edit_comment_action' ) );
513 }
514
515 /*
516 * This is really annoying. If you edit a comment, but don't change the status, WordPress doesn't fire the transition_comment_status hook.
517 * That means we have to catch these comments on the edit_comment hook, but ignore comments on that hook when the transition_comment_status does fire.
518 */
519 function edit_comment_action( $comment_id ) {
520 $comment = get_comment( $comment_id );
521 $new_status = $this->translate_comment_status( $comment->comment_approved );
522 add_action( "comment_{$new_status}_{$comment->comment_type}", array( $this, 'transition_comment_status_for_comments_whose_status_does_not_change' ), 10, 2 );
523 }
524
525 function wp_insert_comment_action( $comment_id, $comment ) {
526 $this->transition_comment_status_action( $comment->comment_approved, 'new', $comment );
527 }
528
529 function transition_comment_status_for_comments_whose_status_does_not_change( $comment_id, $comment ) {
530 if ( isset( $this->comment_transitions[$comment_id] ) ) {
531 return $this->transition_comment_status_action( $comment->comment_approved, $this->comment_transitions[$comment_id][1], $comment );
532 }
533
534 return $this->transition_comment_status_action( $comment->comment_approved, $comment->comment_approved, $comment );
535 }
536
537 function translate_comment_status( $status ) {
538 switch ( (string) $status ) {
539 case '0' :
540 case 'hold' :
541 return 'unapproved';
542 case '1' :
543 case 'approve' :
544 return 'approved';
545 }
546
547 return $status;
548 }
549
550 function transition_comment_status_action( $new_status, $old_status, $comment ) {
551 $post = get_post( $comment->comment_post_ID );
552 if ( !$post ) {
553 return false;
554 }
555
556 foreach ( array( 'new_status', 'old_status' ) as $_status ) {
557 $$_status = $this->translate_comment_status( $$_status );
558 }
559
560 // Track comment transitions
561 if ( isset( $this->comment_transitions[$comment->comment_ID] ) ) {
562 // status changed more than once - keep tha most recent $new_status
563 $this->comment_transitions[$comment->comment_ID][0] = $new_status;
564 } else {
565 $this->comment_transitions[$comment->comment_ID] = array( $new_status, $old_status );
566 }
567
568 $post_sync = $this->get_post_sync_operation( $post->post_status, '_jetpack_test_sync', $post, $this->sync_conditions['comments'] );
569
570 if ( !$post_sync ) {
571 // No module wants to sync this comment because its post doesn't match any sync conditions
572 return false;
573 }
574
575 if ( 'delete' == $post_sync['operation'] ) {
576 // Had we been looking at post sync operations (instead of comment sync operations),
577 // this comment's post would have been deleted. Don't sync the comment.
578 return false;
579 }
580
581 $delete_on_behalf_of = array();
582 $submit_on_behalf_of = array();
583 $delete_stati = array( 'delete' );
584
585 foreach ( $this->sync_conditions['comments'] as $module => $conditions ) {
586 if ( !in_array( $comment->comment_type, $conditions['comment_types'] ) ) {
587 continue;
588 }
589
590 $deleted_comment = in_array( $new_status, $delete_stati );
591
592 if ( $deleted_comment ) {
593 $delete_on_behalf_of[] = $module;
594 }
595
596 $old_status_in_stati = in_array( $old_status, $conditions['comment_stati'] );
597 $new_status_in_stati = in_array( $new_status, $conditions['comment_stati'] );
598
599 if ( $old_status_in_stati && !$new_status_in_stati ) {
600 // Jetpack no longer needs the comment
601 if ( !$deleted_comment ) {
602 $delete_on_behalf_of[] = $module;
603 } // else, we've already flagged it above
604 continue;
605 }
606
607 if ( !$new_status_in_stati ) {
608 continue;
609 }
610
611 // At this point, we know we want to sync the comment, not delete it
612 $submit_on_behalf_of[] = $module;
613 }
614
615 if ( ! empty( $submit_on_behalf_of ) ) {
616 $this->register_post( $comment->comment_post_ID, array( 'on_behalf_of' => $submit_on_behalf_of ) );
617 return $this->register_comment( $comment->comment_ID, array( 'on_behalf_of' => $submit_on_behalf_of ) );
618 }
619
620 if ( !empty( $delete_on_behalf_of ) ) {
621 return $this->register( 'delete_comment', $comment->comment_ID, array( 'on_behalf_of' => $delete_on_behalf_of ) );
622 }
623
624 return false;
625 }
626
627 /**
628 * Get a comment and associated data in the standard JP format.
629 * Cannot be called statically
630 *
631 * @param int $id Comment ID
632 * @return Array containing full comment details
633 */
634 function get_comment( $id ) {
635 $comment_obj = get_comment( $id );
636 if ( !$comment_obj )
637 return false;
638 $comment = get_object_vars( $comment_obj );
639
640 $meta = get_comment_meta( $id, false );
641 $comment['meta'] = array();
642 foreach ( $meta as $key => $value ) {
643 $comment['meta'][$key] = array_map( 'maybe_unserialize', $value );
644 }
645
646 return $comment;
647 }
648
649 /* Options Sync */
650
651 /* Ah... so much simpler than Posts and Comments :) */
652 function options( $file, $option /*, $option, ... */ ) {
653 $options = func_get_args();
654 $file = array_shift( $options );
655
656 $module_slug = Jetpack::get_module_slug( $file );
657
658 if ( !isset( $this->sync_options[$module_slug] ) ) {
659 $this->sync_options[$module_slug] = array();
660 }
661
662 foreach ( $options as $option ) {
663 $this->sync_options[$module_slug][] = $option;
664 add_action( "delete_option_{$option}", array( $this, 'deleted_option_action' ) );
665 add_action( "update_option_{$option}", array( $this, 'updated_option_action' ) );
666 add_action( "add_option_{$option}", array( $this, 'added_option_action' ) );
667 }
668
669 $this->sync_options[$module_slug] = array_unique( $this->sync_options[$module_slug] );
670 }
671
672 function deleted_option_action( $option ) {
673 $this->register( 'delete_option', $option );
674 }
675
676 function updated_option_action( $old_value ) {
677 // The value of $option isn't passed to the filter
678 // Calculate it
679 $option = current_filter();
680 $prefix = 'update_option_';
681 if ( 0 !== strpos( $option, $prefix ) ) {
682 return;
683 }
684 $option = substr( $option, strlen( $prefix ) );
685
686 $this->added_option_action( $option );
687 }
688
689 function added_option_action( $option ) {
690 $this->register( 'option', $option );
691 }
692
693 function sync_all_module_options( $module_slug ) {
694 if ( empty( $this->sync_options[$module_slug] ) ) {
695 return;
696 }
697
698 foreach ( $this->sync_options[$module_slug] as $option ) {
699 $this->added_option_action( $option );
700 }
701 }
702
703 function sync_all_registered_options( $options = array() ) {
704 if ( 'jetpack_sync_all_registered_options' == current_filter() ) {
705 $all_registered_options = array_unique( call_user_func_array( 'array_merge', $this->sync_options ) );
706 foreach ( $all_registered_options as $option ) {
707 $this->added_option_action( $option );
708 }
709 } else {
710 wp_schedule_single_event( time(), 'jetpack_sync_all_registered_options', array( $this->sync_options ) );
711 }
712 }
713 }
714