PluginProbe
ActivityPub / 2.6.0
ActivityPub v2.6.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-comment.php +138 -11 2.1.02.6.0 View file →
@@ -1,11 +1,13 @@
1 1 <?php
2 2
3 3 namespace Activitypub;
4 4
5 +use Activitypub\Collection\Users;
5 6 use WP_Comment_Query;
6 7
7 8 use function Activitypub\is_user_disabled;
9 +use function Activitypub\is_single_user;
8 10
9 11 /**
10 12 * ActivityPub Comment Class
11 13 *
@@ -19,8 +21,9 @@
19 21 public static function init() {
20 22 \add_filter( 'comment_reply_link', array( self::class, 'comment_reply_link' ), 10, 3 );
21 23 \add_filter( 'comment_class', array( self::class, 'comment_class' ), 10, 3 );
22 24 \add_filter( 'get_comment_link', array( self::class, 'remote_comment_link' ), 11, 3 );
25 + \add_action( 'wp_enqueue_scripts', array( self::class, 'enqueue_scripts' ) );
23 26 }
24 27
25 28 /**
26 29 * Filter the comment reply link.
@@ -35,15 +38,51 @@
35 38 * @return string The filtered HTML markup for the comment reply link.
36 39 */
37 40 public static function comment_reply_link( $link, $args, $comment ) {
38 41 if ( self::are_comments_allowed( $comment ) ) {
42 + $user_id = get_current_user_id();
43 + if ( $user_id && self::was_received( $comment ) && \user_can( $user_id, 'activitypub' ) ) {
44 + return self::create_fediverse_reply_link( $link, $args );
45 + }
46 +
39 47 return $link;
40 48 }
41 49
42 - return apply_filters( 'activitypub_comment_reply_link', '' );
50 + $attrs = array(
51 + 'selectedComment' => self::generate_id( $comment ),
52 + 'commentId' => $comment->comment_ID,
53 + );
54 +
55 + $div = sprintf(
56 + '<div class="activitypub-remote-reply" data-attrs="%s"></div>',
57 + esc_attr( wp_json_encode( $attrs ) )
58 + );
59 +
60 + return apply_filters( 'activitypub_comment_reply_link', $div );
43 61 }
44 62
45 63 /**
64 + * Create a link to reply to a federated comment.
65 + * This function adds a title attribute to the reply link to inform the user
66 + * that the comment was received from the fediverse and the reply will be sent
67 + * to the original author.
68 + *
69 + * @param string $link The HTML markup for the comment reply link.
70 + * @param array $args The args provided by the `comment_reply_link` filter.
71 + *
72 + * @return string The modified HTML markup for the comment reply link.
73 + */
74 + private static function create_fediverse_reply_link( $link, $args ) {
75 + $str_to_replace = sprintf( '>%s<', $args['reply_text'] );
76 + $replace_with = sprintf(
77 + ' title="%s">%s<',
78 + esc_attr__( 'This comment was received from the fediverse and your reply will be sent to the original author', 'activitypub' ),
79 + esc_html__( 'Reply with federation', 'activitypub' )
80 + );
81 + return str_replace( $str_to_replace, $replace_with, $link );
82 + }
83 +
84 + /**
46 85 * Check if it is allowed to comment to a comment.
47 86 *
48 87 * Checks if the comment is local only or if the user can comment federated comments.
49 88 *
@@ -63,8 +102,13 @@
63 102 if ( ! $current_user ) {
64 103 return false;
65 104 }
66 105
106 + if ( is_single_user() && \user_can( $current_user, 'publish_posts' ) ) {
107 + // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user
108 + $current_user = Users::BLOG_USER_ID;
109 + }
110 +
67 111 $is_user_disabled = is_user_disabled( $current_user );
68 112
69 113 if ( $is_user_disabled ) {
70 114 return false;
@@ -168,8 +212,13 @@
168 212 if ( ! $user_id ) {
169 213 return false;
170 214 }
171 215
216 + if ( is_single_user() && \user_can( $user_id, 'publish_posts' ) ) {
217 + // On a single user site, comments by users with the `publish_posts` capability will be federated as the blog user
218 + $user_id = Users::BLOG_USER_ID;
219 + }
220 +
172 221 $is_user_disabled = is_user_disabled( $user_id );
173 222
174 223 // user is disabled for federation
175 224 if ( $is_user_disabled ) {
@@ -191,9 +240,9 @@
191 240 * Examine a comment ID and look up an existing comment it represents.
192 241 *
193 242 * @param string $id ActivityPub object ID (usually a URL) to check.
194 243 *
195 - * @return int|boolean Comment ID, or false on failure.
244 + * @return \WP_Comment|false Comment object, or false on failure.
196 245 */
197 246 public static function object_id_to_comment( $id ) {
198 247 $comment_query = new WP_Comment_Query(
199 248 array(
@@ -226,9 +275,9 @@
226 275 return null;
227 276 }
228 277
229 278 // check for local comment
230 - if ( \wp_parse_url( \site_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) {
279 + if ( \wp_parse_url( \home_url(), \PHP_URL_HOST ) === \wp_parse_url( $url, \PHP_URL_HOST ) ) {
231 280 $query = \wp_parse_url( $url, \PHP_URL_QUERY );
232 281
233 282 if ( $query ) {
234 283 parse_str( $query, $params );
@@ -318,21 +367,99 @@
318 367 *
319 368 * @return string ActivityPub URI for comment
320 369 */
321 370 public static function generate_id( $comment ) {
322 - $comment = get_comment( $comment );
371 + $comment = \get_comment( $comment );
372 + $comment_meta = \get_comment_meta( $comment->comment_ID );
323 373
324 374 // show external comment ID if it exists
325 - $source_id = get_comment_meta( $comment->comment_ID, 'source_id', true );
326 - if ( ! empty( $source_id ) ) {
327 - return $source_id;
375 + if ( ! empty( $comment_meta['source_id'][0] ) ) {
376 + return $comment_meta['source_id'][0];
377 + } elseif ( ! empty( $comment_meta['source_url'][0] ) ) {
378 + return $comment_meta['source_url'][0];
328 379 }
329 380
330 381 // generate URI based on comment ID
331 - return \add_query_arg(
382 + return \add_query_arg( 'c', $comment->comment_ID, \trailingslashit( \home_url() ) );
383 + }
384 +
385 + /**
386 + * Check if a post has remote comments
387 + *
388 + * @param int $post_id The post ID.
389 + *
390 + * @return bool True if the post has remote comments, false otherwise.
391 + */
392 + private static function post_has_remote_comments( $post_id ) {
393 + $comments = \get_comments(
332 394 array(
333 - 'c' => $comment->comment_ID,
334 - ),
335 - \trailingslashit( site_url() )
395 + 'post_id' => $post_id,
396 + 'meta_query' => array(
397 + 'relation' => 'AND',
398 + array(
399 + 'key' => 'protocol',
400 + 'value' => 'activitypub',
401 + 'compare' => '=',
402 + ),
403 + array(
404 + 'key' => 'source_id',
405 + 'compare' => 'EXISTS',
406 + ),
407 + ),
408 + )
336 409 );
410 +
411 + return ! empty( $comments );
412 + }
413 +
414 + /**
415 + * Enqueue scripts for remote comments
416 + */
417 + public static function enqueue_scripts() {
418 + if ( ! \is_singular() || \is_user_logged_in() ) {
419 + // only on single pages, only for logged out users
420 + return;
421 + }
422 +
423 + if ( ! \post_type_supports( \get_post_type(), 'activitypub' ) ) {
424 + // post type does not support ActivityPub
425 + return;
426 + }
427 +
428 + if ( ! \comments_open() || ! \get_comments_number() ) {
429 + // no comments, no need to load the script
430 + return;
431 + }
432 +
433 + if ( ! self::post_has_remote_comments( \get_the_ID() ) ) {
434 + // no remote comments, no need to load the script
435 + return;
436 + }
437 +
438 + $handle = 'activitypub-remote-reply';
439 + $data = array(
440 + 'namespace' => ACTIVITYPUB_REST_NAMESPACE,
441 + );
442 + $js = sprintf( 'var _activityPubOptions = %s;', wp_json_encode( $data ) );
443 + $asset_file = ACTIVITYPUB_PLUGIN_DIR . 'build/remote-reply/index.asset.php';
444 +
445 + if ( \file_exists( $asset_file ) ) {
446 + $assets = require_once $asset_file;
447 +
448 + \wp_enqueue_script(
449 + $handle,
450 + \plugins_url( 'build/remote-reply/index.js', __DIR__ ),
451 + $assets['dependencies'],
452 + $assets['version'],
453 + true
454 + );
455 + \wp_add_inline_script( $handle, $js, 'before' );
456 +
457 + \wp_enqueue_style(
458 + $handle,
459 + \plugins_url( 'build/remote-reply/style-index.css', __DIR__ ),
460 + [ 'wp-components' ],
461 + $assets['version']
462 + );
463 + }
337 464 }
338 465 }