PluginProbe
ActivityPub / 9.0.0
ActivityPub v9.0.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-activity.php

functions-activity.php in ActivityPub 9.0.0, at includes/functions-activity.php

338 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 * Activity functions.
4 *
5 * Functions for working with ActivityPub activities, objects, and actors.
6 *
7 * @package Activitypub
8 */
9
10 namespace Activitypub;
11
12 use Activitypub\Activity\Activity;
13 use Activitypub\Activity\Actor;
14 use Activitypub\Activity\Base_Object;
15
16 /**
17 * Returns the ActivityPub default JSON-context.
18 *
19 * @return array The activitypub context.
20 *
21 * @deprecated 7.6.0 Use the respective context function instead.
22 */
23 function get_context() {
24 \_deprecated_function( __FUNCTION__, '7.6.0', 'Use the respective context function instead.' );
25
26 $context = Activity::JSON_LD_CONTEXT;
27
28 /**
29 * Filters the ActivityPub JSON-LD context.
30 *
31 * This filter allows developers to modify or extend the JSON-LD context used
32 * in ActivityPub responses. The context defines the vocabulary and terms used
33 * in the ActivityPub JSON objects.
34 *
35 * @param array $context The default ActivityPub JSON-LD context array.
36 */
37 return \apply_filters( 'activitypub_json_context', $context );
38 }
39
40 /**
41 * Extract recipient URLs from Activity object.
42 *
43 * @param array $data The Activity object as array.
44 *
45 * @return array The list of user URLs.
46 */
47 function extract_recipients_from_activity( $data ) {
48 $recipient_items = array();
49
50 foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience' ) as $i ) {
51 $recipient_items = \array_merge( $recipient_items, extract_recipients_from_activity_property( $i, $data ) );
52 }
53
54 return \array_unique( $recipient_items );
55 }
56
57 /**
58 * Extract recipient URLs from a specific property of an Activity object.
59 *
60 * Checks the activity level first, then falls back to the object property,
61 * and finally checks the instrument property (used by QuoteRequest activities).
62 *
63 * @param string $property The property to extract recipients from (e.g., 'to', 'cc').
64 * @param array $data The Activity object as array.
65 *
66 * @return array The list of user URLs.
67 */
68 function extract_recipients_from_activity_property( $property, $data ) {
69 $recipients = array();
70
71 if ( ! empty( $data[ $property ] ) ) {
72 $recipients = $data[ $property ];
73 } elseif ( ! empty( $data['object'][ $property ] ) ) {
74 $recipients = $data['object'][ $property ];
75 } elseif ( ! empty( $data['instrument'][ $property ] ) ) {
76 // QuoteRequest activities have addressing in the instrument (the quoting Note).
77 $recipients = $data['instrument'][ $property ];
78 }
79
80 $recipients = \array_map( '\Activitypub\object_to_uri', (array) $recipients );
81
82 return \array_unique( \array_filter( $recipients ) );
83 }
84
85 /**
86 * Determine the visibility of the activity based on its recipients.
87 *
88 * @param array $activity The activity data.
89 *
90 * @return string One of ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
91 * ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, or
92 * ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE.
93 */
94 function get_activity_visibility( $activity ) {
95 // Set default visibility for specific activity types.
96 if ( ! empty( $activity['type'] ) && in_array( $activity['type'], array( 'Accept', 'Delete', 'Follow', 'Reject', 'Undo' ), true ) ) {
97 return ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE;
98 }
99
100 // Check 'to' field for public visibility.
101 $to = extract_recipients_from_activity_property( 'to', $activity );
102 if ( ! empty( array_intersect( $to, ACTIVITYPUB_PUBLIC_AUDIENCE_IDENTIFIERS ) ) ) {
103 return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
104 }
105
106 // Check 'cc' field for quiet public visibility.
107 $cc = extract_recipients_from_activity_property( 'cc', $activity );
108 if ( ! empty( array_intersect( $cc, ACTIVITYPUB_PUBLIC_AUDIENCE_IDENTIFIERS ) ) ) {
109 return ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC;
110 }
111
112 return ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE;
113 }
114
115 /**
116 * Check if passed Activity is Public.
117 *
118 * @see https://github.com/w3c/activitypub/issues/404#issuecomment-2926310561
119 * @see https://www.w3.org/TR/activitypub/#delivery (Section 7.1, "Silent and private activities")
120 *
121 * @param Base_Object|array $data The Activity object as Base_Object or array.
122 *
123 * @return boolean True if public, false if not.
124 */
125 function is_activity_public( $data ) {
126 if ( $data instanceof Base_Object ) {
127 $data = $data->to_array();
128 }
129
130 $recipients = extract_recipients_from_activity( $data );
131
132 if ( empty( $recipients ) ) {
133 return false;
134 }
135
136 return ! empty( array_intersect( $recipients, ACTIVITYPUB_PUBLIC_AUDIENCE_IDENTIFIERS ) );
137 }
138
139 /**
140 * Check if passed Activity is a reply.
141 *
142 * @param array $data The Activity object as array.
143 *
144 * @return boolean True if a reply, false if not.
145 */
146 function is_activity_reply( $data ) {
147 return ! empty( $data['object']['inReplyTo'] );
148 }
149
150 /**
151 * Check if passed Activity is a quote.
152 *
153 * Checks for quote properties: quote, quoteUrl, quoteUri, or _misskey_quote.
154 *
155 * @param array $data The Activity object as array.
156 *
157 * @return boolean True if a quote, false if not.
158 */
159 function is_quote_activity( $data ) {
160 return ! empty( $data['object']['quote'] ) ||
161 ! empty( $data['object']['quoteUrl'] ) ||
162 ! empty( $data['object']['quoteUri'] ) ||
163 ! empty( $data['object']['_misskey_quote'] );
164 }
165
166 /**
167 * Get the URI of an ActivityPub object.
168 *
169 * @param array|string $data The ActivityPub object.
170 *
171 * @return string|null The URI of the ActivityPub object.
172 */
173 function object_to_uri( $data ) {
174 // Check whether it is already simple.
175 if ( ! $data || is_string( $data ) ) {
176 return $data;
177 }
178
179 if ( is_object( $data ) ) {
180 $data = $data->to_array();
181 }
182
183 /*
184 * Check if it is a list, then take first item.
185 * This plugin does not support collections.
186 */
187 if ( array_is_list( $data ) ) {
188 $data = $data[0];
189 }
190
191 // Check if it is simplified now.
192 if ( is_string( $data ) ) {
193 return $data;
194 }
195
196 $type = 'Object';
197 if ( isset( $data['type'] ) ) {
198 $type = $data['type'];
199 }
200
201 // Return part of Object that makes most sense.
202 switch ( $type ) {
203 case 'Audio': // See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audio.
204 case 'Document': // See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-document.
205 case 'Image': // See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-image.
206 case 'Video': // See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-video.
207 $data = object_to_uri( $data['url'] );
208 break;
209
210 case 'Link': // See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-link.
211 case 'Mention': // See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-mention.
212 $data = $data['href'];
213 break;
214
215 case 'FeaturedItem': // See https://github.com/mastodon/featured_collections/pull/1.
216 $data = object_to_uri( $data['featuredObject'] ?? null );
217 break;
218
219 default:
220 if ( isset( $data['id'] ) ) {
221 $data = $data['id'];
222 } elseif ( isset( $data['url'] ) ) {
223 $data = object_to_uri( $data['url'] );
224 } elseif ( isset( $data['href'] ) ) {
225 $data = $data['href'];
226 } else {
227 $data = null;
228 }
229 break;
230 }
231
232 return $data;
233 }
234
235 /**
236 * Check if an `$data` is an Activity.
237 *
238 * @see https://www.w3.org/ns/activitystreams#activities
239 *
240 * @param array|object|string $data The data to check.
241 *
242 * @return boolean True if the `$data` is an Activity, false otherwise.
243 */
244 function is_activity( $data ) {
245 /**
246 * Filters the activity types.
247 *
248 * @param array $types The activity types.
249 */
250 $types = apply_filters( 'activitypub_activity_types', Activity::TYPES );
251
252 return _is_type_of( $data, $types );
253 }
254
255 /**
256 * Check if an `$data` is an Activity Object.
257 *
258 * @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
259 *
260 * @param array|object|string $data The data to check.
261 *
262 * @return boolean True if the `$data` is an Activity Object, false otherwise.
263 */
264 function is_activity_object( $data ) {
265 /**
266 * Filters the activity object types.
267 *
268 * @param array $types The activity object types.
269 */
270 $types = \apply_filters( 'activitypub_activity_object_types', Base_Object::TYPES );
271
272 return _is_type_of( $data, $types );
273 }
274
275 /**
276 * Check if an `$data` is an Actor.
277 *
278 * @see https://www.w3.org/ns/activitystreams#actor
279 *
280 * @param array|object|string $data The data to check.
281 *
282 * @return boolean True if the `$data` is an Actor, false otherwise.
283 */
284 function is_actor( $data ) {
285 /**
286 * Filters the actor types.
287 *
288 * @param array $types The actor types.
289 */
290 $types = apply_filters( 'activitypub_actor_types', Actor::TYPES );
291
292 return _is_type_of( $data, $types );
293 }
294
295 /**
296 * Check if an `$data` is a Collection.
297 *
298 * @see https://www.w3.org/ns/activitystreams#collections
299 *
300 * @param array|object|string $data The data to check.
301 *
302 * @return boolean True if the `$data` is a Collection, false otherwise.
303 */
304 function is_collection( $data ) {
305 /**
306 * Filters the collection types.
307 *
308 * @param array $types The collection types.
309 */
310 $types = apply_filters( 'activitypub_collection_types', array( 'Collection', 'OrderedCollection', 'CollectionPage', 'OrderedCollectionPage' ) );
311
312 return _is_type_of( $data, $types );
313 }
314
315 /**
316 * Private helper to check if $data is of a given type set.
317 *
318 * @param array|object|string $data The data to check.
319 * @param array $types The types to check against.
320 *
321 * @return boolean True if $data is of one of the types, false otherwise.
322 */
323 function _is_type_of( $data, $types ) {
324 if ( is_string( $data ) ) {
325 return in_array( $data, $types, true );
326 }
327
328 if ( is_array( $data ) && isset( $data['type'] ) ) {
329 return in_array( $data['type'], $types, true );
330 }
331
332 if ( $data instanceof Base_Object ) {
333 return in_array( $data->get_type(), $types, true );
334 }
335
336 return false;
337 }
338