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