Skip to content

Commit 09b020a

Browse files
authored
Update handler (#2381)
1 parent ffccdf6 commit 09b020a

21 files changed

+638
-250
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: changed
3+
4+
Refactored activity handling to support multiple recipients per activity, allowing posts and interactions to be linked to several local users at once.

includes/collection/class-posts.php

Lines changed: 111 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,23 @@ class Posts {
2828
/**
2929
* Add an object to the collection.
3030
*
31-
* @param array $activity The activity object data.
32-
* @param int $user_id The local user ID.
31+
* @param array $activity The activity object data.
32+
* @param int|int[] $recipients The id(s) of the local blog-user(s).
3333
*
3434
* @return \WP_Post|\WP_Error The object post or WP_Error on failure.
3535
*/
36-
public static function add( $activity, $user_id ) {
36+
public static function add( $activity, $recipients ) {
37+
$recipients = (array) $recipients;
3738
$activity_object = $activity['object'];
38-
$actor = Remote_Actors::fetch_by_uri( object_to_uri( $activity_object['attributedTo'] ) );
39+
40+
$existing = self::get_by_guid( $activity_object['id'] );
41+
// If post exists, call update instead.
42+
if ( ! \is_wp_error( $existing ) ) {
43+
return self::update( $activity, $recipients );
44+
}
45+
46+
// Post doesn't exist, create new post.
47+
$actor = Remote_Actors::fetch_by_uri( object_to_uri( $activity_object['attributedTo'] ) );
3948

4049
if ( \is_wp_error( $actor ) ) {
4150
return $actor;
@@ -49,7 +58,11 @@ public static function add( $activity, $user_id ) {
4958
}
5059

5160
\add_post_meta( $post_id, '_activitypub_remote_actor_id', $actor->ID );
52-
\add_post_meta( $post_id, '_activitypub_user_id', $user_id );
61+
62+
// Add recipients as separate meta entries after post is created.
63+
foreach ( $recipients as $user_id ) {
64+
self::add_recipient( $post_id, $user_id );
65+
}
5366

5467
self::add_taxonomies( $post_id, $activity_object );
5568

@@ -104,12 +117,14 @@ public static function get_by_guid( $guid ) {
104117
/**
105118
* Update an object in the collection.
106119
*
107-
* @param array $activity The activity object data.
108-
* @param int $user_id The local user ID.
120+
* @param array $activity The activity object data.
121+
* @param int|int[] $recipients The id(s) of the local blog-user(s).
109122
*
110123
* @return \WP_Post|\WP_Error The updated object post or WP_Error on failure.
111124
*/
112-
public static function update( $activity, $user_id ) {
125+
public static function update( $activity, $recipients ) {
126+
$recipients = (array) $recipients;
127+
113128
$post = self::get_by_guid( $activity['object']['id'] );
114129
if ( \is_wp_error( $post ) ) {
115130
return $post;
@@ -123,9 +138,9 @@ public static function update( $activity, $user_id ) {
123138
return $post_id;
124139
}
125140

126-
$post_meta = \get_post_meta( $post_id, '_activitypub_user_id', false );
127-
if ( \is_array( $post_meta ) && ! \in_array( (string) $user_id, $post_meta, true ) ) {
128-
\add_post_meta( $post_id, '_activitypub_user_id', $user_id );
141+
// Add new recipients using add_recipient (handles deduplication).
142+
foreach ( $recipients as $user_id ) {
143+
self::add_recipient( $post_id, $user_id );
129144
}
130145

131146
self::add_taxonomies( $post_id, $activity['object'] );
@@ -250,4 +265,89 @@ public static function get_by_remote_actor_id( $actor_id ) {
250265

251266
return $query->posts;
252267
}
268+
269+
/**
270+
* Get all recipients for a post.
271+
*
272+
* @param int $post_id The post ID.
273+
*
274+
* @return int[] Array of user IDs who are recipients.
275+
*/
276+
public static function get_recipients( $post_id ) {
277+
// Get all meta values with key '_activitypub_user_id' (single => false).
278+
$recipients = \get_post_meta( $post_id, '_activitypub_user_id', false );
279+
$recipients = \array_map( 'intval', $recipients );
280+
281+
return $recipients;
282+
}
283+
284+
/**
285+
* Check if a user is a recipient of a post.
286+
*
287+
* @param int $post_id The post ID.
288+
* @param int $user_id The user ID to check.
289+
*
290+
* @return bool True if user is a recipient, false otherwise.
291+
*/
292+
public static function has_recipient( $post_id, $user_id ) {
293+
$recipients = self::get_recipients( $post_id );
294+
295+
return \in_array( (int) $user_id, $recipients, true );
296+
}
297+
298+
/**
299+
* Add a recipient to an existing post.
300+
*
301+
* @param int $post_id The post ID.
302+
* @param int $user_id The user ID to add.
303+
*
304+
* @return bool True on success, false on failure.
305+
*/
306+
public static function add_recipient( $post_id, $user_id ) {
307+
$user_id = (int) $user_id;
308+
// Allow 0 for blog user, but reject negative values.
309+
if ( $user_id < 0 ) {
310+
return false;
311+
}
312+
313+
// Check if already a recipient.
314+
if ( self::has_recipient( $post_id, $user_id ) ) {
315+
return true;
316+
}
317+
318+
// Add new recipient as separate meta entry.
319+
return (bool) \add_post_meta( $post_id, '_activitypub_user_id', $user_id, false );
320+
}
321+
322+
/**
323+
* Add multiple recipients to an existing post.
324+
*
325+
* @param int $post_id The post ID.
326+
* @param int[] $user_ids The user ID or array of user IDs to add.
327+
*/
328+
public static function add_recipients( $post_id, $user_ids ) {
329+
foreach ( $user_ids as $user_id ) {
330+
self::add_recipient( $post_id, $user_id );
331+
}
332+
}
333+
334+
/**
335+
* Remove a recipient from a post.
336+
*
337+
* @param int $post_id The post ID.
338+
* @param int $user_id The user ID to remove.
339+
*
340+
* @return bool True on success, false on failure.
341+
*/
342+
public static function remove_recipient( $post_id, $user_id ) {
343+
$user_id = (int) $user_id;
344+
345+
// Allow 0 for blog user, but reject negative values.
346+
if ( $user_id < 0 ) {
347+
return false;
348+
}
349+
350+
// Delete the specific meta entry with this value.
351+
return \delete_post_meta( $post_id, '_activitypub_user_id', $user_id );
352+
}
253353
}

includes/handler/class-accept.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ public static function init() {
2828
/**
2929
* Handles "Accept" requests.
3030
*
31-
* @param array $accept The activity-object.
32-
* @param int $user_id The id of the local blog-user.
31+
* @param array $accept The activity-object.
32+
* @param int|int[] $user_ids The id of the local blog-user.
3333
*/
34-
public static function handle_accept( $accept, $user_id ) {
34+
public static function handle_accept( $accept, $user_ids ) {
3535
// Validate that there is a Follow Activity.
3636
$outbox_post = Outbox::get_by_guid( $accept['object']['id'] );
3737

@@ -48,18 +48,19 @@ public static function handle_accept( $accept, $user_id ) {
4848
return;
4949
}
5050

51+
$user_id = is_array( $user_ids ) ? reset( $user_ids ) : $user_ids;
5152
$result = Following::accept( $actor_post, $user_id );
5253
$success = ! \is_wp_error( $result );
5354

5455
/**
5556
* Fires after an ActivityPub Accept activity has been handled.
5657
*
57-
* @param array $accept The ActivityPub activity data.
58-
* @param int $user_id The local user ID.
59-
* @param bool $success True on success, false otherwise.
60-
* @param \WP_Post|\WP_Error $result The remote actor post or error.
58+
* @param array $accept The ActivityPub activity data.
59+
* @param int[] $user_ids The local user IDs.
60+
* @param bool $success True on success, false otherwise.
61+
* @param \WP_Post|\WP_Error $result The remote actor post or error.
6162
*/
62-
\do_action( 'activitypub_handled_accept', $accept, $user_id, $success, $result );
63+
\do_action( 'activitypub_handled_accept', $accept, (array) $user_ids, $success, $result );
6364
}
6465

6566
/**

includes/handler/class-announce.php

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ public static function init() {
3030
* Handles "Announce" requests.
3131
*
3232
* @param array $announcement The activity-object.
33-
* @param int $user_id The id of the local blog-user.
33+
* @param int|int[] $user_ids The id(s) of the local blog-user(s).
3434
* @param \Activitypub\Activity\Activity $activity The activity object.
3535
*/
36-
public static function handle_announce( $announcement, $user_id, $activity = null ) {
36+
public static function handle_announce( $announcement, $user_ids, $activity = null ) {
3737
// Check if Activity is public or not.
3838
if ( ! is_activity_public( $announcement ) ) {
3939
// @todo maybe send email
@@ -45,7 +45,7 @@ public static function handle_announce( $announcement, $user_id, $activity = nul
4545
return;
4646
}
4747

48-
self::maybe_save_announce( $announcement, $user_id );
48+
self::maybe_save_announce( $announcement, $user_ids );
4949

5050
if ( is_string( $announcement['object'] ) ) {
5151
$object = Http::get_remote_object( $announcement['object'] );
@@ -67,29 +67,29 @@ public static function handle_announce( $announcement, $user_id, $activity = nul
6767
* Fires after an Announce has been received.
6868
*
6969
* @param array $object The object.
70-
* @param int $user_id The id of the local blog-user.
70+
* @param int[] $user_ids The ids of the local blog-users.
7171
* @param string $type The type of the activity.
7272
* @param \Activitypub\Activity\Activity|null $activity The activity object.
7373
*/
74-
\do_action( 'activitypub_inbox', $object, $user_id, $type, $activity );
74+
\do_action( 'activitypub_inbox', $object, (array) $user_ids, $type, $activity );
7575

7676
/**
7777
* Fires after an Announce of a specific type has been received.
7878
*
7979
* @param array $object The object.
80-
* @param int $user_id The id of the local blog-user.
80+
* @param int[] $user_ids The ids of the local blog-users.
8181
* @param \Activitypub\Activity\Activity|null $activity The activity object.
8282
*/
83-
\do_action( "activitypub_inbox_{$type}", $object, $user_id, $activity );
83+
\do_action( "activitypub_inbox_{$type}", $object, (array) $user_ids, $activity );
8484
}
8585

8686
/**
8787
* Try to save the Announce.
8888
*
89-
* @param array $activity The activity-object.
90-
* @param int $user_id The id of the local blog-user.
89+
* @param array $activity The activity-object.
90+
* @param int|int[] $user_ids The id of the local blog-user.
9191
*/
92-
public static function maybe_save_announce( $activity, $user_id ) {
92+
public static function maybe_save_announce( $activity, $user_ids ) {
9393
$url = object_to_uri( $activity );
9494

9595
if ( empty( $url ) ) {
@@ -118,10 +118,10 @@ public static function maybe_save_announce( $activity, $user_id ) {
118118
* Fires after an ActivityPub Announce activity has been handled.
119119
*
120120
* @param array $activity The ActivityPub activity data.
121-
* @param int $user_id The local user ID.
121+
* @param int[] $user_ids The local user IDs.
122122
* @param bool $success True on success, false otherwise.
123123
* @param array|string|int|\WP_Error|false $result The WP_Comment object of the created announce/repost comment, or null if creation failed.
124124
*/
125-
\do_action( 'activitypub_handled_announce', $activity, $user_id, $success, $result );
125+
\do_action( 'activitypub_handled_announce', $activity, (array) $user_ids, $success, $result );
126126
}
127127
}

includes/handler/class-collection-sync.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public static function init() {
3939
*
4040
* @see https://codeberg.org/fediverse/fep/src/branch/main/fep/8fcf/fep-8fcf.md
4141
*
42-
* @param array $data The activity data.
43-
* @param int $user_id The local user ID.
42+
* @param array $data The activity data.
43+
* @param int|int[] $user_ids The user ID(s).
4444
*/
45-
public static function handle_collection_synchronization( $data, $user_id ) {
45+
public static function handle_collection_synchronization( $data, $user_ids ) {
4646
if ( empty( $_SERVER['HTTP_COLLECTION_SYNCHRONIZATION'] ) ) {
4747
return;
4848
}
@@ -87,6 +87,8 @@ public static function handle_collection_synchronization( $data, $user_id ) {
8787
return;
8888
}
8989

90+
// Extract the user ID for cache key (collection sync is always for a single user).
91+
$user_id = \is_array( $user_ids ) ? \reset( $user_ids ) : $user_ids;
9092
$cache_key = 'activitypub_collection_sync_received_' . $user_id . '_' . md5( $actor_url );
9193
if ( false === \get_transient( $cache_key ) ) {
9294
$frequency = self::get_frequency();
@@ -101,11 +103,11 @@ public static function handle_collection_synchronization( $data, $user_id ) {
101103
* This allows for async processing of the reconciliation.
102104
*
103105
* @param string $collection_type The collection type (e.g., 'followers', 'following', 'liked').
104-
* @param int $user_id The local user ID.
106+
* @param int[] $user_ids The local user IDs.
105107
* @param string $actor_url The remote actor URL.
106108
* @param array $params The parsed Collection-Synchronization header parameters.
107109
*/
108-
\do_action( 'activitypub_collection_sync', $collection_type, $user_id, $actor_url, $params );
110+
\do_action( 'activitypub_collection_sync', $collection_type, (array) $user_ids, $actor_url, $params );
109111
}
110112

111113
/**

0 commit comments

Comments
 (0)