If you’re trying to use MemberPress to protect posts that contain custom fields, relying on MemberPress rules alone will not work.
Jump down here to skip everything else and see how I created the single template file.
Why won’t MemberPress rules alone work?
As explained in this knowledge base (“KB”) article on the MemberPress website, this is because MemberPress rules only protect content that is outputted through the WordPress the_content() function.
(Note that the MemberPress knowledge base article misleadingly references “ACF fields”, suggesting that the solution pertains only to custom fields created with the Advanced Custom Fields plugin. Not true. The solution pertains to any custom fields created in any way.)
Enter Custom Post Types
Although any post type can contain custom fields, the most common use cases for custom fields are custom post types. So, if you’re trying to use MemberPress to protect custom post types, you’ll want to read on.
Two Primary Approaches:
As the above-referenced article points out, there are two primary approaches for using MemberPress to protect posts that contain custom fields:
- using Unauthorized Redirection
- protecting individual custom fields
Drawbacks to Unauthorized Redirection
As the KB article points out, there are potential drawbacks to using Unauthorized Redirection:
- It applies globally to all protected content.
- This means that rule-specific unauthorized messages aren’t possible: Unless you write custom code, unauthorized users will see the same (necessarily generic) message, regardless of what content they’re attempting to access.
- If you want to display excerpts for some protected content but not for others — or login forms for one but not for others, or registration links for some and not for others — you’re out of luck (unless you write a whole bunch of custom code).
In this article I’ll share how I used the second approach — protecting individual custom fields — using the current_user_can('mepr-active', rules:xxx') conditional.
Background/Use Case
I’m offering a one-time membership that allows members to watch on-demand videos.
Desired Outcomes:
If the user is not logged in:
User sees notice about not being logged in and is presented login form.

If the user is logged in but hasn’t purchased the membership:
User sees notice about being logged in, but s/he hasn’t purchased the needed membership. A link to the registration page is included.

If the user is logged in and has purchased the membership:
User gets to watch the video.

Custom Post Type
sdodv (Sahara Dance On-Demand Video, actual name = “SD On-Demand Video”)
The CPT supports title, thumbnail, and page-attributes. I’m not currently utilizing the thumbnail, and the page-attributes feature is enabled so that the posts can be ordered.
Code below for registering CPT
There are currently two posts:
3pm Show – Sahara Dance Fall Caravan 2024
6pm Show – Sahara Dance Fall Caravan 2024
Custom Fields, using ACF Pro
The custom fields are:
- description (odv_description)
- video embed code (odv_embed_code_iframe)
Custom Taxonomy
There is a custom taxonomy, ‘access-category’.
Both CPT posts have the “fall-2024-caravan-access” access-category term assigned to them.
Code below for registering custom taxonomy
MEPR Rule
I have a MemberPress rule ID 186): “SD On-Demand Video with Access Category: Fall 2024 Caravan Access”. The rule restricts the content to people who have a particular membership.
Custom Unauthorized Message:
The custom unauthorized message uses several MemberPress shortcodes.
- Lines 2, 5, 7, 11: Opening and closing of mepr-show. Line 2 begins the section for logged-in users; line 7 begins the section for logged-out users.
- Line 9: The mepr-membership-link shortcode displays the registration form for the membership that allows viewing these videos.
- Line 10: Display the login form.
- Lines 3 and 8: Just for display purposes. The “callout”-related classes allow the messages to be displayed distinctively.
(Only logged-in users who purchased the "Fall 2024 Caravan" videos can watch this video.) [mepr-show if="loggedin"] <div class="callout callout-warning">You are logged in, but you have not purchased these videos.</div> [mepr-membership-link id="93"]Purchase these videos[/mepr-membership-link] [/mepr-show] [mepr-show if="loggedout"] <div class="callout callout-caution">You are not logged in. If you <strong>have</strong> an account, log in using the form below.</div> If you <strong>do not</strong> have an account, [mepr-membership-link id="93"]create an account and purchase these videos now[/mepr-membership-link]. [mepr-login-form use_redirect="true"] [/mepr-show]
Archive & Single Templates
I created 2 very simple pages for the archive and single. For the purposes of this article, I’ll only show and explain the single template.
single-sdodv.php — Where the magic happens
<?php
get_header();
?>
<?php if ( have_posts() ) : ?>
<div class="wrapper">
<?php while ( have_posts() ) : the_post(); ?>
<?php
// Put custom field values in simple variables:
$description = get_post_meta($post->ID, 'odv_description', true);
$video = get_post_meta($post->ID, 'odv_embed_code_iframe', true);
// Set variable for access:
$current_user_can = current_user_can('mepr-active','rules:186');
// Dim the dessciption for unauthorized, via CSS class and rule:
$about_class = ( $current_user_can ) ? 'auth' : 'no-auth';
?>
<div class="item">
<h2><?php the_title();?></h2>
<?php if( $current_user_can ) : ?>
<div class="video"><?php echo $video;?></div>
<div class="description <?php echo $about_class;?>"><?php echo wpautop($description);?></div>
<?php else: ?>
<div class="description <?php echo $about_class;?>"><?php echo wpautop($description);?></div>
<div><?php echo do_shortcode('[mepr-unauthorized-message]'); ?></div>
<?php endif; ?>
</div>
<?php endwhile; ?>
</div>
<?php endif; ?>
<?php get_footer(); ?>
Notes on highlighted lines:
- Line 12: Setting the variable for access, using
current_user_can()function with themepr-activecapability - Lines 19-20: If the user is authorized, show the video above the description.
- Line 22: If the user is not authorized, show the desciption only.
- Line 23: Use the
do_shortcode()function to display the unauthorzed message assigned to the rule.
Extra Stuff
The stuff in this section doesn’t impact the protection issues that are the main topic of this article. But if you’ve read this far, you might be interested in the code I used to register the custom post type and the custom taxonomy. So here’s that…
Code for registering the CPT
function register_sdodv() {
$labels = array(
'name' => _x( 'SD On-Demand Video', 'Post Type General Name', 'text_domain' ),
'singular_name' => _x( 'SD On-Demand Video', 'Post Type Singular Name', 'text_domain' ),
'menu_name' => __( 'SD On-Demand Videos', 'text_domain' ),
'name_admin_bar' => __( 'SD On-Demand Video', 'text_domain' ),
'archives' => __( 'SD On-Demand Videos Archives', 'text_domain' ),
'attributes' => __( 'SD On-Demand Video Attributes', 'text_domain' ),
'parent_item_colon' => __( 'Parent SD On-Demand Video:', 'text_domain' ),
'all_items' => __( 'All SD On-Demand Videos', 'text_domain' ),
'add_new_item' => __( 'Add New SD On-Demand Video', 'text_domain' ),
'add_new' => __( 'Add New', 'text_domain' ),
'new_item' => __( 'New SD On-Demand Video', 'text_domain' ),
'edit_item' => __( 'Edit SD On-Demand Video', 'text_domain' ),
'update_item' => __( 'Update SD On-Demand Video', 'text_domain' ),
'view_item' => __( 'View SD On-Demand Video', 'text_domain' ),
'view_items' => __( 'View SD On-Demand Videos', 'text_domain' ),
'search_items' => __( 'Search SD On-Demand Video', 'text_domain' ),
'not_found' => __( 'Not found', 'text_domain' ),
'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ),
'featured_image' => __( 'Featured Image', 'text_domain' ),
'set_featured_image' => __( 'Set featured image', 'text_domain' ),
'remove_featured_image' => __( 'Remove featured image', 'text_domain' ),
'use_featured_image' => __( 'Use as featured image', 'text_domain' ),
'insert_into_item' => __( 'Insert into item', 'text_domain' ),
'uploaded_to_this_item' => __( 'Uploaded to this item', 'text_domain' ),
'items_list' => __( 'Items list', 'text_domain' ),
'items_list_navigation' => __( 'Items list navigation', 'text_domain' ),
'filter_items_list' => __( 'Filter items list', 'text_domain' ),
);
$args = array(
'label' => __( 'SD On-Demand Video', 'text_domain' ),
'description' => sdodv_blurb(),
'labels' => $labels,
'supports' => array( 'title', 'thumbnail', 'page-attributes' ),
'taxonomies' => array( 'access-category' ),
'hierarchical' => true,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'show_in_admin_bar' => true,
'show_in_nav_menus' => true,
'can_export' => true,
'has_archive' => 'sdodv',
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'page',
'show_in_rest' => true,
);
register_post_type( 'sdodv', $args );
}
add_action( 'init', 'register_sdodv', 0 );
Code for registering taxonomy
function register_sd_access_catetory() {
$labels = array(
'name' => 'Access Categories',
'singular_name' => 'Access Category',
'menu_name' => 'Access Categories',
'all_items' => 'All Access Categories',
'new_item_name' => 'New Access Category',
'add_new_item' => 'Add New Access Category',
'edit_item' => 'Edit Access Category',
'update_item' => 'Update Access Category',
'view_item' => 'View Access Category',
'separate_items_with_commas' => 'Separate items with commas',
'add_or_remove_items' => 'Add or remove Access Categories',
'choose_from_most_used' => 'Choose from the most used',
'popular_items' => 'Popular Access Categories',
'search_items' => 'Search Access Category',
'not_found' => 'Not Found',
'no_terms' => 'No items',
'items_list' => 'Access Categories list',
'items_list_navigation' => 'Access Categories list navigation',
);
$args = array(
'labels' => $labels,
'hierarchical' => true,
'public' => true,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => true,
);
register_taxonomy( 'access-category', array( 'sdodv' ), $args );
}
add_action( 'init', 'register_sd_access_catetory', 0 );
Like what you see? Share with others and join my mailing list. No long-term commitment, unsubscribe any time.

Leave a Reply