We’ve been looking at how to create a simple CRM system in WordPress. In the last part of this series, we explored how to use Roles to restrict User access to parts of the WordPress Administration interface.
Today we’ll cover how to restrict access to only the Contacts Custom Post Type using Custom Capabilities.
Custom Capabilities
Whilst we previously reduced the level of access our WordPress Users have, by assigning them to an Editor or Author role, Users are still able to manage Posts and Comments. Neither of these functions are useful to us, so we need to restrict access further, allowing assigned Users to only have access to the Contacts Custom Post Type.
We can do this by:
- registering Custom Capabilities on our Custom Post Type
- creating a new WordPress User Role, assigning just our new custom capabilities to that Role
- creating / editing WordPress Users, assigning them to the new Contacts Role
Registering Custom Capabilities on Our Custom Post Type
Registering custom capabilities allows us to later define which WordPress Role(s) have access to these capabilities—for example, whether an Author can create a new Contact.
Let’s edit our plugin file’s register_post_type()
function call, replacing capability_type => 'post'
with the following:
'capabilities' => array( 'edit_others_posts' => 'edit_others_contacts', 'delete_others_posts' => 'delete_others_contacts', 'delete_private_posts' => 'delete_private_contacts', 'edit_private_posts' => 'edit_private_contacts', 'read_private_posts' => 'read_private_contacts', 'edit_published_posts' => 'edit_published_contacts', 'publish_posts' => 'publish_contacts', 'delete_published_posts'=> 'delete_published_contacts', 'edit_posts' => 'edit_contacts' , 'delete_posts' => 'delete_contacts', 'edit_post' => 'edit_contact', 'read_post' => 'read_contact', 'delete_post' => 'delete_contact', ), 'map_meta_cap' => true,
Our register_post_type()
function should now look like:
/** * Registers a Custom Post Type called contact */ function register_custom_post_type() { register_post_type( 'contact', array( 'labels' => array( 'name' => _x( 'Contacts', 'post type general name', 'tuts-crm' ), 'singular_name' => _x( 'Contact', 'post type singular name', 'tuts-crm' ), 'menu_name' => _x( 'Contacts', 'admin menu', 'tuts-crm' ), 'name_admin_bar' => _x( 'Contact', 'add new on admin bar', 'tuts-crm' ), 'add_new' => _x( 'Add New', 'contact', 'tuts-crm' ), 'add_new_item' => __( 'Add New Contact', 'tuts-crm' ), 'new_item' => __( 'New Contact', 'tuts-crm' ), 'edit_item' => __( 'Edit Contact', 'tuts-crm' ), 'view_item' => __( 'View Contact', 'tuts-crm' ), 'all_items' => __( 'All Contacts', 'tuts-crm' ), 'search_items' => __( 'Search Contacts', 'tuts-crm' ), 'parent_item_colon' => __( 'Parent Contacts:', 'tuts-crm' ), 'not_found' => __( 'No contacts found.', 'tuts-crm' ), 'not_found_in_trash' => __( 'No contacts found in Trash.', 'tuts-crm' ), ), // Frontend 'has_archive' => false, 'public' => false, 'publicly_queryable' => false, // Admin 'capabilities' => array( 'edit_others_posts' => 'edit_others_contacts', 'delete_others_posts' => 'delete_others_contacts', 'delete_private_posts' => 'delete_private_contacts', 'edit_private_posts' => 'edit_private_contacts', 'read_private_posts' => 'read_private_contacts', 'edit_published_posts' => 'edit_published_contacts', 'publish_posts' => 'publish_contacts', 'delete_published_posts'=> 'delete_published_contacts', 'edit_posts' => 'edit_contacts' , 'delete_posts' => 'delete_contacts', 'edit_post' => 'edit_contact', 'read_post' => 'read_contact', 'delete_post' => 'delete_contact', ), 'map_meta_cap' => true, 'menu_icon' => 'dashicons-businessman', 'menu_position' => 10, 'query_var' => true, 'show_in_menu' => true, 'show_ui' => true, 'supports' => array( 'title', 'author', 'comments', ), ) ); }
Here, two things happen:
- We’ve defined our own capabilities using the
capabilities
argument, mapping them to their Post equivalents. This ensures WordPress understands precisely what the capabilities mean (i.e.edit_contact
behaves in the same way as theedit_post
capability, except that it’s for our Contacts Custom Post Type). - We’ve told WordPress to map the above capabilities to WordPress’s primitive capabilities, using
map_meta_cap
, so they are enforced.
Reload the WordPress Administration as any User, and you’ll see our Contacts Custom Post Type has disappeared from the WordPress Administration Menu:
This happened because we now need to tell WordPress which Roles have our new Contacts Capabilities (edit_contact
, edit_contacts
, etc).
Creating a New WordPress User Role, Assigning Just Our New Custom Capabilities to It
Using add_role()
, we can create a new WordPress User Role and assign our Contacts Capabilities to it. The role is stored in the WordPress Options data, so we only need to make this function call once.
To do this, add the following function below the end of the __construct()
function in our plugin:
/** * Activation hook to register a new Role and assign it our Contact Capabilities */ function plugin_activation() { // Define our custom capabilities $customCaps = array( 'edit_others_contacts' => true, 'delete_others_contacts' => true, 'delete_private_contacts' => true, 'edit_private_contacts' => true, 'read_private_contacts' => true, 'edit_published_contacts' => true, 'publish_contacts' => true, 'delete_published_contacts' => true, 'edit_contacts' => true, 'delete_contacts' => true, 'edit_contact' => true, 'read_contact' => true, 'delete_contact' => true, 'read' => true, ); // Create our CRM role and assign the custom capabilities to it add_role( 'crm', __( 'CRM', 'tuts-crm'), $customCaps ); }
This function will add a new role called CRM to WordPress. Users assigned to this Role will only have access to the Contacts Capabilities. Therefore, they will only have access to the Contacts Custom Post Type.
Notice we also assign the read
capability to this Role. This is required to allow Users to edit their Profile (Name, Password, etc). We need to allow Users to do this, because when they log in, WordPress will automatically redirect them to the Profile screen.
If we didn’t assign the read
capability, this is what would happen when the User logs in:
To run our plugin_activation()
function once, let’s add the following code to the end of our plugin file:
register_activation_hook( __FILE__, array( &$wpTutsCRM, 'plugin_activation' ) );
This tells WordPress that, on plugin activation, it needs to call the plugin_activation()
function inside our WPTutsCRM
class.
Creating / Editing WordPress Users, Assigning Them to the New Contacts Role
Next, deactivate and re-activate your Plugin, and then navigate to Users > Add New in the WordPress Administration interface.
If everything has worked successfully, you will see the new CRM role appear on the dropdown list:
Let’s go ahead and create a new User called crm, and log in as that new User. We should now see our Contacts, with the Dashboard and Profile as the only other menu options:
Assigning Custom Capabilities to Other Roles
If we log out and log back in as a WordPress Administrator, Editor or Author, you’ll notice that the Contacts Custom Post Type is still missing from the WordPress Administration Menu:
This is because we have only assigned the CRM
Role our Contact Custom Post Type’s Capabilities. Therefore, all other User Roles still do not have access to this Post Type.
To fix this, let’s assign the Custom Capabilities to the Administrator and Editor Roles by adding the following code to the end of the plugin_activation()
function:
// Add custom capabilities to Admin and Editor Roles $roles = array( 'administrator', 'editor' ); foreach ( $roles as $roleName ) { // Get role $role = get_role( $roleName ); // Check role exists if ( is_null( $role) ) { continue; } // Iterate through our custom capabilities, adding them // to this role if they are enabled foreach ( $customCaps as $capability => $enabled ) { if ( $enabled ) { // Add capability $role->add_cap( $capability ); } } }
Here, we iterate through the Roles we want to assign Custom Capabilities to, checking the Role exists. If it does, we then iterate through the Custom Capabilities we defined earlier, adding them to the Role.
You’ll notice we haven’t added any Custom Capabilities to the Author Role; this is because we don’t want to assign all of the Capabilities, as the Author Role traditionally only permits write access to that User’s own Posts.
Let’s continue building our plugin_activation()
function by adding some Capabilities for the Author Role:
// Add some of our custom capabilities to the Author Role $role = get_role( 'author' ); $role->add_cap( 'edit_contact' ); $role->add_cap( 'edit_contacts' ); $role->add_cap( 'publish_contacts' ); $role->add_cap( 'read_contact' ); $role->add_cap( 'delete_contact' ); unset( $role );
Our whole function should now look like:
/** * Activation hook to register a new Role and assign it our Contact Capabilities */ function plugin_activation() { // Define our custom capabilities $customCaps = array( 'edit_others_contacts' => true, 'delete_others_contacts' => true, 'delete_private_contacts' => true, 'edit_private_contacts' => true, 'read_private_contacts' => true, 'edit_published_contacts' => true, 'publish_contacts' => true, 'delete_published_contacts' => true, 'edit_contacts' => true, 'delete_contacts' => true, 'edit_contact' => true, 'read_contact' => true, 'delete_contact' => true, 'read' => true, ); // Create our CRM role and assign the custom capabilities to it add_role( 'crm', __( 'CRM', 'tuts-crm'), $customCaps ); // Add custom capabilities to Admin and Editor Roles $roles = array( 'administrator', 'editor' ); foreach ( $roles as $roleName ) { // Get role $role = get_role( $roleName ); // Check role exists if ( is_null( $role) ) { continue; } // Iterate through our custom capabilities, adding them // to this role if they are enabled foreach ( $customCaps as $capability => $enabled ) { if ( $enabled ) { // Add capability $role->add_cap( $capability ); } } } // Add some of our custom capabilities to the Author Role $role = get_role( 'author' ); $role->add_cap( 'edit_contact' ); $role->add_cap( 'edit_contacts' ); $role->add_cap( 'publish_contacts' ); $role->add_cap( 'read_contact' ); $role->add_cap( 'delete_contact' ); unset( $role ); }
Logging in as an Administrator, Editor or Author will now show the Contacts option in the WordPress Administration Menu:
Cleaning Up Our Roles
If the WordPress Administrator deactivates our plugin, the CRM
Role will remain in WordPress. However, given that no other plugin or part of WordPress uses this Role, it’s redundant—so we need to make sure the CRM
Role is removed when our plugin is deactivated.
To do this, add the following function below the plugin_activation()
function:
/** * Deactivation hook to unregister our existing Contacts Role */ function plugin_deactivation() { remove_role( 'crm' ); }
In the same way we used the register_activation_hook()
function when our plugin activated, we can use the register_deactivation_hook()
when our plugin deactivates. Let’s add the following below the register_activation_hook
call:
register_deactivation_hook( __FILE__, array( &$wpTutsCRM, 'plugin_deactivation' ) );
When our plugin is deactivated, our CRM
Role will no longer be available.
Summary
We’ve successfully created a simple CRM system in WordPress, exploring the use of Custom Post Types, Post Meta Fields and third party plugin integration to store information about our customers and prospects.
This tutorial has also covered some of the more advanced aspects of WordPress, by displaying Advanced Custom Fields data through WP_List_Table Columns, filtering our Post Query to search our Advanced Custom Fields data, and managing User access through Roles and Capabilities to restrict access to just our CRM system.
Comments