Skip to content

MR-5: Metered Paywall#15

Open
av3nger wants to merge 9 commits into
feature/MR-31-paywallfrom
feature/MR-5-metered-paywall
Open

MR-5: Metered Paywall#15
av3nger wants to merge 9 commits into
feature/MR-31-paywallfrom
feature/MR-5-metered-paywall

Conversation

@av3nger

@av3nger av3nger commented May 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds a metered paywall on top of the existing Memberful paywall: a "free article allowance" that lets anonymous visitors and registered free members read a configurable number of matching posts within a rolling period before the paywall is shown.

What's included

  • New Memberful - Metering settings tab: enable toggle, rolling period, anonymous limit, registered free member limit, and an "apply to protected posts" option.
  • Visual rule builder. A post is metered when any rule group matches:
    • post type: is any of / is none of
    • category / tag: has any of / has none of
    • URL: contains / does not contain
  • Server-side engine: decision cached per request; paid members, exempt posts, non-matching posts, and ineligible requests (admin, feed, REST, non-GET, authors) are never metered.
  • Anonymous views tracked in a signed, HttpOnly cookie; registered views in user meta; anonymous views merge into the account on login.
  • Per-post "Exempt from metering" metabox.
  • Metering countdown block with a configurable {count} template, shown only while the visitor is still being sampled.

Test plan

  • Open Memberful - Metering, enable it, set a short period, an anonymous limit (e.g. 2) and a registered limit (e.g. 3), add a rule (e.g. post type is any of: Post), and save.
  • As a logged-out visitor, view matching posts past the limit:
    • confirm the first N render in full and the next shows the paywall
    • confirm re-opening an already-read post still renders (no double count)
    • confirm a non-matching post is never metered
  • Log in as a free member with prior anonymous views - confirm they carry over and the registered limit applies.
  • As a paid member, confirm matching posts are never metered.
  • Tick "Exempt this post from metering" and confirm the post stays fully readable and uncounted.
  • Insert the countdown block, use the {count} button, and confirm it shows the remaining count while sampling and disappears once the meter trips.
  • Disable metering and confirm no metering output or cache headers on protected posts.

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: TheCodeCompany/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6af538c2-aeb6-4fd3-bdc0-6d759680fce1

📥 Commits

Reviewing files that changed from the base of the PR and between 29c7c23 and a095303.

📒 Files selected for processing (3)
  • wordpress/wp-content/plugins/memberful-wp/src/metering/sanitizer.php
  • wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
  • wordpress/wp-content/plugins/memberful-wp/views/option_tabs.php

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Metering system to limit free article access with configurable period and limits
    • New Metering settings page with rule-based targeting and admin UI for managing rule groups
    • Metering countdown Gutenberg block showing remaining free articles (editor toolbar to insert {count})
    • Per-post “Exempt from meter” option and metabox to opt posts out of metering
    • Anonymous/user view tracking and migration on login; admin styles and editor script included

Walkthrough

Adds a full article metering system: configurable rule-based sampling, per-period view tracking (cookie/user-meta), admin UI and JS for rules, metabox exemption, Gutenberg countdown block, content integration, and build wiring.

Changes

Metering Feature Implementation

Layer / File(s) Summary
Storage infrastructure and configuration management
src/metering/config.php, src/metering/sanitizer.php, src/metering/storage.php, src/options.php, src/urls.php
Configuration defaults, sanitizer, signed-cookie anonymous view storage, user-meta view storage, pruning, payload signing/verification, and option/url helpers.
Metering access decisions and view enforcement
src/metering/access.php, src/metering.php, memberful-wp.php, src/content_filter.php
Decision engine evaluating eligibility and rules on template_redirect, recording/merging views, caching per-request decisions, login-time merge of anonymous views, and content-gate integration.
Admin settings page and rule configuration backend
src/admin.php, views/metering/settings.php, views/option_tabs.php
Admin settings screen and POST handler for metering configuration, rendering rule groups, localized data for JS, and adding the Metering tab to plugin options.
Admin JavaScript for dynamic rule group management
js/src/metering-admin.js
Client-side logic to build/manage rule groups and condition rows, render field/operator/value controls, handle add/remove actions, and keep form indices in sync.
Metering countdown Gutenberg block
js/src/blocks/metering-countdown/block.json, js/src/blocks/metering-countdown/edit.js, js/src/blocks/metering-countdown/index.js, js/src/blocks/metering-countdown/render.php, js/src/editor-scripts.js
Block metadata, editor component with {count} placeholder insertion, block registration, editor import wiring, and server-side rendering that displays remaining views when sampling is allowed.
Per-post metering exemption controls
src/metering/metabox.php, views/metering/metabox.php, stylesheets/admin.css
Metabox registration/render/save for per-post exempt flag with nonce/capability checks and CSS for metering admin UI.
Admin styling and settings navigation
stylesheets/admin.css, views/option_tabs.php
CSS for metering rule-group UI and adding the Metering tab to plugin settings navigation.
Build configuration for metering admin script
webpack.config.js
Added metering-admin Webpack entry to build the admin bundle.

