Simple Content Restrictor Plugin

Origin Story

I had a number of emails from people trying to restrict content in various ways. One person wanted to restrict access because they were editing previously published content. Another person wanted more of a traditional membership-style option with the ability to restrict certain content based on user roles. I looked at some of the plugins out there but felt that building something a little less corporate1 would be pretty easy. It’ll likely evolve as I get some people using it but it’s here now if you want to mess with it.

Making It

This plugin uses the Advanced Custom Fields plugin as it saves me tons of hassle building custom field interfaces.

ACF Stuff

First, these two pieces enable me to sync up ACF field data without the drama of import/export or hard coding it into PHP.2

add_filter('acf/settings/save_json', 'save_acf_files_here');
 
function save_acf_files_here( $path ) {
    
    // update path
    $path = plugin_dir_path( __FILE__ ) . '/acf-json';
    // return
    return $path;
    
}

add_filter('acf/settings/load_json', 'my_acf_json_load_point');

function my_acf_json_load_point( $paths ) {
    
    // remove original path (optional)
    unset($paths[0]);
    
    
    // append path
    $paths[] = plugin_dir_path( __FILE__ ) . '/acf-json';
    
    // return
    return $paths;
    
}

Now, I started off hand-writing the user levels as items. While easy, it felt wrong because there might be other user levels created by other plugins and they’d end up left out. I realized I could just add the user roles automatically like so. This will list all the user roles on the site as options.


add_filter('acf/load_field/name=privacy_settings', 'populate_user_levels');

//ADD ALL AVAILABLE USER ROLES AUTOMATICALLY
function populate_user_levels( $field )
{	
	// reset choices
	$field['privacy_settings'] = array();
	
	global $wp_roles;
	//print("<pre>".print_r($wp_roles,true)."</pre>"); 
	$roles = $wp_roles->get_names();
	foreach ($roles as $role) {
		$field['choices'][ $role ] = $role;
	}

	return $field;
}

Filtering the Content

My original idea was that I could just use use WP’s content filter. That is what I did but things got more complex as I got into the details. I moved from the current_user_can(‘administrator’) because it’s not as reliable and ended up using array_intersect to find if the array of roles that the user had intersected with the array of roles allowed to view the content. That’s what’s going on below and it’s spitting out some additional messages. One warns people who can edit the post that it’s restricted viewing.

The more interesting portion, gives non-logged in people a button to click on to login. That URL has an origin piece that redirects them back to the original post once they log in.

//filter content
function super_privacy_content_filter($content) {
  global $post;
  $post_id = $post->ID;
  $source_url = get_permalink($post_id);
  $warning_flag = '';
  $user = wp_get_current_user();
  if (get_acf_privacy_level($post_id)){
	  $allowed_roles = array_map('strtolower', get_acf_privacy_level($post_id));
	  $ability = implode(", ", $allowed_roles);
	  if (array_intersect($allowed_roles, $user->roles ) && is_user_logged_in() && current_user_can( 'edit', $post_id )){
	  	  if (current_user_can('editor')){
	  	  	$warning_flag = '<div id="access-flag">The content below is restricted to the following roles: '. $ability .'. <br>This message only appears for those who can edit this content. </div>';	  	  
	  	  }
		  return $warning_flag . $content;
		} 
		else if (!array_intersect($allowed_roles, $user->roles ) && is_user_logged_in()) {
			return 'Your access to this content is restricted. You need to be one of the following roles to see this content.<p class="ok-roles"><strong>Roles:</strong> ' . $ability . '</p>' ;
		} else if (!is_user_logged_in()){
			return 'Please <a href="' . wp_login_url() . '?origin=' . $source_url .'" title="Login">login</a> to see if you have access to this content.';
		}
	} else {
		return $content;
	}

}
add_filter( 'the_content', 'super_privacy_content_filter' );

This portion works with the login button to redirect users back to the content.

//LOGIN REDIRECT TO ORIGIN PAGE WHERE YOU COULDN'T SEE STUFF 
function acf_security_login_redirect( $redirect_to, $request, $user ) {
    $source_url = $_GET["origin"];
    if ($source_url != false) {      
            $redirect_to =  $source_url;
            return $redirect_to;
        } else {
        	return $redirect_to;
        }
 }

add_filter( 'login_redirect', 'acf_security_login_redirect', 10, 3 );

Cleaning the Various Feeds and JSON

This stuff just cleans up the RSS and JSON so that we’re not showing content to people in ways that we’re unaware of.


//clean RSS feed
function cleanse_feed_content($content) {
	global $post;
  	$post_id = $post->ID;
	if(count(get_acf_privacy_level($post_id))>0) {
		return 'Content is restricted. You need to go to the site and login.';
	} else {
		return $content;
	}
}
 add_filter( 'the_content_feed', 'cleanse_feed_content');
 add_filter( 'the_excerpt_rss', 'cleanse_feed_content');


//CLEAN JSON 

function cleanse_json_content($response, $post, $request) {
 	global $post;
  	$post_id = $post->ID;
  	$restricted = 'Content is restricted. You need to go to the site and login.';
    if (count(get_acf_privacy_level($post_id))>0) {       
        $response->data['content']['rendered'] = $restricted;
        $response->data['excerpt']['rendered'] = $restricted;
    }
    return $response;
}
add_filter('rest_prepare_post', 'cleanse_json_content', 10, 3);



1 The desperately grinding e-capitalist will become a villain hated by our children’s children.

2 I regret doing this very much on another project as I then started hand coding changes and now am stuck hand coding ACF data stuff. Less than desirable.

Privacy Statement