Skip to content

Commit 532b4b0

Browse files
authored
Fix Pixelfed interactions (Likes) (#2448)
1 parent 265bcde commit 532b4b0

File tree

6 files changed

+123
-7
lines changed

6 files changed

+123
-7
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: fixed
3+
4+
Fixed compatibility with Pixelfed and similar platforms by treating activities without recipients as public, ensuring boosts and reposts work correctly.

includes/functions.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,12 @@ function get_activity_visibility( $activity ) {
588588
return ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC;
589589
}
590590

591+
// Activities with no recipients are treated as public.
592+
$recipients = extract_recipients_from_activity( $activity );
593+
if ( empty( $recipients ) ) {
594+
return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
595+
}
596+
591597
return ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE;
592598
}
593599

@@ -607,6 +613,10 @@ function is_activity_public( $data ) {
607613

608614
$recipients = extract_recipients_from_activity( $data );
609615

616+
if ( empty( $recipients ) ) {
617+
return true;
618+
}
619+
610620
return ! empty( array_intersect( $recipients, ACTIVITYPUB_PUBLIC_AUDIENCE_IDENTIFIERS ) );
611621
}
612622

tests/phpunit/tests/includes/class-test-functions.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,7 @@ public function public_activity_provider() {
10031003
'monkey' => 'https://www.w3.org/ns/activitystreams#Public',
10041004
),
10051005
),
1006-
false,
1006+
true,
10071007
),
10081008
array(
10091009
array(
@@ -1334,13 +1334,13 @@ public function visibility_data_provider() {
13341334
'expected' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
13351335
'description' => 'Public visibility via as:Public identifier',
13361336
),
1337-
// Empty activity.
1337+
// Empty activity - no recipients means public.
13381338
array(
13391339
'activity' => array(
13401340
'type' => 'Create',
13411341
),
1342-
'expected' => ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE,
1343-
'description' => 'Empty activity defaults to private',
1342+
'expected' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
1343+
'description' => 'Empty activity (no recipients) is treated as public',
13441344
),
13451345
);
13461346
}

tests/phpunit/tests/includes/collection/class-test-inbox.php

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ public function test_add_activity_with_post_meta() {
7575
$remote_actor_meta = \get_post_meta( $inbox_id, '_activitypub_activity_remote_actor', true );
7676
$this->assertEquals( 'https://remote.example.com/users/testuser', $remote_actor_meta );
7777

78-
// Test activitypub_content_visibility meta.
78+
// Activities with no recipients are treated as public.
7979
$visibility_meta = \get_post_meta( $inbox_id, 'activitypub_content_visibility', true );
80-
$this->assertEquals( ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, $visibility_meta );
80+
$this->assertEquals( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, $visibility_meta );
8181
}
8282

