Skip to content
Draft
68 changes: 68 additions & 0 deletions includes/class-migration.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@

namespace Activitypub;

use Activitypub\Activity\Activity;
use Activitypub\Collection\Actors;
use Activitypub\Collection\Extra_Fields;
use Activitypub\Collection\Followers;
use Activitypub\Collection\Following;
use Activitypub\Collection\Outbox;
use Activitypub\Collection\Remote_Actors;
use Activitypub\Model\Blog;
use Activitypub\Model\User;
use Activitypub\Transformer\Factory;

/**
Expand Down Expand Up @@ -215,6 +218,11 @@ public static function maybe_migrate() {
\wp_schedule_single_event( \time(), 'activitypub_migrate_avatar_to_remote_actors' );
}

if ( \version_compare( $version_from_db, 'unreleased', '<' ) ) {
self::migrate_blog_user_to_query_param_id();
self::migrate_users_to_query_param_id();
}

// Ensure all required cron schedules are registered.
Scheduler::register_schedules();

Expand Down Expand Up @@ -1083,6 +1091,66 @@ private static function clean_up_inbox() {
}
}

/**
* Migrate blog user from permalink-based ID to query param ID.
*
* This sends a Move activity from the old @username URL to the new ?author= URL
* for the blog actor, allowing followers to update their records.
*/
private static function migrate_blog_user_to_query_param_id() {
$use_permalink = \get_option( 'activitypub_use_permalink_as_id_for_blog', false );

if ( ! $use_permalink ) {
return;
}

$blog = new Blog();
$old_id = \esc_url( \trailingslashit( get_home_url() ) . '@' . $blog->get_preferred_username() );

$activity = new Activity();
$activity->set_type( 'Move' );
$activity->set_actor( $old_id );
$activity->set_origin( $old_id );
$activity->set_object( $old_id );
$activity->set_target( $blog->get_id() );

add_to_outbox( $activity, null, Actors::BLOG_USER_ID, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE );
}

/**
* Migrate users from permalink-based ID to query param ID.
*
* This sends a Move activity from the old author URL to the new ?author= URL
* for each user that had the permalink-as-id setting.
*/
private static function migrate_users_to_query_param_id() {
$users = \get_users(
array(
'capability__in' => array( 'activitypub' ),
)
);

foreach ( $users as $wp_user ) {
$use_permalink = \get_user_option( 'activitypub_use_permalink_as_id', $wp_user->ID );

if ( '1' !== $use_permalink ) {
continue;
}

$user = new User( $wp_user->ID );
$old_id = $user->get_url();

$activity = new Activity();
$activity->set_type( 'Move' );
$activity->set_actor( $old_id );
$activity->set_origin( $old_id );
$activity->set_object( $old_id );
$activity->set_target( $user->get_id() );

add_to_outbox( $activity, null, $wp_user->ID, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE );
}
}

/**
* Migrate avatar URLs from comment meta to remote actors in batches.
*
Expand Down
4 changes: 2 additions & 2 deletions includes/class-move.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public static function externally( $from, $to ) {
$activity->set_target( $target_actor->get_id() );

// Add to outbox.
return add_to_outbox( $activity, null, $user->get__id(), ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC );
return add_to_outbox( $activity, null, $user->get__id(), ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE );
}

/**
Expand Down Expand Up @@ -162,7 +162,7 @@ public static function internally( $from, $to ) {
$activity->set_object( $actor );
$activity->set_target( $to );

return add_to_outbox( $activity, null, $user->get__id(), ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC );
return add_to_outbox( $activity, null, $user->get__id(), ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE );
}

/**
Expand Down
2 changes: 1 addition & 1 deletion includes/class-scheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ public static function cleanup_remote_actors() {
* @param int $id The ID of the outbox item.
* @param int $offset The offset to add to the scheduled time.
*/
public static function schedule_outbox_activity_for_federation( $id, $offset = 0 ) {
public static function schedule_outbox_activity_for_federation( $id, $offset = 5 ) {
$hook = 'activitypub_process_outbox';
$args = array( $id );

Expand Down
5 changes: 3 additions & 2 deletions includes/collection/class-followers.php
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,9 @@ public static function get_inboxes( $user_id ) {
*/
public static function get_inboxes_for_activity( $json, $actor_id, $batch_size = 50, $offset = 0 ) {
$activity = \json_decode( $json, true );
// Only if this is a Delete. Create handles its own "Announce" in dual user mode.
if ( 'Delete' === ( $activity['type'] ?? null ) ) {
// Delete and Move activities should be sent to all known inboxes.
// Create handles its own "Announce" in dual user mode.
if ( \in_array( $activity['type'] ?? null, array( 'Delete', 'Move' ), true ) ) {
$inboxes = Remote_Actors::get_inboxes();
} else {
$inboxes = self::get_inboxes( $actor_id );
Expand Down
30 changes: 22 additions & 8 deletions includes/model/class-blog.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,6 @@ public function get_id() {
return $id;
}

$permalink = \get_option( 'activitypub_use_permalink_as_id_for_blog', false );

if ( $permalink ) {
return encode_url_path( \home_url( '/@' . $this->get_preferred_username() ) );
}

return \add_query_arg( 'author', $this->_id, \home_url( '/' ) );
}

Expand Down Expand Up @@ -556,11 +550,31 @@ public function get_also_known_as() {
/**
* Returns the movedTo.
*
* @return string The movedTo.
* @return string|null The movedTo URL or null.
*/
public function get_moved_to() {
$moved_to = \get_option( 'activitypub_blog_user_moved_to' );

return $moved_to && $moved_to !== $this->get_id() ? $moved_to : null;
if ( $moved_to && $moved_to !== $this->get_id() ) {
return $moved_to;
}

// If the blog had the old permalink-as-id setting and is being accessed
// via the old permalink URL (no author in query string), return the new ID.
$use_permalink = \get_option( 'activitypub_use_permalink_as_id_for_blog', false );

if ( $use_permalink ) {
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
$query_string = \wp_parse_url( $request_uri, \PHP_URL_QUERY );
$query_params = array();

\wp_parse_str( $query_string ?? '', $query_params );

if ( ! isset( $query_params['author'] ) ) {
return $this->get_id();
}
}

return null;
}
}
30 changes: 22 additions & 8 deletions includes/model/class-user.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,6 @@ public function get_id() {
return $id;
}

$permalink = \get_user_option( 'activitypub_use_permalink_as_id', $this->_id );

if ( '1' === $permalink ) {
return $this->get_url();
}

return \add_query_arg( 'author', $this->_id, \home_url( '/' ) );
}

Expand Down Expand Up @@ -461,11 +455,31 @@ public function get_also_known_as() {
/**
* Returns the movedTo.
*
* @return string The movedTo.
* @return string|null The movedTo URL or null.
*/
public function get_moved_to() {
$moved_to = \get_user_option( 'activitypub_moved_to', $this->_id );

return $moved_to && $moved_to !== $this->get_id() ? $moved_to : null;
if ( $moved_to && $moved_to !== $this->get_id() ) {
return $moved_to;
}

// If this user had the old permalink-as-id setting and is being accessed
// via the old permalink URL (no author in query string), return the new ID.
$use_permalink = \get_user_option( 'activitypub_use_permalink_as_id', $this->_id );

if ( '1' === $use_permalink ) {
$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? \esc_url_raw( \wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
$query_string = \wp_parse_url( $request_uri, \PHP_URL_QUERY );
$query_params = array();

\wp_parse_str( $query_string ?? '', $query_params );

if ( ! isset( $query_params['author'] ) ) {
return $this->get_id();
}
}

return null;
}
}