Skip to content

Commit be6f800

Browse files
authored
Add Delete activity support for permanently deleted federated comments (#2141)
1 parent 4a1d3ea commit be6f800

File tree

3 files changed

+126
-6
lines changed

3 files changed

+126
-6
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: added
3+
4+
Add Delete activity support for permanently deleted federated comments.

includes/scheduler/class-comment.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
namespace Activitypub\Scheduler;
99

10+
use Activitypub\Comment as Comment_Utils;
11+
1012
use function Activitypub\add_to_outbox;
1113
use function Activitypub\should_comment_be_federated;
1214

@@ -25,6 +27,7 @@ public static function init() {
2527
// Comment transitions.
2628
\add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 );
2729
\add_action( 'wp_insert_comment', array( self::class, 'schedule_comment_activity_on_insert' ), 10, 2 );
30+
\add_action( 'delete_comment', array( self::class, 'schedule_comment_delete_activity' ), 10, 2 );
2831
}
2932

3033
/**
@@ -60,6 +63,7 @@ public static function schedule_comment_activity( $new_status, $old_status, $com
6063
\update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', time(), true );
6164
} elseif (
6265
'trash' === $new_status ||
66+
( 'delete' === $new_status && '' === $old_status ) || // Went through schedule_comment_delete_activity().
6367
'spam' === $new_status
6468
) {
6569
$type = 'Delete';
@@ -88,4 +92,17 @@ public static function schedule_comment_activity_on_insert( $comment_id, $commen
8892
self::schedule_comment_activity( 'approved', '', $comment );
8993
}
9094
}
95+
96+
/**
97+
* Schedule Delete activity when a comment is permanently deleted.
98+
*
99+
* @param int $comment_id Comment ID.
100+
* @param \WP_Comment $comment Comment object.
101+
*/
102+
public static function schedule_comment_delete_activity( $comment_id, $comment ) {
103+
// Only send Delete activities for comments that were previously federated.
104+
if ( Comment_Utils::was_sent( $comment ) ) {
105+
self::schedule_comment_activity( 'delete', '', $comment );
106+
}
107+
}
91108
}

tests/includes/scheduler/class-test-comment.php

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
namespace Activitypub\Tests\Scheduler;
99

