FCHubFCHub.co

Content Protection

How FCHub Memberships protects content across your WordPress site — posts, pages, custom post types, taxonomies, menus, URLs, comments, and special pages.

Content protection is how FCHub Memberships controls who sees what on your site. The plugin hooks into WordPress at multiple levels — from individual post content to URL routing — to enforce membership access rules.

How Protection Works

When a visitor requests a page, the plugin evaluates whether that content is protected and whether the visitor has access. This evaluation happens through the AccessEvaluator class, which checks:

  1. Is the resource protected? — Check explicit protection rules and plan-based rules
  2. Is the user an admin? — Admins bypass all protection by default (configurable)
  3. Does the user have a paused membership? — Paused members see a specific message
  4. Does the user have a direct grant? — Check for an active grant matching this resource
  5. Does the user have plan-based access? — Check if any of the user's plans include this resource
  6. Is the content drip-locked? — The user may have access but the drip date hasn't arrived yet
  7. Does the user have a wildcard grant? — Check for resource_id: * grants
  8. Taxonomy inheritance — Does the user have access to a taxonomy term that covers this post?

If all checks fail, the user sees a restriction message, is redirected, or sees teaser content, depending on your configuration.

Protection Modes

You can configure how restricted content appears to non-members:

ModeBehavior
Restriction MessageThe original content is replaced with a customizable message, plan names, and optional CTA button
RedirectThe visitor is redirected to a URL you specify (pricing page, login page, etc.)
TeaserPart of the content is shown as a teaser, followed by the restriction message

Teaser Options

When teaser mode is enabled, you can choose how much content to reveal:

Teaser ModeDescription
noneNo teaser — show restriction message only
excerptShow the post excerpt
more_tagShow content before the <!--more--> tag
wordsShow the first N words (configurable, 1-500)
customShow custom teaser text that you write

Each post can have its own teaser configuration through the Membership Protection meta box, or you can set a site-wide default in Memberships settings.

Protection by Resource Type

The ContentProtection class is the primary protection handler for posts, pages, and custom post types. It hooks into:

  • the_content (priority 999) — replaces content with restriction message
  • get_the_excerpt (priority 999) — filters excerpts for protected content
  • template_redirect — handles full-page redirects when protection mode is set to redirect
  • pre_get_posts — excludes protected posts from archive queries (when enabled in settings)
  • rest_prepare_{post_type} — filters content in REST API responses

Per-Post Protection: Edit any post or page and look for the Membership Protection meta box in the sidebar. Here you can:

  • Toggle protection on/off
  • Select which plans grant access
  • Choose a teaser mode and configure it
  • Write a custom restriction message (supports {plan_names}, {login_url}, {pricing_url}, {user_name} placeholders)
  • Set a CTA button with text and URL

Bulk Protection: Select multiple posts in the post list and use the bulk actions dropdown to "Protect with Membership" or "Remove Membership Protection". This creates or removes protection rules in bulk.

Implicit Protection: Posts that appear in any plan's content rules are automatically protected — you don't need to set per-post protection for them. The AccessEvaluator.isProtected() method checks both explicit protection rules and plan rule membership.

Archive Filtering: When "Hide protected content in archives" is enabled in settings, protected posts are excluded from archive pages, the blog index, and search results for non-members. Members still see protected posts in these listings. This uses the pre_get_posts hook to add post__not_in exclusions.

Admin Bypass

By default, administrators (users with manage_options capability) bypass all content protection. They always see full content regardless of membership status. This is configurable in Memberships > Settings via the admin_bypass setting.

Restriction Message Placeholders

Custom restriction messages support these placeholders:

PlaceholderReplaced With
{plan_names}Comma-separated list of active plans that grant access to this content
{login_url}WordPress login URL with return to the current page
{pricing_url}Your pricing page URL (configured in settings)
{user_name}The current user's display name, or "Guest" for logged-out visitors
{unlock_date}The drip unlock date (only in drip-locked context)

REST API Content Filtering

Protected content is also filtered in WordPress REST API responses. When a non-member requests a post through the REST API:

  • The content.rendered field is replaced with the restriction message
  • A content.protected flag is set to true
  • Excerpts are filtered based on teaser settings

This ensures that headless WordPress setups and third-party apps respect membership protection.

Cache Invalidation

The AccessEvaluator maintains two levels of caching:

  1. Per-request cache — static array cache cleared at the end of each request
  2. Transient cache — WordPress transients with 5-minute TTL for batch access checks

Cache is automatically invalidated when:

  • A grant is created, revoked, paused, resumed, or renewed
  • Protection rules are modified
  • The AccessEvaluator::clearUserCache() or AccessEvaluator::clearCache() methods are called

The ContentProtection class listens to all grant lifecycle hooks and clears the relevant user's cache when their membership status changes.

On this page