PluginProbe
ActivityPub / 5.3.1
ActivityPub v5.3.1
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/handler/class-delete.php +70 -241 8.2.15.3.1 View file →
@@ -6,15 +6,13 @@
6 6 */
7 7
8 8 namespace Activitypub\Handler;
9 9
10 +use WP_REST_Request;
11 +use Activitypub\Http;
12 +use Activitypub\Collection\Followers;
10 13 use Activitypub\Collection\Interactions;
11 -use Activitypub\Collection\Remote_Actors;
12 -use Activitypub\Collection\Remote_Posts;
13 -use Activitypub\Tombstone;
14 14
15 -use function Activitypub\object_to_uri;
16 -
17 15 /**
18 16 * Handles Delete requests.
19 17 */
20 18 class Delete {
@@ -21,26 +19,35 @@
21 19 /**
22 20 * Initialize the class, registering WordPress hooks.
23 21 */
24 22 public static function init() {
25 - \add_action( 'activitypub_inbox_delete', array( self::class, 'handle_delete' ), 10, 2 );
26 - \add_filter( 'activitypub_skip_inbox_storage', array( self::class, 'skip_inbox_storage' ), 10, 2 );
27 - \add_filter( 'activitypub_defer_signature_verification', array( self::class, 'defer_signature_verification' ), 10, 3 );
28 - \add_action( 'activitypub_delete_remote_actor_interactions', array( self::class, 'delete_interactions' ) );
29 - \add_action( 'activitypub_delete_remote_actor_posts', array( self::class, 'delete_posts' ) );
23 + \add_action(
24 + 'activitypub_inbox_delete',
25 + array( self::class, 'handle_delete' )
26 + );
30 27
31 - \add_filter( 'activitypub_get_outbox_activity', array( self::class, 'outbox_activity' ) );
32 - \add_action( 'post_activitypub_add_to_outbox', array( self::class, 'maybe_bury' ), 10, 2 );
28 + // Defer signature verification for `Delete` requests.
29 + \add_filter(
30 + 'activitypub_defer_signature_verification',
31 + array( self::class, 'defer_signature_verification' ),
32 + 10,
33 + 2
34 + );
35 +
36 + // Side effect.
37 + \add_action(
38 + 'activitypub_delete_actor_interactions',
39 + array( self::class, 'delete_interactions' )
40 + );
33 41 }
34 42
35 43 /**
36 44 * Handles "Delete" requests.
37 45 *
38 - * @param array $activity The delete activity.
39 - * @param int|int[] $user_ids The local user ID(s).
46 + * @param array $activity The delete activity.
40 47 */
41 - public static function handle_delete( $activity, $user_ids ) {
42 - $object_type = $activity['object']['type'] ?? '';
48 + public static function handle_delete( $activity ) {
49 + $object_type = isset( $activity['object']['type'] ) ? $activity['object']['type'] : '';
43 50
44 51 switch ( $object_type ) {
45 52 /*
46 53 * Actor Types.
@@ -51,9 +58,9 @@
51 58 case 'Group':
52 59 case 'Organization':
53 60 case 'Service':
54 61 case 'Application':
55 - self::delete_remote_actor( $activity, $user_ids );
62 + self::maybe_delete_follower( $activity );
56 63 break;
57 64
58 65 /*
59 66 * Object and Link Types.
@@ -66,9 +73,9 @@
66 73 case 'Audio':
67 74 case 'Video':
68 75 case 'Event':
69 76 case 'Document':
70 - self::delete_object( $activity, $user_ids );
77 + self::maybe_delete_interaction( $activity );
71 78 break;
72 79
73 80 /*
74 81 * Tombstone Type.
@@ -75,9 +82,9 @@
75 82 *
76 83 * @see: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone
77 84 */
78 85 case 'Tombstone':
79 - self::delete_object( $activity, $user_ids );
86 + self::maybe_delete_interaction( $activity );
80 87 break;
81 88
82 89 /*
83 90 * Minimal Activity.
@@ -84,13 +91,18 @@
84 91 *
85 92 * @see https://www.w3.org/TR/activitystreams-core/#example-1
86 93 */
87 94 default:
95 + // Ignore non Minimal Activities.
96 + if ( ! is_string( $activity['object'] ) ) {
97 + return;
98 + }
99 +
88 100 // Check if Object is an Actor.
89 - if ( object_to_uri( $activity['object'] ) === $activity['actor'] ) {
90 - self::delete_remote_actor( $activity, $user_ids );
91 - } else { // Assume an object otherwise.
92 - self::delete_object( $activity, $user_ids );
101 + if ( $activity['actor'] === $activity['object'] ) {
102 + self::maybe_delete_follower( $activity );
103 + } else { // Assume an interaction otherwise.
104 + self::maybe_delete_interaction( $activity );
93 105 }
94 106 // Maybe handle Delete Activity for other Object Types.
95 107 break;
96 108 }
@@ -96,228 +108,84 @@
96 108 }
97 109 }
98 110
99 111 /**
100 - * Delete an Object.
101 - *
102 - * @param array $activity The Activity object.
103 - * @param int|int[] $user_ids The user ID(s).
104 - */
105 - public static function delete_object( $activity, $user_ids ) {
106 - $result = self::maybe_delete_interaction( $activity );
107 -
108 - if ( ! $result ) {
109 - $result = self::maybe_delete_post( $activity );
110 - }
111 -
112 - $success = ( $result && ! \is_wp_error( $result ) );
113 -
114 - /**
115 - * Fires after an ActivityPub Delete activity has been handled.
116 - *
117 - * @param array $activity The ActivityPub activity data.
118 - * @param int[] $user_ids The local user IDs.
119 - * @param bool $success True on success, false otherwise.
120 - * @param mixed|null $result The result of the delete operation.
121 - */
122 - \do_action( 'activitypub_handled_delete', $activity, (array) $user_ids, $success, $result );
123 - }
124 -
125 - /**
126 - * Delete an Actor.
127 - *
128 - * @param array $activity The Activity object.
129 - * @param int|int[] $user_ids The user ID(s).
130 - */
131 - public static function delete_remote_actor( $activity, $user_ids ) {
132 - $result = self::maybe_delete_follower( $activity );
133 - $success = ( $result && ! \is_wp_error( $result ) );
134 -
135 - /**
136 - * Fires after an ActivityPub Delete activity has been handled.
137 - *
138 - * @param array $activity The ActivityPub activity data.
139 - * @param int[] $user_ids The local user IDs.
140 - * @param bool $success True on success, false otherwise.
141 - * @param mixed|null $result The result of the delete operation.
142 - */
143 - \do_action( 'activitypub_handled_delete', $activity, (array) $user_ids, $success, $result );
144 -
145 - return $result;
146 - }
147 -
148 - /**
149 112 * Delete a Follower if Actor-URL is a Tombstone.
150 113 *
151 114 * @param array $activity The delete activity.
152 - *
153 - * @return bool True on success, false otherwise.
154 115 */
155 116 public static function maybe_delete_follower( $activity ) {
156 - $follower = Remote_Actors::get_by_uri( $activity['actor'] );
117 + /* @var \Activitypub\Model\Follower $follower Follower object. */
118 + $follower = Followers::get_follower_by_actor( $activity['actor'] );
157 119
158 120 // Verify that Actor is deleted.
159 - if ( ! is_wp_error( $follower ) && Tombstone::exists( $activity['actor'] ) ) {
160 - self::maybe_delete_interactions( $follower->ID );
161 - self::maybe_delete_posts( $follower->ID );
162 - $state = Remote_Actors::delete( $follower->ID );
121 + if ( $follower && Http::is_tombstone( $activity['actor'] ) ) {
122 + $follower->delete();
123 + self::maybe_delete_interactions( $activity );
163 124 }
164 -
165 - return $state ?? false;
166 125 }
167 126
168 127 /**
169 - * Schedule Deletion of Interactions of a Remote Actor.
128 + * Delete Reactions if Actor-URL is a Tombstone.
170 129 *
171 - * @param int $id The remote actor ID.
130 + * @param array $activity The delete activity.
172 131 */
173 - public static function maybe_delete_interactions( $id ) {
174 - \wp_schedule_single_event(
175 - \time(),
176 - 'activitypub_delete_remote_actor_interactions',
177 - array( $id )
178 - );
179 - }
180 -
181 - /**
182 - * Schedule Deletion of Reader Items of a Remote Actor.
183 - *
184 - * @param int $id The remote actor ID.
185 - */
186 - public static function maybe_delete_posts( $id ) {
187 - \wp_schedule_single_event(
188 - \time(),
189 - 'activitypub_delete_remote_actor_posts',
190 - array( $id )
191 - );
192 - }
193 -
194 - /**
195 - * Delete Interactions from a Remote Actor.
196 - *
197 - * @param int $id The ID of the actor whose comments to delete.
198 - *
199 - * @return bool True on success, false otherwise.
200 - */
201 - public static function delete_interactions( $id ) {
202 - $comments = Interactions::get_by_remote_actor_id( $id );
203 -
204 - foreach ( $comments as $comment ) {
205 - \wp_delete_comment( $comment, true );
132 + public static function maybe_delete_interactions( $activity ) {
133 + // Verify that Actor is deleted.
134 + if ( Http::is_tombstone( $activity['actor'] ) ) {
135 + \wp_schedule_single_event(
136 + \time(),
137 + 'activitypub_delete_actor_interactions',
138 + array( $activity['actor'] )
139 + );
206 140 }
207 -
208 - if ( $comments ) {
209 - return true;
210 - } else {
211 - return false;
212 - }
213 141 }
214 142
215 143 /**
216 - * Delete Reader Items from an Actor.
144 + * Delete comments from an Actor.
217 145 *
218 - * @param int $id The ID of the actor whose comments to delete.
219 - *
220 - * @return bool True on success, false otherwise.
146 + * @param string $actor The URL of the actor whose comments to delete.
221 147 */
222 - public static function delete_posts( $id ) {
223 - $posts = Remote_Posts::get_by_remote_actor_id( $id );
148 + public static function delete_interactions( $actor ) {
149 + $comments = Interactions::get_interactions_by_actor( $actor );
224 150
225 - foreach ( $posts as $post ) {
226 - Remote_Posts::delete( $post->ID );
151 + if ( is_array( $comments ) ) {
152 + foreach ( $comments as $comment ) {
153 + wp_delete_comment( $comment->comment_ID, true );
154 + }
227 155 }
228 -
229 - if ( $posts ) {
230 - return true;
231 - } else {
232 - return false;
233 - }
234 156 }
235 157
236 158 /**
237 159 * Delete a Reaction if URL is a Tombstone.
238 160 *
239 - * Note: When comments are deleted, WordPress automatically deletes all associated
240 - * comment meta including _activitypub_remote_actor_id. The remote actor post itself
241 - * is not deleted, as it may be referenced by other comments or may be needed for
242 - * future interactions.
243 - *
244 161 * @param array $activity The delete activity.
245 - *
246 - * @return bool True on success, false otherwise.
247 162 */
248 163 public static function maybe_delete_interaction( $activity ) {
249 - $id = object_to_uri( $activity['object'] );
250 - $comments = Interactions::get_by_id( $id );
164 + if ( is_array( $activity['object'] ) ) {
165 + $id = $activity['object']['id'];
166 + } else {
167 + $id = $activity['object'];
168 + }
251 169
252 - if ( $comments && Tombstone::exists( $id ) ) {
170 + $comments = Interactions::get_interaction_by_id( $id );
171 +
172 + if ( $comments && Http::is_tombstone( $id ) ) {
253 173 foreach ( $comments as $comment ) {
254 - // WordPress will automatically delete all comment meta including _activitypub_remote_actor_id.
255 174 wp_delete_comment( $comment->comment_ID, true );
256 175 }
257 -
258 - return true;
259 176 }
260 -
261 - return false;
262 177 }
263 178
264 179 /**
265 - * Delete a post from the Posts collection.
266 - *
267 - * @param array $activity The delete activity.
268 - *
269 - * @return bool|\WP_Error True on success, false or WP_Error on failure.
270 - */
271 - public static function maybe_delete_post( $activity ) {
272 - $id = object_to_uri( $activity['object'] );
273 -
274 - // Check if the object exists and is a tombstone.
275 - if ( Tombstone::exists( $id ) ) {
276 - return Remote_Posts::delete_by_guid( $id );
277 - }
278 -
279 - return false;
280 - }
281 -
282 - /**
283 - * Skip inbox storage for `Delete` requests.
284 - *
285 - * @param bool $skip Whether to skip inbox storage.
286 - * @param array $data The activity data array.
287 - *
288 - * @return bool Whether to skip inbox storage.
289 - */
290 - public static function skip_inbox_storage( $skip, $data ) {
291 - if ( isset( $data['type'] ) && 'Delete' === $data['type'] ) {
292 - return true;
293 - }
294 -
295 - return $skip;
296 - }
297 -
298 - /**
299 180 * Defer signature verification for `Delete` requests.
300 181 *
301 - * Endpoints that opt in to mandatory signing by calling
302 - * `verify_signature( $request, true )` must not be overridden — the
303 - * Delete carve-out is only for the default inbox path where the
304 - * remote actor's keys may legitimately be gone before the Delete
305 - * arrives.
182 + * @param bool $defer Whether to defer signature verification.
183 + * @param WP_REST_Request $request The request object.
306 184 *
307 - * @since 8.2.0 The `$force_signature` parameter is now respected.
308 - *
309 - * @param bool $defer Whether to defer signature verification.
310 - * @param \WP_REST_Request $request The request object.
311 - * @param bool $force_signature Whether the caller has forced signature verification.
312 - *
313 185 * @return bool Whether to defer signature verification.
314 186 */
315 - public static function defer_signature_verification( $defer, $request, $force_signature = false ) {
316 - if ( $force_signature ) {
317 - return $defer;
318 - }
319 -
187 + public static function defer_signature_verification( $defer, $request ) {
320 188 $json = $request->get_json_params();
321 189
322 190 if ( isset( $json['type'] ) && 'Delete' === $json['type'] ) {
323 191 return true;
@@ -322,46 +190,7 @@
322 190 if ( isset( $json['type'] ) && 'Delete' === $json['type'] ) {
323 191 return true;
324 192 }
325 193
326 - return $defer;
327 - }
328 -
329 - /**
330 - * Set the object to the object ID.
331 - *
332 - * @param \Activitypub\Activity\Activity $activity The Activity object.
333 - *
334 - * @return \Activitypub\Activity\Activity The filtered Activity object.
335 - */
336 - public static function outbox_activity( $activity ) {
337 - if ( 'Delete' === $activity->get_type() ) {
338 - $activity->set_object( object_to_uri( $activity->get_object() ) );
339 - }
340 -
341 - return $activity;
342 - }
343 -
344 - /**
345 - * Add a URL to the tombstone registry when a Delete activity is sent.
346 - *
347 - * @param int $outbox_id The ID of the outbox activity.
348 - * @param \Activitypub\Activity\Activity $activity The Activity object.
349 - */
350 - public static function maybe_bury( $outbox_id, $activity ) {
351 - if ( 'Delete' !== $activity->get_type() ) {
352 - return;
353 - }
354 -
355 - $object = $activity->get_object();
356 -
357 - if ( ! $object ) {
358 - return;
359 - }
360 -
361 - Tombstone::bury( object_to_uri( $object ) );
362 -
363 - if ( \is_object( $object ) ) {
364 - Tombstone::bury( $object->get_id(), $object->get_url() );
365 - }
194 + return false;
366 195 }
367 196 }