PluginProbe
ActivityPub / 8.2.0
ActivityPub v8.2.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 / functions-federation.php

functions-federation.php in ActivityPub 8.2.0, at includes/functions-federation.php

259 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Federation functions.
4 *
5 * Functions for managing federation state, outbox, and follow/unfollow operations.
6 *
7 * @package Activitypub
8 */
9
10 namespace Activitypub;
11
12 use Activitypub\Activity\Activity;
13 use Activitypub\Collection\Actors;
14 use Activitypub\Collection\Following;
15 use Activitypub\Collection\Outbox;
16 use Activitypub\Collection\Remote_Actors;
17 use Activitypub\Transformer\Factory as Transformer_Factory;
18
19 /**
20 * Set the federation state of a WordPress object.
21 *
22 * @param \WP_Comment|\WP_Post $wp_object The WordPress object.
23 * @param string $state The state of the object.
24 */
25 function set_wp_object_state( $wp_object, $state ) {
26 if ( $wp_object instanceof \WP_Post ) {
27 $meta_type = 'post';
28 $object_id = $wp_object->ID;
29 } elseif ( $wp_object instanceof \WP_Comment ) {
30 $meta_type = 'comment';
31 $object_id = $wp_object->comment_ID;
32 } else {
33 /**
34 * Allow plugins to mark WordPress objects as federated.
35 *
36 * @param \WP_Comment|\WP_Post $wp_object The WordPress object.
37 */
38 \do_action( 'activitypub_mark_wp_object_as_federated', $wp_object );
39 return;
40 }
41
42 \update_metadata( $meta_type, $object_id, 'activitypub_status', $state );
43
44 if ( ACTIVITYPUB_OBJECT_STATE_DELETED === $state ) {
45 \update_metadata( $meta_type, $object_id, 'activitypub_deleted_at', \time() );
46 } else {
47 \delete_metadata( $meta_type, $object_id, 'activitypub_deleted_at' );
48 }
49 }
50
51 /**
52 * Get the federation state of a WordPress object.
53 *
54 * @param \WP_Comment|\WP_Post $wp_object The WordPress object.
55 *
56 * @return string|false The state of the object or false if not found.
57 */
58 function get_wp_object_state( $wp_object ) {
59 if ( $wp_object instanceof \WP_Post ) {
60 $meta_type = 'post';
61 $object_id = $wp_object->ID;
62 } elseif ( $wp_object instanceof \WP_Comment ) {
63 $meta_type = 'comment';
64 $object_id = $wp_object->comment_ID;
65 } else {
66 /**
67 * Allow plugins to get the federation state of a WordPress object.
68 *
69 * @param false $state The state of the object.
70 * @param \WP_Comment|\WP_Post $wp_object The WordPress object.
71 */
72 return \apply_filters( 'activitypub_get_wp_object_state', false, $wp_object );
73 }
74
75 return \get_metadata( $meta_type, $object_id, 'activitypub_status', true );
76 }
77
78 /**
79 * Check if an ID is from the same domain as the site.
80 *
81 * @param string $id The ID URI to check.
82 *
83 * @return boolean True if the ID is a self-ping, false otherwise.
84 */
85 function is_self_ping( $id ) {
86 $query_string = \wp_parse_url( $id, PHP_URL_QUERY );
87
88 if ( ! $query_string ) {
89 return false;
90 }
91
92 $query = array();
93 \parse_str( $query_string, $query );
94
95 if (
96 is_same_domain( $id ) &&
97 in_array( 'c', array_keys( $query ), true )
98 ) {
99 return true;
100 }
101
102 return false;
103 }
104
105 /**
106 * Add an object to the outbox.
107 *
108 * @param mixed $data The object to add to the outbox.
109 * @param string|null $activity_type Optional. The type of the Activity or null if `$data` is an Activity. Default null.
110 * @param integer $user_id Optional. The User-ID. Default 0.
111 * @param string $content_visibility Optional. The visibility of the content. See `constants.php` for possible values: `ACTIVITYPUB_CONTENT_VISIBILITY_*`. Default null.
112 *
113 * @return boolean|int The ID of the outbox item or false on failure.
114 */
115 function add_to_outbox( $data, $activity_type = null, $user_id = 0, $content_visibility = null ) {
116 // If the user is disabled, fall back to the blog user when available.
117 if ( ! user_can_activitypub( $user_id ) ) {
118 if ( user_can_activitypub( Actors::BLOG_USER_ID ) ) {
119 $user_id = Actors::BLOG_USER_ID;
120 } else {
121 return false;
122 }
123 }
124
125 $transformer = Transformer_Factory::get_transformer( $data );
126
127 if ( ! $transformer || is_wp_error( $transformer ) ) {
128 return false;
129 }
130
131 if ( $content_visibility ) {
132 $transformer->set_content_visibility( $content_visibility );
133 } else {
134 $content_visibility = $transformer->get_content_visibility();
135 }
136
137 if ( $activity_type ) {
138 $activity = $transformer->to_activity( $activity_type );
139 $activity->set_actor( Actors::get_by_id( $user_id )->get_id() );
140 } else {
141 $activity = $transformer->to_object();
142 }
143
144 if ( ! $activity || \is_wp_error( $activity ) ) {
145 /**
146 * Action triggered when adding an object to the outbox fails.
147 *
148 * @param \WP_Error $activity The error object or false.
149 * @param mixed $data The object that failed to be added to the outbox.
150 * @param string|null $activity_type The type of the Activity or null if `$data` is an Activity.
151 * @param int $user_id The User ID.
152 * @param string $content_visibility The visibility of the content. See `constants.php` for possible values: `ACTIVITYPUB_CONTENT_VISIBILITY_*`.
153 */
154 \do_action( 'activitypub_add_to_outbox_failed', $activity, $data, $activity_type, $user_id, $content_visibility );
155
156 return false;
157 }
158
159 $outbox_activity_id = Outbox::add( $activity, $user_id, $content_visibility );
160
161 if ( ! $outbox_activity_id || \is_wp_error( $outbox_activity_id ) ) {
162 /**
163 * Action triggered when adding an object to the outbox fails.
164 *
165 * @param false|\WP_Error $outbox_activity_id The error object or false.
166 * @param mixed $data The object that failed to be added to the outbox.
167 * @param string|null $activity_type The type of the Activity or null if `$data` is an Activity.
168 * @param int $user_id The User ID.
169 * @param string $content_visibility The visibility of the content. See `constants.php` for possible values: `ACTIVITYPUB_CONTENT_VISIBILITY_*`.
170 */
171 \do_action( 'activitypub_add_to_outbox_failed', $outbox_activity_id, $data, $activity_type, $user_id, $content_visibility );
172
173 return false;
174 }
175
176 /**
177 * Action triggered after an object has been added to the outbox.
178 *
179 * @param int $outbox_activity_id The ID of the outbox item.
180 * @param Activity $activity The activity object.
181 * @param int $user_id The User-ID.
182 * @param string $content_visibility The visibility of the content. See `constants.php` for possible values: `ACTIVITYPUB_CONTENT_VISIBILITY_*`.
183 */
184 \do_action( 'post_activitypub_add_to_outbox', $outbox_activity_id, $activity, $user_id, $content_visibility );
185
186 // Update state based on activity.
187 $state_map = array(
188 'Create' => ACTIVITYPUB_OBJECT_STATE_FEDERATED,
189 'Update' => ACTIVITYPUB_OBJECT_STATE_FEDERATED,
190 'Delete' => ACTIVITYPUB_OBJECT_STATE_DELETED,
191 );
192
193 if ( $activity_type && isset( $state_map[ $activity_type ] ) ) {
194 set_wp_object_state( $data, $state_map[ $activity_type ] );
195 }
196
197 return $outbox_activity_id;
198 }
199
200 /**
201 * Follow a user.
202 *
203 * @param string|int $remote_actor The Actor URL, WebFinger Resource or Post-ID of the remote Actor.
204 * @param int $user_id The ID of the WordPress User.
205 *
206 * @return int|\WP_Error The Outbox ID on success or a WP_Error on failure.
207 */
208 function follow( $remote_actor, $user_id ) {
209 if ( \is_numeric( $remote_actor ) ) {
210 return Following::follow( $remote_actor, $user_id );
211 }
212
213 if ( ! \filter_var( $remote_actor, FILTER_VALIDATE_URL ) ) {
214 $remote_actor = Webfinger::resolve( $remote_actor );
215 }
216
217 if ( \is_wp_error( $remote_actor ) ) {
218 return $remote_actor;
219 }
220
221 $remote_actor_post = Remote_Actors::fetch_by_uri( $remote_actor );
222
223 if ( \is_wp_error( $remote_actor_post ) ) {
224 return $remote_actor_post;
225 }
226
227 return Following::follow( $remote_actor_post, $user_id );
228 }
229
230 /**
231 * Unfollow a user.
232 *
233 * @param string|int $remote_actor The Actor URL, WebFinger Resource or Post-ID of the remote Actor.
234 * @param int $user_id The ID of the WordPress User.
235 *
236 * @return \WP_Post|\WP_Error The Actor post or a WP_Error.
237 */
238 function unfollow( $remote_actor, $user_id ) {
239 if ( \is_numeric( $remote_actor ) ) {
240 return Following::unfollow( $remote_actor, $user_id );
241 }
242
243 if ( ! \filter_var( $remote_actor, FILTER_VALIDATE_URL ) ) {
244 $remote_actor = Webfinger::resolve( $remote_actor );
245 }
246
247 if ( \is_wp_error( $remote_actor ) ) {
248 return $remote_actor;
249 }
250
251 $remote_actor_post = Remote_Actors::fetch_by_uri( $remote_actor );
252
253 if ( \is_wp_error( $remote_actor_post ) ) {
254 return $remote_actor_post;
255 }
256
257 return Following::unfollow( $remote_actor_post, $user_id );
258 }
259