Restrict content to registered users

There are a lot of sites out there, particularly digital education sites, such as Net Tuts and Blender Cookie, that push out a lot of “premium” content that is only accessible to registered users.

In this tutorial, I’m going to show you some of the basics of writing a WordPress plugin from scratch that will allow you to restrict content on your site to registered users. You will be able to restrict complete pages / posts by clicking a check box or just sections of content using WordPress shortcodes.

Note: I will not be showing how to create a paid membership system, or how to integrate this plugin into a paid membership system, such as aMember. This plugin will deal with choosing which content is restricted, but not how users register. It will use the default WordPress user roles: Administrator, Editor, Author, Subscriber, and None.

1 – Getting Started – The Plugin Basics

WordPress plugin development is very similar to theme development and can employ many of the same tricks to achieve the end goal.

If you are not at all familiar with WordPress plugin development, I recommend you read Writing a WordPress Plugin from the WordPress codex.

2 – The Plugin Header

The first thing to do, once we’re ready to start writing our plugin, is to create a new file called restrict_content.php and to enter all of the plugin’s necessary meta data, such as name, author, description, url, etc.

1
2
3
4
5
6
7
8
9
10
11
<?php
/*
Plugin Name: Restrict Content
Plugin URI: http://pippinspages.com/freebies/restricted-content-plugin-free/
Description: Restrict Content to registered users only.
Version: 1.0
Author: Pippin Williamson
Author URL: http://pippinspages.com
*/
 
?>

If you upload this file to your wp-content/plugins directory and click Activate from the WordPress plugins menu, the plugin will activate fine, though it won’t actually do anything at this time.

3 – Create the Shortcode

Now it’s time to begin really writing the plugin.

First we are going to create a function that will allow us to use WordPress shortcodes to restrict sections of content like this:

[restrict] . . . this is restricted to logged in users . . . [/restrict]

Paste this below the plugin meta info we entered earlier:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function restrict_shortcode( $atts, $content = null ) {
   extract( shortcode_atts( array(
      'userlevel' => 'none',
      ), $atts ) );
      if ($userlevel == 'admin' && current_user_can('switch_themes'))
      {
      	return do_shortcode($content);
      }
      if ($userlevel == 'editor' && current_user_can('moderate_comments'))
      {
      	return do_shortcode($content);
      }
      if ($userlevel == 'author' && current_user_can('upload_files'))
      {
      	return do_shortcode($content);
      }
      if ($userlevel == 'subscriber' && current_user_can('read'))
      {
	      	return do_shortcode($content);
	  }
	  if ($userlevel == 'none' && is_user_logged_in)
      {
	      	return do_shortcode($content);
	  }
      else return '<span style="color: red;">Some content is only viewable by ' . $userlevel . 's</span>';
}
add_shortcode('restrict', 'restrict_shortcode');

What does this code do?

It first creates a place for us to store options (in the variable $atts), and a place to store the content contained within the shortcode (in the variable $content).

Next, an option is created called userlevel and it is given a default value of none. This option allows us to define which users will be able to see the content inside of the shortcode.

When loading the content, every visitor’s user status must be checked, and only to those whom have sufficient privileges will the content be displayed. This check is done like this:

1
2
3
4
if ($userlevel == 'admin' && current_user_can('switch_themes'))
{
  return do_shortcode($content);
}

This will check whether the user level is set to admin and if the current user has the ability to switch themes. If both of these are true, the content contained in the shortcode will be displayed. If one of the above checks is not true, then this happens:

1
else return '<span style="color: red;">Some content is only viewable by ' . $userlevel . 's</span>';

Which will display the message: Some content is only viewable by admins

These two checks are done for every user level.

Lastly, we need to make the shortcode available for use by doing this:

1
add_shortcode('restrict', 'restrict_shortcode');

Now we are able to use something like this in our posts / pages:

[restrict userlevel="editor"] . . . this is restricted to logged in users . . . [/restrict]

The available options are:

  • admin
  • editor
  • author
  • subscriber
  • none (this is the same as using just [restrict] . . . [/restrict] )

4 – Creating the Meta Box

The next step is to add an option to all posts / pages that will allow us to restrict the entire post / page. This is much better than simply wrapping shortcodes around our entire post / page, even though it would accomplish the same thing.

We will do this by adding a custom meta box to our editor screen. This will be done with multiple functions, all pasted just below the code we already have in our restrict_content.php plugin file.

The first function defines all of the options for our meta box:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//custom meta boxes
$prefix = 'rc';
 
