Skip to content

MR-5: Metered Paywall - #15

Closed
av3nger wants to merge 62 commits into
feature/MR-31-paywallfrom
feature/MR-5-metered-paywall
Closed

MR-5: Metered Paywall#15
av3nger wants to merge 62 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.

Note

High Risk
Changes who sees full content vs the paywall and persists view counts in cookies/user meta; misconfiguration or edge-cache bypass could leak metered HTML or block legitimate readers.

Overview
Adds a metered paywall on top of Memberful’s existing protection: visitors get a rolling allowance of free reads on matching content before the paywall applies.

Publishers configure it from a new Metering settings tab—enable toggle, period and anonymous vs free-member limits, optional metering of members-only posts, and include/except rule groups (post type, category/tag, URL) built with new admin JS. Views are stored in a signed anonymous cookie or user meta, merged on login; metered responses set no-cache headers.

the_content now respects a per-request metering decision only for the singular post under view: samples render in full; a tripped meter forces the paywall even when normal ACL might differ. The paywall primary CTA can switch to free registration when a logged-out visitor hits the limit and registered users get a higher allowance.

Editors get a metering countdown block ({count} templates) and a per-post exempt from metering metabox.

Reviewed by Cursor Bugbot for commit 6f07859. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a new “Metering” settings area in the WordPress admin for configuring free-view limits, reset timing, and rule-based access.
    • Introduced a metering countdown block for editors, plus a per-post option to exempt content from metering.
    • Added rule builder controls for including/excluding content by post type, category, tag, or URL.
  • Bug Fixes

    • Improved content access handling so sample views, protected content, and metering limits behave more consistently across posts.

Walkthrough

Adds configurable metering rules, sampled-view tracking, admin editing surfaces, a metering countdown block, and per-post exemption handling across front-end and admin paths.

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, sanitization, signed anonymous view storage, user-meta view storage, pruning, option defaults, and admin URL helpers.
Metering access decisions and view enforcement
src/metering/access.php, src/metering.php, memberful-wp.php, src/block-editor.php, src/content_filter.php
Registers metering hooks, computes per-request decisions, merges anonymous and logged-in views, applies content gating, and resolves block registration for the metering countdown block.
Admin settings page and rule configuration backend
src/admin.php, views/option_tabs.php, views/metering/settings.php
Adds the metering settings page, saves configuration, exposes localized editor data, and adds the metering tab to the plugin options UI.
Admin rule UI templates for groups and conditions
views/metering/group.php, views/metering/condition.php, views/metering/chip.php, views/metering/icon-remove.php
Server-rendered templates for rule groups, condition rows, value chips, and remove icons used by the metering settings interface.
Admin JavaScript and styling for dynamic rule editing
js/src/metering-admin.js, webpack.config.js, stylesheets/admin.css
Client-side logic for adding and editing rule groups and tokenfield values, plus the admin stylesheet and build entry for the metering admin bundle.
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
Registers the metering countdown block, provides the editor UI for inserting the {count} placeholder, and renders the sampled message on the server.
Per-post metering exemption controls
src/metering/metabox.php, views/metering/metabox.php
Registers the metabox for exempting a post from metering and renders the checkbox UI used by the save handler.

Possibly Related PRs

  • TheCodeCompany/memberful-wp#8: Shares the content-gating path in src/content_filter.php; this PR changes how metering decisions alter that flow.

Poem

A tiny meter starts to tick,
With rules and chips and admin clicks.
{count} appears on pages bright,
While exempt posts slip from sight.
Cookies hum and settings bloom,
Quiet meters fill the room.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly identifies the main change: adding a metered paywall.
Description check ✅ Passed The description accurately explains the metered paywall, configuration, tracking, rule builder, metabox, and countdown block changes.
✨ 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
⚔️ Resolve merge conflicts
  • Resolve merge conflict 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.

@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
av3nger and others added 12 commits June 8, 2026 13:38
…b-view-shortcode

