PluginProbe
ActivityPub / 2.6.0
ActivityPub v2.6.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.6.0, at includes/collection/class-interactions.php

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