WordPress Roles and Capabilities: A Real Life Example

This is a four-part series tutorial covering the WordPress users, roles and capabilities topic. The series will cover the architecture and design of user roles in WordPress; highlight the most important functions for interacting with users and managing roles and capabilities; and in the last tutorial, we are going to build a real-life example that demonstrates the usefulness of this API.


Introduction

This tutorial will be focused on building a practical solution using the WordPress roles and capabilities system. If you have missed the last two tutorials, I'd highly suggest that you check them out. The first part "WordPress Roles and Capabilities: The Basics" explains the design of this system; while the second part "WordPress Roles and Capabilities: Functions of Note" focuses on the functions and classes WordPress offers to interact with the system.

The solution proposed in this tutorial is the same I'm using for one of my premium WordPress plugins. I have chosen it after trying different approaches. It's simple, short and encapsulated in one class. You can easily adapt it for your own plugin. The code is available on GitHub; and is not licensed. It also comes with no warranties, and you are free to use and license it as you wish.


Step 1 The Scenario

We are building a WordPress plugin which has a special client admin panel. This admin panel should be accessible only to a limited set of users. These users can be selected by the blog administrator. He can enable different roles, users or all of them to access the client admin panel or features.

Apart from that, we need to have a restricted Media Library access for users with an access to the Client Panel. WordPress has a special capability to access and upload files to the Media Library: "upload_files". However, this gives the user (or role) full access to the Media Library. This is not a good thing, especially that photos (or files) can't be hierarchized under different categories where you can restrict the access for each one.

We need to restrict the Media Library access only to the files the user has uploaded. He shouldn't have access to other users' uploads. This restriction should be applied only to the users who don't have the "upload_files" capability. Other users and roles aren't concerned about this restriction since they already have a full access to the Media Library.

Our blog will have these four categories of users:

The first two sets of users are the ones who will not have access to the plugin Client Panel. I have highlighted the fact that there are users who have access to the Media Library, and a set which doesn't. It's essential that our solution doesn't affect the first two categories and leave their capabilities intact.

With that in mind, our class should do two things:

  • Set the right permissions to the third and fourth set of users
  • Enable a restricted Media Library access to the fourth category. It should make sure that after disabling the plugin or changing the users' rights, that this category of users gets back their original permissions.

Step 2 The Plugin Interface

Before creating our class, let's have a deeper idea about the plugin. The plugin has a pretty simple structure. It's composed of two different menus: One for the administrator and it serves as an administrative panel, accessible only for users with a capability of "manage_options". The second menu is for clients and gives access to the Client Panel. This panel requires a "wptuts_client_page" capability.

This capability doesn't exist in WordPress; we need to add and assign it to the specified users or roles. But before that, let's take a look into the plugin.

We have two "admin_menu" action hooks which add the Admin menu for both the administrator and client. We can shorten it to only one hook which adds both of them; but I prefer to separate the two. Each "add_menu_page" function hooks to another function which will display the page content.

Next, we need to initialize our Roles class. The class code is placed in another file; and the initialization process is done during the "init" hook which ensures that WordPress has finished loading.

The constructor function accepts three parameters:

  • $all Boolean (Optional) If you want to give the client access to all users on the blog, you can set this to true and ignore the remaining parameters.
  • $roles Array (Optional) An array with roles' ids which will have access to the client panel.
  • $users Array (Optional) An array with users' usernames which will have access to the client panel.

Step 3 The Roles Class

The Properties

The class has only 3 properties: $all, $roles and $users. These are the same variables that you pass to the constructor function. The value of the variables is not changed in our example; but in a real practical case, you may want to merge with another source. For this, you have the set_entities function. You can accommodate it to your own needs.

The Constructor Function

In a first step, the constructor function initializes the $all, $roles and $users variables using the set_entitites() function. Next, it calls a private function for setting the capabilities, and another one for the Media Library restriction. These are exactly the steps we defined in our scenario.

Static Functions

Static functions don't require class initialization. They are similar to independent functions, but simply linked to the specified class. I have decided to keep some functions static because they can be used independently; and you might find them useful in another context.

These functions are filter_roles() and filter_users(); which are associated with two other functions role_has_caps() and user_has_caps(). The functions play the part of a filter. They filter roles (or users) based on capabilities they have and don't have.

Both functions accept two arguments:

  • $include Array An array of capabilities that the role has.
  • $exclude Array An array of capabilities that the role doesn't have.

The functions loop through all of the roles and users in the database. For each role (or user), it checks if it has the required capabilities, and doesn't have the capabilities to exclude. This check is done with the role_has_caps() and user_has_caps() functions.

These two functions (role_has_caps() and user_has_caps()) accept two arguments:

  • $role (or $user) String The role ID or the user ID.
  • $caps Array An array of capabilities to check against.

If the role (or user) has the specified capabilities in the $caps array, the function returns true. In the other case, the function returns false. The function basically loops through each capability and checks that the role (or user) has the specified capability.

Adding Permissions

This is the first step on imposing the law of our plugin. I have distributed the functionality over 3 functions: One for setting permissions to all users, one for setting permissions to the roles, and another for setting permissions to the specified users. The main function simply decides which functions to call.

The set_all_permissions() function loops through all users in the blog, and add (or remove) the "wptuts_client" capability depending on the value of the $all variable. We get the full list of users using the get_users() function; and initialize a new WP_User object to get access to the add_cap() and remove_cap() functions.

The set_roles_permissions() function loops through all the roles in the blog and removes the "wptuts_client" capability. After that, it loops through roles in the $roles array and adds the "wptuts_client" capability. The first step was to ensure that we clean the capability from roles which may have previously had it.

The set_users_permissions() function does the same thing as the last function. The only difference is that it targets users instead of roles.

Media Library Filter

Now we have set the right permissions for the right entities. (Being either a user or a role) We also have to restrict the Media Library access for the fourth category that we distinguished in the scenario.

This category of roles (or users) has the "wptuts_client" capability, but doesn't have the "upload_files" capability. And that's where our filter functions come into play. They'll help us filter and return this category of roles (or users).

For this category, we'll add two capabilities "upload_files" and "remove_upload_files". The "upload_files" will give full access to the Media Library; and the other capability will be used to filter the Media Library posts, and it'll also be used to remove the "upload_files" capability once the "wptuts_client" capability is also removed.

After setting the capabilities to this category, we hook to the "parse_query" filter. This filter allows us to change the posts returned by WP_Query. In our case, we are going to set the "author" query variable. This results in returning only posts created by the specified author.

The Full Code


Conclusion

In this tutorial, I tried to use the material we learned from the previous two posts to create a custom solution for roles and capabilities. The solution was encapsulated in one class which can be customized for your own needs or plugins. You can find the code on Github.

If you have any questions, suggestions or improvements feel free to post it in the comments.

Tags:

Comments

Related Articles