From c3a65ff1cfe04130edd68102049b4f501414079e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 30 Jul 2025 12:11:02 +0200 Subject: [PATCH 01/17] Init --- includes/class-cli.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 4950d5101..172f13ddd 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -23,15 +23,19 @@ class Cli extends \WP_CLI_Command { * * ## EXAMPLES * - * $ wp activitypub self-destruct + * $ wp activitypub self_destruct * * @param array|null $args The arguments. * @param array|null $assoc_args The associative arguments. * * @return void */ - public function self_destruct( $args, $assoc_args ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - \WP_CLI::warning( 'Self-Destructing is not implemented yet.' ); + public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $question = 'We are in the process of deleting your blog from the Fediverse. This action could be irreversible, so are you sure you want to continue?'; + + \WP_CLI::confirm( \WP_CLI::colorize( "%r{$question}%n" ), $assoc_args ); + + \WP_CLI::success( 'Deleting your Blog from the Fediverse...' ); } /** From 473b795029d439601003c3c187bb75c16afa6575 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 6 Aug 2025 12:14:56 +0200 Subject: [PATCH 02/17] Enhance self_destruct CLI command for user deletion Improves the self_destruct method to schedule deletion for all users with the 'activitypub' capability, logs each scheduled user, and updates messaging for clarity. Adds an option to mark the deletion process and instructs users to keep the plugin active until completion. --- includes/class-cli.php | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 172f13ddd..93b8e5373 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -31,11 +31,29 @@ class Cli extends \WP_CLI_Command { * @return void */ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - $question = 'We are in the process of deleting your blog from the Fediverse. This action could be irreversible, so are you sure you want to continue?'; + $question = 'You are in the process of deleting your blog from the Fediverse. This action could be irreversible, so are you sure you want to continue?'; - \WP_CLI::confirm( \WP_CLI::colorize( "%r{$question}%n" ), $assoc_args ); + \WP_CLI::confirm( \WP_CLI::colorize( "%y{$question}%n" ), $assoc_args ); - \WP_CLI::success( 'Deleting your Blog from the Fediverse...' ); + \WP_CLI::log( 'Deleting your Blog from the Fediverse. Scheduled delete for:' ); + + \add_option( 'activitypub_self_destruct', true ); + + $user_ids = \get_users( + array( + 'fields' => 'ids', + 'capability__in' => array( 'activitypub' ), + ) + ); + + foreach ( $user_ids as $user_id ) { + $actor = Actors::get_by_id( $user_id ); + add_to_outbox( $actor, 'Delete', $user_id ); + + \WP_CLI::line( ' - ' . $actor->get_name() ); + } + + \WP_CLI::success( 'All users scheduled for deletion. This process can take a while. Please keep the plugin active, we will notify you when it is done.' ); } /** From d5e3566d5d00e3ad641e8e1f282fc578b0bce582 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 6 Aug 2025 12:30:31 +0200 Subject: [PATCH 03/17] Improve CLI self-destruct messaging and process Enhanced the self_destruct command to provide clearer warnings, detailed process steps, and progress feedback when deleting a blog from the Fediverse. Added checks for user existence, improved error handling, and clarified next steps for users after scheduling deletions. --- includes/class-cli.php | 57 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 93b8e5373..e16648acf 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -31,14 +31,19 @@ class Cli extends \WP_CLI_Command { * @return void */ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - $question = 'You are in the process of deleting your blog from the Fediverse. This action could be irreversible, so are you sure you want to continue?'; + // Display warning header. + \WP_CLI::line( \WP_CLI::colorize( '%R⚠️ DESTRUCTIVE OPERATION ⚠️%n' ) ); + \WP_CLI::line( '' ); - \WP_CLI::confirm( \WP_CLI::colorize( "%y{$question}%n" ), $assoc_args ); + $question = 'You are about to delete your blog from the Fediverse. This action is IRREVERSIBLE and will:'; + \WP_CLI::line( \WP_CLI::colorize( "%y{$question}%n" ) ); + \WP_CLI::line( \WP_CLI::colorize( '%y• Send Delete activities to all followers%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%y• Remove your blog from ActivityPub networks%n' ) ); + \WP_CLI::line( '' ); - \WP_CLI::log( 'Deleting your Blog from the Fediverse. Scheduled delete for:' ); - - \add_option( 'activitypub_self_destruct', true ); + \WP_CLI::confirm( 'Are you absolutely sure you want to continue?', $assoc_args ); + // Get users before starting. $user_ids = \get_users( array( 'fields' => 'ids', @@ -46,14 +51,52 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore ) ); + if ( empty( $user_ids ) ) { + \WP_CLI::warning( 'No ActivityPub users found. Nothing to delete.' ); + return; + } + + // Show what will be processed. + $user_count = \count( $user_ids ); + \WP_CLI::line( \WP_CLI::colorize( '%GStarting Fediverse deletion process...%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( "%BFound {$user_count} ActivityPub user(s) to process:%n" ) ); + \WP_CLI::line( '' ); + + // Set the self-destruct flag. + \add_option( 'activitypub_self_destruct', true ); + + // Process each user with progress indication. + $processed = 0; foreach ( $user_ids as $user_id ) { $actor = Actors::get_by_id( $user_id ); + + if ( ! $actor ) { + \WP_CLI::line( \WP_CLI::colorize( '%R✗ Failed to load user ID: {$user_id}%n' ) ); + continue; + } + add_to_outbox( $actor, 'Delete', $user_id ); + ++$processed; + + \WP_CLI::line( \WP_CLI::colorize( "%G✓%n [{$processed}/{$user_count}] Scheduled deletion for: %B{$actor->get_name()}%n" ) ); + } + + \WP_CLI::line( '' ); - \WP_CLI::line( ' - ' . $actor->get_name() ); + if ( 0 === $processed ) { + \WP_CLI::error( 'Failed to schedule any deletions. Please check your configuration.' ); + return; } - \WP_CLI::success( 'All users scheduled for deletion. This process can take a while. Please keep the plugin active, we will notify you when it is done.' ); + // Final success message with clear next steps. + \WP_CLI::success( "Successfully scheduled {$processed} user(s) for Fediverse deletion." ); + \WP_CLI::line( '' ); + \WP_CLI::line( \WP_CLI::colorize( '%Y📋 Next Steps:%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y• Keep the ActivityPub plugin active%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y• Delete activities will be sent automatically%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y• Process may take several minutes to complete%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y• The plugin will notify you when the process is done.%n' ) ); + \WP_CLI::line( '' ); } /** From ed88240993652c758d703b2d38a858d31e5a1c1f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 6 Aug 2025 13:11:06 +0200 Subject: [PATCH 04/17] Add self-destruct completion notification for ActivityPub Implements a mechanism to check when all Delete activities for ActivityPub self-destruct are processed, removes the self-destruct flag, and displays an admin notice upon completion. Enhances user feedback and ensures proper cleanup after self-destruct operations. --- includes/class-cli.php | 8 +++-- includes/class-scheduler.php | 49 +++++++++++++++++++++++++++++++ includes/wp-admin/class-admin.php | 18 ++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index e16648acf..affd57ede 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -7,6 +7,7 @@ namespace Activitypub; +use Activitypub\Scheduler; use Activitypub\Collection\Actors; use Activitypub\Collection\Followers; use Activitypub\Collection\Outbox; @@ -43,10 +44,10 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore \WP_CLI::confirm( 'Are you absolutely sure you want to continue?', $assoc_args ); - // Get users before starting. + // Get all users with ActivityPub capabilities. $user_ids = \get_users( array( - 'fields' => 'ids', + 'fields' => 'ID', 'capability__in' => array( 'activitypub' ), ) ); @@ -65,6 +66,9 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore // Set the self-destruct flag. \add_option( 'activitypub_self_destruct', true ); + // Hook into outbox processing completion to notify when self-destruct is done. + \add_action( 'activitypub_self_destruct_processing_complete', array( Scheduler::class, 'check_self_destruct_completion' ), 10, 4 ); + // Process each user with progress indication. $processed = 0; foreach ( $user_ids as $user_id ) { diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index b12632c5f..0d0111cac 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -73,6 +73,55 @@ public static function register_schedulers() { do_action( 'activitypub_register_schedulers' ); } + /** + * Check if self-destruct process is complete and notify. + * + * @param array $inboxes The inboxes. + * @param string $json The ActivityPub Activity JSON. + * @param int $actor_id The actor ID. + * @param int $outbox_item_id The Outbox item ID. + */ + public static function check_self_destruct_completion( $inboxes, $json, $actor_id, $outbox_item_id ) { + // Only proceed if self-destruct is active. + if ( ! \get_option( 'activitypub_self_destruct', false ) ) { + return; + } + + // Check if this is a Delete activity (part of self-destruct). + $activity_data = \json_decode( $json, true ); + if ( ! isset( $activity_data['type'] ) || 'Delete' !== $activity_data['type'] ) { + return; + } + + // Check if there are any more pending Delete activities for self-destruct. + $pending_deletes = \get_posts( + array( + 'post_type' => 'ap_outbox', + 'post_status' => 'draft', + 'posts_per_page' => 1, + 'meta_query' => array( + array( + 'key' => 'activitypub_activity_type', + 'value' => 'Delete', + 'compare' => '=', + ), + ), + ) + ); + + // If no more pending Delete activities, self-destruct is complete. + if ( empty( $pending_deletes ) ) { + // Remove the self-destruct flag. + \delete_option( 'activitypub_self_destruct' ); + + // Remove the completion hook to avoid future notifications. + \remove_action( 'activitypub_self_destruct_processing_complete', array( self::class, 'check_self_destruct_completion' ), 10 ); + + // Add an admin notice for completion. + \add_option( 'activitypub_self_destruct_complete', \time() ); + } + } + /** * Schedule all ActivityPub schedules. */ diff --git a/includes/wp-admin/class-admin.php b/includes/wp-admin/class-admin.php index b97a02e39..39169eabd 100644 --- a/includes/wp-admin/class-admin.php +++ b/includes/wp-admin/class-admin.php @@ -81,6 +81,24 @@ public static function admin_notices() { if ( ! $current_screen ) { return; } + + // Check for self-destruct completion notice. + $self_destruct_complete = \get_option( 'activitypub_self_destruct_complete' ); + if ( $self_destruct_complete ) { + // Show the notice only once, then remove it. + \delete_option( 'activitypub_self_destruct_complete' ); + ?> +
+

+ +

+

+ +

+
+ base && Extra_Fields::is_extra_fields_post_type( $current_screen->post_type ) ) { ?>
From 14d363ee5185ec9f311fba924210a05785708b64 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 6 Aug 2025 13:15:36 +0200 Subject: [PATCH 05/17] Update self-destruct completion hook and logic Changed the action hook from 'activitypub_self_destruct_processing_complete' to 'activitypub_outbox_processing_complete' and updated the method signature for check_self_destruct_completion to accept only inboxes and JSON. Adjusted post type, status, and meta key in the pending deletes query for consistency with Outbox implementation. --- includes/class-cli.php | 2 +- includes/class-scheduler.php | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index affd57ede..40655819b 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -67,7 +67,7 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore \add_option( 'activitypub_self_destruct', true ); // Hook into outbox processing completion to notify when self-destruct is done. - \add_action( 'activitypub_self_destruct_processing_complete', array( Scheduler::class, 'check_self_destruct_completion' ), 10, 4 ); + \add_action( 'activitypub_outbox_processing_complete', array( Scheduler::class, 'check_self_destruct_completion' ), 10, 2 ); // Process each user with progress indication. $processed = 0; diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 0d0111cac..8857790a5 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -78,10 +78,8 @@ public static function register_schedulers() { * * @param array $inboxes The inboxes. * @param string $json The ActivityPub Activity JSON. - * @param int $actor_id The actor ID. - * @param int $outbox_item_id The Outbox item ID. */ - public static function check_self_destruct_completion( $inboxes, $json, $actor_id, $outbox_item_id ) { + public static function check_self_destruct_completion( $inboxes, $json ) { // Only proceed if self-destruct is active. if ( ! \get_option( 'activitypub_self_destruct', false ) ) { return; @@ -96,12 +94,13 @@ public static function check_self_destruct_completion( $inboxes, $json, $actor_i // Check if there are any more pending Delete activities for self-destruct. $pending_deletes = \get_posts( array( - 'post_type' => 'ap_outbox', - 'post_status' => 'draft', + 'post_type' => Outbox::POST_TYPE, + 'post_status' => 'pending', 'posts_per_page' => 1, + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => array( array( - 'key' => 'activitypub_activity_type', + 'key' => '_activitypub_activity_type', 'value' => 'Delete', 'compare' => '=', ), @@ -115,7 +114,7 @@ public static function check_self_destruct_completion( $inboxes, $json, $actor_i \delete_option( 'activitypub_self_destruct' ); // Remove the completion hook to avoid future notifications. - \remove_action( 'activitypub_self_destruct_processing_complete', array( self::class, 'check_self_destruct_completion' ), 10 ); + \remove_action( 'activitypub_outbox_processing_complete', array( self::class, 'check_self_destruct_completion' ), 10 ); // Add an admin notice for completion. \add_option( 'activitypub_self_destruct_complete', \time() ); From e60da0be660bcaf95ea2a0714c6afe31ce81af78 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 6 Aug 2025 13:19:13 +0200 Subject: [PATCH 06/17] Update includes/class-cli.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- includes/class-cli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 40655819b..7d4588267 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -75,7 +75,7 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore $actor = Actors::get_by_id( $user_id ); if ( ! $actor ) { - \WP_CLI::line( \WP_CLI::colorize( '%R✗ Failed to load user ID: {$user_id}%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( "%R✗ Failed to load user ID: {$user_id}%n" ) ); continue; } From a44cddaa5275251df5ae0e2e2d924724f53ad976 Mon Sep 17 00:00:00 2001 From: Automattic Bot Date: Wed, 6 Aug 2025 13:21:28 +0200 Subject: [PATCH 07/17] Add changelog --- .github/changelog/2046-from-description | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/changelog/2046-from-description diff --git a/.github/changelog/2046-from-description b/.github/changelog/2046-from-description new file mode 100644 index 000000000..b04a4d6af --- /dev/null +++ b/.github/changelog/2046-from-description @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Adds a self-destruct feature to remove a blog from the Fediverse by sending Delete activities to followers. From 68d3f74ed32e8ccb6b371dcadfe49ce441ac379e Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 6 Aug 2025 13:55:12 +0200 Subject: [PATCH 08/17] Refactor outbox deletion to use Activity object Replaces direct parameters with an Activity object when adding a 'Delete' activity to the outbox. This change improves consistency and leverages the Activity class for better structure. --- includes/class-cli.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 7d4588267..07661b423 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -7,10 +7,11 @@ namespace Activitypub; -use Activitypub\Scheduler; +use Activitypub\Activity\Activity; use Activitypub\Collection\Actors; use Activitypub\Collection\Followers; use Activitypub\Collection\Outbox; +use Activitypub\Scheduler; /** * WP-CLI commands. @@ -79,7 +80,12 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore continue; } - add_to_outbox( $actor, 'Delete', $user_id ); + $activity = new Activity(); + $activity->set_actor( $actor->get_id() ); + $activity->set_object( $actor->get_id() ); + $activity->set_type( 'Delete' ); + + add_to_outbox( $activity, null, $user_id ); ++$processed; \WP_CLI::line( \WP_CLI::colorize( "%G✓%n [{$processed}/{$user_count}] Scheduled deletion for: %B{$actor->get_name()}%n" ) ); From 2c146911d771241f9d04a22c149276e9a9b6f1ca Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 18:20:07 +0200 Subject: [PATCH 09/17] Refactor and enhance self-destruct CLI command The self_destruct WP-CLI command for ActivityPub is now refactored for modularity and clarity, with improved documentation, confirmation handling, and status checking. The process is split into smaller private methods, and a new --status flag allows users to check progress. The self-destruct completion logic and related hook are removed from Scheduler, and status tracking is now handled directly in the CLI command. --- includes/class-cli.php | 265 ++++++++++++++++++++++++++++++----- includes/class-scheduler.php | 48 ------- 2 files changed, 232 insertions(+), 81 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 07661b423..f9317685b 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -23,17 +23,100 @@ class Cli extends \WP_CLI_Command { /** * Remove the entire blog from the Fediverse. * + * This command permanently removes your blog from ActivityPub networks by sending + * Delete activities to all followers. This action is IRREVERSIBLE. + * + * ## OPTIONS + * + * [--status] + * : Check the status of the self-destruct process instead of running it. + * Use this to monitor progress after initiating the deletion process. + * + * [--yes] + * : Skip the confirmation prompt and proceed with deletion immediately. + * Use with extreme caution as this bypasses all safety checks. + * * ## EXAMPLES * + * # Start the self-destruct process (with confirmation prompt) * $ wp activitypub self_destruct * - * @param array|null $args The arguments. - * @param array|null $assoc_args The associative arguments. + * # Check the status of an ongoing self-destruct process + * $ wp activitypub self_destruct --status + * + * # Force deletion without confirmation (dangerous!) + * $ wp activitypub self_destruct --yes + * + * ## WHAT THIS DOES + * + * - Finds all users with ActivityPub capabilities + * - Creates Delete activities for each user + * - Sends these activities to all followers + * - Removes your blog from ActivityPub discovery + * - Sets a flag to track completion status + * + * ## IMPORTANT NOTES + * + * - This action cannot be undone + * - Keep the ActivityPub plugin active during the process + * - The process may take several minutes to complete + * - You will be notified when the process finishes + * + * @param array|null $args The positional arguments (unused). + * @param array|null $assoc_args The associative arguments (--status, --yes). * * @return void */ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - // Display warning header. + // Check if --status flag is provided. + if ( isset( $assoc_args['status'] ) ) { + $this->show_self_destruct_status(); + return; + } + + // Check if self-destruct has already been run. + if ( \get_option( 'activitypub_self_destruct' ) ) { + \WP_CLI::error( 'Self-destruct has already been initiated. The process may still be running or has completed.' . PHP_EOL . \WP_CLI::colorize( 'To check the status, run: %Bwp activitypub self_destruct --status%n' ) ); + return; + } + + $this->execute_self_destruct( $assoc_args ); + } + + /** + * Execute the self-destruct process. + * + * This method handles the actual deletion process: + * 1. Displays warning and confirmation prompt + * 2. Retrieves all ActivityPub-capable users + * 3. Creates and schedules Delete activities for each user + * 4. Sets the self-destruct flag for status tracking + * 5. Provides progress feedback and completion instructions + * + * @param array $assoc_args The associative arguments from WP-CLI. + * + * @return void + */ + private function execute_self_destruct( $assoc_args ) { + $this->display_self_destruct_warning(); + \WP_CLI::confirm( 'Are you absolutely sure you want to continue?', $assoc_args ); + + $user_ids = $this->get_activitypub_users(); + if ( empty( $user_ids ) ) { + \WP_CLI::warning( 'No ActivityPub users found. Nothing to delete.' ); + return; + } + + $processed = $this->process_user_deletions( $user_ids ); + $this->display_completion_message( $processed ); + } + + /** + * Display the self-destruct warning message. + * + * @return void + */ + private function display_self_destruct_warning() { \WP_CLI::line( \WP_CLI::colorize( '%R⚠️ DESTRUCTIVE OPERATION ⚠️%n' ) ); \WP_CLI::line( '' ); @@ -42,23 +125,30 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore \WP_CLI::line( \WP_CLI::colorize( '%y• Send Delete activities to all followers%n' ) ); \WP_CLI::line( \WP_CLI::colorize( '%y• Remove your blog from ActivityPub networks%n' ) ); \WP_CLI::line( '' ); + } - \WP_CLI::confirm( 'Are you absolutely sure you want to continue?', $assoc_args ); - - // Get all users with ActivityPub capabilities. - $user_ids = \get_users( + /** + * Get all users with ActivityPub capabilities. + * + * @return array Array of user IDs with ActivityPub capabilities. + */ + private function get_activitypub_users() { + return \get_users( array( 'fields' => 'ID', 'capability__in' => array( 'activitypub' ), ) ); + } - if ( empty( $user_ids ) ) { - \WP_CLI::warning( 'No ActivityPub users found. Nothing to delete.' ); - return; - } - - // Show what will be processed. + /** + * Process user deletions and create Delete activities. + * + * @param array $user_ids Array of user IDs to process. + * + * @return int Number of users successfully processed. + */ + private function process_user_deletions( $user_ids ) { $user_count = \count( $user_ids ); \WP_CLI::line( \WP_CLI::colorize( '%GStarting Fediverse deletion process...%n' ) ); \WP_CLI::line( \WP_CLI::colorize( "%BFound {$user_count} ActivityPub user(s) to process:%n" ) ); @@ -67,38 +157,67 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore // Set the self-destruct flag. \add_option( 'activitypub_self_destruct', true ); - // Hook into outbox processing completion to notify when self-destruct is done. - \add_action( 'activitypub_outbox_processing_complete', array( Scheduler::class, 'check_self_destruct_completion' ), 10, 2 ); - - // Process each user with progress indication. $processed = 0; foreach ( $user_ids as $user_id ) { - $actor = Actors::get_by_id( $user_id ); - - if ( ! $actor ) { - \WP_CLI::line( \WP_CLI::colorize( "%R✗ Failed to load user ID: {$user_id}%n" ) ); - continue; + if ( $this->create_delete_activity_for_user( $user_id, $processed, $user_count ) ) { + ++$processed; } + } + + \WP_CLI::line( '' ); + + if ( 0 === $processed ) { + \WP_CLI::error( 'Failed to schedule any deletions. Please check your configuration.' ); + } + + return $processed; + } - $activity = new Activity(); - $activity->set_actor( $actor->get_id() ); - $activity->set_object( $actor->get_id() ); - $activity->set_type( 'Delete' ); + /** + * Create a Delete activity for a specific user. + * + * @param int $user_id The user ID to process. + * @param int $processed Number of users already processed. + * @param int $user_count Total number of users to process. + * + * @return bool True if the activity was created successfully, false otherwise. + */ + private function create_delete_activity_for_user( $user_id, $processed, $user_count ) { + $actor = Actors::get_by_id( $user_id ); - add_to_outbox( $activity, null, $user_id ); - ++$processed; + if ( ! $actor ) { + \WP_CLI::line( \WP_CLI::colorize( "%R✗ Failed to load user ID: {$user_id}%n" ) ); + return false; + } + + $activity = new Activity(); + $activity->set_actor( $actor->get_id() ); + $activity->set_object( $actor->get_id() ); + $activity->set_type( 'Delete' ); - \WP_CLI::line( \WP_CLI::colorize( "%G✓%n [{$processed}/{$user_count}] Scheduled deletion for: %B{$actor->get_name()}%n" ) ); + $result = add_to_outbox( $activity, null, $user_id ); + if ( is_wp_error( $result ) ) { + \WP_CLI::line( \WP_CLI::colorize( "%R✗ Failed to schedule deletion for: %B{$actor->get_name()}%n - {$result->get_error_message()}" ) ); + return false; } - \WP_CLI::line( '' ); + $current = $processed + 1; + \WP_CLI::line( \WP_CLI::colorize( "%G✓%n [{$current}/{$user_count}] Scheduled deletion for: %B{$actor->get_name()}%n" ) ); + return true; + } + /** + * Display the completion message after processing. + * + * @param int $processed Number of users successfully processed. + * + * @return void + */ + private function display_completion_message( $processed ) { if ( 0 === $processed ) { - \WP_CLI::error( 'Failed to schedule any deletions. Please check your configuration.' ); - return; + return; // Error already displayed in process_user_deletions } - // Final success message with clear next steps. \WP_CLI::success( "Successfully scheduled {$processed} user(s) for Fediverse deletion." ); \WP_CLI::line( '' ); \WP_CLI::line( \WP_CLI::colorize( '%Y📋 Next Steps:%n' ) ); @@ -109,6 +228,86 @@ public function self_destruct( $args, $assoc_args = array() ) { // phpcs:ignore \WP_CLI::line( '' ); } + /** + * Show the status of the self-destruct process. + * + * Checks the current state of the self-destruct process by: + * - Verifying if the process has been initiated + * - Counting remaining pending Delete activities + * - Displaying appropriate status messages and progress + * - Providing guidance on next steps + * + * Status can be: + * - NOT STARTED: Process hasn't been initiated + * - IN PROGRESS: Delete activities are still being processed + * - COMPLETED: All Delete activities have been sent + * + * @return void + */ + private function show_self_destruct_status() { + // Only proceed if self-destruct is active. + if ( ! \get_option( 'activitypub_self_destruct', false ) ) { + \WP_CLI::line( \WP_CLI::colorize( '%C❌ Status: NOT STARTED%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%CThe self-destruct process has not been initiated.%n' ) ); + \WP_CLI::line( '' ); + \WP_CLI::line( \WP_CLI::colorize( '%C💡 To start the process, run:%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%C wp activitypub self_destruct%n' ) ); + \WP_CLI::line( '' ); + return; + } + + \WP_CLI::line( \WP_CLI::colorize( '%B🔍 Self-Destruct Status Check%n' ) ); + \WP_CLI::line( '' ); + + // Check if there are any more pending Delete activities for self-destruct. + $pending_deletes = \get_posts( + array( + 'post_type' => Outbox::POST_TYPE, + 'post_status' => 'pending', + 'posts_per_page' => -1, + 'fields' => 'ids', + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + array( + 'key' => '_activitypub_activity_type', + 'value' => 'Delete', + 'compare' => '=', + ), + ), + ) + ); + + // Get count of pending Delete activities + $pending_count = count( $pending_deletes ); + + // If no more pending Delete activities, self-destruct is complete. + if ( 0 === $pending_count ) { + \WP_CLI::line( \WP_CLI::colorize( '%G✅ Status: COMPLETED%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%GYour blog has been successfully removed from the Fediverse.%n' ) ); + \WP_CLI::line( '' ); + \WP_CLI::line( \WP_CLI::colorize( '%Y📋 What happened:%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y• Delete activities were sent to all followers%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y• Your blog is no longer discoverable on ActivityPub networks%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y• The self-destruct process has finished%n' ) ); + } else { + \WP_CLI::line( \WP_CLI::colorize( '%Y⏳ Status: IN PROGRESS%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%YThe self-destruct process is currently running.%n' ) ); + \WP_CLI::line( '' ); + + if ( $pending_count > 0 ) { + \WP_CLI::line( \WP_CLI::colorize( "%Y📊 Progress: {$pending_count} Delete Activities still pending%n" ) ); + } else { + \WP_CLI::line( \WP_CLI::colorize( '%Y📊 Progress: All Delete Activities have been processed%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%Y⚠️ Completion notification may be pending%n' ) ); + } + + \WP_CLI::line( '' ); + \WP_CLI::line( \WP_CLI::colorize( '%Y💡 Note: The process may take several minutes to complete.%n' ) ); + } + + \WP_CLI::line( '' ); + } + /** * Delete or Update a Post, Page, Custom Post Type or Attachment. * diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 8cdd41d74..0db20bc36 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -73,54 +73,6 @@ public static function register_schedulers() { do_action( 'activitypub_register_schedulers' ); } - /** - * Check if self-destruct process is complete and notify. - * - * @param array $inboxes The inboxes. - * @param string $json The ActivityPub Activity JSON. - */ - public static function check_self_destruct_completion( $inboxes, $json ) { - // Only proceed if self-destruct is active. - if ( ! \get_option( 'activitypub_self_destruct', false ) ) { - return; - } - - // Check if this is a Delete activity (part of self-destruct). - $activity_data = \json_decode( $json, true ); - if ( ! isset( $activity_data['type'] ) || 'Delete' !== $activity_data['type'] ) { - return; - } - - // Check if there are any more pending Delete activities for self-destruct. - $pending_deletes = \get_posts( - array( - 'post_type' => Outbox::POST_TYPE, - 'post_status' => 'pending', - 'posts_per_page' => 1, - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query - 'meta_query' => array( - array( - 'key' => '_activitypub_activity_type', - 'value' => 'Delete', - 'compare' => '=', - ), - ), - ) - ); - - // If no more pending Delete activities, self-destruct is complete. - if ( empty( $pending_deletes ) ) { - // Remove the self-destruct flag. - \delete_option( 'activitypub_self_destruct' ); - - // Remove the completion hook to avoid future notifications. - \remove_action( 'activitypub_outbox_processing_complete', array( self::class, 'check_self_destruct_completion' ), 10 ); - - // Add an admin notice for completion. - \add_option( 'activitypub_self_destruct_complete', \time() ); - } - } - /** * Schedule all ActivityPub schedules. */ From 1d6c85192251649e9dcb6e062eb518d79b854434 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 18:23:11 +0200 Subject: [PATCH 10/17] Add missing periods to inline comments Standardized inline comments by adding missing periods for consistency and improved readability in class-cli.php. --- includes/class-cli.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index f9317685b..aa7b8ce08 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -215,7 +215,7 @@ private function create_delete_activity_for_user( $user_id, $processed, $user_co */ private function display_completion_message( $processed ) { if ( 0 === $processed ) { - return; // Error already displayed in process_user_deletions + return; // Error already displayed in process_user_deletions. } \WP_CLI::success( "Successfully scheduled {$processed} user(s) for Fediverse deletion." ); @@ -277,7 +277,7 @@ private function show_self_destruct_status() { ) ); - // Get count of pending Delete activities + // Get count of pending Delete activities. $pending_count = count( $pending_deletes ); // If no more pending Delete activities, self-destruct is complete. From 7c963a23757b6790e6a3883e2cf100db68bcd82c Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 20:02:07 +0200 Subject: [PATCH 11/17] Update includes/class-cli.php Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- includes/class-cli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index aa7b8ce08..9da0b7794 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -155,7 +155,7 @@ private function process_user_deletions( $user_ids ) { \WP_CLI::line( '' ); // Set the self-destruct flag. - \add_option( 'activitypub_self_destruct', true ); + \update_option( 'activitypub_self_destruct', true ); $processed = 0; foreach ( $user_ids as $user_id ) { From fce53008a050d635f77e9c8122beed6147b7975b Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 21:12:57 +0200 Subject: [PATCH 12/17] Update includes/class-cli.php Co-authored-by: Konstantin Obenland --- includes/class-cli.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 9da0b7794..2ca9043f4 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -294,12 +294,7 @@ private function show_self_destruct_status() { \WP_CLI::line( \WP_CLI::colorize( '%YThe self-destruct process is currently running.%n' ) ); \WP_CLI::line( '' ); - if ( $pending_count > 0 ) { - \WP_CLI::line( \WP_CLI::colorize( "%Y📊 Progress: {$pending_count} Delete Activities still pending%n" ) ); - } else { - \WP_CLI::line( \WP_CLI::colorize( '%Y📊 Progress: All Delete Activities have been processed%n' ) ); - \WP_CLI::line( \WP_CLI::colorize( '%Y⚠️ Completion notification may be pending%n' ) ); - } + \WP_CLI::line( \WP_CLI::colorize( "%Y📊 Progress: {$pending_count} Delete Activities still pending%n" ) ); \WP_CLI::line( '' ); \WP_CLI::line( \WP_CLI::colorize( '%Y💡 Note: The process may take several minutes to complete.%n' ) ); From 36bb6ab8996fb751b034d4c591f93500ad469f9d Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 21:13:13 +0200 Subject: [PATCH 13/17] Update includes/class-cli.php Co-authored-by: Konstantin Obenland --- includes/class-cli.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 2ca9043f4..a433b70d4 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -271,7 +271,6 @@ private function show_self_destruct_status() { array( 'key' => '_activitypub_activity_type', 'value' => 'Delete', - 'compare' => '=', ), ), ) From 99e7644c3b5034dc9fbba643faea341ac364efb1 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 21:14:23 +0200 Subject: [PATCH 14/17] Fix array alignment in meta_query for CLI command Adjusted the alignment of 'key' and 'value' in the meta_query array for better code readability and consistency. --- includes/class-cli.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index a433b70d4..421f114ba 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -269,8 +269,8 @@ private function show_self_destruct_status() { // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => array( array( - 'key' => '_activitypub_activity_type', - 'value' => 'Delete', + 'key' => '_activitypub_activity_type', + 'value' => 'Delete', ), ), ) From 46c4be0ad0215e02ac3c9bd4106c21b11f56a4af Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 21:16:15 +0200 Subject: [PATCH 15/17] Remove emojis from CLI progress messages Emojis were removed from the progress and note messages in the self-destruct process output to improve compatibility and readability in CLI environments. --- includes/class-cli.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 421f114ba..25535ff2e 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -293,10 +293,10 @@ private function show_self_destruct_status() { \WP_CLI::line( \WP_CLI::colorize( '%YThe self-destruct process is currently running.%n' ) ); \WP_CLI::line( '' ); - \WP_CLI::line( \WP_CLI::colorize( "%Y📊 Progress: {$pending_count} Delete Activities still pending%n" ) ); + \WP_CLI::line( \WP_CLI::colorize( "%YProgress: {$pending_count} Delete Activities still pending%n" ) ); \WP_CLI::line( '' ); - \WP_CLI::line( \WP_CLI::colorize( '%Y💡 Note: The process may take several minutes to complete.%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%YNote: The process may take several minutes to complete.%n' ) ); } \WP_CLI::line( '' ); From a5a6160adb90241bf9d644ae32173507a38f6eed Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Tue, 26 Aug 2025 21:23:59 +0200 Subject: [PATCH 16/17] Improve CLI self-destruct process instructions Consolidated and reformatted the message shown when the self-destruct process has not started, making the command to run more visible and easier to follow. --- includes/class-cli.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 25535ff2e..c292ccddf 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -250,8 +250,7 @@ private function show_self_destruct_status() { \WP_CLI::line( \WP_CLI::colorize( '%C❌ Status: NOT STARTED%n' ) ); \WP_CLI::line( \WP_CLI::colorize( '%CThe self-destruct process has not been initiated.%n' ) ); \WP_CLI::line( '' ); - \WP_CLI::line( \WP_CLI::colorize( '%C💡 To start the process, run:%n' ) ); - \WP_CLI::line( \WP_CLI::colorize( '%C wp activitypub self_destruct%n' ) ); + \WP_CLI::line( \WP_CLI::colorize( '%CTo start the process, run:%n %Bwp activitypub self_destruct%n' ) ); \WP_CLI::line( '' ); return; } From 43e95d2ba959b3b84e50bdbc14e9e927980a2d82 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 27 Aug 2025 08:32:21 +0200 Subject: [PATCH 17/17] Fix incorrect import in class-cli.php Replaces the import of Followers with Outbox in the class-cli.php file to ensure correct class usage. --- includes/class-cli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-cli.php b/includes/class-cli.php index 7a3b41eac..40bbb77ba 100644 --- a/includes/class-cli.php +++ b/includes/class-cli.php @@ -9,7 +9,7 @@ use Activitypub\Activity\Activity; use Activitypub\Collection\Actors; -use Activitypub\Collection\Followers; +use Activitypub\Collection\Outbox; /** * WP-CLI commands.