Skip to content

Add bulk soft delete action for posts#2840

Open
pfefferle wants to merge 15 commits into
trunkfrom
add/bulk-soft-delete-posts
Open

Add bulk soft delete action for posts#2840
pfefferle wants to merge 15 commits into
trunkfrom
add/bulk-soft-delete-posts

Conversation

@pfefferle

Copy link
Copy Markdown
Member

Fixes #2566

Proposed changes:

  • Add bulk action "Soft Delete" to post list tables for all ActivityPub-enabled post types
  • Add row action "Soft Delete" for individual federated posts
  • Unify user and post soft delete confirmation templates into a single shared template
  • Add proper admin notices for success/failure/no selection cases
  • Add removable query args filter for one-time notices

Other information:

  • Have you written new tests for your changes, if applicable?

Testing instructions:

  1. Create a post and publish it
  2. Wait for it to be federated (check the ActivityPub Status column shows "Federated")
  3. Go to Posts > All Posts
  4. Select one or more federated posts using the checkboxes
  5. From the "Bulk actions" dropdown, select "Soft Delete" and click "Apply"
  6. Verify you see the confirmation page listing the selected posts
  7. Click "Delete from Fediverse" to confirm
  8. Verify you see a success notice and the posts' ActivityPub status changes to "Deleted"

Testing row action:

  1. On the Posts list, hover over a federated post
  2. Click "Soft Delete" in the row actions
  3. Confirm the deletion in the browser dialog
  4. Verify success notice appears

Testing non-federated posts:

  1. Select posts that haven't been federated yet
  2. Apply "Soft Delete" bulk action
  3. Verify you see a warning notice that no posts were federated

Changelog entry

  • Automatically create a changelog entry from the details below.
Changelog Entry Details

Significance

  • Patch

Type

  • Added - for new features

Message

Add bulk and row action to soft delete posts from the Fediverse.

- Add bulk action "Soft Delete" to post list tables for all ActivityPub-enabled post types
- Add row action "Soft Delete" for individual federated posts
- Unify user and post soft delete confirmation templates into one
- Add proper admin notices for success/failure/no selection cases
- Add removable query args filter for one-time notices
- Add comprehensive test coverage for Admin bulk delete functionality

Fixes #2566
Copilot AI review requested due to automatic review settings January 29, 2026 16:24
@pfefferle pfefferle self-assigned this Jan 29, 2026
@pfefferle pfefferle requested a review from a team January 29, 2026 16:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds bulk and individual soft delete actions for federated posts, enabling users to remove posts from the Fediverse while keeping them on the WordPress site. The implementation unifies the confirmation template to handle both user and post deletions.

Changes:

  • Added bulk action "Soft Delete" to post list tables for ActivityPub-enabled post types
  • Added row action "Soft Delete" for individual federated posts with confirmation dialog
  • Unified user and post soft delete confirmation into a single reusable template

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
includes/wp-admin/class-admin.php Implements bulk and single post deletion handlers, registers bulk actions for ActivityPub-supported post types, adds admin notices and row actions for federated posts
templates/bulk-delete-confirmation.php New unified template handling both user and post deletion confirmations with appropriate messaging and table layouts
templates/bulk-actor-delete-confirmation.php Removed old user-specific template, replaced by unified template
tests/phpunit/tests/includes/wp-admin/class-test-admin.php Comprehensive test coverage for bulk actions, row actions, permissions, and edge cases
.github/changelog/2840-from-description Changelog entry documenting the new feature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread templates/bulk-delete-confirmation.php Outdated
Comment thread includes/wp-admin/class-admin.php Outdated
Add 13 new tests covering:
- Non-existent post handling
- Mixed federated/non-federated posts
- Deleted and failed post states
- Nonce and confirmation dialog in row actions
- Complete removable query args verification
- Page post type support
- Empty post array handling
- Draft post edge case
- Title attribute verification
…late

- Add 'checked' param (default: true) to control checkbox default state
- Add 'cancel_label' param (default: 'Cancel') for the cancel button text
- Use wp_parse_args for cleaner default handling
- Pass checked=false and cancel_label='Skip' for user deletion flow
  to preserve original UX behavior
After successfully sending a Delete activity, set the post's
activitypub_content_visibility to private. This prevents
accidental re-federation while allowing users to manually
re-federate by changing visibility back to public.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@pfefferle pfefferle marked this pull request as draft March 30, 2026 14:31
pfefferle added 2 commits June 8, 2026 10:55
- Mark soft-deleted posts as local-only (not private) so they stay on
  the site and are not re-federated; private means followers-only.
