| @@ -10,9 +10,8 @@ | ||
| 10 | 10 | use Activitypub\Collection\Actors; |
| 11 | 11 | use Activitypub\Collection\Extra_Fields; |
| 12 | 12 | use Activitypub\Collection\Followers; |
| 13 | 13 | use Activitypub\Collection\Following; |
| 14 | -use Activitypub\Collection\Inbox; | |
| 15 | 14 | use Activitypub\Collection\Outbox; |
| 16 | 15 | use Activitypub\Collection\Remote_Actors; |
| 17 | 16 | use Activitypub\Transformer\Factory; |
| 18 | 17 | |
| @@ -32,11 +31,8 @@ | ||
| 32 | 31 | Scheduler::register_async_batch_callback( 'activitypub_update_comment_counts', array( self::class, 'update_comment_counts' ) ); |
| 33 | 32 | Scheduler::register_async_batch_callback( 'activitypub_create_post_outbox_items', array( self::class, 'create_post_outbox_items' ) ); |
| 34 | 33 | Scheduler::register_async_batch_callback( 'activitypub_create_comment_outbox_items', array( self::class, 'create_comment_outbox_items' ) ); |
| 35 | 34 | Scheduler::register_async_batch_callback( 'activitypub_migrate_avatar_to_remote_actors', array( self::class, 'migrate_avatar_to_remote_actors' ) ); |
| 36 | - Scheduler::register_async_batch_callback( 'activitypub_migrate_actor_emoji', array( self::class, 'migrate_actor_emoji' ) ); | |
| 37 | - Scheduler::register_async_batch_callback( 'activitypub_backfill_statistics', array( Statistics::class, 'backfill_historical_stats' ) ); | |
| 38 | - Scheduler::register_async_batch_callback( 'activitypub_tombstone_migrate', array( self::class, 'migrate_tombstones_to_cpt' ) ); | |
| 39 | 35 | } |
| 40 | 36 | |
| 41 | 37 | /** |
| 42 | 38 | * The current version of the database structure. |
| @@ -43,9 +39,9 @@ | ||
| 43 | 39 | * |
| 44 | 40 | * @return string The current version. |
| 45 | 41 | */ |
| 46 | 42 | public static function get_version() { |
| 47 | - return \get_option( 'activitypub_db_version', 0 ); | |
| 43 | + return get_option( 'activitypub_db_version', 0 ); | |
| 48 | 44 | } |
| 49 | 45 | |
| 50 | 46 | /** |
| 51 | 47 | * Locks the database migration process to prevent simultaneous migrations. |
| @@ -161,16 +157,21 @@ | ||
| 161 | 157 | } |
| 162 | 158 | if ( \version_compare( $version_from_db, '4.7.2', '<' ) ) { |
| 163 | 159 | self::migrate_to_4_7_2(); |
| 164 | 160 | } |
| 161 | + if ( \version_compare( $version_from_db, '4.7.3', '<' ) ) { | |
| 162 | + add_action( 'init', 'flush_rewrite_rules', 20 ); | |
| 163 | + } | |
| 165 | 164 | if ( \version_compare( $version_from_db, '5.0.0', '<' ) ) { |
| 166 | 165 | Scheduler::register_schedules(); |
| 167 | 166 | \wp_schedule_single_event( \time(), 'activitypub_create_post_outbox_items' ); |
| 168 | 167 | \wp_schedule_single_event( \time() + 15, 'activitypub_create_comment_outbox_items' ); |
| 168 | + add_action( 'init', 'flush_rewrite_rules', 20 ); | |
| 169 | 169 | } |
| 170 | 170 | if ( \version_compare( $version_from_db, '5.4.0', '<' ) ) { |
| 171 | 171 | \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_actor_json_slashing' ) ); |
| 172 | 172 | \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_comment_author_emails' ) ); |
| 173 | + \add_action( 'init', 'flush_rewrite_rules', 20 ); | |
| 173 | 174 | } |
| 174 | 175 | if ( \version_compare( $version_from_db, '5.7.0', '<' ) ) { |
| 175 | 176 | self::delete_mastodon_api_orphaned_extra_fields(); |
| 176 | 177 | } |
| @@ -176,64 +177,45 @@ | ||
| 176 | 177 | } |
| 177 | 178 | if ( \version_compare( $version_from_db, '5.8.0', '<' ) ) { |
| 178 | 179 | self::update_notification_options(); |
| 179 | 180 | } |
| 181 | + | |
| 180 | 182 | if ( \version_compare( $version_from_db, '6.0.0', '<' ) ) { |
| 181 | 183 | self::migrate_followers_to_ap_actor_cpt(); |
| 182 | 184 | \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_actor_json_storage' ) ); |
| 183 | 185 | } |
| 186 | + | |
| 184 | 187 | if ( \version_compare( $version_from_db, '6.0.1', '<' ) ) { |
| 185 | 188 | self::migrate_followers_to_ap_actor_cpt(); |
| 186 | 189 | \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_actor_json_storage' ) ); |
| 187 | 190 | } |
| 191 | + | |
| 188 | 192 | if ( \version_compare( $version_from_db, '7.0.0', '<' ) ) { |
| 189 | - \wp_unschedule_hook( 'activitypub_update_followers' ); | |
| 190 | - \wp_unschedule_hook( 'activitypub_cleanup_followers' ); | |
| 193 | + wp_unschedule_hook( 'activitypub_update_followers' ); | |
| 194 | + wp_unschedule_hook( 'activitypub_cleanup_followers' ); | |
| 191 | 195 | |
| 192 | 196 | if ( ! \wp_next_scheduled( 'activitypub_update_remote_actors' ) ) { |
| 193 | - \wp_schedule_event( \time(), 'hourly', 'activitypub_update_remote_actors' ); | |
| 197 | + \wp_schedule_event( time(), 'hourly', 'activitypub_update_remote_actors' ); | |
| 194 | 198 | } |
| 195 | 199 | |
| 196 | 200 | if ( ! \wp_next_scheduled( 'activitypub_cleanup_remote_actors' ) ) { |
| 197 | - \wp_schedule_event( \time(), 'daily', 'activitypub_cleanup_remote_actors' ); | |
| 201 | + \wp_schedule_event( time(), 'daily', 'activitypub_cleanup_remote_actors' ); | |
| 198 | 202 | } |
| 199 | 203 | } |
| 204 | + | |
| 200 | 205 | if ( \version_compare( $version_from_db, '7.3.0', '<' ) ) { |
| 201 | 206 | self::remove_pending_application_user_follow_requests(); |
| 202 | 207 | } |
| 208 | + | |
| 203 | 209 | if ( \version_compare( $version_from_db, '7.5.0', '<' ) ) { |
| 204 | 210 | self::sync_jetpack_following_meta(); |
| 205 | 211 | } |
| 212 | + | |
| 206 | 213 | if ( \version_compare( $version_from_db, '7.6.0', '<' ) ) { |
| 207 | 214 | self::clean_up_inbox(); |
| 208 | 215 | \wp_schedule_single_event( \time(), 'activitypub_migrate_avatar_to_remote_actors' ); |
| 209 | 216 | } |
| 210 | - if ( \version_compare( $version_from_db, '7.9.0', '<' ) ) { | |
| 211 | - \wp_schedule_single_event( \time(), 'activitypub_migrate_actor_emoji' ); | |
| 212 | - } | |
| 213 | - if ( \version_compare( $version_from_db, '8.1.0', '<' ) && ! \wp_next_scheduled( 'activitypub_backfill_statistics' ) ) { | |
| 214 | - // Backfill historical statistics data (delay + jitter to avoid load spikes on hosts running many sites). | |
| 215 | - \wp_schedule_single_event( \time() + HOUR_IN_SECONDS + \wp_rand( 0, 6 * HOUR_IN_SECONDS ), 'activitypub_backfill_statistics' ); | |
| 216 | - } | |
| 217 | 217 | |
| 218 | - if ( \version_compare( $version_from_db, '8.3.0', '<' ) ) { | |
| 219 | - if ( ! \wp_next_scheduled( 'activitypub_tombstone_migrate' ) ) { | |
| 220 | - \wp_schedule_single_event( \time() + MINUTE_IN_SECONDS, 'activitypub_tombstone_migrate' ); | |
| 221 | - } | |
| 222 | - } | |
| 223 | - if ( \version_compare( $version_from_db, '9.1.0', '<' ) ) { | |
| 224 | - self::migrate_application_keypair_option(); | |
| 225 | - self::delete_application_outbox_items(); | |
| 226 | - } | |
| 227 | - | |
| 228 | - /* | |
| 229 | - * Defer the flush to late in the `init` cycle (priority 20). Migration::init | |
| 230 | - * runs at priority 1, which is earlier than most plugins register their | |
| 231 | - * rewrite rules. Flushing synchronously here would persist a truncated | |
| 232 | - * ruleset that omits third-party rules added on `init` at priority 10. | |
| 233 | - */ | |
| 234 | - \add_action( 'init', array( Activitypub::class, 'flush_rewrite_rules' ), 20 ); | |
| 235 | - | |
| 236 | 218 | // Ensure all required cron schedules are registered. |
| 237 | 219 | Scheduler::register_schedules(); |
| 238 | 220 | |
| 239 | 221 | /* |
| @@ -302,10 +284,10 @@ | ||
| 302 | 284 | * Updates the DB-schema of the followers-list. |
| 303 | 285 | */ |
| 304 | 286 | public static function migrate_from_0_17() { |
| 305 | 287 | // Migrate followers. |
| 306 | - foreach ( \get_users( array( 'fields' => 'ID' ) ) as $user_id ) { | |
| 307 | - $followers = \get_user_meta( $user_id, 'activitypub_followers', true ); | |
| 288 | + foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) { | |
| 289 | + $followers = get_user_meta( $user_id, 'activitypub_followers', true ); | |
| 308 | 290 | |
| 309 | 291 | if ( $followers ) { |
| 310 | 292 | foreach ( $followers as $actor ) { |
| 311 | 293 | Followers::add( $user_id, $actor ); |
| @@ -311,8 +293,10 @@ | ||
| 311 | 293 | Followers::add( $user_id, $actor ); |
| 312 | 294 | } |
| 313 | 295 | } |
| 314 | 296 | } |
| 297 | + | |
| 298 | + Activitypub::flush_rewrite_rules(); | |
| 315 | 299 | } |
| 316 | 300 | |
| 317 | 301 | /** |
| 318 | 302 | * Clear the cache after updating to 1.3.0. |
| @@ -325,9 +309,9 @@ | ||
| 325 | 309 | ) |
| 326 | 310 | ); |
| 327 | 311 | |
| 328 | 312 | foreach ( $user_ids as $user_id ) { |
| 329 | - \wp_cache_delete( \sprintf( Followers::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); | |
| 313 | + wp_cache_delete( sprintf( Followers::CACHE_KEY_INBOXES, $user_id ), 'activitypub' ); | |
| 330 | 314 | } |
| 331 | 315 | } |
| 332 | 316 | |
| 333 | 317 | /** |
| @@ -333,15 +317,15 @@ | ||
| 333 | 317 | /** |
| 334 | 318 | * Unschedule Hooks after updating to 2.0.0. |
| 335 | 319 | */ |
| 336 | 320 | private static function migrate_from_2_0_0() { |
| 337 | - \wp_clear_scheduled_hook( 'activitypub_send_post_activity' ); | |
| 338 | - \wp_clear_scheduled_hook( 'activitypub_send_update_activity' ); | |
| 339 | - \wp_clear_scheduled_hook( 'activitypub_send_delete_activity' ); | |
| 321 | + wp_clear_scheduled_hook( 'activitypub_send_post_activity' ); | |
| 322 | + wp_clear_scheduled_hook( 'activitypub_send_update_activity' ); | |
| 323 | + wp_clear_scheduled_hook( 'activitypub_send_delete_activity' ); | |
| 340 | 324 | |
| 341 | - \wp_unschedule_hook( 'activitypub_send_post_activity' ); | |
| 342 | - \wp_unschedule_hook( 'activitypub_send_update_activity' ); | |
| 343 | - \wp_unschedule_hook( 'activitypub_send_delete_activity' ); | |
| 325 | + wp_unschedule_hook( 'activitypub_send_post_activity' ); | |
| 326 | + wp_unschedule_hook( 'activitypub_send_update_activity' ); | |
| 327 | + wp_unschedule_hook( 'activitypub_send_delete_activity' ); | |
| 344 | 328 | |
| 345 | 329 | $object_type = \get_option( 'activitypub_object_type', ACTIVITYPUB_DEFAULT_OBJECT_TYPE ); |
| 346 | 330 | if ( 'article' === $object_type ) { |
| 347 | 331 | \update_option( 'activitypub_object_type', 'wordpress-post-format' ); |
| @@ -360,9 +344,9 @@ | ||
| 360 | 344 | /** |
| 361 | 345 | * Rename DB fields. |
| 362 | 346 | */ |
| 363 | 347 | private static function migrate_from_2_6_0() { |
| 364 | - \wp_cache_flush(); | |
| 348 | + wp_cache_flush(); | |
| 365 | 349 | |
| 366 | 350 | self::update_usermeta_key( 'activitypub_user_description', 'activitypub_description' ); |
| 367 | 351 | |
| 368 | 352 | self::update_options_key( 'activitypub_blog_user_description', 'activitypub_blog_description' ); |
| @@ -376,9 +360,9 @@ | ||
| 376 | 360 | private static function migrate_to_4_0_0() { |
| 377 | 361 | $latest_post_id = 0; |
| 378 | 362 | |
| 379 | 363 | // Get the ID of the latest blog post and save it to the options table. |
| 380 | - $latest_post = \get_posts( | |
| 364 | + $latest_post = get_posts( | |
| 381 | 365 | array( |
| 382 | 366 | 'numberposts' => 1, |
| 383 | 367 | 'orderby' => 'ID', |
| 384 | 368 | 'order' => 'DESC', |
| @@ -489,9 +473,9 @@ | ||
| 489 | 473 | $followers = $wpdb->get_col( |
| 490 | 474 | $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s", Remote_Actors::POST_TYPE ) |
| 491 | 475 | ); |
| 492 | 476 | foreach ( $followers as $id ) { |
| 493 | - \clean_post_cache( $id ); | |
| 477 | + clean_post_cache( $id ); | |
| 494 | 478 | } |
| 495 | 479 | } |
| 496 | 480 | |
| 497 | 481 | /** |
| @@ -507,9 +491,9 @@ | ||
| 507 | 491 | global $wpdb; |
| 508 | 492 | |
| 509 | 493 | Comment::register_comment_types(); |
| 510 | 494 | $comment_types = Comment::get_comment_type_slugs(); |
| 511 | - $type_inclusion = "AND comment_type IN ('" . \implode( "','", $comment_types ) . "')"; | |
| 495 | + $type_inclusion = "AND comment_type IN ('" . implode( "','", $comment_types ) . "')"; | |
| 512 | 496 | |
| 513 | 497 | // Get and process this batch. |
| 514 | 498 | $post_ids = $wpdb->get_col( // phpcs:ignore WordPress.DB |
| 515 | 499 | $wpdb->prepare( |
| @@ -523,9 +507,9 @@ | ||
| 523 | 507 | foreach ( $post_ids as $post_id ) { |
| 524 | 508 | \wp_update_comment_count_now( $post_id ); |
| 525 | 509 | } |
| 526 | 510 | |
| 527 | - if ( \count( $post_ids ) === $batch_size ) { | |
| 511 | + if ( count( $post_ids ) === $batch_size ) { | |
| 528 | 512 | // Schedule next batch. |
| 529 | 513 | return array( $batch_size, $offset + $batch_size ); |
| 530 | 514 | } |
| 531 | 515 | } |
| @@ -547,9 +531,9 @@ | ||
| 547 | 531 | // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query |
| 548 | 532 | 'meta_query' => array( |
| 549 | 533 | array( |
| 550 | 534 | 'key' => 'activitypub_status', |
| 551 | - 'value' => ACTIVITYPUB_OBJECT_STATE_FEDERATED, | |
| 535 | + 'value' => 'federated', | |
| 552 | 536 | ), |
| 553 | 537 | ), |
| 554 | 538 | ) |
| 555 | 539 | ); |
| @@ -567,9 +551,9 @@ | ||
| 567 | 551 | self::add_to_outbox( $post, 'Update', $post->post_author, $visibility ); |
| 568 | 552 | } |
| 569 | 553 | } |
| 570 | 554 | |
| 571 | - if ( \count( $posts ) === $batch_size ) { | |
| 555 | + if ( count( $posts ) === $batch_size ) { | |
| 572 | 556 | return array( |
| 573 | 557 | 'batch_size' => $batch_size, |
| 574 | 558 | 'offset' => $offset + $batch_size, |
| 575 | 559 | ); |
| @@ -594,9 +578,9 @@ | ||
| 594 | 578 | // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query |
| 595 | 579 | 'meta_query' => array( |
| 596 | 580 | array( |
| 597 | 581 | 'key' => 'activitypub_status', |
| 598 | - 'value' => ACTIVITYPUB_OBJECT_STATE_FEDERATED, | |
| 582 | + 'value' => 'federated', | |
| 599 | 583 | ), |
| 600 | 584 | ), |
| 601 | 585 | ) |
| 602 | 586 | ); |
| @@ -604,9 +588,9 @@ | ||
| 604 | 588 | foreach ( $comments as $comment ) { |
| 605 | 589 | self::add_to_outbox( $comment, 'Create', $comment->user_id ); |
| 606 | 590 | } |
| 607 | 591 | |
| 608 | - if ( \count( $comments ) === $batch_size ) { | |
| 592 | + if ( count( $comments ) === $batch_size ) { | |
| 609 | 593 | return array( |
| 610 | 594 | 'batch_size' => $batch_size, |
| 611 | 595 | 'offset' => $offset + $batch_size, |
| 612 | 596 | ); |
| @@ -700,9 +684,9 @@ | ||
| 700 | 684 | ) |
| 701 | 685 | ); |
| 702 | 686 | } |
| 703 | 687 | |
| 704 | - if ( \count( $comments ) === $batch_size ) { | |
| 688 | + if ( count( $comments ) === $batch_size ) { | |
| 705 | 689 | return array( |
| 706 | 690 | 'batch_size' => $batch_size, |
| 707 | 691 | 'offset' => $offset + $batch_size, |
| 708 | 692 | ); |
| @@ -1034,9 +1018,9 @@ | ||
| 1034 | 1018 | $wpdb->delete( |
| 1035 | 1019 | $wpdb->postmeta, |
| 1036 | 1020 | array( |
| 1037 | 1021 | 'meta_key' => '_activitypub_following', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key |
| 1038 | - 'meta_value' => -1, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value | |
| 1022 | + 'meta_value' => Actors::APPLICATION_USER_ID, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value | |
| 1039 | 1023 | ) |
| 1040 | 1024 | ); |
| 1041 | 1025 | } |
| 1042 | 1026 | |
| @@ -1088,9 +1072,9 @@ | ||
| 1088 | 1072 | // phpcs:ignore WordPress.DB.DirectDatabaseQuery |
| 1089 | 1073 | $inbox_ids = $wpdb->get_col( |
| 1090 | 1074 | $wpdb->prepare( |
| 1091 | 1075 | "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s", |
| 1092 | - Inbox::POST_TYPE | |
| 1076 | + \Activitypub\Collection\Inbox::POST_TYPE | |
| 1093 | 1077 | ) |
| 1094 | 1078 | ); |
| 1095 | 1079 | |
| 1096 | 1080 | // Delete all inbox items and their metadata. |
| @@ -1099,121 +1083,8 @@ | ||
| 1099 | 1083 | } |
| 1100 | 1084 | } |
| 1101 | 1085 | |
| 1102 | 1086 | /** |
| 1103 | - * Migrate URLs from the legacy `activitypub_tombstone_urls` option into the | |
| 1104 | - * `ap_tombstone` custom post type. | |
| 1105 | - * | |
| 1106 | - * Chunked async migration. Locking and rescheduling is handled by | |
| 1107 | - * Scheduler::async_batch — the callback returns `array( 'batch_size' => N )` | |
| 1108 | - * to request another run, or `null` when the option is fully drained. | |
| 1109 | - * | |
| 1110 | - * Legacy entries are already-normalized strings (no scheme), so we bypass | |
| 1111 | - * URL validation and insert directly via wp_insert_post. | |
| 1112 | - * | |
| 1113 | - * @since 8.3.0 | |
| 1114 | - * | |
| 1115 | - * @param int $batch_size Optional. Number of URLs to process per call. Default 500. | |
| 1116 | - * @return array|null Args for the next run, or null when migration is complete. | |
| 1117 | - */ | |
| 1118 | - public static function migrate_tombstones_to_cpt( $batch_size = 500 ) { | |
| 1119 | - global $wpdb; | |
| 1120 | - | |
| 1121 | - $urls = \get_option( 'activitypub_tombstone_urls', null ); | |
| 1122 | - | |
| 1123 | - if ( null === $urls || ! \is_array( $urls ) || empty( $urls ) ) { | |
| 1124 | - \delete_option( 'activitypub_tombstone_urls' ); | |
| 1125 | - return null; | |
| 1126 | - } | |
| 1127 | - | |
| 1128 | - $chunk = \array_slice( $urls, 0, (int) $batch_size ); | |
| 1129 | - $remaining = \array_slice( $urls, (int) $batch_size ); | |
| 1130 | - $progressed = false; | |
| 1131 | - | |
| 1132 | - foreach ( $chunk as $normalized ) { | |
| 1133 | - if ( ! \is_string( $normalized ) || '' === $normalized ) { | |
| 1134 | - // Drop garbage entries — counts as progress. | |
| 1135 | - $progressed = true; | |
| 1136 | - continue; | |
| 1137 | - } | |
| 1138 | - | |
| 1139 | - $hash = \md5( $normalized ); | |
| 1140 | - | |
| 1141 | - /* | |
| 1142 | - * Light existence check. `get_page_by_path()` would hydrate a | |
| 1143 | - * full `WP_Post` per loop iteration; on a large registry that | |
| 1144 | - * adds up fast. We only need a boolean here. | |
| 1145 | - */ | |
| 1146 | - $exists = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery | |
| 1147 | - $wpdb->prepare( | |
| 1148 | - "SELECT 1 FROM {$wpdb->posts} WHERE post_type = %s AND post_name = %s LIMIT 1", | |
| 1149 | - Tombstone::POST_TYPE, | |
| 1150 | - $hash | |
| 1151 | - ) | |
| 1152 | - ); | |
| 1153 | - if ( $exists ) { | |
| 1154 | - $progressed = true; | |
| 1155 | - continue; | |
| 1156 | - } | |
| 1157 | - | |
| 1158 | - /* | |
| 1159 | - * `guid` is intentionally omitted: the legacy option only kept | |
| 1160 | - * the normalized (schemeless) form, so we can't reconstruct the | |
| 1161 | - * original URL. Storing the schemeless string would be mangled | |
| 1162 | - * by `esc_url()`. Leave WordPress to auto-generate the guid | |
| 1163 | - * — it's not used for lookups, only for debugging. | |
| 1164 | - */ | |
| 1165 | - $result = \wp_insert_post( | |
| 1166 | - array( | |
| 1167 | - 'post_type' => Tombstone::POST_TYPE, | |
| 1168 | - 'post_status' => 'publish', | |
| 1169 | - 'post_name' => $hash, | |
| 1170 | - 'post_author' => 0, | |
| 1171 | - ), | |
| 1172 | - true | |
| 1173 | - ); | |
| 1174 | - | |
| 1175 | - if ( \is_wp_error( $result ) || ! $result ) { | |
| 1176 | - /* | |
| 1177 | - * Keep failed inserts in the legacy option so the next batch | |
| 1178 | - * retries them. `Tombstone::exists_local()` still falls back | |
| 1179 | - * to the option, so the tombstone remains discoverable. | |
| 1180 | - */ | |
| 1181 | - $remaining[] = $normalized; | |
| 1182 | - } else { | |
| 1183 | - $progressed = true; | |
| 1184 | - } | |
| 1185 | - } | |
| 1186 | - | |
| 1187 | - if ( empty( $remaining ) ) { | |
| 1188 | - \delete_option( 'activitypub_tombstone_urls' ); | |
| 1189 | - return null; | |
| 1190 | - } | |
| 1191 | - | |
| 1192 | - /* | |
| 1193 | - * Disable autoload while we drain. The point of the migration is to | |
| 1194 | - * stop this option from contributing to `alloptions` pressure, so | |
| 1195 | - * flip the flag immediately rather than waiting for the option to | |
| 1196 | - * be fully empty before the relief kicks in. | |
| 1197 | - */ | |
| 1198 | - \update_option( 'activitypub_tombstone_urls', \array_values( $remaining ), false ); | |
| 1199 | - | |
| 1200 | - /* | |
| 1201 | - * If nothing in this batch was drained — every insert errored and | |
| 1202 | - * nothing was already migrated — halt the scheduler so we don't loop | |
| 1203 | - * forever on a persistent failure. The legacy option still backs | |
| 1204 | - * exists_local(), so the data isn't lost; an admin can re-trigger | |
| 1205 | - * the migration via `wp cron event run activitypub_tombstone_migrate` | |
| 1206 | - * after fixing the underlying cause. | |
| 1207 | - */ | |
| 1208 | - if ( ! $progressed ) { | |
| 1209 | - return null; | |
| 1210 | - } | |
| 1211 | - | |
| 1212 | - return array( 'batch_size' => (int) $batch_size ); | |
| 1213 | - } | |
| 1214 | - | |
| 1215 | - /** | |
| 1216 | 1087 | * Migrate avatar URLs from comment meta to remote actors in batches. |
| 1217 | 1088 | * |
| 1218 | 1089 | * This migration: |
| 1219 | 1090 | * 1. Finds all comments with ActivityPub protocol and avatar_url meta |
| @@ -1276,9 +1147,9 @@ | ||
| 1276 | 1147 | } |
| 1277 | 1148 | } |
| 1278 | 1149 | |
| 1279 | 1150 | // Return batch info if there are more comments to process. |
| 1280 | - if ( \count( $comments ) === $batch_size ) { | |
| 1151 | + if ( count( $comments ) === $batch_size ) { | |
| 1281 | 1152 | return array( |
| 1282 | 1153 | 'batch_size' => $batch_size, |
| 1283 | 1154 | ); |
| 1284 | 1155 | } |
| @@ -1283,115 +1154,6 @@ | ||
| 1283 | 1154 | ); |
| 1284 | 1155 | } |
| 1285 | 1156 | |
| 1286 | 1157 | return null; |
| 1287 | - } | |
| 1288 | - | |
| 1289 | - /** | |
| 1290 | - * Migrate emoji data from stored actor JSON to post meta. | |
| 1291 | - * | |
| 1292 | - * This migration: | |
| 1293 | - * 1. Finds all remote actor posts without _activitypub_emoji meta | |
| 1294 | - * 2. Extracts emoji from stored JSON in post_content | |
| 1295 | - * 3. Stores as _activitypub_emoji post meta | |
| 1296 | - * | |
| 1297 | - * @param int $batch_size Optional. Number of actors to process per batch. Default 50. | |
| 1298 | - * @param int $offset Optional. Offset for pagination. Default 0. | |
| 1299 | - * @return array|null Array with batch size if there are more actors to process, null otherwise. | |
| 1300 | - */ | |
| 1301 | - public static function migrate_actor_emoji( $batch_size = 50, $offset = 0 ) { | |
| 1302 | - $actors = \get_posts( | |
| 1303 | - array( | |
| 1304 | - 'post_type' => Remote_Actors::POST_TYPE, | |
| 1305 | - 'posts_per_page' => $batch_size, | |
| 1306 | - 'offset' => $offset, | |
| 1307 | - 'post_status' => 'any', | |
| 1308 | - 'orderby' => 'ID', | |
| 1309 | - 'order' => 'ASC', | |
| 1310 | - ) | |
| 1311 | - ); | |
| 1312 | - | |
| 1313 | - foreach ( $actors as $actor_post ) { | |
| 1314 | - if ( empty( $actor_post->post_content ) ) { | |
| 1315 | - continue; | |
| 1316 | - } | |
| 1317 | - | |
| 1318 | - $actor_data = \json_decode( $actor_post->post_content, true ); | |
| 1319 | - if ( ! $actor_data ) { | |
| 1320 | - continue; | |
| 1321 | - } | |
| 1322 | - | |
| 1323 | - $emoji_meta = Emoji::prepare_actor_meta( $actor_data ); | |
| 1324 | - if ( ! empty( $emoji_meta['_activitypub_emoji'] ) ) { | |
| 1325 | - \update_post_meta( $actor_post->ID, '_activitypub_emoji', $emoji_meta['_activitypub_emoji'] ); | |
| 1326 | - } | |
| 1327 | - } | |
| 1328 | - | |
| 1329 | - // Return batch info if there are more actors to process. | |
| 1330 | - if ( \count( $actors ) === $batch_size ) { | |
| 1331 | - return array( | |
| 1332 | - 'batch_size' => $batch_size, | |
| 1333 | - 'offset' => $offset + $batch_size, | |
| 1334 | - ); | |
| 1335 | - } | |
| 1336 | - | |
| 1337 | - return null; | |
| 1338 | - } | |
| 1339 | - | |
| 1340 | - /** | |
| 1341 | - * Migrate the Application key pair option from the old name to the new name. | |
| 1342 | - * | |
| 1343 | - * Renames `activitypub_keypair_for_-1` to `activitypub_application_keypair`. | |
| 1344 | - * Older separate key options (activitypub_application_user_public_key / | |
| 1345 | - * activitypub_application_user_private_key) are migrated lazily on first read. | |
| 1346 | - * | |
| 1347 | - * @since 9.1.0 | |
| 1348 | - */ | |
| 1349 | - public static function migrate_application_keypair_option() { | |
| 1350 | - self::update_options_key( 'activitypub_keypair_for_-1', Application::KEYPAIR_OPTION_KEY ); | |
| 1351 | - | |
| 1352 | - // The raw rename bypasses the options API, so drop only the two stale option caches (plus the autoload bucket) instead of flushing everything. | |
| 1353 | - \wp_cache_delete( 'activitypub_keypair_for_-1', 'options' ); | |
| 1354 | - \wp_cache_delete( Application::KEYPAIR_OPTION_KEY, 'options' ); | |
| 1355 | - \wp_cache_delete( 'alloptions', 'options' ); | |
| 1356 | - | |
| 1357 | - /* | |
| 1358 | - * If an early Application::get_keypair() read already created the destination | |
| 1359 | - * option, the rename above is a no-op blocked by the unique `option_name`, | |
| 1360 | - * leaving the legacy row behind. Drop it once the destination is in place. | |
| 1361 | - */ | |
| 1362 | - if ( false !== \get_option( Application::KEYPAIR_OPTION_KEY, false ) ) { | |
| 1363 | - \delete_option( 'activitypub_keypair_for_-1' ); | |
| 1364 | - } | |
| 1365 | - } | |
| 1366 | - | |
| 1367 | - /** | |
| 1368 | - * Delete outbox items that belonged to the Application actor. | |
| 1369 | - * | |
| 1370 | - * The Application used to queue Reject activities through the Outbox as user | |
| 1371 | - * ID -1. It no longer dispatches activities, so any pending items are | |
| 1372 | - * undeliverable and are removed. | |
| 1373 | - * | |
| 1374 | - * @since 9.1.0 | |
| 1375 | - */ | |
| 1376 | - public static function delete_application_outbox_items() { | |
| 1377 | - $items = \get_posts( | |
| 1378 | - array( | |
| 1379 | - 'post_type' => Outbox::POST_TYPE, | |
| 1380 | - 'post_status' => 'any', | |
| 1381 | - 'nopaging' => true, | |
| 1382 | - 'fields' => 'ids', | |
| 1383 | - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query | |
| 1384 | - 'meta_query' => array( | |
| 1385 | - array( | |
| 1386 | - 'key' => '_activitypub_activity_actor', | |
| 1387 | - 'value' => 'application', | |
| 1388 | - ), | |
| 1389 | - ), | |
| 1390 | - ) | |
| 1391 | - ); | |
| 1392 | - | |
| 1393 | - foreach ( $items as $item_id ) { | |
| 1394 | - \wp_delete_post( $item_id, true ); | |
| 1395 | - } | |
| 1396 | 1158 | } |
| 1397 | 1159 | } |