PluginProbe
ActivityPub / 2.1.0
ActivityPub v2.1.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
activitypub / includes / class-scheduler.php

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

359 lines 9.1 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 // Migration
68 \add_action( 'admin_init', array( self::class, 'schedule_migration' ) );
69
70 // profile updates for blog options
71 if ( ! is_user_type_disabled( 'blog' ) ) {
72 \add_action( 'update_option_site_icon', array( self::class, 'blog_user_update' ) );
73 \add_action( 'update_option_blogdescription', array( self::class, 'blog_user_update' ) );
74 \add_action( 'update_option_blogname', array( self::class, 'blog_user_update' ) );
75 \add_filter( 'pre_set_theme_mod_custom_logo', array( self::class, 'blog_user_update' ) );
76 \add_filter( 'pre_set_theme_mod_header_image', array( self::class, 'blog_user_update' ) );
77 }
78
79 // profile updates for user options
80 if ( ! is_user_type_disabled( 'user' ) ) {
81 \add_action( 'wp_update_user', array( self::class, 'user_update' ) );
82 \add_action( 'updated_user_meta', array( self::class, 'user_meta_update' ), 10, 3 );
83 // @todo figure out a feasible way of updating the header image since it's not unique to any user.
84 }
85 }
86
87 /**
88 * Schedule all ActivityPub schedules.
89 *
90 * @return void
91 */
92 public static function register_schedules() {
93 if ( ! \wp_next_scheduled( 'activitypub_update_followers' ) ) {
94 \wp_schedule_event( time(), 'hourly', 'activitypub_update_followers' );
95 }
96
97 if ( ! \wp_next_scheduled( 'activitypub_cleanup_followers' ) ) {
98 \wp_schedule_event( time(), 'daily', 'activitypub_cleanup_followers' );
99 }
100 }
101
102 /**
103 * Unscedule all ActivityPub schedules.
104 *
105 * @return void
106 */
107 public static function deregister_schedules() {
108 wp_unschedule_hook( 'activitypub_update_followers' );
109 wp_unschedule_hook( 'activitypub_cleanup_followers' );
110 }
111
112
113 /**
114 * Schedule Activities.
115 *
116 * @param string $new_status New post status.
117 * @param string $old_status Old post status.
118 * @param WP_Post $post Post object.
119 */
120 public static function schedule_post_activity( $new_status, $old_status, $post ) {
121 $post = get_post( $post );
122
123 // Do not send activities if post is password protected.
124 if ( \post_password_required( $post ) ) {
125 return;
126 }
127
128 // Check if post-type supports ActivityPub.
129 $post_types = \get_post_types_by_support( 'activitypub' );
130 if ( ! \in_array( $post->post_type, $post_types, true ) ) {
131 return;
132 }
133
134 $type = false;
135
136 if ( 'publish' === $new_status && 'publish' !== $old_status ) {
137 $type = 'Create';
138 } elseif ( 'publish' === $new_status ) {
139 $type = 'Update';
140 } elseif ( 'trash' === $new_status ) {
141 $type = 'Delete';
142 }
143
144 if ( empty( $type ) ) {
145 return;
146 }
147
148 \wp_schedule_single_event(
149 \time(),
150 'activitypub_send_activity',
151 array( $post, $type )
152 );
153
154 \wp_schedule_single_event(
155 \time(),
156 sprintf(
157 'activitypub_send_%s_activity',
158 \strtolower( $type )
159 ),
160 array( $post )
161 );
162 }
163
164 /**
165 * Schedule Comment Activities
166 *
167 * transition_comment_status()
168 *
169 * @param string $new_status New comment status.
170 * @param string $old_status Old comment status.
171 * @param WP_Comment $comment Comment object.
172 */
173 public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
174 $comment = get_comment( $comment );
175
176 // federate only comments that are written by a registered user.
177 if ( ! $comment->user_id ) {
178 return;
179 }
180
181 $type = false;
182
183 if (
184 'approved' === $new_status &&
185 'approved' !== $old_status
186 ) {
187 $type = 'Create';
188 } elseif ( 'approved' === $new_status ) {
189 $type = 'Update';
190 \update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', time(), true );
191 } elseif (
192 'trash' === $new_status ||
193 'spam' === $new_status
194 ) {
195 $type = 'Delete';
196 }
197
198 if ( empty( $type ) ) {
199 return;
200 }
201
202 // check if comment should be federated or not
203 if ( ! should_comment_be_federated( $comment ) ) {
204 return;
205 }
206
207 set_wp_object_state( $comment, 'federate' );
208
209 \wp_schedule_single_event(
210 \time(),
211 'activitypub_send_activity',
212 array( $comment, $type )
213 );
214
215 \wp_schedule_single_event(
216 \time(),
217 sprintf(
218 'activitypub_send_%s_activity',
219 \strtolower( $type )
220 ),
221 array( $comment )
222 );
223 }
224
225 /**
226 * Update followers
227 *
228 * @return void
229 */
230 public static function update_followers() {
231 $number = 5;
232
233 if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
234 $number = 50;
235 }
236
237 $number = apply_filters( 'activitypub_update_followers_number', $number );
238 $followers = Followers::get_outdated_followers( $number );
239
240 foreach ( $followers as $follower ) {
241 $meta = get_remote_metadata_by_actor( $follower->get_id(), false );
242
243 if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
244 Followers::add_error( $follower->get__id(), $meta );
245 } else {
246 $follower->from_array( $meta );
247 $follower->update();
248 }
249 }
250 }
251
252 /**
253 * Cleanup followers
254 *
255 * @return void
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 $number = apply_filters( 'activitypub_update_followers_number', $number );
265 $followers = Followers::get_faulty_followers( $number );
266
267 foreach ( $followers as $follower ) {
268 $meta = get_remote_metadata_by_actor( $follower->get_url(), false );
269
270 if ( is_tombstone( $meta ) ) {
271 $follower->delete();
272 } elseif ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
273 if ( $follower->count_errors() >= 5 ) {
274 $follower->delete();
275 } else {
276 Followers::add_error( $follower->get__id(), $meta );
277 }
278 } else {
279 $follower->reset_errors();
280 }
281 }
282 }
283
284 /**
285 * Schedule migration if DB-Version is not up to date.
286 *
287 * @return void
288 */
289 public static function schedule_migration() {
290 if ( ! \wp_next_scheduled( 'activitypub_schedule_migration' ) && ! Migration::is_latest_version() ) {
291 \wp_schedule_single_event( \time(), 'activitypub_schedule_migration' );
292 }
293 }
294
295 /**
296 * Send a profile update when relevant user meta is updated.
297 *
298 * @param int $meta_id Meta ID being updated.
299 * @param int $user_id User ID being updated.
300 * @param string $meta_key Meta key being updated.
301 *
302 * @return void
303 */
304 public static function user_meta_update( $meta_id, $user_id, $meta_key ) {
305 // don't bother if the user can't publish
306 if ( ! \user_can( $user_id, 'publish_posts' ) ) {
307 return;
308 }
309 // the user meta fields that affect a profile.
310 $fields = array(
311 'activitypub_user_description',
312 'description',
313 'user_url',
314 'display_name',
315 );
316 if ( in_array( $meta_key, $fields, true ) ) {
317 self::schedule_profile_update( $user_id );
318 }
319 }
320
321 /**
322 * Send a profile update when a user is updated.
323 *
324 * @param int $user_id User ID being updated.
325 *
326 * @return void
327 */
328 public static function user_update( $user_id ) {
329 // don't bother if the user can't publish
330 if ( ! \user_can( $user_id, 'publish_posts' ) ) {
331 return;
332 }
333
334 self::schedule_profile_update( $user_id );
335 }
336
337 /**
338 * Theme mods only have a dynamic filter so we fudge it like this.
339 * @param mixed $value
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 * @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(),
354 'activitypub_send_update_profile_activity',
355 array( $user_id )
356 );
357 }
358 }
359