PluginProbe
ActivityPub / 7.8.2
ActivityPub v7.8.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
activitypub / includes / transformer / class-comment.php

class-comment.php in ActivityPub 7.8.2, at includes/transformer/class-comment.php

419 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WordPress Comment Transformer file.
4 *
5 * @package Activitypub
6 */
7
8 namespace Activitypub\Transformer;
9
10 use Activitypub\Collection\Actors;
11 use Activitypub\Collection\Replies;
12 use Activitypub\Comment as Comment_Utils;
13 use Activitypub\Model\Blog;
14 use Activitypub\Webfinger;
15
16 use function Activitypub\get_comment_ancestors;
17 use function Activitypub\get_rest_url_by_path;
18 use function Activitypub\is_single_user;
19 use function Activitypub\was_comment_received;
20
21 /**
22 * WordPress Comment Transformer.
23 *
24 * The Comment Transformer is responsible for transforming a WP_Comment object into different
25 * Object-Types.
26 *
27 * Currently supported are:
28 *
29 * - Activitypub\Activity\Base_Object
30 */
31 class Comment extends Base {
32 /**
33 * The User as Actor Object.
34 *
35 * @var \Activitypub\Activity\Actor
36 */
37 private $actor_object = null;
38
39 /**
40 * Transforms the WP_Comment object to an ActivityPub Object.
41 *
42 * @return \Activitypub\Activity\Base_Object The ActivityPub Object.
43 */
44 public function to_object() {
45 $comment = $this->item;
46 $object = parent::to_object();
47
48 $object->set_url( $this->get_id() );
49 $object->set_type( 'Note' );
50
51 $published = \strtotime( $comment->comment_date_gmt );
52 $object->set_published( \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, $published ) );
53
54 $updated = \get_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', true );
55 if ( $updated > $published ) {
56 $object->set_updated( \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, $updated ) );
57 }
58
59 $object->set_content_map(
60 array(
61 $this->get_locale() => $this->get_content(),
62 )
63 );
64
65 return $object;
66 }
67
68 /**
69 * Get the content visibility.
70 *
71 * @return string The content visibility.
72 */
73 public function get_content_visibility() {
74 if ( $this->content_visibility ) {
75 return $this->content_visibility;
76 }
77
78 $comment = $this->item;
79 $post = \get_post( $comment->comment_post_ID );
80
81 if ( ! $post ) {
82 return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
83 }
84
85 $content_visibility = \get_post_meta( $post->ID, 'activitypub_content_visibility', true );
86
87 if ( ! $content_visibility ) {
88 return ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC;
89 }
90
91 $this->content_visibility = $content_visibility;
92
93 return $this->content_visibility;
94 }
95
96 /**
97 * Returns the User-URL of the Author of the Post.
98 *
99 * If `single_user` mode is enabled, the URL of the Blog-User is returned.
100 *
101 * @return string The User-URL.
102 */
103 protected function get_attributed_to() {
104 // If the comment was received via ActivityPub, return the author URL.
105 if ( was_comment_received( $this->item ) ) {
106 return $this->item->comment_author_url;
107 }
108
109 return $this->get_actor_object()->get_id();
110 }
111
112 /**
113 * Returns the content for the ActivityPub Item.
114 *
115 * The content will be generated based on the user settings.
116 *
117 * @return string The content.
118 */
119 protected function get_content() {
120 $comment = $this->item;
121 $content = $comment->comment_content;
122 $mentions = '';
123
124 foreach ( $this->extract_reply_context() as $acct => $url ) {
125 $mentions .= sprintf(
126 '<a rel="mention" class="u-url mention" href="%1$s" title="%2$s">%3$s</a> ',
127 esc_url( $url ),
128 esc_attr( $acct ),
129 esc_html( '@' . strtok( $acct, '@' ) )
130 );
131 }
132 $content = $mentions . $content;
133
134 /**
135 * Filter the content of the comment.
136 *
137 * @param string $content The content of the comment.
138 * @param \WP_Comment $comment The comment object.
139 * @param array $args The arguments.
140 *
141 * @return string The filtered content of the comment.
142 */
143 $content = \apply_filters( 'comment_text', $content, $comment, array() );
144 $content = \preg_replace( '/[\n\r\t]/', '', $content );
145 $content = \trim( $content );
146
147 /**
148 * Filter the content of the comment.
149 *
150 * @param string $content The content of the comment.
151 * @param \WP_Comment $comment The comment object.
152 *
153 * @return string The filtered content of the comment.
154 */
155 return \apply_filters( 'activitypub_the_content', $content, $comment );
156 }
157
158 /**
159 * Returns the in-reply-to for the ActivityPub Item.
160 *
161 * @return false|string|null The URL of the in-reply-to.
162 */
163 protected function get_in_reply_to() {
164 $comment = $this->item;
165 $parent_comment = null;
166
167 if ( $comment->comment_parent ) {
168 $parent_comment = \get_comment( $comment->comment_parent );
169 }
170
171 if ( $parent_comment ) {
172 $in_reply_to = Comment_Utils::get_source_id( $parent_comment->comment_ID );
173 if ( ! $in_reply_to && ! empty( $parent_comment->user_id ) ) {
174 $in_reply_to = Comment_Utils::generate_id( $parent_comment );
175 }
176 } else {
177 $in_reply_to = \get_permalink( $comment->comment_post_ID );
178 }
179
180 return $in_reply_to;
181 }
182
183 /**
184 * Returns the ID of the ActivityPub Object.
185 *
186 * @see https://www.w3.org/TR/activitypub/#obj-id
187 * @see https://github.com/tootsuite/mastodon/issues/13879
188 *
189 * @return string ActivityPub URI for comment
190 */
191 protected function get_id() {
192 $comment = $this->item;
193 return Comment_Utils::generate_id( $comment );
194 }
195
196 /**
197 * Returns the User-Object of the Author of the Post.
198 *
199 * If `single_user` mode is enabled, the Blog-User is returned.
200 *
201 * @return \Activitypub\Activity\Actor The User-Object.
202 */
203 protected function get_actor_object() {
204 if ( $this->actor_object ) {
205 return $this->actor_object;
206 }
207
208 $blog_user = new Blog();
209 $this->actor_object = $blog_user;
210
211 if ( is_single_user() ) {
212 return $blog_user;
213 }
214
215 $user = Actors::get_by_id( $this->item->user_id );
216
217 if ( $user && ! is_wp_error( $user ) ) {
218 $this->actor_object = $user;
219 return $user;
220 }
221
222 return $blog_user;
223 }
224
225 /**
226 * Helper function to get the @-Mentions from the comment content.
227 *
228 * @return array The list of @-Mentions.
229 */
230 protected function get_mentions() {
231 \add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) );
232
233 /**
234 * Filter the mentions in the comment.
235 *
236 * @param array $mentions The list of mentions.
237 * @param string $content The content of the comment.
238 * @param \WP_Comment $comment The comment object.
239 *
240 * @return array The filtered list of mentions.
241 */
242 return apply_filters( 'activitypub_extract_mentions', array(), $this->item->comment_content, $this->item );
243 }
244
245 /**
246 * Gets the ancestors of the comment, but only the ones that are ActivityPub comments.
247 *
248 * @return array The list of ancestors.
249 */
250 protected function get_comment_ancestors() {
251 $ancestors = get_comment_ancestors( $this->item );
252
253 // Now that we have the full tree of ancestors, only return the ones received from the fediverse.
254 return array_filter(
255 $ancestors,
256 static function ( $comment_id ) {
257 return \get_comment_meta( $comment_id, 'protocol', true ) === 'activitypub';
258 }
259 );
260 }
261
262 /**
263 * Collect all other Users that participated in this comment-thread
264 * to send them a notification about the new reply.
265 *
266 * @param array $mentions Optional. The already mentioned ActivityPub users. Default empty array.
267 *
268 * @return array The list of all Repliers.
269 */
270 public function extract_reply_context( $mentions = array() ) {
271 // Check if `$this->item` is a WP_Comment.
272 if ( 'WP_Comment' !== get_class( $this->item ) ) {
273 return $mentions;
274 }
275
276 $ancestors = $this->get_comment_ancestors();
277 if ( ! $ancestors ) {
278 return $mentions;
279 }
280
281 foreach ( $ancestors as $comment_id ) {
282 $comment = \get_comment( $comment_id );
283 if ( $comment && ! empty( $comment->comment_author_url ) ) {
284 $acct = Webfinger::uri_to_acct( $comment->comment_author_url );
285 if ( $acct && ! is_wp_error( $acct ) ) {
286 $acct = str_replace( 'acct:', '@', $acct );
287 $mentions[ $acct ] = $comment->comment_author_url;
288 }
289 }
290 }
291
292 return $mentions;
293 }
294
295 /**
296 * Returns the updated date of the comment.
297 *
298 * @return string|null The updated date of the comment.
299 */
300 public function get_updated() {
301 $updated = \get_comment_meta( $this->item->comment_ID, 'activitypub_comment_modified', true );
302 $published = \get_comment_meta( $this->item->comment_ID, 'activitypub_comment_published', true );
303
304 if ( $updated > $published ) {
305 return \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, $updated );
306 }
307
308 return null;
309 }
310
311 /**
312 * Returns the published date of the comment.
313 *
314 * @return string The published date of the comment.
315 */
316 public function get_published() {
317 return \gmdate( ACTIVITYPUB_DATE_TIME_RFC3339, \strtotime( $this->item->comment_date_gmt ) );
318 }
319
320 /**
321 * Returns the URL of the comment.
322 *
323 * @return string The URL of the comment.
324 */
325 public function get_url() {
326 return $this->get_id();
327 }
328
329 /**
330 * Returns the type of the comment.
331 *
332 * @return string The type of the comment.
333 */
334 public function get_type() {
335 return 'Note';
336 }
337
338 /**
339 * Get the context of the post.
340 *
341 * @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
342 *
343 * @return string The context of the post.
344 */
345 protected function get_context() {
346 if ( $this->item->comment_post_ID ) {
347 return get_rest_url_by_path( sprintf( 'posts/%d/context', $this->item->comment_post_ID ) );
348 }
349
350 return null;
351 }
352
353 /**
354 * Get the replies Collection.
355 *
356 * @return array|null The replies collection on success or null on failure.
357 */
358 public function get_replies() {
359 return Replies::get_collection( $this->item );
360 }
361
362 /**
363 * Get the attachment for the comment.
364 *
365 * Extracts images from comment content and returns them as ActivityPub attachments.
366 *
367 * @return array The attachments array for ActivityPub.
368 */
369 protected function get_attachment() {
370 $max_media = \get_option( 'activitypub_max_image_attachments', \ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS );
371
372 /**
373 * Filters the maximum number of media attachments allowed in a comment.
374 *
375 * @param int $max_media Maximum number of media attachments.
376 * @param \WP_Comment $item The comment object.
377 */
378 $max_media = (int) \apply_filters( 'activitypub_max_image_attachments', $max_media, $this->item );
379
380 if ( 0 === $max_media ) {
381 return array();
382 }
383
384 $media = array(
385 'image' => array(),
386 'audio' => array(),
387 'video' => array(),
388 );
389
390 // Get comment content and parse for image embeds.
391 $media = $this->parse_html_images( $media, $max_media, $this->item->comment_content );
392 $media = $this->filter_unique_attachments( $media['image'] );
393 $media = \array_slice( $media, 0, $max_media );
394
395 /**
396 * Filter the attachment IDs for a comment.
397 *
398 * @param array $media The media array.
399 * @param \WP_Comment $item The comment object.
400 *
401 * @return array The filtered attachment IDs.
402 */
403 $media = \apply_filters( 'activitypub_comment_attachment_ids', $media, $this->item );
404
405 // Transform to ActivityStreams format using Base class method.
406 $attachments = \array_filter( \array_map( array( $this, 'transform_attachment' ), $media ) );
407
408 /**
409 * Filter the attachments for a comment.
410 *
411 * @param array $attachments The attachments.
412 * @param \WP_Comment $item The comment object.
413 *
414 * @return array The filtered attachments.
415 */
416 return \apply_filters( 'activitypub_comment_attachments', $attachments, $this->item );
417 }
418 }
419