Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
27f1f0e
Add metering settings and rule builder
av3nger May 20, 2026
d6593fd
Save work in progress
av3nger May 21, 2026
1aabbe2
Fix product owner and cookie size issues
av3nger May 25, 2026
9ab0e75
Carry over anonymous metering views on login
av3nger May 25, 2026
61e9862
Add per-post metering exemption metabox
av3nger May 25, 2026
f329c39
Adjustments and improvements
av3nger May 25, 2026
7b79e4f
Add meterin countdown block
av3nger May 25, 2026
29c7c23
Allow inserting count template
av3nger May 25, 2026
a095303
Fix flagged issues
av3nger May 26, 2026
9108743
Add subscription view shortcode
av3nger Jun 8, 2026
d852448
Escape URL
av3nger Jun 8, 2026
f5667b2
Merge pull request #415 from TheCodeCompany/feature/MR-33-sub-view-sh…
mrhead Jun 19, 2026
26e44f1
Update changelog
mrhead Jun 19, 2026
9aef3ed
Improve the debug tool
mrhead Jun 19, 2026
cba0b8a
Remove redundant wp_user_id filter from mapped users count
mrhead Jun 19, 2026
1b714d3
Fix paywall divider alignment
av3nger Jun 21, 2026
190fb4e
Fix block protection styling
av3nger Jun 22, 2026
9abe189
Update copy
av3nger Jun 22, 2026
db002ac
Merge pull request #419 from TheCodeCompany/fix/mr-35-paywall-divider
mrhead Jun 22, 2026
b5d3149
Merge pull request #418 from TheCodeCompany/fix/MR-34-block-protection
mrhead Jun 22, 2026
8bc1dda
Update based on feedback
av3nger Jun 23, 2026
77a2fcb
Merge pull request #417 from TheCodeCompany/fix/MR-29-copy-updates
mrhead Jun 23, 2026
dd979b9
Fix minor bugs
av3nger Jun 29, 2026
75a8798
Allow setting paragraph count before paywall
av3nger Jul 1, 2026
0761b85
Fix issues
av3nger Jul 1, 2026
4734f05
Drop lookup_ prefix from debug endpoint params
mrhead Jul 8, 2026
93436ad
Add changelog entry
mrhead Jul 8, 2026
f8b4beb
Merge pull request #416 from memberful/improve-debug-tool
mrhead Jul 8, 2026
a9fade9
Bump websocket-driver in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Jul 15, 2026
d8e63aa
Bump axios in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Jul 20, 2026
1692b19
Bump immutable in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Jul 24, 2026
f1fbdb6
Bump svgo in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Jul 24, 2026
c56241a
Bump shell-quote in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Jul 25, 2026
5feec2e
Bump postcss in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Aug 2, 2026
fe33665
Bump ip-address in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Aug 3, 2026
947329f
Merge branch 'feature/MR-31-paywall' into feature/MR-5-metered-paywall
av3nger Aug 4, 2026
620220b
Show free registration upsell when the meter blocks anonymous visitors
av3nger Aug 4, 2026
6f07859
Use dedicated countdown messages for the last and second-to-last free…
av3nger Aug 4, 2026
9678017
Bump fast-uri in /wordpress/wp-content/plugins/memberful-wp
dependabot[bot] Aug 5, 2026
3df2272
Merge pull request #429 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
37d0c22
Merge pull request #427 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
a97f936
Merge pull request #426 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
951b594
Merge pull request #424 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
13c8237
Merge pull request #423 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
cd06f8d
Merge pull request #422 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
8118f4b
Merge pull request #420 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
722d5a2
Merge pull request #428 from memberful/dependabot/npm_and_yarn/wordpr…
mrhead Aug 6, 2026
d92d532
Bump @wordpress/scripts from 31.8.0 to 34.0.0
mrhead Aug 6, 2026
4b09986
Merge pull request #413 from TheCodeCompany/feature/MR-31-paywall
mrhead Aug 12, 2026
5e404b1
Bump version to 1.81.0
mrhead Aug 7, 2026
e8d8a1d
Add changelog entry for paywall builder
mrhead Aug 12, 2026
12dfc65
Fix issues with paragraph count
av3nger Aug 24, 2026
da8bd39
Respect the paywall divider in generated excerpts
av3nger Aug 24, 2026
c2d2cad
Extract paragraph count into shared helpers
av3nger Aug 24, 2026
e8c748e
Merge pull request #431 from TheCodeCompany/feature/MR-10-paragraph-c…
mrhead Aug 24, 2026
2876e9b
Add changelog entry for paragraph count setting
mrhead Aug 24, 2026
6321664
Fix option sanitize
av3nger Aug 30, 2026
2d2cc0f
Merge branch 'main' into feature/MR-5-metered-paywall
av3nger Aug 31, 2026
0681139
Remove anonymous-to-registered merge on login
av3nger Aug 31, 2026
6dbb066
Show the free-view count in the paywall
av3nger Aug 31, 2026
1698450
Fix listing teasers for a paywall divider nested in other blocks
av3nger Aug 31, 2026
401f941
Document metering limits and warn when the global paywall is off
av3nger Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
__experimentalSpacer as Spacer,
ToggleControl,
SelectControl,
CheckboxControl,
Expand Down Expand Up @@ -95,11 +96,11 @@ const MemberfulVisibilityControlsOptions = (props) => {
{ value: "none", label: __("All users (Default)", "memberful") },
{
value: "logged_in",
label: __("All logged-in members", "memberful"),
label: __("All members (active, inactive, or free)", "memberful"),
},
{
value: "specific",
label: __("Specific membership plan", "memberful"),
label: __("Members with an active subscription", "memberful"),
},
]}
/>
Expand All @@ -123,9 +124,11 @@ const MemberfulVisibilityControlsOptions = (props) => {
/>
))}
{memberful_visibility_plans.length === 0 && (
<Notice status="error" isDismissible={false}>
{__("Please select at least one plan.", "memberful")}
</Notice>
<Spacer marginBottom={3}>
<Notice status="error" isDismissible={false}>
{__("Please select at least one plan.", "memberful")}
</Notice>
</Spacer>
)}
</>
)}
Expand All @@ -149,7 +152,7 @@ const MemberfulVisibilityControls = createHigherOrderComponent((BlockEdit) => {
{props.isSelected && !excludedBlocks.includes(props.name) && (
<InspectorControls>
<PanelBody
title={__("Memberful Visibility", "memberful")}
title={__("Memberful: Restrict Access", "memberful")}
initialOpen={false}
>
{MemberfulVisibilityControlsOptions(props)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "memberful/metering-countdown",
"version": "1.0.0",
"title": "Memberful Metering Countdown",
"category": "memberful",
"icon": "clock",
"description": "Shows visitors how many free articles they have left under metering.",
"keywords": [
"Memberful",
"Metering",
"Paywall"
],
"textdomain": "memberful",
"attributes": {
"template": {
"type": "string",
"default": "You have {count} free articles left."
},
"singularTemplate": {
"type": "string",
"default": "You have {count} free article left."
},
"lastArticleTemplate": {
"type": "string",
"default": "This is your last free article."
}
},
"supports": {
"html": false
},
"editorScript": "memberful-wp-block-editor",
"render": "file:./render.php"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* Retrieves the translation of text.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
*/
import { __ } from '@wordpress/i18n';

/**
* React hook that is used to mark the block wrapper element.
* It provides all the necessary props like the class name.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
*/
import {
BlockControls,
InspectorControls,
RichText,
store as blockEditorStore,
useBlockProps,
} from '@wordpress/block-editor';
import {
PanelBody,
TextControl,
ToolbarButton,
ToolbarGroup,
} from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { create, insert, toHTMLString } from '@wordpress/rich-text';

const COUNT_PLACEHOLDER = '{count}';

/**
* The edit function describes the structure of your block in the context of the
* editor. This represents what the editor will render when the block is used.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
*
* @return {Element} Element to render.
*/
export default function Edit( { attributes, setAttributes, clientId } ) {
const { template, singularTemplate, lastArticleTemplate } = attributes;
const { selectionStart, selectionEnd } = useSelect(
( select ) => {
const { getSelectionStart, getSelectionEnd } =
select( blockEditorStore );
const start = getSelectionStart();
const end = getSelectionEnd();

if (
start.clientId !== clientId ||
end.clientId !== clientId ||
start.attributeKey !== 'template' ||
end.attributeKey !== 'template'
) {
return {};
}

return {
selectionStart: start.offset,
selectionEnd: end.offset,
};
},
[ clientId ]
);

const insertCountPlaceholder = () => {
const value = create( { html: template || '' } );
const selectedStart = Number.isInteger( selectionStart )
? selectionStart
: value.text.length;
const selectedEnd = Number.isInteger( selectionEnd )
? selectionEnd
: selectedStart;
const insertStart = Math.max(
0,
Math.min( selectedStart, selectedEnd, value.text.length )
);
const insertEnd = Math.max(
0,
Math.min( Math.max( selectedStart, selectedEnd ), value.text.length )
);
const nextValue = insert(
value,
COUNT_PLACEHOLDER,
insertStart,
insertEnd
);

setAttributes( {
template: toHTMLString( { value: nextValue } ),
} );
};

return (
<>
<InspectorControls>
<PanelBody title={ __( 'Countdown messages', 'memberful' ) }>
<TextControl
__next40pxDefaultSize
__nextHasNoMarginBottom
label={ __( 'One article left message', 'memberful' ) }
help={ __(
'Shown when exactly one free article remains after this one. Leave empty to hide the block at that point.',
'memberful'
) }
value={ singularTemplate }
onChange={ ( value ) =>
setAttributes( { singularTemplate: value } )
}
placeholder={ __(
'You have {count} free article left.',
'memberful'
) }
/>
<TextControl
__next40pxDefaultSize
__nextHasNoMarginBottom
label={ __( 'Last article message', 'memberful' ) }
help={ __(
'Shown instead of the countdown when the visitor is reading their last free article. Leave empty to hide the block on the last article.',
'memberful'
) }
value={ lastArticleTemplate }
onChange={ ( value ) =>
setAttributes( { lastArticleTemplate: value } )
}
placeholder={ __(
'This is your last free article.',
'memberful'
) }
/>
</PanelBody>
</InspectorControls>
<BlockControls group="block">
<ToolbarGroup>
<ToolbarButton
title={ __( 'Insert count placeholder', 'memberful' ) }
onClick={ insertCountPlaceholder }
>
{ COUNT_PLACEHOLDER }
</ToolbarButton>
</ToolbarGroup>
</BlockControls>
<RichText
{ ...useBlockProps() }
identifier="template"
tagName="p"
value={ template }
onChange={ ( value ) => setAttributes( { template: value } ) }
placeholder={ __(
'You have {count} free articles left.',
'memberful'
) }
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Registers a new block provided a unique name and an object defining its behavior.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
import { registerBlockType } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import Edit from './edit';
import metadata from './block.json';

/**
* Every block starts by registering a new block type definition.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
*/
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit: Edit,
save: () => null,
} );
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* PHP file to use when rendering the block type on the server to show on the front end.
*
* The following variables are exposed to the file:
* $attributes (array): The block attributes.
* $content (string): The block default content.
* $block (WP_Block): The block instance.
*
* @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
*/

$post_id = get_queried_object_id();

if ( Memberful_Metering_Access::DECISION_ALLOW_SAMPLE !== Memberful_Metering_Access::get_current_decision( $post_id ) ) {
return;
}

$remaining = Memberful_Metering_Access::get_current_remaining( $post_id );

/**
* Remaining counts views left *after* this article, so on the last free article it is zero.
* Swap in the dedicated message there instead of rendering "0 free articles left", and use
* the singular wording when exactly one view remains.
*/
if ( 0 === $remaining ) {
$template = isset( $attributes['lastArticleTemplate'] ) ? (string) $attributes['lastArticleTemplate'] : '';
} elseif ( 1 === $remaining ) {
$template = isset( $attributes['singularTemplate'] ) ? (string) $attributes['singularTemplate'] : '';
} else {
$template = isset( $attributes['template'] ) ? (string) $attributes['template'] : '';
}

$message = str_replace(
'{count}',
number_format_i18n( $remaining ),
$template
);

if ( '' === trim( $message ) ) {
return;
}

printf(
'<p %s>%s</p>',
get_block_wrapper_attributes(),
wp_kses_post( $message )
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ registerBlockType(metadata.name, {
{...blockProps}
style={{
borderTop: "2px dashed currentColor",
margin: "16px 0",
margin: "16px auto",
paddingTop: "8px",
opacity: 0.6,
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
// Block imports.
import "./blocks/extensions";
import "./blocks/paywall-divider";
import "./blocks/metering-countdown";
Loading