PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 3.0.6
Jetpack – WP Security, Backup, Speed, & Growth v3.0.6
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 3.0.6, at class.jetpack-sync.php

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