PluginProbe
ActivityPub / trunk
ActivityPub vtrunk
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 / outbox / class-undo.php

class-undo.php in ActivityPub trunk, at includes/handler/outbox/class-undo.php

117 lines 3.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Outbox Undo handler file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Handler\Outbox;
9
10 use Activitypub\Collection\Actors;
11 use Activitypub\Collection\Outbox as Outbox_Collection;
12 use Activitypub\Moderation;
13
14 use function Activitypub\object_to_uri;
15 use function Activitypub\unfollow;
16
17 /**
18 * Handle outgoing Undo activities.
19 */
20 class Undo {
21 /**
22 * Initialize the class, registering WordPress hooks.
23 */
24 public static function init() {
25 \add_filter( 'activitypub_outbox_undo', array( self::class, 'handle_undo' ), 10, 2 );
26 }
27
28 /**
29 * Handle outgoing "Undo" activities from local actors.
30 *
31 * Resolves the referenced activity from the outbox and delegates
32 * to the appropriate collection method to reverse its side effects
33 * and create the Undo activity.
34 *
35 * @param array $data The activity data array.
36 * @param int $user_id The user ID.
37 *
38 * @return int|\WP_Error The undo outbox item ID, or WP_Error on failure.
39 */
40 public static function handle_undo( $data, $user_id = null ) {
41 $object = $data['object'] ?? '';
42 $id = object_to_uri( $object );
43
44 if ( empty( $id ) ) {
45 /*
46 * The embedded object has no `id` — common for clients that
47 * inline the activity to undo. Mastodon and other major
48 * implementations match an id-less Undo→Follow on the inner
49 * Follow's target. Mirror that fallback here so spec-valid
50 * bodies don't bypass the local unfollow logic.
51 */
52 if ( \is_array( $object ) && 'Follow' === ( $object['type'] ?? '' ) ) {
53 $embedded_actor = object_to_uri( $object['actor'] ?? '' );
54 $user_actor = Actors::get_by_id( $user_id );
55
56 if ( \is_wp_error( $user_actor ) || ! $embedded_actor || $embedded_actor !== $user_actor->get_id() ) {
57 return new \WP_Error(
58 'activitypub_forbidden',
59 \__( 'You can only undo your own activities.', 'activitypub' ),
60 array( 'status' => 403 )
61 );
62 }
63
64 $target = object_to_uri( $object['object'] ?? '' );
65
66 if ( $target ) {
67 return unfollow( $target, $user_id );
68 }
69 }
70
71 return $data;
72 }
73
74 $outbox_item = Outbox_Collection::get_by_guid( $id );
75
76 if ( \is_wp_error( $outbox_item ) ) {
77 return $data;
78 }
79
80 // Verify the user owns this outbox item (blog actor user_id === 0 can undo any).
81 if ( $user_id > 0 && (int) $outbox_item->post_author !== $user_id ) {
82 return new \WP_Error(
83 'activitypub_forbidden',
84 \__( 'You can only undo your own activities.', 'activitypub' ),
85 array( 'status' => 403 )
86 );
87 }
88
89 $activity_type = \get_post_meta( $outbox_item->ID, '_activitypub_activity_type', true );
90
91 switch ( $activity_type ) {
92 case 'Follow':
93 $stored = \json_decode( $outbox_item->post_content, true );
94 $target = object_to_uri( $stored['object'] ?? '' );
95
96 if ( $target ) {
97 return unfollow( $target, $user_id );
98 }
99
100 return $data;
101
102 case 'Block':
103 $stored = \json_decode( $outbox_item->post_content, true );
104 $actor_uri = \is_array( $stored ) ? object_to_uri( $stored['object'] ?? '' ) : '';
105
106 if ( $actor_uri ) {
107 Moderation::remove_user_block( $user_id, Moderation::TYPE_ACTOR, $actor_uri );
108 }
109
110 return Outbox_Collection::undo( $outbox_item );
111
112 default:
113 return Outbox_Collection::undo( $outbox_item );
114 }
115 }
116 }
117