PluginProbe
ActivityPub / 7.6.0
ActivityPub v7.6.0
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
activitypub / includes / handler / class-delete.php

class-delete.php in ActivityPub 7.6.0, at includes/handler/class-delete.php

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