PluginProbe
ActivityPub / 5.3.1
ActivityPub v5.3.1
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 +587 -41 2.1.05.3.1 View file →
@@ -1,10 +1,17 @@
1 1 <?php
2 +/**
3 + * Migration class file.
4 + *
5 + * @package Activitypub
6 + */
7 +
2 8 namespace Activitypub;
3 9
4 -use Activitypub\Activitypub;
5 -use Activitypub\Model\Blog_User;
10 +use Activitypub\Collection\Actors;
6 11 use Activitypub\Collection\Followers;
12 +use Activitypub\Collection\Outbox;
13 +use Activitypub\Transformer\Factory;
7 14
8 15 /**
9 16 * ActivityPub Migration Class
10 17 *
@@ -11,12 +18,16 @@
11 18 * @author Matthias Pfefferle
12 19 */
13 20 class Migration {
14 21 /**
15 - * Initialize the class, registering WordPress hooks
22 + * Initialize the class, registering WordPress hooks.
16 23 */
17 24 public static function init() {
18 - \add_action( 'activitypub_schedule_migration', array( self::class, 'maybe_migrate' ) );
25 + \add_action( 'activitypub_migrate', array( self::class, 'async_migration' ) );
26 + \add_action( 'activitypub_upgrade', array( self::class, 'async_upgrade' ), 10, 99 );
27 + \add_action( 'activitypub_update_comment_counts', array( self::class, 'update_comment_counts' ), 10, 2 );
28 +
29 + self::maybe_migrate();
19 30 }
20 31
21 32 /**
22 33 * Get the target version.
@@ -23,12 +34,16 @@
23 34 *
24 35 * This is the version that the database structure will be updated to.
25 36 * It is the same as the plugin version.
26 37 *
38 + * @deprecated 4.2.0 Use constant ACTIVITYPUB_PLUGIN_VERSION directly.
39 + *
27 40 * @return string The target version.
28 41 */
29 42 public static function get_target_version() {
30 - return get_plugin_version();
43 + _deprecated_function( __FUNCTION__, '4.2.0', 'ACTIVITYPUB_PLUGIN_VERSION' );
44 +
45 + return ACTIVITYPUB_PLUGIN_VERSION;
31 46 }
32 47
33 48 /**
34 49 * The current version of the database structure.
@@ -41,18 +56,25 @@
41 56
42 57 /**
43 58 * Locks the database migration process to prevent simultaneous migrations.
44 59 *
45 - * @return void
60 + * @return bool|int True if the lock was successful, timestamp of existing lock otherwise.
46 61 */
47 62 public static function lock() {
48 - \update_option( 'activitypub_migration_lock', \time() );
63 + global $wpdb;
64 +
65 + // Try to lock.
66 + $lock_result = (bool) $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", 'activitypub_migration_lock', \time() ) ); // phpcs:ignore WordPress.DB
67 +
68 + if ( ! $lock_result ) {
69 + $lock_result = \get_option( 'activitypub_migration_lock' );
70 + }
71 +
72 + return $lock_result;
49 73 }
50 74
51 75 /**
52 76 * Unlocks the database migration process.
53 - *
54 - * @return void
55 77 */
56 78 public static function unlock() {
57 79 \delete_option( 'activitypub_migration_lock' );
58 80 }
@@ -84,11 +106,11 @@
84 106 *
85 107 * @return bool True if the database structure is up to date, false otherwise.
86 108 */
87 109 public static function is_latest_version() {
88 - return (bool) version_compare(
110 + return (bool) \version_compare(
89 111 self::get_version(),
90 - self::get_target_version(),
112 + ACTIVITYPUB_PLUGIN_VERSION,
91 113 '=='
92 114 );
93 115 }
94 116
@@ -107,57 +129,143 @@
107 129 self::lock();
108 130
109 131 $version_from_db = self::get_version();
110 132
111 - if ( version_compare( $version_from_db, '0.17.0', '<' ) ) {
133 + // Check for initial migration.
134 + if ( ! $version_from_db ) {
135 + self::add_default_settings();
136 + $version_from_db = ACTIVITYPUB_PLUGIN_VERSION;
137 + }
138 +
139 + // Schedule the async migration.
140 + if ( ! \wp_next_scheduled( 'activitypub_migrate', $version_from_db ) ) {
141 + \wp_schedule_single_event( \time(), 'activitypub_migrate', array( $version_from_db ) );
142 + }
143 + if ( \version_compare( $version_from_db, '0.17.0', '<' ) ) {
112 144 self::migrate_from_0_16();
113 145 }
114 - if ( version_compare( $version_from_db, '1.0.0', '<' ) ) {
115 - self::migrate_from_0_17();
116 - }
117 - if ( version_compare( $version_from_db, '1.3.0', '<' ) ) {
146 + if ( \version_compare( $version_from_db, '1.3.0', '<' ) ) {
118 147 self::migrate_from_1_2_0();
119 148 }
120 - if ( version_compare( $version_from_db, '2.1.0', '<' ) ) {
149 + if ( \version_compare( $version_from_db, '2.1.0', '<' ) ) {
121 150 self::migrate_from_2_0_0();
122 151 }
152 + if ( \version_compare( $version_from_db, '2.3.0', '<' ) ) {
153 + self::migrate_from_2_2_0();
154 + }
155 + if ( \version_compare( $version_from_db, '3.0.0', '<' ) ) {
156 + self::migrate_from_2_6_0();
157 + }
158 + if ( \version_compare( $version_from_db, '4.0.0', '<' ) ) {
159 + self::migrate_to_4_0_0();
160 + }
161 + if ( \version_compare( $version_from_db, '4.1.0', '<' ) ) {
162 + self::migrate_to_4_1_0();
163 + }
164 + if ( \version_compare( $version_from_db, '4.5.0', '<' ) ) {
165 + \wp_schedule_single_event( \time() + MINUTE_IN_SECONDS, 'activitypub_update_comment_counts' );
166 + }
167 + if ( \version_compare( $version_from_db, '4.7.1', '<' ) ) {
168 + self::migrate_to_4_7_1();
169 + }
170 + if ( \version_compare( $version_from_db, '4.7.2', '<' ) ) {
171 + self::migrate_to_4_7_2();
172 + }
173 + if ( \version_compare( $version_from_db, '4.7.3', '<' ) ) {
174 + add_action( 'init', 'flush_rewrite_rules', 20 );
175 + }
176 + if ( \version_compare( $version_from_db, '5.0.0', '<' ) ) {
177 + Scheduler::register_schedules();
178 + \wp_schedule_single_event( \time(), 'activitypub_upgrade', array( 'create_post_outbox_items' ) );
179 + \wp_schedule_single_event( \time() + 15, 'activitypub_upgrade', array( 'create_comment_outbox_items' ) );
180 + add_action( 'init', 'flush_rewrite_rules', 20 );
181 + }
182 + if ( \version_compare( $version_from_db, '5.2.0', '<' ) ) {
183 + Scheduler::register_schedules();
184 + }
185 + if ( \version_compare( $version_from_db, '5.3.0', '<' ) ) {
186 + add_action( 'init', 'flush_rewrite_rules', 20 );
187 + }
123 188
124 - update_option( 'activitypub_db_version', self::get_target_version() );
189 + /*
190 + * Add new update routines above this comment. ^
191 + *
192 + * Use 'unreleased' as the version number for new migrations and add tests for the callback directly.
193 + * The release script will automatically replace it with the actual version number.
194 + * Example:
195 + *
196 + * if ( \version_compare( $version_from_db, 'unreleased', '<' ) ) {
197 + * // Update routine.
198 + * }
199 + */
125 200
201 + /**
202 + * Fires when the system has to be migrated.
203 + *
204 + * @param string $version_from_db The version from which to migrate.
205 + * @param string $target_version The target version to migrate to.
206 + */
207 + \do_action( 'activitypub_migrate', $version_from_db, ACTIVITYPUB_PLUGIN_VERSION );
208 +
209 + \update_option( 'activitypub_db_version', ACTIVITYPUB_PLUGIN_VERSION );
210 +
126 211 self::unlock();
127 212 }
128 213
129 214 /**
130 - * Updates the DB-schema of the followers-list
215 + * Asynchronously migrates the database structure.
131 216 *
132 - * @return void
217 + * @param string $version_from_db The version from which to migrate.
133 218 */
134 - private static function migrate_from_0_17() {
135 - // migrate followers
136 - foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) {
137 - $followers = get_user_meta( $user_id, 'activitypub_followers', true );
219 + public static function async_migration( $version_from_db ) {
220 + if ( \version_compare( $version_from_db, '1.0.0', '<' ) ) {
221 + self::migrate_from_0_17();
222 + }
223 + }
138 224
139 - if ( $followers ) {
140 - foreach ( $followers as $actor ) {
141 - Followers::add_follower( $user_id, $actor );
142 - }
143 - }
225 + /**
226 + * Asynchronously runs upgrade routines.
227 + *
228 + * @param callable $callback Callable upgrade routine. Must be a method of this class.
229 + * @params mixed ...$args Optional. Parameters that get passed to the callback.
230 + */
231 + public static function async_upgrade( $callback ) {
232 + $args = \func_get_args();
233 +
234 + // Bail if the existing lock is still valid.
235 + if ( self::is_locked() ) {
236 + \wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'activitypub_upgrade', $args );
237 + return;
144 238 }
145 239
146 - Activitypub::flush_rewrite_rules();
240 + self::lock();
241 +
242 + $callback = array_shift( $args ); // Remove $callback from arguments.
243 + $next = \call_user_func_array( array( self::class, $callback ), $args );
244 +
245 + self::unlock();
246 +
247 + if ( ! empty( $next ) ) {
248 + // Schedule the next run, adding the result to the arguments.
249 + \wp_schedule_single_event(
250 + \time() + 30,
251 + 'activitypub_upgrade',
252 + \array_merge( array( $callback ), \array_values( $next ) )
253 + );
254 + }
147 255 }
148 256
149 257 /**
150 258 * Updates the custom template to use shortcodes instead of the deprecated templates.
151 - *
152 - * @return void
153 259 */
154 260 private static function migrate_from_0_16() {
155 261 // Get the custom template.
156 262 $old_content = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
157 263
158 - // If the old content exists but is a blank string, we're going to need a flag to updated it even
159 - // after setting it to the default contents.
264 + /*
265 + * If the old content exists but is a blank string, we're going to need a flag to updated it even
266 + * after setting it to the default contents.
267 + */
160 268 $need_update = false;
161 269
162 270 // If the old contents is blank, use the defaults.
163 271 if ( '' === $old_content ) {
@@ -183,14 +291,30 @@
183 291 }
184 292 }
185 293
186 294 /**
187 - * Clear the cache after updating to 1.3.0
188 - *
189 - * @return void
295 + * Updates the DB-schema of the followers-list.
190 296 */
297 + public static function migrate_from_0_17() {
298 + // Migrate followers.
299 + foreach ( get_users( array( 'fields' => 'ID' ) ) as $user_id ) {
300 + $followers = get_user_meta( $user_id, 'activitypub_followers', true );
301 +
302 + if ( $followers ) {
303 + foreach ( $followers as $actor ) {
304 + Followers::add_follower( $user_id, $actor );
305 + }
306 + }
307 + }
308 +
309 + Activitypub::flush_rewrite_rules();
310 + }
311 +
312 + /**
313 + * Clear the cache after updating to 1.3.0.
314 + */
191 315 private static function migrate_from_1_2_0() {
192 - $user_ids = get_users(
316 + $user_ids = \get_users(
193 317 array(
194 318 'fields' => 'ID',
195 319 'capability__in' => array( 'publish_posts' ),
196 320 )
@@ -201,11 +325,9 @@
201 325 }
202 326 }
203 327
204 328 /**
205 - * Unschedule Hooks after updating to 2.0.0
206 - *
207 - * @return void
329 + * Unschedule Hooks after updating to 2.0.0.
208 330 */
209 331 private static function migrate_from_2_0_0() {
210 332 wp_clear_scheduled_hook( 'activitypub_send_post_activity' );
211 333 wp_clear_scheduled_hook( 'activitypub_send_update_activity' );
@@ -213,6 +335,430 @@
213 335
214 336 wp_unschedule_hook( 'activitypub_send_post_activity' );
215 337 wp_unschedule_hook( 'activitypub_send_update_activity' );
216 338 wp_unschedule_hook( 'activitypub_send_delete_activity' );
339 +
340 + $object_type = \get_option( 'activitypub_object_type', ACTIVITYPUB_DEFAULT_OBJECT_TYPE );
341 + if ( 'article' === $object_type ) {
342 + \update_option( 'activitypub_object_type', 'wordpress-post-format' );
343 + }
344 + }
345 +
346 + /**
347 + * Add the ActivityPub capability to all users that can publish posts
348 + * Delete old meta to store followers.
349 + */
350 + private static function migrate_from_2_2_0() {
351 + // Add the ActivityPub capability to all users that can publish posts.
352 + self::add_activitypub_capability();
353 + }
354 +
355 + /**
356 + * Rename DB fields.
357 + */
358 + private static function migrate_from_2_6_0() {
359 + wp_cache_flush();
360 +
361 + self::update_usermeta_key( 'activitypub_user_description', 'activitypub_description' );
362 +
363 + self::update_options_key( 'activitypub_blog_user_description', 'activitypub_blog_description' );
364 + self::update_options_key( 'activitypub_blog_user_identifier', 'activitypub_blog_identifier' );
365 + }
366 +
367 + /**
368 + * * Update actor-mode settings.
369 + * * Get the ID of the latest blog post and save it to the options table.
370 + */
371 + private static function migrate_to_4_0_0() {
372 + $latest_post_id = 0;
373 +
374 + // Get the ID of the latest blog post and save it to the options table.
375 + $latest_post = get_posts(
376 + array(
377 + 'numberposts' => 1,
378 + 'orderby' => 'ID',
379 + 'order' => 'DESC',
380 + 'post_type' => 'any',
381 + 'post_status' => 'publish',
382 + )
383 + );
384 +
385 + if ( $latest_post ) {
386 + $latest_post_id = $latest_post[0]->ID;
387 + }
388 +
389 + \update_option( 'activitypub_last_post_with_permalink_as_id', $latest_post_id );
390 +
391 + $users = \get_users(
392 + array(
393 + 'capability__in' => array( 'activitypub' ),
394 + )
395 + );
396 +
397 + foreach ( $users as $user ) {
398 + $followers = Followers::get_followers( $user->ID );
399 +
400 + if ( $followers ) {
401 + \update_user_option( $user->ID, 'activitypub_use_permalink_as_id', '1' );
402 + }
403 + }
404 +
405 + $followers = Followers::get_followers( Actors::BLOG_USER_ID );
406 +
407 + if ( $followers ) {
408 + \update_option( 'activitypub_use_permalink_as_id_for_blog', '1' );
409 + }
410 +
411 + self::migrate_actor_mode();
412 + }
413 +
414 + /**
415 + * Upate to 4.1.0
416 + *
417 + * * Migrate the `activitypub_post_content_type` to only use `activitypub_custom_post_content`.
418 + */
419 + public static function migrate_to_4_1_0() {
420 + $content_type = \get_option( 'activitypub_post_content_type' );
421 +
422 + switch ( $content_type ) {
423 + case 'excerpt':
424 + $template = "[ap_excerpt]\n\n[ap_permalink type=\"html\"]";
425 + break;
426 + case 'title':
427 + $template = "[ap_title type=\"html\"]\n\n[ap_permalink type=\"html\"]";
428 + break;
429 + case 'content':
430 + $template = "[ap_content]\n\n[ap_permalink type=\"html\"]\n\n[ap_hashtags]";
431 + break;
432 + case 'custom':
433 + $template = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
434 + break;
435 + default:
436 + $template = ACTIVITYPUB_CUSTOM_POST_CONTENT;
437 + break;
438 + }
439 +
440 + \update_option( 'activitypub_custom_post_content', $template );
441 +
442 + \delete_option( 'activitypub_post_content_type' );
443 +
444 + $object_type = \get_option( 'activitypub_object_type', false );
445 + if ( ! $object_type ) {
446 + \update_option( 'activitypub_object_type', 'note' );
447 + }
448 +
449 + // Clean up empty visibility meta.
450 + global $wpdb;
451 + $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
452 + "DELETE FROM $wpdb->postmeta
453 + WHERE meta_key = 'activitypub_content_visibility'
454 + AND (meta_value IS NULL OR meta_value = '')"
455 + );
456 + }
457 +
458 + /**
459 + * Updates post meta keys to be prefixed with an underscore.
460 + */
461 + public static function migrate_to_4_7_1() {
462 + global $wpdb;
463 +
464 + $meta_keys = array(
465 + 'activitypub_actor_json',
466 + 'activitypub_canonical_url',
467 + 'activitypub_errors',
468 + 'activitypub_inbox',
469 + 'activitypub_user_id',
470 + );
471 +
472 + foreach ( $meta_keys as $meta_key ) {
473 + // phpcs:ignore WordPress.DB
474 + $wpdb->update( $wpdb->postmeta, array( 'meta_key' => '_' . $meta_key ), array( 'meta_key' => $meta_key ) );
475 + }
476 + }
477 +
478 + /**
479 + * Clears the post cache for Followers, we should have done this in 4.7.1 when we renamed those keys.
480 + */
481 + public static function migrate_to_4_7_2() {
482 + global $wpdb;
483 + // phpcs:ignore WordPress.DB
484 + $followers = $wpdb->get_col(
485 + $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s", Followers::POST_TYPE )
486 + );
487 + foreach ( $followers as $id ) {
488 + clean_post_cache( $id );
489 + }
490 + }
491 +
492 + /**
493 + * Update comment counts for posts in batches.
494 + *
495 + * @see Comment::pre_wp_update_comment_count_now()
496 + * @param int $batch_size Optional. Number of posts to process per batch. Default 100.
497 + * @param int $offset Optional. Number of posts to skip. Default 0.
498 + */
499 + public static function update_comment_counts( $batch_size = 100, $offset = 0 ) {
500 + global $wpdb;
501 +
502 + // Bail if the existing lock is still valid.
503 + if ( self::is_locked() ) {
504 + \wp_schedule_single_event(
505 + time() + ( 5 * MINUTE_IN_SECONDS ),
506 + 'activitypub_update_comment_counts',
507 + array(
508 + 'batch_size' => $batch_size,
509 + 'offset' => $offset,
510 + )
511 + );
512 + return;
513 + }
514 +
515 + self::lock();
516 +
517 + Comment::register_comment_types();
518 + $comment_types = Comment::get_comment_type_slugs();
519 + $type_inclusion = "AND comment_type IN ('" . implode( "','", $comment_types ) . "')";
520 +
521 + // Get and process this batch.
522 + $post_ids = $wpdb->get_col( // phpcs:ignore WordPress.DB
523 + $wpdb->prepare(
524 + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
525 + "SELECT DISTINCT comment_post_ID FROM {$wpdb->comments} WHERE comment_approved = '1' {$type_inclusion} ORDER BY comment_post_ID LIMIT %d OFFSET %d",
526 + $batch_size,
527 + $offset
528 + )
529 + );
530 +
531 + foreach ( $post_ids as $post_id ) {
532 + \wp_update_comment_count_now( $post_id );
533 + }
534 +
535 + if ( count( $post_ids ) === $batch_size ) {
536 + // Schedule next batch.
537 + \wp_schedule_single_event(
538 + time() + MINUTE_IN_SECONDS,
539 + 'activitypub_update_comment_counts',
540 + array(
541 + 'batch_size' => $batch_size,
542 + 'offset' => $offset + $batch_size,
543 + )
544 + );
545 + }
546 +
547 + self::unlock();
548 + }
549 +
550 + /**
551 + * Create outbox items for posts in batches.
552 + *
553 + * @param int $batch_size Optional. Number of posts to process per batch. Default 50.
554 + * @param int $offset Optional. Number of posts to skip. Default 0.
555 + * @return array|null Array with batch size and offset if there are more posts to process, null otherwise.
556 + */
557 + public static function create_post_outbox_items( $batch_size = 50, $offset = 0 ) {
558 + $posts = \get_posts(
559 + array(
560 + // our own `ap_outbox` will be excluded from `any` by virtue of its `exclude_from_search` arg.
561 + 'post_type' => 'any',
562 + 'posts_per_page' => $batch_size,
563 + 'offset' => $offset,
564 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
565 + 'meta_query' => array(
566 + array(
567 + 'key' => 'activitypub_status',
568 + 'value' => 'federated',
569 + ),
570 + ),
571 + )
572 + );
573 +
574 + // Avoid multiple queries for post meta.
575 + \update_postmeta_cache( \wp_list_pluck( $posts, 'ID' ) );
576 +
577 + foreach ( $posts as $post ) {
578 + $visibility = \get_post_meta( $post->ID, 'activitypub_content_visibility', true );
579 +
580 + self::add_to_outbox( $post, 'Create', $post->post_author, $visibility );
581 +
582 + // Add Update activity when the post has been modified.
583 + if ( $post->post_modified !== $post->post_date ) {
584 + self::add_to_outbox( $post, 'Update', $post->post_author, $visibility );
585 + }
586 + }
587 +
588 + if ( count( $posts ) === $batch_size ) {
589 + return array(
590 + 'batch_size' => $batch_size,
591 + 'offset' => $offset + $batch_size,
592 + );
593 + }
594 +
595 + return null;
596 + }
597 +
598 + /**
599 + * Create outbox items for comments in batches.
600 + *
601 + * @param int $batch_size Optional. Number of posts to process per batch. Default 50.
602 + * @param int $offset Optional. Number of posts to skip. Default 0.
603 + * @return array|null Array with batch size and offset if there are more posts to process, null otherwise.
604 + */
605 + public static function create_comment_outbox_items( $batch_size = 50, $offset = 0 ) {
606 + $comments = \get_comments(
607 + array(
608 + 'author__not_in' => array( 0 ), // Limit to comments by registered users.
609 + 'number' => $batch_size,
610 + 'offset' => $offset,
611 + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
612 + 'meta_query' => array(
613 + array(
614 + 'key' => 'activitypub_status',
615 + 'value' => 'federated',
616 + ),
617 + ),
618 + )
619 + );
620 +
621 + foreach ( $comments as $comment ) {
622 + self::add_to_outbox( $comment, 'Create', $comment->user_id );
623 + }
624 +
625 + if ( count( $comments ) === $batch_size ) {
626 + return array(
627 + 'batch_size' => $batch_size,
628 + 'offset' => $offset + $batch_size,
629 + );
630 + }
631 +
632 + return null;
633 + }
634 +
635 + /**
636 + * Set the defaults needed for the plugin to work.
637 + *
638 + * Add the ActivityPub capability to all users that can publish posts.
639 + */
640 + public static function add_default_settings() {
641 + self::add_activitypub_capability();
642 + self::add_notification_defaults();
643 + }
644 +
645 + /**
646 + * Add an activity to the outbox without federating it.
647 + *
648 + * @param \WP_Post|\WP_Comment $comment The comment or post object.
649 + * @param string $activity_type The type of activity.
650 + * @param int $user_id The user ID.
651 + * @param string $visibility Optional. The visibility of the content. Default 'public'.
652 + */
653 + private static function add_to_outbox( $comment, $activity_type, $user_id, $visibility = ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC ) {
654 + $transformer = Factory::get_transformer( $comment );
655 + if ( ! $transformer || \is_wp_error( $transformer ) ) {
656 + return;
657 + }
658 +
659 + $activity = $transformer->to_object();
660 + if ( ! $activity || \is_wp_error( $activity ) ) {
661 + return;
662 + }
663 +
664 + // If the user is disabled, fall back to the blog user when available.
665 + if ( is_user_disabled( $user_id ) ) {
666 + if ( is_user_disabled( Actors::BLOG_USER_ID ) ) {
667 + return;
668 + } else {
669 + $user_id = Actors::BLOG_USER_ID;
670 + }
671 + }
672 +
673 + $post_id = Outbox::add( $activity, $activity_type, $user_id, $visibility );
674 +
675 + // Immediately set to publish, no federation needed.
676 + \wp_publish_post( $post_id );
677 + }
678 +
679 + /**
680 + * Add the ActivityPub capability to all users that can publish posts.
681 + */
682 + private static function add_activitypub_capability() {
683 + // Get all WP_User objects that can publish posts.
684 + $users = \get_users(
685 + array(
686 + 'capability__in' => array( 'publish_posts' ),
687 + )
688 + );
689 +
690 + // Add ActivityPub capability to all users that can publish posts.
691 + foreach ( $users as $user ) {
692 + $user->add_cap( 'activitypub' );
693 + }
694 + }
695 +
696 + /**
697 + * Add default notification settings.
698 + */
699 + private static function add_notification_defaults() {
700 + \add_option( 'activitypub_mailer_new_follower', '1' );
701 + \add_option( 'activitypub_mailer_new_dm', '1' );
702 + }
703 +
704 + /**
705 + * Rename meta keys.
706 + *
707 + * @param string $old_key The old comment meta key.
708 + * @param string $new_key The new comment meta key.
709 + */
710 + private static function update_usermeta_key( $old_key, $new_key ) {
711 + global $wpdb;
712 +
713 + $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
714 + $wpdb->usermeta,
715 + array( 'meta_key' => $new_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
716 + array( 'meta_key' => $old_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
717 + array( '%s' ),
718 + array( '%s' )
719 + );
720 + }
721 +
722 + /**
723 + * Rename option keys.
724 + *
725 + * @param string $old_key The old option key.
726 + * @param string $new_key The new option key.
727 + */
728 + private static function update_options_key( $old_key, $new_key ) {
729 + global $wpdb;
730 +
731 + $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
732 + $wpdb->options,
733 + array( 'option_name' => $new_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
734 + array( 'option_name' => $old_key ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
735 + array( '%s' ),
736 + array( '%s' )
737 + );
738 + }
739 +
740 + /**
741 + * Migrate the actor mode settings.
742 + */
743 + public static function migrate_actor_mode() {
744 + $blog_profile = \get_option( 'activitypub_enable_blog_user', '0' );
745 + $author_profiles = \get_option( 'activitypub_enable_users', '1' );
746 +
747 + if (
748 + '1' === $blog_profile &&
749 + '1' === $author_profiles
750 + ) {
751 + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE );
752 + } elseif (
753 + '1' === $blog_profile &&
754 + '1' !== $author_profiles
755 + ) {
756 + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE );
757 + } elseif (
758 + '1' !== $blog_profile &&
759 + '1' === $author_profiles
760 + ) {
761 + \update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE );
762 + }
217 763 }
218 764 }