77
88namespace  Activitypub ;
99
10+ use  Activitypub \Activity \Activity ;
11+ use  Activitypub \Collection \Actors ;
1012use  Activitypub \Collection \Outbox ;
1113
1214/** 
@@ -19,17 +21,282 @@ class Cli extends \WP_CLI_Command {
1921	/** 
2022	 * Remove the entire blog from the Fediverse. 
2123	 * 
24+ 	 * This command permanently removes your blog from ActivityPub networks by sending 
25+ 	 * Delete activities to all followers. This action is IRREVERSIBLE. 
26+ 	 * 
27+ 	 * ## OPTIONS 
28+ 	 * 
29+ 	 * [--status] 
30+ 	 * : Check the status of the self-destruct process instead of running it. 
31+ 	 * Use this to monitor progress after initiating the deletion process. 
32+ 	 * 
33+ 	 * [--yes] 
34+ 	 * : Skip the confirmation prompt and proceed with deletion immediately. 
35+ 	 * Use with extreme caution as this bypasses all safety checks. 
36+ 	 * 
2237	 * ## EXAMPLES 
2338	 * 
24- 	 *     $ wp activitypub self-destruct 
39+ 	 *     # Start the self-destruct process (with confirmation prompt) 
40+ 	 *     $ wp activitypub self_destruct 
41+ 	 * 
42+ 	 *     # Check the status of an ongoing self-destruct process 
43+ 	 *     $ wp activitypub self_destruct --status 
44+ 	 * 
45+ 	 *     # Force deletion without confirmation (dangerous!) 
46+ 	 *     $ wp activitypub self_destruct --yes 
47+ 	 * 
48+ 	 * ## WHAT THIS DOES 
49+ 	 * 
50+ 	 * - Finds all users with ActivityPub capabilities 
51+ 	 * - Creates Delete activities for each user 
52+ 	 * - Sends these activities to all followers 
53+ 	 * - Removes your blog from ActivityPub discovery 
54+ 	 * - Sets a flag to track completion status 
55+ 	 * 
56+ 	 * ## IMPORTANT NOTES 
57+ 	 * 
58+ 	 * - This action cannot be undone 
59+ 	 * - Keep the ActivityPub plugin active during the process 
60+ 	 * - The process may take several minutes to complete 
61+ 	 * - You will be notified when the process finishes 
62+ 	 * 
63+ 	 * @param array|null $args       The positional arguments (unused). 
64+ 	 * @param array|null $assoc_args The associative arguments (--status, --yes). 
65+ 	 * 
66+ 	 * @return void 
67+ 	 */ 
68+ 	public  function  self_destruct ( $ args , $ assoc_args  = array () ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable 
69+ 		// Check if --status flag is provided. 
70+ 		if  ( isset ( $ assoc_args ['status ' ] ) ) {
71+ 			$ this  ->show_self_destruct_status ();
72+ 			return ;
73+ 		}
74+ 
75+ 		// Check if self-destruct has already been run. 
76+ 		if  ( \get_option ( 'activitypub_self_destruct '  ) ) {
77+ 			\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 '  ) );
78+ 			return ;
79+ 		}
80+ 
81+ 		$ this  ->execute_self_destruct ( $ assoc_args  );
82+ 	}
83+ 
84+ 	/** 
85+ 	 * Execute the self-destruct process. 
86+ 	 * 
87+ 	 * This method handles the actual deletion process: 
88+ 	 * 1. Displays warning and confirmation prompt 
89+ 	 * 2. Retrieves all ActivityPub-capable users 
90+ 	 * 3. Creates and schedules Delete activities for each user 
91+ 	 * 4. Sets the self-destruct flag for status tracking 
92+ 	 * 5. Provides progress feedback and completion instructions 
93+ 	 * 
94+ 	 * @param array $assoc_args The associative arguments from WP-CLI. 
95+ 	 * 
96+ 	 * @return void 
97+ 	 */ 
98+ 	private  function  execute_self_destruct ( $ assoc_args  ) {
99+ 		$ this  ->display_self_destruct_warning ();
100+ 		\WP_CLI ::confirm ( 'Are you absolutely sure you want to continue? ' , $ assoc_args  );
101+ 
102+ 		$ user_ids  = $ this  ->get_activitypub_users ();
103+ 		if  ( empty ( $ user_ids  ) ) {
104+ 			\WP_CLI ::warning ( 'No ActivityPub users found. Nothing to delete. '  );
105+ 			return ;
106+ 		}
107+ 
108+ 		$ processed  = $ this  ->process_user_deletions ( $ user_ids  );
109+ 		$ this  ->display_completion_message ( $ processed  );
110+ 	}
111+ 
112+ 	/** 
113+ 	 * Display the self-destruct warning message. 
114+ 	 * 
115+ 	 * @return void 
116+ 	 */ 
117+ 	private  function  display_self_destruct_warning () {
118+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%R⚠️  DESTRUCTIVE OPERATION ⚠️%n '  ) );
119+ 		\WP_CLI ::line ( ''  );
120+ 
121+ 		$ question  = 'You are about to delete your blog from the Fediverse. This action is IRREVERSIBLE and will: ' ;
122+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( "%y {$ question }%n "  ) );
123+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%y• Send Delete activities to all followers%n '  ) );
124+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%y• Remove your blog from ActivityPub networks%n '  ) );
125+ 		\WP_CLI ::line ( ''  );
126+ 	}
127+ 
128+ 	/** 
129+ 	 * Get all users with ActivityPub capabilities. 
130+ 	 * 
131+ 	 * @return array Array of user IDs with ActivityPub capabilities. 
132+ 	 */ 
133+ 	private  function  get_activitypub_users () {
134+ 		return  \get_users (
135+ 			array (
136+ 				'fields '          => 'ID ' ,
137+ 				'capability__in '  => array ( 'activitypub '  ),
138+ 			)
139+ 		);
140+ 	}
141+ 
142+ 	/** 
143+ 	 * Process user deletions and create Delete activities. 
144+ 	 * 
145+ 	 * @param array $user_ids Array of user IDs to process. 
25146	 * 
26- 	 * @param array|null $args       The arguments. 
27- 	 * @param array|null $assoc_args The associative arguments. 
147+ 	 * @return int Number of users successfully processed. 
148+ 	 */ 
149+ 	private  function  process_user_deletions ( $ user_ids  ) {
150+ 		$ user_count  = \count ( $ user_ids  );
151+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%GStarting Fediverse deletion process...%n '  ) );
152+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( "%BFound  {$ user_count } ActivityPub user(s) to process:%n "  ) );
153+ 		\WP_CLI ::line ( ''  );
154+ 
155+ 		// Set the self-destruct flag. 
156+ 		\update_option ( 'activitypub_self_destruct ' , true  );
157+ 
158+ 		$ processed  = 0 ;
159+ 		foreach  ( $ user_ids  as  $ user_id  ) {
160+ 			if  ( $ this  ->create_delete_activity_for_user ( $ user_id , $ processed , $ user_count  ) ) {
161+ 				++$ processed ;
162+ 			}
163+ 		}
164+ 
165+ 		\WP_CLI ::line ( ''  );
166+ 
167+ 		if  ( 0  === $ processed  ) {
168+ 			\WP_CLI ::error ( 'Failed to schedule any deletions. Please check your configuration. '  );
169+ 		}
170+ 
171+ 		return  $ processed ;
172+ 	}
173+ 
174+ 	/** 
175+ 	 * Create a Delete activity for a specific user. 
176+ 	 * 
177+ 	 * @param int $user_id    The user ID to process. 
178+ 	 * @param int $processed  Number of users already processed. 
179+ 	 * @param int $user_count Total number of users to process. 
180+ 	 * 
181+ 	 * @return bool True if the activity was created successfully, false otherwise. 
182+ 	 */ 
183+ 	private  function  create_delete_activity_for_user ( $ user_id , $ processed , $ user_count  ) {
184+ 		$ actor  = Actors::get_by_id ( $ user_id  );
185+ 
186+ 		if  ( ! $ actor  ) {
187+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( "%R✗ Failed to load user ID:  {$ user_id }%n "  ) );
188+ 			return  false ;
189+ 		}
190+ 
191+ 		$ activity  = new  Activity ();
192+ 		$ activity ->set_actor ( $ actor ->get_id () );
193+ 		$ activity ->set_object ( $ actor ->get_id () );
194+ 		$ activity ->set_type ( 'Delete '  );
195+ 
196+ 		$ result  = add_to_outbox ( $ activity , null , $ user_id  );
197+ 		if  ( is_wp_error ( $ result  ) ) {
198+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( "%R✗ Failed to schedule deletion for: %B {$ actor ->get_name ()}%n -  {$ result ->get_error_message ()}"  ) );
199+ 			return  false ;
200+ 		}
201+ 
202+ 		$ current  = $ processed  + 1 ;
203+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( "%G✓%n [ {$ current }/ {$ user_count }] Scheduled deletion for: %B {$ actor ->get_name ()}%n "  ) );
204+ 		return  true ;
205+ 	}
206+ 
207+ 	/** 
208+ 	 * Display the completion message after processing. 
209+ 	 * 
210+ 	 * @param int $processed Number of users successfully processed. 
211+ 	 * 
212+ 	 * @return void 
213+ 	 */ 
214+ 	private  function  display_completion_message ( $ processed  ) {
215+ 		if  ( 0  === $ processed  ) {
216+ 			return ; // Error already displayed in process_user_deletions. 
217+ 		}
218+ 
219+ 		\WP_CLI ::success ( "Successfully scheduled  {$ processed } user(s) for Fediverse deletion. "  );
220+ 		\WP_CLI ::line ( ''  );
221+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y📋 Next Steps:%n '  ) );
222+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y• Keep the ActivityPub plugin active%n '  ) );
223+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y• Delete activities will be sent automatically%n '  ) );
224+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y• Process may take several minutes to complete%n '  ) );
225+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y• The plugin will notify you when the process is done.%n '  ) );
226+ 		\WP_CLI ::line ( ''  );
227+ 	}
228+ 
229+ 	/** 
230+ 	 * Show the status of the self-destruct process. 
231+ 	 * 
232+ 	 * Checks the current state of the self-destruct process by: 
233+ 	 * - Verifying if the process has been initiated 
234+ 	 * - Counting remaining pending Delete activities 
235+ 	 * - Displaying appropriate status messages and progress 
236+ 	 * - Providing guidance on next steps 
237+ 	 * 
238+ 	 * Status can be: 
239+ 	 * - NOT STARTED: Process hasn't been initiated 
240+ 	 * - IN PROGRESS: Delete activities are still being processed 
241+ 	 * - COMPLETED: All Delete activities have been sent 
28242	 * 
29243	 * @return void 
30244	 */ 
31- 	public  function  self_destruct ( $ args , $ assoc_args  ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable 
32- 		\WP_CLI ::warning ( 'Self-Destructing is not implemented yet. '  );
245+ 	private  function  show_self_destruct_status () {
246+ 		// Only proceed if self-destruct is active. 
247+ 		if  ( ! \get_option ( 'activitypub_self_destruct ' , false  ) ) {
248+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%C❌ Status: NOT STARTED%n '  ) );
249+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%CThe self-destruct process has not been initiated.%n '  ) );
250+ 			\WP_CLI ::line ( ''  );
251+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%CTo start the process, run:%n %Bwp activitypub self_destruct%n '  ) );
252+ 			\WP_CLI ::line ( ''  );
253+ 			return ;
254+ 		}
255+ 
256+ 		\WP_CLI ::line ( \WP_CLI ::colorize ( '%B🔍 Self-Destruct Status Check%n '  ) );
257+ 		\WP_CLI ::line ( ''  );
258+ 
259+ 		// Check if there are any more pending Delete activities for self-destruct. 
260+ 		$ pending_deletes  = \get_posts (
261+ 			array (
262+ 				'post_type '       => Outbox::POST_TYPE ,
263+ 				'post_status '     => 'pending ' ,
264+ 				'posts_per_page '  => -1 ,
265+ 				'fields '          => 'ids ' ,
266+ 				// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 
267+ 				'meta_query '      => array (
268+ 					array (
269+ 						'key '    => '_activitypub_activity_type ' ,
270+ 						'value '  => 'Delete ' ,
271+ 					),
272+ 				),
273+ 			)
274+ 		);
275+ 
276+ 		// Get count of pending Delete activities. 
277+ 		$ pending_count  = count ( $ pending_deletes  );
278+ 
279+ 		// If no more pending Delete activities, self-destruct is complete. 
280+ 		if  ( 0  === $ pending_count  ) {
281+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%G✅ Status: COMPLETED%n '  ) );
282+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%GYour blog has been successfully removed from the Fediverse.%n '  ) );
283+ 			\WP_CLI ::line ( ''  );
284+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y📋 What happened:%n '  ) );
285+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y• Delete activities were sent to all followers%n '  ) );
286+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y• Your blog is no longer discoverable on ActivityPub networks%n '  ) );
287+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y• The self-destruct process has finished%n '  ) );
288+ 		} else  {
289+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%Y⏳ Status: IN PROGRESS%n '  ) );
290+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%YThe self-destruct process is currently running.%n '  ) );
291+ 			\WP_CLI ::line ( ''  );
292+ 
293+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( "%YProgress:  {$ pending_count } Delete Activities still pending%n "  ) );
294+ 
295+ 			\WP_CLI ::line ( ''  );
296+ 			\WP_CLI ::line ( \WP_CLI ::colorize ( '%YNote: The process may take several minutes to complete.%n '  ) );
297+ 		}
298+ 
299+ 		\WP_CLI ::line ( ''  );
33300	}
34301
35302	/** 
0 commit comments