PluginProbe
ActivityPub / 4.0.2
ActivityPub v4.0.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
activitypub / includes / class-scheduler.php

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

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