PluginProbe
ActivityPub / 7.7.0
ActivityPub v7.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
← All changes | includes/class-post-types.php +50 -561 9.3.07.7.0 View file →
@@ -9,19 +9,12 @@
9 9
10 10 use Activitypub\Activity\Activity;
11 11 use Activitypub\Collection\Extra_Fields;
12 12 use Activitypub\Collection\Followers;
13 -use Activitypub\Collection\Following;
14 13 use Activitypub\Collection\Inbox;
15 14 use Activitypub\Collection\Outbox;
15 +use Activitypub\Collection\Posts;
16 16 use Activitypub\Collection\Remote_Actors;
17 -use Activitypub\Collection\Remote_Posts;
18 -use Activitypub\OAuth\Client;
19 -use Activitypub\OAuth\Scope;
20 -use Activitypub\OAuth\Token;
21 -use Activitypub\Rest\Reader_Terms_Controller;
22 -use Activitypub\Rest\Remote_Actors_Controller;
23 -use Activitypub\Rest\Remote_Posts_Controller;
24 17
25 18 /**
26 19 * Post Types class.
27 20 */
@@ -35,21 +28,11 @@
35 28 \add_action( 'init', array( self::class, 'register_outbox_post_type' ), 11 );
36 29 \add_action( 'init', array( self::class, 'register_post_post_type' ), 11 );
37 30 \add_action( 'init', array( self::class, 'register_extra_fields_post_types' ), 11 );
38 31 \add_action( 'init', array( self::class, 'register_activitypub_post_meta' ), 11 );
39 - \add_action( 'init', array( self::class, 'register_oauth_post_types' ), 11 );
40 - \add_action( 'init', array( self::class, 'register_tombstone_post_type' ), 11 );
41 32
42 33 \add_action( 'rest_api_init', array( self::class, 'register_ap_actor_rest_field' ) );
43 - \add_action( 'rest_api_init', array( self::class, 'register_ap_post_actor_rest_field' ) );
44 - \add_action( 'rest_api_init', array( self::class, 'register_ap_post_rest_params' ) );
45 34
46 - \add_filter( 'rest_ap_post_query', array( self::class, 'filter_ap_post_by_user' ), 10, 2 );
47 - \add_filter( 'rest_ap_object_type_query', array( self::class, 'filter_object_type_by_user' ), 10, 2 );
48 - \add_filter( 'rest_ap_tag_query', array( self::class, 'filter_tag_by_user' ), 10, 2 );
49 - \add_filter( 'rest_ap_object_type_collection_params', array( self::class, 'register_object_type_user_param' ) );
50 - \add_filter( 'rest_ap_tag_collection_params', array( self::class, 'register_object_type_user_param' ) );
51 -
52 35 \add_filter( 'activitypub_get_actor_extra_fields', array( Extra_Fields::class, 'default_actor_extra_fields' ), 10, 2 );
53 36
54 37 \add_filter( 'add_post_metadata', array( self::class, 'prevent_empty_post_meta' ), 10, 4 );
55 38 \add_filter( 'update_post_metadata', array( self::class, 'prevent_empty_post_meta' ), 10, 4 );
@@ -66,24 +49,20 @@
66 49 public static function register_remote_actors_post_type() {
67 50 \register_post_type(
68 51 Remote_Actors::POST_TYPE,
69 52 array(
70 - 'labels' => array(
53 + 'labels' => array(
71 54 'name' => \_x( 'Followers', 'post_type plural name', 'activitypub' ),
72 55 'singular_name' => \_x( 'Follower', 'post_type single name', 'activitypub' ),
73 56 ),
74 - 'public' => false,
75 - 'capabilities' => array(
76 - 'create_posts' => false,
77 - ),
78 - 'show_in_rest' => true,
79 - 'rest_controller_class' => Remote_Actors_Controller::class,
80 - 'hierarchical' => false,
81 - 'rewrite' => false,
82 - 'query_var' => false,
83 - 'delete_with_user' => false,
84 - 'can_export' => true,
85 - 'supports' => array( 'custom-fields' ),
57 + 'public' => false,
58 + 'show_in_rest' => true,
59 + 'hierarchical' => false,
60 + 'rewrite' => false,
61 + 'query_var' => false,
62 + 'delete_with_user' => false,
63 + 'can_export' => true,
64 + 'supports' => array(),
86 65 )
87 66 );
88 67
89 68 // Register meta for Remote Actors post type.
@@ -163,9 +142,9 @@
163 142 'type' => 'string',
164 143 'description' => 'The type of the activity',
165 144 'single' => true,
166 145 'show_in_rest' => true,
167 - 'sanitize_callback' => static function ( $value ) {
146 + 'sanitize_callback' => function ( $value ) {
168 147 $schema = array(
169 148 'type' => 'string',
170 149 'enum' => Activity::TYPES,
171 150 'default' => 'Create',
@@ -197,9 +176,9 @@
197 176 array(
198 177 'type' => 'string',
199 178 'single' => true,
200 179 'show_in_rest' => true,
201 - 'sanitize_callback' => static function ( $value ) {
180 + 'sanitize_callback' => function ( $value ) {
202 181 $schema = array(
203 182 'type' => 'string',
204 183 'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ),
205 184 'default' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
@@ -266,9 +245,9 @@
266 245 'type' => 'string',
267 246 'description' => 'The type of the activity',
268 247 'single' => true,
269 248 'show_in_rest' => true,
270 - 'sanitize_callback' => static function ( $value ) {
249 + 'sanitize_callback' => function ( $value ) {
271 250 $schema = array(
272 251 'type' => 'string',
273 252 'enum' => Activity::TYPES,
274 253 'default' => 'Announce',
@@ -289,9 +268,9 @@
289 268 array(
290 269 'type' => 'string',
291 270 'single' => true,
292 271 'show_in_rest' => true,
293 - 'sanitize_callback' => static function ( $value ) {
272 + 'sanitize_callback' => function ( $value ) {
294 273 $schema = array(
295 274 'type' => 'string',
296 275 'enum' => array( 'application', 'blog', 'user' ),
297 276 'default' => 'user',
@@ -335,9 +314,9 @@
335 314 array(
336 315 'type' => 'string',
337 316 'single' => true,
338 317 'show_in_rest' => true,
339 - 'sanitize_callback' => static function ( $value ) {
318 + 'sanitize_callback' => function ( $value ) {
340 319 $schema = array(
341 320 'type' => 'string',
342 321 'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ),
343 322 'default' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
@@ -357,55 +336,52 @@
357 336 * Register the Post post type.
358 337 */
359 338 public static function register_post_post_type() {
360 339 \register_post_type(
361 - Remote_Posts::POST_TYPE,
340 + Posts::POST_TYPE,
362 341 array(
363 - 'labels' => array(
342 + 'labels' => array(
364 343 'name' => \_x( 'Posts', 'post_type plural name', 'activitypub' ),
365 344 'singular_name' => \_x( 'Post', 'post_type single name', 'activitypub' ),
366 345 ),
367 - 'map_meta_cap' => true,
368 - 'public' => false,
369 - 'capabilities' => array(
346 + 'capabilities' => array(
370 347 'create_posts' => false,
371 348 ),
372 - 'show_in_rest' => true,
373 - 'rest_controller_class' => Remote_Posts_Controller::class,
374 - 'rewrite' => false,
375 - 'query_var' => false,
376 - 'supports' => array( 'title', 'editor', 'author', 'custom-fields', 'excerpt', 'comments' ),
377 - 'delete_with_user' => true,
378 - 'can_export' => true,
379 - 'exclude_from_search' => true,
380 - 'taxonomies' => array( 'ap_tag', 'ap_object_type' ),
349 + 'map_meta_cap' => true,
350 + 'public' => false,
351 + 'show_in_rest' => false,
352 + 'rewrite' => false,
353 + 'query_var' => false,
354 + 'supports' => array( 'title', 'editor', 'author', 'custom-fields', 'excerpt', 'comments' ),
355 + 'delete_with_user' => true,
356 + 'can_export' => true,
357 + 'exclude_from_search' => true,
358 + 'taxonomies' => array( 'ap_tag', 'ap_object_type' ),
381 359 )
382 360 );
383 361
384 362 \register_taxonomy(
385 363 'ap_tag',
386 - array( Remote_Posts::POST_TYPE ),
364 + array( Posts::POST_TYPE ),
387 365 array(
388 - 'public' => false,
389 - 'query_var' => true,
390 - 'show_in_rest' => true,
391 - 'rest_controller_class' => Reader_Terms_Controller::class,
366 + 'public' => false,
367 + 'query_var' => true,
368 + 'show_in_rest' => true,
392 369 )
393 370 );
394 371
395 372 \register_taxonomy(
396 373 'ap_object_type',
397 - array( Remote_Posts::POST_TYPE ),
374 + array( Posts::POST_TYPE ),
398 375 array(
399 - 'public' => false,
400 - 'query_var' => true,
401 - 'show_in_rest' => true,
402 - 'rest_controller_class' => Reader_Terms_Controller::class,
376 + 'public' => false,
377 + 'query_var' => true,
378 + 'show_in_rest' => true,
403 379 )
404 380 );
405 381
406 382 \register_post_meta(
407 - Remote_Posts::POST_TYPE,
383 + Posts::POST_TYPE,
408 384 '_activitypub_remote_actor_id',
409 385 array(
410 386 'type' => 'integer',
411 387 'single' => true,
@@ -414,9 +390,9 @@
414 390 )
415 391 );
416 392
417 393 \register_post_meta(
418 - Remote_Posts::POST_TYPE,
394 + Posts::POST_TYPE,
419 395 '_activitypub_user_id',
420 396 array(
421 397 'type' => 'integer',
422 398 'single' => true,
@@ -454,9 +430,8 @@
454 430 'map_meta_cap' => true,
455 431 'show_ui' => true,
456 432 'supports' => array( 'title', 'editor', 'page-attributes', 'author' ),
457 433 'capabilities' => array(
458 - 'create_posts' => 'activitypub', // Require activitypub capability to create extra fields.
459 434 'edit_others_posts' => 'do_not_allow', // Disallow editing others' Extra Fields (only own ones).
460 435 ),
461 436 );
462 437
@@ -461,10 +436,9 @@
461 436 );
462 437
463 438 \register_post_type( Extra_Fields::USER_POST_TYPE, $extra_field_args );
464 439
465 - // Blog Extra Fields require manage_options capability.
466 - $extra_field_args['capabilities'] = array( 'create_posts' => 'manage_options' );
440 + unset( $extra_field_args['capabilities'] ); // Allow editing the Blog's Extra Fields.
467 441 \register_post_type( Extra_Fields::BLOG_POST_TYPE, $extra_field_args );
468 442
469 443 /**
470 444 * Fires after ActivityPub custom post types have been registered.
@@ -472,143 +446,8 @@
472 446 \do_action( 'activitypub_after_register_post_type' );
473 447 }
474 448
475 449 /**
476 - * Register OAuth 2.0 post types for C2S support.
477 - *
478 - * Registers post type for OAuth clients.
479 - * Note: Tokens are stored in user meta and authorization codes in transients.
480 - */
481 - public static function register_oauth_post_types() {
482 - // OAuth Clients post type.
483 - \register_post_type(
484 - Client::POST_TYPE,
485 - array(
486 - 'labels' => array(
487 - 'name' => \_x( 'OAuth Clients', 'post_type plural name', 'activitypub' ),
488 - 'singular_name' => \_x( 'OAuth Client', 'post_type single name', 'activitypub' ),
489 - ),
490 - 'public' => false,
491 - 'show_in_rest' => false,
492 - 'hierarchical' => false,
493 - 'rewrite' => false,
494 - 'query_var' => false,
495 - 'delete_with_user' => false,
496 - 'can_export' => true,
497 - 'supports' => array( 'title', 'editor', 'custom-fields' ),
498 - 'exclude_from_search' => true,
499 - )
500 - );
501 -
502 - // OAuth Client meta.
503 - \register_post_meta(
504 - Client::POST_TYPE,
505 - '_activitypub_client_id',
506 - array(
507 - 'type' => 'string',
508 - 'single' => true,
509 - 'description' => 'Unique OAuth client identifier (UUID).',
510 - 'sanitize_callback' => 'sanitize_text_field',
511 - )
512 - );
513 -
514 - \register_post_meta(
515 - Client::POST_TYPE,
516 - '_activitypub_client_secret_hash',
517 - array(
518 - 'type' => 'string',
519 - 'single' => true,
520 - 'description' => 'SHA-256 hash of the client secret (null for public clients).',
521 - 'sanitize_callback' => 'sanitize_text_field',
522 - )
523 - );
524 -
525 - \register_post_meta(
526 - Client::POST_TYPE,
527 - '_activitypub_redirect_uris',
528 - array(
529 - 'type' => 'array',
530 - 'single' => true,
531 - 'description' => 'Allowed redirect URIs for this client.',
532 - 'sanitize_callback' => static function ( $value ) {
533 - if ( ! \is_array( $value ) ) {
534 - return array();
535 - }
536 - return \array_map( array( Sanitize::class, 'redirect_uri' ), $value );
537 - },
538 - )
539 - );
540 -
541 - \register_post_meta(
542 - Client::POST_TYPE,
543 - '_activitypub_allowed_scopes',
544 - array(
545 - 'type' => 'array',
546 - 'single' => true,
547 - 'description' => 'Allowed OAuth scopes for this client.',
548 - 'sanitize_callback' => array( Scope::class, 'sanitize' ),
549 - )
550 - );
551 -
552 - \register_post_meta(
553 - Client::POST_TYPE,
554 - '_activitypub_is_public',
555 - array(
556 - 'type' => 'boolean',
557 - 'single' => true,
558 - 'description' => 'Whether this is a public client (PKCE-only, no secret).',
559 - 'sanitize_callback' => 'rest_sanitize_boolean',
560 - 'default' => true,
561 - )
562 - );
563 -
564 - \register_post_meta(
565 - Client::POST_TYPE,
566 - Token::USER_META_KEY,
567 - array(
568 - 'type' => 'integer',
569 - 'single' => false,
570 - 'description' => 'User IDs that have active tokens for this client.',
571 - 'sanitize_callback' => 'absint',
572 - )
573 - );
574 - }
575 -
576 - /**
577 - * Register the ap_tombstone post type.
578 - *
579 - * Stores local tombstone URLs out of the autoloaded options row.
580 - * The post type is fully internal — never queried publicly, never shown in UI.
581 - *
582 - * @since 8.3.0
583 - */
584 - public static function register_tombstone_post_type() {
585 - \register_post_type(
586 - Tombstone::POST_TYPE,
587 - array(
588 - 'labels' => array(
589 - 'name' => \_x( 'Tombstones', 'post_type plural name', 'activitypub' ),
590 - 'singular_name' => \_x( 'Tombstone', 'post_type single name', 'activitypub' ),
591 - ),
592 - 'public' => false,
593 - 'publicly_queryable' => false,
594 - 'show_ui' => false,
595 - 'show_in_menu' => false,
596 - 'show_in_nav_menus' => false,
597 - 'show_in_admin_bar' => false,
598 - 'show_in_rest' => false,
599 - 'exclude_from_search' => true,
600 - 'has_archive' => false,
601 - 'rewrite' => false,
602 - 'query_var' => false,
603 - 'can_export' => false,
604 - 'delete_with_user' => false,
605 - 'supports' => array(),
606 - )
607 - );
608 - }
609 -
610 - /**
611 450 * Register post meta for ActivityPub supported post types.
612 451 */
613 452 public static function register_activitypub_post_meta() {
614 453 $ap_post_types = \get_post_types_by_support( 'activitypub' );
@@ -630,9 +469,9 @@
630 469 array(
631 470 'type' => 'string',
632 471 'single' => true,
633 472 'show_in_rest' => true,
634 - 'sanitize_callback' => static function ( $value ) {
473 + 'sanitize_callback' => function ( $value ) {
635 474 $schema = array(
636 475 'type' => 'string',
637 476 'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ),
638 477 'default' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC,
@@ -665,10 +504,9 @@
665 504 array(
666 505 'type' => 'string',
667 506 'single' => true,
668 507 'show_in_rest' => true,
669 - 'default' => \get_option( 'activitypub_default_quote_policy', ACTIVITYPUB_INTERACTION_POLICY_ANYONE ),
670 - 'sanitize_callback' => static function ( $value ) {
508 + 'sanitize_callback' => function ( $value ) {
671 509 $schema = array(
672 510 'type' => 'string',
673 511 'enum' => array( ACTIVITYPUB_INTERACTION_POLICY_ANYONE, ACTIVITYPUB_INTERACTION_POLICY_FOLLOWERS, ACTIVITYPUB_INTERACTION_POLICY_ME ),
674 512 'default' => ACTIVITYPUB_INTERACTION_POLICY_ANYONE,
@@ -689,23 +527,13 @@
689 527 array(
690 528 'type' => 'string',
691 529 'single' => true,
692 530 'show_in_rest' => true,
693 - 'sanitize_callback' => static function ( $value ) {
694 - // Allow empty values to pass through without setting a default.
695 - if ( empty( $value ) ) {
696 - return '';
697 - }
698 -
531 + 'sanitize_callback' => function ( $value ) {
699 532 $schema = array(
700 533 'type' => 'string',
701 - 'enum' => array(
702 - ACTIVITYPUB_OBJECT_STATE_PENDING,
703 - ACTIVITYPUB_OBJECT_STATE_FEDERATED,
704 - ACTIVITYPUB_OBJECT_STATE_FAILED,
705 - ACTIVITYPUB_OBJECT_STATE_DELETED,
706 - ),
707 - 'default' => '',
534 + 'enum' => array( 'pending', 'federated', 'failed' ),
535 + 'default' => 'pending',
708 536 );
709 537
710 538 if ( \is_wp_error( \rest_validate_enum( $value, $schema, '' ) ) ) {
711 539 return $schema['default'];
@@ -731,9 +559,9 @@
731 559 *
732 560 * @param array $response Prepared response array.
733 561 * @return string The raw post content.
734 562 */
735 - 'get_callback' => static function ( $response ) {
563 + 'get_callback' => function ( $response ) {
736 564 return \get_post_field( 'post_content', $response['id'] );
737 565 },
738 566 'schema' => array(
739 567 'description' => 'Raw ActivityPub JSON data without WordPress content filtering',
@@ -741,351 +569,11 @@
741 569 'context' => array( 'view', 'edit' ),
742 570 ),
743 571 )
744 572 );
745 -
746 - // Add formatted actor data field.
747 - \register_rest_field(
748 - Remote_Actors::POST_TYPE,
749 - 'actor_info',
750 - array(
751 - 'get_callback' => function ( $response ) {
752 - $actor = Remote_Actors::get_actor( $response['id'] );
753 - if ( \is_wp_error( $actor ) ) {
754 - return null;
755 - }
756 - return array(
757 - 'username' => $actor->get_preferred_username(),
758 - 'name' => $actor->get_name() ?? $actor->get_preferred_username(),
759 - 'icon' => \sanitize_url( object_to_uri( $actor->get_icon() ) ?? '' ),
760 - 'url' => \sanitize_url( object_to_uri( $actor->get_url() ?? $actor->get_id() ) ?? '' ),
761 - 'webfinger' => Remote_Actors::get_acct( $response['id'] ),
762 - 'identifier' => $actor->get_id(),
763 - );
764 - },
765 - 'schema' => array(
766 - 'description' => 'Parsed ActivityPub actor information',
767 - 'type' => 'object',
768 - 'context' => array( 'view', 'edit' ),
769 - ),
770 - )
771 - );
772 -
773 - // Add follow status field.
774 - \register_rest_field(
775 - Remote_Actors::POST_TYPE,
776 - 'follow_status',
777 - array(
778 - 'get_callback' => function ( $response ) {
779 - $current_user_id = \get_current_user_id();
780 - if ( ! $current_user_id ) {
781 - return array( 'follows_back' => false );
782 - }
783 - return array(
784 - 'follows_back' => Following::check_status( $current_user_id, $response['id'] ),
785 - );
786 - },
787 - 'schema' => array(
788 - 'description' => 'Follow relationship status',
789 - 'type' => 'object',
790 - 'context' => array( 'view', 'edit' ),
791 - ),
792 - )
793 - );
794 -
795 - // Add custom query parameter for filtering by follower relationships.
796 - \add_filter( 'rest_ap_actor_query', array( self::class, 'filter_ap_actor_query_by_follower' ), 10, 2 );
797 573 }
798 574
799 575 /**
800 - * Filter WP_Query args to support follower_of parameter.
801 - *
802 - * @param array $args Array of arguments for WP_Query.
803 - * @param \WP_REST_Request $request The REST API request.
804 - * @return array Modified query arguments.
805 - */
806 - public static function filter_ap_actor_query_by_follower( $args, $request ) {
807 - $follower_of = isset( $request['follower_of'] ) ? (int) $request['follower_of'] : null;
808 -
809 - // Users who cannot list users may only ever see their own followers.
810 - if ( ! \current_user_can( 'list_users' ) ) {
811 - $follower_of = \get_current_user_id();
812 - }
813 -
814 - if ( null === $follower_of ) {
815 - return $args;
816 - }
817 -
818 - // Add meta_query to filter by _activitypub_following.
819 - if ( ! isset( $args['meta_query'] ) ) {
820 - $args['meta_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
821 - }
822 -
823 - $args['meta_query'][] = array(
824 - 'key' => Followers::FOLLOWER_META_KEY,
825 - 'value' => $follower_of,
826 - );
827 -
828 - return $args;
829 - }
830 -
831 - /**
832 - * Register a REST field for the ap_post post type to embed remote actor data.
833 - */
834 - public static function register_ap_post_actor_rest_field() {
835 - \register_rest_field(
836 - Remote_Posts::POST_TYPE,
837 - 'actor_info',
838 - array(
839 - /**
840 - * Get the remote actor data for an ap_post.
841 - *
842 - * @param array $response Prepared response array.
843 - * @return array|null The actor data or null if not found.
844 - */
845 - 'get_callback' => function ( $response ) {
846 - $id = \get_post_meta( $response['id'], '_activitypub_remote_actor_id', true );
847 - $actor = Remote_Actors::get_actor( $id );
848 -
849 - if ( \is_wp_error( $actor ) ) {
850 - return null;
851 - }
852 -
853 - return array(
854 - 'username' => $actor->get_preferred_username(),
855 - 'name' => $actor->get_name() ?? $actor->get_preferred_username(),
856 - 'icon' => \sanitize_url( object_to_uri( $actor->get_icon() ) ?? '' ),
857 - 'url' => \sanitize_url( object_to_uri( $actor->get_url() ?? $actor->get_id() ) ?? '' ),
858 - 'webfinger' => Remote_Actors::get_acct( $id ),
859 - 'identifier' => $actor->get_id(),
860 - );
861 - },
862 - 'schema' => array(
863 - 'description' => 'Remote actor data',
864 - 'type' => 'object',
865 - 'context' => array( 'view', 'edit' ),
866 - ),
867 - )
868 - );
869 - }
870 -
871 - /**
872 - * Register custom REST API parameters for ap_post endpoint.
873 - */
874 - public static function register_ap_post_rest_params() {
875 - \add_filter(
876 - 'rest_' . Remote_Posts::POST_TYPE . '_collection_params',
877 - function ( $params ) {
878 - $params['user_id'] = array(
879 - 'description' => \__( 'Filter posts by user ID (0 for site/blog actor).', 'activitypub' ),
880 - 'type' => 'integer',
881 - 'sanitize_callback' => 'absint',
882 - );
883 -
884 - $params['ap_object_type'] = array(
885 - 'description' => 'Filter posts by ActivityPub object type.',
886 - 'type' => 'array',
887 - 'items' => array(
888 - 'type' => 'integer',
889 - 'minimum' => 0,
890 - ),
891 - );
892 -
893 - $params['ap_tag'] = array(
894 - 'description' => 'Filter posts by ActivityPub tag (term IDs).',
895 - 'type' => 'array',
896 - 'items' => array(
897 - 'type' => 'integer',
898 - 'minimum' => 0,
899 - ),
900 - );
901 -
902 - return $params;
903 - }
904 - );
905 - }
906 -
907 - /**
908 - * Filter ap_post REST query to only show posts for the current user.
909 - *
910 - * @param array $args Query arguments.
911 - * @param \WP_REST_Request $request The REST API request.
912 - *
913 - * @return array Modified query arguments.
914 - */
915 - public static function filter_ap_post_by_user( $args, $request ) {
916 - /*
917 - * Scope to one actor's feed. `scope_user_id()` pins the value to the current user unless
918 - * the caller can `list_users`, so only a privileged caller can ask for another actor or
919 - * for 0, the site/blog actor. This runs for every request, whatever else is being
920 - * filtered on, or a tag or object type filter would match the whole cache.
921 - */
922 - if ( ! isset( $args['meta_query'] ) ) {
923 - $args['meta_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
924 - }
925 -
926 - $args['meta_query'][] = array(
927 - 'key' => '_activitypub_user_id',
928 - 'value' => self::scope_user_id( isset( $request['user_id'] ) ? $request['user_id'] : null ),
929 - 'compare' => '=',
930 - );
931 -
932 - // Filter by tag if provided.
933 - $ap_tag = $request->get_param( 'ap_tag' );
934 - if ( ! empty( $ap_tag ) ) {
935 - if ( ! isset( $args['tax_query'] ) ) {
936 - $args['tax_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
937 - }
938 -
939 - $args['tax_query'][] = array(
940 - 'taxonomy' => 'ap_tag',
941 - 'field' => 'term_id',
942 - 'terms' => $ap_tag,
943 - );
944 - }
945 -
946 - // Filter by object type if provided.
947 - if ( ! empty( $request['ap_object_type'] ) ) {
948 - if ( ! isset( $args['tax_query'] ) ) {
949 - $args['tax_query'] = array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
950 - }
951 -
952 - $args['tax_query'][] = array(
953 - 'taxonomy' => 'ap_object_type',
954 - 'field' => 'term_id',
955 - 'terms' => $request['ap_object_type'],
956 - );
957 - }
958 -
959 - return $args;
960 - }
961 -
962 - /**
963 - * Clamp a requested user ID to a feed the current user is allowed to read.
964 - *
965 - * Users who can list users may read any actor's reader data, everybody else is
966 - * limited to their own.
967 - *
968 - * @since 9.3.0
969 - *
970 - * @param int|null $requested_user_id The requested user ID, or null when none was given.
971 - * @return int The user ID to scope the query to.
972 - */
973 - private static function scope_user_id( $requested_user_id ) {
974 - $current_user_id = \get_current_user_id();
975 -
976 - if ( null === $requested_user_id ) {
977 - return $current_user_id;
978 - }
979 -
980 - $requested_user_id = (int) $requested_user_id;
981 -
982 - if ( $requested_user_id !== $current_user_id && ! \current_user_can( 'list_users' ) ) {
983 - return $current_user_id;
984 - }
985 -
986 - return $requested_user_id;
987 - }
988 -
989 - /**
990 - * Register user_id parameter for ap_object_type taxonomy REST API.
991 - *
992 - * @param array $params Existing collection parameters.
993 - *
994 - * @return array Modified collection parameters.
995 - */
996 - public static function register_object_type_user_param( $params ) {
997 - $params['user_id'] = array(
998 - 'description' => \__( 'Filter terms to those with posts from this user ID.', 'activitypub' ),
999 - 'type' => 'integer',
1000 - 'sanitize_callback' => 'absint',
1001 - );
1002 -
1003 - return $params;
1004 - }
1005 -
1006 - /**
1007 - * Filter ap_object_type REST query to only return terms that have posts for the given user.
1008 - *
1009 - * Uses a direct SQL query to efficiently get term IDs without loading all post IDs.
1010 - *
1011 - * @param array $args Query arguments.
1012 - * @param \WP_REST_Request $request The REST API request.
1013 - *
1014 - * @return array Modified query arguments.
1015 - */
1016 - public static function filter_object_type_by_user( $args, $request ) {
1017 - return self::filter_terms_by_user( $args, $request, 'ap_object_type' );
1018 - }
1019 -
1020 - /**
1021 - * Filter the ap_tag REST query to terms that have posts for the given user.
1022 - *
1023 - * @param array $args Query arguments.
1024 - * @param \WP_REST_Request $request The REST API request.
1025 - *
1026 - * @return array Modified query arguments.
1027 - */
1028 - public static function filter_tag_by_user( $args, $request ) {
1029 - return self::filter_terms_by_user( $args, $request, 'ap_tag' );
1030 - }
1031 -
1032 - /**
1033 - * Filter a reader taxonomy REST query to terms that have posts for the given user.
1034 - *
1035 - * @param array $args Query arguments.
1036 - * @param \WP_REST_Request $request The REST API request.
1037 - * @param string $taxonomy The taxonomy to scope.
1038 - *
1039 - * @return array Modified query arguments.
1040 - */
1041 - private static function filter_terms_by_user( $args, $request, $taxonomy ) {
1042 - $user_id = $request->get_param( 'user_id' );
1043 -
1044 - // Users who cannot list users may only ever see terms from their own feed.
1045 - if ( ! \current_user_can( 'list_users' ) ) {
1046 - $user_id = \get_current_user_id();
1047 - }
1048 -
1049 - if ( null === $user_id ) {
1050 - return $args;
1051 - }
1052 -
1053 - global $wpdb;
1054 -
1055 - // Get term IDs that have at least one ap_post for this user.
1056 - $term_ids = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
1057 - $wpdb->prepare(
1058 - "SELECT DISTINCT tt.term_id
1059 - FROM {$wpdb->term_taxonomy} tt
1060 - INNER JOIN {$wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
1061 - INNER JOIN {$wpdb->posts} p ON tr.object_id = p.ID
1062 - INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
1063 - WHERE tt.taxonomy = %s
1064 - AND p.post_type = %s
1065 - AND pm.meta_key = '_activitypub_user_id'
1066 - AND pm.meta_value = %s",
1067 - $taxonomy,
1068 - Remote_Posts::POST_TYPE,
1069 - $user_id
1070 - )
1071 - );
1072 -
1073 - /*
1074 - * `include => array( 0 )` does not restrict anything: `WP_Term_Query` adds the `IN` clause
1075 - * only when the imploded id list is truthy, and the string "0" is not, so the clause is
1076 - * dropped and every term comes back. An id that cannot exist forces the empty result.
1077 - */
1078 - if ( empty( $term_ids ) ) {
1079 - $term_ids = array( PHP_INT_MAX );
1080 - }
1081 -
1082 - $args['include'] = \array_map( 'intval', $term_ids );
1083 -
1084 - return $args;
1085 - }
1086 -
1087 - /**
1088 576 * Prevent empty or default meta values.
1089 577 *
1090 578 * @param null|bool $check Whether to allow updating metadata for the given type.
1091 579 * @param int $object_id ID of the object metadata is for.
@@ -1093,15 +581,16 @@
1093 581 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
1094 582 */
1095 583 public static function prevent_empty_post_meta( $check, $object_id, $meta_key, $meta_value ) {
1096 584 $post_metas = array(
1097 - 'activitypub_content_visibility' => '',
1098 - 'activitypub_content_warning' => '',
1099 - 'activitypub_max_image_attachments' => (string) \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ),
585 + 'activitypub_content_visibility' => '',
586 + 'activitypub_content_warning' => '',
587 + 'activitypub_interaction_policy_quote' => ACTIVITYPUB_INTERACTION_POLICY_ANYONE,
588 + 'activitypub_max_image_attachments' => (string) \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ),
1100 589 );
1101 590
1102 591 if ( isset( $post_metas[ $meta_key ] ) && $post_metas[ $meta_key ] === (string) $meta_value ) {
1103 - if ( 'update_post_metadata' === \current_action() ) {
592 + if ( 'update_post_metadata' === current_action() ) {
1104 593 \delete_post_meta( $object_id, $meta_key );
1105 594 }
1106 595
1107 596 $check = true;