There are several WordPress plugins that will convert your theme to a fully capable membership system. These let you track your website users, their capabilities on the site, and their access to specific features such as downloads, links, and certain pages. Plugins like S2Member or aMember are extremely powerful – but they aren’t without their drawbacks. Those plugins must be as general as possible to cover a broad spectrum of needs from the various types of users that employ them. For that same reason, much of the plugins’ functionality never gets used.
As part of a recent project, we needed membership capabilities with a simplified approach to work with our custom theme. One of the reasons we didn’t go with an existing plugin was a lack of an approval system. We needed a system that allowed board members to vote on prospective member applications before they gain access to resources and privileges.
Goals and Assumptions
I want to show how easy it is to create a members-only system—a way to make exclusive content available for your registered users—without having to resort to bulky membership plugins. This is not meant as a replacement to those plugins. This tutorial assumes that you are building a more robust system for your own purposes and as such will not simply drop the code in place.
Prerequisites
For this tutorial you should have a basic working knowledge of PHP and WordPress development, including action and filter hooks, a set of predefined roles and capabilities, as well as custom field toggles.
Note: This is not a turnkey solution for a full membership site. Check out Part 2 and Part 3 in this series on how to create user registration and a paywall to convert your members to a premium or subscription model.
The Permissions
First things first: if you’re not using a plugin for role management (like User Role Editor) then you’ll need to architect the roles and capabilities of your site. The first thing to do is determine what capabilities, native and custom, that your members will need.
function rolescheck() { $default_caps = array( 'read' => false, 'upload_files' => false, 'manage_options' => false, 'manage_members' => false, 'is_premium_member' => false, 'is_approved_member' => false, ); if ( current_user_can('administrator') && $admin = get_role('administrator') ) { // upgrade admin user for theme if (! current_user_can('manage_options') ) $admin->add_cap('manage_options'); if (! current_user_can('manage_members') ) $admin->add_cap('manage_members'); if (! current_user_can('is_approved_member') ) $admin->add_cap('is_approved_member'); if (! current_user_can('is_premium_member') ) $admin->add_cap('is_premium_member'); } if (! get_role( 'pending' ) ) { add_role('pending', 'Pending Approval', $default_caps); } $default_caps['read'] = true; $default_caps['is_approved_member'] = true; $default_caps['upload_files'] = true; if (! get_role( 'standard' ) ) { add_role('standard', 'Standard', $default_caps); } $default_caps['is_premium_member'] = true; if (! get_role( 'premium' ) ) { add_role('premium', 'Premium', $default_caps); } } add_action('init', 'rolescheck');
The above function and hook will create three user roles: standard, premium, and pending. You can make as many or as few roles as you like, but for this example, there are essentially two real memberships, the standard and premium, and a third membership, pending. We use the pending role to pool all new user registrations, which might require approval before we give users access to standard or premium features.
You may opt to exclude the pending role so that a user does not need to be approved; however, this is also handy if you need to suspend a user account, but do not want to delete the user from your site. The function is built so that the roles are only created if they do not currently exist for your site. The roles should now appear in the dropdown on the Users page in the WordPress admin area.
The Access
Before we get down and dirty with members-only features, let’s prevent pending users from logging in.
/** * Authenticates by username or email and only allow members to login * * @param user * @param username * @param password * @return bool */ function auth_email_username( $user, $username, $password ) { $field = is_email($username) ? 'email' : 'login'; $user = get_user_by($field, $username); if ( $user && !is_wp_error($user) ) $username = $user->user_login; if ( user_can($user, 'administrator') || user_can($user, 'is_approved_member')) { return wp_authenticate_username_password( null, $username, $password ); } return new WP_Error('login', __('You do not have permission to login at this time.')); } add_filter( 'authenticate', 'auth_email_username', 20, 3 );
The above function and filter allow for authentication by email or login, which mirrors most login services’ authentication flows. We then authenticate the user if they have administrator or is_approved_member capabilities.
The Functionality
Our objectives should now be to check if a resource is members-only, and to permit or deny access to that resource based on the user’s membership level. For this, we will create three functions: one to check the resource, one to return the resource, and one to test membership against the user. My first function will ensure the proper post types are being scrutinized and that the current user can access those pages.
/** * Check if post is members-only content and redirects if necessary. * * @return bool */ function is_members_only() { global $post; $post_check = array( 'offer', 'news', 'attachment', 'event', 'job', 'page' ); $members_only = get_post_meta($post->ID, '_members_only', true); if ( !in_array(get_post_type(), $post_check) ) return; if ( !is_member() && !headers_sent() && (is_single() || is_page()) ) { // not an approved or logged in user if ( $members_only && 'get_header' == current_filter()) { wp_redirect( site_url() ); exit; } else return $members_only; } else { return $members_only; } } add_action('get_header', 'is_members_only');
In the above function, we reference a custom post meta where the key, _members_only, contains either a 1 or 0 boolean value. I use custom field toggle to create the members-only toggle on each post type or page that can be protected. Now all your editor must do is declare a certain page to be members-only by using the toggle.
The above code will run just before the header is output using the get_header hook so that we can redirect to a default page (e.g. the home page) in the event the user does not have access to the target content. The function is built like a template tag so that you can use it anywhere to determine if the current page is_members_only()
.
Before we move to returning the resource, we have created a template tag to determine if the user has a particular capability.
/** * Check if user is registered member * * @return bool */ function is_member(){ return current_user_can('is_approved_member'); }
Now when we use the template tag is_member()
, we can ensure that the currently logged in user has the capability is_approved_member, but you can make the capability whatever you want so long as only a certain subset of your users, or members, has this permission. This could be modified to encompass one or more permissions, but the general part of this tag is to be used as a conditional to hide or show members-only content within a template. Since we have another level, you might try creating another template tag for the premium membership. Try making is_premium_member()
.
The Filters
Finally we wish to return a resource, which in this case is a link to a page or post or attachment. To do this, all we’re going to do is filter links.
/** * Filter the permalink based on the post type. * * @return string */ function the_download_link($url, $post = null) { if ( is_null($post)) { global $post; } $members_only = is_object($post) ? $post->_members_only : get_post_meta($post, "_members_only", true); if ($members_only && !is_member()) return "#login"; $post_type = get_post_type( $post ); switch( $post_type ) { default: return $url; } } add_filter('post_type_link', 'the_download_link', 10, 2); add_filter('the_permalink', 'the_download_link');
In the above, we add two filters, which call the same function the_download_link()
, which takes two arguments: the url to be filtered, and the post object/ID. In some cases the filter does not populate the post object, so we initially run a check to determine if the post object is null or not. Also, one or more of the filters used populates the $post
argument with an ID instead of the post object, so we can assume that if the $post is not an object that it is a numeric ID.
We then ensure the queried resource is members-only or not by checking the suitable post meta field. If the user is not a member, and we already know the post is members-only, we return a hash. In this case, the hash #login will be a handler for our front-end javascript that triggers a login/registration modal or a refresh to the login/registration page. You can further refine the target post type and the url returned based on the post type.
/** * Filter nav menu items that are members_only. * * @return array */ function nav_item_filter($items){ foreach($items as $key => $post) $items[$key]->url = the_download_link($post->url, $post->object_id); return $items; } add_filter('wp_get_nav_menu_items', 'nav_item_filter');
In order for menus to receive the same filter, you must iterate through each of the nav item objects and simply pass it the same function as the link filters. You can repeat this process any time something on the site displays a link that isn’t reachable by one of your existing filters.
Using the above techniques, you can really refine the way users register and become members of your website or organization, depending on their permission, and depending on restrictions you put in the theme. Another way to enhance this method is to ensure that all links get the same treatment, as there are a number of ways that links might be accessed. The two included here cover most cases such as menus generated using wp_nav and permalinks retrieved with get_permalink()
or the_permalink()
. You might have to include a few more filters to cover your bases. Ones not covered here might include the get_term_link
, archive links, and category/taxonomy-type filters.
Conclusion
This solution may not work for everyone and all cases, but it is nice to have a simple way to create members-only content without having to deal with a ton of configuration. You can see a full class implementation on GitHub.
If you found this post useful or want to learn more about creating a membership site, be sure to check out Part 2 of this series on User Registration.
Illustrations by Cindy Leong
Pingback: cash loans
Pingback: direct payday loan no direct deposit lender
Pingback: drugrehabcentershotline.com alcohol treatment