PluginProbe
ActivityPub / 5.3.1
ActivityPub v5.3.1
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/class-activitypub.php +413 -111 2.1.05.3.1 View file →
@@ -1,20 +1,21 @@
1 1 <?php
2 +/**
3 + * ActivityPub Class.
4 + *
5 + * @package Activitypub
6 + */
7 +
2 8 namespace Activitypub;
3 9
4 10 use Exception;
5 -use Activitypub\Signature;
6 -use Activitypub\Collection\Users;
11 +use Activitypub\Transformer\Factory;
12 +use Activitypub\Collection\Outbox;
7 13 use Activitypub\Collection\Followers;
14 +use Activitypub\Collection\Extra_Fields;
8 15
9 -use function Activitypub\is_comment;
10 -use function Activitypub\sanitize_url;
11 -use function Activitypub\is_local_comment;
12 -use function Activitypub\is_activitypub_request;
13 -use function Activitypub\should_comment_be_federated;
14 -
15 16 /**
16 - * ActivityPub Class
17 + * ActivityPub Class.
17 18 *
18 19 * @author Matthias Pfefferle
19 20 */
20 21 class Activitypub {
@@ -21,15 +22,16 @@
21 22 /**
22 23 * Initialize the class, registering WordPress hooks.
23 24 */
24 25 public static function init() {
25 - \add_filter( 'template_include', array( self::class, 'render_json_template' ), 99 );
26 + \add_filter( 'template_include', array( self::class, 'render_activitypub_template' ), 99 );
26 27 \add_action( 'template_redirect', array( self::class, 'template_redirect' ) );
28 + \add_filter( 'redirect_canonical', array( self::class, 'redirect_canonical' ), 10, 2 );
27 29 \add_filter( 'query_vars', array( self::class, 'add_query_vars' ) );
28 30 \add_filter( 'pre_get_avatar_data', array( self::class, 'pre_get_avatar_data' ), 11, 2 );
29 31
30 - // Add support for ActivityPub to custom post types
31 - $post_types = \get_option( 'activitypub_support_post_types', array( 'post' ) ) ? \get_option( 'activitypub_support_post_types', array( 'post' ) ) : array();
32 + // Add support for ActivityPub to custom post types.
33 + $post_types = \get_option( 'activitypub_support_post_types', array( 'post' ) );
32 34
33 35 foreach ( $post_types as $post_type ) {
34 36 \add_post_type_support( $post_type, 'activitypub' );
35 37 }
@@ -37,44 +39,84 @@
37 39 \add_action( 'wp_trash_post', array( self::class, 'trash_post' ), 1 );
38 40 \add_action( 'untrash_post', array( self::class, 'untrash_post' ), 1 );
39 41
40 42 \add_action( 'init', array( self::class, 'add_rewrite_rules' ), 11 );
43 + \add_action( 'init', array( self::class, 'theme_compat' ), 11 );
41 44
42 - \add_action( 'after_setup_theme', array( self::class, 'theme_compat' ), 99 );
45 + \add_action( 'user_register', array( self::class, 'user_register' ) );
43 46
44 47 \add_action( 'in_plugin_update_message-' . ACTIVITYPUB_PLUGIN_BASENAME, array( self::class, 'plugin_update_message' ) );
45 48
46 - // register several post_types
49 + if ( site_supports_blocks() ) {
50 + \add_action( 'tool_box', array( self::class, 'tool_box' ) );
51 + }
52 +
53 + \add_filter( 'activitypub_get_actor_extra_fields', array( Extra_Fields::class, 'default_actor_extra_fields' ), 10, 2 );
54 +
55 + \add_action( 'updated_postmeta', array( self::class, 'updated_postmeta' ), 10, 4 );
56 + \add_action( 'added_post_meta', array( self::class, 'updated_postmeta' ), 10, 4 );
57 +
58 + // Register several post_types.
47 59 self::register_post_types();
48 60 }
49 61
50 62 /**
51 - * Activation Hook
52 - *
53 - * @return void
63 + * Activation Hook.
54 64 */
55 65 public static function activate() {
56 66 self::flush_rewrite_rules();
57 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();
58 71 }
59 72
60 73 /**
61 - * Deactivation Hook
62 - *
63 - * @return void
74 + * Deactivation Hook.
64 75 */
65 76 public static function deactivate() {
66 77 self::flush_rewrite_rules();
67 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 );
68 82 }
69 83
70 84 /**
71 - * Uninstall Hook
72 - *
73 - * @return void
85 + * Uninstall Hook.
74 86 */
75 87 public static function uninstall() {
76 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_attribution_domains' );
95 + delete_option( 'activitypub_authorized_fetch' );
96 + delete_option( 'activitypub_application_user_private_key' );
97 + delete_option( 'activitypub_application_user_public_key' );
98 + delete_option( 'activitypub_blog_user_private_key' );
99 + delete_option( 'activitypub_blog_user_public_key' );
100 + delete_option( 'activitypub_blog_description' );
101 + delete_option( 'activitypub_blog_identifier' );
102 + delete_option( 'activitypub_custom_post_content' );
103 + delete_option( 'activitypub_db_version' );
104 + delete_option( 'activitypub_default_extra_fields' );
105 + delete_option( 'activitypub_enable_blog_user' );
106 + delete_option( 'activitypub_enable_users' );
107 + delete_option( 'activitypub_header_image' );
108 + delete_option( 'activitypub_last_post_with_permalink_as_id' );
109 + delete_option( 'activitypub_mailer_new_follower' );
110 + delete_option( 'activitypub_mailer_new_dm' );
111 + delete_option( 'activitypub_max_image_attachments' );
112 + delete_option( 'activitypub_migration_lock' );
113 + delete_option( 'activitypub_object_type' );
114 + delete_option( 'activitypub_outbox_purge_days' );
115 + delete_option( 'activitypub_support_post_types' );
116 + delete_option( 'activitypub_use_hashtags' );
117 + delete_option( 'activitypub_use_opengraph' );
118 + delete_option( 'activitypub_use_permalink_as_id_for_blog' );
77 119 }
78 120
79 121 /**
80 122 * Return a AS2 JSON version of an author, post or page.
@@ -82,51 +124,133 @@
82 124 * @param string $template The path to the template object.
83 125 *
84 126 * @return string The new path to the JSON template.
85 127 */
86 - public static function render_json_template( $template ) {
128 + public static function render_activitypub_template( $template ) {
87 129 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
88 130 return $template;
89 131 }
90 132
133 + self::add_headers();
134 +
91 135 if ( ! is_activitypub_request() ) {
92 136 return $template;
93 137 }
94 138
95 - $json_template = false;
139 + $activitypub_template = false;
140 + $activitypub_object = Query::get_instance()->get_activitypub_object();
96 141
97 - // check if user can publish posts
98 - if ( \is_author() && is_wp_error( Users::get_by_id( \get_the_author_meta( 'ID' ) ) ) ) {
99 - return $template;
142 + if ( $activitypub_object ) {
143 + if ( \get_query_var( 'preview' ) ) {
144 + \define( 'ACTIVITYPUB_PREVIEW', true );
145 +
146 + /**
147 + * Filter the template used for the ActivityPub preview.
148 + *
149 + * @param string $activitypub_template Absolute path to the template file.
150 + */
151 + $activitypub_template = apply_filters( 'activitypub_preview_template', ACTIVITYPUB_PLUGIN_DIR . '/templates/post-preview.php' );
152 + } else {
153 + $activitypub_template = ACTIVITYPUB_PLUGIN_DIR . 'templates/activitypub-json.php';
154 + }
100 155 }
101 156
102 - // check if blog-user is enabled
103 - if ( \is_home() && is_wp_error( Users::get_by_id( Users::BLOG_USER_ID ) ) ) {
104 - return $template;
157 + /*
158 + * Check if the request is authorized.
159 + *
160 + * @see https://www.w3.org/wiki/SocialCG/ActivityPub/Primer/Authentication_Authorization#Authorized_fetch
161 + * @see https://swicg.github.io/activitypub-http-signature/#authorized-fetch
162 + */
163 + if ( $activitypub_template && use_authorized_fetch() ) {
164 + $verification = Signature::verify_http_signature( $_SERVER );
165 + if ( \is_wp_error( $verification ) ) {
166 + header( 'HTTP/1.1 401 Unauthorized' );
167 +
168 + // Fallback as template_loader can't return http headers.
169 + return $template;
170 + }
105 171 }
106 172
107 - if ( \is_author() ) {
108 - $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/author-json.php';
109 - } elseif ( is_comment() ) {
110 - $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/comment-json.php';
111 - } elseif ( \is_singular() ) {
112 - $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
113 - } elseif ( \is_home() ) {
114 - $json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/blog-json.php';
173 + if ( $activitypub_template ) {
174 + \set_query_var( 'is_404', false );
175 +
176 + // Check if header already sent.
177 + if ( ! \headers_sent() ) {
178 + // Send 200 status header.
179 + \status_header( 200 );
180 + }
181 +
182 + return $activitypub_template;
115 183 }
116 184
117 - if ( ACTIVITYPUB_AUTHORIZED_FETCH ) {
118 - $verification = Signature::verify_http_signature( $_SERVER );
119 - if ( \is_wp_error( $verification ) ) {
120 - // fallback as template_loader can't return http headers
121 - return $template;
185 + return $template;
186 + }
187 +
188 + /**
189 + * Add the 'self' link to the header.
190 + */
191 + public static function add_headers() {
192 + $id = Query::get_instance()->get_activitypub_object_id();
193 +
194 + if ( ! $id ) {
195 + return;
196 + }
197 +
198 + if ( ! headers_sent() ) {
199 + \header( 'Link: <' . esc_url( $id ) . '>; title="ActivityPub (JSON)"; rel="alternate"; type="application/activity+json"', false );
200 +
201 + if ( ACTIVITYPUB_SEND_VARY_HEADER ) {
202 + // Send Vary header for Accept header.
203 + \header( 'Vary: Accept', false );
122 204 }
123 205 }
124 206
125 - return $json_template;
207 + add_action(
208 + 'wp_head',
209 + function () use ( $id ) {
210 + echo PHP_EOL . '<link rel="alternate" title="ActivityPub (JSON)" type="application/activity+json" href="' . esc_url( $id ) . '" />' . PHP_EOL;
211 + }
212 + );
126 213 }
127 214
128 215 /**
216 + * Add support for `p` and `author` query vars.
217 + *
218 + * @param string $redirect_url The URL to redirect to.
219 + * @param string $requested_url The requested URL.
220 + *
221 + * @return string $redirect_url
222 + */
223 + public static function redirect_canonical( $redirect_url, $requested_url ) {
224 + if ( ! is_activitypub_request() ) {
225 + return $redirect_url;
226 + }
227 +
228 + $query = \wp_parse_url( $requested_url, PHP_URL_QUERY );
229 +
230 + if ( ! $query ) {
231 + return $redirect_url;
232 + }
233 +
234 + $query_params = \wp_parse_args( $query );
235 + unset( $query_params['activitypub'] );
236 +
237 + if ( 1 !== count( $query_params ) ) {
238 + return $redirect_url;
239 + }
240 +
241 + if ( isset( $query_params['p'] ) ) {
242 + return null;
243 + }
244 +
245 + if ( isset( $query_params['author'] ) ) {
246 + return null;
247 + }
248 +
249 + return $requested_url;
250 + }
251 +
252 + /**
129 253 * Custom redirects for ActivityPub requests.
130 254 *
131 255 * @return void
132 256 */
@@ -132,9 +256,9 @@
132 256 */
133 257 public static function template_redirect() {
134 258 $comment_id = get_query_var( 'c', null );
135 259
136 - // check if it seems to be a comment
260 + // Check if it seems to be a comment.
137 261 if ( ! $comment_id ) {
138 262 return;
139 263 }
140 264
@@ -139,9 +263,9 @@
139 263 }
140 264
141 265 $comment = get_comment( $comment_id );
142 266
143 - // load a 404 page if `c` is set but not valid
267 + // Load a 404 page if `c` is set but not valid.
144 268 if ( ! $comment ) {
145 269 global $wp_query;
146 270 $wp_query->set_404();
147 271 return;
@@ -146,9 +270,9 @@
146 270 $wp_query->set_404();
147 271 return;
148 272 }
149 273
150 - // stop if it's not an ActivityPub comment
274 + // Stop if it's not an ActivityPub comment.
151 275 if ( is_activitypub_request() && ! is_local_comment( $comment ) ) {
152 276 return;
153 277 }
154 278
@@ -157,11 +281,17 @@
157 281 }
158 282
159 283 /**
160 284 * Add the 'activitypub' query variable so WordPress won't mangle it.
285 + *
286 + * @param array $vars The query variables.
287 + *
288 + * @return array The query variables.
161 289 */
162 290 public static function add_query_vars( $vars ) {
163 291 $vars[] = 'activitypub';
292 + $vars[] = 'preview';
293 + $vars[] = 'author';
164 294 $vars[] = 'c';
165 295 $vars[] = 'p';
166 296
167 297 return $vars;
@@ -201,16 +331,18 @@
201 331 // Check if comment has an avatar.
202 332 $avatar = self::get_avatar_url( $id_or_email->comment_ID );
203 333
204 334 if ( $avatar ) {
205 - if ( ! isset( $args['class'] ) || ! \is_array( $args['class'] ) ) {
206 - $args['class'] = array( 'u-photo' );
207 - } else {
208 - $args['class'][] = 'u-photo';
209 - $args['class'] = \array_unique( $args['class'] );
335 + if ( empty( $args['class'] ) ) {
336 + $args['class'] = array();
337 + } elseif ( \is_string( $args['class'] ) ) {
338 + $args['class'] = \explode( ' ', $args['class'] );
210 339 }
340 +
211 341 $args['url'] = $avatar;
212 342 $args['class'][] = 'avatar-activitypub';
343 + $args['class'][] = 'u-photo';
344 + $args['class'] = \array_unique( $args['class'] );
213 345 }
214 346
215 347 return $args;
216 348 }
@@ -217,11 +349,11 @@
217 349
218 350 /**
219 351 * Function to retrieve Avatar URL if stored in meta.
220 352 *
221 - * @param int|WP_Comment $comment
353 + * @param int|\WP_Comment $comment The comment ID or object.
222 354 *
223 - * @return string $url
355 + * @return string The Avatar URL.
224 356 */
225 357 public static function get_avatar_url( $comment ) {
226 358 if ( \is_numeric( $comment ) ) {
227 359 $comment = \get_comment( $comment );
@@ -232,15 +364,13 @@
232 364 /**
233 365 * Store permalink in meta, to send delete Activity.
234 366 *
235 367 * @param string $post_id The Post ID.
236 - *
237 - * @return void
238 368 */
239 369 public static function trash_post( $post_id ) {
240 370 \add_post_meta(
241 371 $post_id,
242 - 'activitypub_canonical_url',
372 + '_activitypub_canonical_url',
243 373 \get_permalink( $post_id ),
244 374 true
245 375 );
246 376 }
@@ -245,24 +375,24 @@
245 375 );
246 376 }
247 377
248 378 /**
249 - * Delete permalink from meta
379 + * Delete permalink from meta.
250 380 *
251 - * @param string $post_id The Post ID
252 - *
253 - * @return void
381 + * @param string $post_id The Post ID.
254 382 */
255 383 public static function untrash_post( $post_id ) {
256 - \delete_post_meta( $post_id, 'activitypub_canonical_url' );
384 + \delete_post_meta( $post_id, '_activitypub_canonical_url' );
257 385 }
258 386
259 387 /**
260 - * Add rewrite rules
388 + * Add rewrite rules.
261 389 */
262 390 public static function add_rewrite_rules() {
263 - // If another system needs to take precedence over the ActivityPub rewrite rules,
264 - // they can define their own and will manually call the appropriate functions as required.
391 + /*
392 + * If another system needs to take precedence over the ActivityPub rewrite rules,
393 + * they can define their own and will manually call the appropriate functions as required.
394 + */
265 395 if ( ACTIVITYPUB_DISABLE_REWRITES ) {
266 396 return;
267 397 }
268 398
@@ -276,21 +406,16 @@
276 406
277 407 if ( ! \class_exists( 'Nodeinfo_Endpoint' ) && true === (bool) \get_option( 'blog_public', 1 ) ) {
278 408 \add_rewrite_rule(
279 409 '^.well-known/nodeinfo',
280 - 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo/discovery',
410 + 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo',
281 411 'top'
282 412 );
283 - \add_rewrite_rule(
284 - '^.well-known/x-nodeinfo2',
285 - 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/nodeinfo2',
286 - 'top'
287 - );
288 413 }
289 414
290 415 \add_rewrite_rule(
291 - '^@([\w\-\.]+)',
292 - 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/users/$matches[1]',
416 + '^@([\w\-\.]+)$',
417 + 'index.php?rest_route=/' . ACTIVITYPUB_REST_NAMESPACE . '/actors/$matches[1]',
293 418 'top'
294 419 );
295 420
296 421 \add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES );
@@ -296,9 +421,9 @@
296 421 \add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES );
297 422 }
298 423
299 424 /**
300 - * Flush rewrite rules;
425 + * Flush rewrite rules.
301 426 */
302 427 public static function flush_rewrite_rules() {
303 428 self::add_rewrite_rules();
304 429 \flush_rewrite_rules();
@@ -304,45 +429,42 @@
304 429 \flush_rewrite_rules();
305 430 }
306 431
307 432 /**
308 - * Theme compatibility stuff
309 - *
310 - * @return void
433 + * Adds metabox on wp-admin/tools.php.
311 434 */
312 - public static function theme_compat() {
313 - $site_icon = get_theme_support( 'custom-logo' );
314 -
315 - if ( ! $site_icon ) {
316 - // custom logo support
317 - add_theme_support(
318 - 'custom-logo',
319 - array(
320 - 'height' => 80,
321 - 'width' => 80,
322 - )
323 - );
435 + public static function tool_box() {
436 + if ( \current_user_can( 'edit_posts' ) ) {
437 + \load_template( ACTIVITYPUB_PLUGIN_DIR . 'templates/toolbox.php' );
324 438 }
439 + }
325 440
326 - $custom_header = get_theme_support( 'custom-header' );
327 -
328 - if ( ! $custom_header ) {
329 - // This theme supports a custom header
330 - $custom_header_args = array(
331 - 'width' => 1250,
332 - 'height' => 600,
333 - 'header-text' => true,
334 - );
335 - add_theme_support( 'custom-header', $custom_header_args );
441 + /**
442 + * Theme compatibility stuff.
443 + */
444 + public static function theme_compat() {
445 + // We assume that you want to use Post-Formats when enabling the setting.
446 + if ( 'wordpress-post-format' === \get_option( 'activitypub_object_type', ACTIVITYPUB_DEFAULT_OBJECT_TYPE ) ) {
447 + if ( ! get_theme_support( 'post-formats' ) ) {
448 + // Add support for the Aside, Gallery Post Formats...
449 + add_theme_support(
450 + 'post-formats',
451 + array(
452 + 'gallery',
453 + 'status',
454 + 'image',
455 + 'video',
456 + 'audio',
457 + )
458 + );
459 + }
336 460 }
337 461 }
338 462
339 463 /**
340 - * Display plugin upgrade notice to users
464 + * Display plugin upgrade notice to users.
341 465 *
342 - * @param array $data The plugin data
343 - *
344 - * @return void
466 + * @param array $data The plugin data.
345 467 */
346 468 public static function plugin_update_message( $data ) {
347 469 if ( ! isset( $data['upgrade_notice'] ) ) {
348 470 return;
@@ -362,11 +484,9 @@
362 484 );
363 485 }
364 486
365 487 /**
366 - * Register the "Followers" Taxonomy
367 - *
368 - * @return void
488 + * Register Custom Post Types.
369 489 */
370 490 private static function register_post_types() {
371 491 \register_post_type(
372 492 Followers::POST_TYPE,
@@ -386,9 +506,9 @@
386 506 );
387 507
388 508 \register_post_meta(
389 509 Followers::POST_TYPE,
390 - 'activitypub_inbox',
510 + '_activitypub_inbox',
391 511 array(
392 512 'type' => 'string',
393 513 'single' => true,
394 514 'sanitize_callback' => 'sanitize_url',
@@ -396,9 +516,9 @@
396 516 );
397 517
398 518 \register_post_meta(
399 519 Followers::POST_TYPE,
400 - 'activitypub_errors',
520 + '_activitypub_errors',
401 521 array(
402 522 'type' => 'string',
403 523 'single' => false,
404 524 'sanitize_callback' => function ( $value ) {
@@ -412,9 +532,9 @@
412 532 );
413 533
414 534 \register_post_meta(
415 535 Followers::POST_TYPE,
416 - 'activitypub_user_id',
536 + '_activitypub_user_id',
417 537 array(
418 538 'type' => 'string',
419 539 'single' => false,
420 540 'sanitize_callback' => function ( $value ) {
@@ -424,9 +544,9 @@
424 544 );
425 545
426 546 \register_post_meta(
427 547 Followers::POST_TYPE,
428 - 'activitypub_actor_json',
548 + '_activitypub_actor_json',
429 549 array(
430 550 'type' => 'string',
431 551 'single' => true,
432 552 'sanitize_callback' => function ( $value ) {
@@ -434,7 +554,189 @@
434 554 },
435 555 )
436 556 );
437 557
558 + // Register Outbox Post-Type.
559 + register_post_type(
560 + Outbox::POST_TYPE,
561 + array(
562 + 'labels' => array(
563 + 'name' => _x( 'Outbox', 'post_type plural name', 'activitypub' ),
564 + 'singular_name' => _x( 'Outbox Item', 'post_type single name', 'activitypub' ),
565 + ),
566 + 'capabilities' => array(
567 + 'create_posts' => false,
568 + ),
569 + 'map_meta_cap' => true,
570 + 'public' => false,
571 + 'show_in_rest' => true,
572 + 'rewrite' => false,
573 + 'query_var' => false,
574 + 'supports' => array( 'title', 'editor', 'author', 'custom-fields' ),
575 + 'delete_with_user' => true,
576 + 'can_export' => true,
577 + 'exclude_from_search' => true,
578 + )
579 + );
580 +
581 + /**
582 + * Register Activity Type meta for Outbox items.
583 + *
584 + * @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
585 + */
586 + \register_post_meta(
587 + Outbox::POST_TYPE,
588 + '_activitypub_activity_type',
589 + array(
590 + 'type' => 'string',
591 + 'description' => 'The type of the activity',
592 + 'single' => true,
593 + 'show_in_rest' => true,
594 + 'sanitize_callback' => function ( $value ) {
595 + $value = ucfirst( strtolower( $value ) );
596 + $schema = array(
597 + 'type' => 'string',
598 + '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' ),
599 + 'default' => 'Announce',
600 + );
601 +
602 + if ( is_wp_error( rest_validate_enum( $value, $schema, '' ) ) ) {
603 + return $schema['default'];
604 + }
605 +
606 + return $value;
607 + },
608 + )
609 + );
610 +
611 + \register_post_meta(
612 + Outbox::POST_TYPE,
613 + '_activitypub_activity_actor',
614 + array(
615 + 'type' => 'string',
616 + 'single' => true,
617 + 'show_in_rest' => true,
618 + 'sanitize_callback' => function ( $value ) {
619 + $schema = array(
620 + 'type' => 'string',
621 + 'enum' => array( 'application', 'blog', 'user' ),
622 + 'default' => 'user',
623 + );
624 +
625 + if ( is_wp_error( rest_validate_enum( $value, $schema, '' ) ) ) {
626 + return $schema['default'];
627 + }
628 +
629 + return $value;
630 + },
631 + )
632 + );
633 +
634 + \register_post_meta(
635 + Outbox::POST_TYPE,
636 + '_activitypub_outbox_offset',
637 + array(
638 + 'type' => 'integer',
639 + 'single' => true,
640 + 'description' => 'Keeps track of the followers offset when processing outbox items.',
641 + 'sanitize_callback' => 'absint',
642 + 'default' => 0,
643 + )
644 + );
645 +
646 + \register_post_meta(
647 + Outbox::POST_TYPE,
648 + '_activitypub_object_id',
649 + array(
650 + 'type' => 'string',
651 + 'single' => true,
652 + 'description' => 'The ID (ActivityPub URI) of the object that the outbox item is about.',
653 + 'sanitize_callback' => 'sanitize_url',
654 + )
655 + );
656 +
657 + \register_post_meta(
658 + Outbox::POST_TYPE,
659 + 'activitypub_content_visibility',
660 + array(
661 + 'type' => 'string',
662 + 'single' => true,
663 + 'show_in_rest' => true,
664 + 'sanitize_callback' => function ( $value ) {
665 + $schema = array(
666 + 'type' => 'string',
667 + 'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ),
668 + 'default' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
669 + );
670 +
671 + if ( is_wp_error( rest_validate_enum( $value, $schema, '' ) ) ) {
672 + return $schema['default'];
673 + }
674 +
675 + return $value;
676 + },
677 + )
678 + );
679 +
680 + // Both User and Blog Extra Fields types have the same args.
681 + $args = array(
682 + 'labels' => array(
683 + 'name' => _x( 'Extra fields', 'post_type plural name', 'activitypub' ),
684 + 'singular_name' => _x( 'Extra field', 'post_type single name', 'activitypub' ),
685 + 'add_new' => __( 'Add new', 'activitypub' ),
686 + 'add_new_item' => __( 'Add new extra field', 'activitypub' ),
687 + 'new_item' => __( 'New extra field', 'activitypub' ),
688 + 'edit_item' => __( 'Edit extra field', 'activitypub' ),
689 + 'view_item' => __( 'View extra field', 'activitypub' ),
690 + 'all_items' => __( 'All extra fields', 'activitypub' ),
691 + ),
692 + 'public' => false,
693 + 'hierarchical' => false,
694 + 'query_var' => false,
695 + 'has_archive' => false,
696 + 'publicly_queryable' => false,
697 + 'show_in_menu' => false,
698 + 'delete_with_user' => true,
699 + 'can_export' => true,
700 + 'exclude_from_search' => true,
701 + 'show_in_rest' => true,
702 + 'map_meta_cap' => true,
703 + 'show_ui' => true,
704 + 'supports' => array( 'title', 'editor', 'page-attributes' ),
705 + );
706 +
707 + \register_post_type( Extra_Fields::USER_POST_TYPE, $args );
708 + \register_post_type( Extra_Fields::BLOG_POST_TYPE, $args );
709 +
710 + /**
711 + * Fires after ActivityPub custom post types have been registered.
712 + */
438 713 \do_action( 'activitypub_after_register_post_type' );
714 + }
715 +
716 + /**
717 + * Add the 'activitypub' capability to users who can publish posts.
718 + *
719 + * @param int $user_id User ID.
720 + */
721 + public static function user_register( $user_id ) {
722 + if ( \user_can( $user_id, 'publish_posts' ) ) {
723 + $user = \get_user_by( 'id', $user_id );
724 + $user->add_cap( 'activitypub' );
725 + }
726 + }
727 +
728 + /**
729 + * Delete `activitypub_content_visibility` when updated to an empty value.
730 + *
731 + * @param int $meta_id ID of updated metadata entry.
732 + * @param int $object_id Post ID.
733 + * @param string $meta_key Metadata key.
734 + * @param mixed $meta_value Metadata value. This will be a PHP-serialized string representation of the value
735 + * if the value is an array, an object, or itself a PHP-serialized string.
736 + */
737 + public static function updated_postmeta( $meta_id, $object_id, $meta_key, $meta_value ) {
738 + if ( 'activitypub_content_visibility' === $meta_key && empty( $meta_value ) ) {
739 + \delete_post_meta( $object_id, 'activitypub_content_visibility' );
740 + }
439 741 }
440 742 }