PluginProbe
ActivityPub / 7.0.0
ActivityPub v7.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 / class-activitypub.php

class-activitypub.php in ActivityPub 7.0.0, at includes/class-activitypub.php

869 lines 24.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ActivityPub Class.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub;
9
10 use Exception;
11 use Activitypub\Options;
12 use Activitypub\Collection\Actors;
13 use Activitypub\Collection\Outbox;
14 use Activitypub\Collection\Followers;
15 use Activitypub\Collection\Extra_Fields;
16
17 /**
18 * ActivityPub Class.
19 *
20 * @author Matthias Pfefferle
21 */
22 class Activitypub {
23 /**
24 * Initialize the class, registering WordPress hooks.
25 */
26 public static function init() {
27 \add_filter( 'template_include', array( self::class, 'render_activitypub_template' ), 99 );
28 \add_action( 'template_redirect', array( self::class, 'template_redirect' ) );
29 \add_filter( 'redirect_canonical', array( self::class, 'redirect_canonical' ), 10, 2 );
30 \add_filter( 'redirect_canonical', array( self::class, 'no_trailing_redirect' ), 10, 2 );
31 \add_filter( 'query_vars', array( self::class, 'add_query_vars' ) );
32 \add_filter( 'pre_get_avatar_data', array( self::class, 'pre_get_avatar_data' ), 11, 2 );
33
34 // Add support for ActivityPub to custom post types.
35 $post_types = \get_option( 'activitypub_support_post_types', array( 'post' ) );
36
37 foreach ( $post_types as $post_type ) {
38 \add_post_type_support( $post_type, 'activitypub' );
39 }
40
41 \add_action( 'wp_trash_post', array( self::class, 'trash_post' ), 1 );
42 \add_action( 'untrash_post', array( self::class, 'untrash_post' ), 1 );
43
44 \add_action( 'init', array( self::class, 'add_rewrite_rules' ), 11 );
45 \add_action( 'init', array( self::class, 'theme_compat' ), 11 );
46
47 \add_action( 'user_register', array( self::class, 'user_register' ) );
48
49 \add_filter( 'activitypub_get_actor_extra_fields', array( Extra_Fields::class, 'default_actor_extra_fields' ), 10, 2 );
50
51 \add_filter( 'add_post_metadata', array( self::class, 'prevent_empty_post_meta' ), 10, 4 );
52 \add_filter( 'update_post_metadata', array( self::class, 'prevent_empty_post_meta' ), 10, 4 );
53
54 \add_action( 'init', array( self::class, 'register_user_meta' ), 11 );
55
56 // Register several post_types.
57 self::register_post_types();
58
59 self::register_oembed_providers();
60 Embed::init();
61 }
62
63 /**
64 * Activation Hook.
65 */
66 public static function activate() {
67 self::flush_rewrite_rules();
68 Scheduler::register_schedules();
69
70 \add_filter( 'pre_wp_update_comment_count_now', array( Comment::class, 'pre_wp_update_comment_count_now' ), 10, 3 );
71 Migration::update_comment_counts();
72 }
73
74 /**
75 * Deactivation Hook.
76 */
77 public static function deactivate() {
78 self::flush_rewrite_rules();
79 Scheduler::deregister_schedules();
80
81 \remove_filter( 'pre_wp_update_comment_count_now', array( Comment::class, 'pre_wp_update_comment_count_now' ) );
82 Migration::update_comment_counts( 2000 );
83 }
84
85 /**
86 * Uninstall Hook.
87 */
88 public static function uninstall() {
89 Scheduler::deregister_schedules();
90
91 \remove_filter( 'pre_wp_update_comment_count_now', array( Comment::class, 'pre_wp_update_comment_count_now' ) );
92 Migration::update_comment_counts( 2000 );
93
94 Options::delete();
95 }
96
97 /**
98 * Return a AS2 JSON version of an author, post or page.
99 *
100 * @param string $template The path to the template object.
101 *
102 * @return string The new path to the JSON template.
103 */
104 public static function render_activitypub_template( $template ) {
105 if ( \wp_is_serving_rest_request() || \wp_doing_ajax() ) {
106 return $template;
107 }
108
109 self::add_headers();
110
111 if ( ! is_activitypub_request() || ! should_negotiate_content() ) {
112 if ( \get_query_var( 'p' ) && Outbox::POST_TYPE === \get_post_type( \get_query_var( 'p' ) ) ) {
113 \set_query_var( 'is_404', true );
114 \status_header( 406 );
115 }
116 return $template;
117 }
118
119 $activitypub_template = false;
120 $activitypub_object = Query::get_instance()->get_activitypub_object();
121
122 if ( $activitypub_object ) {
123 if ( \get_query_var( 'preview' ) ) {
124 \define( 'ACTIVITYPUB_PREVIEW', true );
125
126 /**
127 * Filter the template used for the ActivityPub preview.
128 *
129 * @param string $activitypub_template Absolute path to the template file.
130 */
131 $activitypub_template = apply_filters( 'activitypub_preview_template', ACTIVITYPUB_PLUGIN_DIR . '/templates/post-preview.php' );
132 } else {
133 $activitypub_template = ACTIVITYPUB_PLUGIN_DIR . 'templates/activitypub-json.php';
134 }
135 }
136
137 /*
138 * Check if the request is authorized.
139 *
140 * @see https://www.w3.org/wiki/SocialCG/ActivityPub/Primer/Authentication_Authorization#Authorized_fetch
141 * @see https://swicg.github.io/activitypub-http-signature/#authorized-fetch
142 */
143 if ( $activitypub_template && use_authorized_fetch() ) {
144 $verification = Signature::verify_http_signature( $_SERVER );
145 if ( \is_wp_error( $verification ) ) {
146 \status_header( 401 );
147
148 // Fallback as template_loader can't return http headers.
149 return $template;
150 }
151 }
152
153 if ( $activitypub_template ) {
154 \set_query_var( 'is_404', false );
155
156 // Check if header already sent.
157 if ( ! \headers_sent() ) {
158 // Send 200 status header.
159 \status_header( 200 );
160 }
161
162 return $activitypub_template;
163 }
164
165 return $template;
166 }
167
168 /**
169 * Add the 'self' link to the header.
170 */
171 public static function add_headers() {
172 $id = Query::get_instance()->get_activitypub_object_id();
173
174 if ( ! $id ) {
175 return;
176 }
177
178 if ( ! headers_sent() ) {
179 \header( 'Link: <' . esc_url( $id ) . '>; title="ActivityPub (JSON)"; rel="alternate"; type="application/activity+json"', false );
180
181 if ( \get_option( 'activitypub_vary_header', '1' ) ) {
182 // Send Vary header for Accept header.
183 \header( 'Vary: Accept', false );
184 }
185 }
186
187 add_action(
188 'wp_head',
189 function () use ( $id ) {
190 echo PHP_EOL . '<link rel="alternate" title="ActivityPub (JSON)" type="application/activity+json" href="' . esc_url( $id ) . '" />' . PHP_EOL;
191 }
192 );
193 }
194
195 /**
196 * Remove trailing slash from ActivityPub @username requests.
197 *
198 * @param string $redirect_url The URL to redirect to.
199 * @param string $requested_url The requested URL.
200 *
201 * @return string $redirect_url The possibly-unslashed redirect URL.
202 */
203 public static function no_trailing_redirect( $redirect_url, $requested_url ) {
204 if ( get_query_var( 'actor' ) ) {
205 return $requested_url;
206 }
207
208 return $redirect_url;
209 }
210
211 /**
212 * Add support for `p` and `author` query vars.
213 *
214 * @param string $redirect_url The URL to redirect to.
215 * @param string $requested_url The requested URL.
216 *
217 * @return string $redirect_url
218 */
219 public static function redirect_canonical( $redirect_url, $requested_url ) {
220 if ( ! is_activitypub_request() ) {
221 return $redirect_url;
222 }
223
224 $query = \wp_parse_url( $requested_url, PHP_URL_QUERY );
225
226 if ( ! $query ) {
227 return $redirect_url;
228 }
229
230 $query_params = \wp_parse_args( $query );
231 unset( $query_params['activitypub'] );
232
233 if ( 1 !== count( $query_params ) ) {
234 return $redirect_url;
235 }
236
237 if ( isset( $query_params['p'] ) ) {
238 return null;
239 }
240
241 if ( isset( $query_params['author'] ) ) {
242 return null;
243 }
244
245 return $requested_url;
246 }
247
248 /**
249 * Custom redirects for ActivityPub requests.
250 *
251 * @return void
252 */
253 public static function template_redirect() {
254 global $wp_query;
255
256 $comment_id = get_query_var( 'c', null );
257
258 // Check if it seems to be a comment.
259 if ( $comment_id ) {
260 $comment = get_comment( $comment_id );
261
262 // Load a 404-page if `c` is set but not valid.
263 if ( ! $comment ) {
264 $wp_query->set_404();
265 return;
266 }
267
268 // Stop if it's not an ActivityPub comment.
269 if ( is_activitypub_request() && ! is_local_comment( $comment ) ) {
270 return;
271 }
272
273 wp_safe_redirect( get_comment_link( $comment ) );
274 exit;
275 }
276
277 $actor = get_query_var( 'actor', null );
278 if ( $actor ) {
279 $actor = Actors::get_by_username( $actor );
280 if ( ! $actor || \is_wp_error( $actor ) ) {
281 $wp_query->set_404();
282 return;
283 }
284
285 if ( is_activitypub_request() ) {
286 return;
287 }
288
289 if ( $actor->get__id() > 0 ) {
290 $redirect_url = $actor->get_url();
291 } else {
292 $redirect_url = get_bloginfo( 'url' );
293 }
294
295 wp_safe_redirect( $redirect_url, 301 );
296 exit;
297 }
298 }
299
300 /**
301 * Add the 'activitypub' query variable so WordPress won't mangle it.
302 *
303 * @param array $vars The query variables.
304 *
305 * @return array The query variables.
306 */
307 public static function add_query_vars( $vars ) {
308 $vars[] = 'activitypub';
309 $vars[] = 'preview';
310 $vars[] = 'author';
311 $vars[] = 'actor';
312 $vars[] = 'type';
313 $vars[] = 'c';
314 $vars[] = 'p';
315
316 return $vars;
317 }
318
319 /**
320 * Replaces the default avatar.
321 *
322 * @param array $args Arguments passed to get_avatar_data(), after processing.
323 * @param int|string|object $id_or_email A user ID, email address, or comment object.
324 *
325 * @return array $args
326 */
327 public static function pre_get_avatar_data( $args, $id_or_email ) {
328 if (
329 ! $id_or_email instanceof \WP_Comment ||
330 ! isset( $id_or_email->comment_type ) ||
331 $id_or_email->user_id
332 ) {
333 return $args;
334 }
335
336 /**
337 * Filter allowed comment types for avatars.
338 *
339 * @param array $allowed_comment_types Array of allowed comment types.
340 */
341 $allowed_comment_types = \apply_filters( 'get_avatar_comment_types', array( 'comment' ) );
342 if ( ! \in_array( $id_or_email->comment_type ?: 'comment', $allowed_comment_types, true ) ) { // phpcs:ignore Universal.Operators.DisallowShortTernary
343 return $args;
344 }
345
346 // Check if comment has an avatar.
347 $avatar = \get_comment_meta( $id_or_email->comment_ID, 'avatar_url', true );
348
349 if ( $avatar ) {
350 if ( empty( $args['class'] ) ) {
351 $args['class'] = array();
352 } elseif ( \is_string( $args['class'] ) ) {
353 $args['class'] = \explode( ' ', $args['class'] );
354 }
355
356 /** This filter is documented in wp-includes/link-template.php */
357 $args['url'] = \apply_filters( 'get_avatar_url', $avatar, $id_or_email, $args );
358 $args['class'][] = 'avatar-activitypub';
359 $args['class'][] = 'u-photo';
360 $args['class'] = \array_unique( $args['class'] );
361 }
362
363 return $args;
364 }
365
366 /**
367 * Store permalink in meta, to send delete Activity.
368 *
369 * @param string $post_id The Post ID.
370 */
371 public static function trash_post( $post_id ) {
372 \add_post_meta(
373 $post_id,
374 '_activitypub_canonical_url',
375 \get_permalink( $post_id ),
376 true
377 );
378 }
379
380 /**
381 * Delete permalink from meta.
382 *
383 * @param string $post_id The Post ID.
384 */
385 public static function untrash_post( $post_id ) {
386 \delete_post_meta( $post_id, '_activitypub_canonical_url' );
387 }
388
389 /**
390 * Add rewrite rules.
391 */
392 public static function add_rewrite_rules() {
393 /*
394 * If another system needs to take precedence over the ActivityPub rewrite rules,
395 * they can define their own and will manually call the appropriate functions as required.
396 */
397 if ( ACTIVITYPUB_DISABLE_REWRITES ) {
398 return;
399 }
400
401 if ( ! \class_exists( 'Webfinger' ) ) {
402 \add_rewrite_rule(
403 '^.well-known/webfinger',
404 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/webfinger',
405 'top'
406 );
407 }
408
409 if ( ! \class_exists( 'Nodeinfo_Endpoint' ) && true === (bool) \get_option( 'blog_public', 1 ) ) {
410 \add_rewrite_rule(
411 '^.well-known/nodeinfo',
412 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo',
413 'top'
414 );
415 }
416
417 \add_rewrite_rule( '^@([\w\-\.]+)\/?$', 'index.php?actor=$matches[1]', 'top' );
418 \add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES );
419 }
420
421 /**
422 * Flush rewrite rules.
423 */
424 public static function flush_rewrite_rules() {
425 self::add_rewrite_rules();
426 \flush_rewrite_rules();
427 }
428
429 /**
430 * Theme compatibility stuff.
431 */
432 public static function theme_compat() {
433 // We assume that you want to use Post-Formats when enabling the setting.
434 if ( 'wordpress-post-format' === \get_option( 'activitypub_object_type', ACTIVITYPUB_DEFAULT_OBJECT_TYPE ) ) {
435 if ( ! get_theme_support( 'post-formats' ) ) {
436 // Add support for the Aside, Gallery Post Formats...
437 add_theme_support(
438 'post-formats',
439 array(
440 'gallery',
441 'status',
442 'image',
443 'video',
444 'audio',
445 )
446 );
447 }
448 }
449 }
450
451 /**
452 * Register Custom Post Types.
453 */
454 private static function register_post_types() {
455 \register_post_type(
456 Actors::POST_TYPE,
457 array(
458 'labels' => array(
459 'name' => _x( 'Followers', 'post_type plural name', 'activitypub' ),
460 'singular_name' => _x( 'Follower', 'post_type single name', 'activitypub' ),
461 ),
462 'public' => false,
463 'hierarchical' => false,
464 'rewrite' => false,
465 'query_var' => false,
466 'delete_with_user' => false,
467 'can_export' => true,
468 'supports' => array(),
469 )
470 );
471
472 \register_post_meta(
473 Actors::POST_TYPE,
474 '_activitypub_inbox',
475 array(
476 'type' => 'string',
477 'single' => true,
478 'sanitize_callback' => 'sanitize_url',
479 )
480 );
481
482 \register_post_meta(
483 Actors::POST_TYPE,
484 '_activitypub_errors',
485 array(
486 'type' => 'string',
487 'single' => false,
488 'sanitize_callback' => function ( $value ) {
489 if ( ! is_string( $value ) ) {
490 throw new Exception( 'Error message is no valid string' );
491 }
492
493 return esc_sql( $value );
494 },
495 )
496 );
497
498 \register_post_meta(
499 Actors::POST_TYPE,
500 Followers::FOLLOWER_META_KEY,
501 array(
502 'type' => 'string',
503 'single' => false,
504 'sanitize_callback' => function ( $value ) {
505 return esc_sql( $value );
506 },
507 )
508 );
509
510 // Register Outbox Post-Type.
511 register_post_type(
512 Outbox::POST_TYPE,
513 array(
514 'labels' => array(
515 'name' => _x( 'Outbox', 'post_type plural name', 'activitypub' ),
516 'singular_name' => _x( 'Outbox Item', 'post_type single name', 'activitypub' ),
517 ),
518 'capabilities' => array(
519 'create_posts' => false,
520 ),
521 'map_meta_cap' => true,
522 'public' => false,
523 'show_in_rest' => true,
524 'rewrite' => false,
525 'query_var' => false,
526 'supports' => array( 'title', 'editor', 'author', 'custom-fields' ),
527 'delete_with_user' => true,
528 'can_export' => true,
529 'exclude_from_search' => true,
530 )
531 );
532
533 /**
534 * Register Activity Type meta for Outbox items.
535 *
536 * @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
537 */
538 \register_post_meta(
539 Outbox::POST_TYPE,
540 '_activitypub_activity_type',
541 array(
542 'type' => 'string',
543 'description' => 'The type of the activity',
544 'single' => true,
545 'show_in_rest' => true,
546 'sanitize_callback' => function ( $value ) {
547 $value = ucfirst( strtolower( $value ) );
548 $schema = array(
549 'type' => 'string',
550 'enum' => array( 'Accept', 'Add', 'Announce', 'Arrive', 'Block', 'Create', 'Delete', 'Dislike', 'Flag', 'Follow', 'Ignore', 'Invite', 'Join', 'Leave', 'Like', 'Listen', 'Move', 'Offer', 'Question', 'Reject', 'Read', 'Remove', 'TentativeReject', 'TentativeAccept', 'Travel', 'Undo', 'Update', 'View' ),
551 'default' => 'Announce',
552 );
553
554 if ( is_wp_error( rest_validate_enum( $value, $schema, '' ) ) ) {
555 return $schema['default'];
556 }
557
558 return $value;
559 },
560 )
561 );
562
563 \register_post_meta(
564 Outbox::POST_TYPE,
565 '_activitypub_activity_actor',
566 array(
567 'type' => 'string',
568 'single' => true,
569 'show_in_rest' => true,
570 'sanitize_callback' => function ( $value ) {
571 $schema = array(
572 'type' => 'string',
573 'enum' => array( 'application', 'blog', 'user' ),
574 'default' => 'user',
575 );
576
577 if ( is_wp_error( rest_validate_enum( $value, $schema, '' ) ) ) {
578 return $schema['default'];
579 }
580
581 return $value;
582 },
583 )
584 );
585
586 \register_post_meta(
587 Outbox::POST_TYPE,
588 '_activitypub_outbox_offset',
589 array(
590 'type' => 'integer',
591 'single' => true,
592 'description' => 'Keeps track of the followers offset when processing outbox items.',
593 'sanitize_callback' => 'absint',
594 'default' => 0,
595 )
596 );
597
598 \register_post_meta(
599 Outbox::POST_TYPE,
600 '_activitypub_object_id',
601 array(
602 'type' => 'string',
603 'single' => true,
604 'description' => 'The ID (ActivityPub URI) of the object that the outbox item is about.',
605 'sanitize_callback' => 'sanitize_url',
606 )
607 );
608
609 \register_post_meta(
610 Outbox::POST_TYPE,
611 'activitypub_content_visibility',
612 array(
613 'type' => 'string',
614 'single' => true,
615 'show_in_rest' => true,
616 'sanitize_callback' => function ( $value ) {
617 $schema = array(
618 'type' => 'string',
619 'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ),
620 'default' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
621 );
622
623 if ( is_wp_error( rest_validate_enum( $value, $schema, '' ) ) ) {
624 return $schema['default'];
625 }
626
627 return $value;
628 },
629 )
630 );
631
632 // Both User and Blog Extra Fields types have the same args.
633 $args = array(
634 'labels' => array(
635 'name' => _x( 'Extra fields', 'post_type plural name', 'activitypub' ),
636 'singular_name' => _x( 'Extra field', 'post_type single name', 'activitypub' ),
637 'add_new' => __( 'Add new', 'activitypub' ),
638 'add_new_item' => __( 'Add new extra field', 'activitypub' ),
639 'new_item' => __( 'New extra field', 'activitypub' ),
640 'edit_item' => __( 'Edit extra field', 'activitypub' ),
641 'view_item' => __( 'View extra field', 'activitypub' ),
642 'all_items' => __( 'All extra fields', 'activitypub' ),
643 ),
644 'public' => false,
645 'hierarchical' => false,
646 'query_var' => false,
647 'has_archive' => false,
648 'publicly_queryable' => false,
649 'show_in_menu' => false,
650 'delete_with_user' => true,
651 'can_export' => true,
652 'exclude_from_search' => true,
653 'show_in_rest' => true,
654 'map_meta_cap' => true,
655 'show_ui' => true,
656 'supports' => array( 'title', 'editor', 'page-attributes' ),
657 );
658
659 \register_post_type( Extra_Fields::USER_POST_TYPE, $args );
660 \register_post_type( Extra_Fields::BLOG_POST_TYPE, $args );
661
662 /**
663 * Fires after ActivityPub custom post types have been registered.
664 */
665 \do_action( 'activitypub_after_register_post_type' );
666 }
667
668 /**
669 * Add the 'activitypub' capability to users who can publish posts.
670 *
671 * @param int $user_id User ID.
672 */
673 public static function user_register( $user_id ) {
674 if ( \user_can( $user_id, 'publish_posts' ) ) {
675 $user = \get_user_by( 'id', $user_id );
676 $user->add_cap( 'activitypub' );
677 }
678 }
679
680 /**
681 * Prevent empty or default meta values.
682 *
683 * @param null|bool $check Whether to allow updating metadata for the given type.
684 * @param int $object_id ID of the object metadata is for.
685 * @param string $meta_key Metadata key.
686 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
687 */
688 public static function prevent_empty_post_meta( $check, $object_id, $meta_key, $meta_value ) {
689 $post_metas = array(
690 'activitypub_content_visibility' => '',
691 'activitypub_content_warning' => '',
692 'activitypub_max_image_attachments' => (string) \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ),
693 );
694
695 if ( isset( $post_metas[ $meta_key ] ) && $post_metas[ $meta_key ] === (string) $meta_value ) {
696 if ( 'update_post_metadata' === current_action() ) {
697 \delete_post_meta( $object_id, $meta_key );
698 }
699
700 $check = true;
701 }
702
703 return $check;
704 }
705
706 /**
707 * Register some Mastodon oEmbed providers.
708 */
709 public static function register_oembed_providers() {
710 \wp_oembed_add_provider( '#https?://mastodon\.social/(@.+)/([0-9]+)#i', 'https://mastodon.social/api/oembed', true );
711 \wp_oembed_add_provider( '#https?://mastodon\.online/(@.+)/([0-9]+)#i', 'https://mastodon.online/api/oembed', true );
712 \wp_oembed_add_provider( '#https?://mastodon\.cloud/(@.+)/([0-9]+)#i', 'https://mastodon.cloud/api/oembed', true );
713 \wp_oembed_add_provider( '#https?://mstdn\.social/(@.+)/([0-9]+)#i', 'https://mstdn.social/api/oembed', true );
714 \wp_oembed_add_provider( '#https?://mastodon\.world/(@.+)/([0-9]+)#i', 'https://mastodon.world/api/oembed', true );
715 \wp_oembed_add_provider( '#https?://mas\.to/(@.+)/([0-9]+)#i', 'https://mas.to/api/oembed', true );
716 }
717
718 /**
719 * Register user meta.
720 */
721 public static function register_user_meta() {
722 $blog_prefix = $GLOBALS['wpdb']->get_blog_prefix();
723
724 \register_meta(
725 'user',
726 $blog_prefix . 'activitypub_also_known_as',
727 array(
728 'type' => 'array',
729 'description' => 'An array of URLs that the user is known by.',
730 'single' => true,
731 'default' => array(),
732 'sanitize_callback' => array( Sanitize::class, 'url_list' ),
733 )
734 );
735
736 \register_meta(
737 'user',
738 $blog_prefix . 'activitypub_old_host_data',
739 array(
740 'description' => 'Actor object for the user on the old host.',
741 'single' => true,
742 )
743 );
744
745 \register_meta(
746 'user',
747 $blog_prefix . 'activitypub_moved_to',
748 array(
749 'type' => 'string',
750 'description' => 'The new URL of the user.',
751 'single' => true,
752 'sanitize_callback' => 'sanitize_url',
753 )
754 );
755
756 \register_meta(
757 'user',
758 $blog_prefix . 'activitypub_description',
759 array(
760 'type' => 'string',
761 'description' => 'The user’s description.',
762 'single' => true,
763 'default' => '',
764 'sanitize_callback' => function ( $value ) {
765 return wp_kses( $value, 'user_description' );
766 },
767 )
768 );
769
770 \register_meta(
771 'user',
772 $blog_prefix . 'activitypub_icon',
773 array(
774 'type' => 'integer',
775 'description' => 'The attachment ID for user’s profile image.',
776 'single' => true,
777 'default' => 0,
778 'sanitize_callback' => 'absint',
779 )
780 );
781
782 \register_meta(
783 'user',
784 $blog_prefix . 'activitypub_header_image',
785 array(
786 'type' => 'integer',
787 'description' => 'The attachment ID for the user’s header image.',
788 'single' => true,
789 'default' => 0,
790 'sanitize_callback' => 'absint',
791 )
792 );
793
794 \register_meta(
795 'user',
796 $blog_prefix . 'activitypub_mailer_new_dm',
797 array(
798 'type' => 'integer',
799 'description' => 'Send a notification when someone sends this user a direct message.',
800 'single' => true,
801 'sanitize_callback' => 'absint',
802 )
803 );
804 \add_filter( 'get_user_option_activitypub_mailer_new_dm', array( self::class, 'user_options_default' ) );
805
806 \register_meta(
807 'user',
808 $blog_prefix . 'activitypub_mailer_new_follower',
809 array(
810 'type' => 'integer',
811 'description' => 'Send a notification when someone starts to follow this user.',
812 'single' => true,
813 'sanitize_callback' => 'absint',
814 )
815 );
816 \add_filter( 'get_user_option_activitypub_mailer_new_follower', array( self::class, 'user_options_default' ) );
817
818 \register_meta(
819 'user',
820 $blog_prefix . 'activitypub_mailer_new_mention',
821 array(
822 'type' => 'integer',
823 'description' => 'Send a notification when someone mentions this user.',
824 'single' => true,
825 'sanitize_callback' => 'absint',
826 )
827 );
828 \add_filter( 'get_user_option_activitypub_mailer_new_mention', array( self::class, 'user_options_default' ) );
829
830 \register_meta(
831 'user',
832 'activitypub_show_welcome_tab',
833 array(
834 'type' => 'integer',
835 'description' => 'Whether to show the welcome tab.',
836 'single' => true,
837 'default' => 1,
838 'sanitize_callback' => 'absint',
839 )
840 );
841
842 \register_meta(
843 'user',
844 'activitypub_show_advanced_tab',
845 array(
846 'type' => 'integer',
847 'description' => 'Whether to show the advanced tab.',
848 'single' => true,
849 'default' => 0,
850 'sanitize_callback' => 'absint',
851 )
852 );
853 }
854
855 /**
856 * Set default values for user options.
857 *
858 * @param bool|string $value Option value.
859 * @return bool|string
860 */
861 public static function user_options_default( $value ) {
862 if ( false === $value ) {
863 return '1';
864 }
865
866 return $value;
867 }
868 }
869