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