$meta_box = array(
    'id' => 'rcMetaBox',
    'title' => 'Restrict this content',
    'context' => 'normal',
    'priority' => 'high',
    'fields' => array(
        array(
            'name' => 'User Level',
            'id' => $prefix . 'UserLevel',
            'type' => 'select',
            'desc' => 'Choose the user level that can see this page / post',
            'options' => array('None', 'Administrator', 'Editor', 'Author', 'Subscriber'),
            'std' => 'None'
        ),
        array(
        	'name' => 'Hide from Feed?',
        	'id' => $prefix . 'FeedHide',
        	'type' => 'checkbox',
        	'desc' => 'HIde the excerpt of this post / page from the Feed?',
        	'std' => ''
     	)
    )
);

We have now created an option for choosing the user level to which we’d like to restrict the content, and an option to hide the content from being displayed (even as an excerpt) in the feed.

Now let’s actually make the meta box display on the editor screen:

1
2
3
4
5
6
7
// Add meta box
function rcAddMetaBoxes() {
    global $meta_box;
	foreach (array('post','page') as $type)     
    add_meta_box($meta_box['id'], $meta_box['title'], 'rcShowMetaBox', $type, $meta_box['context'], $meta_box['priority']);
}
add_action('admin_menu', 'rcAddMetaBoxes');

Inside of the add_meta_box command, we are calling all of the options we defined above.

The next step is to make a function that controls how the meta options are displayed. This next function is called by the third variable (rcShowMetaBox) in the above function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Callback function to show fields in meta box
function rcShowMetaBox() {
    global $meta_box, $post;
 
    // Use nonce for verification
    echo '<input type="hidden" name="rcMetaNonce" value="', wp_create_nonce(basename(__FILE__)), '" />';
 
    echo '<table class="form-table">';
 
    foreach ($meta_box['fields'] as $field) {
        // get current post meta data
        $meta = get_post_meta($post->ID, $field['id'], true);
 
        echo '<tr>',
                '<th style="width:20%"><label for="', $field['id'], '">', $field['name'], '</label></th>',
                '<td>';
        switch ($field['type']) {
            case 'select':
                echo '<select name="', $field['id'], '" id="', $field['id'], '">';
                foreach ($field['options'] as $option) {
                    echo '<option', $meta == $option ? ' selected="selected"' : '', '>', $option, '</option>';
                }
                echo '</select>';
                break;
            case 'checkbox':
                echo '<input type="checkbox" name="', $field['id'], '" id="', $field['id'], '"', $meta ? ' checked="checked"' : '', ' />';
                break;
        }
        echo     '<td>', $field['desc'], '</td><td>',
            '</tr>';
    }
 
    echo '</table>';
}

This will now provide us with a nice layout for our “restrict content” options. At the top of this function we have included a hidden input for the nonce verification. This makes sure that no one edits these options who does not have permission.

Finally, we need to have a function to save our data we have inputed. Once again, copy this into restrict_content.php beneath all of the code you have already entered.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 // Save data from meta box
function rcSaveData($post_id) {
    global $meta_box;
 
    // verify nonce
    if (!wp_verify_nonce($_POST['rcMetaNonce'], basename(__FILE__))) {
        return $post_id;
    }
 
    // check autosave
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return $post_id;
    }
 
    // check permissions
    if ('page' == $_POST['post_type']) {
        if (!current_user_can('edit_page', $post_id)) {
            return $post_id;
        }
    } elseif (!current_user_can('edit_post', $post_id)) {
        return $post_id;
    }
 
    foreach ($meta_box['fields'] as $field) {
        $old = get_post_meta($post_id, $field['id'], true);
        $new = $_POST[$field['id']];
 
        if ($new && $new != $old) {
            update_post_meta($post_id, $field['id'], $new);
        } elseif ('' == $new && $old) {
            delete_post_meta($post_id, $field['id'], $old);
        }
    }
}
add_action('save_post', 'rcSaveData');

I’m not going to go into detail about what exactly this code does as it is rather detailed, but essentially it first checks to make sure the updates were made by a person with permission to do so, then checks to make sure that we’ve made changes. If both of these checks come out okay, the data is saved to the database.

That’s it for the meta box.

5 – User Check #1

It is now time to create a series of functions that will each check for whether or not there is a restriction level set and then display the appropriate content (or lack thereof).

These functions are the first of two user checks we will perform.

Let’s first take a look at what one of these functions look like, then we will put them all together.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function rcMetaDisplaySubscriber($error = ' ')
{
	$custom_meta = get_post_custom($post->ID);
	$rcUserLevel = $custom_meta['rcUserLevel'][0];
 
	if ($rcUserLevel == 'Administrator' || $rcUserLevel == 'Editor' || $rcUserLevel == 'Author')
	{
		echo 'This content is restricted to ' . $rcUserLevel;
	}
	else
	{
		$error .= "";
		return $error;
	}
}

This function first gets the user level info from the custom meta box we created above and then displays an error message if the user level is set to a value that is above the current user.

Note: the user level of the current user will be determined later.

We need a function like this for each user level, so all together it looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
function rcMetaDisplayEditor($error = ' ')
{
	$custom_meta = get_post_custom($post->ID);
	$rcUserLevel = $custom_meta['rcUserLevel'][0];
 
	if ($rcUserLevel == 'Administrator')
	{
		echo 'This content is restricted to ' . $rcUserLevel;
	}
	else
	{
		$error .= "";
		return $error;
	}
}
function rcMetaDisplayAuthor($error = ' ')
{
 
	$custom_meta = get_post_custom($post->ID);
	$rcUserLevel = $custom_meta['rcUserLevel'][0];
 
	if ($rcUserLevel == 'Administrator' || $rcUserLevel == 'Editor')
	{
		echo 'This content is restricted to ' . $rcUserLevel;
	}
	else
	{
		$error .= "";
		return $error;
	}
}
function rcMetaDisplaySubscriber($error = ' ')
{
	$custom_meta = get_post_custom($post->ID);
	$rcUserLevel = $custom_meta['rcUserLevel'][0];
 
	if ($rcUserLevel == 'Administrator' || $rcUserLevel == 'Editor' || $rcUserLevel == 'Author')
	{
		echo 'This content is restricted to ' . $rcUserLevel;
	}
	else
	{
		$error .= "";
		return $error;
	}
}
function rcMetaDisplayNone($error = ' ')
{
	$custom_meta = get_post_custom($post->ID);
	$rcUserLevel = $custom_meta['rcUserLevel'][0];
 
	if (!current_user_can('read') && $rcUserLevel == 'Administrator' || $rcUserLevel == 'Editor' || $rcUserLevel == 'Author' || $rcUserLevel == 'Subscriber')
	{
		echo 'This content is restricted to ' . $rcUserLevel;
	}
	else
	{
		$error .= "";
		return $error;
	}
}

6 – User Check #2

The second user check is the one that is going to run inside of the WordPress loop. Even though I’ve label it User Check #2, it’s actually the first check that is performed. It is run from within the WordPress loop.

Let’s take a look at it, then we will dissect it. Copy the code below into the bottom of your plugin file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function checkUser()
{
 
	if (current_user_can('read'))
	{		
		if (current_user_can('upload_files'))
		{
			if (current_user_can('moderate_comments'))
			{
				if (current_user_can('switch_themes'))
				{
					//do nothing here for admin
				}
				else
				{
					add_filter('the_content', 'rcMetaDisplayEditor');
				}
			}
			else
			{
				add_filter('the_content', 'rcMetaDisplayAuthor');
			}
		}
		else
		{
			add_filter('the_content', 'rcMetaDisplaySubscriber');
		}
 
	}
	else
	{
		add_filter('the_content', 'rcMetaDisplayNone');
	}
 
}
add_action('loop_start', 'checkUser');

This is a linear capability check that first tests to see if the logged-in user has the minimum permission level, and if they do, it proceeds onto the second permission level check. At each stage the user either passes or fails.

One of the important things to notice with this user check is that all users, except for administrators, end up having a filter applied to the the_content() function. These filters tie directly into the functions we wrote in the above step with User Check #1.

User Check #2 allows us to determine the permission level of the current user and pass them to the correct function in User Check #1, which will filter the content accordingly.

Now that we have performed both of the user checks, we only have two more functions.

7 – Add Filter to Feed Header

This next function will add a small content filter to the top of all the WordPress feeds that will allow us to hide content from the feed if a post/page is restricted.

At the bottom of your file, put:

1
2
3
4
5
function rcCheckFeed()
{
	add_filter('the_content', 'rcIsFeed');
}
add_action('rss_head', 'rcCheckFeed');

8 – Check for “Hide from Feed”

The last thing we need to do to finish up our plugin is to add a small function that will check whether the “Hide from Feed” option is turned on. If it is turned on, we then need to display a message like “This content is restricted to Editors“. If it is not turned on, we do nothing but display the regular content.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function rcIsFeed($error = ' ')
{
	$custom_meta = get_post_custom($post->ID);
	$rcUserLevel = $custom_meta['rcUserLevel'][0];
	$rcFeedHide = $custom_meta['rcFeedHide'][0];
	if (is_feed && $rcFeedHide == 'on')
	{
		echo 'This content is restricted to ' . $rcUserLevel . 's';
	}
	else
	{
		$error .= "";
		return $error;
	}
 
}

That’s it! Your Restricted Content plugin is complete!

9 – Going Further

The first thing to do, of course, is to give your new plugin a thorough test run. Next is to extend it to have further capabilities, such as restricted categories.

The complete version of this plugin is available as a free download from my site.

Enjoy!

Enjoy this post? You should follow me on Twitter!