PluginProbe
ActivityPub / 3.2.4
ActivityPub v3.2.4
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
← All changes | includes/model/class-follower.php +128 -92 8.2.13.2.4 View file →
@@ -1,21 +1,14 @@
1 1 <?php
2 -/**
3 - * Follower class file.
4 - *
5 - * @package Activitypub
6 - */
7 -
8 2 namespace Activitypub\Model;
9 3
4 +use WP_Error;
5 +use WP_Query;
10 6 use Activitypub\Activity\Actor;
11 7 use Activitypub\Collection\Followers;
12 -use Activitypub\Collection\Remote_Actors;
13 8
14 -use function Activitypub\extract_name_from_uri;
15 -
16 9 /**
17 - * ActivityPub Follower Class.
10 + * ActivityPub Follower Class
18 11 *
19 12 * This Object represents a single Follower.
20 13 * There is no direct reference to a WordPress User here.
21 14 *
@@ -21,62 +14,31 @@
21 14 *
22 15 * @author Matt Wiebe
23 16 * @author Matthias Pfefferle
24 17 *
25 - * @deprecated 7.0.0
26 18 * @see https://www.w3.org/TR/activitypub/#follow-activity-inbox
27 - *
28 - * @method int get__id() Gets the post ID of the follower record.
29 - * @method string[]|null get_image() Gets the follower's profile image data.
30 - * @method string|null get_inbox() Gets the follower's ActivityPub inbox URL.
31 - * @method string[]|null get_endpoints() Gets the follower's ActivityPub endpoints.
32 - *
33 - * @method Follower set__id( int $id ) Sets the post ID of the follower record.
34 - * @method Follower set_id( string $guid ) Sets the follower's GUID.
35 - * @method Follower set_name( string $name ) Sets the follower's display name.
36 - * @method Follower set_summary( string $summary ) Sets the follower's bio/summary.
37 - * @method Follower set_published( string $datetime ) Sets the follower's published datetime in ISO 8601 format.
38 - * @method Follower set_updated( string $datetime ) Sets the follower's last updated datetime in ISO 8601 format.
39 19 */
40 20 class Follower extends Actor {
41 21 /**
42 - * The complete Remote-Profile of the Follower.
22 + * The complete Remote-Profile of the Follower
43 23 *
44 - * @var int
24 + * @var array
45 25 */
46 26 protected $_id; // phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore
47 27
48 28 /**
49 - * Constructor.
50 - *
51 - * @deprecated Use Actor instead.
52 - */
53 - public function __construct() {
54 - \_deprecated_class( __CLASS__, '7.0.0', Actor::class );
55 - }
56 -
57 - /**
58 29 * Get the errors.
59 30 *
60 31 * @return mixed
61 32 */
62 33 public function get_errors() {
63 - return Remote_Actors::get_errors( $this->_id );
34 + return get_post_meta( $this->_id, 'activitypub_errors' );
64 35 }
65 36
66 37 /**
67 - * Clear the errors for the current Follower.
68 - *
69 - * @return bool True on success, false on failure.
70 - */
71 - public function clear_errors() {
72 - return Remote_Actors::clear_errors( $this->_id );
73 - }
74 -
75 - /**
76 38 * Get the Summary.
77 39 *
78 - * @return string The Summary.
40 + * @return int The Summary.
79 41 */
80 42 public function get_summary() {
81 43 if ( isset( $this->summary ) ) {
82 44 return $this->summary;
@@ -88,9 +50,9 @@
88 50 /**
89 51 * Getter for URL attribute.
90 52 *
91 53 * Falls back to ID, if no URL is set. This is relevant for
92 - * Platforms like Lemmy, where the ID is the URL.
54 + * Plattforms like Lemmy, where the ID is the URL.
93 55 *
94 56 * @return string The URL.
95 57 */
96 58 public function get_url() {
@@ -103,12 +65,12 @@
103 65
104 66 /**
105 67 * Reset (delete) all errors.
106 68 *
107 - * @return bool True on success, false on failure.
69 + * @return void
108 70 */
109 71 public function reset_errors() {
110 - return Remote_Actors::clear_errors( $this->_id );
72 + delete_post_meta( $this->_id, 'activitypub_errors' );
111 73 }
112 74
113 75 /**
114 76 * Count the errors.
@@ -115,9 +77,15 @@
115 77 *
116 78 * @return int The number of errors.
117 79 */
118 80 public function count_errors() {
119 - return Remote_Actors::count_errors( $this->_id );
81 + $errors = $this->get_errors();
82 +
83 + if ( is_array( $errors ) && ! empty( $errors ) ) {
84 + return count( $errors );
85 + }
86 +
87 + return 0;
120 88 }
121 89
122 90 /**
123 91 * Return the latest error message.
@@ -126,10 +94,10 @@
126 94 */
127 95 public function get_latest_error_message() {
128 96 $errors = $this->get_errors();
129 97
130 - if ( \is_array( $errors ) && ! empty( $errors ) ) {
131 - return \reset( $errors );
98 + if ( is_array( $errors ) && ! empty( $errors ) ) {
99 + return reset( $errors );
132 100 }
133 101
134 102 return '';
135 103 }
@@ -134,9 +102,11 @@
134 102 return '';
135 103 }
136 104
137 105 /**
138 - * Update the current Follower object.
106 + * Update the current Follower-Object.
107 + *
108 + * @return void
139 109 */
140 110 public function update() {
141 111 $this->save();
142 112 }
@@ -141,14 +111,14 @@
141 111 $this->save();
142 112 }
143 113
144 114 /**
145 - * Validate the current Follower object.
115 + * Validate the current Follower-Object.
146 116 *
147 117 * @return boolean True if the verification was successful.
148 118 */
149 119 public function is_valid() {
150 - // The minimum required attributes.
120 + // the minimum required attributes
151 121 $required_attributes = array(
152 122 'id',
153 123 'preferredUsername',
154 124 'inbox',
@@ -165,30 +135,66 @@
165 135 return true;
166 136 }
167 137
168 138 /**
169 - * Save the current Follower object.
139 + * Save the current Follower-Object.
170 140 *
171 - * @return int|\WP_Error The post ID or an WP_Error.
141 + * @return int|WP_Error The Post-ID or an WP_Error.
172 142 */
173 143 public function save() {
174 144 if ( ! $this->is_valid() ) {
175 - return new \WP_Error( 'activitypub_invalid_follower', __( 'Invalid Follower', 'activitypub' ), array( 'status' => 400 ) );
145 + return new WP_Error( 'activitypub_invalid_follower', __( 'Invalid Follower', 'activitypub' ), array( 'status' => 400 ) );
176 146 }
177 147
178 - $id = Remote_Actors::upsert( $this );
179 - if ( \is_wp_error( $id ) ) {
180 - return $id;
148 + if ( ! $this->get__id() ) {
149 + global $wpdb;
150 +
151 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
152 + $post_id = $wpdb->get_var(
153 + $wpdb->prepare(
154 + "SELECT ID FROM $wpdb->posts WHERE guid=%s",
155 + esc_sql( $this->get_id() )
156 + )
157 + );
158 +
159 + if ( $post_id ) {
160 + $post = get_post( $post_id );
161 + $this->set__id( $post->ID );
162 + }
181 163 }
182 164
183 - $this->set__id( $id );
184 - return $id;
165 + $post_id = $this->get__id();
166 +
167 + $args = array(
168 + 'ID' => $post_id,
169 + 'guid' => esc_url_raw( $this->get_id() ),
170 + 'post_title' => wp_strip_all_tags( sanitize_text_field( $this->get_name() ) ),
171 + 'post_author' => 0,
172 + 'post_type' => Followers::POST_TYPE,
173 + 'post_name' => esc_url_raw( $this->get_id() ),
174 + 'post_excerpt' => sanitize_text_field( wp_kses( $this->get_summary(), 'user_description' ) ),
175 + 'post_status' => 'publish',
176 + 'meta_input' => $this->get_post_meta_input(),
177 + );
178 +
179 + if ( ! empty( $post_id ) ) {
180 + // If this is an update, prevent the "followed" date from being
181 + // overwritten by the current date.
182 + $post = get_post( $post_id );
183 + $args['post_date'] = $post->post_date;
184 + $args['post_date_gmt'] = $post->post_date_gmt;
185 + }
186 +
187 + $post_id = wp_insert_post( $args );
188 + $this->_id = $post_id;
189 +
190 + return $post_id;
185 191 }
186 192
187 193 /**
188 - * Upsert the current Follower object.
194 + * Upsert the current Follower-Object.
189 195 *
190 - * @return int|\WP_Error The post ID or an WP_Error.
196 + * @return int|WP_Error The Post-ID or an WP_Error.
191 197 */
192 198 public function upsert() {
193 199 return $this->save();
194 200 }
@@ -193,26 +199,40 @@
193 199 return $this->save();
194 200 }
195 201
196 202 /**
197 - * Delete the current Follower object.
203 + * Delete the current Follower-Object.
198 204 *
199 205 * Beware that this os deleting a Follower for ALL users!!!
200 206 *
201 207 * To delete only the User connection (unfollow)
208 + * @see \Activitypub\Rest\Followers::remove_follower()
202 209 *
203 - * @see \Activitypub\Rest\Followers::remove_follower()
210 + * @return void
204 211 */
205 212 public function delete() {
206 - Followers::remove_follower( $this->_id, $this->get_id() );
213 + wp_delete_post( $this->_id );
207 214 }
208 215
209 216 /**
217 + * Update the post meta.
218 + *
219 + * @return void
220 + */
221 + protected function get_post_meta_input() {
222 + $meta_input = array();
223 + $meta_input['activitypub_inbox'] = $this->get_shared_inbox();
224 + $meta_input['activitypub_actor_json'] = $this->to_json();
225 +
226 + return $meta_input;
227 + }
228 +
229 + /**
210 230 * Get the icon.
211 231 *
212 232 * Sets a fallback to better handle API and HTML outputs.
213 233 *
214 - * @return string[] The icon.
234 + * @return array The icon.
215 235 */
216 236 public function get_icon() {
217 237 if ( isset( $this->icon['url'] ) ) {
218 238 return $this->icon;
@@ -218,11 +238,11 @@
218 238 return $this->icon;
219 239 }
220 240
221 241 return array(
222 - 'type' => 'Image',
242 + 'type' => 'Image',
223 243 'mediaType' => 'image/jpeg',
224 - 'url' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg',
244 + 'url' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg',
225 245 );
226 246 }
227 247
228 248 /**
@@ -257,9 +277,9 @@
257 277 return $this->extract_name_from_uri();
258 278 }
259 279
260 280 /**
261 - * Get the Icon URL (Avatar).
281 + * Get the Icon URL (Avatar)
262 282 *
263 283 * @return string The URL to the Avatar.
264 284 */
265 285 public function get_icon_url() {
@@ -268,9 +288,9 @@
268 288 if ( ! $icon ) {
269 289 return '';
270 290 }
271 291
272 - if ( \is_array( $icon ) ) {
292 + if ( is_array( $icon ) ) {
273 293 return $icon['url'];
274 294 }
275 295
276 296 return $icon;
@@ -276,9 +296,9 @@
276 296 return $icon;
277 297 }
278 298
279 299 /**
280 - * Get the Icon URL (Avatar).
300 + * Get the Icon URL (Avatar)
281 301 *
282 302 * @return string The URL to the Avatar.
283 303 */
284 304 public function get_image_url() {
@@ -287,9 +307,9 @@
287 307 if ( ! $image ) {
288 308 return '';
289 309 }
290 310
291 - if ( \is_array( $image ) ) {
311 + if ( is_array( $image ) ) {
292 312 return $image['url'];
293 313 }
294 314
295 315 return $image;
@@ -312,25 +332,15 @@
312 332
313 333 /**
314 334 * Convert a Custom-Post-Type input to an Activitypub\Model\Follower.
315 335 *
316 - * @param \WP_Post $post The post object.
317 - * @return Follower|false The Follower object or false on failure.
336 + * @return string The JSON string.
337 + *
338 + * @return array Activitypub\Model\Follower
318 339 */
319 340 public static function init_from_cpt( $post ) {
320 - if ( empty( $post->post_content ) ) {
321 - $json = \get_post_meta( $post->ID, '_activitypub_actor_json', true );
322 - } else {
323 - $json = $post->post_content;
324 - }
325 -
326 - /* @var Follower $object Follower object. */
327 - $object = self::init_from_json( $json );
328 -
329 - if ( \is_wp_error( $object ) ) {
330 - return false;
331 - }
332 -
341 + $actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true );
342 + $object = self::init_from_json( $actor_json );
333 343 $object->set__id( $post->ID );
334 344 $object->set_id( $post->guid );
335 345 $object->set_name( $post->post_title );
336 346 $object->set_summary( $post->post_excerpt );
@@ -348,12 +358,38 @@
348 358 */
349 359 protected function extract_name_from_uri() {
350 360 // prefer the URL, but fall back to the ID.
351 361 if ( $this->url ) {
352 - $uri = $this->url;
362 + $name = $this->url;
353 363 } else {
354 - $uri = $this->id;
364 + $name = $this->id;
355 365 }
356 366
357 - return extract_name_from_uri( $uri );
367 + if ( \filter_var( $name, FILTER_VALIDATE_URL ) ) {
368 + $name = \rtrim( $name, '/' );
369 + $path = \wp_parse_url( $name, PHP_URL_PATH );
370 +
371 + if ( $path ) {
372 + if ( \strpos( $name, '@' ) !== false ) {
373 + // expected: https://example.com/@user (default URL pattern)
374 + $name = \preg_replace( '|^/@?|', '', $path );
375 + } else {
376 + // expected: https://example.com/users/user (default ID pattern)
377 + $parts = \explode( '/', $path );
378 + $name = \array_pop( $parts );
379 + }
380 + }
381 + } elseif (
382 + \is_email( $name ) ||
383 + \strpos( $name, 'acct' ) === 0 ||
384 + \strpos( $name, '@' ) === 0
385 + ) {
386 + // expected: [email protected] or acct:user@example (WebFinger)
387 + $name = \ltrim( $name, '@' );
388 + $name = \ltrim( $name, 'acct:' );
389 + $parts = \explode( '@', $name );
390 + $name = $parts[0];
391 + }
392 +
393 + return $name;
358 394 }
359 395 }