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
activitypub / includes / class-activitypub.php

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

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