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
activitypub / includes / class-scheduler.php

class-scheduler.php in ActivityPub 3.2.4, at includes/class-scheduler.php

360 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Activitypub;
4
5 use Activitypub\Transformer\Post;
6 use Activitypub\Collection\Users;
7 use Activitypub\Collection\Followers;
8
9 use function Activitypub\was_comment_sent;
10 use function Activitypub\is_user_type_disabled;
11 use function Activitypub\should_comment_be_federated;
12 use function Activitypub\get_remote_metadata_by_actor;
13
14 /**
15 * ActivityPub Scheduler Class
16 *
17 * @author Matthias Pfefferle
18 */
19 class Scheduler {
20
21 /**
22 * Initialize the class, registering WordPress hooks
23 */
24 public static function init() {
25 // Post transitions
26 \add_action( 'transition_post_status', array( self::class, 'schedule_post_activity' ), 33, 3 );
27 \add_action(
28 'edit_attachment',
29 function ( $post_id ) {
30 self::schedule_post_activity( 'publish', 'publish', $post_id );
31 }
32 );
33 \add_action(
34 'add_attachment',
35 function ( $post_id ) {
36 self::schedule_post_activity( 'publish', '', $post_id );
37 }
38 );
39 \add_action(
40 'delete_attachment',
41 function ( $post_id ) {
42 self::schedule_post_activity( 'trash', '', $post_id );
43 }
44 );
45
46 if ( ! ACTIVITYPUB_DISABLE_OUTGOING_INTERACTIONS ) {
47 // Comment transitions
48 \add_action( 'transition_comment_status', array( self::class, 'schedule_comment_activity' ), 20, 3 );
49 \add_action(
50 'edit_comment',
51 function ( $comment_id ) {
52 self::schedule_comment_activity( 'approved', 'approved', $comment_id );
53 }
54 );
55 \add_action(
56 'wp_insert_comment',
57 function ( $comment_id ) {
58 self::schedule_comment_activity( 'approved', '', $comment_id );
59 }
60 );
61 }
62
63 // Follower Cleanups
64 \add_action( 'activitypub_update_followers', array( self::class, 'update_followers' ) );
65 \add_action( 'activitypub_cleanup_followers', array( self::class, 'cleanup_followers' ) );
66
67 // profile updates for blog options
68 if ( ! is_user_type_disabled( 'blog' ) ) {
69 \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) );
70 \add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) );
71 \add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) );
72 \add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) );
73 \add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) );
74 }
75
76 // profile updates for user options
77 if ( ! is_user_type_disabled( 'user' ) ) {
78 \add_action( 'wp_update_user', array( self::class, 'user_update' ) );
79 \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 );
80 // @todo figure out a feasible way of updating the header image since it's not unique to any user.
81 }
82 }
83
84 /**
85 * Schedule all ActivityPub schedules.
86 *
87 * @return void
88 */
89 public static function register_schedules() {
90 if ( ! \wp_next_scheduled( 'activitypub_update_followers' ) ) {
91 \wp_schedule_event( time(), 'hourly', 'activitypub_update_followers' );
92 }
93
94 if ( ! \wp_next_scheduled( 'activitypub_cleanup_followers' ) ) {
95 \wp_schedule_event( time(), 'daily', 'activitypub_cleanup_followers' );
96 }
97 }
98
99 /**
100 * Unscedule all ActivityPub schedules.
101 *
102 * @return void
103 */
104 public static function deregister_schedules() {
105 wp_unschedule_hook( 'activitypub_update_followers' );
106 wp_unschedule_hook( 'activitypub_cleanup_followers' );
107 }
108
109
110 /**
111 * Schedule Activities.
112 *
113 * @param string $new_status New post status.
114 * @param string $old_status Old post status.
115 * @param WP_Post $post Post object.
116 */
117 public static function schedule_post_activity( $new_status, $old_status, $post ) {
118 $post = get_post( $post );
119
120 if ( ! $post ) {
121 return;
122 }
123
124 if ( 'ap_extrafield' === $post->post_type ) {
125 self::schedule_profile_update( $post->post_author );
126 return;
127 }
128
129 if ( 'ap_extrafield_blog' === $post->post_type ) {
130 self::schedule_profile_update( 0 );
131 return;
132 }
133
134 // Do not send activities if post is password protected.
135 if ( \post_password_required( $post ) ) {
136 return;
137 }
138
139 // Check if post-type supports ActivityPub.
140 $post_types = \get_post_types_by_support( 'activitypub' );
141 if ( ! \in_array( $post->post_type, $post_types, true ) ) {
142 return;
143 }
144
145 $type = false;
146
147 if (
148 'publish' === $new_status &&
149 'publish' !== $old_status
150 ) {
151 $type = 'Create';
152 } elseif (
153 'publish' === $new_status ||
154 // We want to send updates for posts that are published and then moved to draft.
155 ( 'draft' === $new_status &&
156 'publish' === $old_status )
157 ) {
158 $type = 'Update';
159 } elseif ( 'trash' === $new_status ) {
160 $type = 'Delete';
161 }
162
163 if ( empty( $type ) ) {
164 return;
165 }
166
167 $hook = 'activitypub_send_post';
168 $args = array( $post->ID, $type );
169
170 if ( false === wp_next_scheduled( $hook, $args ) ) {
171 set_wp_object_state( $post, 'federate' );
172 \wp_schedule_single_event( \time(), $hook, $args );
173 }
174 }
175
176 /**
177 * Schedule Comment Activities
178 *
179 * transition_comment_status()
180 *
181 * @param string $new_status New comment status.
182 * @param string $old_status Old comment status.
183 * @param WP_Comment $comment Comment object.
184 */
185 public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
186 $comment = get_comment( $comment );
187
188 // federate only comments that are written by a registered user.
189 if ( ! $comment || ! $comment->user_id ) {
190 return;
191 }
192
193 $type = false;
194
195 if (
196 'approved' === $new_status &&
197 'approved' !== $old_status
198 ) {
199 $type = 'Create';
200 } elseif ( 'approved' === $new_status ) {
201 $type = 'Update';
202 \update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', time(), true );
203 } elseif (
204 'trash' === $new_status ||
205 'spam' === $new_status
206 ) {
207 $type = 'Delete';
208 }
209
210 if ( empty( $type ) ) {
211 return;
212 }
213
214 // check if comment should be federated or not
215 if ( ! should_comment_be_federated( $comment ) ) {
216 return;
217 }
218
219 $hook = 'activitypub_send_comment';
220 $args = array( $comment->comment_ID, $type );
221
222 if ( false === wp_next_scheduled( $hook, $args ) ) {
223 set_wp_object_state( $comment, 'federate' );
224 \wp_schedule_single_event( \time(), $hook, $args );
225 }
226 }
227
228 /**
229 * Update followers
230 *
231 * @return void
232 */
233 public static function update_followers() {
234 $number = 5;
235
236 if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
237 $number = 50;
238 }
239
240 $number = apply_filters( 'activitypub_update_followers_number', $number );
241 $followers = Followers::get_outdated_followers( $number );
242
243 foreach ( $followers as $follower ) {
244 $meta = get_remote_metadata_by_actor( $follower->get_id(), false );
245
246 if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
247 Followers::add_error( $follower->get__id(), $meta );
248 } else {
249 $follower->from_array( $meta );
250 $follower->update();
251 }
252 }
253 }
254
255 /**
256 * Cleanup followers
257 *
258 * @return void
259 */
260 public static function cleanup_followers() {
261 $number = 5;
262
263 if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
264 $number = 50;
265 }
266
267 $number = apply_filters( 'activitypub_update_followers_number', $number );
268 $followers = Followers::get_faulty_followers( $number );
269
270 foreach ( $followers as $follower ) {
271 $meta = get_remote_metadata_by_actor( $follower->get_url(), false );
272
273 if ( is_tombstone( $meta ) ) {
274 $follower->delete();
275 } elseif ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
276 if ( $follower->count_errors() >= 5 ) {
277 $follower->delete();
278 \wp_schedule_single_event(
279 \time(),
280 'activitypub_delete_actor_interactions',
281 array( $follower->get_id() )
282 );
283 } else {
284 Followers::add_error( $follower->get__id(), $meta );
285 }
286 } else {
287 $follower->reset_errors();
288 }
289 }
290 }
291
292 /**
293 * Send a profile update when relevant user meta is updated.
294 *
295 * @param int $meta_id Meta ID being updated.
296 * @param int $user_id User ID being updated.
297 * @param string $meta_key Meta key being updated.
298 *
299 * @return void
300 */
301 public static function user_meta_update( $meta_id, $user_id, $meta_key ) {
302 // don't bother if the user can't publish
303 if ( ! \user_can( $user_id, 'activitypub' ) ) {
304 return;
305 }
306 // the user meta fields that affect a profile.
307 $fields = array(
308 'activitypub_description',
309 'activitypub_header_image',
310 'description',
311 'user_url',
312 'display_name',
313 );
314 if ( in_array( $meta_key, $fields, true ) ) {
315 self::schedule_profile_update( $user_id );
316 }
317 }
318
319 /**
320 * Send a profile update when a user is updated.
321 *
322 * @param int $user_id User ID being updated.
323 *
324 * @return void
325 */
326 public static function user_update( $user_id ) {
327 // don't bother if the user can't publish
328 if ( ! \user_can( $user_id, 'activitypub' ) ) {
329 return;
330 }
331
332 self::schedule_profile_update( $user_id );
333 }
334
335 /**
336 * Theme mods only have a dynamic filter so we fudge it like this.
337 *
338 * @param mixed $value
339 *
340 * @return mixed
341 */
342 public static function blog_user_update( $value = null ) {
343 self::schedule_profile_update( 0 );
344 return $value;
345 }
346
347 /**
348 * Send a profile update to all followers. Gets hooked into all relevant options/meta etc.
349 *
350 * @param int $user_id The user ID to update (Could be 0 for Blog-User).
351 */
352 public static function schedule_profile_update( $user_id ) {
353 \wp_schedule_single_event(
354 \time(),
355 'activitypub_send_update_profile_activity',
356 array( $user_id )
357 );
358 }
359 }
360