Skip to content

Commit ea4221a

Browse files
committed
Add scheduled cleanup for remote posts (ap_post)
Add purge functionality for remote ActivityPub posts similar to existing outbox and inbox cleanup. Key features: - Preserves posts that have comments (likes, reposts, quotes) from local users as these indicate meaningful interactions - Uses same threshold (200 posts) and default retention (180 days) as inbox - Option not user-facing yet (no settings UI) Also refactors purge logic by moving it from Scheduler into the respective collection classes (Outbox, Inbox, Posts), making the code more maintainable and allowing direct use of purge methods.
1 parent c3f9db7 commit ea4221a

File tree

7 files changed

+365
-53
lines changed

7 files changed

+365
-53
lines changed

includes/class-scheduler.php

Lines changed: 33 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Activitypub\Collection\Actors;
1313
use Activitypub\Collection\Inbox;
1414
use Activitypub\Collection\Outbox;
15+
use Activitypub\Collection\Posts;
1516
use Activitypub\Collection\Remote_Actors;
1617
use Activitypub\Scheduler\Actor;
1718
use Activitypub\Scheduler\Collection_Sync;
@@ -61,6 +62,7 @@ public static function init() {
6162
\add_action( 'activitypub_reprocess_outbox', array( self::class, 'reprocess_outbox' ) );
6263
\add_action( 'activitypub_outbox_purge', array( self::class, 'purge_outbox' ) );
6364
\add_action( 'activitypub_inbox_purge', array( self::class, 'purge_inbox' ) );
65+
\add_action( 'activitypub_ap_post_purge', array( self::class, 'purge_ap_posts' ) );
6466
\add_action( 'activitypub_inbox_create_item', array( self::class, 'process_inbox_activity' ) );
6567
\add_action( 'activitypub_sync_blocklist_subscriptions', array( Blocklist_Subscriptions::class, 'sync_all' ) );
6668

@@ -69,6 +71,7 @@ public static function init() {
6971

7072
\add_action( 'update_option_activitypub_outbox_purge_days', array( self::class, 'handle_outbox_purge_days_update' ), 10, 2 );
7173
\add_action( 'update_option_activitypub_inbox_purge_days', array( self::class, 'handle_inbox_purge_days_update' ), 10, 2 );
74+
\add_action( 'update_option_activitypub_ap_post_purge_days', array( self::class, 'handle_ap_post_purge_days_update' ), 10, 2 );
7275
}
7376

7477
/**
@@ -134,6 +137,10 @@ public static function register_schedules() {
134137
\wp_schedule_event( time(), 'daily', 'activitypub_inbox_purge' );
135138
}
136139

140+
if ( ! \wp_next_scheduled( 'activitypub_ap_post_purge' ) ) {
141+
\wp_schedule_event( time(), 'daily', 'activitypub_ap_post_purge' );
142+
}
143+
137144
if ( ! \wp_next_scheduled( 'activitypub_sync_blocklist_subscriptions' ) ) {
138145
\wp_schedule_event( time(), 'weekly', 'activitypub_sync_blocklist_subscriptions' );
139146
}
@@ -150,6 +157,7 @@ public static function deregister_schedules() {
150157
\wp_unschedule_hook( 'activitypub_reprocess_outbox' );
151158
\wp_unschedule_hook( 'activitypub_outbox_purge' );
152159
\wp_unschedule_hook( 'activitypub_inbox_purge' );
160+
\wp_unschedule_hook( 'activitypub_ap_post_purge' );
153161
\wp_unschedule_hook( 'activitypub_sync_blocklist_subscriptions' );
154162
}
155163

@@ -315,66 +323,24 @@ public static function reprocess_outbox() {
315323
* Purge outbox items based on a schedule.
316324
*/
317325
public static function purge_outbox() {
318-
$total_posts = (int) wp_count_posts( Outbox::POST_TYPE )->publish;
319-
if ( $total_posts <= 20 ) {
320-
return;
321-
}
322-
323-
$days = (int) get_option( 'activitypub_outbox_purge_days', 180 );
324-
$post_ids = \get_posts(
325-
array(
326-
'post_type' => Outbox::POST_TYPE,
327-
'post_status' => 'any',
328-
'fields' => 'ids',
329-
'numberposts' => -1,
330-
'date_query' => array(
331-
array(
332-
'before' => gmdate( 'Y-m-d', time() - ( $days * DAY_IN_SECONDS ) ),
333-
),
334-
),
335-
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
336-
'meta_query' => array(
337-
array(
338-
'key' => '_activitypub_activity_type',
339-
'value' => 'Follow',
340-
'compare' => '!=',
341-
),
342-
),
343-
)
344-
);
345-
346-
foreach ( $post_ids as $post_id ) {
347-
\wp_delete_post( $post_id, true );
348-
}
326+
$days = (int) get_option( 'activitypub_outbox_purge_days', 180 );
327+
Outbox::purge( $days );
349328
}
350329

351330
/**
352331
* Purge inbox items based on a schedule.
353332
*/
354333
public static function purge_inbox() {
355-
$total_posts = (int) wp_count_posts( Inbox::POST_TYPE )->publish;
356-
if ( $total_posts <= 200 ) {
357-
return;
358-
}
359-
360-
$days = (int) get_option( 'activitypub_inbox_purge_days', 180 );
361-
$post_ids = \get_posts(
362-
array(
363-
'post_type' => Inbox::POST_TYPE,
364-
'post_status' => 'any',
365-
'fields' => 'ids',
366-
'numberposts' => -1,
367-
'date_query' => array(
368-
array(
369-
'before' => gmdate( 'Y-m-d', time() - ( $days * DAY_IN_SECONDS ) ),
370-
),
371-
),
372-
)
373-
);
334+
$days = (int) get_option( 'activitypub_inbox_purge_days', 180 );
335+
Inbox::purge( $days );
336+
}
374337

375-
foreach ( $post_ids as $post_id ) {
376-
\wp_delete_post( $post_id, true );
377-
}
338+
/**
339+
* Purge remote posts based on a schedule.
340+
*/
341+
public static function purge_ap_posts() {
342+
$days = (int) get_option( 'activitypub_ap_post_purge_days', 180 );
343+
Posts::purge( $days );
378344
}
379345

380346
/**
@@ -452,6 +418,20 @@ public static function handle_inbox_purge_days_update( $old_value, $value ) {
452418
}
453419
}
454420

421+
/**
422+
* Update schedules when remote posts purge days settings change.
423+
*
424+
* @param int $old_value The old value.
425+
* @param int $value The new value.
426+
*/
427+
public static function handle_ap_post_purge_days_update( $old_value, $value ) {
428+
if ( 0 === (int) $value ) {
429+
\wp_clear_scheduled_hook( 'activitypub_ap_post_purge' );
430+
} elseif ( ! \wp_next_scheduled( 'activitypub_ap_post_purge' ) ) {
431+
\wp_schedule_event( \time(), 'daily', 'activitypub_ap_post_purge' );
432+
}
433+
}
434+
455435
/**
456436
* Asynchronously runs batch processing routines.
457437
*

includes/collection/class-inbox.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,4 +464,42 @@ public static function deduplicate( $guid ) {
464464

465465
return $primary;
466466
}
467+
468+
/**
469+
* Purge old inbox items.
470+
*
471+
* Deletes inbox items older than the specified number of days.
472+
*
473+
* @param int $days Number of days to keep items. Items older than this will be deleted.
474+
*
475+
* @return int The number of items deleted.
476+
*/
477+
public static function purge( $days ) {
478+
$total_posts = (int) \wp_count_posts( self::POST_TYPE )->publish;
479+
if ( $total_posts <= 200 ) {
480+
return 0;
481+
}
482+
483+
$post_ids = \get_posts(
484+
array(
485+
'post_type' => self::POST_TYPE,
486+
'post_status' => 'any',
487+
'fields' => 'ids',
488+
'numberposts' => -1,
489+
'date_query' => array(
490+
array(
491+
'before' => \gmdate( 'Y-m-d', \time() - ( $days * DAY_IN_SECONDS ) ),
492+
),
493+
),
494+
)
495+
);
496+
497+
$deleted = 0;
498+
foreach ( $post_ids as $post_id ) {
499+
\wp_delete_post( $post_id, true );
500+
++$deleted;
501+
}
502+
503+
return $deleted;
504+
}
467505
}