MR-33: Add subscription view shortcode
The debug tool renders all mapped and unmapped users. This is an issue
on sites with many members because rendering the debug output fails.

This commit fixes it by removing the mapping info from the debug output,
and adding an option to filter user/member mappings by either of email,
`member_id`, or `wp_user_id`. In the UI this is a separate tab, so the
debug tool is now split into system info and a member/user lookup. Over
the debug endpoint the same `debug_tab` parameter selects the section,
defaulting to a usage page that documents the available parameters.

Other small improvements:
- sensitive information is masked in system info and the error log
- error logs are HTML-escaped
- individual errors in the error log are separated so it's easier to scan

https://3.basecamp.com/3293071/buckets/9856127/todos/10000472282
…l-divider

MR-35: Paywall divider is misaligned
…protection

MR-34: Block protection styling needs a margin fix
Comment thread wordpress/wp-content/plugins/memberful-wp/src/metering/access.php Outdated
Comment thread wordpress/wp-content/plugins/memberful-wp/src/metering/config.php
Comment thread wordpress/wp-content/plugins/memberful-wp/src/metering/access.php
Comment thread wordpress/wp-content/plugins/memberful-wp/src/content_filter.php

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wordpress/wp-content/plugins/memberful-wp/src/metering/access.php (1)

305-309: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid repeated term queries for taxonomy conditions.

condition_matches() can call term_slugs_for_post() multiple times per request for the same post/taxonomy, causing redundant wp_get_post_terms() lookups.

  private static function term_slugs_for_post( int $post_id, string $taxonomy ): array {
    static $cache = array();

    $cache_key = $post_id . ':' . $taxonomy;
    if ( isset( $cache[ $cache_key ] ) ) {
      return $cache[ $cache_key ];
    }

    $terms = wp_get_post_terms( $post_id, $taxonomy, array( 'fields' => 'all' ) );
    if ( is_wp_error( $terms ) || empty( $terms ) ) {
      $cache[ $cache_key ] = array();
      return $cache[ $cache_key ];
    }

    $result = array();
    foreach ( $terms as $term ) {
      $result[] = strtolower( $term->slug );
      $result[] = strtolower( $term->name );
    }

    $cache[ $cache_key ] = array_values( array_unique( $result ) );
    return $cache[ $cache_key ];
  }

Also applies to: 346-359

🤖 Prompt for 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.

In `@wordpress/wp-content/plugins/memberful-wp/src/metering/access.php` around
lines 305 - 309, The term_slugs_for_post() method performs redundant
wp_get_post_terms() lookups when called multiple times for the same post and
taxonomy combination within a single request. Implement a static cache within
the term_slugs_for_post() method using a cache key combining post_id and
taxonomy (e.g., "post_id:taxonomy"). At the start of the method, check if the
result already exists in this static cache and return it immediately if found.
If not cached, proceed with the wp_get_post_terms() call, then store the
processed result (including both lowercase slugs and names) in the static cache
before returning it. This eliminates redundant database queries for the same
post/taxonomy combinations across multiple condition evaluations in a single
request.
🤖 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.

Outside diff comments:
In `@wordpress/wp-content/plugins/memberful-wp/src/metering/access.php`:
- Around line 305-309: The term_slugs_for_post() method performs redundant
wp_get_post_terms() lookups when called multiple times for the same post and
taxonomy combination within a single request. Implement a static cache within
the term_slugs_for_post() method using a cache key combining post_id and
taxonomy (e.g., "post_id:taxonomy"). At the start of the method, check if the
result already exists in this static cache and return it immediately if found.
If not cached, proceed with the wp_get_post_terms() call, then store the
processed result (including both lowercase slugs and names) in the static cache
before returning it. This eliminates redundant database queries for the same
post/taxonomy combinations across multiple condition evaluations in a single
request.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro

Run ID: 73b27091-dc92-486f-a075-97f4e09e0940

📥 Commits

