Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Jetpack_Sync often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Sync, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
7 | class Jetpack_Sync { |
||
8 | // What modules want to sync what content |
||
9 | public $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 | public $sync_options = array(); |
||
13 | |||
14 | public $sync_constants = array(); |
||
15 | |||
16 | // 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. |
||
17 | public $post_transitions = array(); |
||
18 | public $comment_transitions = array(); |
||
19 | |||
20 | // Objects to sync |
||
21 | public $sync = array(); |
||
22 | |||
23 | function __construct() { |
||
24 | // WP Cron action. Only used on upgrade |
||
25 | add_action( 'jetpack_sync_all_registered_options', array( $this, 'sync_all_registered_options' ) ); |
||
26 | add_action( 'jetpack_heartbeat', array( $this, 'sync_all_registered_options' ) ); |
||
27 | |||
28 | // Sync constants on heartbeat and plugin upgrade and connects |
||
29 | add_action( 'init', array( $this, 'register_constants_as_options' ) ); |
||
30 | add_action( 'jetpack_sync_all_registered_options', array( $this, 'sync_all_constants' ) ); |
||
31 | add_action( 'jetpack_heartbeat', array( $this, 'sync_all_constants' ) ); |
||
32 | |||
33 | add_action( 'jetpack_activate_module', array( $this, 'sync_module_constants' ), 10, 1 ); |
||
34 | } |
||
35 | |||
36 | /* Static Methods for Modules */ |
||
37 | |||
38 | /** |
||
39 | * @param string $file __FILE__ |
||
40 | * @param array settings: |
||
41 | * post_types => array( post_type slugs ): The post types to sync. Default: post, page |
||
42 | * post_stati => array( post_status slugs ): The post stati to sync. Default: publish |
||
43 | */ |
||
44 | View Code Duplication | static function sync_posts( $file, array $settings = null ) { |
|
45 | if ( is_network_admin() ) return; |
||
46 | $jetpack = Jetpack::init(); |
||
47 | $args = func_get_args(); |
||
48 | return call_user_func_array( array( $jetpack->sync, 'posts' ), $args ); |
||
49 | } |
||
50 | |||
51 | /** |
||
52 | * @param string $file __FILE__ |
||
53 | * @param array settings: |
||
54 | * post_types => array( post_type slugs ): The post types to sync. Default: post, page |
||
55 | * post_stati => array( post_status slugs ): The post stati to sync. Default: publish |
||
56 | * comment_types => array( comment_type slugs ): The comment types to sync. Default: '', comment, trackback, pingback |
||
57 | * comment_stati => array( comment_status slugs ): The comment stati to sync. Default: approved |
||
58 | */ |
||
59 | View Code Duplication | static function sync_comments( $file, array $settings = null ) { |
|
60 | if ( is_network_admin() ) return; |
||
61 | $jetpack = Jetpack::init(); |
||
62 | $args = func_get_args(); |
||
63 | return call_user_func_array( array( $jetpack->sync, 'comments' ), $args ); |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * @param string $file __FILE__ |
||
68 | * @param string $option, Option name to sync |
||
69 | * @param string $option ... |
||
70 | */ |
||
71 | View Code Duplication | static function sync_options( $file, $option /*, $option, ... */ ) { |
|
72 | if ( is_network_admin() ) return; |
||
73 | $jetpack = Jetpack::init(); |
||
74 | $args = func_get_args(); |
||
75 | return call_user_func_array( array( $jetpack->sync, 'options' ), $args ); |
||
76 | } |
||
77 | /** |
||
78 | * @param string $file __FILE__ |
||
79 | * @param string $option, Option name to sync |
||
80 | * @param string $option ... |
||
81 | */ |
||
82 | View Code Duplication | static function sync_constant( $file, $constant ) { |
|
83 | if ( is_network_admin() ) return; |
||
84 | $jetpack = Jetpack::init(); |
||
85 | $args = func_get_args(); |
||
86 | return call_user_func_array( array( $jetpack->sync, 'constant' ), $args ); |
||
87 | } |
||
88 | |||
89 | /* Internal Methods */ |
||
90 | |||
91 | /** |
||
92 | * Create a sync object/request |
||
93 | * |
||
94 | * @param string $object Type of object to sync -- [ post | comment | option ] |
||
95 | * @param int $id Unique identifier |
||
96 | * @param array $settings |
||
97 | */ |
||
98 | function register( $object, $id = false, array $settings = null ) { |
||
99 | // Since we've registered something for sync, hook it up to execute on shutdown if we haven't already |
||
100 | if ( !$this->sync ) { |
||
101 | if ( function_exists( 'ignore_user_abort' ) ) { |
||
102 | ignore_user_abort( true ); |
||
103 | } |
||
104 | add_action( 'shutdown', array( $this, 'sync' ), 9 ); // Right before async XML-RPC |
||
105 | } |
||
106 | |||
107 | $defaults = array( |
||
108 | 'on_behalf_of' => array(), // What modules want this data |
||
109 | ); |
||
110 | $settings = wp_parse_args( $settings, $defaults ); |
||
111 | |||
112 | if ( !isset( $this->sync[$object] ) ) { |
||
113 | $this->sync[$object] = array(); |
||
114 | } |
||
115 | |||
116 | // Store the settings for this object |
||
117 | if ( |
||
118 | // First time for this object |
||
119 | !isset( $this->sync[$object][$id] ) |
||
120 | ) { |
||
121 | // Easy: store the current settings |
||
122 | $this->sync[$object][$id] = $settings; |
||
123 | } else { |
||
124 | // Not as easy: we have to manually merge the settings from previous runs for this object with the settings for this run |
||
125 | |||
126 | $this->sync[$object][$id]['on_behalf_of'] = array_unique( array_merge( $this->sync[$object][$id]['on_behalf_of'], $settings['on_behalf_of'] ) ); |
||
127 | } |
||
128 | |||
129 | $delete_prefix = 'delete_'; |
||
130 | if ( 0 === strpos( $object, $delete_prefix ) ) { |
||
131 | $unset_object = substr( $object, strlen( $delete_prefix ) ); |
||
132 | } else { |
||
133 | $unset_object = "{$delete_prefix}{$object}"; |
||
134 | } |
||
135 | |||
136 | // Ensure post ... delete_post yields a delete operation |
||
137 | // Ensure delete_post ... post yields a sync post operation |
||
138 | // Ensure update_option() ... delete_option() ends up as a delete |
||
139 | // Ensure delete_option() ... update_option() ends up as an update |
||
140 | // Etc. |
||
141 | unset( $this->sync[$unset_object][$id] ); |
||
142 | |||
143 | return true; |
||
144 | } |
||
145 | |||
146 | function get_common_sync_data() { |
||
147 | $available_modules = Jetpack::get_available_modules(); |
||
148 | $active_modules = Jetpack::get_active_modules(); |
||
149 | $modules = array(); |
||
150 | foreach ( $available_modules as $available_module ) { |
||
151 | $modules[$available_module] = in_array( $available_module, $active_modules ); |
||
152 | } |
||
153 | $modules['vaultpress'] = class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ); |
||
154 | |||
155 | $sync_data = array( |
||
156 | 'modules' => $modules, |
||
157 | 'version' => JETPACK__VERSION, |
||
158 | 'is_multisite' => is_multisite(), |
||
159 | ); |
||
160 | |||
161 | return $sync_data; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Set up all the data and queue it for the outgoing XML-RPC request |
||
166 | */ |
||
167 | function sync() { |
||
168 | if ( !$this->sync ) { |
||
169 | return false; |
||
170 | } |
||
171 | |||
172 | // Don't sync anything from a staging site. |
||
173 | if ( Jetpack::is_development_mode() || Jetpack::is_staging_site() ) { |
||
174 | return false; |
||
175 | } |
||
176 | |||
177 | $sync_data = $this->get_common_sync_data(); |
||
178 | |||
179 | $wp_importing = defined( 'WP_IMPORTING' ) && WP_IMPORTING; |
||
180 | |||
181 | foreach ( $this->sync as $sync_operation_type => $sync_operations ) { |
||
182 | switch ( $sync_operation_type ) { |
||
183 | View Code Duplication | case 'post': |
|
184 | if ( $wp_importing ) { |
||
185 | break; |
||
186 | } |
||
187 | |||
188 | $global_post = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null; |
||
189 | $GLOBALS['post'] = null; |
||
190 | foreach ( $sync_operations as $post_id => $settings ) { |
||
191 | $sync_data['post'][$post_id] = $this->get_post( $post_id ); |
||
192 | if ( isset( $this->post_transitions[$post_id] ) ) { |
||
193 | $sync_data['post'][$post_id]['transitions'] = $this->post_transitions[$post_id]; |
||
194 | } else { |
||
195 | $sync_data['post'][$post_id]['transitions'] = array( false, false ); |
||
196 | } |
||
197 | $sync_data['post'][$post_id]['on_behalf_of'] = $settings['on_behalf_of']; |
||
198 | } |
||
199 | $GLOBALS['post'] = $global_post; |
||
200 | unset( $global_post ); |
||
201 | break; |
||
202 | View Code Duplication | case 'comment': |
|
203 | if ( $wp_importing ) { |
||
204 | break; |
||
205 | } |
||
206 | |||
207 | $global_comment = isset( $GLOBALS['comment'] ) ? $GLOBALS['comment'] : null; |
||
208 | unset( $GLOBALS['comment'] ); |
||
209 | foreach ( $sync_operations as $comment_id => $settings ) { |
||
210 | $sync_data['comment'][$comment_id] = $this->get_comment( $comment_id ); |
||
211 | if ( isset( $this->comment_transitions[$comment_id] ) ) { |
||
212 | $sync_data['comment'][$comment_id]['transitions'] = $this->comment_transitions[$comment_id]; |
||
213 | } else { |
||
214 | $sync_data['comment'][$comment_id]['transitions'] = array( false, false ); |
||
215 | } |
||
216 | $sync_data['comment'][$comment_id]['on_behalf_of'] = $settings['on_behalf_of']; |
||
217 | } |
||
218 | $GLOBALS['comment'] = $global_comment; |
||
219 | unset( $global_comment ); |
||
220 | break; |
||
221 | case 'option' : |
||
222 | foreach ( $sync_operations as $option => $settings ) { |
||
223 | $sync_data['option'][ $option ] = array( 'value' => get_option( $option ) ); |
||
224 | } |
||
225 | break; |
||
226 | |||
227 | case 'constant' : |
||
228 | foreach( $sync_operations as $constant => $settings ) { |
||
229 | $sync_data['constant'][ $constant ] = array( 'value' => $this->get_constant( $constant ) ); |
||
230 | } |
||
231 | break; |
||
232 | |||
233 | case 'delete_post': |
||
234 | case 'delete_comment': |
||
235 | foreach ( $sync_operations as $object_id => $settings ) { |
||
236 | $sync_data[$sync_operation_type][$object_id] = array( 'on_behalf_of' => $settings['on_behalf_of'] ); |
||
237 | } |
||
238 | break; |
||
239 | case 'delete_option' : |
||
240 | foreach ( $sync_operations as $object_id => $settings ) { |
||
241 | $sync_data[$sync_operation_type][$object_id] = true; |
||
242 | } |
||
243 | break; |
||
244 | } |
||
245 | } |
||
246 | Jetpack::xmlrpc_async_call( 'jetpack.syncContent', $sync_data ); |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * Format and return content data from a direct xmlrpc request for it. |
||
251 | * |
||
252 | * @param array $content_ids: array( 'posts' => array of ids, 'comments' => array of ids, 'options' => array of options ) |
||
253 | */ |
||
254 | function get_content( $content_ids ) { |
||
255 | $sync_data = $this->get_common_sync_data(); |
||
256 | |||
257 | if ( isset( $content_ids['posts'] ) ) { |
||
258 | foreach ( $content_ids['posts'] as $id ) { |
||
259 | $sync_data['post'][$id] = $this->get_post( $id ); |
||
260 | } |
||
261 | } |
||
262 | |||
263 | if ( isset( $content_ids['comments'] ) ) { |
||
264 | foreach ( $content_ids['comments'] as $id ) { |
||
265 | $sync_data['comment'][$id] = $this->get_post( $id ); |
||
266 | } |
||
267 | } |
||
268 | |||
269 | if ( isset( $content_ids['options'] ) ) { |
||
270 | foreach ( $content_ids['options'] as $option ) { |
||
271 | $sync_data['option'][$option] = array( 'value' => get_option( $option ) ); |
||
272 | } |
||
273 | } |
||
274 | |||
275 | return $sync_data; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Helper method for registering a post for sync |
||
280 | * |
||
281 | * @param int $id wp_posts.ID |
||
282 | * @param array $settings Sync data |
||
283 | */ |
||
284 | function register_post( $id, array $settings = null ) { |
||
285 | $id = (int) $id; |
||
286 | if ( !$id ) { |
||
287 | return false; |
||
288 | } |
||
289 | |||
290 | $post = get_post( $id ); |
||
291 | if ( !$post ) { |
||
292 | return false; |
||
293 | } |
||
294 | |||
295 | $settings = wp_parse_args( $settings, array( |
||
296 | 'on_behalf_of' => array(), |
||
297 | ) ); |
||
298 | |||
299 | return $this->register( 'post', $id, $settings ); |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * Helper method for registering a comment for sync |
||
304 | * |
||
305 | * @param int $id wp_comments.comment_ID |
||
306 | * @param array $settings Sync data |
||
307 | */ |
||
308 | function register_comment( $id, array $settings = null ) { |
||
309 | $id = (int) $id; |
||
310 | if ( !$id ) { |
||
311 | return false; |
||
312 | } |
||
313 | |||
314 | $comment = get_comment( $id ); |
||
315 | if ( !$comment || empty( $comment->comment_post_ID ) ) { |
||
316 | return false; |
||
317 | } |
||
318 | |||
319 | $post = get_post( $comment->comment_post_ID ); |
||
320 | if ( !$post ) { |
||
321 | return false; |
||
322 | } |
||
323 | |||
324 | $settings = wp_parse_args( $settings, array( |
||
325 | 'on_behalf_of' => array(), |
||
326 | ) ); |
||
327 | |||
328 | return $this->register( 'comment', $id, $settings ); |
||
329 | } |
||
330 | |||
331 | /* Posts Sync */ |
||
332 | |||
333 | function posts( $file, array $settings = null ) { |
||
334 | $module_slug = Jetpack::get_module_slug( $file ); |
||
335 | |||
336 | $defaults = array( |
||
337 | 'post_types' => array( 'post', 'page' ), |
||
338 | 'post_stati' => array( 'publish' ), |
||
339 | ); |
||
340 | |||
341 | $this->sync_conditions['posts'][$module_slug] = wp_parse_args( $settings, $defaults ); |
||
342 | |||
343 | add_action( 'transition_post_status', array( $this, 'transition_post_status_action' ), 10, 3 ); |
||
344 | add_action( 'delete_post', array( $this, 'delete_post_action' ) ); |
||
345 | } |
||
346 | |||
347 | function delete_post_action( $post_id ) { |
||
348 | $post = get_post( $post_id ); |
||
349 | if ( !$post ) { |
||
350 | return $this->register( 'delete_post', (int) $post_id ); |
||
351 | } |
||
352 | |||
353 | $this->transition_post_status_action( 'delete', $post->post_status, $post ); |
||
354 | } |
||
355 | |||
356 | function transition_post_status_action( $new_status, $old_status, $post ) { |
||
357 | $sync = $this->get_post_sync_operation( $new_status, $old_status, $post, $this->sync_conditions['posts'] ); |
||
358 | if ( !$sync ) { |
||
359 | // No module wants to sync this post |
||
360 | return false; |
||
361 | } |
||
362 | |||
363 | // Track post transitions |
||
364 | View Code Duplication | if ( isset( $this->post_transitions[$post->ID] ) ) { |
|
365 | // status changed more than once - keep tha most recent $new_status |
||
366 | $this->post_transitions[$post->ID][0] = $new_status; |
||
367 | } else { |
||
368 | $this->post_transitions[$post->ID] = array( $new_status, $old_status ); |
||
369 | } |
||
370 | |||
371 | $operation = $sync['operation']; |
||
372 | unset( $sync['operation'] ); |
||
373 | |||
374 | switch ( $operation ) { |
||
375 | case 'delete' : |
||
376 | return $this->register( 'delete_post', (int) $post->ID, $sync ); |
||
377 | case 'submit' : |
||
378 | return $this->register_post( (int) $post->ID, $sync ); |
||
379 | } |
||
380 | } |
||
381 | |||
382 | function get_post_sync_operation( $new_status, $old_status, $post, $module_conditions ) { |
||
383 | $delete_on_behalf_of = array(); |
||
384 | $submit_on_behalf_of = array(); |
||
385 | $delete_stati = array( 'delete' ); |
||
386 | $cache_cleared = false; |
||
387 | |||
388 | foreach ( $module_conditions as $module => $conditions ) { |
||
389 | if ( !in_array( $post->post_type, $conditions['post_types'] ) ) { |
||
390 | continue; |
||
391 | } |
||
392 | |||
393 | $deleted_post = in_array( $new_status, $delete_stati ); |
||
394 | |||
395 | if ( $deleted_post ) { |
||
396 | $delete_on_behalf_of[] = $module; |
||
397 | } else { |
||
398 | if ( ! $cache_cleared ) { |
||
399 | // inefficient to clear cache more than once |
||
400 | clean_post_cache( $post->ID ); |
||
401 | $cache_cleared = true; |
||
402 | } |
||
403 | $new_status = get_post_status( $post->ID ); // Inherited status is resolved here |
||
404 | } |
||
405 | |||
406 | $old_status_in_stati = in_array( $old_status, $conditions['post_stati'] ); |
||
407 | $new_status_in_stati = in_array( $new_status, $conditions['post_stati'] ); |
||
408 | |||
409 | if ( $old_status_in_stati && !$new_status_in_stati ) { |
||
410 | // Jetpack no longer needs the post |
||
411 | if ( !$deleted_post ) { |
||
412 | $delete_on_behalf_of[] = $module; |
||
413 | } // else, we've already flagged it above |
||
414 | continue; |
||
415 | } |
||
416 | |||
417 | if ( !$new_status_in_stati ) { |
||
418 | continue; |
||
419 | } |
||
420 | |||
421 | // At this point, we know we want to sync the post, not delete it |
||
422 | $submit_on_behalf_of[] = $module; |
||
423 | } |
||
424 | |||
425 | if ( !empty( $submit_on_behalf_of ) ) { |
||
426 | return array( 'operation' => 'submit', 'on_behalf_of' => $submit_on_behalf_of ); |
||
427 | } |
||
428 | |||
429 | if ( !empty( $delete_on_behalf_of ) ) { |
||
430 | return array( 'operation' => 'delete', 'on_behalf_of' => $delete_on_behalf_of ); |
||
431 | } |
||
432 | |||
433 | return false; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Get a post and associated data in the standard JP format. |
||
438 | * Cannot be called statically |
||
439 | * |
||
440 | * @param int $id Post ID |
||
441 | * @return Array containing full post details |
||
442 | */ |
||
443 | function get_post( $id ) { |
||
560 | |||
561 | /** |
||
562 | * Decide whether a post/page/attachment is visible to the public. |
||
563 | * |
||
564 | * @param array $post |
||
565 | * @return bool |
||
566 | */ |
||
567 | function is_post_public( $post ) { |
||
568 | if ( !is_array( $post ) ) { |
||
569 | $post = (array) $post; |
||
570 | } |
||
571 | |||
572 | if ( 0 < strlen( $post['post_password'] ) ) |
||
573 | return false; |
||
574 | if ( ! in_array( $post['post_type'], get_post_types( array( 'public' => true ) ) ) ) |
||
575 | return false; |
||
576 | $post_status = get_post_status( $post['ID'] ); // Inherited status is resolved here. |
||
577 | if ( ! in_array( $post_status, get_post_stati( array( 'public' => true ) ) ) ) |
||
578 | return false; |
||
579 | return true; |
||
580 | } |
||
581 | |||
582 | /* Comments Sync */ |
||
583 | |||
584 | function comments( $file, array $settings = null ) { |
||
585 | $module_slug = Jetpack::get_module_slug( $file ); |
||
586 | |||
587 | $defaults = array( |
||
588 | 'post_types' => array( 'post', 'page' ), // For what post types will we sync comments? |
||
589 | 'post_stati' => array( 'publish' ), // For what post stati will we sync comments? |
||
590 | 'comment_types' => array( '', 'comment', 'trackback', 'pingback' ), // What comment types will we sync? |
||
591 | 'comment_stati' => array( 'approved' ), // What comment stati will we sync? |
||
592 | ); |
||
593 | |||
594 | $settings = wp_parse_args( $settings, $defaults ); |
||
595 | |||
596 | $this->sync_conditions['comments'][$module_slug] = $settings; |
||
597 | |||
598 | add_action( 'wp_insert_comment', array( $this, 'wp_insert_comment_action' ), 10, 2 ); |
||
599 | add_action( 'transition_comment_status', array( $this, 'transition_comment_status_action' ), 10, 3 ); |
||
600 | add_action( 'edit_comment', array( $this, 'edit_comment_action' ) ); |
||
601 | } |
||
602 | |||
603 | /* |
||
604 | * This is really annoying. If you edit a comment, but don't change the status, WordPress doesn't fire the transition_comment_status hook. |
||
605 | * 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. |
||
606 | */ |
||
607 | function edit_comment_action( $comment_id ) { |
||
608 | $comment = get_comment( $comment_id ); |
||
609 | $new_status = $this->translate_comment_status( $comment->comment_approved ); |
||
610 | add_action( "comment_{$new_status}_{$comment->comment_type}", array( $this, 'transition_comment_status_for_comments_whose_status_does_not_change' ), 10, 2 ); |
||
611 | } |
||
612 | |||
613 | function wp_insert_comment_action( $comment_id, $comment ) { |
||
616 | |||
617 | function transition_comment_status_for_comments_whose_status_does_not_change( $comment_id, $comment ) { |
||
618 | if ( isset( $this->comment_transitions[$comment_id] ) ) { |
||
619 | return $this->transition_comment_status_action( $comment->comment_approved, $this->comment_transitions[$comment_id][1], $comment ); |
||
620 | } |
||
621 | |||
622 | return $this->transition_comment_status_action( $comment->comment_approved, $comment->comment_approved, $comment ); |
||
623 | } |
||
624 | |||
625 | function translate_comment_status( $status ) { |
||
626 | switch ( (string) $status ) { |
||
627 | case '0' : |
||
628 | case 'hold' : |
||
629 | return 'unapproved'; |
||
630 | case '1' : |
||
631 | case 'approve' : |
||
632 | return 'approved'; |
||
633 | } |
||
634 | |||
635 | return $status; |
||
636 | } |
||
637 | |||
638 | function transition_comment_status_action( $new_status, $old_status, $comment ) { |
||
639 | $post = get_post( $comment->comment_post_ID ); |
||
640 | if ( !$post ) { |
||
641 | return false; |
||
642 | } |
||
643 | |||
644 | foreach ( array( 'new_status', 'old_status' ) as $_status ) { |
||
645 | $$_status = $this->translate_comment_status( $$_status ); |
||
646 | } |
||
647 | |||
648 | // Track comment transitions |
||
649 | View Code Duplication | if ( isset( $this->comment_transitions[$comment->comment_ID] ) ) { |
|
650 | // status changed more than once - keep tha most recent $new_status |
||
651 | $this->comment_transitions[$comment->comment_ID][0] = $new_status; |
||
652 | } else { |
||
653 | $this->comment_transitions[$comment->comment_ID] = array( $new_status, $old_status ); |
||
654 | } |
||
655 | |||
656 | $post_sync = $this->get_post_sync_operation( $post->post_status, '_jetpack_test_sync', $post, $this->sync_conditions['comments'] ); |
||
657 | |||
658 | if ( !$post_sync ) { |
||
659 | // No module wants to sync this comment because its post doesn't match any sync conditions |
||
660 | return false; |
||
661 | } |
||
662 | |||
663 | if ( 'delete' == $post_sync['operation'] ) { |
||
664 | // Had we been looking at post sync operations (instead of comment sync operations), |
||
665 | // this comment's post would have been deleted. Don't sync the comment. |
||
666 | return false; |
||
667 | } |
||
668 | |||
669 | $delete_on_behalf_of = array(); |
||
670 | $submit_on_behalf_of = array(); |
||
671 | $delete_stati = array( 'delete' ); |
||
672 | |||
673 | foreach ( $this->sync_conditions['comments'] as $module => $conditions ) { |
||
674 | if ( !in_array( $comment->comment_type, $conditions['comment_types'] ) ) { |
||
675 | continue; |
||
676 | } |
||
677 | |||
678 | $deleted_comment = in_array( $new_status, $delete_stati ); |
||
679 | |||
680 | if ( $deleted_comment ) { |
||
681 | $delete_on_behalf_of[] = $module; |
||
682 | } |
||
683 | |||
684 | $old_status_in_stati = in_array( $old_status, $conditions['comment_stati'] ); |
||
685 | $new_status_in_stati = in_array( $new_status, $conditions['comment_stati'] ); |
||
686 | |||
687 | if ( $old_status_in_stati && !$new_status_in_stati ) { |
||
688 | // Jetpack no longer needs the comment |
||
689 | if ( !$deleted_comment ) { |
||
690 | $delete_on_behalf_of[] = $module; |
||
691 | } // else, we've already flagged it above |
||
692 | continue; |
||
693 | } |
||
694 | |||
695 | if ( !$new_status_in_stati ) { |
||
696 | continue; |
||
697 | } |
||
698 | |||
699 | // At this point, we know we want to sync the comment, not delete it |
||
700 | $submit_on_behalf_of[] = $module; |
||
701 | } |
||
702 | |||
703 | if ( ! empty( $submit_on_behalf_of ) ) { |
||
704 | $this->register_post( $comment->comment_post_ID, array( 'on_behalf_of' => $submit_on_behalf_of ) ); |
||
705 | return $this->register_comment( $comment->comment_ID, array( 'on_behalf_of' => $submit_on_behalf_of ) ); |
||
706 | } |
||
707 | |||
708 | if ( !empty( $delete_on_behalf_of ) ) { |
||
709 | return $this->register( 'delete_comment', $comment->comment_ID, array( 'on_behalf_of' => $delete_on_behalf_of ) ); |
||
710 | } |
||
711 | |||
712 | return false; |
||
713 | } |
||
714 | |||
715 | /** |
||
716 | * Get a comment and associated data in the standard JP format. |
||
717 | * Cannot be called statically |
||
718 | * |
||
719 | * @param int $id Comment ID |
||
720 | * @return Array containing full comment details |
||
721 | */ |
||
722 | function get_comment( $id ) { |
||
723 | $comment_obj = get_comment( $id ); |
||
724 | if ( !$comment_obj ) |
||
725 | return false; |
||
726 | $comment = get_object_vars( $comment_obj ); |
||
727 | |||
728 | $meta = get_comment_meta( $id, false ); |
||
729 | $comment['meta'] = array(); |
||
730 | foreach ( $meta as $key => $value ) { |
||
731 | $comment['meta'][$key] = array_map( 'maybe_unserialize', $value ); |
||
732 | } |
||
733 | |||
734 | return $comment; |
||
735 | } |
||
736 | |||
737 | /* Options Sync */ |
||
738 | |||
739 | /* Ah... so much simpler than Posts and Comments :) */ |
||
740 | function options( $file, $option /*, $option, ... */ ) { |
||
741 | $options = func_get_args(); |
||
742 | $file = array_shift( $options ); |
||
743 | |||
744 | $module_slug = Jetpack::get_module_slug( $file ); |
||
745 | |||
746 | if ( !isset( $this->sync_options[$module_slug] ) ) { |
||
747 | $this->sync_options[$module_slug] = array(); |
||
748 | } |
||
749 | |||
750 | foreach ( $options as $option ) { |
||
751 | $this->sync_options[$module_slug][] = $option; |
||
752 | add_action( "delete_option_{$option}", array( $this, 'deleted_option_action' ) ); |
||
753 | add_action( "update_option_{$option}", array( $this, 'updated_option_action' ) ); |
||
754 | add_action( "add_option_{$option}", array( $this, 'added_option_action' ) ); |
||
755 | } |
||
756 | |||
757 | $this->sync_options[$module_slug] = array_unique( $this->sync_options[$module_slug] ); |
||
758 | } |
||
759 | |||
760 | function deleted_option_action( $option ) { |
||
763 | |||
764 | function updated_option_action() { |
||
765 | // The value of $option isn't passed to the filter |
||
766 | // Calculate it |
||
767 | $option = current_filter(); |
||
768 | $prefix = 'update_option_'; |
||
769 | if ( 0 !== strpos( $option, $prefix ) ) { |
||
770 | return; |
||
771 | } |
||
772 | $option = substr( $option, strlen( $prefix ) ); |
||
773 | |||
774 | $this->added_option_action( $option ); |
||
775 | } |
||
776 | |||
777 | function added_option_action( $option ) { |
||
780 | |||
781 | function sync_all_module_options( $module_slug ) { |
||
782 | if ( empty( $this->sync_options[$module_slug] ) ) { |
||
783 | return; |
||
784 | } |
||
785 | |||
786 | foreach ( $this->sync_options[$module_slug] as $option ) { |
||
787 | $this->added_option_action( $option ); |
||
788 | } |
||
789 | } |
||
790 | |||
791 | function sync_all_registered_options() { |
||
792 | if ( 'jetpack_sync_all_registered_options' == current_filter() ) { |
||
793 | add_action( 'shutdown', array( $this, 'register_all_options' ), 8 ); |
||
794 | } else { |
||
795 | wp_schedule_single_event( time(), 'jetpack_sync_all_registered_options', array( $this->sync_options ) ); |
||
796 | } |
||
797 | } |
||
798 | |||
799 | /** |
||
800 | * All the options that are defined in modules as well as class.jetpack.php will get synced. |
||
801 | * Registers all options to be synced. |
||
802 | */ |
||
803 | function register_all_options() { |
||
804 | $all_registered_options = array_unique( call_user_func_array( 'array_merge', $this->sync_options ) ); |
||
805 | foreach ( $all_registered_options as $option ) { |
||
806 | $this->added_option_action( $option ); |
||
807 | } |
||
808 | } |
||
809 | |||
810 | /* Constants Sync */ |
||
811 | |||
812 | function get_all_constants() { |
||
813 | return array( |
||
814 | 'EMPTY_TRASH_DAYS', |
||
815 | 'WP_POST_REVISIONS', |
||
816 | 'AUTOMATIC_UPDATER_DISABLED', |
||
817 | 'ABSPATH', |
||
818 | 'WP_CONTENT_DIR', |
||
819 | 'FS_METHOD', |
||
820 | 'DISALLOW_FILE_EDIT', |
||
821 | 'DISALLOW_FILE_MODS', |
||
822 | 'WP_AUTO_UPDATE_CORE', |
||
823 | 'WP_HTTP_BLOCK_EXTERNAL', |
||
824 | 'WP_ACCESSIBLE_HOSTS', |
||
825 | ); |
||
826 | } |
||
827 | /** |
||
828 | * This lets us get the constant value like get_option( 'jetpack_constant_CONSTANT' ); |
||
829 | * Not the best way to get the constant value but necessery in some cases like in the API. |
||
830 | */ |
||
831 | function register_constants_as_options() { |
||
832 | foreach( $this->get_all_constants() as $constant ) { |
||
833 | add_filter( 'pre_option_jetpack_constant_'. $constant, array( $this, 'get_default_constant' ) ); |
||
834 | } |
||
835 | } |
||
836 | |||
837 | function sync_all_constants() { |
||
838 | // add the constant to sync. |
||
839 | foreach( $this->get_all_constants() as $constant ) { |
||
840 | $this->register_constant( $constant ); |
||
841 | } |
||
842 | add_action( 'shutdown', array( $this, 'register_all_module_constants' ), 8 ); |
||
843 | } |
||
844 | |||
845 | /** |
||
846 | * Returns default values of Constants |
||
847 | */ |
||
848 | function default_constant( $constant ) { |
||
849 | switch( $constant ) { |
||
850 | case 'WP_AUTO_UPDATE_CORE': |
||
851 | return 'minor'; |
||
852 | break; |
||
853 | |||
854 | default: |
||
855 | return null; |
||
856 | break; |
||
857 | } |
||
858 | } |
||
859 | |||
860 | function register_all_module_constants() { |
||
861 | // also add the contstants from each module to be synced. |
||
862 | foreach( $this->sync_constants as $module ) { |
||
863 | foreach( $module as $constant ) { |
||
864 | $this->register_constant( $constant ); |
||
865 | } |
||
866 | } |
||
867 | } |
||
868 | |||
869 | /** |
||
870 | * Sync constants required by the module that was just activated. |
||
871 | * If you add Jetpack_Sync::sync_constant( __FILE__, 'HELLO_WORLD' ); |
||
872 | * to the module it will start syncing the constant after the constant has been updated. |
||
873 | * |
||
874 | * This function gets called on module activation. |
||
875 | */ |
||
876 | function sync_module_constants( $module ) { |
||
885 | |||
886 | public function reindex_needed() { |
||
889 | |||
890 | public function reindex_trigger() { |
||
891 | $response = array( 'status' => 'ERROR' ); |
||
892 | |||
893 | // Force a privacy check |
||
894 | Jetpack::check_privacy( JETPACK__PLUGIN_FILE ); |
||
895 | |||
896 | Jetpack::load_xml_rpc_client(); |
||
897 | $client = new Jetpack_IXR_Client( array( |
||
898 | 'user_id' => JETPACK_MASTER_USER, |
||
899 | ) ); |
||
900 | |||
901 | $client->query( 'jetpack.reindexTrigger' ); |
||
902 | |||
903 | if ( !$client->isError() ) { |
||
904 | $response = $client->getResponse(); |
||
905 | Jetpack_Options::update_option( 'sync_bulk_reindexing', true ); |
||
906 | } |
||
907 | |||
908 | return $response; |
||
909 | } |
||
910 | |||
911 | public function reindex_status() { |
||
912 | $response = array( 'status' => 'ERROR' ); |
||
913 | |||
914 | // Assume reindexing is done if it was not triggered in the first place |
||
915 | if ( false === Jetpack_Options::get_option( 'sync_bulk_reindexing' ) ) { |
||
916 | return array( 'status' => 'DONE' ); |
||
917 | } |
||
918 | |||
919 | Jetpack::load_xml_rpc_client(); |
||
920 | $client = new Jetpack_IXR_Client( array( |
||
921 | 'user_id' => JETPACK_MASTER_USER, |
||
922 | ) ); |
||
923 | |||
924 | $client->query( 'jetpack.reindexStatus' ); |
||
925 | |||
926 | if ( !$client->isError() ) { |
||
927 | $response = $client->getResponse(); |
||
928 | if ( 'DONE' == $response['status'] ) { |
||
929 | Jetpack_Options::delete_option( 'sync_bulk_reindexing' ); |
||
930 | } |
||
931 | } |
||
932 | |||
933 | return $response; |
||
934 | } |
||
935 | |||
936 | public function reindex_ui() { |
||
937 | $strings = json_encode( array( |
||
938 | 'WAITING' => array( |
||
939 | 'action' => __( 'Refresh Status', 'jetpack' ), |
||
940 | 'status' => __( 'Indexing request queued and waiting…', 'jetpack' ), |
||
941 | ), |
||
942 | 'INDEXING' => array( |
||
943 | 'action' => __( 'Refresh Status', 'jetpack' ), |
||
944 | 'status' => __( 'Indexing posts', 'jetpack' ), |
||
945 | ), |
||
946 | 'DONE' => array( |
||
947 | 'action' => __( 'Reindex Posts', 'jetpack' ), |
||
948 | 'status' => __( 'Posts indexed.', 'jetpack' ), |
||
949 | ), |
||
950 | 'ERROR' => array( |
||
951 | 'action' => __( 'Refresh Status', 'jetpack' ), |
||
952 | 'status' => __( 'Status unknown.', 'jetpack' ), |
||
953 | ), |
||
954 | 'ERROR:LARGE' => array( |
||
955 | 'action' => __( 'Refresh Status', 'jetpack' ), |
||
956 | 'status' => __( 'This site is too large, please contact Jetpack support to sync.', 'jetpack' ), |
||
957 | ), |
||
958 | ) ); |
||
959 | |||
960 | wp_enqueue_script( |
||
961 | 'jetpack_sync_reindex_control', |
||
962 | plugins_url( '_inc/jquery.jetpack-sync.js', JETPACK__PLUGIN_FILE ), |
||
963 | array( 'jquery' ), |
||
964 | JETPACK__VERSION |
||
965 | ); |
||
966 | |||
967 | $template = <<<EOT |
||
968 | <p class="jetpack_sync_reindex_control" id="jetpack_sync_reindex_control" data-strings="%s"> |
||
969 | <input type="submit" class="jetpack_sync_reindex_control_action button" value="%s" disabled /> |
||
970 | <span class="jetpack_sync_reindex_control_status">…</span> |
||
971 | </p> |
||
972 | EOT; |
||
973 | |||
974 | return sprintf( |
||
975 | $template, |
||
976 | esc_attr( $strings ), |
||
977 | esc_attr__( 'Refresh Status', 'jetpack' ) |
||
978 | ); |
||
979 | } |
||
980 | |||
981 | private function _get_post_count_local() { |
||
982 | global $wpdb; |
||
983 | return (int) $wpdb->get_var( |
||
984 | "SELECT count(*) |
||
985 | FROM {$wpdb->posts} |
||
986 | WHERE post_status = 'publish' AND post_password = ''" |
||
987 | ); |
||
988 | } |
||
989 | |||
990 | private function _get_post_count_cloud() { |
||
991 | $blog_id = Jetpack::init()->get_option( 'id' ); |
||
992 | |||
993 | $body = array( |
||
994 | 'size' => 1, |
||
995 | ); |
||
996 | |||
997 | $response = wp_remote_post( |
||
998 | "https://public-api.wordpress.com/rest/v1/sites/$blog_id/search", |
||
999 | array( |
||
1000 | 'timeout' => 10, |
||
1001 | 'user-agent' => 'jetpack_related_posts', |
||
1002 | 'sslverify' => true, |
||
1003 | 'body' => $body, |
||
1004 | ) |
||
1005 | ); |
||
1006 | |||
1007 | if ( is_wp_error( $response ) ) { |
||
1008 | return 0; |
||
1009 | } |
||
1010 | |||
1011 | $results = json_decode( wp_remote_retrieve_body( $response ), true ); |
||
1012 | |||
1013 | return isset( $results['results'] ) && isset( $results['results']['total'] ) ? (int) $results['results']['total'] : 0; |
||
1014 | } |
||
1015 | |||
1016 | /** |
||
1017 | * Sometimes we need to fake options to be able to sync data with .com |
||
1018 | * This is a helper function. That will make it easier to do just that. |
||
1019 | * |
||
1020 | * It will make sure that the options are synced when do_action( 'jetpack_sync_all_registered_options' ); |
||
1021 | * |
||
1022 | * Which should happen everytime we update Jetpack to a new version or daily by Jetpack_Heartbeat. |
||
1023 | * |
||
1024 | * $callback is a function that is passed into a filter that returns the value of the option. |
||
1025 | * This value should never be false. Since we want to short circuit the get_option function |
||
1026 | * to return the value of the our callback. |
||
1027 | * |
||
1028 | * You can also trigger an update when a something else changes by calling the |
||
1029 | * do_action( 'add_option_jetpack_' . $option, 'jetpack_'.$option, $callback_function ); |
||
1030 | * on the action that should that would trigger the update. |
||
1031 | * |
||
1032 | * |
||
1033 | * @param string $option Option will always be prefixed with Jetpack and be saved on .com side |
||
1034 | * @param string or array $callback |
||
1035 | */ |
||
1036 | function mock_option( $option , $callback ) { |
||
1037 | add_filter( 'pre_option_jetpack_'. $option, $callback ); |
||
1038 | // This shouldn't happen but if it does we return the same as before. |
||
1039 | add_filter( 'option_jetpack_'. $option, $callback ); |
||
1040 | // Instead of passing a file we just pass in a string. |
||
1041 | $this->options( 'mock-option' , 'jetpack_' . $option ); |
||
1042 | |||
1043 | } |
||
1044 | /** |
||
1045 | * Sometimes you need to sync constants to .com |
||
1046 | * Using the function will allow you to do just that. |
||
1047 | * |
||
1048 | * @param 'string' $constant Constants defined in code. |
||
1049 | * |
||
1050 | */ |
||
1051 | function register_constant( $constant ) { |
||
1054 | |||
1055 | function get_default_constant() { |
||
1068 | /** |
||
1069 | * Simular to $this->options() function. |
||
1070 | * Add the constant to be synced to .com when we activate the module. |
||
1071 | * As well as on heartbeat and plugin upgrade and connection to .com. |
||
1072 | * |
||
1073 | * @param string $file |
||
1074 | * @param string $constant |
||
1075 | */ |
||
1076 | function constant( $file, $constant ) { |
||
1077 | $constants = func_get_args(); |
||
1078 | $file = array_shift( $constants ); |
||
1079 | |||
1080 | $module_slug = Jetpack::get_module_slug( $file ); |
||
1081 | |||
1082 | if ( ! isset( $this->sync_constants[ $module_slug ] ) ) { |
||
1083 | $this->sync_constants[ $module_slug ] = array(); |
||
1084 | } |
||
1085 | |||
1086 | foreach ( $constants as $constant ) { |
||
1087 | $this->sync_constants[ $module_slug ][] = $constant; |
||
1088 | } |
||
1089 | } |
||
1090 | |||
1091 | /** |
||
1092 | * Helper function to return the constants value. |
||
1093 | * |
||
1094 | * @param string $constant |
||
1095 | * @return value of the constant or null if the constant is set to false or doesn't exits. |
||
1096 | */ |
||
1097 | static function get_constant( $constant ) { |
||
1098 | if ( defined( $constant ) ) { |
||
1099 | return constant( $constant ); |
||
1100 | } |
||
1101 | |||
1102 | return null; |
||
1103 | } |
||
1104 | } |
||
1105 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.