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

class-comment.php in ActivityPub 5.3.1, at includes/class-comment.php

799 lines 22.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ActivityPub Comment Class
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub;
9
10 use Activitypub\Collection\Actors;
11 use WP_Comment_Query;
12
13 /**
14 * ActivityPub Comment Class.
15 *
16 * This class is a helper/utils class that provides a collection of static
17 * methods that are used to handle comments.
18 */
19 class Comment {
20 /**
21 * Initialize the class, registering WordPress hooks.
22 */
23 public static function init() {
24 self::register_comment_types();
25
26 \add_filter( 'comment_reply_link', array( self::class, 'comment_reply_link' ), 10, 3 );
27 \add_filter( 'comment_class', array( self::class, 'comment_class' ), 10, 3 );
28 \add_filter( 'get_comment_link', array( self::class, 'remote_comment_link' ), 11, 3 );
29 \add_action( 'wp_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
30 \add_action( 'pre_get_comments', array( static::class, 'comment_query' ) );
31 \add_filter( 'pre_comment_approved', array( static::class, 'pre_comment_approved' ), 10, 2 );
32 \add_filter( 'get_avatar_comment_types', array( static::class, 'get_avatar_comment_types' ), 99 );
33 \add_filter( 'pre_wp_update_comment_count_now', array( static::class, 'pre_wp_update_comment_count_now' ), 10, 3 );
34 }
35
36 /**
37 * Filter the comment reply link.
38 *
39 * We don't want to show the comment reply link for federated comments
40 * if the user is disabled for federation.
41 *
42 * @param string $link The HTML markup for the comment reply link.
43 * @param array $args An array of arguments overriding the defaults.
44 * @param \WP_Comment $comment The object of the comment being replied.
45 *
46 * @return string The filtered HTML markup for the comment reply link.
47 */
48 public static function comment_reply_link( $link, $args, $comment ) {
49 if ( self::are_comments_allowed( $comment ) ) {
50 $user_id = get_current_user_id();
51 if ( $user_id && self::was_received( $comment ) && \user_can( $user_id, 'activitypub' ) ) {
52 return self::create_fediverse_reply_link( $link, $args );
53 }
54
55 return $link;
56 }
57
58 $attrs = array(
59 'selectedComment' => self::generate_id( $comment ),
60 'commentId' => $comment->comment_ID,
61 );
62
63 $div = sprintf(
64 '<div class="activitypub-remote-reply" data-attrs="%s"></div>',
65 esc_attr( wp_json_encode( $attrs ) )
66 );
67
68 /**
69 * Filters the HTML markup for the ActivityPub remote comment reply container.
70 *
71 * @param string $div The HTML markup for the remote reply container. Default is a div
72 * with class 'activitypub-remote-reply' and data attributes for
73 * the selected comment ID and internal comment ID.
74 */
75 return apply_filters( 'activitypub_comment_reply_link', $div );
76 }
77
78 /**
79 * Create a link to reply to a federated comment.
80 *
81 * This function adds a title attribute to the reply link to inform the user
82 * that the comment was received from the fediverse and the reply will be sent
83 * to the original author.
84 *
85 * @param string $link The HTML markup for the comment reply link.
86 * @param array $args The args provided by the `comment_reply_link` filter.
87 *
88 * @return string The modified HTML markup for the comment reply link.
89 */
90 private static function create_fediverse_reply_link( $link, $args ) {
91 $str_to_replace = sprintf( '>%s<', $args['reply_text'] );
92 $replace_with = sprintf(
93 ' title="%s">%s<',
94 esc_attr__( 'This comment was received from the fediverse and your reply will be sent to the original author', 'activitypub' ),
95 esc_html__( 'Reply with federation', 'activitypub' )
96 );
97 return str_replace( $str_to_replace, $replace_with, $link );
98 }
99
100 /**
101 * Check if it is allowed to comment to a comment.
102 *
103 * Checks if the comment is local only or if the user can comment federated comments.
104 *
105 * @param mixed $comment Comment object or ID.
106 *
107 * @return boolean True if the user can comment, false otherwise.
108 */
109 public static function are_comments_allowed( $comment ) {
110 $comment = \get_comment( $comment );
111
112 if ( ! self::was_received( $comment ) ) {
113 return true;
114 }
115
116 $current_user = get_current_user_id();
117
118 if ( ! $current_user ) {
119 return false;
120 }
121
122 if ( is_single_user() && \user_can( $current_user, 'publish_posts' ) ) {
123 // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user.
124 $current_user = Actors::BLOG_USER_ID;
125 }
126
127 $is_user_disabled = is_user_disabled( $current_user );
128
129 if ( $is_user_disabled ) {
130 return false;
131 }
132
133 return true;
134 }
135
136 /**
137 * Check if a comment is federated.
138 *
139 * We consider a comment federated if comment was received via ActivityPub.
140 *
141 * Use this function to check if it is comment that was received via ActivityPub.
142 *
143 * @param mixed $comment Comment object or ID.
144 *
145 * @return boolean True if the comment is federated, false otherwise.
146 */
147 public static function was_received( $comment ) {
148 $comment = \get_comment( $comment );
149
150 if ( ! $comment ) {
151 return false;
152 }
153
154 $protocol = \get_comment_meta( $comment->comment_ID, 'protocol', true );
155
156 if ( 'activitypub' === $protocol ) {
157 return true;
158 }
159
160 return false;
161 }
162
163 /**
164 * Check if a comment was federated.
165 *
166 * This function checks if a comment was federated via ActivityPub.
167 *
168 * @param mixed $comment Comment object or ID.
169 *
170 * @return boolean True if the comment was federated, false otherwise.
171 */
172 public static function was_sent( $comment ) {
173 $comment = \get_comment( $comment );
174
175 if ( ! $comment ) {
176 return false;
177 }
178
179 $status = \get_comment_meta( $comment->comment_ID, 'activitypub_status', true );
180
181 if ( $status ) {
182 return true;
183 }
184
185 return false;
186 }
187
188 /**
189 * Check if a comment is local only.
190 *
191 * This function checks if a comment is local only and was not sent or received via ActivityPub.
192 *
193 * @param mixed $comment Comment object or ID.
194 *
195 * @return boolean True if the comment is local only, false otherwise.
196 */
197 public static function is_local( $comment ) {
198 if ( self::was_sent( $comment ) || self::was_received( $comment ) ) {
199 return false;
200 }
201
202 return true;
203 }
204
205 /**
206 * Check if a comment should be federated.
207 *
208 * We consider a comment should be federated if it is authored by a user that is
209 * not disabled for federation and if it is a reply directly to the post or to a
210 * federated comment.
211 *
212 * Use this function to check if a comment should be federated.
213 *
214 * @param mixed $comment Comment object or ID.
215 *
216 * @return boolean True if the comment should be federated, false otherwise.
217 */
218 public static function should_be_federated( $comment ) {
219 // We should not federate federated comments.
220 if ( self::was_received( $comment ) ) {
221 return false;
222 }
223
224 $comment = \get_comment( $comment );
225 $user_id = $comment->user_id;
226
227 // Comments without user can't be federated.
228 if ( ! $user_id ) {
229 return false;
230 }
231
232 if ( is_single_user() && \user_can( $user_id, 'activitypub' ) ) {
233 // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user.
234 $user_id = Actors::BLOG_USER_ID;
235 }
236
237 $is_user_disabled = is_user_disabled( $user_id );
238
239 // User is disabled for federation.
240 if ( $is_user_disabled ) {
241 return false;
242 }
243
244 // It is a comment to the post and can be federated.
245 if ( empty( $comment->comment_parent ) ) {
246 return true;
247 }
248
249 // Check if parent comment is federated.
250 $parent_comment = \get_comment( $comment->comment_parent );
251
252 return ! self::is_local( $parent_comment );
253 }
254
255 /**
256 * Examine a comment ID and look up an existing comment it represents.
257 *
258 * @param string $id ActivityPub object ID (usually a URL) to check.
259 *
260 * @return \WP_Comment|false Comment object, or false on failure.
261 */
262 public static function object_id_to_comment( $id ) {
263 $comment_query = new WP_Comment_Query(
264 array(
265 'meta_key' => 'source_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
266 'meta_value' => $id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
267 'orderby' => 'comment_date',
268 'order' => 'DESC',
269 )
270 );
271
272 if ( ! $comment_query->comments ) {
273 return false;
274 }
275
276 return $comment_query->comments[0];
277 }
278
279 /**
280 * Verify if URL is a local comment, or if it is a previously received
281 * remote comment (For threading comments locally).
282 *
283 * @param string $url The URL to check.
284 *
285 * @return string|null Comment ID or null if not found.
286 */
287 public static function url_to_commentid( $url ) {
288 if ( ! $url || ! filter_var( $url, \FILTER_VALIDATE_URL ) ) {
289 return null;
290 }
291
292 // Check for local comment.
293 if ( \wp_parse_url( \home_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) {
294 $query = \wp_parse_url( $url, \PHP_URL_QUERY );
295
296 if ( $query ) {
297 parse_str( $query, $params );
298
299 if ( ! empty( $params['c'] ) ) {
300 $comment = \get_comment( $params['c'] );
301
302 if ( $comment ) {
303 return $comment->comment_ID;
304 }
305 }
306 }
307 }
308
309 $args = array(
310 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
311 'meta_query' => array(
312 'relation' => 'OR',
313 array(
314 'key' => 'source_url',
315 'value' => $url,
316 ),
317 array(
318 'key' => 'source_id',
319 'value' => $url,
320 ),
321 ),
322 );
323
324 $query = new WP_Comment_Query();
325 $comments = $query->query( $args );
326
327 if ( $comments && is_array( $comments ) ) {
328 return $comments[0]->comment_ID;
329 }
330
331 return null;
332 }
333
334 /**
335 * Filters the CSS classes to add an ActivityPub class.
336 *
337 * @param string[] $classes An array of comment classes.
338 * @param string[] $css_class An array of additional classes added to the list.
339 * @param string $comment_id The comment ID as a numeric string.
340 *
341 * @return string[] An array of classes.
342 */
343 public static function comment_class( $classes, $css_class, $comment_id ) {
344 // Check if ActivityPub comment.
345 if ( 'activitypub' === get_comment_meta( $comment_id, 'protocol', true ) ) {
346 $classes[] = 'activitypub-comment';
347 }
348
349 return $classes;
350 }
351
352 /**
353 * Gets the public comment id via the WordPress comments meta.
354 *
355 * @param int $wp_comment_id The internal WordPress comment ID.
356 * @param bool $fallback Whether the code should fall back to `source_url` if `source_id` is not set.
357 *
358 * @return string|null The ActivityPub id/url of the comment.
359 */
360 public static function get_source_id( $wp_comment_id, $fallback = true ) {
361 $comment_meta = \get_comment_meta( $wp_comment_id );
362
363 if ( ! empty( $comment_meta['source_id'][0] ) ) {
364 return $comment_meta['source_id'][0];
365 } elseif ( ! empty( $comment_meta['source_url'][0] ) && $fallback ) {
366 return $comment_meta['source_url'][0];
367 }
368
369 return null;
370 }
371
372 /**
373 * Gets the public comment url via the WordPress comments meta.
374 *
375 * @param int $wp_comment_id The internal WordPress comment ID.
376 * @param bool $fallback Whether the code should fall back to `source_id` if `source_url` is not set.
377 *
378 * @return string|null The ActivityPub id/url of the comment.
379 */
380 public static function get_source_url( $wp_comment_id, $fallback = true ) {
381 $comment_meta = \get_comment_meta( $wp_comment_id );
382
383 if ( ! empty( $comment_meta['source_url'][0] ) ) {
384 return $comment_meta['source_url'][0];
385 } elseif ( ! empty( $comment_meta['source_id'][0] ) && $fallback ) {
386 return $comment_meta['source_id'][0];
387 }
388
389 return null;
390 }
391
392 /**
393 * Link remote comments to source url.
394 *
395 * @param string $comment_link The comment link.
396 * @param object|\WP_Comment $comment The comment object.
397 *
398 * @return string $url
399 */
400 public static function remote_comment_link( $comment_link, $comment ) {
401 if ( ! $comment || is_admin() ) {
402 return $comment_link;
403 }
404
405 $public_comment_link = self::get_source_url( $comment->comment_ID );
406
407 return $public_comment_link ?? $comment_link;
408 }
409
410
411 /**
412 * Generates an ActivityPub URI for a comment
413 *
414 * @param \WP_Comment|int $comment A comment object or comment ID.
415 *
416 * @return string ActivityPub URI for comment
417 */
418 public static function generate_id( $comment ) {
419 $comment = \get_comment( $comment );
420
421 // Show external comment ID if it exists.
422 $public_comment_link = self::get_source_id( $comment->comment_ID );
423
424 if ( $public_comment_link ) {
425 return $public_comment_link;
426 }
427
428 // Generate URI based on comment ID.
429 return \add_query_arg( 'c', $comment->comment_ID, \trailingslashit( \home_url() ) );
430 }
431
432 /**
433 * Check if a post has remote comments
434 *
435 * @param int $post_id The post ID.
436 *
437 * @return bool True if the post has remote comments, false otherwise.
438 */
439 private static function post_has_remote_comments( $post_id ) {
440 $comments = \get_comments(
441 array(
442 'post_id' => $post_id,
443 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
444 'meta_query' => array(
445 'relation' => 'AND',
446 array(
447 'key' => 'protocol',
448 'value' => 'activitypub',
449 'compare' => '=',
450 ),
451 array(
452 'key' => 'source_id',
453 'compare' => 'EXISTS',
454 ),
455 ),
456 )
457 );
458
459 return ! empty( $comments );
460 }
461
462 /**
463 * Enqueue scripts for remote comments
464 */
465 public static function enqueue_scripts() {
466 if ( ! \is_singular() || \is_user_logged_in() ) {
467 // Only on single pages, only for logged-out users.
468 return;
469 }
470
471 if ( ! \post_type_supports( \get_post_type(), 'activitypub' ) ) {
472 // Post type does not support ActivityPub.
473 return;
474 }
475
476 if ( ! \comments_open() || ! \get_comments_number() ) {
477 // No comments, no need to load the script.
478 return;
479 }
480
481 if ( ! self::post_has_remote_comments( \get_the_ID() ) ) {
482 // No remote comments, no need to load the script.
483 return;
484 }
485
486 $handle = 'activitypub-remote-reply';
487 $data = array(
488 'namespace' => ACTIVITYPUB_REST_NAMESPACE,
489 'defaultAvatarUrl' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg',
490 );
491 $js = sprintf( 'var _activityPubOptions = %s;', wp_json_encode( $data ) );
492 $asset_file = ACTIVITYPUB_PLUGIN_DIR . 'build/remote-reply/index.asset.php';
493
494 if ( \file_exists( $asset_file ) ) {
495 $assets = require_once $asset_file;
496
497 \wp_enqueue_script(
498 $handle,
499 \plugins_url( 'build/remote-reply/index.js', __DIR__ ),
500 $assets['dependencies'],
501 $assets['version'],
502 true
503 );
504 \wp_add_inline_script( $handle, $js, 'before' );
505
506 \wp_enqueue_style(
507 $handle,
508 \plugins_url( 'build/remote-reply/style-index.css', __DIR__ ),
509 array( 'wp-components' ),
510 $assets['version']
511 );
512 }
513 }
514
515 /**
516 * Get the comment type by activity type.
517 *
518 * @param string $activity_type The activity type.
519 *
520 * @return array|null The comment type.
521 */
522 public static function get_comment_type_by_activity_type( $activity_type ) {
523 $activity_type = \strtolower( $activity_type );
524 $activity_type = \sanitize_key( $activity_type );
525 $comment_types = self::get_comment_types();
526
527 foreach ( $comment_types as $comment_type ) {
528 if ( in_array( $activity_type, $comment_type['activity_types'], true ) ) {
529 return $comment_type;
530 }
531 }
532
533 return null;
534 }
535
536 /**
537 * Return the registered custom comment types.
538 *
539 * @return array The registered custom comment types
540 */
541 public static function get_comment_types() {
542 global $activitypub_comment_types;
543
544 return $activitypub_comment_types;
545 }
546
547 /**
548 * Is this a registered comment type.
549 *
550 * @param string $slug The slug of the type.
551 *
552 * @return boolean True if registered.
553 */
554 public static function is_registered_comment_type( $slug ) {
555 $slug = \strtolower( $slug );
556 $slug = \sanitize_key( $slug );
557
558 $comment_types = self::get_comment_types();
559
560 return isset( $comment_types[ $slug ] );
561 }
562
563 /**
564 * Return the registered custom comment type slugs.
565 *
566 * @return array The registered custom comment type slugs.
567 */
568 public static function get_comment_type_slugs() {
569 return array_keys( self::get_comment_types() );
570 }
571
572 /**
573 * Return the registered custom comment type slugs.
574 *
575 * @deprecated 4.5.0 Use get_comment_type_slugs instead.
576 *
577 * @return array The registered custom comment type slugs.
578 */
579 public static function get_comment_type_names() {
580 _deprecated_function( __METHOD__, '4.5.0', 'get_comment_type_slugs' );
581
582 return self::get_comment_type_slugs();
583 }
584
585 /**
586 * Get the custom comment type.
587 *
588 * Check if the type is registered, if not, check if it is a custom type.
589 *
590 * It looks for the array key in the registered types and returns the array.
591 * If it is not found, it looks for the type in the custom types and returns the array.
592 *
593 * @param string $type The comment type.
594 *
595 * @return array The comment type.
596 */
597 public static function get_comment_type( $type ) {
598 $type = strtolower( $type );
599 $type = sanitize_key( $type );
600
601 $comment_types = self::get_comment_types();
602 $type_array = array();
603
604 // Check array keys.
605 if ( in_array( $type, array_keys( $comment_types ), true ) ) {
606 $type_array = $comment_types[ $type ];
607 }
608
609 /**
610 * Filter the comment type.
611 *
612 * @param array $type_array The comment type.
613 */
614 return apply_filters( "activitypub_comment_type_{$type}", $type_array );
615 }
616
617 /**
618 * Get a comment type attribute.
619 *
620 * @param string $type The comment type.
621 * @param string $attr The attribute to get.
622 *
623 * @return mixed The value of the attribute.
624 */
625 public static function get_comment_type_attr( $type, $attr ) {
626 $type_array = self::get_comment_type( $type );
627
628 if ( $type_array && isset( $type_array[ $attr ] ) ) {
629 $value = $type_array[ $attr ];
630 } else {
631 $value = '';
632 }
633
634 /**
635 * Filter the comment type attribute.
636 *
637 * @param mixed $value The value of the attribute.
638 * @param string $type The comment type.
639 */
640 return apply_filters( "activitypub_comment_type_{$attr}", $value, $type );
641 }
642
643 /**
644 * Register the comment types used by the ActivityPub plugin.
645 */
646 public static function register_comment_types() {
647 register_comment_type(
648 'repost',
649 array(
650 'label' => __( 'Reposts', 'activitypub' ),
651 'singular' => __( 'Repost', 'activitypub' ),
652 'description' => __( 'A repost on the indieweb is a post that is purely a 100% re-publication of another (typically someone else\'s) post.', 'activitypub' ),
653 'icon' => '♻️',
654 'class' => 'p-repost',
655 'type' => 'repost',
656 'collection' => 'reposts',
657 'activity_types' => array( 'announce' ),
658 'excerpt' => html_entity_decode( \__( '&hellip; reposted this!', 'activitypub' ) ),
659 /* translators: %d: Number of reposts */
660 'count_single' => _x( '%d repost', 'number of reposts', 'activitypub' ),
661 /* translators: %d: Number of reposts */
662 'count_plural' => _x( '%d reposts', 'number of reposts', 'activitypub' ),
663 )
664 );
665
666 register_comment_type(
667 'like',
668 array(
669 'label' => __( 'Likes', 'activitypub' ),
670 'singular' => __( 'Like', 'activitypub' ),
671 'description' => __( 'A like is a popular webaction button and in some cases post type on various silos such as Facebook and Instagram.', 'activitypub' ),
672 'icon' => '👍',
673 'class' => 'p-like',
674 'type' => 'like',
675 'collection' => 'likes',
676 'activity_types' => array( 'like' ),
677 'excerpt' => html_entity_decode( \__( '&hellip; liked this!', 'activitypub' ) ),
678 /* translators: %d: Number of likes */
679 'count_single' => _x( '%d like', 'number of likes', 'activitypub' ),
680 /* translators: %d: Number of likes */
681 'count_plural' => _x( '%d likes', 'number of likes', 'activitypub' ),
682 )
683 );
684 }
685
686 /**
687 * Show avatars on Activities if set.
688 *
689 * @param array $types List of avatar enabled comment types.
690 *
691 * @return array show avatars on Activities
692 */
693 public static function get_avatar_comment_types( $types ) {
694 $comment_types = self::get_comment_type_slugs();
695 $types = array_merge( $types, $comment_types );
696
697 return array_unique( $types );
698 }
699
700 /**
701 * Excludes likes and reposts from comment queries.
702 *
703 * @author Jan Boddez
704 *
705 * @see https://github.com/janboddez/indieblocks/blob/a2d59de358031056a649ee47a1332ce9e39d4ce2/includes/functions.php#L423-L432
706 *
707 * @param WP_Comment_Query $query Comment count.
708 */
709 public static function comment_query( $query ) {
710 if ( ! $query instanceof WP_Comment_Query ) {
711 return;
712 }
713
714 // Do not exclude likes and reposts on ActivityPub requests.
715 if ( defined( 'ACTIVITYPUB_REQUEST' ) && ACTIVITYPUB_REQUEST ) {
716 return;
717 }
718
719 // Do not exclude likes and reposts on REST requests.
720 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
721 return;
722 }
723
724 // Do not exclude likes and reposts on admin pages or on non-singular pages.
725 if ( is_admin() || ! is_singular() ) {
726 return;
727 }
728
729 // Do not exclude likes and reposts if the query is for comments.
730 if ( ! empty( $query->query_vars['type__in'] ) || ! empty( $query->query_vars['type'] ) ) {
731 return;
732 }
733
734 // Exclude likes and reposts by the ActivityPub plugin.
735 $query->query_vars['type__not_in'] = self::get_comment_type_slugs();
736 }
737
738 /**
739 * Filter the comment status before it is set.
740 *
741 * @param string $approved The approved comment status.
742 * @param array $commentdata The comment data.
743 *
744 * @return boolean `true` if the comment is approved, `false` otherwise.
745 */
746 public static function pre_comment_approved( $approved, $commentdata ) {
747 if ( $approved || \is_wp_error( $approved ) ) {
748 return $approved;
749 }
750
751 if ( '1' !== \get_option( 'comment_previously_approved' ) ) {
752 return $approved;
753 }
754
755 if (
756 empty( $commentdata['comment_meta']['protocol'] ) ||
757 'activitypub' !== $commentdata['comment_meta']['protocol']
758 ) {
759 return $approved;
760 }
761
762 global $wpdb;
763
764 $author = $commentdata['comment_author'];
765 $author_url = $commentdata['comment_author_url'];
766 // phpcs:ignore
767 $ok_to_comment = $wpdb->get_var( $wpdb->prepare( "SELECT comment_approved FROM $wpdb->comments WHERE comment_author = %s AND comment_author_url = %s and comment_approved = '1' LIMIT 1", $author, $author_url ) );
768
769 if ( 1 === (int) $ok_to_comment ) {
770 return 1;
771 }
772
773 return $approved;
774 }
775
776 /**
777 * Filters the comment count to exclude ActivityPub comment types.
778 *
779 * @param int|null $new_count The new comment count. Default null.
780 * @param int $old_count The old comment count.
781 * @param int $post_id Post ID.
782 *
783 * @return int|null The updated comment count, or null to use the default query.
784 */
785 public static function pre_wp_update_comment_count_now( $new_count, $old_count, $post_id ) {
786 if ( null === $new_count ) {
787 global $wpdb;
788
789 $excluded_types = self::get_comment_type_slugs();
790
791 // phpcs:ignore WordPress.DB
792 $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 ) );
793
794 }
795
796 return $new_count;
797 }
798 }
799