Possibly Related PRs

Poem

Meter ticks, a tiny counter bright,
Anonymous cookies, logged-in sight,
Admin rules hum and JS arranges,
Countdown blocks show remaining ranges,
Exempt a post and keep delight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: addition of a metered paywall feature to the Memberful WordPress plugin.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, covering the feature summary, included components, and test plan for the metered paywall implementation.
Docstring Coverage ✅ Passed Docstring coverage is 88.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/MR-5-metered-paywall

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wordpress/wp-content/plugins/memberful-wp/src/metering/sanitizer.php`:
- Around line 30-31: The sanitizer currently coerces missing limits to 0 by
using $input['anonymous_limit'] ?? 0 and $input['registered_limit'] ?? 0 which
can prematurely trip meters; update the logic in the sanitizer to fall back to
the provided $defaults (e.g. $defaults['anonymous_limit'] and
$defaults['registered_limit']) before applying absint and min, so compute
$anonymous_limit = absint($input['anonymous_limit'] ??
$defaults['anonymous_limit']) and $registered_limit =
absint($input['registered_limit'] ?? $defaults['registered_limit']) and then set
$clean['anonymous_limit'] = min($anonymous_limit,
Memberful_Metering_Storage::MAX_VIEWS) and $clean['registered_limit'] =
min($registered_limit, Memberful_Metering_Storage::MAX_VIEWS).

In `@wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css`:
- Around line 170-172: The section comment
"/*--------------------------------------------------------- Metering
------------------------------------------------------------ */" violates
Stylelint's comment-whitespace-inside; update the comment in admin.css (the
Metering section header) to include a space after the opening /* (e.g. "/*
---------------------------------------------------------") so there is
whitespace inside the comment delimiters and the linter rule passes.

In `@wordpress/wp-content/plugins/memberful-wp/views/option_tabs.php`:
- Around line 18-22: The translated tab title for the 'metering' tab is missing
the plugin text domain; update the array entry with id 'metering' so the 'title'
uses the plugin domain (use __('Metering', 'memberful')) instead of __(
'Metering' ) to ensure proper translation loading in Memberful's locale
files—modify the 'title' value in the array where id => 'metering' and leave the
rest unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: TheCodeCompany/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 13cf76e1-dd64-4067-8296-8dbb280c28c5

📥 Commits

Reviewing files that changed from the base of the PR and between e6f32c1 and 29c7c23.

📒 Files selected for processing (23)
  • wordpress/wp-content/plugins/memberful-wp/js/src/blocks/metering-countdown/block.json
  • wordpress/wp-content/plugins/memberful-wp/js/src/blocks/metering-countdown/edit.js
  • wordpress/wp-content/plugins/memberful-wp/js/src/blocks/metering-countdown/index.js
  • wordpress/wp-content/plugins/memberful-wp/js/src/blocks/metering-countdown/render.php
  • wordpress/wp-content/plugins/memberful-wp/js/src/editor-scripts.js
  • wordpress/wp-content/plugins/memberful-wp/js/src/metering-admin.js
  • wordpress/wp-content/plugins/memberful-wp/memberful-wp.php
  • wordpress/wp-content/plugins/memberful-wp/src/admin.php
  • wordpress/wp-content/plugins/memberful-wp/src/block-editor.php
  • wordpress/wp-content/plugins/memberful-wp/src/content_filter.php
  • wordpress/wp-content/plugins/memberful-wp/src/metering.php
  • wordpress/wp-content/plugins/memberful-wp/src/metering/access.php
  • wordpress/wp-content/plugins/memberful-wp/src/metering/config.php
  • wordpress/wp-content/plugins/memberful-wp/src/metering/metabox.php
  • wordpress/wp-content/plugins/memberful-wp/src/metering/sanitizer.php
  • wordpress/wp-content/plugins/memberful-wp/src/metering/storage.php
  • wordpress/wp-content/plugins/memberful-wp/src/options.php
  • wordpress/wp-content/plugins/memberful-wp/src/urls.php
  • wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
  • wordpress/wp-content/plugins/memberful-wp/views/metering/metabox.php
  • wordpress/wp-content/plugins/memberful-wp/views/metering/settings.php
  • wordpress/wp-content/plugins/memberful-wp/views/option_tabs.php
  • wordpress/wp-content/plugins/memberful-wp/webpack.config.js

Comment thread wordpress/wp-content/plugins/memberful-wp/src/metering/sanitizer.php Outdated
Comment thread wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css Outdated
Comment thread wordpress/wp-content/plugins/memberful-wp/views/option_tabs.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant