PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 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 All 506 releases
← All changes | jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php +320 -126 13.8.3 → 16.3-a.7 View file →
@@ -8,11 +8,16 @@
8 8 namespace Automattic\Jetpack\Sync\Modules;
9 9
10 10 use Automattic\Jetpack\Constants as Jetpack_Constants;
11 11 use Automattic\Jetpack\Roles;
12 +use Automattic\Jetpack\Sync\Activity_Log_Event;
12 13 use Automattic\Jetpack\Sync\Modules;
13 14 use Automattic\Jetpack\Sync\Settings;
14 15
16 +if ( ! defined( 'ABSPATH' ) ) {
17 + exit( 0 );
18 +}
19 +
15 20 /**
16 21 * Class to handle sync for posts.
17 22 */
18 23 class Posts extends Module {
@@ -43,8 +48,17 @@
43 48 */
44 49 private $action_handler;
45 50
46 51 /**
52 + * Mark posts that are deleted in the current request.
53 + *
54 + * @access private
55 + *
56 + * @var array
57 + */
58 + private static $deleted_posts_in_request = array();
59 +
60 + /**
47 61 * Import end.
48 62 *
49 63 * @access private
50 64 *
@@ -64,28 +78,8 @@
64 78 */
65 79 const MAX_POST_CONTENT_LENGTH = 5000000;
66 80
67 81 /**
68 - * Max bytes allowed for post meta_value => length.
69 - * Current Setting : 2MB.
70 - *
71 - * @access public
72 - *
73 - * @var int
74 - */
75 - const MAX_POST_META_LENGTH = 2000000;
76 -
77 - /**
78 - * Max bytes allowed for full sync upload.
79 - * Current Setting : 7MB.
80 - *
81 - * @access public
82 - *
83 - * @var int
84 - */
85 - const MAX_SIZE_FULL_SYNC = 7000000;
86 -
87 - /**
88 82 * Default previous post state.
89 83 * Used for default previous post status.
90 84 *
91 85 * @access public
@@ -105,19 +99,33 @@
105 99 return 'posts';
106 100 }
107 101
108 102 /**
109 - * The table in the database.
103 + * The table name.
110 104 *
111 105 * @access public
112 106 *
113 107 * @return string
108 + * @deprecated since 3.11.0 Use table() instead.
114 109 */
115 110 public function table_name() {
111 + _deprecated_function( __METHOD__, '3.11.0', 'Automattic\\Jetpack\\Sync\\Posts->table' );
116 112 return 'posts';
117 113 }
118 114
119 115 /**
116 + * The table in the database with the prefix.
117 + *
118 + * @access public
119 + *
120 + * @return string|bool
121 + */
122 + public function table() {
123 + global $wpdb;
124 + return $wpdb->posts;
125 + }
126 +
127 + /**
120 128 * Retrieve a post by its ID.
121 129 *
122 130 * @access public
123 131 *
@@ -145,8 +153,9 @@
145 153 */
146 154 public function init_listeners( $callable ) {
147 155 $this->action_handler = $callable;
148 156
157 + add_action( 'before_delete_post', array( $this, 'mark_post_is_being_deleted' ), 0, 1 );
149 158 add_action( 'wp_insert_post', array( $this, 'wp_insert_post' ), 11, 3 );
150 159 add_action( 'wp_after_insert_post', array( $this, 'wp_after_insert_post' ), 11, 2 );
151 160 add_action( 'jetpack_sync_save_post', $callable, 10, 4 );
152 161
@@ -157,9 +166,11 @@
157 166 add_action( 'transition_post_status', array( $this, 'save_published' ), 10, 3 );
158 167
159 168 // Listen for meta changes.
160 169 $this->init_listeners_for_meta_type( 'post', $callable );
161 - $this->init_meta_whitelist_handler( 'post', array( $this, 'filter_meta' ) );
170 + add_filter( 'jetpack_sync_before_enqueue_added_post_meta', array( $this, 'filter_meta' ) );
171 + add_filter( 'jetpack_sync_before_enqueue_updated_post_meta', array( $this, 'filter_updated_post_meta' ) );
172 + add_filter( 'jetpack_sync_before_enqueue_deleted_post_meta', array( $this, 'filter_deleted_post_meta' ) );
162 173
163 174 add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_post', array( $this, 'filter_jetpack_sync_before_enqueue_jetpack_sync_save_post' ) );
164 175 add_filter( 'jetpack_sync_before_enqueue_jetpack_published_post', array( $this, 'filter_jetpack_sync_before_enqueue_jetpack_published_post' ) );
165 176
@@ -165,8 +176,10 @@
165 176
166 177 add_action( 'jetpack_daily_akismet_meta_cleanup_before', array( $this, 'daily_akismet_meta_cleanup_before' ) );
167 178 add_action( 'jetpack_daily_akismet_meta_cleanup_after', array( $this, 'daily_akismet_meta_cleanup_after' ) );
168 179 add_action( 'jetpack_post_meta_batch_delete', $callable, 10, 2 );
180 +
181 + add_action( 'deleted_post', array( $this, 'unmark_post_being_deleted' ), 11, 1 );
169 182 }
170 183
171 184 /**
172 185 * Before Akismet's daily cleanup of spam detection metadata.
@@ -227,15 +240,15 @@
227 240 * @access public
228 241 */
229 242 public function init_before_send() {
230 243 // meta.
231 - add_filter( 'jetpack_sync_before_send_added_post_meta', array( $this, 'trim_post_meta' ) );
232 - add_filter( 'jetpack_sync_before_send_updated_post_meta', array( $this, 'trim_post_meta' ) );
244 + add_filter( 'jetpack_sync_before_send_added_post_meta', array( $this, 'filter_added_post_meta_before_send' ), 5 ); // Incase this filter is used elsewhere, we run early.
245 + add_filter( 'jetpack_sync_before_send_updated_post_meta', array( $this, 'filter_updated_post_meta_before_send' ), 5 ); // Incase this filter is used elsewhere, we run early.
233 246 add_filter( 'jetpack_sync_before_send_deleted_post_meta', array( $this, 'trim_post_meta' ) );
234 247 // Full sync.
235 248 $sync_module = Modules::get_module( 'full-sync' );
236 249 if ( $sync_module instanceof Full_Sync_Immediately ) {
237 - add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'add_term_relationships' ) );
250 + add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'build_full_sync_action_array' ) );
238 251 } else {
239 252 add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_posts_with_metadata_and_terms' ) );
240 253 }
241 254 }
@@ -263,9 +276,9 @@
263 276 *
264 277 * @todo Use $wpdb->prepare for the SQL query.
265 278 *
266 279 * @param array $config Full sync configuration for this sync module.
267 - * @return array Number of items yet to be enqueued.
280 + * @return int Number of items yet to be enqueued.
268 281 */
269 282 public function estimate_full_sync_actions( $config ) {
270 283 global $wpdb;
271 284
@@ -287,9 +300,9 @@
287 300 public function get_where_sql( $config ) {
288 301 $where_sql = Settings::get_blacklisted_post_types_sql();
289 302
290 303 // Config is a list of post IDs to sync.
291 - if ( is_array( $config ) ) {
304 + if ( is_array( $config ) && ! empty( $config ) ) {
292 305 $where_sql .= ' AND ID IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
293 306 }
294 307
295 308 return $where_sql;
@@ -306,9 +319,9 @@
306 319 return array( 'jetpack_full_sync_posts' );
307 320 }
308 321
309 322 /**
310 - * Filter meta arguments so that we don't sync meta_values over MAX_POST_META_LENGTH.
323 + * Filter meta arguments so that we don't sync meta_values over MAX_META_LENGTH.
311 324 *
312 325 * @param array $args action arguments.
313 326 *
314 327 * @return array filtered action arguments.
@@ -317,9 +330,9 @@
317 330 list( $meta_id, $object_id, $meta_key, $meta_value ) = $args;
318 331 // Explicitly truncate meta_value when it exceeds limit.
319 332 // Large content will cause OOM issues and break Sync.
320 333 $serialized_value = maybe_serialize( $meta_value );
321 - if ( strlen( $serialized_value ) >= self::MAX_POST_META_LENGTH ) {
334 + if ( $serialized_value === null || strlen( $serialized_value ) >= self::MAX_META_LENGTH ) {
322 335 $meta_value = '';
323 336 }
324 337 return array( $meta_id, $object_id, $meta_key, $meta_value );
325 338 }
@@ -324,8 +337,112 @@
324 337 return array( $meta_id, $object_id, $meta_key, $meta_value );
325 338 }
326 339
327 340 /**
341 + * Updated post meta send-time filter: refreshes _wp_attachment_metadata to the latest DB value, then trims.
342 + *
343 + * @param array $args [ $meta_id, $object_id, $meta_key, $meta_value ].
344 + * @return array Filtered args.
345 + */
346 + public function filter_updated_post_meta_before_send( $args ) {
347 + if ( ! is_array( $args ) || count( $args ) < 4 ) {
348 + return $args;
349 + }
350 + list( $meta_id, $object_id, $meta_key, $meta_value ) = $args;
351 + if ( '_wp_attachment_metadata' !== $meta_key || 'attachment' !== get_post_type( (int) $object_id ) ) {
352 + return $this->trim_post_meta( $args );
353 + }
354 + $current_value = wp_get_attachment_metadata( (int) $object_id );
355 + if ( is_array( $current_value ) && ! empty( $current_value ) ) {
356 + $meta_value = $current_value;
357 + }
358 + return $this->trim_post_meta( array( $meta_id, $object_id, $meta_key, $meta_value ) );
359 + }
360 +
361 + /**
362 + * Added post meta send-time filter: refreshes _wp_attachment_metadata to the latest DB value, then trims.
363 + *
364 + * @param array $args [ $meta_id, $object_id, $meta_key, $meta_value ].
365 + * @return array|false Filtered args, or false to skip sending when the snapshot is clearly incomplete.
366 + */
367 + public function filter_added_post_meta_before_send( $args ) {
368 + if ( ! is_array( $args ) || count( $args ) < 4 ) {
369 + return $args;
370 + }
371 + list( $meta_id, $object_id, $meta_key, $meta_value ) = $args;
372 + if ( '_wp_attachment_metadata' !== $meta_key || 'attachment' !== get_post_type( (int) $object_id ) ) {
373 + return $this->trim_post_meta( $args );
374 + }
375 + $current_value = wp_get_attachment_metadata( (int) $object_id );
376 + // For added_post_meta, skip clearly incomplete snapshots (e.g., missing or empty sizes).
377 + if ( ! is_array( $current_value ) || empty( $current_value ) ) {
378 + return false;
379 + }
380 + if ( isset( $current_value['sizes'] ) && is_array( $current_value['sizes'] ) && count( $current_value['sizes'] ) === 0 ) {
381 + return false;
382 + }
383 + $meta_value = $current_value;
384 + return $this->trim_post_meta( array( $meta_id, $object_id, $meta_key, $meta_value ) );
385 + }
386 +
387 + /**
388 + * Mark a post as being deleted in the current request.
389 + *
390 + * @param int $post_id ID of the post being deleted.
391 + */
392 + public function mark_post_is_being_deleted( $post_id ) {
393 + self::$deleted_posts_in_request[ (int) $post_id ] = true;
394 + }
395 +
396 + /**
397 + * Enqueue-time per-request dedupe for deleted post metadata, if the post itself is being deleted.
398 + *
399 + * @param array $args [ $meta_id, $post_id, $meta_key, $meta_value ].
400 + * @return array|false
401 + */
402 + public function maybe_skip_deleted_post_meta( $args ) {
403 + if ( is_array( $args ) && isset( $args[1] ) && is_numeric( $args[1] ) ) {
404 + $post_id = (int) $args[1];
405 + if ( isset( self::$deleted_posts_in_request[ $post_id ] ) ) {
406 + return false;
407 + }
408 + }
409 + return $args;
410 + }
411 +
412 + /**
413 + * Unmark a post as being deleted in the current request, to clean up.
414 + *
415 + * @param int $post_id ID of the post.
416 + */
417 + public function unmark_post_being_deleted( $post_id ) {
418 + unset( self::$deleted_posts_in_request[ (int) $post_id ] );
419 + }
420 +
421 + /**
422 + * Enqueue-time per-request dedupe for updated attachment metadata.
423 + *
424 + * @param array $args [ $meta_id, $object_id, $meta_key, $meta_value ].
425 + * @return array|false
426 + */
427 + public function on_before_enqueue_updated_attachment_metadata( $args ) {
428 + if ( ! is_array( $args ) || count( $args ) < 3 ) {
429 + return $args;
430 + }
431 + $post_id = (int) $args[1];
432 + $meta_key = $args[2];
433 + if ( '_wp_attachment_metadata' !== $meta_key || 'attachment' !== get_post_type( $post_id ) ) {
434 + return $args;
435 + }
436 + static $seen_updated_meta_for_post = array();
437 + if ( isset( $seen_updated_meta_for_post[ $post_id ] ) ) {
438 + return false;
439 + }
440 + $seen_updated_meta_for_post[ $post_id ] = true;
441 + return $args;
442 + }
443 +
444 + /**
328 445 * Process content before send.
329 446 *
330 447 * @param array $args Arguments of the `wp_insert_post` hook.
331 448 *
@@ -342,15 +459,33 @@
342 459 * @param array $args Hook arguments.
343 460 * @return array|false Hook arguments, or false if the post type is a blacklisted one.
344 461 */
345 462 public function filter_jetpack_sync_before_enqueue_jetpack_sync_save_post( $args ) {
346 - list( $post_id, $post, $update, $previous_state ) = $args;
463 + if (
464 + ! is_array( $args )
465 + || ! array_key_exists( 0, $args ) || ! is_numeric( $args[0] )
466 + || ! array_key_exists( 1, $args ) || ! ( $args[1] instanceof \WP_Post )
467 + ) {
468 + return false;
469 + }
347 470
471 + list( $post_id, $post, $update, $previous_state ) = array_pad( $args, 4, null );
472 +
348 473 if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
349 474 return false;
350 475 }
351 476
352 - return array( $post_id, $this->filter_post_content_and_add_links( $post ), $update, $previous_state );
477 + // During incremental sync, skip posts whose type is not registered (e.g. CPT unregistered before sync).
478 + // Full sync may have already sent them; we simply don't enqueue incremental updates for them.
479 + if ( ! get_post_type_object( $post->post_type ) ) {
480 + return false;
481 + }
482 +
483 + if ( Activity_Log_Event::POST_TYPE === $post->post_type && ! Activity_Log_Event::is_valid_post( $post ) ) {
484 + return false;
485 + }
486 +
487 + return array( (int) $post_id, $this->filter_post_content_and_add_links( $post ), $update, $previous_state );
353 488 }
354 489
355 490 /**
356 491 * Add filtered post content.
@@ -355,14 +490,27 @@
355 490 /**
356 491 * Add filtered post content.
357 492 *
358 493 * @param array $args Hook arguments.
359 - * @return array Hook arguments.
494 + * @return array|false Hook arguments, or false if the arguments are invalid.
360 495 */
361 496 public function filter_jetpack_sync_before_enqueue_jetpack_published_post( $args ) {
497 + if (
498 + ! is_array( $args )
499 + || ! array_key_exists( 0, $args ) || ! is_numeric( $args[0] )
500 + || ! array_key_exists( 1, $args ) || ! is_array( $args[1] )
501 + || ! array_key_exists( 2, $args ) || ! ( $args[2] instanceof \WP_Post )
502 + ) {
503 + return false;
504 + }
505 +
362 506 list( $post_id, $flags, $post ) = $args;
363 507
364 - return array( $post_id, $flags, $this->filter_post_content_and_add_links( $post ) );
508 + if ( Activity_Log_Event::POST_TYPE === $post->post_type && ! Activity_Log_Event::is_valid_post( $post ) ) {
509 + return false;
510 + }
511 +
512 + return array( (int) $post_id, $flags, $this->filter_post_content_and_add_links( $post ) );
365 513 }
366 514
367 515 /**
368 516 * Filter all blacklisted post types.
@@ -370,9 +518,11 @@
370 518 * @param array $args Hook arguments.
371 519 * @return array|false Hook arguments, or false if the post type is a blacklisted one.
372 520 */
373 521 public function filter_blacklisted_post_types_deleted( $args ) {
374 -
522 + if ( ! is_array( $args ) || ! array_key_exists( 0, $args ) || ! is_numeric( $args[0] ) ) {
523 + return false;
524 + }
375 525 // deleted_post is called after the SQL delete but before cache cleanup.
376 526 // There is the potential we can't detect post_type at this point.
377 527 if ( ! $this->is_post_type_allowed( $args[0] ) ) {
378 528 return false;
@@ -383,20 +533,84 @@
383 533
384 534 /**
385 535 * Filter all meta that is not blacklisted, or is stored for a disallowed post type.
386 536 *
387 - * @param array $args Hook arguments.
537 + * @param array|false $args Hook arguments.
388 538 * @return array|false Hook arguments, or false if meta was filtered.
389 539 */
390 540 public function filter_meta( $args ) {
391 - if ( $this->is_post_type_allowed( $args[1] ) && $this->is_whitelisted_post_meta( $args[2] ) ) {
392 - return $args;
541 + if ( ! $this->has_valid_meta_args( $args ) || ! is_numeric( $args[1] ) ) {
542 + return false;
393 543 }
394 544
395 - return false;
545 + return $this->is_allowed_post_meta( $args[1], $args[2] ) ? $args : false;
396 546 }
397 547
398 548 /**
549 + * Filter updated post meta that is not whitelisted, is stored for a disallowed post type,
550 + * or is duplicate attachment metadata.
551 + *
552 + * @param array|false $args Hook arguments.
553 + * @return array|false Hook arguments, or false if meta was filtered.
554 + */
555 + public function filter_updated_post_meta( $args ) {
556 + $args = $this->on_before_enqueue_updated_attachment_metadata( $args );
557 + if ( false === $args ) {
558 + return false;
559 + }
560 +
561 + return $this->filter_meta( $args );
562 + }
563 +
564 + /**
565 + * Filter deleted post meta that is not whitelisted, or is stored for a disallowed post type.
566 + *
567 + * @param array|false $args Hook arguments.
568 + * @return array|false Hook arguments, or false if meta was filtered.
569 + */
570 + public function filter_deleted_post_meta( $args ) {
571 + if ( ! $this->has_valid_meta_args( $args ) ) {
572 + return false;
573 + }
574 + // Core uses post ID 0 on this hook for delete-all metadata operations. Only mirror value-constrained deletes.
575 + if ( 0 === $args[1] ) {
576 + if ( ! array_key_exists( 3, $args ) || '' === $args[3] || null === $args[3] || false === $args[3] ) {
577 + return false;
578 + }
579 +
580 + return $this->is_whitelisted_post_meta( $args[2] ) ? $args : false;
581 + }
582 +
583 + $args = $this->filter_meta( $args );
584 + if ( false === $args ) {
585 + return false;
586 + }
587 +
588 + return $this->maybe_skip_deleted_post_meta( $args );
589 + }
590 +
591 + /**
592 + * Whether metadata hook arguments include a meta key.
593 + *
594 + * @param array|false $args Hook arguments.
595 + * @return bool Whether metadata hook arguments include a meta key.
596 + */
597 + private function has_valid_meta_args( $args ) {
598 + return is_array( $args ) && array_key_exists( 1, $args ) && array_key_exists( 2, $args ) && is_string( $args[2] );
599 + }
600 +
601 + /**
602 + * Whether post metadata is allowed to sync.
603 + *
604 + * @param int|string $object_id Post ID.
605 + * @param string $meta_key Meta key.
606 + * @return bool Whether post metadata is allowed to sync.
607 + */
608 + private function is_allowed_post_meta( $object_id, $meta_key ) {
609 + return $this->is_post_type_allowed( $object_id ) && $this->is_whitelisted_post_meta( $meta_key );
610 + }
611 +
612 + /**
399 613 * Whether a post meta key is whitelisted.
400 614 *
401 615 * @param string $meta_key Meta key.
402 616 * @return boolean Whether the post meta key is whitelisted.
@@ -401,10 +615,13 @@
401 615 * @param string $meta_key Meta key.
402 616 * @return boolean Whether the post meta key is whitelisted.
403 617 */
404 618 public function is_whitelisted_post_meta( $meta_key ) {
405 - // The _wpas_skip_ meta key is used by Publicize.
406 - return in_array( $meta_key, Settings::get_setting( 'post_meta_whitelist' ), true ) || str_starts_with( $meta_key, '_wpas_skip_' );
619 + if ( ! is_string( $meta_key ) ) {
620 + return false;
621 + }
622 + // The '_wpas_skip_' meta key prefix is used by Publicize to mark posts that should be skipped.
623 + return str_starts_with( $meta_key, '_wpas_skip_' ) || in_array( $meta_key, Settings::get_setting( 'post_meta_whitelist' ), true );
407 624 }
408 625
409 626 /**
410 627 * Whether a post type is allowed.
@@ -602,8 +819,11 @@
602 819 * @param string $old_status Old post status.
603 820 * @param \WP_Post $post Post object.
604 821 */
605 822 public function save_published( $new_status, $old_status, $post ) {
823 + if ( ! $post instanceof \WP_Post ) {
824 + return;
825 + }
606 826 if ( 'publish' === $new_status && 'publish' !== $old_status ) {
607 827 $this->just_published[ $post->ID ] = true;
608 828 }
609 829
@@ -639,20 +859,20 @@
639 859 * @param \WP_Post $post Post object.
640 860 * @param boolean $update Whether this is an existing post being updated or not.
641 861 */
642 862 public function wp_insert_post( $post_ID, $post = null, $update = null ) {
643 - if ( ! is_numeric( $post_ID ) || $post === null ) {
863 + if ( ! is_numeric( $post_ID ) || ! $post instanceof \WP_Post ) {
644 864 return;
645 865 }
646 866
647 867 // Workaround for https://github.com/woocommerce/woocommerce/issues/18007.
648 - if ( $post && 'shop_order' === $post->post_type ) {
868 + if ( 'shop_order' === $post->post_type ) {
649 869 $post = get_post( $post_ID );
650 870 }
651 871
652 - $previous_status = isset( $this->previous_status[ $post_ID ] ) ? $this->previous_status[ $post_ID ] : self::DEFAULT_PREVIOUS_STATE;
872 + $previous_status = $this->previous_status[ $post_ID ] ?? self::DEFAULT_PREVIOUS_STATE;
653 873
654 - $just_published = isset( $this->just_published[ $post_ID ] ) ? $this->just_published[ $post_ID ] : false;
874 + $just_published = $this->just_published[ $post_ID ] ?? false;
655 875
656 876 $state = array(
657 877 'is_auto_save' => (bool) Jetpack_Constants::get_constant( 'DOING_AUTOSAVE' ),
658 878 'previous_status' => $previous_status,
@@ -683,14 +903,14 @@
683 903 * @param int $post_ID Post ID.
684 904 * @param \WP_Post $post Post object.
685 905 **/
686 906 public function wp_after_insert_post( $post_ID, $post ) {
687 - if ( ! is_numeric( $post_ID ) || $post === null ) {
907 + if ( ! is_numeric( $post_ID ) || ! $post instanceof \WP_Post ) {
688 908 return;
689 909 }
690 910
691 911 // Workaround for https://github.com/woocommerce/woocommerce/issues/18007.
692 - if ( $post && 'shop_order' === $post->post_type ) {
912 + if ( 'shop_order' === $post->post_type ) {
693 913 $post = get_post( $post_ID );
694 914 }
695 915
696 916 $this->send_published( $post_ID, $post );
@@ -761,8 +981,11 @@
761 981 * Send additional sync action for Activity Log when post is a Customizer publish
762 982 */
763 983 if ( 'customize_changeset' === $post->post_type ) {
764 984 $post_content = json_decode( $post->post_content, true );
985 + if ( ! is_iterable( $post_content ) ) {
986 + return;
987 + }
765 988 foreach ( $post_content as $key => $value ) {
766 989 // Skip if it isn't a widget.
767 990 if ( 'widget_' !== substr( $key, 0, strlen( 'widget_' ) ) ) {
768 991 continue;
@@ -776,9 +999,9 @@
776 999 if ( isset( $wp_registered_widgets[ $key ] ) ) {
777 1000 $widget_data = array(
778 1001 'name' => $wp_registered_widgets[ $key ]['name'],
779 1002 'id' => $key,
780 - 'title' => $value['value']['title'],
1003 + 'title' => $value['value']['title'] ?? '',
781 1004 );
782 1005 do_action( 'jetpack_widget_edited', $widget_data );
783 1006 }
784 1007 }
@@ -785,8 +1008,27 @@
785 1008 }
786 1009 }
787 1010
788 1011 /**
1012 + * Build the full sync action object for Posts.
1013 + *
1014 + * @access public
1015 + *
1016 + * @param array $args An array with the posts and the previous end.
1017 + *
1018 + * @return array An array with the posts, postmeta and the previous end.
1019 + */
1020 + public function build_full_sync_action_array( $args ) {
1021 + list( $filtered_posts, $previous_end ) = $args;
1022 + return array(
1023 + $filtered_posts['objects'],
1024 + $filtered_posts['meta'],
1025 + array(), // WPCOM does not process term relationships in full sync posts actions for a while now, let's skip them.
1026 + $previous_end,
1027 + );
1028 + }
1029 +
1030 + /**
789 1031 * Add term relationships to post objects within a hook before they are serialized and sent to the server.
790 1032 * This is used in Full Sync Immediately
791 1033 *
792 1034 * @access public
@@ -792,17 +1034,18 @@
792 1034 * @access public
793 1035 *
794 1036 * @param array $args The hook parameters.
795 1037 * @return array $args The expanded hook parameters.
1038 + * @deprecated since 4.7.0
796 1039 */
797 1040 public function add_term_relationships( $args ) {
798 - list( $filtered_posts, $previous_interval_end ) = $args;
799 - list( $filtered_post_ids, $filtered_posts, $filtered_posts_metadata ) = $filtered_posts;
1041 + _deprecated_function( __METHOD__, '4.7.0' );
1042 + list( $filtered_posts, $previous_interval_end ) = $args;
800 1043
801 1044 return array(
802 - $filtered_posts,
803 - $filtered_posts_metadata,
804 - $this->get_term_relationships( $filtered_post_ids ),
1045 + $filtered_posts['objects'],
1046 + $filtered_posts['meta'],
1047 + $this->get_term_relationships( $filtered_posts['object_ids'] ),
805 1048 $previous_interval_end,
806 1049 );
807 1050 }
808 1051
@@ -861,17 +1104,35 @@
861 1104 if ( empty( $post_ids ) ) {
862 1105 return array();
863 1106 }
864 1107
865 - $posts = $this->expand_posts( $post_ids );
866 - $posts_metadata = $this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) );
1108 + $posts = $this->expand_posts( $post_ids );
867 1109
868 - // Filter posts and metadata based on maximum size constraints.
869 - list( $filtered_post_ids, $filtered_posts, $filtered_posts_metadata ) = $this->filter_posts_and_metadata_max_size( $posts, $posts_metadata );
1110 + // If no posts were fetched, make sure to return the expected structure so that status is updated correctly.
1111 + if ( empty( $posts ) ) {
1112 + return array(
1113 + 'object_ids' => $post_ids,
1114 + 'objects' => array(),
1115 + 'meta' => array(),
1116 + );
1117 + }
1118 + // Get the post IDs from the posts that were fetched.
1119 + $fetched_post_ids = wp_list_pluck( $posts, 'ID' );
1120 + $metadata = $this->get_metadata( $fetched_post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) );
1121 +
1122 + // Filter the posts and metadata based on the maximum size constraints.
1123 + list( $filtered_post_ids, $filtered_posts, $filtered_posts_metadata ) = $this->filter_objects_and_metadata_by_size(
1124 + 'post',
1125 + $posts,
1126 + $metadata,
1127 + self::MAX_META_LENGTH,
1128 + self::MAX_SIZE_FULL_SYNC
1129 + );
1130 +
870 1131 return array(
871 - $filtered_post_ids,
872 - $filtered_posts,
873 - $filtered_posts_metadata,
1132 + 'object_ids' => $filtered_post_ids,
1133 + 'objects' => $filtered_posts,
1134 + 'meta' => $filtered_posts_metadata,
874 1135 );
875 1136 }
876 1137
877 1138 /**
@@ -885,73 +1146,6 @@
885 1146 $posts = array_filter( array_map( array( 'WP_Post', 'get_instance' ), $post_ids ) );
886 1147 $posts = array_map( array( $this, 'filter_post_content_and_add_links' ), $posts );
887 1148 $posts = array_values( $posts ); // Reindex in case posts were deleted.
888 1149 return $posts;
889 - }
890 -
891 - /**
892 - * Filters posts and metadata based on maximum size constraints.
893 - * It always allows the first post with its metadata even if they exceed the limit, otherwise they will never be synced.
894 - *
895 - * @access public
896 - *
897 - * @param array $posts The array of posts to filter.
898 - * @param array $metadata The array of metadata to filter.
899 - * @return array An array containing the filtered post IDs, filtered posts, and filtered metadata.
900 - */
901 - public function filter_posts_and_metadata_max_size( $posts, $metadata ) {
902 - $filtered_posts = array();
903 - $filtered_metadata = array();
904 - $filtered_post_ids = array();
905 - $current_size = 0;
906 - foreach ( $posts as $post ) {
907 - $post_content_size = isset( $post->post_content ) ? strlen( $post->post_content ) : 0;
908 - $current_metadata = array();
909 - $metadata_size = 0;
910 - foreach ( $metadata as $key => $metadata_item ) {
911 - if ( (int) $metadata_item->post_id === $post->ID ) {
912 - // Trimming metadata if it exceeds limit. Similar to trim_post_meta.
913 - $metadata_item_size = strlen( maybe_serialize( $metadata_item->meta_value ) );
914 - if ( $metadata_item_size >= self::MAX_POST_META_LENGTH ) {
915 - $metadata_item->meta_value = '';
916 - }
917 - $current_metadata[] = $metadata_item;
918 - $metadata_size += $metadata_item_size >= self::MAX_POST_META_LENGTH ? 0 : $metadata_item_size;
919 - if ( ! empty( $filtered_post_ids ) && ( $current_size + $post_content_size + $metadata_size ) > ( self::MAX_SIZE_FULL_SYNC ) ) {
920 - break 2; // Break both foreach loops.
921 - }
922 - unset( $metadata[ $key ] );
923 - }
924 - }
925 - // Always allow the first post with its metadata.
926 - if ( empty( $filtered_post_ids ) || ( $current_size + $post_content_size + $metadata_size ) <= ( self::MAX_SIZE_FULL_SYNC ) ) {
927 - $filtered_post_ids[] = strval( $post->ID );
928 - $filtered_posts[] = $post;
929 - $filtered_metadata = array_merge( $filtered_metadata, $current_metadata );
930 - $current_size += $post_content_size + $metadata_size;
931 - } else {
932 - break;
933 - }
934 - }
935 - return array(
936 - $filtered_post_ids,
937 - $filtered_posts,
938 - $filtered_metadata,
939 - );
940 - }
941 -
942 - /**
943 - * Set the status of the full sync action based on the objects that were sent.
944 - *
945 - * @access public
946 - *
947 - * @param array $status This module Full Sync status.
948 - * @param array $objects This module Full Sync objects.
949 - *
950 - * @return array The updated status.
951 - */
952 - public function set_send_full_sync_actions_status( $status, $objects ) {
953 - $status['last_sent'] = end( $objects[0] );
954 - $status['sent'] += count( $objects[0] );
955 - return $status;
956 1150 }
957 1151 }