Prevent from deleting any user role but subscriber

In the administration of my blog, I would like to prevent anyone (admin included) from deleting any user unless it is a simple subscriber.

Basicaly, in the “users” section of the administration, I would like the option “delete” to only be visible under the subscribers, not under the administrators, editors, authors or contributors.

Read More

How can I do that in my functions.php file (I’m not interested in a plugin)?

I tried the “remove_cap (‘delete_users’)” function but it has a too large scope as it prevents from deleting any user while I would like to prevent deleting any user BUT the subscribers.

Thank you for your help.

Related posts

3 comments

  1. If you mean the delete button on the user list at /wp-admin/users.php, then that button is created by the WP_Users_List_Table class around (currently) line 256. If you look a little further down– a few lines– you will see a filter called user_row_actions. You can use that to hide the ‘delete’ link.

    add_filter(
      'user_row_actions',
      function($actions, $user_object) {
        if (1 >= count($user_object->roles) && 'subscriber' !== $user_object->roles[0]) {
          unset($actions['delete']);
        }
        return $actions;
      },
      1,2
    );
    

    Minimally tested, but I think that logic is correct. You should see the ‘delete’ link for subscribers but not for users with any other role, including users with “subscriber” and some other role just in case you have that odd setup.

    If you look carefully, and have a mischievous mind, you will notice that you can still delete any user you want by manipulating the user ID in the URL. Look carefully:

    /wp-admin/users.php?action=delete&user=9&_wpnonce=8059e669c1
    

    The filter above just hides the link. It does not prevent access to the delete screen. To do that, we’ll need more code:

    add_action(
      'load-users.php',
      function() {
        if (isset($_GET['action']) && 'delete' === $_GET['action']) {
          if (isset($_GET['user'])) {
            $user_object = get_userdata($_GET['user']);
            if (1 >= count($user_object->roles) && 'subscriber' !== $user_object->roles[0]) {
              wp_die('This user cannot be deleted');
            }
          }
        }
      }
    );
    

    Even with that in place, a clever user might still be able to push the right POST values through and delete a user anyway, so you might also want a final fail-safe right before user deletion.

    add_action(
      'delete_user', 
      function($id) {
        $user_object = get_userdata($id);
        if (1 >= count($user_object->roles) && 'subscriber' !== $user_object->roles[0]) {
          wp_die('This user cannot be deleted');
        }
      }
    );
    

    Note: this code was tested in a MU-Plugin file. I think is should probably work from functions.php but be sure to verify that. Also, in practice, I’d probably try to extract some of that logic so that it is reusable. Those callbacks are dangerously close to repetitive.

  2. Ok s_ha_dum, I got it to work with some changes:

    the “count” seems to ruin the function but like this, it worked like a charm:

    function test($actions, $user_object) 
    {
        if ( 'subscriber' !== $user_object->roles[0]) {unset($actions['delete']);}
        return $actions;
    }
    add_filter('user_row_actions','test',1,2);
    

    Thank you for you great help!

  3. Yes we can achieve it by following .

    add_filter( 'user_row_actions', 'custom_action_function', 1, 2 );
    function custom_action_function($actions, $user_object) {
        if ( 1 >= count( $user_object->roles ) && 'subscriber' !== $user_object->roles[0] ) {
          unset( $actions['delete'] );
        }
        return $actions;
    }
    

Comments are closed.