Prevent from deleting any user role but subscriber

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.

Leave a Comment