PluginProbe
ActivityPub / 3.2.2
ActivityPub v3.2.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/model/class-user.php +66 -318 9.0.23.2.2 View file →
@@ -1,27 +1,19 @@
1 1 <?php
2 -/**
3 - * User model file.
4 - *
5 - * @package Activitypub
6 - */
7 -
8 2 namespace Activitypub\Model;
9 3
4 +use WP_Query;
5 +use WP_Error;
6 +use Activitypub\Migration;
7 +use Activitypub\Signature;
8 +use Activitypub\Model\Blog;
10 9 use Activitypub\Activity\Actor;
11 -use Activitypub\Collection\Actors;
10 +use Activitypub\Collection\Users;
12 11 use Activitypub\Collection\Extra_Fields;
13 12
14 -use function Activitypub\get_attribution_domains;
13 +use function Activitypub\is_user_disabled;
15 14 use function Activitypub\get_rest_url_by_path;
16 -use function Activitypub\is_blog_public;
17 -use function Activitypub\user_can_activitypub;
18 15
19 -/**
20 - * User class.
21 - *
22 - * @method int get__id() Gets the WordPress user ID.
23 - */
24 16 class User extends Actor {
25 17 /**
26 18 * The local User-ID (WP_User).
27 19 *
@@ -29,10 +21,24 @@
29 21 */
30 22 protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
31 23
32 24 /**
33 - * Whether the User is discoverable.
25 + * The Featured-Posts.
34 26 *
27 + * @see https://docs.joinmastodon.org/spec/activitypub/#featured
28 + *
29 + * @context {
30 + * "@id": "http://joinmastodon.org/ns#featured",
31 + * "@type": "@id"
32 + * }
33 + *
34 + * @var string
35 + */
36 + protected $featured;
37 +
38 + /**
39 + * If the User is discoverable.
40 + *
35 41 * @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
36 42 *
37 43 * @context http://joinmastodon.org/ns#discoverable
38 44 *
@@ -40,62 +46,30 @@
40 46 */
41 47 protected $discoverable = true;
42 48
43 49 /**
44 - * The generator of the object.
50 + * If the User is indexable.
45 51 *
46 - * @see https://www.w3.org/TR/activitypub/#generator
47 - * @see https://codeberg.org/fediverse/fep/src/branch/main/fep/844e/fep-844e.md#discovery-through-an-actor
52 + * @context http://joinmastodon.org/ns#indexable
48 53 *
49 - * @var array
54 + * @var boolean
50 55 */
51 - protected $generator = array(
52 - 'type' => 'Application',
53 - 'implements' => array(
54 - array(
55 - 'href' => 'https://datatracker.ietf.org/doc/html/rfc9421',
56 - 'name' => 'RFC-9421: HTTP Message Signatures',
57 - ),
58 - ),
59 - );
56 + protected $indexable;
60 57
61 58 /**
62 - * Constructor.
59 + * The WebFinger Resource.
63 60 *
64 - * @param int $user_id Optional. The WordPress user ID. Default null.
61 + * @var string<url>
65 62 */
66 - public function __construct( $user_id = null ) {
67 - if ( $user_id ) {
68 - $this->_id = $user_id;
63 + protected $webfinger;
69 64
70 - /**
71 - * Fires when a model actor is constructed.
72 - *
73 - * @param User $this The User object.
74 - */
75 - \do_action( 'activitypub_construct_model_actor', $this );
76 - }
77 - }
78 -
79 - /**
80 - * The type of the object.
81 - *
82 - * @return string The type of the object.
83 - */
84 65 public function get_type() {
85 66 return 'Person';
86 67 }
87 68
88 - /**
89 - * Generate a User object from a WP_User.
90 - *
91 - * @param int $user_id The user ID.
92 - *
93 - * @return \WP_Error|User The User object or \WP_Error if user not found.
94 - */
95 69 public static function from_wp_user( $user_id ) {
96 - if ( ! user_can_activitypub( $user_id ) ) {
97 - return new \WP_Error(
70 + if ( is_user_disabled( $user_id ) ) {
71 + return new WP_Error(
98 72 'activitypub_user_not_found',
99 73 \__( 'User not found', 'activitypub' ),
100 74 array( 'status' => 404 )
101 75 );
@@ -100,45 +74,36 @@
100 74 array( 'status' => 404 )
101 75 );
102 76 }
103 77
104 - return new static( $user_id );
78 + $object = new static();
79 + $object->_id = $user_id;
80 +
81 + return $object;
105 82 }
106 83
107 84 /**
108 - * Get the user ID.
85 + * Get the User-ID.
109 86 *
110 - * @return string The user ID.
87 + * @return string The User-ID.
111 88 */
112 89 public function get_id() {
113 - $id = parent::get_id();
114 -
115 - if ( $id ) {
116 - return $id;
117 - }
118 -
119 - $permalink = \get_user_option( 'activitypub_use_permalink_as_id', $this->_id );
120 -
121 - if ( '1' === $permalink ) {
122 - return $this->get_url();
123 - }
124 -
125 - return \add_query_arg( 'author', $this->_id, \home_url( '/' ) );
90 + return $this->get_url();
126 91 }
127 92
128 93 /**
129 - * Get the Username.
94 + * Get the User-Name.
130 95 *
131 - * @return string The Username.
96 + * @return string The User-Name.
132 97 */
133 98 public function get_name() {
134 - return \get_the_author_meta( 'display_name', $this->_id );
99 + return \esc_attr( \get_the_author_meta( 'display_name', $this->_id ) );
135 100 }
136 101
137 102 /**
138 - * Get the User description.
103 + * Get the User-Description.
139 104 *
140 - * @return string The User description.
105 + * @return string The User-Description.
141 106 */
142 107 public function get_summary() {
143 108 $description = get_user_option( 'activitypub_description', $this->_id );
144 109 if ( empty( $description ) ) {
@@ -147,11 +112,11 @@
147 112 return \wpautop( \wp_kses( $description, 'default' ) );
148 113 }
149 114
150 115 /**
151 - * Get the User url.
116 + * Get the User-Url.
152 117 *
153 - * @return string The User url.
118 + * @return string The User-Url.
154 119 */
155 120 public function get_url() {
156 121 return \esc_url( \get_author_posts_url( $this->_id ) );
157 122 }
@@ -156,46 +121,21 @@
156 121 return \esc_url( \get_author_posts_url( $this->_id ) );
157 122 }
158 123
159 124 /**
160 - * Returns the User URL with @-Prefix for the username.
125 + * Returns the User-URL with @-Prefix for the username.
161 126 *
162 - * @return string The User URL with @-Prefix for the username.
127 + * @return string The User-URL with @-Prefix for the username.
163 128 */
164 129 public function get_alternate_url() {
165 130 return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
166 131 }
167 132
168 - /**
169 - * Get the preferred username.
170 - *
171 - * @return string The preferred username.
172 - */
173 133 public function get_preferred_username() {
174 - $login = \get_the_author_meta( 'login', $this->_id );
175 -
176 - // Handle cases where login is an email address (e.g., from Site Kit Google login).
177 - if ( \filter_var( $login, FILTER_VALIDATE_EMAIL ) ) {
178 - $login = \get_the_author_meta( 'user_nicename', $this->_id );
179 - }
180 -
181 - return $login;
134 + return \esc_attr( \get_the_author_meta( 'login', $this->_id ) );
182 135 }
183 136
184 - /**
185 - * Get the User icon.
186 - *
187 - * @return string[] The User icon.
188 - */
189 137 public function get_icon() {
190 - $icon = \get_user_option( 'activitypub_icon', $this->_id );
191 - if ( false !== $icon && wp_attachment_is_image( $icon ) ) {
192 - return array(
193 - 'type' => 'Image',
194 - 'url' => esc_url( wp_get_attachment_url( $icon ) ),
195 - );
196 - }
197 -
198 138 $icon = \esc_url(
199 139 \get_avatar_url(
200 140 $this->_id,
201 141 array( 'size' => 120 )
@@ -207,23 +147,18 @@
207 147 'url' => $icon,
208 148 );
209 149 }
210 150
211 - /**
212 - * Returns the header image.
213 - *
214 - * @return string[]|null The header image.
215 - */
216 151 public function get_image() {
217 152 $header_image = get_user_option( 'activitypub_header_image', $this->_id );
218 153 $image_url = null;
219 154
220 - if ( ! $header_image && \has_header_image() ) {
221 - $image_url = \get_header_image();
155 + if ( $header_image ) {
156 + $image_url = \wp_get_attachment_url( $header_image );
222 157 }
223 158
224 - if ( $header_image ) {
225 - $image_url = \wp_get_attachment_url( $header_image );
159 + if ( ! $image_url && \has_header_image() ) {
160 + $image_url = \get_header_image();
226 161 }
227 162
228 163 if ( $image_url ) {
229 164 return array(
@@ -234,27 +169,17 @@
234 169
235 170 return null;
236 171 }
237 172
238 - /**
239 - * Returns the date the user was created.
240 - *
241 - * @return false|string The date the user was created.
242 - */
243 173 public function get_published() {
244 - return \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, \strtotime( \get_the_author_meta( 'registered', $this->_id ) ) );
174 + return \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( \get_the_author_meta( 'registered', $this->_id ) ) );
245 175 }
246 176
247 - /**
248 - * Returns the public key.
249 - *
250 - * @return string[] The public key.
251 - */
252 177 public function get_public_key() {
253 178 return array(
254 - 'id' => $this->get_id() . '#main-key',
255 - 'owner' => $this->get_id(),
256 - 'publicKeyPem' => Actors::get_public_key( $this->get__id() ),
179 + 'id' => $this->get_id() . '#main-key',
180 + 'owner' => $this->get_id(),
181 + 'publicKeyPem' => Signature::get_public_key_for( $this->get__id() ),
257 182 );
258 183 }
259 184
260 185 /**
@@ -293,19 +218,8 @@
293 218 return get_rest_url_by_path( sprintf( 'actors/%d/following', $this->get__id() ) );
294 219 }
295 220
296 221 /**
297 - * Returns the Liked API endpoint.
298 - *
299 - * @since 8.1.0
300 - *
301 - * @return string The Liked endpoint.
302 - */
303 - public function get_liked() {
304 - return get_rest_url_by_path( sprintf( 'actors/%d/liked', $this->get__id() ) );
305 - }
306 -
307 - /**
308 222 * Returns the Featured-API-Endpoint.
309 223 *
310 224 * @return string The Featured-Endpoint.
311 225 */
@@ -312,31 +226,18 @@
312 226 public function get_featured() {
313 227 return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) );
314 228 }
315 229
316 - /**
317 - * Returns the Featured-Tags-API-Endpoint.
318 - *
319 - * @return string The Featured-Tags-Endpoint.
320 - */
321 - public function get_featured_tags() {
322 - return get_rest_url_by_path( sprintf( 'actors/%d/collections/tags', $this->get__id() ) );
323 - }
230 + public function get_endpoints() {
231 + $endpoints = null;
324 232
325 - /**
326 - * Returns the endpoints.
327 - *
328 - * @return string[]|null The endpoints.
329 - */
330 - public function get_endpoints() {
331 - return array(
332 - 'sharedInbox' => get_rest_url_by_path( 'inbox' ),
333 - 'oauthAuthorizationEndpoint' => get_rest_url_by_path( 'oauth/authorize' ),
334 - 'oauthTokenEndpoint' => get_rest_url_by_path( 'oauth/token' ),
335 - 'oauthRegistrationEndpoint' => get_rest_url_by_path( 'oauth/clients' ),
336 - 'proxyUrl' => get_rest_url_by_path( 'proxy' ),
337 - 'proxyEventStream' => get_rest_url_by_path( 'proxy/stream' ),
338 - );
233 + if ( ACTIVITYPUB_SHARED_INBOX_FEATURE ) {
234 + $endpoints = array(
235 + 'sharedInbox' => get_rest_url_by_path( 'inbox' ),
236 + );
237 + }
238 +
239 + return $endpoints;
339 240 }
340 241
341 242 /**
342 243 * Extend the User-Output with Attachments.
@@ -356,177 +257,24 @@
356 257 public function get_webfinger() {
357 258 return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
358 259 }
359 260
360 - /**
361 - * Returns the canonical URL.
362 - *
363 - * @return string The canonical URL.
364 - */
365 261 public function get_canonical_url() {
366 262 return $this->get_url();
367 263 }
368 264
369 - /**
370 - * Returns the streams.
371 - *
372 - * @return null The streams.
373 - */
374 265 public function get_streams() {
375 266 return null;
376 267 }
377 268
378 - /**
379 - * Returns the tag.
380 - *
381 - * @return array The tag.
382 - */
383 269 public function get_tag() {
384 270 return array();
385 271 }
386 272
387 - /**
388 - * Returns the indexable state.
389 - *
390 - * @return bool Whether the user is indexable.
391 - */
392 273 public function get_indexable() {
393 - if ( is_blog_public() ) {
274 + if ( \get_option( 'blog_public', 1 ) ) {
394 275 return true;
395 276 } else {
396 277 return false;
397 - }
398 - }
399 -
400 - /**
401 - * Update the username.
402 - *
403 - * @param string $value The new value.
404 - * @return int|\WP_Error The updated user ID or \WP_Error on failure.
405 - */
406 - public function update_name( $value ) {
407 - $userdata = array(
408 - 'ID' => $this->_id,
409 - 'display_name' => $value,
410 - );
411 - return \wp_update_user( $userdata );
412 - }
413 -
414 - /**
415 - * Update the User description.
416 - *
417 - * @param string $value The new value.
418 - * @return bool True if the attribute was updated, false otherwise.
419 - */
420 - public function update_summary( $value ) {
421 - return \update_user_option( $this->_id, 'activitypub_description', $value );
422 - }
423 -
424 - /**
425 - * Update the User icon.
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_icon( $value ) {
431 - if ( ! wp_attachment_is_image( $value ) ) {
432 - return false;
433 - }
434 - return update_user_option( $this->_id, 'activitypub_icon', $value );
435 - }
436 -
437 - /**
438 - * Update the User-Header-Image.
439 - *
440 - * @param int $value The new value. Should be an attachment ID.
441 - * @return bool True if the attribute was updated, false otherwise.
442 - */
443 - public function update_header( $value ) {
444 - if ( ! wp_attachment_is_image( $value ) ) {
445 - return false;
446 - }
447 - return \update_user_option( $this->_id, 'activitypub_header_image', $value );
448 - }
449 -
450 - /**
451 - * Returns the website hosts allowed to credit this blog.
452 - *
453 - * @return string[]|null The attribution domains or null if not found.
454 - */
455 - public function get_attribution_domains() {
456 - return get_attribution_domains();
457 - }
458 -
459 - /**
460 - * Returns the alsoKnownAs.
461 - *
462 - * @return string[] The alsoKnownAs.
463 - */
464 - public function get_also_known_as() {
465 - $also_known_as = array(
466 - \add_query_arg( 'author', $this->_id, \home_url( '/' ) ),
467 - $this->get_url(),
468 - $this->get_alternate_url(),
469 - );
470 -
471 - $also_known_as = array_merge( $also_known_as, \get_user_option( 'activitypub_also_known_as', $this->_id ) ?: array() );
472 -
473 - return array_unique( $also_known_as );
474 - }
475 -
476 - /**
477 - * Returns the movedTo.
478 - *
479 - * @return string The movedTo.
480 - */
481 - public function get_moved_to() {
482 - $moved_to = \get_user_option( 'activitypub_moved_to', $this->_id );
483 -
484 - return $moved_to && $moved_to !== $this->get_id() ? $moved_to : null;
485 - }
486 -
487 - /**
488 - * Get the actor-level interaction policy.
489 - *
490 - * Overrides the magic property accessor on Base_Object so that we always
491 - * compute the policy from the current site setting rather than returning a
492 - * cached property value. Currently only emits `canFeature` (FEP-7aa9).
493 - * Driven by the site option `activitypub_default_feature_policy` and
494 - * defaults to denying all featured-collection requests, in line with
495 - * FEP-7aa9's "absence of policy = no consent" rule.
496 - *
497 - * @see https://w3id.org/fep/7aa9
498 - *
499 - * @since 9.0.0
500 - *
501 - * @return array
502 - */
503 - public function get_interaction_policy() {
504 - $policy = array( 'canFeature' => $this->build_can_feature_policy() );
505 -
506 - // Merge with an explicitly set interaction policy, if any.
507 - if ( $this->interaction_policy ) {
508 - $policy = \array_merge( (array) $this->interaction_policy, $policy );
509 - }
510 -
511 - return $policy;
512 - }
513 -
514 - /**
515 - * Build the `canFeature` policy array from the site option.
516 - *
517 - * @return array
518 - */
519 - protected function build_can_feature_policy() {
520 - $policy = \get_option( 'activitypub_default_feature_policy', ACTIVITYPUB_INTERACTION_POLICY_ME );
521 -
522 - switch ( $policy ) {
523 - case ACTIVITYPUB_INTERACTION_POLICY_ANYONE:
524 - return array( 'automaticApproval' => array( 'https://www.w3.org/ns/activitystreams#Public' ) );
525 - case ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS:
526 - return array( 'automaticApproval' => array( $this->get_followers() ) );
527 - case ACTIVITYPUB_INTERACTION_POLICY_ME:
528 - default:
529 - return array( 'automaticApproval' => array( $this->get_id() ) );
530 278 }
531 279 }
532 280 }