PluginProbe
ActivityPub / 3.2.4
ActivityPub v3.2.4
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 +85 -274 9.2.03.2.4 View file →
@@ -1,66 +1,62 @@
1 1 <?php
2 -/**
3 - * Delete handler file.
4 - *
5 - * @package Activitypub
6 - */
7 -
8 2 namespace Activitypub\Handler;
9 3
4 +use WP_Error;
5 +use WP_REST_Request;
6 +use Activitypub\Http;
7 +use Activitypub\Collection\Followers;
10 8 use Activitypub\Collection\Interactions;
11 -use Activitypub\Collection\Remote_Actors;
12 -use Activitypub\Collection\Remote_Posts;
13 -use Activitypub\Tombstone;
14 9
15 -use function Activitypub\object_to_uri;
16 -
17 10 /**
18 11 * Handles Delete requests.
19 12 */
20 13 class Delete {
21 14 /**
22 - * Initialize the class, registering WordPress hooks.
15 + * Initialize the class, registering WordPress hooks
23 16 */
24 17 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' ) );
18 + \add_action(
19 + 'activitypub_inbox_delete',
20 + array( self::class, 'handle_delete' )
21 + );
30 22
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 );
23 + // defer signature verification for `Delete` requests.
24 + \add_filter(
25 + 'activitypub_defer_signature_verification',
26 + array( self::class, 'defer_signature_verification' ),
27 + 10,
28 + 2
29 + );
30 +
31 + // side effect
32 + \add_action(
33 + 'activitypub_delete_actor_interactions',
34 + array( self::class, 'delete_interactions' )
35 + );
33 36 }
34 37
35 38 /**
36 39 * Handles "Delete" requests.
37 40 *
38 - * @param array $activity The delete activity.
39 - * @param int|int[] $user_ids The local user ID(s).
41 + * @param array $activity The delete activity.
42 + * @param int $user_id The ID of the user performing the delete activity.
40 43 */
41 - public static function handle_delete( $activity, $user_ids ) {
42 - $object_type = $activity['object']['type'] ?? '';
44 + public static function handle_delete( $activity ) {
45 + $object_type = isset( $activity['object']['type'] ) ? $activity['object']['type'] : '';
43 46
44 47 switch ( $object_type ) {
45 - /*
46 - * Actor Types.
47 - *
48 - * @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
49 - */
48 + // Actor Types
49 + // @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
50 50 case 'Person':
51 51 case 'Group':
52 52 case 'Organization':
53 53 case 'Service':
54 54 case 'Application':
55 - self::delete_remote_actor( $activity, $user_ids );
55 + self::maybe_delete_follower( $activity );
56 56 break;
57 -
58 - /*
59 - * Object and Link Types.
60 - *
61 - * @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
62 - */
57 + // Object and Link Types
58 + // @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
63 59 case 'Note':
64 60 case 'Article':
65 61 case 'Image':
66 62 case 'Audio':
@@ -66,258 +62,112 @@
66 62 case 'Audio':
67 63 case 'Video':
68 64 case 'Event':
69 65 case 'Document':
70 - self::delete_object( $activity, $user_ids );
66 + self::maybe_delete_interaction( $activity );
71 67 break;
72 -
73 - /*
74 - * Tombstone Type.
75 - *
76 - * @see: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone
77 - */
68 + // Tombstone Type
69 + // @see: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone
78 70 case 'Tombstone':
79 - self::delete_object( $activity, $user_ids );
71 + self::maybe_delete_interaction( $activity );
80 72 break;
73 + // Minimal Activity
74 + // @see https://www.w3.org/TR/activitystreams-core/#example-1
75 + default:
76 + // ignore non Minimal Activities.
77 + if ( ! is_string( $activity['object'] ) ) {
78 + return;
79 + }
81 80
82 - /*
83 - * Minimal Activity.
84 - *
85 - * @see https://www.w3.org/TR/activitystreams-core/#example-1
86 - */
87 - default:
88 - // 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 );
81 + // check if Object is an Actor.
82 + if ( $activity['actor'] === $activity['object'] ) {
83 + self::maybe_delete_follower( $activity );
84 + } else { // assume a interaction otherwise.
85 + self::maybe_delete_interaction( $activity );
93 86 }
94 - // Maybe handle Delete Activity for other Object Types.
87 + // maybe handle Delete Activity for other Object Types.
95 88 break;
96 89 }
97 90 }
98 91
99 92 /**
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 93 * Delete a Follower if Actor-URL is a Tombstone.
150 94 *
151 95 * @param array $activity The delete activity.
152 - *
153 - * @return bool True on success, false otherwise.
154 96 */
155 97 public static function maybe_delete_follower( $activity ) {
156 - $follower = Remote_Actors::get_by_uri( $activity['actor'] );
98 + $follower = Followers::get_follower_by_actor( $activity['actor'] );
157 99
158 - // 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 );
100 + // verify if Actor is deleted.
101 + if ( $follower && Http::is_tombstone( $activity['actor'] ) ) {
102 + $follower->delete();
103 + self::maybe_delete_interactions( $activity );
163 104 }
164 -
165 - return $state ?? false;
166 105 }
167 106
168 107 /**
169 - * Schedule Deletion of Interactions of a Remote Actor.
108 + * Delete Reactions if Actor-URL is a Tombstone.
170 109 *
171 - * @param int $id The remote actor ID.
110 + * @param array $activity The delete activity.
172 111 */
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 );
112 + public static function maybe_delete_interactions( $activity ) {
113 + // verify if Actor is deleted.
114 + if ( Http::is_tombstone( $activity['actor'] ) ) {
115 + \wp_schedule_single_event(
116 + \time(),
117 + 'activitypub_delete_actor_interactions',
118 + array( $activity['actor'] )
119 + );
206 120 }
207 -
208 - if ( $comments ) {
209 - return true;
210 - } else {
211 - return false;
212 - }
213 121 }
214 122
215 123 /**
216 - * Delete Reader Items from an Actor.
124 + * Delete comments from an Actor.
217 125 *
218 - * @param int $id The ID of the actor whose comments to delete.
219 - *
220 - * @return bool True on success, false otherwise.
126 + * @param array $comments The comments to delete.
221 127 */
222 - public static function delete_posts( $id ) {
223 - $posts = Remote_Posts::get_by_remote_actor_id( $id );
128 + public static function delete_interactions( $actor ) {
129 + $comments = Interactions::get_interactions_by_actor( $actor );
224 130
225 - foreach ( $posts as $post ) {
226 - Remote_Posts::delete( $post->ID );
131 + if ( is_array( $comments ) ) {
132 + foreach ( $comments as $comment ) {
133 + wp_delete_comment( $comment->comment_ID );
134 + }
227 135 }
228 -
229 - if ( $posts ) {
230 - return true;
231 - } else {
232 - return false;
233 - }
234 136 }
235 137
236 138 /**
237 139 * Delete a Reaction if URL is a Tombstone.
238 140 *
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 141 * @param array $activity The delete activity.
245 142 *
246 - * @return bool True on success, false otherwise.
143 + * @return void
247 144 */
248 145 public static function maybe_delete_interaction( $activity ) {
249 - $id = object_to_uri( $activity['object'] );
250 - $comments = Interactions::get_by_id( $id );
146 + if ( is_array( $activity['object'] ) ) {
147 + $id = $activity['object']['id'];
148 + } else {
149 + $id = $activity['object'];
150 + }
251 151
252 - if ( $comments && Tombstone::exists( $id ) ) {
152 + $comments = Interactions::get_interaction_by_id( $id );
153 +
154 + if ( $comments && Http::is_tombstone( $id ) ) {
253 155 foreach ( $comments as $comment ) {
254 - // WordPress will automatically delete all comment meta including _activitypub_remote_actor_id.
255 - \wp_delete_comment( $comment->comment_ID, true );
156 + wp_delete_comment( $comment->comment_ID, true );
256 157 }
257 -
258 - return true;
259 158 }
260 -
261 - return false;
262 159 }
263 160
264 161 /**
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 162 * Defer signature verification for `Delete` requests.
300 163 *
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.
164 + * @param bool $defer Whether to defer signature verification.
165 + * @param WP_REST_Request $request The request object.
306 166 *
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 167 * @return bool Whether to defer signature verification.
314 168 */
315 - public static function defer_signature_verification( $defer, $request, $force_signature = false ) {
316 - if ( $force_signature ) {
317 - return $defer;
318 - }
319 -
169 + public static function defer_signature_verification( $defer, $request ) {
320 170 $json = $request->get_json_params();
321 171
322 172 if ( isset( $json['type'] ) && 'Delete' === $json['type'] ) {
323 173 return true;
@@ -322,46 +172,7 @@
322 172 if ( isset( $json['type'] ) && 'Delete' === $json['type'] ) {
323 173 return true;
324 174 }
325 175
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 - }
176 + return false;
366 177 }
367 178 }