From 317e9763ed2a241c51d1625f3b41b384fe9a5055 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sat, 28 Feb 2026 21:10:54 +0000 Subject: [PATCH 1/2] Fix: allow `wp post delete` to trash custom post types Use wp_trash_post() directly instead of wp_delete_post() for the trash path, because wp_delete_post() hardcodes auto-trash behavior to only 'post' and 'page' types (WordPress core #43672), permanently deleting all other post types even when $force_delete is false. wp_trash_post() works correctly for all post types, so we call it directly when --force is not set and EMPTY_TRASH_DAYS is enabled. Previously, `wp post delete` on a custom post type would error: 'Posts of type X do not support being sent to trash.' Now it trashes the post like it does for 'post' and 'page' types. Closes #128. --- features/post.feature | 20 +++++++++++++++----- src/Post_Command.php | 32 ++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/features/post.feature b/features/post.feature index bb88873e8..09e6ef449 100644 --- a/features/post.feature +++ b/features/post.feature @@ -41,19 +41,29 @@ Feature: Manage WordPress posts Success: Deleted post {POST_ID}. """ - When I try `wp post delete {CUSTOM_POST_ID}` - Then STDERR should be: + When I run `wp post delete {CUSTOM_POST_ID}` + Then STDOUT should be: """ - Warning: Posts of type 'test' do not support being sent to trash. - Please use the --force flag to skip trash and delete them permanently. + Success: Trashed post {CUSTOM_POST_ID}. """ - When I run `wp post delete {CUSTOM_POST_ID} --force` + When I run the previous command again Then STDOUT should be: """ Success: Deleted post {CUSTOM_POST_ID}. """ + Scenario: Force-deleting a custom post type post skips trash + When I run `wp post create --post_title='Test CPT post' --post_type='book' --porcelain` + Then STDOUT should be a number + And save STDOUT as {BOOK_POST_ID} + + When I run `wp post delete {BOOK_POST_ID} --force` + Then STDOUT should be: + """ + Success: Deleted post {BOOK_POST_ID}. + """ + When I try the previous command again Then the return code should be 1 diff --git a/src/Post_Command.php b/src/Post_Command.php index 3ef1f996d..ecdbc0b15 100644 --- a/src/Post_Command.php +++ b/src/Post_Command.php @@ -506,23 +506,31 @@ protected function delete_callback( $post_id, $assoc_args ) { $status = get_post_status( $post_id ); $post_type = get_post_type( $post_id ); - if ( ! $assoc_args['force'] - && 'trash' !== $status - && ( 'post' !== $post_type && 'page' !== $post_type ) ) { - return [ - 'error', - "Posts of type '{$post_type}' do not support being sent to trash.\n" - . 'Please use the --force flag to skip trash and delete them permanently.', - ]; + if ( $assoc_args['force'] || 'trash' === $status || 'revision' === $post_type ) { + if ( ! wp_delete_post( $post_id, true ) ) { + return [ 'error', "Failed deleting post {$post_id}." ]; + } + + return [ 'success', "Deleted post {$post_id}." ]; } - if ( ! wp_delete_post( $post_id, $assoc_args['force'] ) ) { - return [ 'error', "Failed deleting post {$post_id}." ]; + // Use wp_trash_post() directly because wp_delete_post() only auto-trashes + // 'post' and 'page' types, permanently deleting all other post types even + // when $force_delete is false. wp_trash_post() works for all post types. + if ( EMPTY_TRASH_DAYS && wp_trash_post( $post_id ) ) { + return [ 'success', "Trashed post {$post_id}." ]; } - $action = $assoc_args['force'] || 'trash' === $status || 'revision' === $post_type ? 'Deleted' : 'Trashed'; + // Trash is disabled via EMPTY_TRASH_DAYS, or wp_trash_post() failed. + if ( ! EMPTY_TRASH_DAYS ) { + if ( ! wp_delete_post( $post_id, true ) ) { + return [ 'error', "Failed deleting post {$post_id}." ]; + } + + return [ 'success', "Deleted post {$post_id}." ]; + } - return [ 'success', "{$action} post {$post_id}." ]; + return [ 'error', "Failed trashing post {$post_id}." ]; } /** From 2de55c764993d72e0e7b72db2c95d95e55e553fc Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sat, 28 Feb 2026 21:28:43 +0000 Subject: [PATCH 2/2] Simplify delete_callback per code review Combine the EMPTY_TRASH_DAYS check with the force-delete path to remove duplicated wp_delete_post() call. --- src/Post_Command.php | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Post_Command.php b/src/Post_Command.php index ecdbc0b15..3d85dcc37 100644 --- a/src/Post_Command.php +++ b/src/Post_Command.php @@ -506,7 +506,9 @@ protected function delete_callback( $post_id, $assoc_args ) { $status = get_post_status( $post_id ); $post_type = get_post_type( $post_id ); - if ( $assoc_args['force'] || 'trash' === $status || 'revision' === $post_type ) { + $force_delete = $assoc_args['force'] || 'trash' === $status || 'revision' === $post_type; + + if ( $force_delete || ! EMPTY_TRASH_DAYS ) { if ( ! wp_delete_post( $post_id, true ) ) { return [ 'error', "Failed deleting post {$post_id}." ]; } @@ -517,19 +519,10 @@ protected function delete_callback( $post_id, $assoc_args ) { // Use wp_trash_post() directly because wp_delete_post() only auto-trashes // 'post' and 'page' types, permanently deleting all other post types even // when $force_delete is false. wp_trash_post() works for all post types. - if ( EMPTY_TRASH_DAYS && wp_trash_post( $post_id ) ) { + if ( wp_trash_post( $post_id ) ) { return [ 'success', "Trashed post {$post_id}." ]; } - // Trash is disabled via EMPTY_TRASH_DAYS, or wp_trash_post() failed. - if ( ! EMPTY_TRASH_DAYS ) { - if ( ! wp_delete_post( $post_id, true ) ) { - return [ 'error', "Failed deleting post {$post_id}." ]; - } - - return [ 'success', "Deleted post {$post_id}." ]; - } - return [ 'error', "Failed trashing post {$post_id}." ]; }