PluginProbe
ActivityPub / 8.0.2
ActivityPub v8.0.2
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-comment.php +61 -107 9.3.08.0.2 View file →
@@ -7,9 +7,9 @@
7 7
8 8 namespace Activitypub;
9 9
10 10 use Activitypub\Collection\Actors;
11 -use Activitypub\Collection\Remote_Posts;
11 +use Activitypub\Collection\Posts;
12 12
13 13 /**
14 14 * ActivityPub Comment Class.
15 15 *
@@ -34,9 +34,9 @@
34 34 \add_action( 'update_option_activitypub_allow_likes', array( self::class, 'maybe_update_comment_counts' ), 10, 2 );
35 35 \add_action( 'update_option_activitypub_allow_reposts', array( self::class, 'maybe_update_comment_counts' ), 10, 2 );
36 36 \add_filter( 'pre_wp_update_comment_count_now', array( static::class, 'pre_wp_update_comment_count_now' ), 5, 3 );
37 37 \add_filter( 'get_comment_author', array( static::class, 'render_emoji' ), 10, 2 );
38 - \add_filter( 'comment_author', array( static::class, 'unescape_emoji' ), 20, 2 ); // After esc_html().
38 + \add_filter( 'comment_author', array( static::class, 'unescape_emoji' ), 20 ); // After esc_html().
39 39 \add_filter( 'rest_comment_query', array( static::class, 'rest_comment_query' ) );
40 40 \add_filter( 'comment_text', array( static::class, 'render_blocks' ), 5 ); // Before other filters.
41 41 }
42 42
@@ -112,9 +112,9 @@
112 112 // Logged-in user without ActivityPub capability - show warning instead of reply link.
113 113 if ( \is_user_logged_in() ) {
114 114 $author = \esc_html( $comment->comment_author );
115 115
116 - $message = \sprintf(
116 + $message = sprintf(
117 117 /* translators: %s: comment author name */
118 118 \__( '%s is on the Fediverse. To reply to them, ask your administrator to enable ActivityPub for your account.', 'activitypub' ),
119 119 $author
120 120 );
@@ -120,9 +120,9 @@
120 120 );
121 121
122 122 // Add link to users page if current user can edit users.
123 123 if ( \current_user_can( 'edit_users' ) ) {
124 - $message = \sprintf(
124 + $message = sprintf(
125 125 /* translators: 1: comment author name, 2: URL to the users management page */
126 126 \__( '%1$s is on the Fediverse. To reply to them, <a href="%2$s">enable ActivityPub for your account</a>.', 'activitypub' ),
127 127 $author,
128 128 \esc_url( \admin_url( 'users.php' ) )
@@ -128,9 +128,9 @@
128 128 \esc_url( \admin_url( 'users.php' ) )
129 129 );
130 130 }
131 131
132 - $warning = \sprintf(
132 + $warning = sprintf(
133 133 '<p class="activitypub-reply-warning"><em>%s</em></p>',
134 134 \wp_kses( $message, array( 'a' => array( 'href' => array() ) ) )
135 135 );
136 136
@@ -177,9 +177,9 @@
177 177 if ( ! self::was_received( $comment ) ) {
178 178 return true;
179 179 }
180 180
181 - $current_user = \get_current_user_id();
181 + $current_user = get_current_user_id();
182 182
183 183 if ( ! $current_user ) {
184 184 return false;
185 185 }
@@ -298,19 +298,8 @@
298 298 if ( ! user_can_activitypub( $user_id ) ) {
299 299 return false;
300 300 }
301 301
302 - /*
303 - * Do not federate brand-new comments on a post that is not federated itself
304 - * (e.g. a private post, a post switched to local visibility, or a non-ActivityPub
305 - * post type). This prevents leaking replies on content the post type's read rules
306 - * would otherwise protect. Comments that were already sent are allowed through so
307 - * their Update and Delete activities can still federate (and tear down remote copies).
308 - */
309 - if ( ! self::was_sent( $comment ) && ! is_post_federated( $comment->comment_post_ID ) ) {
310 - return false;
311 - }
312 -
313 302 // It is a comment to the post and can be federated.
314 303 if ( empty( $comment->comment_parent ) ) {
315 304 return true;
316 305 }
@@ -323,33 +312,22 @@
323 312
324 313 /**
325 314 * Examine a comment ID and look up an existing comment it represents.
326 315 *
327 - * @since 9.1.0 Added the `$args` parameter.
316 + * @param string $id ActivityPub object ID (usually a URL) to check.
328 317 *
329 - * @param string $id ActivityPub object ID (usually a URL) to check.
330 - * @param array $args Optional. Additional WP_Comment_Query arguments. Pass `array( 'status' => 'any' )`
331 - * to also match comments in spam or trash, which the default status excludes.
332 - *
333 318 * @return \WP_Comment|false Comment object, or false on failure.
334 319 */
335 - public static function object_id_to_comment( $id, $args = array() ) {
336 - $args = \wp_parse_args(
337 - $args,
320 + public static function object_id_to_comment( $id ) {
321 + $comment_query = new \WP_Comment_Query(
338 322 array(
339 - 'number' => 1,
340 - 'orderby' => 'comment_date',
341 - 'order' => 'DESC',
323 + 'meta_key' => 'source_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
324 + 'meta_value' => $id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
325 + 'orderby' => 'comment_date',
326 + 'order' => 'DESC',
342 327 )
343 328 );
344 329
345 - // Force the lookup key and full comment objects, so callers cannot break the return contract.
346 - $args['fields'] = 'all';
347 - $args['meta_key'] = 'source_id'; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
348 - $args['meta_value'] = $id; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
349 -
350 - $comment_query = new \WP_Comment_Query( $args );
351 -
352 330 if ( ! $comment_query->comments ) {
353 331 return false;
354 332 }
355 333
@@ -403,9 +381,9 @@
403 381
404 382 $query = new \WP_Comment_Query();
405 383 $comments = $query->query( $args );
406 384
407 - if ( $comments && \is_array( $comments ) ) {
385 + if ( $comments && is_array( $comments ) ) {
408 386 return $comments[0]->comment_ID;
409 387 }
410 388
411 389 return null;
@@ -421,9 +399,9 @@
421 399 * @return string[] An array of classes.
422 400 */
423 401 public static function comment_class( $classes, $css_class, $comment_id ) {
424 402 // Check if ActivityPub comment.
425 - if ( 'activitypub' === \get_comment_meta( $comment_id, 'protocol', true ) ) {
403 + if ( 'activitypub' === get_comment_meta( $comment_id, 'protocol', true ) ) {
426 404 $classes[] = 'activitypub-comment';
427 405 }
428 406
429 407 return $classes;
@@ -451,11 +429,12 @@
451 429
452 430 if ( \in_array( $comment_type, $comment_types, true ) ) {
453 431 $where .= $wpdb->prepare( ' AND comment_type = %s', $comment_type );
454 432 } else {
455 - $placeholders = \implode( ', ', \array_fill( 0, \count( $comment_types ), '%s' ) );
433 + $comment_types = \array_map( 'esc_sql', $comment_types );
434 + $placeholders = implode( ', ', array_fill( 0, count( $comment_types ), '%s' ) );
456 435 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.NotPrepared
457 - $where .= $wpdb->prepare( \sprintf( ' AND comment_type NOT IN (%s)', $placeholders ), ...$comment_types );
436 + $where .= $wpdb->prepare( sprintf( ' AND comment_type NOT IN (%s)', $placeholders ), ...$comment_types );
458 437 }
459 438
460 439 return $where;
461 440 }
@@ -585,9 +564,9 @@
585 564 $activity_type = \sanitize_key( $activity_type );
586 565 $comment_types = self::get_comment_types();
587 566
588 567 foreach ( $comment_types as $comment_type ) {
589 - if ( \in_array( $activity_type, $comment_type['activity_types'], true ) ) {
568 + if ( in_array( $activity_type, $comment_type['activity_types'], true ) ) {
590 569 return $comment_type;
591 570 }
592 571 }
593 572
@@ -601,9 +580,9 @@
601 580 */
602 581 public static function get_comment_types() {
603 582 global $activitypub_comment_types;
604 583
605 - return (array) $activitypub_comment_types;
584 + return $activitypub_comment_types;
606 585 }
607 586
608 587 /**
609 588 * Is this a registered comment type.
@@ -626,15 +605,15 @@
626 605 *
627 606 * @return array The registered custom comment type slugs.
628 607 */
629 608 public static function get_comment_type_slugs() {
630 - if ( ! \did_action( 'init' ) ) {
631 - \_doing_it_wrong( __METHOD__, 'This function should not be called before the init action has run. Comment types are only available after init.', '7.5.0' );
609 + if ( ! did_action( 'init' ) ) {
610 + _doing_it_wrong( __METHOD__, 'This function should not be called before the init action has run. Comment types are only available after init.', '7.5.0' );
632 611
633 612 return array();
634 613 }
635 614
636 - return \array_keys( self::get_comment_types() );
615 + return array_keys( self::get_comment_types() );
637 616 }
638 617
639 618 /**
640 619 * Get the custom comment type.
@@ -648,16 +627,16 @@
648 627 *
649 628 * @return array The comment type.
650 629 */
651 630 public static function get_comment_type( $type ) {
652 - $type = \strtolower( $type );
653 - $type = \sanitize_key( $type );
631 + $type = strtolower( $type );
632 + $type = sanitize_key( $type );
654 633
655 634 $comment_types = self::get_comment_types();
656 635 $type_array = array();
657 636
658 637 // Check array keys.
659 - if ( \in_array( $type, \array_keys( $comment_types ), true ) ) {
638 + if ( in_array( $type, array_keys( $comment_types ), true ) ) {
660 639 $type_array = $comment_types[ $type ];
661 640 }
662 641
663 642 /**
@@ -664,9 +643,9 @@
664 643 * Filter the comment type.
665 644 *
666 645 * @param array $type_array The comment type.
667 646 */
668 - return \apply_filters( "activitypub_comment_type_{$type}", $type_array );
647 + return apply_filters( "activitypub_comment_type_{$type}", $type_array );
669 648 }
670 649
671 650 /**
672 651 * Get a comment type attribute.
@@ -690,9 +669,9 @@
690 669 *
691 670 * @param mixed $value The value of the attribute.
692 671 * @param string $type The comment type.
693 672 */
694 - return \apply_filters( "activitypub_comment_type_{$attr}", $value, $type );
673 + return apply_filters( "activitypub_comment_type_{$attr}", $value, $type );
695 674 }
696 675
697 676 /**
698 677 * Register the comment types used by the ActivityPub plugin.
@@ -700,10 +679,10 @@
700 679 public static function register_comment_types() {
701 680 register_comment_type(
702 681 'repost',
703 682 array(
704 - 'label' => \__( 'Reposts', 'activitypub' ),
705 - 'singular' => \__( 'Repost', 'activitypub' ),
683 + 'label' => __( 'Reposts', 'activitypub' ),
684 + 'singular' => __( 'Repost', 'activitypub' ),
706 685 'description' => 'A repost (or Announce) is when a post appears in the timeline because someone else shared it, while still showing the original author as the source.',
707 686 'icon' => '♻️',
708 687 'class' => 'p-repost',
709 688 'type' => 'repost',
@@ -708,13 +687,13 @@
708 687 'class' => 'p-repost',
709 688 'type' => 'repost',
710 689 'collection' => 'reposts',
711 690 'activity_types' => array( 'announce' ),
712 - 'excerpt' => \html_entity_decode( \__( '&hellip; reposted this!', 'activitypub' ) ),
691 + 'excerpt' => html_entity_decode( \__( '&hellip; reposted this!', 'activitypub' ) ),
713 692 /* translators: %d: Number of reposts */
714 - 'count_single' => \_x( '%d repost', 'number of reposts', 'activitypub' ),
693 + 'count_single' => _x( '%d repost', 'number of reposts', 'activitypub' ),
715 694 /* translators: %d: Number of reposts */
716 - 'count_plural' => \_x( '%d reposts', 'number of reposts', 'activitypub' ),
695 + 'count_plural' => _x( '%d reposts', 'number of reposts', 'activitypub' ),
717 696 )
718 697 );
719 698
720 699 register_comment_type(
@@ -719,10 +698,10 @@
719 698
720 699 register_comment_type(
721 700 'like',
722 701 array(
723 - 'label' => \__( 'Likes', 'activitypub' ),
724 - 'singular' => \__( 'Like', 'activitypub' ),
702 + 'label' => __( 'Likes', 'activitypub' ),
703 + 'singular' => __( 'Like', 'activitypub' ),
725 704 'description' => 'A like is a small positive reaction that shows appreciation for a post without sharing it further.',
726 705 'icon' => '👍',
727 706 'class' => 'p-like',
728 707 'type' => 'like',
@@ -727,13 +706,13 @@
727 706 'class' => 'p-like',
728 707 'type' => 'like',
729 708 'collection' => 'likes',
730 709 'activity_types' => array( 'like' ),
731 - 'excerpt' => \html_entity_decode( \__( '&hellip; liked this!', 'activitypub' ) ),
710 + 'excerpt' => html_entity_decode( \__( '&hellip; liked this!', 'activitypub' ) ),
732 711 /* translators: %d: Number of likes */
733 - 'count_single' => \_x( '%d like', 'number of likes', 'activitypub' ),
712 + 'count_single' => _x( '%d like', 'number of likes', 'activitypub' ),
734 713 /* translators: %d: Number of likes */
735 - 'count_plural' => \_x( '%d likes', 'number of likes', 'activitypub' ),
714 + 'count_plural' => _x( '%d likes', 'number of likes', 'activitypub' ),
736 715 )
737 716 );
738 717
739 718 register_comment_type(
@@ -738,10 +717,10 @@
738 717
739 718 register_comment_type(
740 719 'quote',
741 720 array(
742 - 'label' => \__( 'Quotes', 'activitypub' ),
743 - 'singular' => \__( 'Quote', 'activitypub' ),
721 + 'label' => __( 'Quotes', 'activitypub' ),
722 + 'singular' => __( 'Quote', 'activitypub' ),
744 723 'description' => 'A quote is when a post is shared along with an added comment, so the original post appears together with the sharer&#8217;s own words.',
745 724 'icon' => '❞',
746 725 'class' => 'p-quote',
747 726 'type' => 'quote',
@@ -746,13 +725,13 @@
746 725 'class' => 'p-quote',
747 726 'type' => 'quote',
748 727 'collection' => 'quotes',
749 728 'activity_types' => array( 'quote' ),
750 - 'excerpt' => \html_entity_decode( \__( '&hellip; quoted this!', 'activitypub' ) ),
729 + 'excerpt' => html_entity_decode( \__( '&hellip; quoted this!', 'activitypub' ) ),
751 730 /* translators: %d: Number of quotes */
752 - 'count_single' => \_x( '%d quote', 'number of quotes', 'activitypub' ),
731 + 'count_single' => _x( '%d quote', 'number of quotes', 'activitypub' ),
753 732 /* translators: %d: Number of quotes */
754 - 'count_plural' => \_x( '%d quotes', 'number of quotes', 'activitypub' ),
733 + 'count_plural' => _x( '%d quotes', 'number of quotes', 'activitypub' ),
755 734 )
756 735 );
757 736 }
758 737
@@ -764,11 +743,11 @@
764 743 * @return array show avatars on Activities
765 744 */
766 745 public static function get_avatar_comment_types( $types ) {
767 746 $comment_types = self::get_comment_type_slugs();
768 - $types = \array_merge( $types, $comment_types );
747 + $types = array_merge( $types, $comment_types );
769 748
770 - return \array_unique( $types );
749 + return array_unique( $types );
771 750 }
772 751
773 752 /**
774 753 * Excludes likes and reposts from comment queries.
@@ -784,9 +763,9 @@
784 763 return;
785 764 }
786 765
787 766 // Do not exclude likes and reposts on ActivityPub requests.
788 - if ( \defined( 'ACTIVITYPUB_REQUEST' ) && ACTIVITYPUB_REQUEST ) {
767 + if ( defined( 'ACTIVITYPUB_REQUEST' ) && ACTIVITYPUB_REQUEST ) {
789 768 return;
790 769 }
791 770
792 771 // Do not exclude likes and reposts on REST requests (handled by rest_comment_query).
@@ -881,19 +860,8 @@
881 860 ) {
882 861 return 1;
883 862 }
884 863
885 - /*
886 - * Always auto-approve comments on remote posts (ap_post) since
887 - * they are not visible in the WP admin comment moderation screen.
888 - */
889 - $post_id = $comment_data['comment_post_ID'];
890 - $post = \get_post( $post_id );
891 -
892 - if ( $post && \in_array( $post->post_type, self::hide_for(), true ) ) {
893 - return 1;
894 - }
895 -
896 864 if ( '1' !== \get_option( 'comment_previously_approved' ) ) {
897 865 return $approved;
898 866 }
899 867
@@ -914,8 +882,15 @@
914 882 if ( 1 === (int) $ok_to_comment ) {
915 883 return 1;
916 884 }
917 885
886 + $post_id = $comment_data['comment_post_ID'];
887 + $post = \get_post( $post_id );
888 +
889 + if ( $post && in_array( $post->post_type, self::hide_for(), true ) ) {
890 + return 1;
891 + }
892 +
918 893 return $approved;
919 894 }
920 895
921 896 /**
@@ -942,9 +917,9 @@
942 917 * @return int|null The updated comment count, or null to use the default query.
943 918 */
944 919 public static function pre_wp_update_comment_count_now( $new_count, $old_count, $post_id ) {
945 920 if ( null === $new_count ) {
946 - $excluded_types = \array_filter( self::get_comment_type_slugs(), array( self::class, 'is_comment_type_enabled' ) );
921 + $excluded_types = array_filter( self::get_comment_type_slugs(), array( self::class, 'is_comment_type_enabled' ) );
947 922
948 923 if ( ! empty( $excluded_types ) ) {
949 924 /*
950 925 * Include 'note' type when Gutenberg's filter is registered, so a
@@ -966,14 +941,14 @@
966 941 * @param string[] $excluded_types The comment type slugs to exclude.
967 942 * @param int $post_id The post ID.
968 943 */
969 944 $excluded_types = \apply_filters( 'activitypub_excluded_comment_types', $excluded_types, $post_id );
970 - $excluded_types = \array_unique( \array_filter( $excluded_types ) );
945 + $excluded_types = array_unique( array_filter( $excluded_types ) );
971 946
972 947 global $wpdb;
973 948
974 949 // phpcs:ignore WordPress.DB
975 - $new_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1' AND comment_type NOT IN ('" . \implode( "','", $excluded_types ) . "')", $post_id ) );
950 + $new_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1' AND comment_type NOT IN ('" . implode( "','", $excluded_types ) . "')", $post_id ) );
976 951 }
977 952 }
978 953
979 954 return $new_count;
@@ -985,9 +960,9 @@
985 960 * @param string $comment_type The comment type.
986 961 * @return bool True if the comment type is enabled.
987 962 */
988 963 public static function is_comment_type_enabled( $comment_type ) {
989 - return '1' === \get_option( "activitypub_allow_{$comment_type}s", '1' );
964 + return '1' === get_option( "activitypub_allow_{$comment_type}s", '1' );
990 965 }
991 966
992 967 /**
993 968 * Get post types to hide comments for in admin.
@@ -997,9 +972,9 @@
997 972 *
998 973 * @return string[] Array of post type names to hide comments for.
999 974 */
1000 975 public static function hide_for() {
1001 - $post_types = array( Remote_Posts::POST_TYPE );
976 + $post_types = array( Posts::POST_TYPE );
1002 977
1003 978 /**
1004 979 * Filters the list of post types to hide comments for.
1005 980 *
@@ -1039,36 +1014,15 @@
1039 1014 * Selectively unescape emoji images in comment author.
1040 1015 *
1041 1016 * This runs at priority 20 after WordPress's esc_html() filter on comment_author.
1042 1017 *
1043 - * @since 9.3.0 Added the `$comment_id` parameter.
1018 + * @param string $author The comment author name (already escaped by WordPress).
1044 1019 *
1045 - * @param string $author The comment author name (already escaped by WordPress).
1046 - * @param int|string $comment_id Optional. The comment ID, as a numeric string from core. Default 0.
1047 - *
1048 1020 * @return string The comment author name with emoji images unescaped.
1049 1021 */
1050 - public static function unescape_emoji( $author, $comment_id = 0 ) {
1051 - /*
1052 - * Core always passes the comment ID, but plugins and themes re-apply this filter
1053 - * with the name alone. Fall back to the comment in scope so a one-argument caller
1054 - * does not leave the emoji img sitting there as escaped text.
1055 - */
1056 - if ( ! $comment_id ) {
1057 - $comment_id = \get_comment_ID();
1058 - }
1059 -
1060 - /*
1061 - * Only ActivityPub comments can carry emoji, since render_emoji() is what puts the
1062 - * img tags there in the first place. Scope this the same way, so an author name
1063 - * written by anything else is never decoded -- the substring check below is not a
1064 - * reliable signal on its own, and this filter runs on every comment on the site.
1065 - */
1022 + public static function unescape_emoji( $author ) {
1023 + // Only attempt to unescape if there are emoji images present in the escaped string.
1066 1024 if ( false === \strpos( $author, 'class=&quot;emoji&quot;' ) ) {
1067 - return $author;
1068 - }
1069 -
1070 - if ( ! \get_comment_meta( $comment_id, '_activitypub_remote_actor_id', true ) ) {
1071 1025 return $author;
1072 1026 }
1073 1027
1074 1028 // Decode entities so we can selectively restore emoji <img> tags.