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

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

363 lines 9.1 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\Webfinger;
11 use Activitypub\Comment as Comment_Utils;
12 use Activitypub\Model\Blog;
13 use Activitypub\Collection\Actors;
14 use Activitypub\Collection\Replies;
15
16 use function Activitypub\is_single_user;
17 use function Activitypub\get_rest_url_by_path;
18 use function Activitypub\was_comment_received;
19 use function Activitypub\get_comment_ancestors;
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 * @see \Activitypub\Activity\Base_Object
43 *
44 * @return \Activitypub\Activity\Base_Object The ActivityPub Object.
45 */
46 public function to_object() {
47 $comment = $this->item;
48 $object = parent::to_object();
49
50 $object->set_url( $this->get_id() );
51 $object->set_type( 'Note' );
52
53 $published = \strtotime( $comment->comment_date_gmt );
54 $object->set_published( \gmdate( 'Y-m-d\TH:i:s\Z', $published ) );
55
56 $updated = \get_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', true );
57 if ( $updated > $published ) {
58 $object->set_updated( \gmdate( 'Y-m-d\TH:i:s\Z', $updated ) );
59 }
60
61 $object->set_content_map(
62 array(
63 $this->get_locale() => $this->get_content(),
64 )
65 );
66
67 return $object;
68 }
69
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;
96 }
97
98 /**
99 * Returns the User-URL of the Author of the Post.
100 *
101 * If `single_user` mode is enabled, the URL of the Blog-User is returned.
102 *
103 * @return string The User-URL.
104 */
105 protected function get_attributed_to() {
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;
109 }
110
111 return $this->get_actor_object()->get_id();
112 }
113
114 /**
115 * Returns the content for the ActivityPub Item.
116 *
117 * The content will be generated based on the user settings.
118 *
119 * @return string The content.
120 */
121 protected function get_content() {
122 $comment = $this->item;
123 $content = $comment->comment_content;
124 $mentions = '';
125
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() );
145 $content = \preg_replace( '/[\n\r\t]/', '', $content );
146 $content = \trim( $content );
147
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 );
157 }
158
159 /**
160 * Returns the in-reply-to for the ActivityPub Item.
161 *
162 * @return false|string|null The URL of the in-reply-to.
163 */
164 protected function get_in_reply_to() {
165 $comment = $this->item;
166 $parent_comment = null;
167
168 if ( $comment->comment_parent ) {
169 $parent_comment = \get_comment( $comment->comment_parent );
170 }
171
172 if ( $parent_comment ) {
173 $in_reply_to = Comment_Utils::get_source_id( $parent_comment->comment_ID );
174 if ( ! $in_reply_to && ! empty( $parent_comment->user_id ) ) {
175 $in_reply_to = Comment_Utils::generate_id( $parent_comment );
176 }
177 } else {
178 $in_reply_to = \get_permalink( $comment->comment_post_ID );
179 }
180
181 return $in_reply_to;
182 }
183
184 /**
185 * Returns the ID of the ActivityPub Object.
186 *
187 * @see https://www.w3.org/TR/activitypub/#obj-id
188 * @see https://github.com/tootsuite/mastodon/issues/13879
189 *
190 * @return string ActivityPub URI for comment
191 */
192 protected function get_id() {
193 $comment = $this->item;
194 return Comment_Utils::generate_id( $comment );
195 }
196
197 /**
198 * Returns the User-Object of the Author of the Post.
199 *
200 * If `single_user` mode is enabled, the Blog-User is returned.
201 *
202 * @return \Activitypub\Activity\Actor The User-Object.
203 */
204 protected function get_actor_object() {
205 if ( $this->actor_object ) {
206 return $this->actor_object;
207 }
208
209 $blog_user = new Blog();
210 $this->actor_object = $blog_user;
211
212 if ( is_single_user() ) {
213 return $blog_user;
214 }
215
216 $user = Actors::get_by_id( $this->item->user_id );
217
218 if ( $user && ! is_wp_error( $user ) ) {
219 $this->actor_object = $user;
220 return $user;
221 }
222
223 return $blog_user;
224 }
225
226 /**
227 * Helper function to get the @-Mentions from the comment content.
228 *
229 * @return array The list of @-Mentions.
230 */
231 protected function get_mentions() {
232 \add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) );
233
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 );
244 }
245
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 /**
264 * Collect all other Users that participated in this comment-thread
265 * to send them a notification about the new reply.
266 *
267 * @param array $mentions Optional. The already mentioned ActivityPub users. Default empty array.
268 *
269 * @return array The list of all Repliers.
270 */
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 ) ) {
274 return $mentions;
275 }
276
277 $ancestors = $this->get_comment_ancestors();
278 if ( ! $ancestors ) {
279 return $mentions;
280 }
281
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;
289 }
290 }
291 }
292
293 return $mentions;
294 }
295
296 /**
297 * Returns the updated date of the comment.
298 *
299 * @return string|null The updated date of the comment.
300 */
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 );
304
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 );
361 }
362 }
363