PluginProbe
ActivityPub / 2.5.0
ActivityPub v2.5.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
activitypub / includes / collection / class-interactions.php

class-interactions.php in ActivityPub 2.5.0, at includes/collection/class-interactions.php

246 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Activitypub\Collection;
3
4 use WP_Error;
5 use WP_Comment_Query;
6
7 use function Activitypub\object_to_uri;
8 use function Activitypub\url_to_commentid;
9 use function Activitypub\object_id_to_comment;
10 use function Activitypub\get_remote_metadata_by_actor;
11
12 /**
13 * ActivityPub Interactions Collection
14 */
15 class Interactions {
16 /**
17 * Add a comment to a post
18 *
19 * @param array $activity The activity-object
20 *
21 * @return array|false The commentdata or false on failure
22 */
23 public static function add_comment( $activity ) {
24 if (
25 ! isset( $activity['object'] ) ||
26 ! isset( $activity['object']['id'] )
27 ) {
28 return false;
29 }
30
31 if ( ! isset( $activity['object']['inReplyTo'] ) ) {
32 return false;
33 }
34
35 $in_reply_to = \esc_url_raw( $activity['object']['inReplyTo'] );
36 $comment_post_id = \url_to_postid( $in_reply_to );
37 $parent_comment_id = url_to_commentid( $in_reply_to );
38
39 // save only replys and reactions
40 if ( ! $comment_post_id && $parent_comment_id ) {
41 $parent_comment = get_comment( $parent_comment_id );
42 $comment_post_id = $parent_comment->comment_post_ID;
43 }
44
45 // not a reply to a post or comment
46 if ( ! $comment_post_id ) {
47 return false;
48 }
49
50 $actor = object_to_uri( $activity['actor'] );
51 $meta = get_remote_metadata_by_actor( $actor );
52
53 if ( ! $meta || \is_wp_error( $meta ) ) {
54 return false;
55 }
56
57 $commentdata = array(
58 'comment_post_ID' => $comment_post_id,
59 'comment_author' => isset( $meta['name'] ) ? \esc_attr( $meta['name'] ) : \esc_attr( $meta['preferredUsername'] ),
60 'comment_author_url' => \esc_url_raw( $meta['url'] ),
61 'comment_content' => \addslashes( $activity['object']['content'] ),
62 'comment_type' => 'comment',
63 'comment_author_email' => '',
64 'comment_parent' => $parent_comment_id ? $parent_comment_id : 0,
65 'comment_meta' => array(
66 'source_id' => \esc_url_raw( $activity['object']['id'] ),
67 'protocol' => 'activitypub',
68 ),
69 );
70
71 if ( isset( $meta['icon']['url'] ) ) {
72 $commentdata['comment_meta']['avatar_url'] = \esc_url_raw( $meta['icon']['url'] );
73 }
74
75 if ( isset( $activity['object']['url'] ) ) {
76 $commentdata['comment_meta']['source_url'] = \esc_url_raw( $activity['object']['url'] );
77 }
78
79 // disable flood control
80 \remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
81 // do not require email for AP entries
82 \add_filter( 'pre_option_require_name_email', '__return_false' );
83 // No nonce possible for this submission route
84 \add_filter(
85 'akismet_comment_nonce',
86 function () {
87 return 'inactive';
88 }
89 );
90 \add_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10, 2 );
91
92 $comment = \wp_new_comment( $commentdata, true );
93
94 \remove_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10 );
95 \remove_filter( 'pre_option_require_name_email', '__return_false' );
96 // re-add flood control
97 \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
98
99 return $comment;
100 }
101
102 /**
103 * Update a comment
104 *
105 * @param array $activity The activity-object
106 *
107 * @return array|string|int|\WP_Error|false The commentdata or false on failure
108 */
109 public static function update_comment( $activity ) {
110 $meta = get_remote_metadata_by_actor( $activity['actor'] );
111
112 //Determine comment_ID
113 $comment = object_id_to_comment( \esc_url_raw( $activity['object']['id'] ) );
114 $commentdata = \get_comment( $comment, ARRAY_A );
115
116 if ( ! $commentdata ) {
117 return false;
118 }
119
120 //found a local comment id
121 $commentdata['comment_author'] = \esc_attr( $meta['name'] ? $meta['name'] : $meta['preferredUsername'] );
122 $commentdata['comment_content'] = \addslashes( $activity['object']['content'] );
123 if ( isset( $meta['icon']['url'] ) ) {
124 $commentdata['comment_meta']['avatar_url'] = \esc_url_raw( $meta['icon']['url'] );
125 }
126
127 // disable flood control
128 \remove_action( 'check_comment_flood', 'check_comment_flood_db', 10 );
129 // do not require email for AP entries
130 \add_filter( 'pre_option_require_name_email', '__return_false' );
131 // No nonce possible for this submission route
132 \add_filter(
133 'akismet_comment_nonce',
134 function () {
135 return 'inactive';
136 }
137 );
138 \add_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10, 2 );
139
140 $state = \wp_update_comment( $commentdata, true );
141
142 \remove_filter( 'wp_kses_allowed_html', array( self::class, 'allowed_comment_html' ), 10 );
143 \remove_filter( 'pre_option_require_name_email', '__return_false' );
144 // re-add flood control
145 \add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
146
147 if ( 1 === $state ) {
148 return $commentdata;
149 } else {
150 return $state; // Either `false` or a `WP_Error` instance or `0` or `1`!
151 }
152 }
153
154 /**
155 * Get interaction(s) for a given URL/ID.
156 *
157 * @param strin $url The URL/ID to get interactions for.
158 *
159 * @return array The interactions as WP_Comment objects.
160 */
161 public static function get_interaction_by_id( $url ) {
162 $args = array(
163 'nopaging' => true,
164 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
165 'meta_query' => array(
166 'relation' => 'AND',
167 array(
168 'key' => 'protocol',
169 'value' => 'activitypub',
170 ),
171 array(
172 'relation' => 'OR',
173 array(
174 'key' => 'source_url',
175 'value' => $url,
176 ),
177 array(
178 'key' => 'source_id',
179 'value' => $url,
180 ),
181 ),
182 ),
183 );
184
185 $query = new WP_Comment_Query( $args );
186 return $query->comments;
187 }
188
189 /**
190 * Get interaction(s) for a given actor.
191 *
192 * @param string $actor The Actor-URL.
193 *
194 * @return array The interactions as WP_Comment objects.
195 */
196 public static function get_interactions_by_actor( $actor ) {
197 $meta = get_remote_metadata_by_actor( $actor );
198
199 // get URL, because $actor seems to be the ID
200 if ( $meta && ! is_wp_error( $meta ) && isset( $meta['url'] ) ) {
201 $actor = $meta['url'];
202 }
203
204 $args = array(
205 'nopaging' => true,
206 'author_url' => $actor,
207 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
208 'meta_query' => array(
209 array(
210 'key' => 'protocol',
211 'value' => 'activitypub',
212 'compare' => '=',
213 ),
214 ),
215 );
216 $comment_query = new WP_Comment_Query( $args );
217 return $comment_query->comments;
218 }
219
220 /**
221 * Adds line breaks to the list of allowed comment tags.
222 *
223 * @param array $allowed_tags Allowed HTML tags.
224 * @param string $context Context.
225 *
226 * @return array Filtered tag list.
227 */
228 public static function allowed_comment_html( $allowed_tags, $context = '' ) {
229 if ( 'pre_comment_content' !== $context ) {
230 // Do nothing.
231 return $allowed_tags;
232 }
233
234 // Add `p` and `br` to the list of allowed tags.
235 if ( ! array_key_exists( 'br', $allowed_tags ) ) {
236 $allowed_tags['br'] = array();
237 }
238
239 if ( ! array_key_exists( 'p', $allowed_tags ) ) {
240 $allowed_tags['p'] = array();
241 }
242
243 return $allowed_tags;
244 }
245 }
246