- Add and verify a nonce on the bulk delete confirmation page.
- Guard the add_to_outbox() return against WP_Error in both handlers.
- Build the redirect URL with add_query_arg() instead of string concat.
- Replace the row action's inline onclick with a data attribute and a
  delegated handler enqueued on edit.php (also drives the select-all
  checkbox), and drop the template's inline script.
- Move HTML markup out of translatable strings and add an aria-label to
  the select-all checkbox.
- Add tests for the single delete handler (local-only state, nonce).
@pfefferle

Copy link
Copy Markdown
Member Author

Addressed review feedback in 5ca11e4:

  1. Inline onclick (row action) — replaced with a class="activitypub-delete-link" + data-activitypub-confirm attribute and a delegated handler enqueued on edit.php (via the core common script). The same handler drives the confirmation page's select-all checkbox, so the template's inline <script> is gone too.
  2. varlet/const — the confirmation template already used const/let in its current form; the inline script that prompted it has now been removed entirely.

Also fixed alongside (from a broader pass):

  • Soft-deleted posts are now marked local-only rather than private (private still federates to followers; local keeps them on-site and prevents re-federation).
  • Added and verified a nonce on the bulk confirmation-page GET.
  • Guarded the add_to_outbox() return against WP_Error.
  • Built the redirect URL with add_query_arg() instead of string concatenation.
  • Moved <strong> markup out of translatable strings; added an aria-label to the select-all checkbox.
  • Added tests for the single-delete handler (local-only state + nonce rejection); suite is 23 → 25.

Still draft. CI re-running on the push.

@pfefferle pfefferle marked this pull request as ready for review June 8, 2026 11:31
@pfefferle pfefferle requested a review from Copilot June 8, 2026 11:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Comment thread includes/wp-admin/class-admin.php Outdated
Comment on lines +1043 to +1047
$query_args = array(
'action' => 'activitypub_confirm_post_removal',
'send_back' => \rawurlencode( $send_back ),
'_wpnonce' => \wp_create_nonce( 'activitypub-confirm-post-removal' ),
);
Comment thread includes/wp-admin/class-admin.php Outdated
Comment on lines +1076 to +1079
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput
$posts = \wp_unslash( $_GET['posts'] ?? array() );
$send_back = \urldecode( \sanitize_text_field( \wp_unslash( $_GET['send_back'] ?? '' ) ) );

Comment on lines +1133 to +1136
if ( empty( $selected_posts ) ) {
\wp_safe_redirect( $send_back );
exit;
}
Comment on lines +1166 to +1172
// Add success count to redirect URL.
$send_back = \add_query_arg( 'activitypub_deleted', $deleted_count, $send_back );

// Redirect back.
\wp_safe_redirect( $send_back );
exit;
}
Comment on lines +987 to +993
public static function register_post_bulk_actions() {
$post_types = \get_post_types_by_support( 'activitypub' );

foreach ( $post_types as $post_type ) {
\add_filter( "bulk_actions-edit-{$post_type}", array( self::class, 'post_bulk_options' ) );
\add_filter( "handle_bulk_actions-edit-{$post_type}", array( self::class, 'handle_post_bulk_request' ), 10, 3 );
}
Large bulk selections previously passed every post or user ID as query
args in the confirmation redirect, which could exceed URL length limits.
Both the post and actor delete flows now store the IDs in a short-lived,
per-user transient and reference them with a one-time token. The actor
confirmation page now also verifies a nonce, matching the post flow.

Add tests for the bulk request token redirect and the bulk confirmation
handler (Delete sent, post marked local-only, invalid nonce rejected).
…ct-all

- Drop the blanket edit_posts gate from the bulk delete handlers; the
  per-post edit_post checks (template render loop and submit loop) are
  the authoritative, capability-type-aware gate, so editors of custom
  post types are no longer blocked from bulk soft delete while the
  single-row action works.
- Enqueue the select-all/confirm handler on users.php as well as
  edit.php, so the select-all checkbox works on the user confirmation
  screen (rendered from users.php), not just the post screen.
- Add tests for a custom-capability editor bulk delete and for the
  select-all handler being enqueued on the user list page.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Manual option to send Delete activity to Fediverse

3 participants