Access (E2)
The access engine enforces content gating in two layers.
Server-side
On template_redirect (priority 5) the engine checks URL-scope rules and either redirects to the paywall page or lets the request through. On the_content (priority 9) it checks post-scope rules and rewrites the body.
Four render modes:
| Mode | Effect |
|---|---|
block | Hide the post; redirect to /paywall?omem_from=<url> |
teaser | Show the first N words (default 60) and a CTA |
blur | Render the full body with a CSS blur overlay |
replace | Swap the body for a custom paywall block |
Render-side
For inline gating, two shortcodes ship:
[omembership_restrict plan="pro"]
Members-only content.
[/omembership_restrict]
[omembership_paywall]Gutenberg blocks register in OMembership_Blocks and reuse the same gating decision.
Rules schema
wp_omem_access_rules:
plan_id BIGINT
scope_type ENUM('post','cpt','category','tag','taxonomy','url','product','download','block')
scope_id_or_pattern VARCHAR(255)
mode ENUM('block','teaser','blur','replace')
drip_rule LONGTEXT (JSON)
expiration_rule LONGTEXT (JSON)Lookup by (scope_type, scope_id_or_pattern) is indexed; resolution is memoised in wp_cache under group omem_access for 5 minutes per (user, scope) pair.
Drip
Drip rules unlock content over time. OMembership_Drip::is_released_for_user() evaluates four strategies:
day_n- N days after the member'sstarted_atdate- absolute UTC date (cohort releases)after_lesson- unlock after a specific lesson is completedafter_quiz_pass- unlock after a quiz passes
The hourly omembership/drip/release worker fires omem/drip/released events the moment a piece of content becomes available, which OMailer can pick up for "your week 2 lesson is ready" emails.
SEO and admin preview
The filter chain on omem/access/can_view includes two bypass layers:
allow_seo_bots(priority 5) - bots get the full content unless a plan opts outallow_admin_preview(priority 4) - admins building courses bypass the gate
Programmatic check
phpOMembership_Access::can_user_view( int $user_id, string $scope_type, string $scope_id ): boolReturns the cached decision and fires omem/access/denied for analytics when it returns false.

