Monday, June 23, 2014

How to Restrict Content To Registered Users [WP Plugin Tutorial]

In recent times, most online news and information publication websites have adopted the freemium model whereby readers who aren’t a registered member are limited to certain number of articles they can read; paying, registered users on the other hand, have unlimited access to articles.

In this article, we will be showing you how to build a simple plugin that gives the administrator of a WordPress-powered site the ability to restrict certain posts, pages and part of a post content to registered users only.

Coding the Plugin

When writing a WordPress plugin, the header (a PHP comment block) section contains information such as name, description, author and author URL of the plugin.
Here is the plugin header:

<?php
  /*
  	Plugin Name: Restrict Content to registered users
  	Plugin URI: http://hongkiat.com
  	Description: Restricting content to registered users only
  	Version: 0.1
  	Author: Agbonghama Collins
  	Author URI: http://tech4sky.com
  */
  

The plugin will have a settings page consisting of a form field which will contain the post or page IDs to be restricted.

The code below will add a sub-menu to the Settings titled Restrict content To Registered User.

add_action('admin_menu', 'rcru_plugin_menu');
  // Adding Submenu to settings
  function rcru_plugin_menu() {
  	add_options_page(
  	'Restrict content To Registered User',
  	'Restrict content To Registered User',
  	'manage_options',
  	'rcru-restrict-content-user',
  	'rcru_content_user_settings'
  	);
  }

The fifth argument rcru_content_user_settings passed to add_options_page above is the function that will output the content for the plugin settings.


  function rcru_content_user_settings() {
  	echo '<div class="wrap">';
  	screen_icon();
  	echo '<h2>Restrict content To Registered User</h2>';
  	echo '<form action="options.php" method="post">';
  	do_settings_sections('rcru-restrict-content-user');
  	settings_fields('rcru_settings_group');
  	submit_button();
  }
  

The form lacks the <input> field and it isn’t capable just yet to save data to the database because we have yet to implement the WordPress settings API.