includes/collection/class-outbox.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,51 @@ private static function get_object_title( $activity_object ) {
393393

394394
return $title;
395395
}
396+
397+
/**
398+
* Purge old outbox items.
399+
*
400+
* Deletes outbox items older than the specified number of days,
401+
* except for Follow activities which are always preserved.
402+
*
403+
* @param int $days Number of days to keep items. Items older than this will be deleted.
404+
*
405+
* @return int The number of items deleted.
406+
*/
407+
public static function purge( $days ) {
408+
$total_posts = (int) \wp_count_posts( self::POST_TYPE )->publish;
409+
if ( $total_posts <= 20 ) {
410+
return 0;
411+
}
412+
413+
$post_ids = \get_posts(
414+
array(
415+
'post_type' => self::POST_TYPE,
416+
'post_status' => 'any',
417+
'fields' => 'ids',
418+
'numberposts' => -1,
419+
'date_query' => array(
420+
array(
421+
'before' => \gmdate( 'Y-m-d', \time() - ( $days * DAY_IN_SECONDS ) ),
422+
),
423+
),
424+
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
425+
'meta_query' => array(
426+
array(
427+
'key' => '_activitypub_activity_type',
428+
'value' => 'Follow',
429+
'compare' => '!=',
430+
),
431+
),
432+
)
433+
);
434+
435+
$deleted = 0;
436+
foreach ( $post_ids as $post_id ) {
437+
\wp_delete_post( $post_id, true );
438+
++$deleted;
439+
}
440+
441+
return $deleted;
442+
}
396443
}

