PluginProbe
ActivityPub / 1.0.0
ActivityPub v1.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
← All changes | includes/model/class-user.php +93 -304 8.3.01.0.0 View file →
@@ -1,27 +1,16 @@
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\Signature;
7 +use Activitypub\Collection\Users;
10 8 use Activitypub\Activity\Actor;
11 -use Activitypub\Collection\Actors;
12 -use Activitypub\Collection\Extra_Fields;
13 9
14 -use function Activitypub\get_attribution_domains;
10 +use function Activitypub\is_user_disabled;
15 11 use function Activitypub\get_rest_url_by_path;
16 -use function Activitypub\is_blog_public;
17 -use function Activitypub\user_can_activitypub;
18 12
19 -/**
20 - * User class.
21 - *
22 - * @method int get__id() Gets the WordPress user ID.
23 - */
24 13 class User extends Actor {
25 14 /**
26 15 * The local User-ID (WP_User).
27 16 *
@@ -29,73 +18,49 @@
29 18 */
30 19 protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
31 20
32 21 /**
33 - * Whether the User is discoverable.
22 + * The Featured-Tags.
34 23 *
35 - * @see https://docs.joinmastodon.org/spec/activitypub/#discoverable
24 + * @see https://docs.joinmastodon.org/spec/activitypub/#featuredTags
36 25 *
37 - * @context http://joinmastodon.org/ns#discoverable
38 - *
39 - * @var boolean
26 + * @var string
40 27 */
41 - protected $discoverable = true;
28 + protected $featured_tags;
42 29
43 30 /**
44 - * The generator of the object.
31 + * The Featured-Posts.
45 32 *
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
33 + * @see https://docs.joinmastodon.org/spec/activitypub/#featured
48 34 *
49 - * @var array
35 + * @var string
50 36 */
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 - );
37 + protected $featured;
60 38
61 39 /**
62 - * Constructor.
40 + * The User-Type
63 41 *
64 - * @param int $user_id Optional. The WordPress user ID. Default null.
42 + * @var string
65 43 */
66 - public function __construct( $user_id = null ) {
67 - if ( $user_id ) {
68 - $this->_id = $user_id;
44 + protected $type = 'Person';
69 45
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 46 /**
80 - * The type of the object.
47 + * If the User is discoverable.
81 48 *
82 - * @return string The type of the object.
49 + * @var boolean
83 50 */
84 - public function get_type() {
85 - return 'Person';
86 - }
51 + protected $discoverable = true;
87 52
88 53 /**
89 - * Generate a User object from a WP_User.
54 + * The WebFinger Resource.
90 55 *
91 - * @param int $user_id The user ID.
92 - *
93 - * @return \WP_Error|User The User object or \WP_Error if user not found.
56 + * @var string<url>
94 57 */
58 + protected $resource;
59 +
95 60 public static function from_wp_user( $user_id ) {
96 - if ( ! user_can_activitypub( $user_id ) ) {
97 - return new \WP_Error(
61 + if ( is_user_disabled( $user_id ) ) {
62 + return new WP_Error(
98 63 'activitypub_user_not_found',
99 64 \__( 'User not found', 'activitypub' ),
100 65 array( 'status' => 404 )
101 66 );
@@ -100,48 +65,39 @@
100 65 array( 'status' => 404 )
101 66 );
102 67 }
103 68
104 - return new static( $user_id );
69 + $object = new static();
70 + $object->_id = $user_id;
71 +
72 + return $object;
105 73 }
106 74
107 75 /**
108 - * Get the user ID.
76 + * Get the User-ID.
109 77 *
110 - * @return string The user ID.
78 + * @return string The User-ID.
111 79 */
112 80 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( '/' ) );
81 + return $this->get_url();
126 82 }
127 83
128 84 /**
129 - * Get the Username.
85 + * Get the User-Name.
130 86 *
131 - * @return string The Username.
87 + * @return string The User-Name.
132 88 */
133 89 public function get_name() {
134 - return \get_the_author_meta( 'display_name', $this->_id );
90 + return \esc_attr( \get_the_author_meta( 'display_name', $this->_id ) );
135 91 }
136 92
137 93 /**
138 - * Get the User description.
94 + * Get the User-Description.
139 95 *
140 - * @return string The User description.
96 + * @return string The User-Description.
141 97 */
142 98 public function get_summary() {
143 - $description = get_user_option( 'activitypub_description', $this->_id );
99 + $description = get_user_meta( $this->_id, 'activitypub_user_description', true );
144 100 if ( empty( $description ) ) {
145 101 $description = get_user_meta( $this->_id, 'description', true );
146 102 }
147 103 return \wpautop( \wp_kses( $description, 'default' ) );
@@ -147,11 +103,11 @@
147 103 return \wpautop( \wp_kses( $description, 'default' ) );
148 104 }
149 105
150 106 /**
151 - * Get the User url.
107 + * Get the User-Url.
152 108 *
153 - * @return string The User url.
109 + * @return string The User-Url.
154 110 */
155 111 public function get_url() {
156 112 return \esc_url( \get_author_posts_url( $this->_id ) );
157 113 }
@@ -156,46 +112,21 @@
156 112 return \esc_url( \get_author_posts_url( $this->_id ) );
157 113 }
158 114
159 115 /**
160 - * Returns the User URL with @-Prefix for the username.
116 + * Returns the User-URL with @-Prefix for the username.
161 117 *
162 - * @return string The User URL with @-Prefix for the username.
118 + * @return string The User-URL with @-Prefix for the username.
163 119 */
164 - public function get_alternate_url() {
165 - return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_preferred_username() );
120 + public function get_at_url() {
121 + return \esc_url( \trailingslashit( get_home_url() ) . '@' . $this->get_username() );
166 122 }
167 123
168 - /**
169 - * Get the preferred username.
170 - *
171 - * @return string The preferred username.
172 - */
173 124 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;
125 + return \esc_attr( \get_the_author_meta( 'login', $this->_id ) );
182 126 }
183 127
184 - /**
185 - * Get the User icon.
186 - *
187 - * @return string[] The User icon.
188 - */
189 128 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 129 $icon = \esc_url(
199 130 \get_avatar_url(
200 131 $this->_id,
201 132 array( 'size' => 120 )
@@ -207,29 +138,14 @@
207 138 'url' => $icon,
208 139 );
209 140 }
210 141
211 - /**
212 - * Returns the header image.
213 - *
214 - * @return string[]|null The header image.
215 - */
216 142 public function get_image() {
217 - $header_image = get_user_option( 'activitypub_header_image', $this->_id );
218 - $image_url = null;
219 -
220 - if ( ! $header_image && \has_header_image() ) {
221 - $image_url = \get_header_image();
222 - }
223 -
224 - if ( $header_image ) {
225 - $image_url = \wp_get_attachment_url( $header_image );
226 - }
227 -
228 - if ( $image_url ) {
143 + if ( \has_header_image() ) {
144 + $image = \esc_url( \get_header_image() );
229 145 return array(
230 146 'type' => 'Image',
231 - 'url' => esc_url( $image_url ),
147 + 'url' => $image,
232 148 );
233 149 }
234 150
235 151 return null;
@@ -234,27 +150,17 @@
234 150
235 151 return null;
236 152 }
237 153
238 - /**
239 - * Returns the date the user was created.
240 - *
241 - * @return false|string The date the user was created.
242 - */
243 154 public function get_published() {
244 - return \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, \strtotime( \get_the_author_meta( 'registered', $this->_id ) ) );
155 + return \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( \get_the_author_meta( 'registered', $this->_id ) ) );
245 156 }
246 157
247 - /**
248 - * Returns the public key.
249 - *
250 - * @return string[] The public key.
251 - */
252 158 public function get_public_key() {
253 159 return array(
254 - 'id' => $this->get_id() . '#main-key',
255 - 'owner' => $this->get_id(),
256 - 'publicKeyPem' => Actors::get_public_key( $this->get__id() ),
160 + 'id' => $this->get_id() . '#main-key',
161 + 'owner' => $this->get_id(),
162 + 'publicKeyPem' => Signature::get_public_key_for( $this->get__id() ),
257 163 );
258 164 }
259 165
260 166 /**
@@ -262,9 +168,9 @@
262 168 *
263 169 * @return string The Inbox-Endpoint.
264 170 */
265 171 public function get_inbox() {
266 - return get_rest_url_by_path( sprintf( 'actors/%d/inbox', $this->get__id() ) );
172 + return get_rest_url_by_path( sprintf( 'users/%d/inbox', $this->get__id() ) );
267 173 }
268 174
269 175 /**
270 176 * Returns the Outbox-API-Endpoint.
@@ -271,9 +177,9 @@
271 177 *
272 178 * @return string The Outbox-Endpoint.
273 179 */
274 180 public function get_outbox() {
275 - return get_rest_url_by_path( sprintf( 'actors/%d/outbox', $this->get__id() ) );
181 + return get_rest_url_by_path( sprintf( 'users/%d/outbox', $this->get__id() ) );
276 182 }
277 183
278 184 /**
279 185 * Returns the Followers-API-Endpoint.
@@ -280,9 +186,9 @@
280 186 *
281 187 * @return string The Followers-Endpoint.
282 188 */
283 189 public function get_followers() {
284 - return get_rest_url_by_path( sprintf( 'actors/%d/followers', $this->get__id() ) );
190 + return get_rest_url_by_path( sprintf( 'users/%d/followers', $this->get__id() ) );
285 191 }
286 192
287 193 /**
288 194 * Returns the Following-API-Endpoint.
@@ -289,29 +195,18 @@
289 195 *
290 196 * @return string The Following-Endpoint.
291 197 */
292 198 public function get_following() {
293 - return get_rest_url_by_path( sprintf( 'actors/%d/following', $this->get__id() ) );
199 + return get_rest_url_by_path( sprintf( 'users/%d/following', $this->get__id() ) );
294 200 }
295 201
296 202 /**
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 203 * Returns the Featured-API-Endpoint.
309 204 *
310 205 * @return string The Featured-Endpoint.
311 206 */
312 207 public function get_featured() {
313 - return get_rest_url_by_path( sprintf( 'actors/%d/collections/featured', $this->get__id() ) );
208 + return get_rest_url_by_path( sprintf( 'users/%d/collections/featured', $this->get__id() ) );
314 209 }
315 210
316 211 /**
317 212 * Returns the Featured-Tags-API-Endpoint.
@@ -318,169 +213,63 @@
318 213 *
319 214 * @return string The Featured-Tags-Endpoint.
320 215 */
321 216 public function get_featured_tags() {
322 - return get_rest_url_by_path( sprintf( 'actors/%d/collections/tags', $this->get__id() ) );
217 + return get_rest_url_by_path( sprintf( 'users/%d/collections/tags', $this->get__id() ) );
323 218 }
324 219
325 220 /**
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 - );
339 - }
340 -
341 - /**
342 221 * Extend the User-Output with Attachments.
343 222 *
344 223 * @return array The extended User-Output.
345 224 */
346 225 public function get_attachment() {
347 - $extra_fields = Extra_Fields::get_actor_fields( $this->_id );
348 - return Extra_Fields::fields_to_attachments( $extra_fields );
349 - }
226 + $array = array();
350 227
351 - /**
352 - * Returns a user@domain type of identifier for the user.
353 - *
354 - * @return string The Webfinger-Identifier.
355 - */
356 - public function get_webfinger() {
357 - return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
358 - }
228 + $array[] = array(
229 + 'type' => 'PropertyValue',
230 + 'name' => \__( 'Blog', 'activitypub' ),
231 + 'value' => \html_entity_decode(
232 + '<a rel="me" title="' . \esc_attr( \home_url( '/' ) ) . '" target="_blank" href="' . \home_url( '/' ) . '">' . \wp_parse_url( \home_url( '/' ), \PHP_URL_HOST ) . '</a>',
233 + \ENT_QUOTES,
234 + 'UTF-8'
235 + ),
236 + );
359 237
360 - /**
361 - * Returns the canonical URL.
362 - *
363 - * @return string The canonical URL.
364 - */
365 - public function get_canonical_url() {
366 - return $this->get_url();
367 - }
368 -
369 - /**
370 - * Returns the streams.
371 - *
372 - * @return null The streams.
373 - */
374 - public function get_streams() {
375 - return null;
376 - }
377 -
378 - /**
379 - * Returns the tag.
380 - *
381 - * @return array The tag.
382 - */
383 - public function get_tag() {
384 - return array();
385 - }
386 -
387 - /**
388 - * Returns the indexable state.
389 - *
390 - * @return bool Whether the user is indexable.
391 - */
392 - public function get_indexable() {
393 - if ( is_blog_public() ) {
394 - return true;
395 - } else {
396 - 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,
238 + $array[] = array(
239 + 'type' => 'PropertyValue',
240 + 'name' => \__( 'Profile', 'activitypub' ),
241 + 'value' => \html_entity_decode(
242 + '<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>',
243 + \ENT_QUOTES,
244 + 'UTF-8'
245 + ),
410 246 );
411 - return \wp_update_user( $userdata );
412 - }
413 247
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;
248 + if ( \get_the_author_meta( 'user_url', $this->get__id() ) ) {
249 + $array[] = array(
250 + 'type' => 'PropertyValue',
251 + 'name' => \__( 'Website', 'activitypub' ),
252 + 'value' => \html_entity_decode(
253 + '<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>',
254 + \ENT_QUOTES,
255 + 'UTF-8'
256 + ),
257 + );
433 258 }
434 - return update_user_option( $this->_id, 'activitypub_icon', $value );
435 - }
436 259
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 );
260 + return $array;
448 261 }
449 262
450 263 /**
451 - * Returns the website hosts allowed to credit this blog.
264 + * Returns a user@domain type of identifier for the user.
452 265 *
453 - * @return string[]|null The attribution domains or null if not found.
266 + * @return string The Webfinger-Identifier.
454 267 */
455 - public function get_attribution_domains() {
456 - return get_attribution_domains();
268 + public function get_resource() {
269 + return $this->get_preferred_username() . '@' . \wp_parse_url( \home_url(), \PHP_URL_HOST );
457 270 }
458 271
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;
272 + public function get_canonical_url() {
273 + return $this->get_url();
485 274 }
486 275 }