8383
/**
@@ -723,4 +723,60 @@ public function test_deduplicate_non_existent() {
723723
$result = Inbox::deduplicate( 'https://remote.example.com/activities/non-existent' );
724724
$this->assertFalse( $result );
725725
}
726+
727+
/**
728+
* Test adding Like activity with trailing slash in object URL.
729+
*
730+
* This test verifies that Like activities from Pixelfed and other platforms
731+
* that include trailing slashes in object URLs are stored correctly in the inbox.
732+
*
733+
* @covers ::add
734+
*/
735+
public function test_add_like_activity_with_trailing_slash() {
736+
// Create a post to be liked.
737+
$post_id = self::factory()->post->create(
738+
array(
739+
'post_title' => 'Test Post for Like',
740+
'post_content' => 'Test content',
741+
'post_status' => 'publish',
742+
)
743+
);
744+
$post_permalink = \get_permalink( $post_id );
745+
746+
// Create a Like activity with trailing slash in object URL (as Pixelfed sends).
747+
$activity = new Activity();
748+
$activity->set_id( 'https://pixelfed.social/users/pfefferle#likes/30434186' );
749+
$activity->set_type( 'Like' );
750+
$activity->set_actor( 'https://pixelfed.social/users/pfefferle' );
751+
$activity->set_object( $post_permalink . '/' ); // Add trailing slash.
752+
753+
$user_id = 1;
754+
755+
// Add activity to inbox.
756+
$inbox_id = Inbox::add( $activity, $user_id );
757+
758+
$this->assertIsInt( $inbox_id );
759+
$this->assertGreaterThan( 0, $inbox_id );
760+
761+
// Verify the post was created.
762+
$post = \get_post( $inbox_id );
763+
$this->assertInstanceOf( 'WP_Post', $post );
764+
$this->assertEquals( Inbox::POST_TYPE, $post->post_type );
765+
766+
// Test _activitypub_object_id meta - should preserve the trailing slash as-is.
767+
$object_id_meta = \get_post_meta( $inbox_id, '_activitypub_object_id', true );
768+
$this->assertEquals( $post_permalink . '/', $object_id_meta );
769+
770+
// Test _activitypub_activity_type meta.
771+
$activity_type_meta = \get_post_meta( $inbox_id, '_activitypub_activity_type', true );
772+
$this->assertEquals( 'Like', $activity_type_meta );
773+
774+
// Test _activitypub_user_id meta.
775+
$user_id_meta = \get_post_meta( $inbox_id, '_activitypub_user_id', true );
776+
$this->assertEquals( $user_id, $user_id_meta );
777+
778+
// Test _activitypub_activity_remote_actor meta.
779+
$remote_actor_meta = \get_post_meta( $inbox_id, '_activitypub_activity_remote_actor', true );
780+
$this->assertEquals( 'https://pixelfed.social/users/pfefferle', $remote_actor_meta );
781+
}
726782
}

tests/phpunit/tests/includes/handler/class-test-like.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,50 @@ public function handle_like_provider() {
185185
);
186186
}
187187

188+
/**
189+
* Test Like activity with trailing slash in object URL.
190+
*
191+
* This test verifies that Like activities from Pixelfed and other platforms
192+
* that include trailing slashes in object URLs are processed correctly.
193+
*
194+
* @covers ::handle_like
195+
* @covers \Activitypub\Collection\Interactions::add_reaction
196+
*/
197+
public function test_handle_like_with_trailing_slash() {
198+
// Create activity with trailing slash in object URL (as Pixelfed sends).
199+
$activity = array(
200+
'@context' => 'https://www.w3.org/ns/activitystreams',
201+
'id' => 'https://pixelfed.social/users/pfefferle#likes/30434186',
202+
'type' => 'Like',
203+
'actor' => $this->user_url,
204+
'object' => $this->post_permalink . '/', // Add trailing slash.
205+
);
206+
207+
// Get comment count before.
208+
$comments_before = \get_comments(
209+
array(
210+
'type' => 'like',
211+
'post_id' => $this->post_id,
212+
)
213+
);
214+
$count_before = count( $comments_before );
215+
216+
// Process the like.
217+
Like::handle_like( $activity, $this->user_id );
218+
219+
// Check that comment was created despite trailing slash.
220+
$comments_after = \get_comments(
221+
array(
222+
'type' => 'like',
223+
'post_id' => $this->post_id,
224+
)
225+
);
226+
$count_after = count( $comments_after );
227+
228+
$this->assertEquals( $count_before + 1, $count_after, 'Like with trailing slash should create comment' );
229+
$this->assertInstanceOf( 'WP_Comment', $comments_after[0], 'Should create WP_Comment object' );
230+
}
231+
188232
/**
189233
* Test duplicate like handling.
190234
*

tests/phpunit/tests/includes/rest/class-test-inbox-controller.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,9 @@ public function test_get_local_recipients_no_recipients() {
475475
$method->setAccessible( true );
476476

477477
$result = $method->invoke( $this->inbox_controller, $activity );
478-
$this->assertEmpty( $result, 'Should return empty array when no recipients' );
478+
// Activities with no recipients are treated as public and delivered to all local actors.
479+
$this->assertNotEmpty( $result, 'Should return all local actors when no recipients (treated as public)' );
480+
$this->assertEquals( Actors::get_all_ids(), $result, 'Should match all local actor IDs' );
479481
}
480482

481483
/**

0 commit comments

Comments
 (0)