includes/collection/class-posts.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,4 +452,50 @@ public static function remove_recipient( $post_id, $user_id ) {
452452
// Delete the specific meta entry with this value.
453453
return \delete_post_meta( $post_id, '_activitypub_user_id', $user_id );
454454
}
455+
456+
/**
457+
* Purge old remote posts.
458+
*
459+
* Deletes remote posts older than the specified number of days,
460+
* but preserves posts that have comments (including likes, reposts, quotes)
461+
* as these indicate meaningful local user interactions.
462+
*
463+
* @param int $days Number of days to keep items. Items older than this will be deleted.
464+
*
465+
* @return int The number of items deleted.
466+
*/
467+
public static function purge( $days ) {
468+
$total_posts = (int) \wp_count_posts( self::POST_TYPE )->publish;
469+
if ( $total_posts <= 200 ) {
470+
return 0;
471+
}
472+
473+
$post_ids = \get_posts(
474+
array(
475+
'post_type' => self::POST_TYPE,
476+
'post_status' => 'any',
477+
'fields' => 'ids',
478+
'numberposts' => -1,
479+
'date_query' => array(
480+
array(
481+
'before' => \gmdate( 'Y-m-d', \time() - ( $days * DAY_IN_SECONDS ) ),
482+
),
483+
),
484+
)
485+
);
486+
487+
$deleted = 0;
488+
foreach ( $post_ids as $post_id ) {
489+
// Preserve posts with comments (includes likes, reposts, quotes).
490+
$comment_count = (int) \get_comments_number( $post_id );
491+
if ( $comment_count > 0 ) {
492+
continue;
493+
}
494+
495+
\wp_delete_post( $post_id, true );
496+
++$deleted;
497+
}
498+
499+
return $deleted;
500+
}
455501
}

includes/wp-admin/class-health-check.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,12 @@ public static function debug_information( $info ) {
342342
'private' => false,
343343
);
344344

345+
$info['activitypub']['fields']['activitypub_ap_post_purge_days'] = array(
346+
'label' => \__( 'Remote Posts Retention Period', 'activitypub' ),
347+
'value' => \esc_attr( (int) \get_option( 'activitypub_ap_post_purge_days', 180 ) ),
348+
'private' => false,
349+
);
350+
345351
$info['activitypub']['fields']['vary_header'] = array(
346352
'label' => \__( 'Vary Header', 'activitypub' ),
347353
'value' => \esc_attr( (int) \get_option( 'activitypub_vary_header', '1' ) ),

includes/wp-admin/class-settings.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,17 @@ public static function register_settings() {
186186
)
187187
);
188188

189+
\register_setting(
190+
'activitypub_advanced',
191+
'activitypub_ap_post_purge_days',
192+
array(
193+
'type' => 'integer',
194+
'description' => \__( 'Number of days to keep remote posts.', 'activitypub' ),
195+
'default' => 180,
196+
'show_in_rest' => false,
197+
)
198+
);
199+
189200
\register_setting(
190201
'activitypub_advanced',
191202
'activitypub_vary_header',

0 commit comments

Comments
 (0)