PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 6.3.7
Jetpack – WP Security, Backup, Speed, & Growth v6.3.7
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 14.4.2 All 500 releases
jetpack / sync / class.jetpack-sync-module-posts.php
class.jetpack-sync-module-posts.php
411 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 require_once dirname( __FILE__ ) . '/class.jetpack-sync-settings.php';
4
5 class Jetpack_Sync_Module_Posts extends Jetpack_Sync_Module {
6
7 private $just_published = array();
8 private $previous_status = array();
9 private $action_handler;
10 private $import_end = false;
11
12 const DEFAULT_PREVIOUS_STATE = 'new';
13
14 public function name() {
15 return 'posts';
16 }
17
18 public function get_object_by_id( $object_type, $id ) {
19 if ( $object_type === 'post' && $post = get_post( intval( $id ) ) ) {
20 return $this->filter_post_content_and_add_links( $post );
21 }
22
23 return false;
24 }
25
26 public function init_listeners( $callable ) {
27 $this->action_handler = $callable;
28
29 // Core < 4.7 doesn't deal with nested wp_insert_post calls very well
30 global $wp_version;
31 $priority = version_compare( $wp_version, '4.7-alpha', '<' ) ? 0 : 11;
32
33 add_action( 'wp_insert_post', array( $this, 'wp_insert_post' ), $priority, 3 );
34 add_action( 'jetpack_sync_save_post', $callable, 10, 4 );
35
36 add_action( 'deleted_post', $callable, 10 );
37 add_action( 'jetpack_published_post', $callable, 10, 2 );
38
39 add_action( 'transition_post_status', array( $this, 'save_published' ), 10, 3 );
40 add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_post', array( $this, 'filter_blacklisted_post_types' ) );
41
42 // listen for meta changes
43 $this->init_listeners_for_meta_type( 'post', $callable );
44 $this->init_meta_whitelist_handler( 'post', array( $this, 'filter_meta' ) );
45
46 add_action( 'jetpack_daily_akismet_meta_cleanup_before', array( $this, 'daily_akismet_meta_cleanup_before' ) );
47 add_action( 'jetpack_daily_akismet_meta_cleanup_after', array( $this, 'daily_akismet_meta_cleanup_after' ) );
48 add_action( 'jetpack_post_meta_batch_delete', $callable, 10, 2 );
49
50 }
51
52
53 public function daily_akismet_meta_cleanup_before( $feedback_ids ) {
54 remove_action( 'deleted_post_meta', $this->action_handler );
55 /**
56 * Used for syncing deletion of batch post meta
57 *
58 * @since 6.1.0
59 *
60 * @module sync
61 *
62 * $param array $feedback_ids feedback post IDs
63 * $param string $meta_key to be deleted
64 */
65 do_action( 'jetpack_post_meta_batch_delete', $feedback_ids, '_feedback_akismet_values');
66 }
67
68 public function daily_akismet_meta_cleanup_after( $feedback_ids ) {
69 add_action( 'deleted_post_meta', $this->action_handler );
70 }
71
72 public function init_full_sync_listeners( $callable ) {
73 add_action( 'jetpack_full_sync_posts', $callable ); // also sends post meta
74 }
75
76 public function init_before_send() {
77 add_filter( 'jetpack_sync_before_send_jetpack_sync_save_post', array( $this, 'expand_jetpack_sync_save_post' ) );
78
79 // full sync
80 add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_post_ids' ) );
81 }
82
83 public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
84 global $wpdb;
85
86 return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_posts', $wpdb->posts, 'ID', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
87 }
88
89 public function estimate_full_sync_actions( $config ) {
90 global $wpdb;
91
92 $query = "SELECT count(*) FROM $wpdb->posts WHERE " . $this->get_where_sql( $config );
93 $count = $wpdb->get_var( $query );
94
95 return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
96 }
97
98 private function get_where_sql( $config ) {
99 $where_sql = Jetpack_Sync_Settings::get_blacklisted_post_types_sql();
100
101 // config is a list of post IDs to sync
102 if ( is_array( $config ) ) {
103 $where_sql .= ' AND ID IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
104 }
105
106 return $where_sql;
107 }
108
109 function get_full_sync_actions() {
110 return array( 'jetpack_full_sync_posts' );
111 }
112
113 /**
114 * Process content before send
115 *
116 * @param array $args wp_insert_post arguments
117 *
118 * @return array
119 */
120 function expand_jetpack_sync_save_post( $args ) {
121 list( $post_id, $post, $update, $previous_state ) = $args;
122 return array( $post_id, $this->filter_post_content_and_add_links( $post ), $update, $previous_state );
123 }
124
125 function filter_blacklisted_post_types( $args ) {
126 $post = $args[1];
127
128 if ( in_array( $post->post_type, Jetpack_Sync_Settings::get_setting( 'post_types_blacklist' ) ) ) {
129 return false;
130 }
131
132 return $args;
133 }
134
135 // Meta
136 function filter_meta( $args ) {
137 if ( $this->is_post_type_allowed( $args[1] ) && $this->is_whitelisted_post_meta( $args[2] ) ) {
138 return $args;
139 }
140
141 return false;
142 }
143
144 function is_whitelisted_post_meta( $meta_key ) {
145 // _wpas_skip_ is used by publicize
146 return in_array( $meta_key, Jetpack_Sync_Settings::get_setting( 'post_meta_whitelist' ) ) || wp_startswith( $meta_key, '_wpas_skip_' );
147 }
148
149 function is_post_type_allowed( $post_id ) {
150 $post = get_post( intval( $post_id ) );
151 if( $post->post_type ) {
152 return ! in_array( $post->post_type, Jetpack_Sync_Settings::get_setting( 'post_types_blacklist' ) );
153 }
154 return false;
155 }
156
157 function remove_embed() {
158 global $wp_embed;
159 remove_filter( 'the_content', array( $wp_embed, 'run_shortcode' ), 8 );
160 // remove the embed shortcode since we would do the part later.
161 remove_shortcode( 'embed' );
162 // Attempts to embed all URLs in a post
163 remove_filter( 'the_content', array( $wp_embed, 'autoembed' ), 8 );
164 }
165
166 function add_embed() {
167 global $wp_embed;
168 add_filter( 'the_content', array( $wp_embed, 'run_shortcode' ), 8 );
169 // Shortcode placeholder for strip_shortcodes()
170 add_shortcode( 'embed', '__return_false' );
171 // Attempts to embed all URLs in a post
172 add_filter( 'the_content', array( $wp_embed, 'autoembed' ), 8 );
173 }
174
175 // Expands wp_insert_post to include filtered content
176 function filter_post_content_and_add_links( $post_object ) {
177 global $post;
178 $post = $post_object;
179
180 // return non existant post
181 $post_type = get_post_type_object( $post->post_type );
182 if ( empty( $post_type ) || ! is_object( $post_type ) ) {
183 $non_existant_post = new stdClass();
184 $non_existant_post->ID = $post->ID;
185 $non_existant_post->post_modified = $post->post_modified;
186 $non_existant_post->post_modified_gmt = $post->post_modified_gmt;
187 $non_existant_post->post_status = 'jetpack_sync_non_registered_post_type';
188
189 return $non_existant_post;
190 }
191 /**
192 * Filters whether to prevent sending post data to .com
193 *
194 * Passing true to the filter will prevent the post data from being sent
195 * to the WordPress.com.
196 * Instead we pass data that will still enable us to do a checksum against the
197 * Jetpacks data but will prevent us from displaying the data on in the API as well as
198 * other services.
199 * @since 4.2.0
200 *
201 * @param boolean false prevent post data from being synced to WordPress.com
202 * @param mixed $post WP_POST object
203 */
204 if ( apply_filters( 'jetpack_sync_prevent_sending_post_data', false, $post ) ) {
205 // We only send the bare necessary object to be able to create a checksum.
206 $blocked_post = new stdClass();
207 $blocked_post->ID = $post->ID;
208 $blocked_post->post_modified = $post->post_modified;
209 $blocked_post->post_modified_gmt = $post->post_modified_gmt;
210 $blocked_post->post_status = 'jetpack_sync_blocked';
211
212 return $blocked_post;
213 }
214
215 // lets not do oembed just yet.
216 $this->remove_embed();
217
218 if ( 0 < strlen( $post->post_password ) ) {
219 $post->post_password = 'auto-' . wp_generate_password( 10, false );
220 }
221
222 /** This filter is already documented in core. wp-includes/post-template.php */
223 if ( Jetpack_Sync_Settings::get_setting( 'render_filtered_content' ) && $post_type->public ) {
224 global $shortcode_tags;
225 /**
226 * Filter prevents some shortcodes from expanding.
227 *
228 * Since we can can expand some type of shortcode better on the .com side and make the
229 * expansion more relevant to contexts. For example [galleries] and subscription emails
230 *
231 * @since 4.5.0
232 *
233 * @param array of shortcode tags to remove.
234 */
235 $shortcodes_to_remove = apply_filters( 'jetpack_sync_do_not_expand_shortcodes', array(
236 'gallery',
237 'slideshow'
238 ) );
239 $removed_shortcode_callbacks = array();
240 foreach ( $shortcodes_to_remove as $shortcode ) {
241 if ( isset ( $shortcode_tags[ $shortcode ] ) ) {
242 $removed_shortcode_callbacks[ $shortcode ] = $shortcode_tags[ $shortcode ];
243 }
244 }
245
246 array_map( 'remove_shortcode', array_keys( $removed_shortcode_callbacks ) );
247
248 $post->post_content_filtered = apply_filters( 'the_content', $post->post_content );
249 $post->post_excerpt_filtered = apply_filters( 'the_excerpt', $post->post_excerpt );
250
251 foreach ( $removed_shortcode_callbacks as $shortcode => $callback ) {
252 add_shortcode( $shortcode, $callback );
253 }
254 }
255
256 $this->add_embed();
257
258 if ( has_post_thumbnail( $post->ID ) ) {
259 $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' );
260 if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
261 $post->featured_image = $image_attributes[0];
262 }
263 }
264
265 $post->permalink = get_permalink( $post->ID );
266 $post->shortlink = wp_get_shortlink( $post->ID );
267
268 if ( function_exists( 'amp_get_permalink' ) ) {
269 $post->amp_permalink = amp_get_permalink( $post->ID );
270 }
271
272 return $post;
273 }
274
275 public function save_published( $new_status, $old_status, $post ) {
276 if ( 'publish' === $new_status && 'publish' !== $old_status ) {
277 $this->just_published[ $post->ID ] = true;
278 }
279
280 $this->previous_status[ $post->ID ] = $old_status;
281 }
282
283 public function wp_insert_post( $post_ID, $post = null, $update = null ) {
284 if ( ! is_numeric( $post_ID ) || is_null( $post ) ) {
285 return;
286 }
287
288 // workaround for https://github.com/woocommerce/woocommerce/issues/18007
289 if ( $post && 'shop_order' === $post->post_type ) {
290 $post = get_post( $post_ID );
291 }
292
293 $previous_status = isset( $this->previous_status[ $post_ID ] ) ?
294 $this->previous_status[ $post_ID ] :
295 self::DEFAULT_PREVIOUS_STATE;
296
297 $just_published = isset( $this->just_published[ $post_ID ] ) ?
298 $this->just_published[ $post_ID ] :
299 false;
300
301 $state = array(
302 'is_auto_save' => (bool) Jetpack_Constants::get_constant( 'DOING_AUTOSAVE' ),
303 'previous_status' => $previous_status,
304 'just_published' => $just_published
305 );
306 /**
307 * Filter that is used to add to the post flags ( meta data ) when a post gets published
308 *
309 * @since 5.8.0
310 *
311 * @param int $post_ID the post ID
312 * @param mixed $post WP_POST object
313 * @param bool $update Whether this is an existing post being updated or not.
314 * @param mixed $state state
315 *
316 * @module sync
317 */
318 do_action( 'jetpack_sync_save_post', $post_ID, $post, $update, $state );
319 unset( $this->previous_status[ $post_ID ] );
320 $this->send_published( $post_ID, $post );
321 }
322
323 public function send_published( $post_ID, $post ) {
324 if ( ! isset( $this->just_published[ $post_ID ] ) ) {
325 return;
326 }
327
328 // Post revisions cause race conditions where this send_published add the action before the actual post gets synced
329 if ( wp_is_post_autosave( $post ) || wp_is_post_revision( $post ) ) {
330 return;
331 }
332
333 $post_flags = array(
334 'post_type' => $post->post_type
335 );
336
337 $author_user_object = get_user_by( 'id', $post->post_author );
338 if ( $author_user_object ) {
339 $post_flags['author'] = array(
340 'id' => $post->post_author,
341 'wpcom_user_id' => get_user_meta( $post->post_author, 'wpcom_user_id', true ),
342 'display_name' => $author_user_object->display_name,
343 'email' => $author_user_object->user_email,
344 'translated_role' => Jetpack::translate_user_to_role( $author_user_object ),
345 );
346 }
347
348 /**
349 * Filter that is used to add to the post flags ( meta data ) when a post gets published
350 *
351 * @since 4.4.0
352 *
353 * @param mixed array post flags that are added to the post
354 * @param mixed $post WP_POST object
355 */
356 $flags = apply_filters( 'jetpack_published_post_flags', $post_flags, $post );
357
358 /**
359 * Action that gets synced when a post type gets published.
360 *
361 * @since 4.4.0
362 *
363 * @param int $post_ID
364 * @param mixed array $flags post flags that are added to the post
365 */
366 do_action( 'jetpack_published_post', $post_ID, $flags );
367 unset( $this->just_published[ $post_ID ] );
368
369 /**
370 * Send additional sync action for Activity Log when post is a Customizer publish
371 */
372 if ( 'customize_changeset' == $post->post_type ) {
373 $post_content = json_decode( $post->post_content, true );
374 foreach ( $post_content as $key => $value ) {
375 //Skip if it isn't a widget
376 if ( 'widget_' != substr( $key, 0, strlen( 'widget_' ) ) ) {
377 continue;
378 }
379 // Change key from "widget_archives[2]" to "archives-2"
380 $key = str_replace( 'widget_', '', $key );
381 $key = str_replace( '[', '-', $key );
382 $key = str_replace( ']', '', $key );
383
384 global $wp_registered_widgets;
385 if ( isset( $wp_registered_widgets[ $key ] ) ) {
386 $widget_data = array(
387 'name' => $wp_registered_widgets[ $key ]['name'],
388 'id' => $key,
389 'title' => $value['value']['title'],
390 );
391 do_action( 'jetpack_widget_edited', $widget_data );
392 }
393 }
394 }
395 }
396
397 public function expand_post_ids( $args ) {
398 $post_ids = $args[0];
399
400 $posts = array_filter( array_map( array( 'WP_Post', 'get_instance' ), $post_ids ) );
401 $posts = array_map( array( $this, 'filter_post_content_and_add_links' ), $posts );
402 $posts = array_values( $posts ); // reindex in case posts were deleted
403
404 return array(
405 $posts,
406 $this->get_metadata( $post_ids, 'post', Jetpack_Sync_Settings::get_setting( 'post_meta_whitelist' ) ),
407 $this->get_term_relationships( $post_ids ),
408 );
409 }
410 }
411