PluginProbe
ActivityPub / 2.5.0
ActivityPub v2.5.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.5.0, at includes/class-scheduler.php

334 lines 8.7 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 // Do not send activities if post is password protected.
121 if ( \post_password_required( $post ) ) {
122 return;
123 }
124
125 // Check if post-type supports ActivityPub.
126 $post_types = \get_post_types_by_support( 'activitypub' );
127 if ( ! \in_array( $post->post_type, $post_types, true ) ) {
128 return;
129 }
130
131 $type = false;
132
133 if ( 'publish' === $new_status && 'publish' !== $old_status ) {
134 $type = 'Create';
135 } elseif ( 'publish' === $new_status ) {
136 $type = 'Update';
137 } elseif ( 'trash' === $new_status ) {
138 $type = 'Delete';
139 }
140
141 if ( empty( $type ) ) {
142 return;
143 }
144
145 $hook = 'activitypub_send_post';
146 $args = array( $post->ID, $type );
147
148 if ( false === wp_next_scheduled( $hook, $args ) ) {
149 set_wp_object_state( $post, 'federate' );
150 \wp_schedule_single_event( \time(), $hook, $args );
151 }
152 }
153
154 /**
155 * Schedule Comment Activities
156 *
157 * transition_comment_status()
158 *
159 * @param string $new_status New comment status.
160 * @param string $old_status Old comment status.
161 * @param WP_Comment $comment Comment object.
162 */
163 public static function schedule_comment_activity( $new_status, $old_status, $comment ) {
164 $comment = get_comment( $comment );
165
166 // federate only comments that are written by a registered user.
167 if ( ! $comment->user_id ) {
168 return;
169 }
170
171 $type = false;
172
173 if (
174 'approved' === $new_status &&
175 'approved' !== $old_status
176 ) {
177 $type = 'Create';
178 } elseif ( 'approved' === $new_status ) {
179 $type = 'Update';
180 \update_comment_meta( $comment->comment_ID, 'activitypub_comment_modified', time(), true );
181 } elseif (
182 'trash' === $new_status ||
183 'spam' === $new_status
184 ) {
185 $type = 'Delete';
186 }
187
188 if ( empty( $type ) ) {
189 return;
190 }
191
192 // check if comment should be federated or not
193 if ( ! should_comment_be_federated( $comment ) ) {
194 return;
195 }
196
197 $hook = 'activitypub_send_comment';
198 $args = array( $comment->comment_ID, $type );
199
200 if ( false === wp_next_scheduled( $hook, $args ) ) {
201 set_wp_object_state( $comment, 'federate' );
202 \wp_schedule_single_event( \time(), $hook, $args );
203 }
204 }
205
206 /**
207 * Update followers
208 *
209 * @return void
210 */
211 public static function update_followers() {
212 $number = 5;
213
214 if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
215 $number = 50;
216 }
217
218 $number = apply_filters( 'activitypub_update_followers_number', $number );
219 $followers = Followers::get_outdated_followers( $number );
220
221 foreach ( $followers as $follower ) {
222 $meta = get_remote_metadata_by_actor( $follower->get_id(), false );
223
224 if ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
225 Followers::add_error( $follower->get__id(), $meta );
226 } else {
227 $follower->from_array( $meta );
228 $follower->update();
229 }
230 }
231 }
232
233 /**
234 * Cleanup followers
235 *
236 * @return void
237 */
238 public static function cleanup_followers() {
239 $number = 5;
240
241 if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) {
242 $number = 50;
243 }
244
245 $number = apply_filters( 'activitypub_update_followers_number', $number );
246 $followers = Followers::get_faulty_followers( $number );
247
248 foreach ( $followers as $follower ) {
249 $meta = get_remote_metadata_by_actor( $follower->get_url(), false );
250
251 if ( is_tombstone( $meta ) ) {
252 $follower->delete();
253 } elseif ( empty( $meta ) || ! is_array( $meta ) || is_wp_error( $meta ) ) {
254 if ( $follower->count_errors() >= 5 ) {
255 $follower->delete();
256 \wp_schedule_single_event(
257 \time(),
258 'activitypub_delete_actor_interactions',
259 array( $follower->get_id() )
260 );
261 } else {
262 Followers::add_error( $follower->get__id(), $meta );
263 }
264 } else {
265 $follower->reset_errors();
266 }
267 }
268 }
269
270 /**
271 * Send a profile update when relevant user meta is updated.
272 *
273 * @param int $meta_id Meta ID being updated.
274 * @param int $user_id User ID being updated.
275 * @param string $meta_key Meta key being updated.
276 *
277 * @return void
278 */
279 public static function user_meta_update( $meta_id, $user_id, $meta_key ) {
280 // don't bother if the user can't publish
281 if ( ! \user_can( $user_id, 'activitypub' ) ) {
282 return;
283 }
284 // the user meta fields that affect a profile.
285 $fields = array(
286 'activitypub_user_description',
287 'description',
288 'user_url',
289 'display_name',
290 );
291 if ( in_array( $meta_key, $fields, true ) ) {
292 self::schedule_profile_update( $user_id );
293 }
294 }
295
296 /**
297 * Send a profile update when a user is updated.
298 *
299 * @param int $user_id User ID being updated.
300 *
301 * @return void
302 */
303 public static function user_update( $user_id ) {
304 // don't bother if the user can't publish
305 if ( ! \user_can( $user_id, 'activitypub' ) ) {
306 return;
307 }
308
309 self::schedule_profile_update( $user_id );
310 }
311
312 /**
313 * Theme mods only have a dynamic filter so we fudge it like this.
314 * @param mixed $value
315 * @return mixed
316 */
317 public static function blog_user_update( $value = null ) {
318 self::schedule_profile_update( 0 );
319 return $value;
320 }
321
322 /**
323 * Send a profile update to all followers. Gets hooked into all relevant options/meta etc.
324 * @param int $user_id The user ID to update (Could be 0 for Blog-User).
325 */
326 public static function schedule_profile_update( $user_id ) {
327 \wp_schedule_single_event(
328 \time(),
329 'activitypub_send_update_profile_activity',
330 array( $user_id )
331 );
332 }
333 }
334