How to prevent a post from being deleted?

For auditing issue, I only want a post can be trashed, but not deleted – for all users.

So I have a plugin like

Read More
add_action('before_delete_post', function($id) {
        wp_die(0);
    });

But seems not all delete action are ajax, so it will be show a black screen with return an error page with result “0”

Feature wise the above code is ok, but are there any better way?

Related posts

Leave a Reply

3 comments

  1. Don’t let the action die, just do a redirect (to wherever you’d like):

    function wpse_92155_before_delete_post() {
        wp_redirect(admin_url('edit.php'));
        exit();
    } // function wpse_92155_before_delete_post
    add_action('before_delete_post', 'wpse_92155_before_delete_post', 1);
    
  2. I would use the hook before_delete_post as the last layer of protection against deletion (using @tf’s solution, and which is the correct Answer).

    And first remove the “Delete” options from sight. The following hides the Bulk Actions and the Empty Trash button, and removes the Delete Permanently row action.

    before

    With this code:

    add_action( 'admin_head-edit.php', 'hide_delete_css_wpse_92155' );
    add_filter( 'post_row_actions', 'hide_row_action_wpse_92155', 10, 2 );
    add_filter( 'page_row_actions', 'hide_row_action_wpse_92155', 10, 2 );
    
    function hide_delete_css_wpse_92155()
    {
        if( isset( $_REQUEST['post_status'] ) && 'trash' == $_REQUEST['post_status'] ) 
        {
            echo "<style>
                .alignleft.actions:first-child, #delete_all {
                    display: none;
                }
                </style>";
        }
    }
    
    function hide_row_action_wpse_92155( $actions, $post ) 
    {
        if( isset( $_REQUEST['post_status'] ) && 'trash' == $_REQUEST['post_status'] ) 
            unset( $actions['delete'] );
            
        return $actions; 
    }
    

    This is the result:

    after

    Note of interest

    There is no hook cpt_row_actions. The hooks page_row_actions and post_row_actions are applied if the post type is hierarchical or not, respectively.

  3. This is an old post, but for others still seeking an answer, I think there’s a more elegant way – filtering user_has_cap. This allows you to remove the users rights to delete a post under whatever circumstances you determine. Filtering user_has_cap automatically removes the UI elements and the action in one simple function and works no matter how the attempt to delete is made. See this post for more details. 

    add_filter ('user_has_cap', 'wpcs_prevent_deletion', 10, 3);
     
    function wpcs_prevent_deletion ($allcaps, $caps, $args) {
        if (isset($args[0]) && isset($args[2]) && $args[0] == 'delete_post') {
            $post = get_post ($args[2]);
            // Insert your logic here
            if ($prevent_deletion) {
                $allcaps[$caps[0]] = false;
            }
        }
        return $allcaps;
    }