Reviewing files that changed from the base of the PR and between a095303 and 8bc1dda.

📒 Files selected for processing (11)
  • wordpress/wp-content/plugins/memberful-wp/js/src/metering-admin.js
  • wordpress/wp-content/plugins/memberful-wp/src/admin.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/sanitizer.php
  • wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
  • wordpress/wp-content/plugins/memberful-wp/views/metering/chip.php
  • wordpress/wp-content/plugins/memberful-wp/views/metering/condition.php
  • wordpress/wp-content/plugins/memberful-wp/views/metering/group.php
  • wordpress/wp-content/plugins/memberful-wp/views/metering/icon-remove.php
  • wordpress/wp-content/plugins/memberful-wp/views/metering/settings.php

mrhead and others added 2 commits June 23, 2026 14:51
…pdates

MR-29: Copy update to gutenberg visibility settings
Comment thread wordpress/wp-content/plugins/memberful-wp/src/metering/access.php

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
wordpress/wp-content/plugins/memberful-wp/views/metering/settings.php (1)

24-25: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the scope-specific fallback match.

$match falls back to 'all' for both scopes, but new exclude_rules groups default to 'any' later in this file. If saved data is missing or carries an invalid match, reopening and saving the form will silently tighten exception groups and meter posts that should stay excluded.

    $conditions    = isset( $group['conditions'] ) && is_array( $group['conditions'] ) ? $group['conditions'] : array();
    $default_match = 'exclude_rules' === $scope ? 'any' : 'all';
    $match         = isset( $group['match'] ) && in_array( $group['match'], Memberful_Metering_Config::MATCH_TYPES, true ) ? $group['match'] : $default_match;
🤖 Prompt for 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.

In `@wordpress/wp-content/plugins/memberful-wp/views/metering/settings.php` around
lines 24 - 25, The scope-specific default for the group match is not being
preserved in the settings form, so invalid or missing values always fall back to
"all". Update the logic in settings.php where $conditions and $match are
initialized to use a scope-aware fallback based on $scope (keeping exclude_rules
on "any" and other scopes on "all"), and make sure the later
form-saving/rendering paths use that same default behavior so reopening and
saving does not tighten exclusion groups.
wordpress/wp-content/plugins/memberful-wp/src/metering/access.php (1)

325-335: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Match URL rules against full permalinks too.

Line 326 strips the permalink to only its path, so an admin-entered full URL like https://example.com/news/foo will not match /news/foo. That can silently skip metering or exclusion rules configured with copied permalinks.

      case 'url':
        $permalink = (string) get_permalink( $post->ID );
        $path      = wp_parse_url( $permalink, PHP_URL_PATH );
        $path      = is_string( $path ) ? $path : '';
        $matches   = false;
        foreach ( $values as $fragment ) {
          $fragment = (string) $fragment;
          if ( '' !== $fragment && ( false !== strpos( $path, $fragment ) || false !== strpos( $permalink, $fragment ) ) ) {
            $matches = true;
            break;
          }
        }
        break;

As per path instructions, review possible edge cases and unexpected behaviour for PHP/WordPress code.

🤖 Prompt for 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.

In `@wordpress/wp-content/plugins/memberful-wp/src/metering/access.php` around
lines 325 - 335, The URL matching logic in the access metering rule currently
only checks the parsed path from get_permalink(), so full URLs entered in admin
rules will not match. Update the 'url' case in access.php to compare each
fragment against both the permalink path and the full permalink string, using
the existing get_permalink(), wp_parse_url(), and strpos() flow, so copied URLs
like https://example.com/news/foo are matched correctly.

Source: Path instructions

🤖 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.

Outside diff comments:
In `@wordpress/wp-content/plugins/memberful-wp/src/metering/access.php`:
- Around line 325-335: The URL matching logic in the access metering rule
currently only checks the parsed path from get_permalink(), so full URLs entered
in admin rules will not match. Update the 'url' case in access.php to compare
each fragment against both the permalink path and the full permalink string,
using the existing get_permalink(), wp_parse_url(), and strpos() flow, so copied
URLs like https://example.com/news/foo are matched correctly.

