PluginProbe
ActivityPub / 8.0.2
ActivityPub v8.0.2
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-follow.php +111 -69 2.0.18.0.2 View file →
@@ -1,109 +1,151 @@
1 1 <?php
2 +/**
3 + * Follow handler file.
4 + *
5 + * @package Activitypub
6 + */
7 +
2 8 namespace Activitypub\Handler;
3 9
4 -use Activitypub\Http;
5 10 use Activitypub\Activity\Activity;
6 -use Activitypub\Collection\Users;
11 +use Activitypub\Collection\Actors;
7 12 use Activitypub\Collection\Followers;
13 +use Activitypub\Collection\Remote_Actors;
8 14
15 +use function Activitypub\add_to_outbox;
16 +
9 17 /**
10 - * Handle Follow requests
18 + * Handle Follow requests.
11 19 */
12 20 class Follow {
13 21 /**
14 - * Initialize the class, registering WordPress hooks
22 + * Initialize the class, registering WordPress hooks.
15 23 */
16 24 public static function init() {
17 - \add_action(
18 - 'activitypub_inbox_follow',
19 - array( self::class, 'handle_follow' )
20 - );
21 -
22 - \add_action(
23 - 'activitypub_followers_post_follow',
24 - array( self::class, 'send_follow_response' ),
25 - 10,
26 - 4
27 - );
25 + \add_action( 'activitypub_inbox_follow', array( self::class, 'handle_follow' ), 10, 2 );
26 + \add_action( 'activitypub_handled_follow', array( self::class, 'queue_accept' ), 10, 4 );
28 27 }
29 28
30 29 /**
31 - * Handle "Follow" requests
30 + * Handle "Follow" requests.
32 31 *
33 - * @param array $activity The activity object
34 - * @param int $user_id The user ID
32 + * @param array $activity The activity object.
33 + * @param int|int[] $user_ids The user ID(s).
35 34 */
36 - public static function handle_follow( $activity ) {
37 - $user = Users::get_by_resource( $activity['object'] );
35 + public static function handle_follow( $activity, $user_ids ) {
36 + // Extract the user ID (follow requests are always for a single user).
37 + $user_id = \is_array( $user_ids ) ? \reset( $user_ids ) : $user_ids;
38 38
39 - if ( ! $user || is_wp_error( $user ) ) {
40 - // If we can not find a user,
41 - // we can not initiate a follow process
39 + if ( Actors::APPLICATION_USER_ID === $user_id ) {
40 + self::queue_reject( $activity, $user_id );
42 41 return;
43 42 }
44 43
45 - $user_id = $user->get__id();
44 + // Check if the actor already follows the user.
45 + $already_following = false;
46 + $remote_actor = Remote_Actors::get_by_uri( $activity['actor'] );
47 + if ( ! \is_wp_error( $remote_actor ) ) {
48 + $already_following = Followers::follows( $remote_actor->ID, $user_id );
49 + }
46 50
47 - // save follower
48 - $follower = Followers::add_follower(
49 - $user_id,
50 - $activity['actor']
51 - );
51 + // Save follower if not already following.
52 + if ( $already_following ) {
53 + $success = false;
54 + } else {
55 + $remote_actor = Followers::add( $user_id, $activity['actor'] );
56 + $success = ! \is_wp_error( $remote_actor );
52 57
53 - do_action(
54 - 'activitypub_followers_post_follow',
55 - $activity['actor'],
56 - $activity,
57 - $user_id,
58 - $follower
59 - );
58 + if ( $success ) {
59 + $remote_actor = \get_post( $remote_actor );
60 + }
61 + }
62 +
63 + /**
64 + * Fires after a new follower has been added.
65 + *
66 + * @deprecated 7.5.0 Use "activitypub_handled_follow" instead.
67 + *
68 + * @param string $actor The URL of the actor (follower) who initiated the follow.
69 + * @param array $activity The complete activity data of the follow request.
70 + * @param int $user_id The ID of the WordPress user being followed.
71 + * @param \WP_Post|\WP_Error $remote_actor The Actor object containing the new follower's data.
72 + */
73 + \do_action_deprecated( 'activitypub_followers_post_follow', array( $activity['actor'], $activity, $user_id, $remote_actor ), '7.5.0', 'activitypub_handled_follow' );
74 +
75 + /**
76 + * Fires after a Follow activity has been handled.
77 + *
78 + * @param array $activity The ActivityPub activity data.
79 + * @param int[] $user_ids The local user IDs.
80 + * @param bool $success True on success, false otherwise.
81 + * @param \WP_Post|\WP_Error $remote_actor The remote actor/follower, or WP_Error if failed.
82 + */
83 + \do_action( 'activitypub_handled_follow', $activity, (array) $user_ids, $success, $remote_actor );
60 84 }
61 85
62 86 /**
63 - * Send Accept response
87 + * Send Accept response.
64 88 *
65 - * @param string $actor The Actor URL
66 - * @param array $object The Activity object
67 - * @param int $user_id The ID of the WordPress User
68 - * @param Activitypub\Model\Follower $follower The Follower object
69 - *
70 - * @return void
89 + * @param array $activity_object The ActivityPub activity data.
90 + * @param int|int[] $user_ids The local user IDs.
91 + * @param bool $success True on success, false otherwise.
92 + * @param \WP_Post|\WP_Error $remote_actor The remote actor/follower, or WP_Error if failed.
71 93 */
72 - public static function send_follow_response( $actor, $object, $user_id, $follower ) {
73 - if ( \is_wp_error( $follower ) ) {
74 - // it is not even possible to send a "Reject" because
75 - // we can not get the Remote-Inbox
94 + public static function queue_accept( $activity_object, $user_ids, $success, $remote_actor ) {
95 + if ( \is_wp_error( $remote_actor ) ) {
96 + // Impossible to send a "Reject" because we can not get the Remote-Inbox.
76 97 return;
77 98 }
78 99
79 - // only send minimal data
80 - $object = array_intersect_key(
81 - $object,
82 - array_flip(
83 - array(
84 - 'id',
85 - 'type',
86 - 'actor',
87 - 'object',
88 - )
100 + // Extract the user ID from the array (follow requests are always for a single user).
101 + $user_id = \is_array( $user_ids ) ? \reset( $user_ids ) : $user_ids;
102 +
103 + $actor = $activity_object['actor'];
104 +
105 + // Only send minimal data.
106 + $activity_object = array_intersect_key(
107 + $activity_object,
108 + array(
109 + 'id' => 1,
110 + 'type' => 1,
111 + 'actor' => 1,
112 + 'object' => 1,
89 113 )
90 114 );
91 115
92 - $user = Users::get_by_id( $user_id );
116 + $activity = new Activity();
117 + $activity->set_type( 'Accept' );
118 + $activity->set_actor( Actors::get_by_id( $user_id )->get_id() );
119 + $activity->set_object( $activity_object );
120 + $activity->set_to( array( $actor ) );
93 121
94 - // get inbox
95 - $inbox = $follower->get_shared_inbox();
122 + add_to_outbox( $activity, null, $user_id, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE );
123 + }
96 124
97 - // send "Accept" activity
125 + /**
126 + * Send Reject response.
127 + *
128 + * @param array $activity The Activity array.
129 + * @param int $user_id The ID of the WordPress User.
130 + */
131 + public static function queue_reject( $activity, $user_id ) {
132 + // Only send minimal data.
133 + $origin_activity = array_intersect_key(
134 + $activity,
135 + array(
136 + 'id' => 1,
137 + 'type' => 1,
138 + 'actor' => 1,
139 + 'object' => 1,
140 + )
141 + );
142 +
98 143 $activity = new Activity();
99 - $activity->set_type( 'Accept' );
100 - $activity->set_object( $object );
101 - $activity->set_actor( $user->get_id() );
102 - $activity->set_to( $actor );
103 - $activity->set_id( $user->get_id() . '#follow-' . \preg_replace( '~^https?://~', '', $actor ) . '-' . \time() );
144 + $activity->set_type( 'Reject' );
145 + $activity->set_actor( Actors::get_by_id( $user_id )->get_id() );
146 + $activity->set_object( $origin_activity );
147 + $activity->set_to( array( $origin_activity['actor'] ) );
104 148
105 - $activity = $activity->to_json();
106 -
107 - Http::post( $inbox, $activity, $user_id );
149 + add_to_outbox( $activity, null, $user_id, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE );
108 150 }
109 151 }