PluginProbe
ActivityPub / 2.1.1
ActivityPub v2.1.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 / collection / class-interactions.php

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

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