PluginProbe
ActivityPub / 5.7.0
ActivityPub v5.7.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
← All changes | includes/model/class-user.php +271 -97 1.2.05.7.0 View file →
@@ -1,16 +1,28 @@
1 1 <?php
2 +/**
3 + * User model file.
4 + *
5 + * @package Activitypub
6 + */
7 +
2 8 namespace Activitypub\Model;
3 9
4 -use WP_Query;
5 -use WP_Error;
10 +use Activitypub\Activity\Actor;
11 +use Activitypub\Collection\Extra_Fields;
12 +use Activitypub\Http;
6 13 use Activitypub\Signature;
7 -use Activitypub\Collection\Users;
8 -use Activitypub\Activity\Actor;
9 14
10 -use function Activitypub\is_user_disabled;
15 +use function Activitypub\is_blog_public;
11 16 use function Activitypub\get_rest_url_by_path;
17 +use function Activitypub\get_attribution_domains;
18 +use function Activitypub\user_can_activitypub;
12 19
20 +/**
21 + * User class.
22 + *
23 + * @method int get__id() Gets the WordPress user ID.
24 + */
13 25 class User extends Actor {
14 26 /**
15 27 * The local User-ID (WP_User).
16 28 *
@@ -22,63 +34,81 @@
22 34 * The Featured-Posts.
23 35 *
24 36 * @see https://docs.joinmastodon.org/spec/activitypub/#featured
25 37 *
38 + * @context {
39 + * "@id": "http://joinmastodon.org/ns#featured",
40 + * "@type": "@id"
41 + * }
42 + *
26 43 * @var string
27 44 */
28 45 protected $featured;
29 46
30 47 /**
31 - * Moderators endpoint.
48 + * Whether the User is discoverable.
32 49 *
33 - * @see https://join-lemmy.org/docs/contributors/05-federation.html
50 + * @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
34 51 *
35 - * @var string
52 + * @context http://joinmastodon.org/ns#discoverable
53 + *
54 + * @var boolean
36 55 */
37 - protected $moderators;
56 + protected $discoverable = true;
38 57
39 58 /**
40 - * The User-Type
59 + * Whether the User is indexable.
41 60 *
42 - * @var string
61 + * @context http://joinmastodon.org/ns#indexable
62 + *
63 + * @var boolean
43 64 */
44 - protected $type = 'Person';
65 + protected $indexable;
45 66
46 67 /**
47 - * If the User is discoverable.
68 + * The WebFinger Resource.
48 69 *
49 - * @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
50 - *
51 - * @var boolean
70 + * @var string
52 71 */
53 - protected $discoverable = true;
72 + protected $webfinger;
54 73
55 74 /**
56 - * If the User is indexable.
75 + * Constructor.
57 76 *
58 - * @var boolean
77 + * @param int $user_id Optional. The WordPress user ID. Default null.
59 78 */
60 - protected $indexable;
79 + public function __construct( $user_id = null ) {
80 + if ( $user_id ) {
81 + $this->_id = $user_id;
61 82
83 + /**
84 + * Fires when a model actor is constructed.
85 + *
86 + * @param User $this The User object.
87 + */
88 + \do_action( 'activitypub_construct_model_actor', $this );
89 + }
90 + }
91 +
62 92 /**
63 - * The WebFinger Resource.
93 + * The type of the object.
64 94 *
65 - * @var string<url>
95 + * @return string The type of the object.
66 96 */
67 - protected $resource;
97 + public function get_type() {
98 + return 'Person';
99 + }
68 100
69 101 /**
70 - * Restrict posting to mods
102 + * Generate a User object from a WP_User.
71 103 *
72 - * @see https://join-lemmy.org/docs/contributors/05-federation.html
104 + * @param int $user_id The user ID.
73 105 *
74 - * @var boolean
106 + * @return \WP_Error|User The User object or \WP_Error if user not found.
75 107 */
76 - protected $posting_restricted_to_mods = null;
77 -
78 108 public static function from_wp_user( $user_id ) {
79 - if ( is_user_disabled( $user_id ) ) {
80 - return new WP_Error(
109 + if ( ! user_can_activitypub( $user_id ) ) {
110 + return new \WP_Error(
81 111 'activitypub_user_not_found',
82 112 \__( 'User not found', 'activitypub' ),
83 113 array( 'status' => 404 )
84 114 );
@@ -83,39 +113,48 @@
83 113 array( 'status' => 404 )
84 114 );
85 115 }
86 116
87 - $object = new static();
88 - $object->_id = $user_id;
89 -
90 - return $object;
117 + return new static( $user_id );
91 118 }
92 119
93 120 /**
94 - * Get the User-ID.
121 + * Get the user ID.
95 122 *
96 - * @return string The User-ID.
123 + * @return string The user ID.
97 124 */
98 125 public function get_id() {
99 - return $this->get_url();
126 + $id = parent::get_id();
127 +
128 + if ( $id ) {
129 + return $id;
130 + }
131 +
132 + $permalink = \get_user_option( 'activitypub_use_permalink_as_id', $this->_id );
133 +
134 + if ( '1' === $permalink ) {
135 + return $this->get_url();
136 + }
137 +
138 + return \add_query_arg( 'author', $this->_id, \trailingslashit( \home_url() ) );
100 139 }
101 140
102 141 /**
103 - * Get the User-Name.
142 + * Get the Username.
104 143 *
105 - * @return string The User-Name.
144 + * @return string The Username.
106 145 */
107 146 public function get_name() {
108 - return \esc_attr( \get_the_author_meta( 'display_name', $this->_id ) );
147 + return \get_the_author_meta( 'display_name', $this->_id );
109 148 }
110 149
111 150 /**
112 - * Get the User-Description.
151 + * Get the User description.
113 152 *
114 - * @return string The User-Description.
153 + * @return string The User description.
115 154 */
116 155 public function get_summary() {
117 - $description = get_user_meta( $this->_id, 'activitypub_user_description', true );
156 + $description = get_user_option( 'activitypub_description', $this->_id );
118 157 if ( empty( $description ) ) {
119 158 $description = get_user_meta( $this->_id, 'description', true );
120 159 }
121 160 return \wpautop( \wp_kses( $description, 'default' ) );
@@ -121,11 +160,11 @@
121 160 return \wpautop( \wp_kses( $description, 'default' ) );
122 161 }
123 162
124 163 /**
125 - * Get the User-Url.
164 + * Get the User url.
126 165 *
127 - * @return string The User-Url.
166 + * @return string The User url.
128 167 */
129 168 public function get_url() {
130 169 return \esc_url( \get_author_posts_url( $this->_id ) );
131 170 }
@@ -130,21 +169,39 @@
130 169 return \esc_url( \get_author_posts_url( $this->_id ) );
131 170 }
132 171
133 172 /**
134 - * Returns the User-URL with @-Prefix for the username.
173 + * Returns the User URL with @-Prefix for the username.
135 174 *
136 - * @return string The User-URL with @-Prefix for the username.
175 + * @return string The User URL with @-Prefix for the username.
137 176 */
138 - public function get_at_url() {
139 - return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_username() );
177 + public function get_alternate_url() {
178 + return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
140 179 }
141 180
181 + /**
182 + * Get the preferred username.
183 + *
184 + * @return string The preferred username.
185 + */
142 186 public function get_preferred_username() {
143 - return \esc_attr( \get_the_author_meta( 'login', $this->_id ) );
187 + return \get_the_author_meta( 'login', $this->_id );
144 188 }
145 189
190 + /**
191 + * Get the User icon.
192 + *
193 + * @return string[] The User icon.
194 + */
146 195 public function get_icon() {
196 + $icon = \get_user_option( 'activitypub_icon', $this->_id );
197 + if ( false !== $icon && wp_attachment_is_image( $icon ) ) {
198 + return array(
199 + 'type' => 'Image',
200 + 'url' => esc_url( wp_get_attachment_url( $icon ) ),
201 + );
202 + }
203 +
147 204 $icon = \esc_url(
148 205 \get_avatar_url(
149 206 $this->_id,
150 207 array( 'size' => 120 )
@@ -156,14 +213,29 @@
156 213 'url' => $icon,
157 214 );
158 215 }
159 216
217 + /**
218 + * Returns the header image.
219 + *
220 + * @return string[]|null The header image.
221 + */
160 222 public function get_image() {
161 - if ( \has_header_image() ) {
162 - $image = \esc_url( \get_header_image() );
223 + $header_image = get_user_option( 'activitypub_header_image', $this->_id );
224 + $image_url = null;
225 +
226 + if ( ! $header_image && \has_header_image() ) {
227 + $image_url = \get_header_image();
228 + }
229 +
230 + if ( $header_image ) {
231 + $image_url = \wp_get_attachment_url( $header_image );
232 + }
233 +
234 + if ( $image_url ) {
163 235 return array(
164 236 'type' => 'Image',
165 - 'url' => $image,
237 + 'url' => esc_url( $image_url ),
166 238 );
167 239 }
168 240
169 241 return null;
@@ -168,16 +240,26 @@
168 240
169 241 return null;
170 242 }
171 243
244 + /**
245 + * Returns the date the user was created.
246 + *
247 + * @return false|string The date the user was created.
248 + */
172 249 public function get_published() {
173 - return \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( \get_the_author_meta( 'registered', $this->_id ) ) );
250 + return \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, \strtotime( \get_the_author_meta( 'registered', $this->_id ) ) );
174 251 }
175 252
253 + /**
254 + * Returns the public key.
255 + *
256 + * @return string[] The public key.
257 + */
176 258 public function get_public_key() {
177 259 return array(
178 - 'id' => $this->get_id() . '#main-key',
179 - 'owner' => $this->get_id(),
260 + 'id' => $this->get_id() . '#main-key',
261 + 'owner' => $this->get_id(),
180 262 'publicKeyPem' => Signature::get_public_key_for( $this->get__id() ),
181 263 );
182 264 }
183 265
@@ -186,9 +268,9 @@
186 268 *
187 269 * @return string The Inbox-Endpoint.
188 270 */
189 271 public function get_inbox() {
190 - return get_rest_url_by_path( sprintf( 'users/%d/inbox', $this->get__id() ) );
272 + return get_rest_url_by_path( sprintf( 'actors/%d/inbox', $this->get__id() ) );
191 273 }
192 274
193 275 /**
194 276 * Returns the Outbox-API-Endpoint.
@@ -195,9 +277,9 @@
195 277 *
196 278 * @return string The Outbox-Endpoint.
197 279 */
198 280 public function get_outbox() {
199 - return get_rest_url_by_path( sprintf( 'users/%d/outbox', $this->get__id() ) );
281 + return get_rest_url_by_path( sprintf( 'actors/%d/outbox', $this->get__id() ) );
200 282 }
201 283
202 284 /**
203 285 * Returns the Followers-API-Endpoint.
@@ -204,9 +286,9 @@
204 286 *
205 287 * @return string The Followers-Endpoint.
206 288 */
207 289 public function get_followers() {
208 - return get_rest_url_by_path( sprintf( 'users/%d/followers', $this->get__id() ) );
290 + return get_rest_url_by_path( sprintf( 'actors/%d/followers', $this->get__id() ) );
209 291 }
210 292
211 293 /**
212 294 * Returns the Following-API-Endpoint.
@@ -213,9 +295,9 @@
213 295 *
214 296 * @return string The Following-Endpoint.
215 297 */
216 298 public function get_following() {
217 - return get_rest_url_by_path( sprintf( 'users/%d/following', $this->get__id() ) );
299 + return get_rest_url_by_path( sprintf( 'actors/%d/following', $this->get__id() ) );
218 300 }
219 301
220 302 /**
221 303 * Returns the Featured-API-Endpoint.
@@ -222,79 +304,171 @@
222 304 *
223 305 * @return string The Featured-Endpoint.
224 306 */
225 307 public function get_featured() {
226 - return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) );
308 + return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) );
227 309 }
228 310
229 311 /**
230 - * Extend the User-Output with Attachments.
312 + * Returns the endpoints.
231 313 *
232 - * @return array The extended User-Output.
314 + * @return string[]|null The endpoints.
233 315 */
234 - public function get_attachment() {
235 - $array = array();
316 + public function get_endpoints() {
317 + $endpoints = null;
236 318
237 - $array[] = array(
238 - 'type' => 'PropertyValue',
239 - 'name' => \__( 'Blog', 'activitypub' ),
240 - 'value' => \html_entity_decode(
241 - '<a rel="me" title="' . \esc_attr( \home_url( '/' ) ) . '" target="_blank" href="' . \home_url( '/' ) . '">' . \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) . '</a>',
242 - \ENT_QUOTES,
243 - 'UTF-8'
244 - ),
245 - );
246 -
247 - $array[] = array(
248 - 'type' => 'PropertyValue',
249 - 'name' => \__( 'Profile', 'activitypub' ),
250 - 'value' => \html_entity_decode(
251 - '<a rel="me" title="' . \esc_attr( \get_author_posts_url( $this->get__id() ) ) . '" target="_blank" href="' . \get_author_posts_url( $this->get__id() ) . '">' . \wp_parse_url( \get_author_posts_url( $this->get__id() ), \PHP_URL_HOST ) . '</a>',
252 - \ENT_QUOTES,
253 - 'UTF-8'
254 - ),
255 - );
256 -
257 - if ( \get_the_author_meta( 'user_url', $this->get__id() ) ) {
258 - $array[] = array(
259 - 'type' => 'PropertyValue',
260 - 'name' => \__( 'Website', 'activitypub' ),
261 - 'value' => \html_entity_decode(
262 - '<a rel="me" title="' . \esc_attr( \get_the_author_meta( 'user_url', $this->get__id() ) ) . '" target="_blank" href="' . \get_the_author_meta( 'user_url', $this->get__id() ) . '">' . \wp_parse_url( \get_the_author_meta( 'user_url', $this->get__id() ), \PHP_URL_HOST ) . '</a>',
263 - \ENT_QUOTES,
264 - 'UTF-8'
265 - ),
319 + if ( \get_option( 'activitypub_shared_inbox' ) ) {
320 + $endpoints = array(
321 + 'sharedInbox' => get_rest_url_by_path( 'inbox' ),
266 322 );
267 323 }
268 324
269 - return $array;
325 + return $endpoints;
270 326 }
271 327
272 328 /**
329 + * Extend the User-Output with Attachments.
330 + *
331 + * @return array The extended User-Output.
332 + */
333 + public function get_attachment() {
334 + $extra_fields = Extra_Fields::get_actor_fields( $this->_id );
335 + return Extra_Fields::fields_to_attachments( $extra_fields );
336 + }
337 +
338 + /**
273 339 * Returns a user@domain type of identifier for the user.
274 340 *
275 341 * @return string The Webfinger-Identifier.
276 342 */
277 - public function get_resource() {
343 + public function get_webfinger() {
278 344 return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
279 345 }
280 346
347 + /**
348 + * Returns the canonical URL.
349 + *
350 + * @return string The canonical URL.
351 + */
281 352 public function get_canonical_url() {
282 353 return $this->get_url();
283 354 }
284 355
356 + /**
357 + * Returns the streams.
358 + *
359 + * @return null The streams.
360 + */
285 361 public function get_streams() {
286 362 return null;
287 363 }
288 364
365 + /**
366 + * Returns the tag.
367 + *
368 + * @return array The tag.
369 + */
289 370 public function get_tag() {
290 371 return array();
291 372 }
292 373
374 + /**
375 + * Returns the indexable state.
376 + *
377 + * @return bool Whether the user is indexable.
378 + */
293 379 public function get_indexable() {
294 - if ( \get_option( 'blog_public', 1 ) ) {
380 + if ( is_blog_public() ) {
295 381 return true;
296 382 } else {
297 383 return false;
298 384 }
385 + }
386 +
387 + /**
388 + * Update the username.
389 + *
390 + * @param string $value The new value.
391 + * @return int|\WP_Error The updated user ID or \WP_Error on failure.
392 + */
393 + public function update_name( $value ) {
394 + $userdata = array(
395 + 'ID' => $this->_id,
396 + 'display_name' => $value,
397 + );
398 + return \wp_update_user( $userdata );
399 + }
400 +
401 + /**
402 + * Update the User description.
403 + *
404 + * @param string $value The new value.
405 + * @return bool True if the attribute was updated, false otherwise.
406 + */
407 + public function update_summary( $value ) {
408 + return \update_user_option( $this->_id, 'activitypub_description', $value );
409 + }
410 +
411 + /**
412 + * Update the User icon.
413 + *
414 + * @param int $value The new value. Should be an attachment ID.
415 + * @return bool True if the attribute was updated, false otherwise.
416 + */
417 + public function update_icon( $value ) {
418 + if ( ! wp_attachment_is_image( $value ) ) {
419 + return false;
420 + }
421 + return update_user_option( $this->_id, 'activitypub_icon', $value );
422 + }
423 +
424 + /**
425 + * Update the User-Header-Image.
426 + *
427 + * @param int $value The new value. Should be an attachment ID.
428 + * @return bool True if the attribute was updated, false otherwise.
429 + */
430 + public function update_header( $value ) {
431 + if ( ! wp_attachment_is_image( $value ) ) {
432 + return false;
433 + }
434 + return \update_user_option( $this->_id, 'activitypub_header_image', $value );
435 + }
436 +
437 + /**
438 + * Returns the website hosts allowed to credit this blog.
439 + *
440 + * @return string[]|null The attribution domains or null if not found.
441 + */
442 + public function get_attribution_domains() {
443 + return get_attribution_domains();
444 + }
445 +
446 + /**
447 + * Returns the alsoKnownAs.
448 + *
449 + * @return string[] The alsoKnownAs.
450 + */
451 + public function get_also_known_as() {
452 + $also_known_as = array(
453 + \add_query_arg( 'author', $this->_id, \home_url( '/' ) ),
454 + $this->get_url(),
455 + $this->get_alternate_url(),
456 + );
457 +
458 + // phpcs:ignore Universal.Operators.DisallowShortTernary.Found
459 + $also_known_as = array_merge( $also_known_as, \get_user_option( 'activitypub_also_known_as', $this->_id ) ?: array() );
460 +
461 + return array_unique( $also_known_as );
462 + }
463 +
464 + /**
465 + * Returns the movedTo.
466 + *
467 + * @return string The movedTo.
468 + */
469 + public function get_moved_to() {
470 + $moved_to = \get_user_option( 'activitypub_moved_to', $this->_id );
471 +
472 + return $moved_to && $moved_to !== $this->get_id() ? $moved_to : null;
299 473 }
300 474 }