How to Make your WordPress site for Members Only
Updated 12/2014
I was putting together a site in WordPress and needed to make it so only logged in users could view posts. In addition, only users logged in could download/view images and documents uploaded through the media library (wp-content/uploads/).
Redirect visitors to login page
First part is easy. Just add this to functions.php:
// Redirect users who are not logged in...
function login_redirect() {
// Current Page
global $pagenow;
// Check if user is not logged in and not on the wp-login page
if(!is_user_logged_in() && $pagenow != 'wp-login.php' )
auth_redirect();
}
add_action( 'wp', 'login_redirect' );
Visitors are now directed to the default WordPress login page. The default login page is bleak and not attractive to someone happening onto the site. You could add a logo to it using a plugin or you could use the Theme My Login plugin and make it match the site. The Theme My Login plugin also takes care of redirecting users to a page outside the normal WordPress admin. One issue that arises is that we now have 4-5 pages (login, logout, forgot password, register, etc…) created by the Theme My Login plugin that need be visible without logging in. We need to go back to our login_redirect() function and whitelist those pages from auth_redirect().
// Redirect users who are not logged in...
function login_redirect() {
// Current page and post obj
global $pagenow, $post;
// Theme My Login pages
$post_ids = array(111, 222, 333, 444);
// Check if user is not logged in and not on the wp-login page
if(!is_user_logged_in() && $pagenow != 'wp-login.php' && !in_array($post->ID,$post_ids) )
auth_redirect();
}
add_action( 'wp', 'login_redirect' );
Now we have an array of post IDs that are whitelisted from auth_redirect().
Select Page/Post visibility with custom meta field
Of course keeping track of post IDs via manual code updates can be tedious. A better way is to just add a custom meta field that allow you select if the page requires a login or not. Advanced Custom Fields is a fantastic plugin that we can use to create that custom meta field.
Create a new field group and call it something like Page Visibility. Fill in the other settings something like:
- Field Label: This page is visible to
- Field Name: visibility
- Field Type: Select
- Required?: Yes
- Choices:
- logged_in : Logged in Users
- public : Anyone (Public)
- Default Value: logged_in
- Location
- Post Type equal to Page
- Options
- Position: Side
New lets go back to the login_redirect function and make some changes. We run a query to find all posts that are set to public. Then we loop through then and add the post IDs to the array of post IDs we want to whitelist.
// Redirect users who are not logged in...
function login_redirect() {
// Current page and post obj
global $pagenow, $post;
// get public pages
$args = array(
'post_type' => 'page',
'meta_query' => array(
array(
'key' => 'visibility',
'value' => 'public',
'compare' => '=',
),
),
'posts_per_page' => '-1'
);
$posts = get_posts($args);
$post_ids = array();
foreach ($posts as $_post) {
$post_ids[] += $_post->ID;
}
// Check if user is not logged in and not on the wp-login page
if(!is_user_logged_in() && $pagenow != 'wp-login.php' && !in_array($post->ID,$post_ids) )
auth_redirect();
}
add_action( 'wp', 'login_redirect' );
Restrict media library uploads to logged in users
I found a great piece of code on StackExchange by Hakre: http://wordpress.stackexchange.com/a/37743
This code uses .htaccess to redirect all files in wp-content/uploads/ through a php file that verifies they are logged in before serving the file request.
One thing I ran into is that if you have any images/files on those pages that are whitelisted (or in the header/footer) they will also be blocked also. To get around that I added a small whitelist array of images to the top of dl-file.php.
require_once('wp-load.php');
global $pagenow;
// whitelist images
$whitelist = array('image01.jpg', 'image02.png');
$logo = get_option('options_logo'); $logo = wp_get_attachment_image_src($logo, 'full'); $whitelist[] = basename($logo[0]);
is_user_logged_in() || in_array(basename($_SERVER['REQUEST_URI']), $whitelist) || auth_redirect();
list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);
Note: If you are using Nginx you will need to create a custom vhost file that tells Nginx to pass through whichever files you don’t want it to cache.
Tags: authentication, php, uploads, wordpress
Categorized in: Code
Posted on February 16, 2014
Comments are closed here.