PluginProbe
ActivityPub / 1.0.0
ActivityPub v1.0.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
← All changes | includes/model/class-follower.php +117 -110 8.3.01.0.0 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,56 @@
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' ) );
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 + $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;
185 181 }
186 182
187 183 /**
188 - * Upsert the current Follower object.
184 + * Upsert the current Follower-Object.
189 185 *
190 - * @return int|\WP_Error The post ID or an WP_Error.
186 + * @return int|WP_Error The Post-ID or an WP_Error.
191 187 */
192 188 public function upsert() {
193 189 return $this->save();
194 190 }
@@ -193,26 +189,40 @@
193 189 return $this->save();
194 190 }
195 191
196 192 /**
197 - * Delete the current Follower object.
193 + * Delete the current Follower-Object.
198 194 *
199 195 * Beware that this os deleting a Follower for ALL users!!!
200 196 *
201 197 * To delete only the User connection (unfollow)
198 + * @see \Activitypub\Rest\Followers::remove_follower()
202 199 *
203 - * @see \Activitypub\Rest\Followers::remove_follower()
200 + * @return void
204 201 */
205 202 public function delete() {
206 - Followers::remove_follower( $this->_id, $this->get_id() );
203 + wp_delete_post( $this->_id );
207 204 }
208 205
209 206 /**
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 + /**
210 220 * Get the icon.
211 221 *
212 222 * Sets a fallback to better handle API and HTML outputs.
213 223 *
214 - * @return string[] The icon.
224 + * @return array The icon.
215 225 */
216 226 public function get_icon() {
217 227 if ( isset( $this->icon['url'] ) ) {
218 228 return $this->icon;
@@ -218,11 +228,11 @@
218 228 return $this->icon;
219 229 }
220 230
221 231 return array(
222 - 'type' => 'Image',
232 + 'type' => 'Image',
223 233 'mediaType' => 'image/jpeg',
224 - 'url' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg',
234 + 'url' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg',
225 235 );
226 236 }
227 237
228 238 /**
@@ -257,9 +267,9 @@
257 267 return $this->extract_name_from_uri();
258 268 }
259 269
260 270 /**
261 - * Get the Icon URL (Avatar).
271 + * Get the Icon URL (Avatar)
262 272 *
263 273 * @return string The URL to the Avatar.
264 274 */
265 275 public function get_icon_url() {
@@ -268,9 +278,9 @@
268 278 if ( ! $icon ) {
269 279 return '';
270 280 }
271 281
272 - if ( \is_array( $icon ) ) {
282 + if ( is_array( $icon ) ) {
273 283 return $icon['url'];
274 284 }
275 285
276 286 return $icon;
@@ -276,27 +286,8 @@
276 286 return $icon;
277 287 }
278 288
279 289 /**
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 - /**
299 290 * Get the shared inbox, with a fallback to the inbox.
300 291 *
301 292 * @return string|null The URL to the shared inbox, the inbox or null.
302 293 */
@@ -312,30 +303,20 @@
312 303
313 304 /**
314 305 * Convert a Custom-Post-Type input to an Activitypub\Model\Follower.
315 306 *
316 - * @param \WP_Post $post The post object.
317 - * @return Follower|false The Follower object or false on failure.
307 + * @return string The JSON string.
308 + *
309 + * @return array Activitypub\Model\Follower
318 310 */
319 311 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 -
312 + $actor_json = get_post_meta( $post->ID, 'activitypub_actor_json', true );
313 + $object = self::init_from_json( $actor_json );
333 314 $object->set__id( $post->ID );
334 315 $object->set_id( $post->guid );
335 316 $object->set_name( $post->post_title );
336 317 $object->set_summary( $post->post_excerpt );
337 - $object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_date ) ) );
318 + $object->set_published( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_published ) ) );
338 319 $object->set_updated( gmdate( 'Y-m-d H:i:s', strtotime( $post->post_modified ) ) );
339 320
340 321 return $object;
341 322 }
@@ -348,12 +329,38 @@
348 329 */
349 330 protected function extract_name_from_uri() {
350 331 // prefer the URL, but fall back to the ID.
351 332 if ( $this->url ) {
352 - $uri = $this->url;
333 + $name = $this->url;
353 334 } else {
354 - $uri = $this->id;
335 + $name = $this->id;
355 336 }
356 337
357 - return extract_name_from_uri( $uri );
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;
358 365 }
359 366 }