In `@wordpress/wp-content/plugins/memberful-wp/views/metering/settings.php`:
- Around line 24-25: The scope-specific default for the group match is not being
preserved in the settings form, so invalid or missing values always fall back to
"all". Update the logic in settings.php where $conditions and $match are
initialized to use a scope-aware fallback based on $scope (keeping exclude_rules
on "any" and other scopes on "all"), and make sure the later
form-saving/rendering paths use that same default behavior so reopening and
saving does not tighten exclusion groups.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: ASSERTIVE

Plan: Pro

Run ID: 943b259b-e4f2-44f0-b016-faa5fdf141c8

📥 Commits

Reviewing files that changed from the base of the PR and between 8bc1dda and dd979b9.

📒 Files selected for processing (5)
  • 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/storage.php
  • wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
  • wordpress/wp-content/plugins/memberful-wp/views/metering/settings.php

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 620220b. Configure here.

Comment thread wordpress/wp-content/plugins/memberful-wp/src/metering/access.php
av3nger and others added 19 commits August 4, 2026 16:46
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.2 to 3.1.5.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](fastify/fast-uri@v3.1.2...v3.1.5)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
…rn/wordpress/wp-content/plugins/memberful-wp/fast-uri-3.1.5

Bump fast-uri from 3.1.2 to 3.1.5 in /wordpress/wp-content/plugins/memberful-wp
…rn/wordpress/wp-content/plugins/memberful-wp/postcss-8.5.25

Bump postcss from 8.5.15 to 8.5.25 in /wordpress/wp-content/plugins/memberful-wp
…rn/wordpress/wp-content/plugins/memberful-wp/shell-quote-1.10.0

Bump shell-quote from 1.8.4 to 1.10.0 in /wordpress/wp-content/plugins/memberful-wp
…rn/wordpress/wp-content/plugins/memberful-wp/svgo-3.3.4

Bump svgo from 3.3.3 to 3.3.4 in /wordpress/wp-content/plugins/memberful-wp
…rn/wordpress/wp-content/plugins/memberful-wp/immutable-5.1.9

Bump immutable from 5.1.6 to 5.1.9 in /wordpress/wp-content/plugins/memberful-wp
…rn/wordpress/wp-content/plugins/memberful-wp/axios-1.18.1

Bump axios from 1.16.1 to 1.18.1 in /wordpress/wp-content/plugins/memberful-wp
…rn/wordpress/wp-content/plugins/memberful-wp/websocket-driver-0.7.5

Bump websocket-driver from 0.7.4 to 0.7.5 in /wordpress/wp-content/plugins/memberful-wp
…rn/wordpress/wp-content/plugins/memberful-wp/ip-address-10.4.0

Bump ip-address from 10.2.0 to 10.4.0 in /wordpress/wp-content/plugins/memberful-wp
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ragraph-count

MR-10: Allow setting paragraph count before paywall
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
$period = absint( $input['period_days'] ?? 0 );
$clean['period_days'] = $period > 0 ? $period : $defaults['period_days'];

$anonymous_limit = absint( $input['anonymous_limit'] ?? $defaults['anonymous_limit'] );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@av3nger an empty limit field saves as 0 and locks every metered post. period_days already falls back to its default.

* @param WP_Post $post Post being rendered.
* @return string Rendered content above the divider block.
*/
function memberful_wp_content_above_divider_block( WP_Post $post ): string {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@av3nger this splits raw block markup on a comment prefix, so a divider nested in a Group or Columns block yields unbalanced HTML. force_balance_tags() on the return may fix this issue if you can test that please.

@av3nger
av3nger marked this pull request as draft August 31, 2026 05:17
@av3nger

av3nger commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator Author

Reworked in #28

@av3nger av3nger closed this Aug 31, 2026
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.

3 participants