The function plugin_option defines the settings section and field.


  // plugin field and sections
  function plugin_option() {
  	add_settings_section(
  	'rcru_settings_section',
  	'Plugin Options',
  	null,
  	'rcru-restrict-content-user
  	');
  
  	add_settings_field(
  	'post-page-id',
  	'<label for="post-page-id">Post and page ID to be restricted.</label>',
  	'post_page_field',
  	'rcru-restrict-content-user',
  	'rcru_settings_section'
  	);
  
  	// register settings
  	register_setting('rcru_settings_group', 'rcru_post-id-option');
  

Mind you, the third argument post_page_field passed to the add_settings_field function above is called to echo the form <input> field.

function post_page_field() {
  	echo "Enter Post or Page ID separated by comma. 
"; echo '<input style="width: 300px; height:80px" type="text" id="post-page-id" name="rcru_post-id-option" value="' . get_option('rcru_post-id-option') . '">'; }

The function plugin_option is finally hooked to the admin_init action to put it into action.


  add_action('admin_init', 'plugin_option');
  

We are done with building the plugin settings page but what use is the data saved by the settings page to the database if it isn’t going to be used?

Next is the coding of the function restrict_content_register_user that will retrieve the post or page ID to be restricted to only registered users (which was saved to the database in the plugin settings page). This ensures the current user viewing the post is registered; otherwise an error message telling the user to register would be displayed.

Finally, the function will be hooked to the_content filter so as to affect the change in the post or page.

function restrict_content_register_user($content) {
  	global $post;
  	$post_database = get_option('rcru_post-id-option');
  	$post_database = explode(',', $post_database);
  	$current_user = wp_get_current_user();
  
  	/* If there is no content, return. */
  	if (is_null($content))
  		return $content;
  
  	foreach ($post_database as $posts) {
  		$posts = trim($posts);
  		if ($posts == $post -> ID) {
  			if (username_exists($current_user -> user_login)) {
  
  				/* Return the private content. */
  				return $content;
  			} else {
  
  				/* Return an alternate message. */
  				return '<div align="center" style="color: #fff; padding: 20px; border: 1px solid border-color: rgb(221, 204, 119); background-color: #3B5998">
  	You must be a registered user to read this content.
  	<br/>
  	<a style="font-size: 20px;" href="' . get_site_url() . '/wp-login.php?action=register">Register Here</a>
  </div>';
  			}
  
  		}
  	}
  	return $content;
  }
  add_filter('the_content', 'restrict_content_register_user');
  

We are now done with the first way the plugin work: the use of the plugin settings page.

What’s left is to add the metabox and shortcode feature to the plugin.

Adding Metabox

To add a metabox containing a checkbox in post and page edit screens will allow restriction of that post or page to registered user when the checkbox is ticked. The function rcru_mb_create will include the meta box in all post and pages when hooked to the add_meta_boxes action.


  function rcru_mb_create() {
  	/**
  	 * @array $screens Write screen on which to show the meta box
  	 * @values post, page, dashboard, link, attachment, custom_post_type
  	 */
  	$screens = array(
  		'post',
  		'page'
  	);
  	foreach ($screens as $screen) {
  		add_meta_box('rcru-meta',
  		'Restrict Post / Page',
  		'rcru_mb_function',
  		$screen,
  		'normal',
  		'high');
  	}
  }
  add_action('add_meta_boxes', 'rcru_mb_create');
  

The function rcru_mb_function contains the checkbox and description of the metabox.


  function rcru_mb_function($post) {
  
  	//retrieve the metadata values if they exist
  	$restrict_post = get_post_meta($post -> ID, '_rcru_restrict_content', true);
  
  	// Add an nonce field so we can check for it later when validating
  	wp_nonce_field('rcru_inner_custom_box', 'rcru_inner_custom_box_nonce');
  
  	echo '<div style="margin: 10px 100px; text-align: center">
      <table>
  		<tr>
  		<th scope="row"><label for="rcru-restrict-content">Restrict content?</label></th>
  			<td>
                          <input type="checkbox" value="1" name="rcru_restrict_content" id="rcru-restrict-content"' . checked($restrict_post, 1, false) . '>
                          <span class="description">Checking this setting will restrict this post to only registered users.</span>
                      </td>
                      </tr>
  	</table>
  </div>';
  }
  

The rcru_mb_save_data function handles the security check and the saving of the form values to the database.


  function rcru_mb_save_data($post_id) {
  	/*
  	 * We need to verify this came from the our screen and with proper authorization,
  	 * because save_post can be triggered at other times.
  	 */
  
  	// Check if our nonce is set.
  	if (!isset($_POST['rcru_inner_custom_box_nonce']))
  		return $post_id;
  
  	$nonce = $_POST['rcru_inner_custom_box_nonce'];
  
  	// Verify that the nonce is valid.
  	if (!wp_verify_nonce($nonce, 'rcru_inner_custom_box'))
  		return $post_id;
  
  	// If this is an autosave, our form has not been submitted, so we don't want to do anything.
  	if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
  		return $post_id;
  
  	// Check the user's permissions.
  	if ('page' == $_POST['post_type']) {
  
  		if (!current_user_can('edit_page', $post_id))
  			return $post_id;
  	} else {
  
  		if (!current_user_can('edit_post', $post_id))
  			return $post_id;
  	}
  
  	/* OK, its safe for us to save the data now. */
  
  	// If old entries exist, retrieve them
  	$old_restrict_post = get_post_meta($post_id, '_rcru_restrict_content', true);
  
  	// Sanitize user input.
  	$restrict_post = sanitize_text_field($_POST['rcru_restrict_content']);
  
  	// Update the meta field in the database.
  	update_post_meta($post_id, '_rcru_restrict_content', $restrict_post, $old_restrict_post);
  
  }
  
  //hook to save the meta box data
  add_action('save_post', 'rcru_mb_save_data');
  

The function restrict_content_metabox will examine a given post or page to see if it has restrictions, check if the user reading the post is registered and display a notice telling the user to register.


  function restrict_content_metabox($content) {
  	global $post;
  	//retrieve the metadata values if they exist
  	$post_restricted = get_post_meta($post -> ID, '_rcru_restrict_content', true);
  	
  	// if the post or page has restriction and the user isn't registered
  	// display the error notice
  	if ($post_restricted == 1 &&  (!username_exists(wp_get_current_user()->user_login)) ) {
  
  		return '<div align="center" style="color: #fff; padding: 20px; border: 1px solid #ddd; background-color: #3B5998">
  	You must be a registered user to read this content.
  	<br/>
  	<a style="font-size: 20px;" href="' . get_site_url() . '/wp-login.php?action=register">Register Here</a>
  </div>';
  	}
  	
  	return $content;
  
  }
  
  // hook the function to the post content to effect the change
  add_filter('the_content', 'restrict_content_metabox');
  

Adding the Shortcode

With the shortcode, part of a post can be restricted to only registered users.

The function rcru_user_shortcodes contains the shortcode hook function add_shortcode that defines the shortcode tag [rcru-private].
The second argument passed to add_shortcode is the callback function that is called when the shortcode is in use.


  /* Function for registering the shortcode. */
  function rcru_user_shortcodes() {
  
  	/* Adds the [rcru-private] shortcode. */
  	add_shortcode('rcru-private', 'rcru_shortcode');
  }
  

The function is then registered to init action so it will be recognised by WordPress internals.


  /* Register shortcodes in 'init'. */
  add_action('init', 'rcru_user_shortcodes');
  

Finally, the rcru_shortcode callback function handles the shortcode output.


  /* Function for handling shortcode output. */
  function rcru_shortcode($attr, $content = '') {
  
  	/* Check if the current user has the 'read_private_content' capability. */
  	$current_reader = wp_get_current_user();
  	if (!username_exists($current_reader -> user_login)) {
  
  		/* Return an alternate message. */
  		return '<div align="center" style="color: #fff; padding: 20px; border: 1px solid border-color: rgb(221, 204, 119); background-color: #3B5998">
  	You must be a registered user to read this content.
  	<br/>
  	<a style="font-size: 20px;" href="' . get_site_url() . '/wp-login.php?action=register">Register Here</a>
  </div>';
  	}
  }
  

How the plugin works

The plugin will have a settings page with a form field that accept Post & Page IDs to be restricted, delimited by a comma.

To restrict a given post or page, enter their respective IDs, separated by a comma (,) in the form field. To get the ID of a post, go to the post edit screen (TinyMCE editor for writing post content), and look at the URL of the page. The number appended to ?post= is the post ID.

For instance, for http://wordpress.dev/wp-admin/post.php?post=88&action=edit, the number 88 is the post or page ID.

Metabox in post & Page edit screen

A post or page can also be restricted to registered users by ticking the Restrict content checkbox located at the metabox (added by the plugin to the post edit screen).

Shortcode

Part or the section of a post or page content can be hidden from public view and displayed only to registered members with the use of a plugin shortcode like this:

[rcru-private]Part of post or page content to be restricted to only registered users.[/rcru-private]
  

Lastly, here is a link to the plugin file. Feel free to explore the code and happy coding!

Editor’s note: This post is written by Agbonghama Collins for Hongkiat.com. Agbonghama is a web developer by day and freelance writer / blogger by night. When not wrangling with code, he is fond of sleeping on the couch and writing on his personal blog tech4sky.com. You can find him on Facebook and G+.

No comments:

Post a Comment