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/transformer/class-comment.php +206 -127 2.1.05.3.1 View file →
@@ -1,21 +1,26 @@
1 1 <?php
2 +/**
3 + * WordPress Comment Transformer file.
4 + *
5 + * @package Activitypub
6 + */
7 +
2 8 namespace Activitypub\Transformer;
3 9
4 -use WP_Comment;
5 -use WP_Comment_Query;
6 -
7 10 use Activitypub\Webfinger;
8 11 use Activitypub\Comment as Comment_Utils;
9 -use Activitypub\Model\Blog_User;
10 -use Activitypub\Collection\Users;
11 -use Activitypub\Transformer\Base;
12 +use Activitypub\Model\Blog;
13 +use Activitypub\Collection\Actors;
14 +use Activitypub\Collection\Replies;
12 15
13 16 use function Activitypub\is_single_user;
14 17 use function Activitypub\get_rest_url_by_path;
18 +use function Activitypub\was_comment_received;
19 +use function Activitypub\get_comment_ancestors;
15 20
16 21 /**
17 - * WordPress Comment Transformer
22 + * WordPress Comment Transformer.
18 23 *
19 24 * The Comment Transformer is responsible for transforming a WP_Comment object into different
20 25 * Object-Types.
21 26 *
@@ -24,34 +29,23 @@
24 29 * - Activitypub\Activity\Base_Object
25 30 */
26 31 class Comment extends Base {
27 32 /**
28 - * Returns the User-ID of the WordPress Comment.
33 + * The User as Actor Object.
29 34 *
30 - * @return int The User-ID of the WordPress Comment
35 + * @var \Activitypub\Activity\Actor
31 36 */
32 - public function get_wp_user_id() {
33 - return $this->wp_object->user_id;
34 - }
37 + private $actor_object = null;
35 38
36 39 /**
37 - * Change the User-ID of the WordPress Comment.
40 + * Transforms the WP_Comment object to an ActivityPub Object.
38 41 *
39 - * @return int The User-ID of the WordPress Comment
40 - */
41 - public function change_wp_user_id( $user_id ) {
42 - $this->wp_object->user_id = $user_id;
43 - }
44 -
45 - /**
46 - * Transforms the WP_Comment object to an ActivityPub Object
47 - *
48 42 * @see \Activitypub\Activity\Base_Object
49 43 *
50 - * @return \Activitypub\Activity\Base_Object The ActivityPub Object
44 + * @return \Activitypub\Activity\Base_Object The ActivityPub Object.
51 45 */
52 46 public function to_object() {
53 - $comment = $this->wp_object;
47 + $comment = $this->item;
54 48 $object = parent::to_object();
55 49
56 50 $object->set_url( $this->get_id() );
57 51 $object->set_type( 'Note' );
@@ -68,18 +62,38 @@
68 62 array(
69 63 $this->get_locale() => $this->get_content(),
70 64 )
71 65 );
72 - $path = sprintf( 'users/%d/followers', intval( $comment->comment_author ) );
73 66
74 - $object->set_to(
75 - array(
76 - 'https://www.w3.org/ns/activitystreams#Public',
77 - get_rest_url_by_path( $path ),
78 - )
79 - );
67 + return $object;
68 + }
80 69
81 - return $object;
70 + /**
71 + * Get the content visibility.
72 + *
73 + * @return string The content visibility.
74 + */
75 + public function get_content_visibility() {
76 + if ( $this->content_visibility ) {
77 + return $this->content_visibility;
78 + }
79 +
80 + $comment = $this->item;
81 + $post = \get_post( $comment->comment_post_ID );
82 +
83 + if ( ! $post ) {
84 + return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
85 + }
86 +
87 + $content_visibility = \get_post_meta( $post->ID, 'activitypub_content_visibility', true );
88 +
89 + if ( ! $content_visibility ) {
90 + return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
91 + }
92 +
93 + $this->content_visibility = $content_visibility;
94 +
95 + return $this->content_visibility;
82 96 }
83 97
84 98 /**
85 99 * Returns the User-URL of the Author of the Post.
@@ -88,14 +102,14 @@
88 102 *
89 103 * @return string The User-URL.
90 104 */
91 105 protected function get_attributed_to() {
92 - if ( is_single_user() ) {
93 - $user = new Blog_User();
94 - return $user->get_url();
106 + // If the comment was received via ActivityPub, return the author URL.
107 + if ( was_comment_received( $this->wp_object ) ) {
108 + return $this->wp_object->comment_author_url;
95 109 }
96 110
97 - return Users::get_by_id( $this->wp_object->user_id )->get_url();
111 + return $this->get_actor_object()->get_id();
98 112 }
99 113
100 114 /**
101 115 * Returns the content for the ActivityPub Item.
@@ -104,30 +118,53 @@
104 118 *
105 119 * @return string The content.
106 120 */
107 121 protected function get_content() {
108 - // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
109 - $comment = $this->wp_object;
110 - $content = $comment->comment_content;
122 + $comment = $this->item;
123 + $content = $comment->comment_content;
124 + $mentions = '';
111 125
112 - $content = \wpautop( $content );
126 + foreach ( $this->extract_reply_context() as $acct => $url ) {
127 + $mentions .= sprintf(
128 + '<a rel="mention" class="u-url mention" href="%s">%s</a> ',
129 + esc_url( $url ),
130 + esc_html( $acct )
131 + );
132 + }
133 + $content = $mentions . $content;
134 +
135 + /**
136 + * Filter the content of the comment.
137 + *
138 + * @param string $content The content of the comment.
139 + * @param \WP_Comment $comment The comment object.
140 + * @param array $args The arguments.
141 + *
142 + * @return string The filtered content of the comment.
143 + */
144 + $content = \apply_filters( 'comment_text', $content, $comment, array() );
113 145 $content = \preg_replace( '/[\n\r\t]/', '', $content );
114 146 $content = \trim( $content );
115 - $content = \apply_filters( 'activitypub_the_content', $content, $comment );
116 147
117 - return $content;
148 + /**
149 + * Filter the content of the comment.
150 + *
151 + * @param string $content The content of the comment.
152 + * @param \WP_Comment $comment The comment object.
153 + *
154 + * @return string The filtered content of the comment.
155 + */
156 + return \apply_filters( 'activitypub_the_content', $content, $comment );
118 157 }
119 158
120 159 /**
121 160 * Returns the in-reply-to for the ActivityPub Item.
122 161 *
123 - * @return int The URL of the in-reply-to.
162 + * @return false|string|null The URL of the in-reply-to.
124 163 */
125 164 protected function get_in_reply_to() {
126 - $comment = $this->wp_object;
127 -
165 + $comment = $this->item;
128 166 $parent_comment = null;
129 - $in_reply_to = null;
130 167
131 168 if ( $comment->comment_parent ) {
132 169 $parent_comment = \get_comment( $comment->comment_parent );
133 170 }
@@ -132,15 +169,10 @@
132 169 $parent_comment = \get_comment( $comment->comment_parent );
133 170 }
134 171
135 172 if ( $parent_comment ) {
136 - $comment_meta = \get_comment_meta( $parent_comment->comment_ID );
137 -
138 - if ( ! empty( $comment_meta['source_id'][0] ) ) {
139 - $in_reply_to = $comment_meta['source_id'][0];
140 - } elseif ( ! empty( $comment_meta['source_url'][0] ) ) {
141 - $in_reply_to = $comment_meta['source_url'][0];
142 - } elseif ( ! empty( $parent_comment->user_id ) ) {
173 + $in_reply_to = Comment_Utils::get_source_id( $parent_comment->comment_ID );
174 + if ( ! $in_reply_to && ! empty( $parent_comment->user_id ) ) {
143 175 $in_reply_to = Comment_Utils::generate_id( $parent_comment );
144 176 }
145 177 } else {
146 178 $in_reply_to = \get_permalink( $comment->comment_post_ID );
@@ -157,55 +189,39 @@
157 189 *
158 190 * @return string ActivityPub URI for comment
159 191 */
160 192 protected function get_id() {
161 - $comment = $this->wp_object;
193 + $comment = $this->item;
162 194 return Comment_Utils::generate_id( $comment );
163 195 }
164 196
165 197 /**
166 - * Returns a list of Mentions, used in the Comment.
198 + * Returns the User-Object of the Author of the Post.
167 199 *
168 - * @see https://docs.joinmastodon.org/spec/activitypub/#Mention
200 + * If `single_user` mode is enabled, the Blog-User is returned.
169 201 *
170 - * @return array The list of Mentions.
202 + * @return \Activitypub\Activity\Actor The User-Object.
171 203 */
172 - protected function get_cc() {
173 - $cc = array();
204 + protected function get_actor_object() {
205 + if ( $this->actor_object ) {
206 + return $this->actor_object;
207 + }
174 208
175 - $mentions = $this->get_mentions();
176 - if ( $mentions ) {
177 - foreach ( $mentions as $mention => $url ) {
178 - $cc[] = $url;
179 - }
209 + $blog_user = new Blog();
210 + $this->actor_object = $blog_user;
211 +
212 + if ( is_single_user() ) {
213 + return $blog_user;
180 214 }
181 215
182 - return array_unique( $cc );
183 - }
216 + $user = Actors::get_by_id( $this->item->user_id );
184 217
185 - /**
186 - * Returns a list of Tags, used in the Comment.
187 - *
188 - * This includes Hash-Tags and Mentions.
189 - *
190 - * @return array The list of Tags.
191 - */
192 - protected function get_tag() {
193 - $tags = array();
194 -
195 - $mentions = $this->get_mentions();
196 - if ( $mentions ) {
197 - foreach ( $mentions as $mention => $url ) {
198 - $tag = array(
199 - 'type' => 'Mention',
200 - 'href' => \esc_url( $url ),
201 - 'name' => \esc_html( $mention ),
202 - );
203 - $tags[] = $tag;
204 - }
218 + if ( $user && ! is_wp_error( $user ) ) {
219 + $this->actor_object = $user;
220 + return $user;
205 221 }
206 222
207 - return \array_unique( $tags, SORT_REGULAR );
223 + return $blog_user;
208 224 }
209 225
210 226 /**
211 227 * Helper function to get the @-Mentions from the comment content.
@@ -214,46 +230,63 @@
214 230 */
215 231 protected function get_mentions() {
216 232 \add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) );
217 233
218 - return apply_filters( 'activitypub_extract_mentions', array(), $this->wp_object->comment_content, $this->wp_object );
234 + /**
235 + * Filter the mentions in the comment.
236 + *
237 + * @param array $mentions The list of mentions.
238 + * @param string $content The content of the comment.
239 + * @param \WP_Comment $comment The comment object.
240 + *
241 + * @return array The filtered list of mentions.
242 + */
243 + return apply_filters( 'activitypub_extract_mentions', array(), $this->item->comment_content, $this->item );
219 244 }
220 245
221 246 /**
247 + * Gets the ancestors of the comment, but only the ones that are ActivityPub comments.
248 + *
249 + * @return array The list of ancestors.
250 + */
251 + protected function get_comment_ancestors() {
252 + $ancestors = get_comment_ancestors( $this->item );
253 +
254 + // Now that we have the full tree of ancestors, only return the ones received from the fediverse.
255 + return array_filter(
256 + $ancestors,
257 + function ( $comment_id ) {
258 + return \get_comment_meta( $comment_id, 'protocol', true ) === 'activitypub';
259 + }
260 + );
261 + }
262 +
263 + /**
222 264 * Collect all other Users that participated in this comment-thread
223 265 * to send them a notification about the new reply.
224 266 *
225 - * @param array $mentions The already mentioned ActivityPub users
267 + * @param array $mentions Optional. The already mentioned ActivityPub users. Default empty array.
226 268 *
227 269 * @return array The list of all Repliers.
228 270 */
229 - public function extract_reply_context( $mentions ) {
230 - // Check if `$this->wp_object` is a WP_Comment
231 - if ( 'WP_Comment' !== get_class( $this->wp_object ) ) {
271 + public function extract_reply_context( $mentions = array() ) {
272 + // Check if `$this->item` is a WP_Comment.
273 + if ( 'WP_Comment' !== get_class( $this->item ) ) {
232 274 return $mentions;
233 275 }
234 276
235 - $comment_query = new WP_Comment_Query(
236 - array(
237 - 'post_id' => $this->wp_object->comment_post_ID,
238 - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
239 - 'meta_query' => array(
240 - array(
241 - 'key' => 'protocol',
242 - 'value' => 'activitypub',
243 - ),
244 - ),
245 - )
246 - );
277 + $ancestors = $this->get_comment_ancestors();
278 + if ( ! $ancestors ) {
279 + return $mentions;
280 + }
247 281
248 - if ( $comment_query->comments ) {
249 - foreach ( $comment_query->comments as $comment ) {
250 - if ( ! empty( $comment->comment_author_url ) ) {
251 - $acct = Webfinger::uri_to_acct( $comment->comment_author_url );
252 - if ( $acct && ! is_wp_error( $acct ) ) {
253 - $acct = str_replace( 'acct:', '@', $acct );
254 - $mentions[ $acct ] = $comment->comment_author_url;
255 - }
282 + foreach ( $ancestors as $comment_id ) {
283 + $comment = \get_comment( $comment_id );
284 + if ( $comment && ! empty( $comment->comment_author_url ) ) {
285 + $acct = Webfinger::uri_to_acct( $comment->comment_author_url );
286 + if ( $acct && ! is_wp_error( $acct ) ) {
287 + $acct = str_replace( 'acct:', '@', $acct );
288 + $mentions[ $acct ] = $comment->comment_author_url;
256 289 }
257 290 }
258 291 }
259 292
@@ -260,24 +293,70 @@
260 293 return $mentions;
261 294 }
262 295
263 296 /**
264 - * Returns the locale of the post.
297 + * Returns the updated date of the comment.
265 298 *
266 - * @return string The locale of the post.
299 + * @return string|null The updated date of the comment.
267 300 */
268 - public function get_locale() {
269 - $comment_id = $this->wp_object->ID;
270 - $lang = \strtolower( \strtok( \get_locale(), '_-' ) );
301 + public function get_updated() {
302 + $updated = \get_comment_meta( $this->item->comment_ID, 'activitypub_comment_modified', true );
303 + $published = \get_comment_meta( $this->item->comment_ID, 'activitypub_comment_published', true );
271 304
272 - /**
273 - * Filter the locale of the comment.
274 - *
275 - * @param string $lang The locale of the comment.
276 - * @param int $comment_id The comment ID.
277 - * @param WP_Post $post The comment object.
278 - *
279 - * @return string The filtered locale of the comment.
280 - */
281 - return apply_filters( 'activitypub_comment_locale', $lang, $comment_id, $this->wp_object );
305 + if ( $updated > $published ) {
306 + return \gmdate( 'Y-m-d\TH:i:s\Z', $updated );
307 + }
308 +
309 + return null;
310 + }
311 +
312 + /**
313 + * Returns the published date of the comment.
314 + *
315 + * @return string The published date of the comment.
316 + */
317 + public function get_published() {
318 + return \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( $this->item->comment_date_gmt ) );
319 + }
320 +
321 + /**
322 + * Returns the URL of the comment.
323 + *
324 + * @return string The URL of the comment.
325 + */
326 + public function get_url() {
327 + return $this->get_id();
328 + }
329 +
330 + /**
331 + * Returns the type of the comment.
332 + *
333 + * @return string The type of the comment.
334 + */
335 + public function get_type() {
336 + return 'Note';
337 + }
338 +
339 + /**
340 + * Get the context of the post.
341 + *
342 + * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
343 + *
344 + * @return string The context of the post.
345 + */
346 + protected function get_context() {
347 + if ( $this->item->comment_post_ID ) {
348 + return get_rest_url_by_path( sprintf( 'posts/%d/context', $this->item->comment_post_ID ) );
349 + }
350 +
351 + return null;
352 + }
353 +
354 + /**
355 + * Get the replies Collection.
356 + *
357 + * @return array|null The replies collection on success or null on failure.
358 + */
359 + public function get_replies() {
360 + return Replies::get_collection( $this->item );
282 361 }
283 362 }