10+
use Activitypub\Collection\Outbox;
11+
use Activitypub\Comment;
12+
1013
/**
1114
* Test Comment scheduler class.
1215
*
@@ -41,15 +44,15 @@ public function test_schedule_comment_activity_on_approval() {
4144
'comment_approved' => 0,
4245
)
4346
);
44-
$activitpub_id = \Activitypub\Comment::generate_id( $comment_id );
47+
$activitpub_id = Comment::generate_id( $comment_id );
4548

4649
wp_set_comment_status( $comment_id, 'approve' );
4750

4851
$post = $this->get_latest_outbox_item( $activitpub_id );
4952
$id = \get_post_meta( $post->ID, '_activitypub_object_id', true );
5053
$this->assertSame( $activitpub_id, $id );
5154

52-
wp_delete_comment( $comment_id, true );
55+
\wp_delete_comment( $comment_id, true );
5356
}
5457

5558
/**
@@ -63,13 +66,13 @@ public function test_schedule_comment_activity_on_insert() {
6366
'comment_approved' => 1,
6467
)
6568
);
66-
$activitpub_id = \Activitypub\Comment::generate_id( $comment_id );
69+
$activitpub_id = Comment::generate_id( $comment_id );
6770

6871
$post = $this->get_latest_outbox_item( $activitpub_id );
6972
$id = \get_post_meta( $post->ID, '_activitypub_object_id', true );
7073
$this->assertSame( $activitpub_id, $id );
7174

72-
wp_delete_comment( $comment_id, true );
75+
\wp_delete_comment( $comment_id, true );
7376
}
7477

7578
/**
@@ -120,10 +123,106 @@ public function test_no_activity_scheduled( $comment_data ) {
120123
}
121124

122125
$comment_id = self::factory()->comment->create( $comment_data );
123-
$activitpub_id = \Activitypub\Comment::generate_id( $comment_id );
126+
$activitpub_id = Comment::generate_id( $comment_id );
124127

125128
$this->assertNull( $this->get_latest_outbox_item( $activitpub_id ) );
126129

127-
wp_delete_comment( $comment_id, true );
130+
\wp_delete_comment( $comment_id, true );
131+
}
132+
133+
/**
134+
* Test scheduling Delete activity when comment is permanently deleted.
135+
*
136+
* @covers ::schedule_comment_delete_activity
137+
*/
138+
public function test_schedule_comment_delete_activity() {
139+
// Create a comment that gets federated.
140+
$comment_id = self::factory()->comment->create(
141+
array(
142+
'comment_post_ID' => self::$comment_post_ID,
143+
'user_id' => self::$user_id,
144+
'comment_approved' => 1,
145+
'comment_meta' => array(
146+
'activitypub_status' => 'federated',
147+
),
148+
)
149+
);
150+
151+
$activitypub_id = Comment::generate_id( $comment_id );
152+
153+
// Permanently delete the comment - this should trigger a Delete activity.
154+
\wp_delete_comment( $comment_id, true );
155+
156+
// Check if a Delete activity was created.
157+
$outbox_posts = \get_posts(
158+
array(
159+
'post_type' => Outbox::POST_TYPE,
160+
'post_status' => array( 'publish', 'draft', 'pending', 'private' ),
161+
'numberposts' => 1,
162+
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
163+
array(
164+
'key' => '_activitypub_object_id',
165+
'value' => $activitypub_id,
166+
),
167+
array(
168+
'key' => '_activitypub_activity_type',
169+
'value' => 'Delete',
170+
),
171+
),
172+
)
173+
);
174+
175+
$this->assertCount( 1, $outbox_posts, 'Should create exactly one Delete activity for permanently deleted federated comment' );
176+
$outbox_post = $outbox_posts[0];
177+
178+
// Verify the outbox post has correct metadata.
179+
$this->assertEquals( 'Delete', \get_post_meta( $outbox_post->ID, '_activitypub_activity_type', true ) );
180+
$this->assertEquals( $activitypub_id, \get_post_meta( $outbox_post->ID, '_activitypub_object_id', true ) );
181+
$this->assertEquals( self::$user_id, $outbox_post->post_author );
182+
}
183+
184+
/**
185+
* Test that non-federated comments don't create Delete activities.
186+
*
187+
* @covers ::schedule_comment_delete_activity
188+
*/
189+
public function test_no_delete_activity_for_non_federated_comment() {
190+
// Create a comment that was NOT federated.
191+
$comment_id = self::factory()->comment->create(
192+
array(
193+
'comment_post_ID' => self::$comment_post_ID,
194+
'user_id' => self::$user_id,
195+
'comment_approved' => 1,
196+
)
197+
);
198+
199+
$activitypub_id = Comment::generate_id( $comment_id );
200+
201+
// Ensure this comment is NOT marked as sent.
202+
\delete_comment_meta( $comment_id, 'activitypub_status' );
203+
204+
// Permanently delete the comment.
205+
\wp_delete_comment( $comment_id, true );
206+
207+
// Check that no Delete activity was created for this specific comment.
208+
$outbox_posts = \get_posts(
209+
array(
210+
'post_type' => Outbox::POST_TYPE,
211+
'post_status' => array( 'publish', 'draft', 'pending', 'private' ),
212+
'numberposts' => 1,
213+
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
214+
array(
215+
'key' => '_activitypub_object_id',
216+
'value' => $activitypub_id,
217+
),
218+
array(
219+
'key' => '_activitypub_activity_type',
220+
'value' => 'Delete',
221+
),
222+
),
223+
)
224+
);
225+
226+
$this->assertEmpty( $outbox_posts, 'Should not create Delete activity for non-federated comment deletion' );
128227
}
129228
}

0 commit comments

Comments
 (0)