PluginProbe
ActivityPub / 8.0.2
ActivityPub v8.0.2
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
← All changes | includes/class-migration.php +17 -131 8.3.08.0.2 View file →
@@ -32,10 +32,8 @@
32 32 Scheduler::register_async_batch_callback( 'activitypub_create_post_outbox_items', array( self::class, 'create_post_outbox_items' ) );
33 33 Scheduler::register_async_batch_callback( 'activitypub_create_comment_outbox_items', array( self::class, 'create_comment_outbox_items' ) );
34 34 Scheduler::register_async_batch_callback( 'activitypub_migrate_avatar_to_remote_actors', array( self::class, 'migrate_avatar_to_remote_actors' ) );
35 35 Scheduler::register_async_batch_callback( 'activitypub_migrate_actor_emoji', array( self::class, 'migrate_actor_emoji' ) );
36 - Scheduler::register_async_batch_callback( 'activitypub_backfill_statistics', array( Statistics::class, 'backfill_historical_stats' ) );
37 - Scheduler::register_async_batch_callback( 'activitypub_tombstone_migrate', array( self::class, 'migrate_tombstones_to_cpt' ) );
38 36 }
39 37
40 38 /**
41 39 * The current version of the database structure.
@@ -160,16 +158,21 @@
160 158 }
161 159 if ( \version_compare( $version_from_db, '4.7.2', '<' ) ) {
162 160 self::migrate_to_4_7_2();
163 161 }
162 + if ( \version_compare( $version_from_db, '4.7.3', '<' ) ) {
163 + add_action( 'init', 'flush_rewrite_rules', 20 );
164 + }
164 165 if ( \version_compare( $version_from_db, '5.0.0', '<' ) ) {
165 166 Scheduler::register_schedules();
166 167 \wp_schedule_single_event( \time(), 'activitypub_create_post_outbox_items' );
167 168 \wp_schedule_single_event( \time() + 15, 'activitypub_create_comment_outbox_items' );
169 + add_action( 'init', 'flush_rewrite_rules', 20 );
168 170 }
169 171 if ( \version_compare( $version_from_db, '5.4.0', '<' ) ) {
170 172 \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_actor_json_slashing' ) );
171 173 \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_comment_author_emails' ) );
174 + \add_action( 'init', 'flush_rewrite_rules', 20 );
172 175 }
173 176 if ( \version_compare( $version_from_db, '5.7.0', '<' ) ) {
174 177 self::delete_mastodon_api_orphaned_extra_fields();
175 178 }
@@ -175,16 +178,19 @@
175 178 }
176 179 if ( \version_compare( $version_from_db, '5.8.0', '<' ) ) {
177 180 self::update_notification_options();
178 181 }
182 +
179 183 if ( \version_compare( $version_from_db, '6.0.0', '<' ) ) {
180 184 self::migrate_followers_to_ap_actor_cpt();
181 185 \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_actor_json_storage' ) );
182 186 }
187 +
183 188 if ( \version_compare( $version_from_db, '6.0.1', '<' ) ) {
184 189 self::migrate_followers_to_ap_actor_cpt();
185 190 \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'update_actor_json_storage' ) );
186 191 }
192 +
187 193 if ( \version_compare( $version_from_db, '7.0.0', '<' ) ) {
188 194 wp_unschedule_hook( 'activitypub_update_followers' );
189 195 wp_unschedule_hook( 'activitypub_cleanup_followers' );
190 196
@@ -195,39 +201,30 @@
195 201 if ( ! \wp_next_scheduled( 'activitypub_cleanup_remote_actors' ) ) {
196 202 \wp_schedule_event( time(), 'daily', 'activitypub_cleanup_remote_actors' );
197 203 }
198 204 }
205 +
199 206 if ( \version_compare( $version_from_db, '7.3.0', '<' ) ) {
200 207 self::remove_pending_application_user_follow_requests();
201 208 }
209 +
202 210 if ( \version_compare( $version_from_db, '7.5.0', '<' ) ) {
203 211 self::sync_jetpack_following_meta();
204 212 }
213 +
205 214 if ( \version_compare( $version_from_db, '7.6.0', '<' ) ) {
206 215 self::clean_up_inbox();
207 216 \wp_schedule_single_event( \time(), 'activitypub_migrate_avatar_to_remote_actors' );
208 217 }
218 +
209 219 if ( \version_compare( $version_from_db, '7.9.0', '<' ) ) {
210 220 \wp_schedule_single_event( \time(), 'activitypub_migrate_actor_emoji' );
211 221 }
212 - if ( \version_compare( $version_from_db, '8.1.0', '<' ) && ! \wp_next_scheduled( 'activitypub_backfill_statistics' ) ) {
213 - // Backfill historical statistics data (delay + jitter to avoid load spikes on hosts running many sites).
214 - \wp_schedule_single_event( \time() + HOUR_IN_SECONDS + \wp_rand( 0, 6 * HOUR_IN_SECONDS ), 'activitypub_backfill_statistics' );
222 +
223 + if ( \version_compare( $version_from_db, '8.0.0', '<' ) ) {
224 + Activitypub::flush_rewrite_rules();
215 225 }
216 - if ( \version_compare( $version_from_db, '8.3.0', '<' ) ) {
217 - if ( ! \wp_next_scheduled( 'activitypub_tombstone_migrate' ) ) {
218 - \wp_schedule_single_event( \time() + MINUTE_IN_SECONDS, 'activitypub_tombstone_migrate' );
219 - }
220 - }
221 226
222 - /*
223 - * Defer the flush to late in the `init` cycle (priority 20). Migration::init
224 - * runs at priority 1, which is earlier than most plugins register their
225 - * rewrite rules. Flushing synchronously here would persist a truncated
226 - * ruleset that omits third-party rules added on `init` at priority 10.
227 - */
228 - \add_action( 'init', array( Activitypub::class, 'flush_rewrite_rules' ), 20 );
229 -
230 227 // Ensure all required cron schedules are registered.
231 228 Scheduler::register_schedules();
232 229
233 230 /*
@@ -305,8 +302,10 @@
305 302 Followers::add( $user_id, $actor );
306 303 }
307 304 }
308 305 }
306 +
307 + Activitypub::flush_rewrite_rules();
309 308 }
310 309
311 310 /**
312 311 * Clear the cache after updating to 1.3.0.
@@ -1090,121 +1089,8 @@
1090 1089 // Delete all inbox items and their metadata.
1091 1090 foreach ( $inbox_ids as $post_id ) {
1092 1091 \wp_delete_post( $post_id, true );
1093 1092 }
1094 - }
1095 -
1096 - /**
1097 - * Migrate URLs from the legacy `activitypub_tombstone_urls` option into the
1098 - * `ap_tombstone` custom post type.
1099 - *
1100 - * Chunked async migration. Locking and rescheduling is handled by
1101 - * Scheduler::async_batch — the callback returns `array( 'batch_size' => N )`
1102 - * to request another run, or `null` when the option is fully drained.
1103 - *
1104 - * Legacy entries are already-normalized strings (no scheme), so we bypass
1105 - * URL validation and insert directly via wp_insert_post.
1106 - *
1107 - * @since 8.3.0
1108 - *
1109 - * @param int $batch_size Optional. Number of URLs to process per call. Default 500.
1110 - * @return array|null Args for the next run, or null when migration is complete.
1111 - */
1112 - public static function migrate_tombstones_to_cpt( $batch_size = 500 ) {
1113 - global $wpdb;
1114 -
1115 - $urls = \get_option( 'activitypub_tombstone_urls', null );
1116 -
1117 - if ( null === $urls || ! \is_array( $urls ) || empty( $urls ) ) {
1118 - \delete_option( 'activitypub_tombstone_urls' );
1119 - return null;
1120 - }
1121 -
1122 - $chunk = \array_slice( $urls, 0, (int) $batch_size );
1123 - $remaining = \array_slice( $urls, (int) $batch_size );
1124 - $progressed = false;
1125 -
1126 - foreach ( $chunk as $normalized ) {
1127 - if ( ! \is_string( $normalized ) || '' === $normalized ) {
1128 - // Drop garbage entries — counts as progress.
1129 - $progressed = true;
1130 - continue;
1131 - }
1132 -
1133 - $hash = \md5( $normalized );
1134 -
1135 - /*
1136 - * Light existence check. `get_page_by_path()` would hydrate a
1137 - * full `WP_Post` per loop iteration; on a large registry that
1138 - * adds up fast. We only need a boolean here.
1139 - */
1140 - $exists = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
1141 - $wpdb->prepare(
1142 - "SELECT 1 FROM {$wpdb->posts} WHERE post_type = %s AND post_name = %s LIMIT 1",
1143 - Tombstone::POST_TYPE,
1144 - $hash
1145 - )
1146 - );
1147 - if ( $exists ) {
1148 - $progressed = true;
1149 - continue;
1150 - }
1151 -
1152 - /*
1153 - * `guid` is intentionally omitted: the legacy option only kept
1154 - * the normalized (schemeless) form, so we can't reconstruct the
1155 - * original URL. Storing the schemeless string would be mangled
1156 - * by `esc_url()`. Leave WordPress to auto-generate the guid
1157 - * — it's not used for lookups, only for debugging.
1158 - */
1159 - $result = \wp_insert_post(
1160 - array(
1161 - 'post_type' => Tombstone::POST_TYPE,
1162 - 'post_status' => 'publish',
1163 - 'post_name' => $hash,
1164 - 'post_author' => 0,
1165 - ),
1166 - true
1167 - );
1168 -
1169 - if ( \is_wp_error( $result ) || ! $result ) {
1170 - /*
1171 - * Keep failed inserts in the legacy option so the next batch
1172 - * retries them. `Tombstone::exists_local()` still falls back
1173 - * to the option, so the tombstone remains discoverable.
1174 - */
1175 - $remaining[] = $normalized;
1176 - } else {
1177 - $progressed = true;
1178 - }
1179 - }
1180 -
1181 - if ( empty( $remaining ) ) {
1182 - \delete_option( 'activitypub_tombstone_urls' );
1183 - return null;
1184 - }
1185 -
1186 - /*
1187 - * Disable autoload while we drain. The point of the migration is to
1188 - * stop this option from contributing to `alloptions` pressure, so
1189 - * flip the flag immediately rather than waiting for the option to
1190 - * be fully empty before the relief kicks in.
1191 - */
1192 - \update_option( 'activitypub_tombstone_urls', \array_values( $remaining ), false );
1193 -
1194 - /*
1195 - * If nothing in this batch was drained — every insert errored and
1196 - * nothing was already migrated — halt the scheduler so we don't loop
1197 - * forever on a persistent failure. The legacy option still backs
1198 - * exists_local(), so the data isn't lost; an admin can re-trigger
1199 - * the migration via `wp cron event run activitypub_tombstone_migrate`
1200 - * after fixing the underlying cause.
1201 - */
1202 - if ( ! $progressed ) {
1203 - return null;
1204 - }
1205 -
1206 - return array( 'batch_size' => (int) $batch_size );
1207 1093 }
1208 1094
1209 1095 /**
1210 1096 * Migrate avatar URLs from comment meta to remote actors in batches.