From ad564160a5b0e2e0a4948c6575814d773f359ba9 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 21 Apr 2026 11:54:42 +1000
Subject: [PATCH 01/28] Create and register paywall config
---
.gitignore | 1 +
.../plugins/memberful-wp/memberful-wp.php | 1 +
.../plugins/memberful-wp/src/options.php | 60 ++++++++--------
.../plugins/memberful-wp/src/paywall.php | 8 +++
.../memberful-wp/src/paywall/config.php | 61 ++++++++++++++++
.../memberful-wp/src/paywall/sanitizer.php | 71 +++++++++++++++++++
6 files changed, 173 insertions(+), 29 deletions(-)
create mode 100644 wordpress/wp-content/plugins/memberful-wp/src/paywall.php
create mode 100644 wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
create mode 100644 wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
diff --git a/.gitignore b/.gitignore
index c0b8df13..cf79054b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.DS_Store
+.idea
# Manage wordpress/wp-content/plugins/memberful-wp and ignore everything else
# from the wordpress/ folder.
diff --git a/wordpress/wp-content/plugins/memberful-wp/memberful-wp.php b/wordpress/wp-content/plugins/memberful-wp/memberful-wp.php
index 5ba9d8db..daeff845 100755
--- a/wordpress/wp-content/plugins/memberful-wp/memberful-wp.php
+++ b/wordpress/wp-content/plugins/memberful-wp/memberful-wp.php
@@ -46,6 +46,7 @@
require_once MEMBERFUL_DIR . '/src/widgets.php';
require_once MEMBERFUL_DIR . '/src/endpoints.php';
require_once MEMBERFUL_DIR . '/src/marketing_content.php';
+require_once MEMBERFUL_DIR . '/src/paywall.php';
require_once MEMBERFUL_DIR . '/src/content_filter.php';
require_once MEMBERFUL_DIR . '/src/search_filter.php';
require_once MEMBERFUL_DIR . '/src/entities.php';
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/options.php b/wordpress/wp-content/plugins/memberful-wp/src/options.php
index 0f16f406..39a611c8 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/options.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/options.php
@@ -1,35 +1,37 @@
NULL,
- 'memberful_client_secret' => NULL,
- 'memberful_site' => NULL,
- 'memberful_custom_domain' => NULL,
- 'memberful_api_key' => NULL,
- 'memberful_webhook_secret' => NULL,
- 'memberful_products' => array(),
- 'memberful_subscriptions' => array(),
- 'memberful_acl' => array(),
- 'memberful_embed_enabled' => FALSE,
- 'memberful_error_log' => array(),
- 'memberful_role_active_customer' => 'subscriber',
- 'memberful_role_inactive_customer' => 'subscriber',
- 'memberful_plan_role_mappings' => array(),
- 'memberful_use_per_plan_roles' => FALSE,
- 'memberful_posts_available_to_any_registered_user' => array(),
- 'memberful_hide_admin_toolbar' => TRUE,
- 'memberful_block_dashboard_access' => TRUE,
- 'memberful_filter_account_menu_items' => TRUE,
- 'memberful_auto_sync_display_names' => FALSE,
- 'memberful_show_protected_content_in_search' => FALSE,
- 'memberful_use_global_marketing' => FALSE,
- 'memberful_use_global_snippets' => TRUE,
- 'memberful_global_marketing_override' => TRUE,
- 'memberful_global_marketing_content' => '',
- 'memberful_ad_provider_settings' => array()
- );
+function memberful_wp_all_options(): array {
+ return array(
+ 'memberful_client_id' => null,
+ 'memberful_client_secret' => null,
+ 'memberful_site' => null,
+ 'memberful_custom_domain' => null,
+ 'memberful_api_key' => null,
+ 'memberful_webhook_secret' => null,
+ 'memberful_products' => array(),
+ 'memberful_subscriptions' => array(),
+ 'memberful_acl' => array(),
+ 'memberful_embed_enabled' => false,
+ 'memberful_error_log' => array(),
+ 'memberful_role_active_customer' => 'subscriber',
+ 'memberful_role_inactive_customer' => 'subscriber',
+ 'memberful_plan_role_mappings' => array(),
+ 'memberful_use_per_plan_roles' => false,
+ 'memberful_posts_available_to_any_registered_user' => array(),
+ 'memberful_hide_admin_toolbar' => true,
+ 'memberful_block_dashboard_access' => true,
+ 'memberful_filter_account_menu_items' => true,
+ 'memberful_auto_sync_display_names' => false,
+ 'memberful_show_protected_content_in_search' => false,
+ 'memberful_use_global_marketing' => false,
+ 'memberful_use_global_snippets' => true,
+ 'memberful_global_marketing_override' => true,
+ 'memberful_global_marketing_content' => '',
+ 'memberful_ad_provider_settings' => array(),
+ Memberful_Paywall_Config::OPTION_KEY => array(),
+ Memberful_Paywall_Config::LEGACY_FLAG_KEY => false,
+ );
}
/**
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
new file mode 100644
index 00000000..89bbe3aa
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
@@ -0,0 +1,8 @@
+ 'builder',
+ 'layout' => 'card',
+ 'heading' => esc_html__( 'Subscribe to keep reading', 'memberful' ),
+ 'heading_tag' => 'h2',
+ 'subheading' => esc_html__( 'This post is for paying subscribers.', 'memberful' ),
+ 'subheading_tag' => 'p',
+ 'features' => array(),
+ 'button_label' => esc_html__( 'Subscribe', 'memberful' ),
+ 'subscribe_url' => '',
+ 'sign_in_url' => '',
+ 'brand_color' => '#2f80ed',
+ 'button_shape' => 'rounded',
+ 'custom_css' => '',
+ );
+ }
+
+ /**
+ * Read the stored config merged over defaults.
+ *
+ * @return array
+ */
+ public static function get(): array {
+ $stored = get_option( self::OPTION_KEY, array() );
+
+ if ( ! is_array( $stored ) ) {
+ $stored = array();
+ }
+
+ return wp_parse_args( $stored, self::defaults() );
+ }
+
+ /**
+ * Validate, sanitize, and persist a config payload.
+ *
+ * @param array $input Raw input (typically from the options form).
+ *
+ * @return bool True when the option was updated, false when unchanged or on failure.
+ */
+ public static function save( array $input ): bool {
+ $clean = Memberful_Paywall_Sanitizer::sanitize( $input, self::defaults() );
+
+ return update_option( self::OPTION_KEY, $clean );
+ }
+}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
new file mode 100644
index 00000000..82eec48e
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
@@ -0,0 +1,71 @@
+ array( 'builder', 'custom_html' ),
+ 'layout' => array( 'simple', 'card', 'banner' ),
+ 'heading_tag' => array( 'h1', 'h2', 'h3' ),
+ 'subheading_tag' => array( 'p', 'h3', 'h4' ),
+ 'button_shape' => array( 'pill', 'rounded', 'square' ),
+ );
+
+ foreach ( $enums as $key => $allowed ) {
+ if ( isset( $input[ $key ] ) && in_array( $input[ $key ], $allowed, true ) ) {
+ $clean[ $key ] = $input[ $key ];
+ }
+ }
+
+ foreach ( array( 'heading', 'subheading', 'button_label' ) as $key ) {
+ if ( isset( $input[ $key ] ) ) {
+ $clean[ $key ] = sanitize_text_field( (string) $input[ $key ] );
+ }
+ }
+
+ if ( isset( $input['features'] ) ) {
+ $features = is_array( $input['features'] )
+ ? $input['features']
+ : preg_split( "/\r\n|\n|\r/", (string) $input['features'] );
+
+ $features = array_map( 'sanitize_text_field', (array) $features );
+ $features = array_map( 'trim', $features );
+ $features = array_values( array_filter( $features, 'strlen' ) );
+
+ $clean['features'] = $features;
+ }
+
+ foreach ( array( 'subscribe_url', 'sign_in_url' ) as $key ) {
+ if ( isset( $input[ $key ] ) ) {
+ $clean[ $key ] = esc_url_raw( (string) $input[ $key ] );
+ }
+ }
+
+ if ( isset( $input['brand_color'] ) ) {
+ $color = sanitize_hex_color( (string) $input['brand_color'] );
+ if ( null !== $color && '' !== $color ) {
+ $clean['brand_color'] = $color;
+ }
+ }
+
+ if ( isset( $input['custom_css'] ) ) {
+ $clean['custom_css'] = wp_strip_all_tags( (string) $input['custom_css'] );
+ }
+
+ return $clean;
+ }
+}
From 28c461e2d9dac5d4b3723b730649af8086168ace Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 21 Apr 2026 12:14:43 +1000
Subject: [PATCH 02/28] Render function + three layout templates
---
.../plugins/memberful-wp/src/paywall.php | 3 +-
.../memberful-wp/src/paywall/renderer.php | 260 ++++++++++++++++++
2 files changed, 262 insertions(+), 1 deletion(-)
create mode 100644 wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
index 89bbe3aa..b184a094 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
@@ -5,4 +5,5 @@
* @package memberful-wp
*/
require_once MEMBERFUL_DIR . '/src/paywall/sanitizer.php';
-require_once MEMBERFUL_DIR . '/src/paywall/config.php';
\ No newline at end of file
+require_once MEMBERFUL_DIR . '/src/paywall/config.php';
+require_once MEMBERFUL_DIR . '/src/paywall/renderer.php';
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
new file mode 100644
index 00000000..7ad224a6
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -0,0 +1,260 @@
+%s%s',
+ esc_attr( $layout ),
+ esc_attr( self::wrapper_style( $config ) ),
+ $body,
+ self::custom_css_block( $config )
+ );
+ }
+
+ /**
+ * Render the "simple" layout.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function render_simple( array $config ): string {
+ return self::heading_block( $config )
+ . self::subheading_block( $config )
+ . self::features_block( $config )
+ . '' . self::primary_cta( $config ) . '
';
+ }
+
+ /**
+ * Render the "card" layout.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function render_card( array $config ): string {
+ return ''
+ . self::heading_block( $config )
+ . self::subheading_block( $config )
+ . self::features_block( $config )
+ . '
'
+ . self::primary_cta( $config )
+ . self::secondary_cta( $config )
+ . '
'
+ . '
';
+ }
+
+ /**
+ * Render the "banner" layout.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function render_banner( array $config ): string {
+ return ''
+ . self::heading_block( $config )
+ . self::subheading_block( $config )
+ . self::features_block( $config )
+ . '
'
+ . '' . self::primary_cta( $config ) . '
';
+ }
+
+ /**
+ * Heading element with the configured tag.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function heading_block( array $config ): string {
+ $tag = in_array( $config['heading_tag'], self::HEADING_TAGS, true ) ? $config['heading_tag'] : 'h2';
+ return sprintf(
+ '<%1$s class="memberful-paywall__heading">%2$s%1$s>',
+ tag_escape( $tag ),
+ esc_html( $config['heading'] )
+ );
+ }
+
+ /**
+ * Subheading element with the configured tag, or empty when blank.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function subheading_block( array $config ): string {
+ if ( '' === $config['subheading'] ) {
+ return '';
+ }
+
+ $tag = in_array( $config['subheading_tag'], self::SUBHEADING_TAGS, true ) ? $config['subheading_tag'] : 'p';
+ return sprintf(
+ '<%1$s class="memberful-paywall__subheading">%2$s%1$s>',
+ tag_escape( $tag ),
+ esc_html( $config['subheading'] )
+ );
+ }
+
+ /**
+ * Feature list with inline check icons, or empty when no features.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function features_block( array $config ): string {
+ if ( empty( $config['features'] ) ) {
+ return '';
+ }
+
+ $items = '';
+ foreach ( $config['features'] as $feature ) {
+ $items .= '' . self::check_icon() . '' . esc_html( $feature ) . ' ';
+ }
+
+ return '';
+ }
+
+ /**
+ * Primary subscribe CTA anchor.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function primary_cta( array $config ): string {
+ return sprintf(
+ '%s ',
+ esc_url( self::subscribe_url( $config ) ),
+ esc_html( $config['button_label'] )
+ );
+ }
+
+ /**
+ * Secondary sign-in CTA anchor (card layout only).
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function secondary_cta( array $config ): string {
+ return sprintf(
+ '%s ',
+ esc_url( self::sign_in_url( $config ) ),
+ esc_html__( 'Sign in', 'memberful' )
+ );
+ }
+
+ /**
+ * Wrapper inline style carrying the brand colour and button radius custom properties.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function wrapper_style( array $config ): string {
+ return sprintf(
+ '--mf-brand:%s;--mf-radius:%s;',
+ $config['brand_color'],
+ self::button_radius( $config['button_shape'] )
+ );
+ }
+
+ /**
+ * Map the button-shape enum to a CSS radius.
+ *
+ * @param string $shape One of the `button_shape` enum values.
+ *
+ * @return string
+ */
+ private static function button_radius( string $shape ): string {
+ switch ( $shape ) {
+ case 'pill':
+ return '999px';
+ case 'square':
+ return '0';
+ case 'rounded':
+ default:
+ return '8px';
+ }
+ }
+
+ /**
+ * Custom CSS ';
+ }
+
+ /**
+ * Resolve the subscribe URL, falling back to the Memberful registration page.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function subscribe_url( array $config ): string {
+ return ! empty( $config['subscribe_url'] ) ? $config['subscribe_url'] : memberful_registration_page_url();
+ }
+
+ /**
+ * Resolve the sign-in URL, falling back to the Memberful sign-in endpoint.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function sign_in_url( array $config ): string {
+ return ! empty( $config['sign_in_url'] ) ? $config['sign_in_url'] : memberful_sign_in_url();
+ }
+
+ /**
+ * Inline check-mark SVG used in the features list.
+ *
+ * @return string
+ */
+ private static function check_icon(): string {
+ return ''
+ . ' '
+ . ' ';
+ }
+}
\ No newline at end of file
From df0ee893888ac1adcba7aea5be312d5d24ec91db Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 21 Apr 2026 13:01:27 +1000
Subject: [PATCH 03/28] Prepare render for paywal config
---
.../plugins/memberful-wp/src/admin.php | 60 +++++++++++--------
.../memberful-wp/src/global_marketing.php | 33 +++++++---
2 files changed, 58 insertions(+), 35 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/admin.php b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
index fa18a503..698a86d9 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/admin.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
@@ -687,32 +687,40 @@ function memberful_wp_add_protected_state_to_post_list($states, $post) {
}
function memberful_wp_global_marketing() {
- if ( isset( $_POST['save_global_marketing'] ) && memberful_wp_valid_nonce( 'memberful_options' ) ) {
- if ( isset( $_POST['memberful_use_global_marketing'] ) ) {
- update_option( 'memberful_use_global_marketing', true );
- update_option( 'memberful_global_marketing_override', filter_input( INPUT_POST, 'memberful_global_marketing_override', FILTER_SANITIZE_NUMBER_INT ) );
- update_option( 'memberful_global_marketing_content', memberful_wp_kses_post( filter_input( INPUT_POST, 'memberful_global_marketing_content' ) ) );
- update_option( 'memberful_use_global_snippets', (int) isset($_POST['memberful_use_global_snippets']));
- } else {
- update_option( 'memberful_use_global_marketing', false );
- }
- }
-
- $use_global_marketing = get_option( 'memberful_use_global_marketing' );
- $use_global_snippets = get_option( 'memberful_use_global_snippets');
- $global_marketing_content = get_option( 'memberful_global_marketing_content' );
- $global_marketing_override = get_option( 'memberful_global_marketing_override', true );
-
- memberful_wp_render(
- 'global_marketing',
- array(
- 'use_global_marketing' => $use_global_marketing,
- 'use_global_snippets' => $use_global_snippets,
- 'global_marketing_content' => $global_marketing_content,
- 'global_marketing_override' => $global_marketing_override,
- 'form_target' => memberful_wp_plugin_global_marketing_url()
- )
- );
+ if ( isset( $_POST['save_global_marketing'] ) && memberful_wp_valid_nonce( 'memberful_options' ) ) {
+ if ( isset( $_POST['memberful_use_global_marketing'] ) ) {
+ update_option( 'memberful_use_global_marketing', true );
+ update_option( 'memberful_global_marketing_override', filter_input( INPUT_POST, 'memberful_global_marketing_override', FILTER_SANITIZE_NUMBER_INT ) );
+ update_option( 'memberful_use_global_snippets', (int) isset( $_POST['memberful_use_global_snippets'] ) );
+
+ $paywall_input = filter_input( INPUT_POST, 'memberful_paywall', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
+ if ( is_array( $paywall_input ) && ! empty( $paywall_input ) ) {
+ Memberful_Paywall_Config::save( $paywall_input );
+ }
+
+ if ( 'custom_html' === Memberful_Paywall_Config::get()['mode'] ) {
+ update_option( 'memberful_global_marketing_content', memberful_wp_kses_post( filter_input( INPUT_POST, 'memberful_global_marketing_content' ) ) );
+ }
+ } else {
+ update_option( 'memberful_use_global_marketing', false );
+ }
+ }
+
+ $use_global_marketing = get_option( 'memberful_use_global_marketing' );
+ $use_global_snippets = get_option( 'memberful_use_global_snippets');
+ $global_marketing_content = get_option( 'memberful_global_marketing_content' );
+ $global_marketing_override = get_option( 'memberful_global_marketing_override', true );
+
+ memberful_wp_render(
+ 'global_marketing',
+ array(
+ 'use_global_marketing' => $use_global_marketing,
+ 'use_global_snippets' => $use_global_snippets,
+ 'global_marketing_content' => $global_marketing_content,
+ 'global_marketing_override' => $global_marketing_override,
+ 'form_target' => memberful_wp_plugin_global_marketing_url()
+ )
+ );
}
/**
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
index ed1598c9..99442ee4 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
@@ -17,18 +17,33 @@
* @return string
*/
function memberful_get_global_replacement($marketing_content){
- $override = get_option( 'memberful_global_marketing_override' );
- $global_marketing_content = get_option( 'memberful_global_marketing_content' );
+ $override = get_option( 'memberful_global_marketing_override' );
+ $global_marketing_content = memberful_wp_resolve_global_marketing_content();
- if($override) {
- return $global_marketing_content;
- }
+ if ( $override ) {
+ return $global_marketing_content;
+ }
- if(empty(trim($marketing_content))){
- return $global_marketing_content;
- }
+ if ( empty( trim( $marketing_content ) ) ) {
+ return $global_marketing_content;
+ }
+
+ return $marketing_content;
+}
+
+/**
+ * Resolve the global marketing HTML from whichever source the paywall config points to.
+ *
+ * @return string
+ */
+function memberful_wp_resolve_global_marketing_content(): string {
+ $config = Memberful_Paywall_Config::get();
+
+ if ( 'builder' === $config['mode'] ) {
+ return Memberful_Paywall_Renderer::render( $config );
+ }
- return $marketing_content;
+ return (string) get_option( 'memberful_global_marketing_content' );
}
/**
From d335a333dc64e6870b96880beb5ee2ab3eb8b5c1 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 21 Apr 2026 16:41:01 +1000
Subject: [PATCH 04/28] Add builder UI
---
.../memberful-wp/js/src/paywall-builder.js | 24 ++++
.../plugins/memberful-wp/src/admin.php | 23 +++-
.../plugins/memberful-wp/src/options.php | 1 -
.../plugins/memberful-wp/src/paywall.php | 5 +-
.../memberful-wp/src/paywall/config.php | 29 +++-
.../memberful-wp/src/paywall/renderer.php | 12 +-
.../memberful-wp/src/paywall/sanitizer.php | 10 +-
.../memberful-wp/stylesheets/admin.css | 35 +++++
.../memberful-wp/views/global_marketing.php | 124 +++++++++---------
.../views/paywall/builder-panel.php | 113 ++++++++++++++++
.../views/paywall/custom-html-panel.php | 13 ++
.../memberful-wp/views/paywall/mode-radio.php | 21 +++
.../plugins/memberful-wp/webpack.config.js | 1 +
13 files changed, 323 insertions(+), 88 deletions(-)
create mode 100644 wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
create mode 100644 wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
create mode 100644 wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
create mode 100644 wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
new file mode 100644
index 00000000..21d34b36
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -0,0 +1,24 @@
+/**
+ * Paywall builder admin script.
+ */
+
+jQuery(function ($) {
+ $('.memberful-paywall-builder__color').wpColorPicker();
+
+ const $modeInputs = $('input[name="memberful_paywall[mode]"]');
+ const $panels = $('.memberful-paywall-builder__panel');
+
+ function applyMode(mode) {
+ $panels.each(function () {
+ this.style.display = this.dataset.panel === mode ? '' : 'none';
+ });
+ }
+
+ $modeInputs.on('change', function () {
+ if (this.checked) {
+ applyMode(this.value);
+ }
+ });
+
+ applyMode($modeInputs.filter(':checked').val() || 'builder');
+});
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/admin.php b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
index 698a86d9..04cf6ff8 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/admin.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
@@ -127,6 +127,20 @@ function memberful_wp_admin_enqueue_scripts() {
);
}
+ if (
+ 'memberful_options' === filter_input( INPUT_GET, 'page' )
+ && 'global_marketing' === filter_input( INPUT_GET, 'subpage' )
+ ) {
+ wp_enqueue_style( 'wp-color-picker' );
+ wp_enqueue_script(
+ 'memberful-paywall-builder',
+ MEMBERFUL_URL . '/js/build/paywall-builder.js',
+ array( 'jquery', 'wp-color-picker' ),
+ MEMBERFUL_VERSION,
+ true
+ );
+ }
+
wp_enqueue_script(
'memberful-menu',
plugins_url( 'js/src/menu.js', dirname( __FILE__ ) ),
@@ -714,11 +728,12 @@ function memberful_wp_global_marketing() {
memberful_wp_render(
'global_marketing',
array(
- 'use_global_marketing' => $use_global_marketing,
- 'use_global_snippets' => $use_global_snippets,
- 'global_marketing_content' => $global_marketing_content,
+ 'use_global_marketing' => $use_global_marketing,
+ 'use_global_snippets' => $use_global_snippets,
+ 'global_marketing_content' => $global_marketing_content,
'global_marketing_override' => $global_marketing_override,
- 'form_target' => memberful_wp_plugin_global_marketing_url()
+ 'paywall_config' => Memberful_Paywall_Config::get(),
+ 'form_target' => memberful_wp_plugin_global_marketing_url(),
)
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/options.php b/wordpress/wp-content/plugins/memberful-wp/src/options.php
index 39a611c8..e6acbf47 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/options.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/options.php
@@ -30,7 +30,6 @@ function memberful_wp_all_options(): array {
'memberful_global_marketing_content' => '',
'memberful_ad_provider_settings' => array(),
Memberful_Paywall_Config::OPTION_KEY => array(),
- Memberful_Paywall_Config::LEGACY_FLAG_KEY => false,
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
index b184a094..b2d46855 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
@@ -4,6 +4,7 @@
*
* @package memberful-wp
*/
-require_once MEMBERFUL_DIR . '/src/paywall/sanitizer.php';
+
require_once MEMBERFUL_DIR . '/src/paywall/config.php';
-require_once MEMBERFUL_DIR . '/src/paywall/renderer.php';
\ No newline at end of file
+require_once MEMBERFUL_DIR . '/src/paywall/sanitizer.php';
+require_once MEMBERFUL_DIR . '/src/paywall/renderer.php';
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index d6ffa8fd..6d099ef0 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -5,8 +5,13 @@
* @package memberful-wp
*/
class Memberful_Paywall_Config {
- const OPTION_KEY = 'memberful_paywall_config';
- const LEGACY_FLAG_KEY = 'memberful_paywall_legacy_detected';
+ const OPTION_KEY = 'memberful_paywall_config';
+
+ const MODES = array( 'builder', 'custom_html' );
+ const LAYOUTS = array( 'simple', 'card', 'banner' );
+ const HEADING_TAGS = array( 'h1', 'h2', 'h3' );
+ const SUBHEADING_TAGS = array( 'p', 'h3', 'h4' );
+ const BUTTON_SHAPES = array( 'pill', 'rounded', 'square' );
/**
* Canonical default configuration shape.
@@ -34,6 +39,10 @@ public static function defaults(): array {
/**
* Read the stored config merged over defaults.
*
+ * On sites with legacy custom HTML in memberful_global_marketing_content and no stored builder config yet, the
+ * default mode swaps to custom_html so the existing content keeps rendering untouched. Once the user saves any
+ * config, the stored value wins and this check short-circuits.
+ *
* @return array
*/
public static function get(): array {
@@ -43,7 +52,21 @@ public static function get(): array {
$stored = array();
}
- return wp_parse_args( $stored, self::defaults() );
+ $defaults = self::defaults();
+ if ( empty( $stored ) && self::has_legacy_content() ) {
+ $defaults['mode'] = 'custom_html';
+ }
+
+ return wp_parse_args( $stored, $defaults );
+ }
+
+ /**
+ * Whether the legacy marketing content option is populated.
+ *
+ * @return bool
+ */
+ private static function has_legacy_content(): bool {
+ return '' !== trim( (string) get_option( 'memberful_global_marketing_content' ) );
}
/**
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index 7ad224a6..91e5c3af 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -13,10 +13,6 @@
* Renders a paywall config array to HTML.
*/
class Memberful_Paywall_Renderer {
- const HEADING_TAGS = array( 'h1', 'h2', 'h3' );
- const SUBHEADING_TAGS = array( 'p', 'h3', 'h4' );
- const LAYOUTS = array( 'simple', 'card', 'banner' );
-
/**
* Render a paywall config to HTML.
*
@@ -27,7 +23,7 @@ class Memberful_Paywall_Renderer {
public static function render( array $config ): string {
$config = wp_parse_args( $config, Memberful_Paywall_Config::defaults() );
- $layout = in_array( $config['layout'], self::LAYOUTS, true ) ? $config['layout'] : 'card';
+ $layout = in_array( $config['layout'], Memberful_Paywall_Config::LAYOUTS, true ) ? $config['layout'] : 'card';
$method = 'render_' . $layout;
$body = self::$method( $config );
@@ -98,7 +94,7 @@ private static function render_banner( array $config ): string {
* @return string
*/
private static function heading_block( array $config ): string {
- $tag = in_array( $config['heading_tag'], self::HEADING_TAGS, true ) ? $config['heading_tag'] : 'h2';
+ $tag = in_array( $config['heading_tag'], Memberful_Paywall_Config::HEADING_TAGS, true ) ? $config['heading_tag'] : 'h2';
return sprintf(
'<%1$s class="memberful-paywall__heading">%2$s%1$s>',
tag_escape( $tag ),
@@ -118,7 +114,7 @@ private static function subheading_block( array $config ): string {
return '';
}
- $tag = in_array( $config['subheading_tag'], self::SUBHEADING_TAGS, true ) ? $config['subheading_tag'] : 'p';
+ $tag = in_array( $config['subheading_tag'], Memberful_Paywall_Config::SUBHEADING_TAGS, true ) ? $config['subheading_tag'] : 'p';
return sprintf(
'<%1$s class="memberful-paywall__subheading">%2$s%1$s>',
tag_escape( $tag ),
@@ -257,4 +253,4 @@ private static function check_icon(): string {
. ' '
. '';
}
-}
\ No newline at end of file
+}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
index 82eec48e..e6e17384 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
@@ -18,11 +18,11 @@ public static function sanitize( array $input, array $defaults ): array {
$clean = $defaults;
$enums = array(
- 'mode' => array( 'builder', 'custom_html' ),
- 'layout' => array( 'simple', 'card', 'banner' ),
- 'heading_tag' => array( 'h1', 'h2', 'h3' ),
- 'subheading_tag' => array( 'p', 'h3', 'h4' ),
- 'button_shape' => array( 'pill', 'rounded', 'square' ),
+ 'mode' => Memberful_Paywall_Config::MODES,
+ 'layout' => Memberful_Paywall_Config::LAYOUTS,
+ 'heading_tag' => Memberful_Paywall_Config::HEADING_TAGS,
+ 'subheading_tag' => Memberful_Paywall_Config::SUBHEADING_TAGS,
+ 'button_shape' => Memberful_Paywall_Config::BUTTON_SHAPES,
);
foreach ( $enums as $key => $allowed ) {
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 314e67b2..45cce5d4 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -270,3 +270,38 @@ Ad Provider Settings
.memberful-ad-provider-settings > div {
margin-left: 1rem;
}
+
+/*---------------------------------------------------------
+Paywall Builder
+------------------------------------------------------------ */
+.memberful-paywall-builder {
+ border-top: 1px solid #dcdcde;
+ margin-top: 2rem;
+ padding-top: 1.5rem;
+}
+.memberful-paywall-builder__mode {
+ margin-bottom: 1rem;
+}
+.memberful-paywall-builder__mode label {
+ margin-right: 1.25rem;
+}
+.memberful-paywall-builder__legend {
+ font-weight: 600;
+ margin-bottom: 0.35rem;
+}
+.memberful-paywall-builder__layout {
+ margin: 1rem 0;
+}
+.memberful-paywall-builder__layout label {
+ margin-right: 1.25rem;
+}
+.memberful-paywall-builder__fields th {
+ width: 160px;
+}
+.memberful-paywall-builder__fields .regular-text,
+.memberful-paywall-builder__fields .large-text {
+ max-width: 480px;
+}
+.memberful-paywall-builder__fields select {
+ margin-left: 0.5rem;
+}
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
index 7b8225d3..4e647f30 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
@@ -1,72 +1,66 @@
-
- 'global_marketing' ) ); ?>
-
-
-
-
+
+
+
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
new file mode 100644
index 00000000..b74073a4
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -0,0 +1,113 @@
+ __( 'Simple', 'memberful' ),
+ 'card' => __( 'Card', 'memberful' ),
+ 'banner' => __( 'Banner', 'memberful' ),
+);
+
+$button_shape_labels = array(
+ 'pill' => __( 'Pill', 'memberful' ),
+ 'rounded' => __( 'Rounded', 'memberful' ),
+ 'square' => __( 'Square', 'memberful' ),
+);
+
+$features_textarea = implode( "\n", (array) $paywall_config['features'] );
+?>
+
+
+
+
+
+ >
+
+
+
+
+
+
+
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
new file mode 100644
index 00000000..f3645318
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
new file mode 100644
index 00000000..a1cbf461
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
@@ -0,0 +1,21 @@
+
+
+
+
+ >
+
+
+
+ >
+
+
+
diff --git a/wordpress/wp-content/plugins/memberful-wp/webpack.config.js b/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
index b32e4222..9a57145c 100644
--- a/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
+++ b/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
@@ -5,5 +5,6 @@ module.exports = {
entry: {
...defaultConfig.entry,
"editor-scripts": "./js/src/editor-scripts.js",
+ "paywall-builder": "./js/src/paywall-builder.js",
},
};
From dc8d45693581982bddb3aa9d31de7efde7265c9a Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 21 Apr 2026 18:25:30 +1000
Subject: [PATCH 05/28] Fix phpcs warnings
---
.../wp-content/plugins/memberful-wp/src/paywall/config.php | 4 ++++
.../plugins/memberful-wp/src/paywall/renderer.php | 2 +-
.../plugins/memberful-wp/src/paywall/sanitizer.php | 6 +++++-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index 6d099ef0..7a1ad9b7 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -4,6 +4,10 @@
*
* @package memberful-wp
*/
+
+/**
+ * Class Memberful_Paywall_Config
+ */
class Memberful_Paywall_Config {
const OPTION_KEY = 'memberful_paywall_config';
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index 91e5c3af..0e82aa72 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -1,6 +1,6 @@
Date: Wed, 22 Apr 2026 11:22:16 +1000
Subject: [PATCH 06/28] Boilerplate paywal preview
---
.../memberful-wp/js/src/paywall-builder.js | 107 +++++++++--
.../plugins/memberful-wp/src/admin.php | 40 +++--
.../plugins/memberful-wp/src/paywall.php | 8 +-
.../memberful-wp/src/paywall/preview.php | 84 +++++++++
.../memberful-wp/stylesheets/paywall.css | 166 ++++++++++++++++++
.../views/paywall/builder-panel.php | 10 ++
6 files changed, 390 insertions(+), 25 deletions(-)
create mode 100644 wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
create mode 100644 wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index 21d34b36..99e49d68 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -1,12 +1,24 @@
-/**
- * Paywall builder admin script.
- */
-
jQuery(function ($) {
- $('.memberful-paywall-builder__color').wpColorPicker();
-
+ const $form = $('.memberful-paywall-builder__panel[data-panel="builder"]');
const $modeInputs = $('input[name="memberful_paywall[mode]"]');
- const $panels = $('.memberful-paywall-builder__panel');
+ const $panels = $('.memberful-paywall-builder__panel');
+ const $preview = $('#mf-paywall-preview');
+ const $colorInput = $('.memberful-paywall-builder__color');
+
+ const preview = window.memberfulPaywallPreview || {};
+ const DEBOUNCE_MS = 250;
+
+ let debounceTimer = null;
+ let requestSeq = 0;
+
+ $colorInput.wpColorPicker({
+ change: function () {
+ setTimeout(scheduleRefresh, 0);
+ },
+ clear: function () {
+ setTimeout(scheduleRefresh, 0);
+ },
+ });
function applyMode(mode) {
$panels.each(function () {
@@ -15,10 +27,85 @@ jQuery(function ($) {
}
$modeInputs.on('change', function () {
- if (this.checked) {
- applyMode(this.value);
+ if (!this.checked) {
+ return;
}
+
+ applyMode(this.value);
+ refreshPreview();
});
applyMode($modeInputs.filter(':checked').val() || 'builder');
-});
\ No newline at end of file
+
+ function collectConfig() {
+ return {
+ mode: $('input[name="memberful_paywall[mode]"]:checked').val() || 'builder',
+ layout: $('input[name="memberful_paywall[layout]"]:checked').val() || 'card',
+ heading: $('#memberful-paywall-heading').val() || '',
+ heading_tag: $('#memberful-paywall-heading-tag').val() || 'h2',
+ subheading: $('#memberful-paywall-subheading').val() || '',
+ subheading_tag: $('#memberful-paywall-subheading-tag').val() || 'p',
+ features: $('#memberful-paywall-features').val() || '',
+ button_label: $('#memberful-paywall-button-label').val() || '',
+ subscribe_url: $('#memberful-paywall-subscribe-url').val() || '',
+ sign_in_url: $('#memberful-paywall-signin-url').val() || '',
+ brand_color: $colorInput.val() || '',
+ button_shape: $('#memberful-paywall-button-shape').val() || 'rounded',
+ custom_css: $('#memberful-paywall-custom-css').val() || '',
+ };
+ }
+
+ function refreshPreview() {
+ if (!preview.ajaxUrl || !preview.action || !preview.nonce || !$preview.length) {
+ return;
+ }
+
+ clearTimeout(debounceTimer);
+
+ const seq = ++requestSeq;
+ const body = new URLSearchParams();
+ body.set('action', preview.action);
+ body.set('nonce', preview.nonce);
+
+ const config = collectConfig();
+ Object.keys(config).forEach(function (key) {
+ body.append('config[' + key + ']', config[key]);
+ });
+
+ fetch(preview.ajaxUrl, {
+ method: 'POST',
+ credentials: 'same-origin',
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
+ body: body.toString(),
+ })
+ .then(function (res) {
+ if (!res.ok) {
+ throw new Error('Request failed: ' + res.status);
+ }
+
+ return res.json();
+ })
+ .then(function (json) {
+ if (seq !== requestSeq) {
+ return;
+ }
+
+ if (json && json.success && json.data && json.data.html) {
+ $preview.attr('srcdoc', json.data.html);
+ }
+ })
+ .catch(function (err) {
+ console.error('Paywall preview failed', err);
+ });
+ }
+
+ function scheduleRefresh() {
+ clearTimeout(debounceTimer);
+ debounceTimer = setTimeout(refreshPreview, DEBOUNCE_MS);
+ }
+
+ $form.on('input', 'input[type="text"], input[type="url"], textarea', scheduleRefresh);
+ $form.on('change', 'input[type="radio"], select', refreshPreview);
+
+ refreshPreview();
+});
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/admin.php b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
index 04cf6ff8..219462a9 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/admin.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
@@ -127,19 +127,33 @@ function memberful_wp_admin_enqueue_scripts() {
);
}
- if (
- 'memberful_options' === filter_input( INPUT_GET, 'page' )
- && 'global_marketing' === filter_input( INPUT_GET, 'subpage' )
- ) {
- wp_enqueue_style( 'wp-color-picker' );
- wp_enqueue_script(
- 'memberful-paywall-builder',
- MEMBERFUL_URL . '/js/build/paywall-builder.js',
- array( 'jquery', 'wp-color-picker' ),
- MEMBERFUL_VERSION,
- true
- );
- }
+ if (
+ 'memberful_options' === filter_input( INPUT_GET, 'page' )
+ && 'global_marketing' === filter_input( INPUT_GET, 'subpage' )
+ ) {
+ wp_enqueue_style( 'wp-color-picker' );
+
+ wp_enqueue_style(
+ 'memberful-paywall',
+ MEMBERFUL_URL . '/stylesheets/paywall.css',
+ array(),
+ MEMBERFUL_VERSION
+ );
+
+ wp_enqueue_script(
+ 'memberful-paywall-builder',
+ MEMBERFUL_URL . '/js/build/paywall-builder.js',
+ array( 'jquery', 'wp-color-picker' ),
+ MEMBERFUL_VERSION,
+ true
+ );
+
+ wp_localize_script(
+ 'memberful-paywall-builder',
+ 'memberfulPaywallPreview',
+ Memberful_Paywall_Preview::script_args()
+ );
+ }
wp_enqueue_script(
'memberful-menu',
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
index b2d46855..366ac454 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
@@ -1,10 +1,14 @@
'forbidden' ), 403 );
+ }
+
+ $raw = filter_input( INPUT_POST, 'config', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
+ $raw = is_array( $raw ) ? $raw : array();
+
+ $config = Memberful_Paywall_Sanitizer::sanitize( $raw, Memberful_Paywall_Config::defaults() );
+
+ wp_send_json_success( array( 'html' => self::document( $config ) ) );
+ }
+
+ /**
+ * Wrap the rendered paywall HTML in a minimal document suitable for an iframe.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ public static function document( array $config ): string {
+ $body = Memberful_Paywall_Renderer::render( $config );
+
+ $paywall_css = plugins_url( 'stylesheets/paywall.css', MEMBERFUL_PLUGIN_FILE );
+ $theme_css = get_stylesheet_uri();
+
+ $links = sprintf( ' ', esc_url( $paywall_css ) );
+ if ( ! empty( $theme_css ) ) {
+ $links .= sprintf( ' ', esc_url( $theme_css ) );
+ }
+
+ return ''
+ . ''
+ . ''
+ . ' '
+ . ' '
+ . $links
+ . ''
+ . ''
+ . '' . $body . ''
+ . '';
+ }
+
+ /**
+ * AJAX args passed to the builder JS via wp_localize_script.
+ *
+ * @return array
+ */
+ public static function script_args(): array {
+ return array(
+ 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
+ 'action' => self::ACTION,
+ 'nonce' => wp_create_nonce( self::NONCE_KEY ),
+ );
+ }
+}
+
+Memberful_Paywall_Preview::register();
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
new file mode 100644
index 00000000..0f7ed2ed
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -0,0 +1,166 @@
+/**
+ * Memberful paywall — frontend + admin preview styles.
+ */
+
+.memberful-paywall {
+ --mf-brand: #2f80ed;
+ --mf-radius: 8px;
+ --mf-text: #1b1b1b;
+ --mf-muted: #555;
+ --mf-surface: #fff;
+ --mf-border: rgba(0, 0, 0, 0.08);
+
+ box-sizing: border-box;
+ color: var(--mf-text);
+ font-family: inherit;
+ line-height: 1.5;
+ margin: 1.5em auto;
+ max-width: 640px;
+ padding: 2rem;
+}
+
+.memberful-paywall *,
+.memberful-paywall *::before,
+.memberful-paywall *::after {
+ box-sizing: border-box;
+}
+
+.memberful-paywall__heading {
+ font-weight: 700;
+ line-height: 1.2;
+ margin: 0 0 0.5rem;
+}
+
+.memberful-paywall__subheading {
+ color: var(--mf-muted);
+ margin: 0 0 1.25rem;
+}
+
+.memberful-paywall__features {
+ list-style: none;
+ margin: 0 0 1.5rem;
+ padding: 0;
+}
+
+.memberful-paywall__features li {
+ align-items: flex-start;
+ display: flex;
+ gap: 0.5rem;
+ padding: 0.25rem 0;
+}
+
+.memberful-paywall__check {
+ color: var(--mf-brand);
+ flex: 0 0 auto;
+ margin-top: 0.2rem;
+}
+
+.memberful-paywall__actions {
+ align-items: center;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+}
+
+.memberful-paywall__button {
+ border-radius: var(--mf-radius);
+ display: inline-block;
+ font-weight: 600;
+ line-height: 1.2;
+ padding: 0.6rem 1.25rem;
+ text-decoration: none;
+ transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
+}
+
+.memberful-paywall__button--primary {
+ background: var(--mf-brand);
+ color: #fff;
+ border: 1px solid var(--mf-brand);
+}
+
+.memberful-paywall__button--primary:hover,
+.memberful-paywall__button--primary:focus {
+ filter: brightness(0.92);
+}
+
+.memberful-paywall__button--secondary {
+ background: transparent;
+ border: 1px solid var(--mf-border);
+ color: var(--mf-brand);
+}
+
+.memberful-paywall__button--secondary:hover,
+.memberful-paywall__button--secondary:focus {
+ border-color: var(--mf-brand);
+}
+
+/* Simple layout - left aligned stack, no surface. */
+.memberful-paywall--simple {
+ text-align: left;
+}
+
+/* Card layout - centred card with border and shadow. */
+.memberful-paywall--card {
+ text-align: center;
+}
+
+.memberful-paywall--card .memberful-paywall__card {
+ background: var(--mf-surface);
+ border: 1px solid var(--mf-border);
+ border-radius: calc(var(--mf-radius) + 4px);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
+ padding: 2rem;
+}
+
+.memberful-paywall--card .memberful-paywall__features {
+ display: inline-block;
+ text-align: left;
+}
+
+.memberful-paywall--card .memberful-paywall__actions {
+ justify-content: center;
+}
+
+/* Banner layout - heading/features on the left, CTA on the right. */
+.memberful-paywall--banner {
+ align-items: center;
+ background: var(--mf-surface);
+ border: 1px solid var(--mf-border);
+ border-radius: var(--mf-radius);
+ display: flex;
+ gap: 1.5rem;
+ max-width: none;
+ padding: 1.5rem 2rem;
+}
+
+.memberful-paywall--banner .memberful-paywall__banner-text {
+ flex: 1 1 auto;
+ min-width: 0;
+}
+
+.memberful-paywall--banner .memberful-paywall__heading {
+ font-size: 1.15rem;
+}
+
+.memberful-paywall--banner .memberful-paywall__subheading {
+ margin-bottom: 0;
+}
+
+.memberful-paywall--banner .memberful-paywall__actions {
+ flex: 0 0 auto;
+}
+
+@media (max-width: 768px) {
+ .memberful-paywall--banner {
+ align-items: stretch;
+ flex-direction: column;
+ }
+
+ .memberful-paywall--banner .memberful-paywall__features {
+ display: none;
+ }
+
+ .memberful-paywall--banner .memberful-paywall__actions {
+ justify-content: center;
+ }
+}
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index b74073a4..2244d86e 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -110,4 +110,14 @@
+
+
+
+
+
From 28a92308e111e6e31a8e9fe7b8ed157eed35eac0 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 22 Apr 2026 12:20:57 +1000
Subject: [PATCH 07/28] Drop custom CSS field from the builder
---
.../memberful-wp/js/src/paywall-builder.js | 1 -
.../memberful-wp/src/paywall/config.php | 1 -
.../memberful-wp/src/paywall/renderer.php | 20 ++-----------------
.../memberful-wp/src/paywall/sanitizer.php | 4 ----
.../views/paywall/builder-panel.php | 7 -------
5 files changed, 2 insertions(+), 31 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index 99e49d68..e1aa211a 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -51,7 +51,6 @@ jQuery(function ($) {
sign_in_url: $('#memberful-paywall-signin-url').val() || '',
brand_color: $colorInput.val() || '',
button_shape: $('#memberful-paywall-button-shape').val() || 'rounded',
- custom_css: $('#memberful-paywall-custom-css').val() || '',
};
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index 7a1ad9b7..243f31ef 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -36,7 +36,6 @@ public static function defaults(): array {
'sign_in_url' => '',
'brand_color' => '#2f80ed',
'button_shape' => 'rounded',
- 'custom_css' => '',
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index 0e82aa72..e5c31bf1 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -29,11 +29,10 @@ public static function render( array $config ): string {
$body = self::$method( $config );
return sprintf(
- '%s
%s',
+ '%s
',
esc_attr( $layout ),
esc_attr( self::wrapper_style( $config ) ),
- $body,
- self::custom_css_block( $config )
+ $body
);
}
@@ -206,21 +205,6 @@ private static function button_radius( string $shape ): string {
}
}
- /**
- * Custom CSS ';
- }
-
/**
* Resolve the subscribe URL, falling back to the Memberful registration page.
*
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
index 1304890b..9332dfa9 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
@@ -66,10 +66,6 @@ public static function sanitize( array $input, array $defaults ): array {
}
}
- if ( isset( $input['custom_css'] ) ) {
- $clean['custom_css'] = wp_strip_all_tags( (string) $input['custom_css'] );
- }
-
return $clean;
}
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 2244d86e..7ffb07a3 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -101,13 +101,6 @@
-
-
-
-
-
-
-
From e8a69d869c1cdb594ad6320c6f362edf1c240e9a Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 22 Apr 2026 13:20:20 +1000
Subject: [PATCH 08/28] Adjust UI/UX
---
.../memberful-wp/js/src/paywall-builder.js | 1 -
.../memberful-wp/src/paywall/config.php | 32 ++-
.../memberful-wp/src/paywall/renderer.php | 6 +-
.../memberful-wp/src/paywall/sanitizer.php | 9 +-
.../memberful-wp/stylesheets/admin.css | 232 ++++++++++++++++--
.../memberful-wp/views/global_marketing.php | 2 +-
.../views/paywall/builder-panel.php | 202 ++++++++-------
.../memberful-wp/views/paywall/mode-radio.php | 22 +-
8 files changed, 358 insertions(+), 148 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index e1aa211a..75d2d89a 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -44,7 +44,6 @@ jQuery(function ($) {
heading: $('#memberful-paywall-heading').val() || '',
heading_tag: $('#memberful-paywall-heading-tag').val() || 'h2',
subheading: $('#memberful-paywall-subheading').val() || '',
- subheading_tag: $('#memberful-paywall-subheading-tag').val() || 'p',
features: $('#memberful-paywall-features').val() || '',
button_label: $('#memberful-paywall-button-label').val() || '',
subscribe_url: $('#memberful-paywall-subscribe-url').val() || '',
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index 243f31ef..8a38b2df 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -11,11 +11,10 @@
class Memberful_Paywall_Config {
const OPTION_KEY = 'memberful_paywall_config';
- const MODES = array( 'builder', 'custom_html' );
- const LAYOUTS = array( 'simple', 'card', 'banner' );
- const HEADING_TAGS = array( 'h1', 'h2', 'h3' );
- const SUBHEADING_TAGS = array( 'p', 'h3', 'h4' );
- const BUTTON_SHAPES = array( 'pill', 'rounded', 'square' );
+ const MODES = array( 'builder', 'custom_html' );
+ const LAYOUTS = array( 'simple', 'card', 'banner' );
+ const HEADING_TAGS = array( 'h1', 'h2', 'h3' );
+ const BUTTON_SHAPES = array( 'pill', 'rounded', 'square' );
/**
* Canonical default configuration shape.
@@ -24,18 +23,17 @@ class Memberful_Paywall_Config {
*/
public static function defaults(): array {
return array(
- 'mode' => 'builder',
- 'layout' => 'card',
- 'heading' => esc_html__( 'Subscribe to keep reading', 'memberful' ),
- 'heading_tag' => 'h2',
- 'subheading' => esc_html__( 'This post is for paying subscribers.', 'memberful' ),
- 'subheading_tag' => 'p',
- 'features' => array(),
- 'button_label' => esc_html__( 'Subscribe', 'memberful' ),
- 'subscribe_url' => '',
- 'sign_in_url' => '',
- 'brand_color' => '#2f80ed',
- 'button_shape' => 'rounded',
+ 'mode' => 'builder',
+ 'layout' => 'card',
+ 'heading' => esc_html__( 'Subscribe to keep reading', 'memberful' ),
+ 'heading_tag' => 'h2',
+ 'subheading' => esc_html__( 'This post is for paying subscribers.', 'memberful' ),
+ 'features' => array(),
+ 'button_label' => esc_html__( 'Subscribe', 'memberful' ),
+ 'subscribe_url' => '',
+ 'sign_in_url' => '',
+ 'brand_color' => '#2f80ed',
+ 'button_shape' => 'rounded',
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index e5c31bf1..63fbcae3 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -102,7 +102,7 @@ private static function heading_block( array $config ): string {
}
/**
- * Subheading element with the configured tag, or empty when blank.
+ * Subheading paragraph, or empty when blank.
*
* @param array $config Sanitized config.
*
@@ -113,10 +113,8 @@ private static function subheading_block( array $config ): string {
return '';
}
- $tag = in_array( $config['subheading_tag'], Memberful_Paywall_Config::SUBHEADING_TAGS, true ) ? $config['subheading_tag'] : 'p';
return sprintf(
- '<%1$s class="memberful-paywall__subheading">%2$s%1$s>',
- tag_escape( $tag ),
+ '%s
',
esc_html( $config['subheading'] )
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
index 9332dfa9..32a7eb2b 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
@@ -22,11 +22,10 @@ public static function sanitize( array $input, array $defaults ): array {
$clean = $defaults;
$enums = array(
- 'mode' => Memberful_Paywall_Config::MODES,
- 'layout' => Memberful_Paywall_Config::LAYOUTS,
- 'heading_tag' => Memberful_Paywall_Config::HEADING_TAGS,
- 'subheading_tag' => Memberful_Paywall_Config::SUBHEADING_TAGS,
- 'button_shape' => Memberful_Paywall_Config::BUTTON_SHAPES,
+ 'mode' => Memberful_Paywall_Config::MODES,
+ 'layout' => Memberful_Paywall_Config::LAYOUTS,
+ 'heading_tag' => Memberful_Paywall_Config::HEADING_TAGS,
+ 'button_shape' => Memberful_Paywall_Config::BUTTON_SHAPES,
);
foreach ( $enums as $key => $allowed ) {
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 45cce5d4..4e816a79 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -274,34 +274,234 @@ Ad Provider Settings
/*---------------------------------------------------------
Paywall Builder
------------------------------------------------------------ */
+.memberful-bulk-apply-box--wide {
+ max-width: none;
+}
.memberful-paywall-builder {
border-top: 1px solid #dcdcde;
margin-top: 2rem;
padding-top: 1.5rem;
}
+.memberful-paywall-builder__section-heading {
+ display: block;
+ font-size: 13px;
+ font-weight: 600;
+ margin: 0 0 0.75rem;
+ padding: 0;
+ text-transform: none;
+}
+
+/* Content source: tab-style segmented control */
.memberful-paywall-builder__mode {
- margin-bottom: 1rem;
+ border: 0;
+ margin: 0 0 1.5rem;
+ padding: 0;
}
-.memberful-paywall-builder__mode label {
- margin-right: 1.25rem;
+.memberful-paywall-builder__mode-tabs {
+ background: #fff;
+ border: 1px solid #c3c4c7;
+ border-radius: 4px;
+ display: inline-flex;
+ overflow: hidden;
}
-.memberful-paywall-builder__legend {
- font-weight: 600;
- margin-bottom: 0.35rem;
+.memberful-paywall-builder__mode-tab {
+ cursor: pointer;
+ margin: 0;
+ position: relative;
+}
+.memberful-paywall-builder__mode-tab input[type="radio"] {
+ opacity: 0;
+ pointer-events: none;
+ position: absolute;
+}
+.memberful-paywall-builder__mode-tab span {
+ background: #fff;
+ border-right: 1px solid #c3c4c7;
+ color: #2c3338;
+ display: inline-block;
+ font-size: 13px;
+ font-weight: 500;
+ line-height: 1.4;
+ padding: 8px 16px;
+}
+.memberful-paywall-builder__mode-tab:last-child span {
+ border-right: 0;
+}
+.memberful-paywall-builder__mode-tab input[type="radio"]:checked + span {
+ background: #2271b1;
+ color: #fff;
}
+.memberful-paywall-builder__mode-tab input[type="radio"]:focus-visible + span {
+ box-shadow: inset 0 0 0 2px #2271b1;
+}
+
+/* Template picker cards */
.memberful-paywall-builder__layout {
- margin: 1rem 0;
+ border: 0;
+ margin: 0 0 1.75rem;
+ padding: 0;
+}
+.memberful-paywall-builder__template-grid {
+ display: grid;
+ gap: 16px;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+}
+.memberful-paywall-builder__template-card {
+ cursor: pointer;
+ display: block;
+ margin: 0;
+ position: relative;
+}
+.memberful-paywall-builder__template-card input[type="radio"] {
+ opacity: 0;
+ pointer-events: none;
+ position: absolute;
+}
+.memberful-paywall-builder__template-card-inner {
+ background: #fff;
+ border: 2px solid #dcdcde;
+ border-radius: 6px;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ overflow: hidden;
+ transition: border-color 120ms ease, box-shadow 120ms ease;
+}
+.memberful-paywall-builder__template-card:hover .memberful-paywall-builder__template-card-inner {
+ border-color: #8c8f94;
+}
+.memberful-paywall-builder__template-card input[type="radio"]:checked + .memberful-paywall-builder__template-card-inner {
+ border-color: #2271b1;
+ box-shadow: 0 0 0 1px #2271b1;
+}
+.memberful-paywall-builder__template-card input[type="radio"]:checked + .memberful-paywall-builder__template-card-inner::after {
+ background: #2271b1 url("data:image/svg+xml;utf8, ") center/12px no-repeat;
+ border-radius: 50%;
+ content: "";
+ height: 20px;
+ position: absolute;
+ right: 10px;
+ top: 10px;
+ width: 20px;
+}
+.memberful-paywall-builder__template-card input[type="radio"]:focus-visible + .memberful-paywall-builder__template-card-inner {
+ box-shadow: 0 0 0 2px #2271b1;
}
-.memberful-paywall-builder__layout label {
- margin-right: 1.25rem;
+
+.memberful-paywall-builder__template-thumb {
+ align-items: center;
+ background: #f0f0f1;
+ border-bottom: 1px solid #dcdcde;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ height: 96px;
+ justify-content: center;
+ position: relative;
+}
+.memberful-paywall-builder__template-thumb--simple {
+ background: #f6f7f7;
+}
+.memberful-paywall-builder__template-thumb--card {
+ background: #f0f0f1;
+}
+.memberful-paywall-builder__template-thumb--banner {
+ background: #1d2327;
+}
+.memberful-paywall-builder__template-thumb--banner .memberful-paywall-builder__thumb-line {
+ background: #50575e;
+}
+.memberful-paywall-builder__thumb-line {
+ background: #c3c4c7;
+ border-radius: 2px;
+ height: 3px;
+ width: 100px;
+}
+.memberful-paywall-builder__thumb-button {
+ background: #2271b1;
+ border-radius: 2px;
+ height: 10px;
+ width: 48px;
+}
+.memberful-paywall-builder__thumb-lock {
+ background: #2271b1 url("data:image/svg+xml;utf8, ") center/18px no-repeat;
+ border-radius: 50%;
+ height: 26px;
+ width: 26px;
+}
+
+.memberful-paywall-builder__template-meta {
+ display: block;
+ padding: 10px 12px 12px;
+}
+.memberful-paywall-builder__template-meta strong {
+ color: #1d2327;
+ display: block;
+ font-size: 13px;
+ margin-bottom: 2px;
+}
+.memberful-paywall-builder__template-meta small {
+ color: #646970;
+ display: block;
+ font-size: 12px;
+ line-height: 1.35;
+}
+
+/* Two-column split: customize | preview */
+.memberful-paywall-builder__split {
+ align-items: start;
+ display: grid;
+ gap: 32px;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+}
+@media (max-width: 900px) {
+ .memberful-paywall-builder__split {
+ grid-template-columns: minmax(0, 1fr);
+ }
+}
+
+.memberful-paywall-builder__customize .memberful-paywall-builder__field {
+ display: block;
+ margin: 0 0 1rem;
+}
+.memberful-paywall-builder__customize label {
+ display: block;
+ font-size: 13px;
+ font-weight: 600;
+ margin-bottom: 4px;
+}
+.memberful-paywall-builder__customize input[type="text"],
+.memberful-paywall-builder__customize input[type="url"],
+.memberful-paywall-builder__customize textarea {
+ box-sizing: border-box;
+ max-width: 100%;
+ width: 100%;
+}
+.memberful-paywall-builder__customize .description {
+ color: #646970;
+ display: block;
+ font-size: 12px;
+ margin-top: 4px;
}
-.memberful-paywall-builder__fields th {
- width: 160px;
+.memberful-paywall-builder__customize .memberful-paywall-builder__field--paired {
+ align-items: start;
+ display: grid;
+ gap: 12px;
+ grid-template-columns: 1fr auto;
+ margin: 0 0 1rem;
+}
+.memberful-paywall-builder__field--paired .memberful-paywall-builder__field-main,
+.memberful-paywall-builder__field--paired .memberful-paywall-builder__field-aside {
+ margin: 0;
}
-.memberful-paywall-builder__fields .regular-text,
-.memberful-paywall-builder__fields .large-text {
- max-width: 480px;
+.memberful-paywall-builder__field--paired .memberful-paywall-builder__field-aside select {
+ min-width: 80px;
}
-.memberful-paywall-builder__fields select {
- margin-left: 0.5rem;
+
+.memberful-paywall-builder__preview-frame {
+ background: #fff;
+ border: 1px solid #dcdcde;
+ border-radius: 4px;
+ min-height: 480px;
+ width: 100%;
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
index 4e647f30..abb195f4 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
@@ -20,7 +20,7 @@
-
+
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 7ffb07a3..7c223827 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -7,110 +7,124 @@
* @var array $paywall_config
*/
-$layout_labels = array(
- 'simple' => __( 'Simple', 'memberful' ),
- 'card' => __( 'Card', 'memberful' ),
- 'banner' => __( 'Banner', 'memberful' ),
-);
-
-$button_shape_labels = array(
- 'pill' => __( 'Pill', 'memberful' ),
- 'rounded' => __( 'Rounded', 'memberful' ),
- 'square' => __( 'Square', 'memberful' ),
-);
-
$features_textarea = implode( "\n", (array) $paywall_config['features'] );
?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
index a1cbf461..68f26ee9 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
@@ -9,13 +9,15 @@
?>
-
-
- >
-
-
-
- >
-
-
-
+
+
+
+ >
+
+
+
+ >
+
+
+
+
\ No newline at end of file
From 03db76cf61c12975e9dc19de9bccafccd2bef10c Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 22 Apr 2026 16:22:26 +1000
Subject: [PATCH 09/28] Save work in progress
---
.../memberful-wp/src/paywall/config.php | 4 +-
.../memberful-wp/src/paywall/preview.php | 10 +-
.../memberful-wp/src/paywall/renderer.php | 117 ++++++++++---
.../memberful-wp/stylesheets/admin.css | 2 +-
.../memberful-wp/stylesheets/paywall.css | 158 ++++++++++--------
.../views/paywall/builder-panel.php | 2 +-
6 files changed, 196 insertions(+), 97 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index 8a38b2df..62ad4889 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -32,8 +32,8 @@ public static function defaults(): array {
'button_label' => esc_html__( 'Subscribe', 'memberful' ),
'subscribe_url' => '',
'sign_in_url' => '',
- 'brand_color' => '#2f80ed',
- 'button_shape' => 'rounded',
+ 'brand_color' => '',
+ 'button_shape' => 'square',
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
index 49f54334..1ea186da 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
@@ -55,15 +55,21 @@ public static function document( array $config ): string {
$links .= sprintf( ' ', esc_url( $theme_css ) );
}
+ $teaser = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae urna id quam faucibus gravida ac sed ipsum. Quisque eget velit dictum leo tempor bibendum nec sed odio.
';
+
+ $styles = 'html,body{margin:0;background:#fff;color:#1b1b1b;}'
+ . '.mf-preview-teaser{font-size:16px;line-height:1.6;padding:24px 24px 0;}'
+ . '.mf-preview-teaser p{margin:0 0 1em;}';
+
return ''
. ''
. ''
. ' '
. ' '
. $links
- . ''
+ . ''
. ''
- . '' . $body . ''
+ . '' . $teaser . $body . ''
. '';
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index 63fbcae3..e708c8c7 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -13,6 +13,54 @@
* Renders a paywall config array to HTML.
*/
class Memberful_Paywall_Renderer {
+ /**
+ * Flag to determine if we should load paywall styles.
+ *
+ * @var bool
+ */
+ private static $should_print_styles = false;
+
+ /**
+ * Register the actions on plugin load.
+ */
+ public static function register(): void {
+ add_filter( 'memberful_wp_protect_content', array( __CLASS__, 'protect_content' ) );
+ add_action( 'wp_footer', array( __CLASS__, 'maybe_print_styles' ) );
+ }
+
+ /**
+ * Conditionally flag paywall loading.
+ *
+ * @param string $content Content.
+ *
+ * @return string
+ */
+ public static function protect_content( string $content ): string {
+ $config = Memberful_Paywall_Config::get();
+
+ if ( 'builder' === $config['mode'] ) {
+ self::$should_print_styles = true;
+ }
+
+ return $content;
+ }
+
+ /**
+ * Render paywall styles.
+ */
+ public static function maybe_print_styles(): void {
+ if ( ! self::$should_print_styles ) {
+ return;
+ }
+
+ wp_enqueue_style(
+ 'memberful-paywall',
+ MEMBERFUL_URL . '/stylesheets/paywall.css',
+ array(),
+ MEMBERFUL_VERSION
+ );
+ }
+
/**
* Render a paywall config to HTML.
*
@@ -26,63 +74,66 @@ public static function render( array $config ): string {
$layout = in_array( $config['layout'], Memberful_Paywall_Config::LAYOUTS, true ) ? $config['layout'] : 'card';
$method = 'render_' . $layout;
- $body = self::$method( $config );
-
return sprintf(
- '%s
',
+ '%3$s
',
esc_attr( $layout ),
esc_attr( self::wrapper_style( $config ) ),
- $body
+ self::$method( $config )
);
}
/**
- * Render the "simple" layout.
+ * Render the "simple" layout — minimal text + CTA on a transparent band.
*
* @param array $config Sanitized config.
*
* @return string
*/
private static function render_simple( array $config ): string {
- return self::heading_block( $config )
+ return ''
+ . self::heading_block( $config )
. self::subheading_block( $config )
. self::features_block( $config )
- . '
' . self::primary_cta( $config ) . '
';
+ . '
' . self::primary_cta( $config ) . '
'
+ . self::sign_in_prompt( $config )
+ . '
';
}
/**
- * Render the "card" layout.
+ * Render the "card" layout — centred white card with lock badge.
*
* @param array $config Sanitized config.
*
* @return string
*/
private static function render_card( array $config ): string {
- return ''
+ return '
'
+ . '
'
+ . self::lock_badge()
. self::heading_block( $config )
. self::subheading_block( $config )
. self::features_block( $config )
- . '
'
- . self::primary_cta( $config )
- . self::secondary_cta( $config )
+ . '
' . self::primary_cta( $config ) . '
'
+ . self::sign_in_prompt( $config )
. '
'
. '
';
}
/**
- * Render the "banner" layout.
+ * Render the "banner" layout — full-width dark band.
*
* @param array $config Sanitized config.
*
* @return string
*/
private static function render_banner( array $config ): string {
- return '
'
+ return '
'
. self::heading_block( $config )
. self::subheading_block( $config )
. self::features_block( $config )
- . '
'
- . '
' . self::primary_cta( $config ) . '
';
+ . '
' . self::primary_cta( $config ) . '
'
+ . self::sign_in_prompt( $config )
+ . '
';
}
/**
@@ -155,20 +206,34 @@ private static function primary_cta( array $config ): string {
}
/**
- * Secondary sign-in CTA anchor (card layout only).
+ * "Already a subscriber? Sign in" text prompt shown under every layout's CTA.
*
* @param array $config Sanitized config.
*
* @return string
*/
- private static function secondary_cta( array $config ): string {
+ private static function sign_in_prompt( array $config ): string {
return sprintf(
- '
%s ',
+ '
%s %s
',
+ esc_html__( 'Already a subscriber?', 'memberful' ),
esc_url( self::sign_in_url( $config ) ),
esc_html__( 'Sign in', 'memberful' )
);
}
+ /**
+ * Circular lock badge shown at the top of the card layout.
+ *
+ * @return string
+ */
+ private static function lock_badge(): string {
+ return '
'
+ . ''
+ . ' '
+ . ' '
+ . ' ';
+ }
+
/**
* Wrapper inline style carrying the brand colour and button radius custom properties.
*
@@ -177,11 +242,13 @@ private static function secondary_cta( array $config ): string {
* @return string
*/
private static function wrapper_style( array $config ): string {
- return sprintf(
- '--mf-brand:%s;--mf-radius:%s;',
- $config['brand_color'],
- self::button_radius( $config['button_shape'] )
- );
+ $parts = array( '--mf-radius:' . self::button_radius( $config['button_shape'] ) );
+
+ if ( ! empty( $config['brand_color'] ) ) {
+ $parts[] = '--mf-brand:' . $config['brand_color'];
+ }
+
+ return implode( ';', $parts ) . ';';
}
/**
@@ -236,3 +303,5 @@ private static function check_icon(): string {
. '';
}
}
+
+Memberful_Paywall_Renderer::register();
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 4e816a79..4ead8d7d 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -502,6 +502,6 @@ Paywall Builder
background: #fff;
border: 1px solid #dcdcde;
border-radius: 4px;
- min-height: 480px;
+ min-height: 520px;
width: 100%;
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index 0f7ed2ed..3b2a02b6 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -3,8 +3,8 @@
*/
.memberful-paywall {
- --mf-brand: #2f80ed;
- --mf-radius: 8px;
+ --mf-brand: var(--wp--preset--color--primary, var(--wp--preset--color--accent, var(--wp-admin-theme-color, #1f2933)));
+ --mf-radius: 0;
--mf-text: #1b1b1b;
--mf-muted: #555;
--mf-surface: #fff;
@@ -14,9 +14,10 @@
color: var(--mf-text);
font-family: inherit;
line-height: 1.5;
- margin: 1.5em auto;
- max-width: 640px;
- padding: 2rem;
+ margin: 0;
+ padding: 0;
+ position: relative;
+ width: 100%;
}
.memberful-paywall *,
@@ -25,28 +26,55 @@
box-sizing: border-box;
}
+/* Shared inner column that constrains content width inside full-bleed layouts. */
+.memberful-paywall__inner {
+ margin: 0 auto;
+ max-width: 600px;
+ padding: 2rem 1.5rem 2.5rem;
+ text-align: center;
+}
+
.memberful-paywall__heading {
font-weight: 700;
line-height: 1.2;
- margin: 0 0 0.5rem;
+ margin: 0 0 0.75rem;
+}
+
+h2.memberful-paywall__heading {
+ font-size: 2rem;
+}
+
+h2.memberful-paywall__heading {
+ font-size: 1.5rem;
+}
+
+h3.memberful-paywall__heading {
+ font-size: 1.175rem;
}
.memberful-paywall__subheading {
color: var(--mf-muted);
- margin: 0 0 1.25rem;
+ font-size: 1rem;
+ margin: 0 0 1.5rem;
}
.memberful-paywall__features {
+ color: var(--mf-muted);
+ column-gap: 1.5rem;
+ display: inline-grid;
+ font-size: 1rem;
+ grid-template-columns: repeat(2, auto);
list-style: none;
margin: 0 0 1.5rem;
padding: 0;
+ row-gap: 0.25rem;
+ text-align: left;
}
.memberful-paywall__features li {
align-items: flex-start;
display: flex;
gap: 0.5rem;
- padding: 0.25rem 0;
}
.memberful-paywall__check {
@@ -60,22 +88,26 @@
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
+ justify-content: center;
+ margin-bottom: 1rem;
}
.memberful-paywall__button {
+ border: 1px solid transparent;
border-radius: var(--mf-radius);
display: inline-block;
+ font-size: 1rem;
font-weight: 600;
line-height: 1.2;
- padding: 0.6rem 1.25rem;
+ padding: 0.85rem 1.75rem;
text-decoration: none;
- transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
+ transition: background-color 150ms ease, color 150ms ease, filter 150ms ease;
}
.memberful-paywall__button--primary {
background: var(--mf-brand);
+ border-color: var(--mf-brand);
color: #fff;
- border: 1px solid var(--mf-brand);
}
.memberful-paywall__button--primary:hover,
@@ -83,84 +115,76 @@
filter: brightness(0.92);
}
-.memberful-paywall__button--secondary {
- background: transparent;
- border: 1px solid var(--mf-border);
- color: var(--mf-brand);
+.memberful-paywall__signin {
+ color: var(--mf-muted);
+ font-size: 0.9rem;
+ margin: 0;
}
-.memberful-paywall__button--secondary:hover,
-.memberful-paywall__button--secondary:focus {
- border-color: var(--mf-brand);
+.memberful-paywall__signin-link {
+ color: inherit;
+ text-decoration: underline;
}
-/* Simple layout - left aligned stack, no surface. */
-.memberful-paywall--simple {
- text-align: left;
+.memberful-paywall__lock {
+ align-items: center;
+ background: var(--mf-brand);
+ border-radius: 50%;
+ color: #fff;
+ display: inline-flex;
+ height: 44px;
+ justify-content: center;
+ margin: 0 auto 1rem;
+ width: 44px;
}
-/* Card layout - centred card with border and shadow. */
-.memberful-paywall--card {
- text-align: center;
+/* Simple — transparent band, minimal spacing. */
+.memberful-paywall--simple {
+ background: var(--mf-surface);
}
-.memberful-paywall--card .memberful-paywall__card {
- background: var(--mf-surface);
- border: 1px solid var(--mf-border);
- border-radius: calc(var(--mf-radius) + 4px);
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
- padding: 2rem;
+.memberful-paywall--simple .memberful-paywall__button {
+ padding: 0.6rem 1.4rem;
}
-.memberful-paywall--card .memberful-paywall__features {
- display: inline-block;
- text-align: left;
+/* Card — centred white surface floating on a muted page backdrop. */
+.memberful-paywall--card {
+ background: #f2f4f7;
}
-.memberful-paywall--card .memberful-paywall__actions {
- justify-content: center;
+.memberful-paywall--card .memberful-paywall__inner {
+ padding-top: 1.5rem;
}
-/* Banner layout - heading/features on the left, CTA on the right. */
-.memberful-paywall--banner {
- align-items: center;
+.memberful-paywall--card .memberful-paywall__card {
background: var(--mf-surface);
border: 1px solid var(--mf-border);
- border-radius: var(--mf-radius);
- display: flex;
- gap: 1.5rem;
- max-width: none;
- padding: 1.5rem 2rem;
+ border-radius: calc(var(--mf-radius) + 4px);
+ box-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
+ margin: 0 auto;
+ max-width: 420px;
+ padding: 2.25rem 2rem;
}
-.memberful-paywall--banner .memberful-paywall__banner-text {
- flex: 1 1 auto;
- min-width: 0;
+.memberful-paywall--card .memberful-paywall__button--primary {
+ display: block;
+ padding: 0.95rem 1.5rem;
+ width: 100%;
}
-.memberful-paywall--banner .memberful-paywall__heading {
- font-size: 1.15rem;
-}
+/* Banner — full-bleed dark band with white copy. */
+.memberful-paywall--banner {
+ --mf-text: #fff;
+ --mf-muted: rgba(255, 255, 255, 0.75);
-.memberful-paywall--banner .memberful-paywall__subheading {
- margin-bottom: 0;
+ background: #1f2933;
+ color: var(--mf-text);
}
-.memberful-paywall--banner .memberful-paywall__actions {
- flex: 0 0 auto;
+.memberful-paywall--banner .memberful-paywall__signin-link {
+ color: #fff;
}
-@media (max-width: 768px) {
- .memberful-paywall--banner {
- align-items: stretch;
- flex-direction: column;
- }
-
- .memberful-paywall--banner .memberful-paywall__features {
- display: none;
- }
-
- .memberful-paywall--banner .memberful-paywall__actions {
- justify-content: center;
- }
-}
\ No newline at end of file
+.memberful-paywall--banner .memberful-paywall__button--primary {
+ padding: 1rem 2rem;
+}
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 7c223827..b5af465a 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -101,7 +101,7 @@
-
+
From e978111fac2a39262d6c968249204b4a78e50cdc Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Thu, 23 Apr 2026 19:46:20 +1000
Subject: [PATCH 10/28] Adjust styles
---
.../memberful-wp/src/global_marketing.php | 3 +-
.../memberful-wp/src/paywall/preview.php | 12 +-
.../memberful-wp/src/paywall/renderer.php | 53 ++++++-
.../memberful-wp/stylesheets/admin.css | 2 +-
.../memberful-wp/stylesheets/paywall.css | 136 +++++++++++-------
5 files changed, 145 insertions(+), 61 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
index 99442ee4..f14db2d8 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
@@ -98,7 +98,8 @@ function memberful_apply_global_snippets_content_filter( $memberful_marketing_co
}
}
- $wrapped_teaser = "$teaser
";
+ $teaser_class = apply_filters( 'memberful_global_teaser_class', 'memberful-global-teaser-content' );
+ $wrapped_teaser = "$teaser
";
if ( $has_teaser && ! did_filter( 'memberful_teaser_css' ) ) {
$wrapped_teaser .= apply_filters( 'memberful_teaser_css', memberful_get_teaser_css() );
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
index 1ea186da..9b2636c0 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
@@ -55,11 +55,15 @@ public static function document( array $config ): string {
$links .= sprintf( ' ', esc_url( $theme_css ) );
}
- $teaser = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae urna id quam faucibus gravida ac sed ipsum. Quisque eget velit dictum leo tempor bibendum nec sed odio.
';
+ $teaser_class = 'memberful-global-teaser-content memberful-global-teaser-content--mf-' . $config['layout'];
+ $teaser = sprintf(
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae urna id quam faucibus gravida ac sed ipsum. Quisque eget velit dictum leo tempor bibendum nec sed odio.
',
+ esc_attr( $teaser_class )
+ );
- $styles = 'html,body{margin:0;background:#fff;color:#1b1b1b;}'
- . '.mf-preview-teaser{font-size:16px;line-height:1.6;padding:24px 24px 0;}'
- . '.mf-preview-teaser p{margin:0 0 1em;}';
+ $styles = 'html,body{background:#fff;color:#1b1b1b;font-size:16px;line-height:1.6;margin:0;}'
+ . '.memberful-global-teaser-content{padding:24px 24px 0;}'
+ . '.memberful-global-teaser-content p{margin:0; padding-bottom: 1rem;}';
return ''
. ''
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index e708c8c7..ae234cf6 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -26,6 +26,36 @@ class Memberful_Paywall_Renderer {
public static function register(): void {
add_filter( 'memberful_wp_protect_content', array( __CLASS__, 'protect_content' ) );
add_action( 'wp_footer', array( __CLASS__, 'maybe_print_styles' ) );
+ add_filter( 'memberful_global_teaser_class', array( __CLASS__, 'filter_teaser_class' ) );
+ add_filter( 'memberful_teaser_css', array( __CLASS__, 'filter_teaser_css' ) );
+ }
+
+ /**
+ * Append a layout modifier to the teaser wrapper class when the builder paywall is active.
+ *
+ * @param string $classes Default teaser wrapper class list.
+ *
+ * @return string
+ */
+ public static function filter_teaser_class( string $classes ): string {
+ if ( ! self::is_builder_mode() ) {
+ return $classes;
+ }
+
+ $config = Memberful_Paywall_Config::get();
+
+ return $classes . ' memberful-global-teaser-content--mf-' . $config['layout'];
+ }
+
+ /**
+ * Suppress the legacy inline teaser fade when the builder paywall owns the fade via paywall.css.
+ *
+ * @param string $css Legacy inline teaser CSS block.
+ *
+ * @return string
+ */
+ public static function filter_teaser_css( string $css ): string {
+ return self::is_builder_mode() ? '' : $css;
}
/**
@@ -36,9 +66,7 @@ public static function register(): void {
* @return string
*/
public static function protect_content( string $content ): string {
- $config = Memberful_Paywall_Config::get();
-
- if ( 'builder' === $config['mode'] ) {
+ if ( self::is_builder_mode() ) {
self::$should_print_styles = true;
}
@@ -227,11 +255,11 @@ private static function sign_in_prompt( array $config ): string {
* @return string
*/
private static function lock_badge(): string {
- return ''
- . ''
- . ' '
+ return ''
+ . '
'
+ . ' '
. ' '
- . '';
+ . '
';
}
/**
@@ -302,6 +330,17 @@ private static function check_icon(): string {
. ' '
. ' ';
}
+
+ /**
+ * Whether the builder paywall is the active rendering mode.
+ *
+ * @return bool
+ */
+ private static function is_builder_mode(): bool {
+ $config = Memberful_Paywall_Config::get();
+
+ return 'builder' === $config['mode'];
+ }
}
Memberful_Paywall_Renderer::register();
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 4ead8d7d..b2265ce4 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -502,6 +502,6 @@ Paywall Builder
background: #fff;
border: 1px solid #dcdcde;
border-radius: 4px;
- min-height: 520px;
+ min-height: 500px;
width: 100%;
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index 3b2a02b6..de8ba768 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -3,12 +3,13 @@
*/
.memberful-paywall {
- --mf-brand: var(--wp--preset--color--primary, var(--wp--preset--color--accent, var(--wp-admin-theme-color, #1f2933)));
+ --mf-border: #e5e5e5;
+ --mf-brand: var(--wp--preset--color--primary, var(--wp--preset--color--accent, var(--wp-admin-theme-color, #2271b1)));
+ --mf-muted: #666;
+ --mf-muted-soft: #999;
--mf-radius: 0;
- --mf-text: #1b1b1b;
- --mf-muted: #555;
--mf-surface: #fff;
- --mf-border: rgba(0, 0, 0, 0.08);
+ --mf-text: #1a1a1a;
box-sizing: border-box;
color: var(--mf-text);
@@ -26,48 +27,49 @@
box-sizing: border-box;
}
-/* Shared inner column that constrains content width inside full-bleed layouts. */
.memberful-paywall__inner {
margin: 0 auto;
max-width: 600px;
- padding: 2rem 1.5rem 2.5rem;
+ padding: 2rem 1.5rem 1.5rem;
text-align: center;
}
.memberful-paywall__heading {
- font-weight: 700;
- line-height: 1.2;
- margin: 0 0 0.75rem;
+ color: var(--mf-text);
+ font-weight: 600;
+ line-height: 1.3;
+ margin: 0 0 0.5rem;
}
-h2.memberful-paywall__heading {
- font-size: 2rem;
+h1.memberful-paywall__heading {
+ font-size: 1.5rem;
}
h2.memberful-paywall__heading {
- font-size: 1.5rem;
+ font-size: 1.125rem;
}
h3.memberful-paywall__heading {
- font-size: 1.175rem;
+ font-size: 1rem;
}
.memberful-paywall__subheading {
color: var(--mf-muted);
- font-size: 1rem;
- margin: 0 0 1.5rem;
+ font-size: 0.875rem;
+ margin: 0 auto 1.25rem;
+ max-width: 28rem;
}
.memberful-paywall__features {
color: var(--mf-muted);
column-gap: 1.5rem;
display: inline-grid;
- font-size: 1rem;
+ font-size: 0.9375rem;
grid-template-columns: repeat(2, auto);
list-style: none;
- margin: 0 0 1.5rem;
+ margin: 0 0 1.25rem;
padding: 0;
- row-gap: 0.25rem;
+ row-gap: 0.375rem;
text-align: left;
}
@@ -89,17 +91,17 @@ h3.memberful-paywall__heading {
flex-wrap: wrap;
gap: 0.75rem;
justify-content: center;
- margin-bottom: 1rem;
+ margin-bottom: 0;
}
.memberful-paywall__button {
border: 1px solid transparent;
border-radius: var(--mf-radius);
display: inline-block;
- font-size: 1rem;
+ font-size: 0.875rem;
font-weight: 600;
line-height: 1.2;
- padding: 0.85rem 1.75rem;
+ padding: 0.5rem 1.5rem;
text-decoration: none;
transition: background-color 150ms ease, color 150ms ease, filter 150ms ease;
}
@@ -116,13 +118,13 @@ h3.memberful-paywall__heading {
}
.memberful-paywall__signin {
- color: var(--mf-muted);
- font-size: 0.9rem;
- margin: 0;
+ color: var(--mf-muted-soft);
+ font-size: 0.75rem;
+ margin: 1rem 0 0;
}
.memberful-paywall__signin-link {
- color: inherit;
+ color: var(--mf-brand);
text-decoration: underline;
}
@@ -132,59 +134,97 @@ h3.memberful-paywall__heading {
border-radius: 50%;
color: #fff;
display: inline-flex;
- height: 44px;
+ height: 40px;
justify-content: center;
margin: 0 auto 1rem;
- width: 44px;
+ width: 40px;
}
-/* Simple — transparent band, minimal spacing. */
+/* Simple — top-divider band on white. */
.memberful-paywall--simple {
background: var(--mf-surface);
+ border-top: 1px solid var(--mf-border);
}
-.memberful-paywall--simple .memberful-paywall__button {
- padding: 0.6rem 1.4rem;
-}
-
-/* Card — centred white surface floating on a muted page backdrop. */
+/* Card — centred white card on muted page. */
.memberful-paywall--card {
- background: #f2f4f7;
+ --mf-surface: #f8f8f8;
+
+ background: var(--mf-surface);
}
.memberful-paywall--card .memberful-paywall__inner {
- padding-top: 1.5rem;
+ padding: 1.5rem;
}
.memberful-paywall--card .memberful-paywall__card {
- background: var(--mf-surface);
- border: 1px solid var(--mf-border);
- border-radius: calc(var(--mf-radius) + 4px);
- box-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
+ background: #fff;
+ border: 1px solid #e0e0e0;
+ border-radius: 0.5rem;
+ box-shadow: 0 10px 25px rgba(15, 23, 42, 0.1);
margin: 0 auto;
- max-width: 420px;
- padding: 2.25rem 2rem;
+ max-width: 24rem;
+ padding: 2rem;
+}
+
+.memberful-paywall--card .memberful-paywall__heading {
+ font-weight: 700;
}
.memberful-paywall--card .memberful-paywall__button--primary {
display: block;
- padding: 0.95rem 1.5rem;
+ padding: 0.625rem 1.5rem;
width: 100%;
}
-/* Banner — full-bleed dark band with white copy. */
+/* Banner — full-bleed dark band, always viewport width. */
.memberful-paywall--banner {
+ --mf-muted: #a0a5aa;
+ --mf-muted-soft: #72777c;
+ --mf-surface: #1d2327;
--mf-text: #fff;
- --mf-muted: rgba(255, 255, 255, 0.75);
- background: #1f2933;
- color: var(--mf-text);
+ background: var(--mf-surface);
+ margin-inline: calc(50% - 50vw);
+ width: 100vw;
+}
+
+.memberful-paywall--banner .memberful-paywall__inner {
+ padding: 2.5rem 2rem;
+}
+
+.memberful-paywall--banner .memberful-paywall__heading {
+ font-weight: 700;
+}
+
+.memberful-paywall--banner h2.memberful-paywall__heading {
+ font-size: 1.25rem;
+}
+
+.memberful-paywall--banner .memberful-paywall__subheading {
+ max-width: 32rem;
}
.memberful-paywall--banner .memberful-paywall__signin-link {
- color: #fff;
+ color: #72aee6;
}
.memberful-paywall--banner .memberful-paywall__button--primary {
- padding: 1rem 2rem;
+ padding: 0.75rem 2rem;
+}
+
+/* Teaser fade. */
+.memberful-global-teaser-content[class*="--mf-"] {
+ position: relative;
+}
+
+.memberful-global-teaser-content[class*="--mf-"]::after {
+ background: linear-gradient(transparent, #fff);
+ bottom: 0;
+ content: "";
+ height: 4rem;
+ left: 0;
+ pointer-events: none;
+ position: absolute;
+ right: 0;
}
From 03978ab2aefcb1af3c00c4e0644ba1480d73cc9a Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Mon, 27 Apr 2026 09:23:07 +1000
Subject: [PATCH 11/28] Fix flagged issues
---
.../memberful-wp/js/src/paywall-builder.js | 9 +-
.../plugins/memberful-wp/src/admin.php | 70 ++++----
.../memberful-wp/src/global_marketing.php | 7 +-
.../plugins/memberful-wp/src/options.php | 58 +++---
.../views/paywall/builder-panel.php | 167 +++++++++---------
.../memberful-wp/views/paywall/mode-radio.php | 24 +--
6 files changed, 170 insertions(+), 165 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index 75d2d89a..a00985e7 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -32,7 +32,10 @@ jQuery(function ($) {
}
applyMode(this.value);
- refreshPreview();
+
+ if (this.value === 'builder') {
+ refreshPreview();
+ }
});
applyMode($modeInputs.filter(':checked').val() || 'builder');
@@ -105,5 +108,7 @@ jQuery(function ($) {
$form.on('input', 'input[type="text"], input[type="url"], textarea', scheduleRefresh);
$form.on('change', 'input[type="radio"], select', refreshPreview);
- refreshPreview();
+ if (($modeInputs.filter(':checked').val() || 'builder') === 'builder') {
+ refreshPreview();
+ }
});
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/admin.php b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
index 219462a9..df8afe52 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/admin.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
@@ -715,41 +715,41 @@ function memberful_wp_add_protected_state_to_post_list($states, $post) {
}
function memberful_wp_global_marketing() {
- if ( isset( $_POST['save_global_marketing'] ) && memberful_wp_valid_nonce( 'memberful_options' ) ) {
- if ( isset( $_POST['memberful_use_global_marketing'] ) ) {
- update_option( 'memberful_use_global_marketing', true );
- update_option( 'memberful_global_marketing_override', filter_input( INPUT_POST, 'memberful_global_marketing_override', FILTER_SANITIZE_NUMBER_INT ) );
- update_option( 'memberful_use_global_snippets', (int) isset( $_POST['memberful_use_global_snippets'] ) );
-
- $paywall_input = filter_input( INPUT_POST, 'memberful_paywall', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
- if ( is_array( $paywall_input ) && ! empty( $paywall_input ) ) {
- Memberful_Paywall_Config::save( $paywall_input );
- }
-
- if ( 'custom_html' === Memberful_Paywall_Config::get()['mode'] ) {
- update_option( 'memberful_global_marketing_content', memberful_wp_kses_post( filter_input( INPUT_POST, 'memberful_global_marketing_content' ) ) );
- }
- } else {
- update_option( 'memberful_use_global_marketing', false );
- }
- }
-
- $use_global_marketing = get_option( 'memberful_use_global_marketing' );
- $use_global_snippets = get_option( 'memberful_use_global_snippets');
- $global_marketing_content = get_option( 'memberful_global_marketing_content' );
- $global_marketing_override = get_option( 'memberful_global_marketing_override', true );
-
- memberful_wp_render(
- 'global_marketing',
- array(
- 'use_global_marketing' => $use_global_marketing,
- 'use_global_snippets' => $use_global_snippets,
- 'global_marketing_content' => $global_marketing_content,
- 'global_marketing_override' => $global_marketing_override,
- 'paywall_config' => Memberful_Paywall_Config::get(),
- 'form_target' => memberful_wp_plugin_global_marketing_url(),
- )
- );
+ if ( isset( $_POST['save_global_marketing'] ) && memberful_wp_valid_nonce( 'memberful_options' ) ) {
+ if ( isset( $_POST['memberful_use_global_marketing'] ) ) {
+ update_option( 'memberful_use_global_marketing', true );
+ update_option( 'memberful_global_marketing_override', filter_input( INPUT_POST, 'memberful_global_marketing_override', FILTER_SANITIZE_NUMBER_INT ) );
+ update_option( 'memberful_use_global_snippets', (int) isset( $_POST['memberful_use_global_snippets'] ) );
+
+ $paywall_input = filter_input( INPUT_POST, 'memberful_paywall', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
+ if ( is_array( $paywall_input ) && ! empty( $paywall_input ) ) {
+ Memberful_Paywall_Config::save( $paywall_input );
+ }
+
+ if ( 'custom_html' === Memberful_Paywall_Config::get()['mode'] ) {
+ update_option( 'memberful_global_marketing_content', memberful_wp_kses_post( filter_input( INPUT_POST, 'memberful_global_marketing_content' ) ) );
+ }
+ } else {
+ update_option( 'memberful_use_global_marketing', false );
+ }
+ }
+
+ $use_global_marketing = get_option( 'memberful_use_global_marketing' );
+ $use_global_snippets = get_option( 'memberful_use_global_snippets');
+ $global_marketing_content = get_option( 'memberful_global_marketing_content' );
+ $global_marketing_override = get_option( 'memberful_global_marketing_override', true );
+
+ memberful_wp_render(
+ 'global_marketing',
+ array(
+ 'use_global_marketing' => $use_global_marketing,
+ 'use_global_snippets' => $use_global_snippets,
+ 'global_marketing_content' => $global_marketing_content,
+ 'global_marketing_override' => $global_marketing_override,
+ 'paywall_config' => Memberful_Paywall_Config::get(),
+ 'form_target' => memberful_wp_plugin_global_marketing_url(),
+ )
+ );
}
/**
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
index f14db2d8..ceea1f42 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
@@ -17,15 +17,14 @@
* @return string
*/
function memberful_get_global_replacement($marketing_content){
- $override = get_option( 'memberful_global_marketing_override' );
- $global_marketing_content = memberful_wp_resolve_global_marketing_content();
+ $override = get_option( 'memberful_global_marketing_override' );
if ( $override ) {
- return $global_marketing_content;
+ return memberful_wp_resolve_global_marketing_content();
}
if ( empty( trim( $marketing_content ) ) ) {
- return $global_marketing_content;
+ return memberful_wp_resolve_global_marketing_content();
}
return $marketing_content;
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/options.php b/wordpress/wp-content/plugins/memberful-wp/src/options.php
index e6acbf47..aa6a5a36 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/options.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/options.php
@@ -2,35 +2,35 @@
require MEMBERFUL_DIR.'/src/user/map_stats.php';
function memberful_wp_all_options(): array {
- return array(
- 'memberful_client_id' => null,
- 'memberful_client_secret' => null,
- 'memberful_site' => null,
- 'memberful_custom_domain' => null,
- 'memberful_api_key' => null,
- 'memberful_webhook_secret' => null,
- 'memberful_products' => array(),
- 'memberful_subscriptions' => array(),
- 'memberful_acl' => array(),
- 'memberful_embed_enabled' => false,
- 'memberful_error_log' => array(),
- 'memberful_role_active_customer' => 'subscriber',
- 'memberful_role_inactive_customer' => 'subscriber',
- 'memberful_plan_role_mappings' => array(),
- 'memberful_use_per_plan_roles' => false,
- 'memberful_posts_available_to_any_registered_user' => array(),
- 'memberful_hide_admin_toolbar' => true,
- 'memberful_block_dashboard_access' => true,
- 'memberful_filter_account_menu_items' => true,
- 'memberful_auto_sync_display_names' => false,
- 'memberful_show_protected_content_in_search' => false,
- 'memberful_use_global_marketing' => false,
- 'memberful_use_global_snippets' => true,
- 'memberful_global_marketing_override' => true,
- 'memberful_global_marketing_content' => '',
- 'memberful_ad_provider_settings' => array(),
- Memberful_Paywall_Config::OPTION_KEY => array(),
- );
+ return array(
+ 'memberful_client_id' => null,
+ 'memberful_client_secret' => null,
+ 'memberful_site' => null,
+ 'memberful_custom_domain' => null,
+ 'memberful_api_key' => null,
+ 'memberful_webhook_secret' => null,
+ 'memberful_products' => array(),
+ 'memberful_subscriptions' => array(),
+ 'memberful_acl' => array(),
+ 'memberful_embed_enabled' => false,
+ 'memberful_error_log' => array(),
+ 'memberful_role_active_customer' => 'subscriber',
+ 'memberful_role_inactive_customer' => 'subscriber',
+ 'memberful_plan_role_mappings' => array(),
+ 'memberful_use_per_plan_roles' => false,
+ 'memberful_posts_available_to_any_registered_user' => array(),
+ 'memberful_hide_admin_toolbar' => true,
+ 'memberful_block_dashboard_access' => true,
+ 'memberful_filter_account_menu_items' => true,
+ 'memberful_auto_sync_display_names' => false,
+ 'memberful_show_protected_content_in_search' => false,
+ 'memberful_use_global_marketing' => false,
+ 'memberful_use_global_snippets' => true,
+ 'memberful_global_marketing_override' => true,
+ 'memberful_global_marketing_content' => '',
+ 'memberful_ad_provider_settings' => array(),
+ Memberful_Paywall_Config::OPTION_KEY => array(),
+ );
}
/**
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index b5af465a..9c8ac17d 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -10,12 +10,12 @@
$features_textarea = implode( "\n", (array) $paywall_config['features'] );
?>
-
-
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
- >H1
- >H2
- >H3
-
-
-
+
+
+
+
+
+
+
+
+ >H1
+ >H2
+ >H3
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
- >
- >
- >
-
-
-
+
+
+
+
+
+
+
+
+ >
+ >
+ >
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
index 68f26ee9..2f0914de 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/mode-radio.php
@@ -9,15 +9,15 @@
?>
-
-
-
- >
-
-
-
- >
-
-
-
-
\ No newline at end of file
+
+
+
+ >
+
+
+
+ >
+
+
+
+
From b8e2df8d6fd35fd701945a7c4f49938b09984a02 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Mon, 27 Apr 2026 09:51:13 +1000
Subject: [PATCH 12/28] Fix issues
---
.../plugins/memberful-wp/src/admin.php | 3 +-
.../memberful-wp/src/global_marketing.php | 26 +++++-----
.../memberful-wp/stylesheets/paywall.css | 2 +-
.../views/paywall/builder-panel.php | 52 +++++++++----------
4 files changed, 42 insertions(+), 41 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/admin.php b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
index df8afe52..d6fd5669 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/admin.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
@@ -726,7 +726,8 @@ function memberful_wp_global_marketing() {
Memberful_Paywall_Config::save( $paywall_input );
}
- if ( 'custom_html' === Memberful_Paywall_Config::get()['mode'] ) {
+ $config_mode = $paywall_input['mode'] ?? 'builder';
+ if ( 'custom_html' === $config_mode ) {
update_option( 'memberful_global_marketing_content', memberful_wp_kses_post( filter_input( INPUT_POST, 'memberful_global_marketing_content' ) ) );
}
} else {
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
index ceea1f42..c3a7eb6c 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
@@ -17,17 +17,17 @@
* @return string
*/
function memberful_get_global_replacement($marketing_content){
- $override = get_option( 'memberful_global_marketing_override' );
+ $override = get_option( 'memberful_global_marketing_override' );
- if ( $override ) {
- return memberful_wp_resolve_global_marketing_content();
- }
+ if ( $override ) {
+ return memberful_wp_resolve_global_marketing_content();
+ }
- if ( empty( trim( $marketing_content ) ) ) {
- return memberful_wp_resolve_global_marketing_content();
- }
+ if ( empty( trim( $marketing_content ) ) ) {
+ return memberful_wp_resolve_global_marketing_content();
+ }
- return $marketing_content;
+ return $marketing_content;
}
/**
@@ -36,13 +36,13 @@ function memberful_get_global_replacement($marketing_content){
* @return string
*/
function memberful_wp_resolve_global_marketing_content(): string {
- $config = Memberful_Paywall_Config::get();
+ $config = Memberful_Paywall_Config::get();
- if ( 'builder' === $config['mode'] ) {
- return Memberful_Paywall_Renderer::render( $config );
- }
+ if ( 'builder' === $config['mode'] ) {
+ return Memberful_Paywall_Renderer::render( $config );
+ }
- return (string) get_option( 'memberful_global_marketing_content' );
+ return (string) get_option( 'memberful_global_marketing_content' );
}
/**
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index de8ba768..0ddaa2de 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -219,7 +219,7 @@ h3.memberful-paywall__heading {
}
.memberful-global-teaser-content[class*="--mf-"]::after {
- background: linear-gradient(transparent, #fff);
+ background: linear-gradient(transparent, var(--wp--preset--color--background, #fff));
bottom: 0;
content: "";
height: 4rem;
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 9c8ac17d..80e0aaf5 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -16,40 +16,40 @@
>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
>
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
From 70af3b511e6f89eabb2756e43ab4b676eb2d8ad6 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 29 Apr 2026 08:38:19 +1000
Subject: [PATCH 13/28] Improve post edit screen
---
.../plugins/memberful-wp/src/metabox.php | 8 ++++
.../plugins/memberful-wp/views/metabox.php | 41 +++++++++++++------
2 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/metabox.php b/wordpress/wp-content/plugins/memberful-wp/src/metabox.php
index d82b7ac3..59fd9765 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/metabox.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/metabox.php
@@ -55,6 +55,10 @@ function memberful_wp_metabox( $post ) {
$view_vars['viewable_by_any_registered_users'] = memberful_wp_get_post_available_to_any_registered_users( $post->ID );
$view_vars['viewable_by_anybody_subscribed_to_a_plan'] = memberful_wp_get_post_available_to_anybody_subscribed_to_a_plan( $post->ID );
+ $paywall_config = Memberful_Paywall_Config::get();
+ $view_vars['global_marketing_overrides_post_content'] = (bool) get_option( 'memberful_global_marketing_override' )
+ || 'builder' === $paywall_config['mode'];
+
memberful_wp_render( 'metabox', $view_vars );
}
@@ -155,6 +159,10 @@ function memberful_wp_add_term_metabox( $term ) {
$view_vars['viewable_by_any_registered_users'] = memberful_wp_is_term_available_to_any_registered_users( $term->term_id );
$view_vars['viewable_by_anybody_subscribed_to_a_plan'] = memberful_wp_is_term_available_to_anybody_subscribed_to_a_plan( $term->term_id );
+ $paywall_config = Memberful_Paywall_Config::get();
+ $view_vars['global_marketing_overrides_post_content'] = (bool) get_option( 'memberful_global_marketing_override' )
+ || 'builder' === $paywall_config['mode'];
+
memberful_wp_render( 'metabox', $view_vars );
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/metabox.php b/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
index 46d6fff3..9d7343fa 100755
--- a/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
@@ -5,19 +5,34 @@
-
-
-
- Click Here
-
- to manage global marketing content.
-
+
+
+
+ global marketing settings. Anything entered here is ignored until those settings change.', 'memberful' ),
+ array( 'a' => array( 'href' => array() ) )
+ ),
+ esc_url( memberful_wp_plugin_global_marketing_url() )
+ );
+ ?>
+
+
+
+
+
+
From 3b40da0503fbfc59043c82e9e623a080056d78c6 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 5 May 2026 08:05:35 +1000
Subject: [PATCH 14/28] Fix code audit issues
---
.../memberful-wp/js/src/paywall-builder.js | 2 +-
.../plugins/memberful-wp/src/options.php | 32 +-
.../memberful-wp/src/paywall/config.php | 6 +-
.../memberful-wp/src/paywall/preview.php | 10 +-
.../memberful-wp/src/paywall/renderer.php | 666 +++++++++---------
.../memberful-wp/stylesheets/paywall.css | 80 +--
.../plugins/memberful-wp/views/metabox.php | 2 +-
.../views/paywall/builder-panel.php | 2 +-
8 files changed, 406 insertions(+), 394 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index a00985e7..72ec8641 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -2,7 +2,7 @@ jQuery(function ($) {
const $form = $('.memberful-paywall-builder__panel[data-panel="builder"]');
const $modeInputs = $('input[name="memberful_paywall[mode]"]');
const $panels = $('.memberful-paywall-builder__panel');
- const $preview = $('#mf-paywall-preview');
+ const $preview = $('#memberful-paywall-preview');
const $colorInput = $('.memberful-paywall-builder__color');
const preview = window.memberfulPaywallPreview || {};
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/options.php b/wordpress/wp-content/plugins/memberful-wp/src/options.php
index aa6a5a36..fd0351be 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/options.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/options.php
@@ -3,30 +3,30 @@
function memberful_wp_all_options(): array {
return array(
- 'memberful_client_id' => null,
- 'memberful_client_secret' => null,
- 'memberful_site' => null,
- 'memberful_custom_domain' => null,
- 'memberful_api_key' => null,
- 'memberful_webhook_secret' => null,
+ 'memberful_client_id' => NULL,
+ 'memberful_client_secret' => NULL,
+ 'memberful_site' => NULL,
+ 'memberful_custom_domain' => NULL,
+ 'memberful_api_key' => NULL,
+ 'memberful_webhook_secret' => NULL,
'memberful_products' => array(),
'memberful_subscriptions' => array(),
'memberful_acl' => array(),
- 'memberful_embed_enabled' => false,
+ 'memberful_embed_enabled' => FALSE,
'memberful_error_log' => array(),
'memberful_role_active_customer' => 'subscriber',
'memberful_role_inactive_customer' => 'subscriber',
'memberful_plan_role_mappings' => array(),
- 'memberful_use_per_plan_roles' => false,
+ 'memberful_use_per_plan_roles' => FALSE,
'memberful_posts_available_to_any_registered_user' => array(),
- 'memberful_hide_admin_toolbar' => true,
- 'memberful_block_dashboard_access' => true,
- 'memberful_filter_account_menu_items' => true,
- 'memberful_auto_sync_display_names' => false,
- 'memberful_show_protected_content_in_search' => false,
- 'memberful_use_global_marketing' => false,
- 'memberful_use_global_snippets' => true,
- 'memberful_global_marketing_override' => true,
+ 'memberful_hide_admin_toolbar' => TRUE,
+ 'memberful_block_dashboard_access' => TRUE,
+ 'memberful_filter_account_menu_items' => TRUE,
+ 'memberful_auto_sync_display_names' => FALSE,
+ 'memberful_show_protected_content_in_search' => FALSE,
+ 'memberful_use_global_marketing' => FALSE,
+ 'memberful_use_global_snippets' => TRUE,
+ 'memberful_global_marketing_override' => TRUE,
'memberful_global_marketing_content' => '',
'memberful_ad_provider_settings' => array(),
Memberful_Paywall_Config::OPTION_KEY => array(),
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index 62ad4889..7c5c3ac4 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -25,11 +25,11 @@ public static function defaults(): array {
return array(
'mode' => 'builder',
'layout' => 'card',
- 'heading' => esc_html__( 'Subscribe to keep reading', 'memberful' ),
+ 'heading' => __( 'Subscribe to keep reading', 'memberful' ),
'heading_tag' => 'h2',
- 'subheading' => esc_html__( 'This post is for paying subscribers.', 'memberful' ),
+ 'subheading' => __( 'This post is for paying subscribers.', 'memberful' ),
'features' => array(),
- 'button_label' => esc_html__( 'Subscribe', 'memberful' ),
+ 'button_label' => __( 'Subscribe', 'memberful' ),
'subscribe_url' => '',
'sign_in_url' => '',
'brand_color' => '',
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
index 9b2636c0..82c1f2b2 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/preview.php
@@ -47,7 +47,7 @@ public static function handle(): void {
public static function document( array $config ): string {
$body = Memberful_Paywall_Renderer::render( $config );
- $paywall_css = plugins_url( 'stylesheets/paywall.css', MEMBERFUL_PLUGIN_FILE );
+ $paywall_css = add_query_arg( 'ver', MEMBERFUL_VERSION, plugins_url( 'stylesheets/paywall.css', MEMBERFUL_PLUGIN_FILE ) );
$theme_css = get_stylesheet_uri();
$links = sprintf( ' ', esc_url( $paywall_css ) );
@@ -55,10 +55,12 @@ public static function document( array $config ): string {
$links .= sprintf( ' ', esc_url( $theme_css ) );
}
- $teaser_class = 'memberful-global-teaser-content memberful-global-teaser-content--mf-' . $config['layout'];
+ $layout = ( isset( $config['layout'] ) && in_array( $config['layout'], Memberful_Paywall_Config::LAYOUTS, true ) ) ? $config['layout'] : 'card';
+ $teaser_class = 'memberful-global-teaser-content memberful-global-teaser-content--memberful-' . $layout;
$teaser = sprintf(
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae urna id quam faucibus gravida ac sed ipsum. Quisque eget velit dictum leo tempor bibendum nec sed odio.
',
- esc_attr( $teaser_class )
+ '',
+ esc_attr( $teaser_class ),
+ esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae urna id quam faucibus gravida ac sed ipsum. Quisque eget velit dictum leo tempor bibendum nec sed odio.', 'memberful' )
);
$styles = 'html,body{background:#fff;color:#1b1b1b;font-size:16px;line-height:1.6;margin:0;}'
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index ae234cf6..373fec0e 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -13,334 +13,344 @@
* Renders a paywall config array to HTML.
*/
class Memberful_Paywall_Renderer {
- /**
- * Flag to determine if we should load paywall styles.
- *
- * @var bool
- */
- private static $should_print_styles = false;
-
- /**
- * Register the actions on plugin load.
- */
- public static function register(): void {
- add_filter( 'memberful_wp_protect_content', array( __CLASS__, 'protect_content' ) );
- add_action( 'wp_footer', array( __CLASS__, 'maybe_print_styles' ) );
- add_filter( 'memberful_global_teaser_class', array( __CLASS__, 'filter_teaser_class' ) );
- add_filter( 'memberful_teaser_css', array( __CLASS__, 'filter_teaser_css' ) );
- }
-
- /**
- * Append a layout modifier to the teaser wrapper class when the builder paywall is active.
- *
- * @param string $classes Default teaser wrapper class list.
- *
- * @return string
- */
- public static function filter_teaser_class( string $classes ): string {
- if ( ! self::is_builder_mode() ) {
- return $classes;
- }
-
- $config = Memberful_Paywall_Config::get();
-
- return $classes . ' memberful-global-teaser-content--mf-' . $config['layout'];
- }
-
- /**
- * Suppress the legacy inline teaser fade when the builder paywall owns the fade via paywall.css.
- *
- * @param string $css Legacy inline teaser CSS block.
- *
- * @return string
- */
- public static function filter_teaser_css( string $css ): string {
- return self::is_builder_mode() ? '' : $css;
- }
-
- /**
- * Conditionally flag paywall loading.
- *
- * @param string $content Content.
- *
- * @return string
- */
- public static function protect_content( string $content ): string {
- if ( self::is_builder_mode() ) {
- self::$should_print_styles = true;
- }
-
- return $content;
- }
-
- /**
- * Render paywall styles.
- */
- public static function maybe_print_styles(): void {
- if ( ! self::$should_print_styles ) {
- return;
- }
-
- wp_enqueue_style(
- 'memberful-paywall',
- MEMBERFUL_URL . '/stylesheets/paywall.css',
- array(),
- MEMBERFUL_VERSION
- );
- }
-
- /**
- * Render a paywall config to HTML.
- *
- * @param array $config Config shape from Memberful_Paywall_Config::get().
- *
- * @return string
- */
- public static function render( array $config ): string {
- $config = wp_parse_args( $config, Memberful_Paywall_Config::defaults() );
-
- $layout = in_array( $config['layout'], Memberful_Paywall_Config::LAYOUTS, true ) ? $config['layout'] : 'card';
- $method = 'render_' . $layout;
-
- return sprintf(
- '%3$s
',
- esc_attr( $layout ),
- esc_attr( self::wrapper_style( $config ) ),
- self::$method( $config )
- );
- }
-
- /**
- * Render the "simple" layout — minimal text + CTA on a transparent band.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function render_simple( array $config ): string {
- return ''
- . self::heading_block( $config )
- . self::subheading_block( $config )
- . self::features_block( $config )
- . '
' . self::primary_cta( $config ) . '
'
- . self::sign_in_prompt( $config )
- . '
';
- }
-
- /**
- * Render the "card" layout — centred white card with lock badge.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function render_card( array $config ): string {
- return ''
- . '
'
- . self::lock_badge()
- . self::heading_block( $config )
- . self::subheading_block( $config )
- . self::features_block( $config )
- . '
' . self::primary_cta( $config ) . '
'
- . self::sign_in_prompt( $config )
- . '
'
- . '
';
- }
-
- /**
- * Render the "banner" layout — full-width dark band.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function render_banner( array $config ): string {
- return ''
- . self::heading_block( $config )
- . self::subheading_block( $config )
- . self::features_block( $config )
- . '
' . self::primary_cta( $config ) . '
'
- . self::sign_in_prompt( $config )
- . '
';
- }
-
- /**
- * Heading element with the configured tag.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function heading_block( array $config ): string {
- $tag = in_array( $config['heading_tag'], Memberful_Paywall_Config::HEADING_TAGS, true ) ? $config['heading_tag'] : 'h2';
- return sprintf(
- '<%1$s class="memberful-paywall__heading">%2$s%1$s>',
- tag_escape( $tag ),
- esc_html( $config['heading'] )
- );
- }
-
- /**
- * Subheading paragraph, or empty when blank.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function subheading_block( array $config ): string {
- if ( '' === $config['subheading'] ) {
- return '';
- }
-
- return sprintf(
- '%s
',
- esc_html( $config['subheading'] )
- );
- }
-
- /**
- * Feature list with inline check icons, or empty when no features.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function features_block( array $config ): string {
- if ( empty( $config['features'] ) ) {
- return '';
- }
-
- $items = '';
- foreach ( $config['features'] as $feature ) {
- $items .= '' . self::check_icon() . '' . esc_html( $feature ) . ' ';
- }
-
- return '';
- }
-
- /**
- * Primary subscribe CTA anchor.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function primary_cta( array $config ): string {
- return sprintf(
- '%s ',
- esc_url( self::subscribe_url( $config ) ),
- esc_html( $config['button_label'] )
- );
- }
-
- /**
- * "Already a subscriber? Sign in" text prompt shown under every layout's CTA.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function sign_in_prompt( array $config ): string {
- return sprintf(
- '%s %s
',
- esc_html__( 'Already a subscriber?', 'memberful' ),
- esc_url( self::sign_in_url( $config ) ),
- esc_html__( 'Sign in', 'memberful' )
- );
- }
-
- /**
- * Circular lock badge shown at the top of the card layout.
- *
- * @return string
- */
- private static function lock_badge(): string {
- return ''
- . '
'
- . ' '
- . ' '
- . '
';
- }
-
- /**
- * Wrapper inline style carrying the brand colour and button radius custom properties.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function wrapper_style( array $config ): string {
- $parts = array( '--mf-radius:' . self::button_radius( $config['button_shape'] ) );
-
- if ( ! empty( $config['brand_color'] ) ) {
- $parts[] = '--mf-brand:' . $config['brand_color'];
- }
-
- return implode( ';', $parts ) . ';';
- }
-
- /**
- * Map the button-shape enum to a CSS radius.
- *
- * @param string $shape One of the `button_shape` enum values.
- *
- * @return string
- */
- private static function button_radius( string $shape ): string {
- switch ( $shape ) {
- case 'pill':
- return '999px';
- case 'square':
- return '0';
- case 'rounded':
- default:
- return '8px';
- }
- }
-
- /**
- * Resolve the subscribe URL, falling back to the Memberful registration page.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function subscribe_url( array $config ): string {
- return ! empty( $config['subscribe_url'] ) ? $config['subscribe_url'] : memberful_registration_page_url();
- }
-
- /**
- * Resolve the sign-in URL, falling back to the Memberful sign-in endpoint.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function sign_in_url( array $config ): string {
- return ! empty( $config['sign_in_url'] ) ? $config['sign_in_url'] : memberful_sign_in_url();
- }
-
- /**
- * Inline check-mark SVG used in the features list.
- *
- * @return string
- */
- private static function check_icon(): string {
- return ''
- . ' '
- . ' ';
- }
-
- /**
- * Whether the builder paywall is the active rendering mode.
- *
- * @return bool
- */
- private static function is_builder_mode(): bool {
- $config = Memberful_Paywall_Config::get();
-
- return 'builder' === $config['mode'];
- }
+ /**
+ * Flag to determine if we should load paywall styles.
+ *
+ * @var bool
+ */
+ private static $should_print_styles = false;
+
+ /**
+ * Register the actions on plugin load.
+ */
+ public static function register(): void {
+ add_filter( 'memberful_wp_protect_content', array( __CLASS__, 'protect_content' ) );
+ add_action( 'wp_footer', array( __CLASS__, 'maybe_print_styles' ) );
+ add_filter( 'memberful_global_teaser_class', array( __CLASS__, 'filter_teaser_class' ) );
+ add_filter( 'memberful_teaser_css', array( __CLASS__, 'filter_teaser_css' ) );
+ }
+
+ /**
+ * Append a layout modifier to the teaser wrapper class when the builder paywall is active.
+ *
+ * @param string $classes Default teaser wrapper class list.
+ *
+ * @return string
+ */
+ public static function filter_teaser_class( string $classes ): string {
+ if ( ! self::is_builder_mode() ) {
+ return $classes;
+ }
+
+ $config = Memberful_Paywall_Config::get();
+
+ return $classes . ' memberful-global-teaser-content--memberful-' . $config['layout'];
+ }
+
+ /**
+ * Suppress the legacy inline teaser fade when the builder paywall owns the fade via paywall.css.
+ *
+ * @param string $css Legacy inline teaser CSS block.
+ *
+ * @return string
+ */
+ public static function filter_teaser_css( string $css ): string {
+ return self::is_builder_mode() ? '' : $css;
+ }
+
+ /**
+ * Conditionally flag paywall loading.
+ *
+ * @param string $content Content.
+ *
+ * @return string
+ */
+ public static function protect_content( string $content ): string {
+ if ( self::is_builder_mode() ) {
+ self::$should_print_styles = true;
+ }
+
+ /**
+ * Filter the protected paywall content before it is returned.
+ *
+ * @param string $content Rendered paywall content.
+ */
+ return apply_filters( 'memberful_paywall_protected_content', $content );
+ }
+
+ /**
+ * Render paywall styles.
+ */
+ public static function maybe_print_styles(): void {
+ /**
+ * Filter whether the bundled paywall stylesheet should be enqueued.
+ *
+ * @param bool $should_print_styles Whether the stylesheet will be enqueued.
+ */
+ if ( ! apply_filters( 'memberful_paywall_print_styles', self::$should_print_styles ) ) {
+ return;
+ }
+
+ wp_enqueue_style(
+ 'memberful-paywall',
+ MEMBERFUL_URL . '/stylesheets/paywall.css',
+ array(),
+ MEMBERFUL_VERSION
+ );
+ }
+
+ /**
+ * Render a paywall config to HTML.
+ *
+ * @param array $config Config shape from Memberful_Paywall_Config::get().
+ *
+ * @return string
+ */
+ public static function render( array $config ): string {
+ $config = wp_parse_args( $config, Memberful_Paywall_Config::defaults() );
+
+ $layout = in_array( $config['layout'], Memberful_Paywall_Config::LAYOUTS, true ) ? $config['layout'] : 'card';
+ $method = 'render_' . $layout;
+
+ return sprintf(
+ '%3$s
',
+ esc_attr( $layout ),
+ esc_attr( self::wrapper_style( $config ) ),
+ self::$method( $config )
+ );
+ }
+
+ /**
+ * Render the "simple" layout — minimal text + CTA on a transparent band.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function render_simple( array $config ): string {
+ return '' . self::render_inner( $config ) . '
';
+ }
+
+ /**
+ * Render the "card" layout — centred white card with lock badge.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function render_card( array $config ): string {
+ return ''
+ . '
'
+ . self::lock_badge()
+ . self::render_inner( $config )
+ . '
'
+ . '
';
+ }
+
+ /**
+ * Render the "banner" layout — full-width dark band.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function render_banner( array $config ): string {
+ return '' . self::render_inner( $config ) . '
';
+ }
+
+ /**
+ * Shared inner content shared by every layout: heading, subheading, features, CTA, sign-in.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function render_inner( array $config ): string {
+ return self::heading_block( $config )
+ . self::subheading_block( $config )
+ . self::features_block( $config )
+ . '' . self::primary_cta( $config ) . '
'
+ . self::sign_in_prompt( $config );
+ }
+
+ /**
+ * Heading element with the configured tag.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function heading_block( array $config ): string {
+ $tag = in_array( $config['heading_tag'], Memberful_Paywall_Config::HEADING_TAGS, true ) ? $config['heading_tag'] : 'h2';
+ return sprintf(
+ '<%1$s class="memberful-paywall__heading">%2$s%1$s>',
+ tag_escape( $tag ),
+ esc_html( $config['heading'] )
+ );
+ }
+
+ /**
+ * Subheading paragraph, or empty when blank.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function subheading_block( array $config ): string {
+ if ( '' === $config['subheading'] ) {
+ return '';
+ }
+
+ return sprintf(
+ '%s
',
+ esc_html( $config['subheading'] )
+ );
+ }
+
+ /**
+ * Feature list with inline check icons, or empty when no features.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function features_block( array $config ): string {
+ if ( empty( $config['features'] ) ) {
+ return '';
+ }
+
+ $items = '';
+ foreach ( $config['features'] as $feature ) {
+ $items .= '' . self::check_icon() . '' . esc_html( $feature ) . ' ';
+ }
+
+ return '';
+ }
+
+ /**
+ * Primary subscribe CTA anchor.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function primary_cta( array $config ): string {
+ return sprintf(
+ '%s ',
+ esc_url( self::subscribe_url( $config ) ),
+ esc_html( $config['button_label'] )
+ );
+ }
+
+ /**
+ * "Already a subscriber? Sign in" text prompt shown under every layout's CTA.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function sign_in_prompt( array $config ): string {
+ return sprintf(
+ '%s %s
',
+ esc_html__( 'Already a subscriber?', 'memberful' ),
+ esc_url( self::sign_in_url( $config ) ),
+ esc_html__( 'Sign in', 'memberful' )
+ );
+ }
+
+ /**
+ * Circular lock badge shown at the top of the card layout.
+ *
+ * @return string
+ */
+ private static function lock_badge(): string {
+ return ''
+ . '
'
+ . ' '
+ . ' '
+ . '
';
+ }
+
+ /**
+ * Wrapper inline style carrying the brand colour and button radius custom properties.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function wrapper_style( array $config ): string {
+ $parts = array( '--memberful-radius:' . self::button_radius( $config['button_shape'] ) );
+
+ $brand_color = isset( $config['brand_color'] ) ? sanitize_hex_color( (string) $config['brand_color'] ) : '';
+ if ( ! empty( $brand_color ) ) {
+ $parts[] = '--memberful-brand:' . $brand_color;
+ }
+
+ return implode( ';', $parts ) . ';';
+ }
+
+ /**
+ * Map the button-shape enum to a CSS radius.
+ *
+ * @param string $shape One of the `button_shape` enum values.
+ *
+ * @return string
+ */
+ private static function button_radius( string $shape ): string {
+ switch ( $shape ) {
+ case 'pill':
+ return '999px';
+ case 'square':
+ return '0';
+ case 'rounded':
+ default:
+ return '8px';
+ }
+ }
+
+ /**
+ * Resolve the subscribe URL, falling back to the Memberful registration page.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function subscribe_url( array $config ): string {
+ return ! empty( $config['subscribe_url'] ) ? $config['subscribe_url'] : memberful_registration_page_url();
+ }
+
+ /**
+ * Resolve the sign-in URL, falling back to the Memberful sign-in endpoint.
+ *
+ * @param array $config Sanitized config.
+ *
+ * @return string
+ */
+ private static function sign_in_url( array $config ): string {
+ return ! empty( $config['sign_in_url'] ) ? $config['sign_in_url'] : memberful_sign_in_url();
+ }
+
+ /**
+ * Inline check-mark SVG used in the features list.
+ *
+ * @return string
+ */
+ private static function check_icon(): string {
+ return ''
+ . ' '
+ . ' ';
+ }
+
+ /**
+ * Whether the builder paywall is the active rendering mode.
+ *
+ * @return bool
+ */
+ private static function is_builder_mode(): bool {
+ $config = Memberful_Paywall_Config::get();
+
+ return 'builder' === $config['mode'];
+ }
}
Memberful_Paywall_Renderer::register();
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index 0ddaa2de..45f692a7 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -3,16 +3,16 @@
*/
.memberful-paywall {
- --mf-border: #e5e5e5;
- --mf-brand: var(--wp--preset--color--primary, var(--wp--preset--color--accent, var(--wp-admin-theme-color, #2271b1)));
- --mf-muted: #666;
- --mf-muted-soft: #999;
- --mf-radius: 0;
- --mf-surface: #fff;
- --mf-text: #1a1a1a;
+ --memberful-border: #e5e5e5;
+ --memberful-brand: var(--wp--preset--color--primary, var(--wp--preset--color--accent, var(--wp-admin-theme-color, #2271b1)));
+ --memberful-muted: #666;
+ --memberful-muted-soft: #999;
+ --memberful-radius: 0;
+ --memberful-surface: #fff;
+ --memberful-text: #1a1a1a;
box-sizing: border-box;
- color: var(--mf-text);
+ color: var(--memberful-text);
font-family: inherit;
line-height: 1.5;
margin: 0;
@@ -30,39 +30,39 @@
.memberful-paywall__inner {
margin: 0 auto;
max-width: 600px;
- padding: 2rem 1.5rem 1.5rem;
+ padding: var(--wp--preset--spacing--50, 2rem) var(--wp--preset--spacing--40, 1.5rem) var(--wp--preset--spacing--40, 1.5rem);
text-align: center;
}
.memberful-paywall__heading {
- color: var(--mf-text);
+ color: var(--memberful-text);
font-weight: 600;
line-height: 1.3;
margin: 0 0 0.5rem;
}
h1.memberful-paywall__heading {
- font-size: 1.5rem;
+ font-size: var(--wp--preset--font-size--large, 1.5rem);
}
h2.memberful-paywall__heading {
- font-size: 1.125rem;
+ font-size: var(--wp--preset--font-size--medium, 1.125rem);
}
h3.memberful-paywall__heading {
- font-size: 1rem;
+ font-size: var(--wp--preset--font-size--medium, 1rem);
}
.memberful-paywall__subheading {
- color: var(--mf-muted);
- font-size: 0.875rem;
+ color: var(--memberful-muted);
+ font-size: var(--wp--preset--font-size--small, 0.875rem);
margin: 0 auto 1.25rem;
max-width: 28rem;
}
.memberful-paywall__features {
- color: var(--mf-muted);
- column-gap: 1.5rem;
+ color: var(--memberful-muted);
+ column-gap: var(--wp--preset--spacing--40, 1.5rem);
display: inline-grid;
font-size: 0.9375rem;
grid-template-columns: repeat(2, auto);
@@ -80,7 +80,7 @@ h3.memberful-paywall__heading {
}
.memberful-paywall__check {
- color: var(--mf-brand);
+ color: var(--memberful-brand);
flex: 0 0 auto;
margin-top: 0.2rem;
}
@@ -96,9 +96,9 @@ h3.memberful-paywall__heading {
.memberful-paywall__button {
border: 1px solid transparent;
- border-radius: var(--mf-radius);
+ border-radius: var(--memberful-radius);
display: inline-block;
- font-size: 0.875rem;
+ font-size: var(--wp--preset--font-size--small, 0.875rem);
font-weight: 600;
line-height: 1.2;
padding: 0.5rem 1.5rem;
@@ -107,8 +107,8 @@ h3.memberful-paywall__heading {
}
.memberful-paywall__button--primary {
- background: var(--mf-brand);
- border-color: var(--mf-brand);
+ background: var(--memberful-brand);
+ border-color: var(--memberful-brand);
color: #fff;
}
@@ -118,19 +118,19 @@ h3.memberful-paywall__heading {
}
.memberful-paywall__signin {
- color: var(--mf-muted-soft);
+ color: var(--memberful-muted-soft);
font-size: 0.75rem;
- margin: 1rem 0 0;
+ margin: var(--wp--preset--spacing--30, 1rem) 0 0;
}
.memberful-paywall__signin-link {
- color: var(--mf-brand);
+ color: var(--memberful-brand);
text-decoration: underline;
}
.memberful-paywall__lock {
align-items: center;
- background: var(--mf-brand);
+ background: var(--memberful-brand);
border-radius: 50%;
color: #fff;
display: inline-flex;
@@ -142,19 +142,19 @@ h3.memberful-paywall__heading {
/* Simple — top-divider band on white. */
.memberful-paywall--simple {
- background: var(--mf-surface);
- border-top: 1px solid var(--mf-border);
+ background: var(--memberful-surface);
+ border-top: 1px solid var(--memberful-border);
}
/* Card — centred white card on muted page. */
.memberful-paywall--card {
- --mf-surface: #f8f8f8;
+ --memberful-surface: #f8f8f8;
- background: var(--mf-surface);
+ background: var(--memberful-surface);
}
.memberful-paywall--card .memberful-paywall__inner {
- padding: 1.5rem;
+ padding: var(--wp--preset--spacing--40, 1.5rem);
}
.memberful-paywall--card .memberful-paywall__card {
@@ -164,7 +164,7 @@ h3.memberful-paywall__heading {
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.1);
margin: 0 auto;
max-width: 24rem;
- padding: 2rem;
+ padding: var(--wp--preset--spacing--50, 2rem);
}
.memberful-paywall--card .memberful-paywall__heading {
@@ -179,12 +179,12 @@ h3.memberful-paywall__heading {
/* Banner — full-bleed dark band, always viewport width. */
.memberful-paywall--banner {
- --mf-muted: #a0a5aa;
- --mf-muted-soft: #72777c;
- --mf-surface: #1d2327;
- --mf-text: #fff;
+ --memberful-muted: #a0a5aa;
+ --memberful-muted-soft: #72777c;
+ --memberful-surface: #1d2327;
+ --memberful-text: #fff;
- background: var(--mf-surface);
+ background: var(--memberful-surface);
margin-inline: calc(50% - 50vw);
width: 100vw;
}
@@ -198,7 +198,7 @@ h3.memberful-paywall__heading {
}
.memberful-paywall--banner h2.memberful-paywall__heading {
- font-size: 1.25rem;
+ font-size: var(--wp--preset--font-size--medium, 1.25rem);
}
.memberful-paywall--banner .memberful-paywall__subheading {
@@ -214,11 +214,11 @@ h3.memberful-paywall__heading {
}
/* Teaser fade. */
-.memberful-global-teaser-content[class*="--mf-"] {
+.memberful-global-teaser-content[class*="--memberful-"] {
position: relative;
}
-.memberful-global-teaser-content[class*="--mf-"]::after {
+.memberful-global-teaser-content[class*="--memberful-"]::after {
background: linear-gradient(transparent, var(--wp--preset--color--background, #fff));
bottom: 0;
content: "";
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/metabox.php b/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
index 9d7343fa..961c663b 100755
--- a/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
@@ -24,7 +24,7 @@
From e50f529106b7fe4eea81f3035ab2479d9bbf16ce Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 5 May 2026 10:05:56 +1000
Subject: [PATCH 16/28] Fix FOC issue
---
.../plugins/memberful-wp/views/global_marketing.php | 9 +++++----
.../plugins/memberful-wp/views/paywall/builder-panel.php | 3 ++-
.../memberful-wp/views/paywall/custom-html-panel.php | 3 ++-
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
index abb195f4..cb1911d9 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
@@ -32,7 +32,7 @@
-
-
+
>
+
$paywall_config ) ); ?>
- $paywall_config ) ); ?>
- $global_marketing_content ) ); ?>
+ $paywall_config, 'is_active' => 'builder' === $paywall_mode ) ); ?>
+ $global_marketing_content, 'is_active' => 'custom_html' === $paywall_mode ) ); ?>
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 355a3fa2..659e94ef 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -5,11 +5,12 @@
* @package memberful-wp
*
* @var array $paywall_config
+ * @var bool $is_active
*/
$features_textarea = implode( "\n", (array) $paywall_config['features'] );
?>
-
+
>
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
index f3645318..02338507 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
@@ -5,9 +5,10 @@
* @package memberful-wp
*
* @var string $global_marketing_content
+ * @var bool $is_active
*/
?>
-
+
>
\ No newline at end of file
From cfef9f65bb0879531d9c40b11cb652ef26975198 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 5 May 2026 10:27:38 +1000
Subject: [PATCH 17/28] Improve paywall logic
---
wordpress/wp-content/plugins/memberful-wp/src/metabox.php | 6 ++----
.../wp-content/plugins/memberful-wp/stylesheets/paywall.css | 1 +
wordpress/wp-content/plugins/memberful-wp/views/metabox.php | 2 +-
3 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/metabox.php b/wordpress/wp-content/plugins/memberful-wp/src/metabox.php
index 19952116..27908fb8 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/metabox.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/metabox.php
@@ -20,10 +20,8 @@ function memberful_wp_metabox_types() {
}
function memberful_global_marketing_overrides_post_content() {
- $paywall_config = Memberful_Paywall_Config::get();
-
- return get_option( 'memberful_global_marketing_override' )
- || 'builder' === $paywall_config['mode'];
+ return get_option( 'memberful_use_global_marketing' )
+ && get_option( 'memberful_global_marketing_override' );
}
function memberful_wp_add_metabox() {
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index 45f692a7..10360b04 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -187,6 +187,7 @@ h3.memberful-paywall__heading {
background: var(--memberful-surface);
margin-inline: calc(50% - 50vw);
width: 100vw;
+ z-index: 99999;
}
.memberful-paywall--banner .memberful-paywall__inner {
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/metabox.php b/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
index 2b3f2df4..28ec7373 100755
--- a/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/metabox.php
@@ -12,7 +12,7 @@
printf(
wp_kses(
/* translators: %s: URL to the global marketing settings screen */
- __( 'Marketing content is currently controlled by the global marketing settings . Anything entered here is ignored until those settings change.', 'memberful' ),
+ __( 'Marketing content is currently controlled by the global marketing settings .', 'memberful' ),
array( 'a' => array( 'href' => array() ) )
),
esc_url( memberful_wp_plugin_global_marketing_url() )
From 4c5ec3182dcfa22575e0dbe09235653b8f6b4a84 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 5 May 2026 11:25:40 +1000
Subject: [PATCH 18/28] Improve paywall banner theme compatibility
---
.../memberful-wp/js/src/paywall-banner.js | 69 +++++++++++++++++++
.../memberful-wp/src/paywall/renderer.php | 12 ++++
.../memberful-wp/stylesheets/paywall.css | 2 +-
3 files changed, 82 insertions(+), 1 deletion(-)
create mode 100644 wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js
new file mode 100644
index 00000000..a4bf850d
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js
@@ -0,0 +1,69 @@
+/**
+ * Memberful paywall banner - measure-based full-bleed.
+ *
+ * Pure CSS can't make a banner span the visible viewport when the post column
+ * is offset by a sidebar (classic themes with non-centered content). This
+ * script measures the banner's parent against documentElement.clientWidth and
+ * applies inline margin/width so the banner reaches both viewport edges
+ * exactly. Runs synchronously from a footer enqueue so first paint is correct
+ * (no flash of off-center content).
+ */
+( function () {
+ 'use strict';
+
+ function measure( banner ) {
+ var parent = banner.parentElement;
+ if ( ! parent ) {
+ return;
+ }
+
+ // Reset any previous measurement so parent rect reflects natural flow.
+ banner.style.marginInlineStart = '';
+ banner.style.marginInlineEnd = '';
+ banner.style.width = '';
+
+ var viewportWidth = document.documentElement.clientWidth;
+ var parentRect = parent.getBoundingClientRect();
+
+ var startMargin = -parentRect.left;
+ var endMargin = parentRect.right - viewportWidth;
+
+ banner.style.marginInlineStart = startMargin + 'px';
+ banner.style.marginInlineEnd = endMargin + 'px';
+ banner.style.width = viewportWidth + 'px';
+ }
+
+ function init() {
+ var banners = document.querySelectorAll( '.memberful-paywall--banner' );
+ if ( ! banners.length ) {
+ return;
+ }
+
+ banners.forEach( function ( banner ) {
+ measure( banner );
+
+ var pending = null;
+ var schedule = function () {
+ if ( pending !== null ) {
+ return;
+ }
+ pending = window.requestAnimationFrame( function () {
+ pending = null;
+ measure( banner );
+ } );
+ };
+
+ window.addEventListener( 'resize', schedule, { passive: true } );
+
+ if ( typeof ResizeObserver !== 'undefined' && banner.parentElement ) {
+ new ResizeObserver( schedule ).observe( banner.parentElement );
+ }
+ } );
+ }
+
+ if ( document.readyState === 'loading' ) {
+ document.addEventListener( 'DOMContentLoaded', init );
+ } else {
+ init();
+ }
+} )();
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index 373fec0e..d5f62833 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -97,6 +97,18 @@ public static function maybe_print_styles(): void {
array(),
MEMBERFUL_VERSION
);
+
+ if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) {
+ return;
+ }
+
+ wp_enqueue_script(
+ 'memberful-paywall-banner',
+ MEMBERFUL_URL . '/js/src/paywall-banner.js',
+ array(),
+ MEMBERFUL_VERSION,
+ true
+ );
}
/**
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index 10360b04..cc40601d 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -120,7 +120,7 @@ h3.memberful-paywall__heading {
.memberful-paywall__signin {
color: var(--memberful-muted-soft);
font-size: 0.75rem;
- margin: var(--wp--preset--spacing--30, 1rem) 0 0;
+ margin: var(--wp--preset--spacing--30, 1rem) 0 0 !important; /* fixes compatibility with some themes */
}
.memberful-paywall__signin-link {
From d1740cf1d17e7256c5c53682bb2975a172c75b3a Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Tue, 5 May 2026 11:38:10 +1000
Subject: [PATCH 19/28] Fix another round of flagged issues
---
wordpress/wp-content/plugins/memberful-wp/src/admin.php | 4 ++--
.../wp-content/plugins/memberful-wp/src/paywall/renderer.php | 2 +-
.../wp-content/plugins/memberful-wp/stylesheets/paywall.css | 2 +-
.../plugins/memberful-wp/views/global_marketing.php | 2 +-
.../plugins/memberful-wp/views/paywall/custom-html-panel.php | 4 ++--
wordpress/wp-content/plugins/memberful-wp/webpack.config.js | 1 +
6 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/admin.php b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
index 0aef6dd2..f0d5baf1 100755
--- a/wordpress/wp-content/plugins/memberful-wp/src/admin.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/admin.php
@@ -726,9 +726,9 @@ function memberful_wp_global_marketing() {
Memberful_Paywall_Config::save( $paywall_input );
}
- $config_mode = ( is_array( $paywall_input ) && isset( $paywall_input['mode'] ) ) ? $paywall_input['mode'] : 'builder';
+ $config_mode = ( is_array( $paywall_input ) && isset( $paywall_input['mode'] ) && in_array( $paywall_input['mode'], Memberful_Paywall_Config::MODES, true ) ) ? $paywall_input['mode'] : 'builder';
if ( 'custom_html' === $config_mode ) {
- update_option( 'memberful_global_marketing_content', memberful_wp_kses_post( filter_input( INPUT_POST, 'memberful_global_marketing_content' ) ) );
+ update_option( 'memberful_global_marketing_content', memberful_wp_kses_post( (string) filter_input( INPUT_POST, 'memberful_global_marketing_content' ) ) );
}
} else {
update_option( 'memberful_use_global_marketing', false );
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index d5f62833..098818f9 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -104,7 +104,7 @@ public static function maybe_print_styles(): void {
wp_enqueue_script(
'memberful-paywall-banner',
- MEMBERFUL_URL . '/js/src/paywall-banner.js',
+ MEMBERFUL_URL . '/js/build/paywall-banner.js',
array(),
MEMBERFUL_VERSION,
true
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index cc40601d..a2dc7af8 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -50,7 +50,7 @@ h2.memberful-paywall__heading {
}
h3.memberful-paywall__heading {
- font-size: var(--wp--preset--font-size--medium, 1rem);
+ font-size: var(--wp--preset--font-size--small, 1rem);
}
.memberful-paywall__subheading {
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
index cb1911d9..03b3d4e2 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/global_marketing.php
@@ -55,7 +55,7 @@
>
-
+
$paywall_config ) ); ?>
$paywall_config, 'is_active' => 'builder' === $paywall_mode ) ); ?>
$global_marketing_content, 'is_active' => 'custom_html' === $paywall_mode ) ); ?>
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
index 02338507..55985843 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/custom-html-panel.php
@@ -10,5 +10,5 @@
?>
>
-
-
\ No newline at end of file
+
+
diff --git a/wordpress/wp-content/plugins/memberful-wp/webpack.config.js b/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
index 9a57145c..96e70f6d 100644
--- a/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
+++ b/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
@@ -5,6 +5,7 @@ module.exports = {
entry: {
...defaultConfig.entry,
"editor-scripts": "./js/src/editor-scripts.js",
+ "paywall-banner": "./js/src/paywall-banner.js",
"paywall-builder": "./js/src/paywall-builder.js",
},
};
From 0e5904442ac8075ae70f2d1833ec3dfac4204e2f Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 6 May 2026 14:32:28 +1000
Subject: [PATCH 20/28] Fix save button
---
wordpress/wp-content/plugins/memberful-wp/js/src/admin.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/admin.js b/wordpress/wp-content/plugins/memberful-wp/js/src/admin.js
index 1bc1984a..50a6a589 100755
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/admin.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/admin.js
@@ -64,16 +64,17 @@ jQuery(document).ready(function($){
let editor = tinyMCE.editors[0];
let globalContent=$('#use_global_marketing_checkbox');
let snippetContent=$('#use_global_snippets_checkbox');
+ let modeRadios=$('input[name="memberful_paywall[mode]"]');
function checkGlobalValidity(e){
let isGlobal=globalContent.is(':checked');
- let isSnippets=snippetContent.is(':checked');
+ let isCustomHtml=modeRadios.filter(':checked').val() === 'custom_html';
let submit=$('button[type="submit"]');
let isContentEmpty=!editor.getContent().trim();
let warning=$('#global_content_required');
- if( isGlobal && isContentEmpty ){
+ if( isGlobal && isCustomHtml && isContentEmpty ){
submit.prop('disabled', true);
warning.show();
@@ -86,6 +87,7 @@ jQuery(document).ready(function($){
globalContent.change(checkGlobalValidity)
snippetContent.change(checkGlobalValidity)
+ modeRadios.change(checkGlobalValidity)
editor.on('change', checkGlobalValidity);
}
From 01df5b48af21ecfc34d1f69186da2ba73a3a0594 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 13 May 2026 15:42:28 +1000
Subject: [PATCH 21/28] Drop banner layout
---
.../memberful-wp/js/src/paywall-banner.js | 69 -------------------
.../memberful-wp/src/paywall/config.php | 2 +-
.../memberful-wp/src/paywall/renderer.php | 27 +-------
.../memberful-wp/stylesheets/admin.css | 10 +--
.../memberful-wp/stylesheets/paywall.css | 43 +-----------
.../views/paywall/builder-panel.php | 21 ++----
.../plugins/memberful-wp/webpack.config.js | 1 -
7 files changed, 12 insertions(+), 161 deletions(-)
delete mode 100644 wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js
deleted file mode 100644
index a4bf850d..00000000
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-banner.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Memberful paywall banner - measure-based full-bleed.
- *
- * Pure CSS can't make a banner span the visible viewport when the post column
- * is offset by a sidebar (classic themes with non-centered content). This
- * script measures the banner's parent against documentElement.clientWidth and
- * applies inline margin/width so the banner reaches both viewport edges
- * exactly. Runs synchronously from a footer enqueue so first paint is correct
- * (no flash of off-center content).
- */
-( function () {
- 'use strict';
-
- function measure( banner ) {
- var parent = banner.parentElement;
- if ( ! parent ) {
- return;
- }
-
- // Reset any previous measurement so parent rect reflects natural flow.
- banner.style.marginInlineStart = '';
- banner.style.marginInlineEnd = '';
- banner.style.width = '';
-
- var viewportWidth = document.documentElement.clientWidth;
- var parentRect = parent.getBoundingClientRect();
-
- var startMargin = -parentRect.left;
- var endMargin = parentRect.right - viewportWidth;
-
- banner.style.marginInlineStart = startMargin + 'px';
- banner.style.marginInlineEnd = endMargin + 'px';
- banner.style.width = viewportWidth + 'px';
- }
-
- function init() {
- var banners = document.querySelectorAll( '.memberful-paywall--banner' );
- if ( ! banners.length ) {
- return;
- }
-
- banners.forEach( function ( banner ) {
- measure( banner );
-
- var pending = null;
- var schedule = function () {
- if ( pending !== null ) {
- return;
- }
- pending = window.requestAnimationFrame( function () {
- pending = null;
- measure( banner );
- } );
- };
-
- window.addEventListener( 'resize', schedule, { passive: true } );
-
- if ( typeof ResizeObserver !== 'undefined' && banner.parentElement ) {
- new ResizeObserver( schedule ).observe( banner.parentElement );
- }
- } );
- }
-
- if ( document.readyState === 'loading' ) {
- document.addEventListener( 'DOMContentLoaded', init );
- } else {
- init();
- }
-} )();
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index 7c5c3ac4..db7e939c 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -12,7 +12,7 @@ class Memberful_Paywall_Config {
const OPTION_KEY = 'memberful_paywall_config';
const MODES = array( 'builder', 'custom_html' );
- const LAYOUTS = array( 'simple', 'card', 'banner' );
+ const LAYOUTS = array( 'inline', 'card' );
const HEADING_TAGS = array( 'h1', 'h2', 'h3' );
const BUTTON_SHAPES = array( 'pill', 'rounded', 'square' );
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index 098818f9..faed3682 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -97,18 +97,6 @@ public static function maybe_print_styles(): void {
array(),
MEMBERFUL_VERSION
);
-
- if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) {
- return;
- }
-
- wp_enqueue_script(
- 'memberful-paywall-banner',
- MEMBERFUL_URL . '/js/build/paywall-banner.js',
- array(),
- MEMBERFUL_VERSION,
- true
- );
}
/**
@@ -133,13 +121,13 @@ public static function render( array $config ): string {
}
/**
- * Render the "simple" layout — minimal text + CTA on a transparent band.
+ * Render the "inline" layout - minimal text + CTA on a transparent band.
*
* @param array $config Sanitized config.
*
* @return string
*/
- private static function render_simple( array $config ): string {
+ private static function render_inline( array $config ): string {
return '' . self::render_inner( $config ) . '
';
}
@@ -159,17 +147,6 @@ private static function render_card( array $config ): string {
. ' ';
}
- /**
- * Render the "banner" layout — full-width dark band.
- *
- * @param array $config Sanitized config.
- *
- * @return string
- */
- private static function render_banner( array $config ): string {
- return '' . self::render_inner( $config ) . '
';
- }
-
/**
* Shared inner content shared by every layout: heading, subheading, features, CTA, sign-in.
*
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index b2265ce4..c6315aa1 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -344,7 +344,7 @@ Paywall Builder
.memberful-paywall-builder__template-grid {
display: grid;
gap: 16px;
- grid-template-columns: repeat(3, minmax(0, 1fr));
+ grid-template-columns: repeat(2, minmax(0, 1fr));
}
.memberful-paywall-builder__template-card {
cursor: pointer;
@@ -399,18 +399,12 @@ Paywall Builder
justify-content: center;
position: relative;
}
-.memberful-paywall-builder__template-thumb--simple {
+.memberful-paywall-builder__template-thumb--inline {
background: #f6f7f7;
}
.memberful-paywall-builder__template-thumb--card {
background: #f0f0f1;
}
-.memberful-paywall-builder__template-thumb--banner {
- background: #1d2327;
-}
-.memberful-paywall-builder__template-thumb--banner .memberful-paywall-builder__thumb-line {
- background: #50575e;
-}
.memberful-paywall-builder__thumb-line {
background: #c3c4c7;
border-radius: 2px;
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index a2dc7af8..caef459e 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -140,10 +140,10 @@ h3.memberful-paywall__heading {
width: 40px;
}
-/* Simple — top-divider band on white. */
-.memberful-paywall--simple {
+/* Inline - top-divider band on white. */
+.memberful-paywall--inline {
background: var(--memberful-surface);
- border-top: 1px solid var(--memberful-border);
+ border-top: 2px solid var(--memberful-brand);
}
/* Card — centred white card on muted page. */
@@ -177,43 +177,6 @@ h3.memberful-paywall__heading {
width: 100%;
}
-/* Banner — full-bleed dark band, always viewport width. */
-.memberful-paywall--banner {
- --memberful-muted: #a0a5aa;
- --memberful-muted-soft: #72777c;
- --memberful-surface: #1d2327;
- --memberful-text: #fff;
-
- background: var(--memberful-surface);
- margin-inline: calc(50% - 50vw);
- width: 100vw;
- z-index: 99999;
-}
-
-.memberful-paywall--banner .memberful-paywall__inner {
- padding: 2.5rem 2rem;
-}
-
-.memberful-paywall--banner .memberful-paywall__heading {
- font-weight: 700;
-}
-
-.memberful-paywall--banner h2.memberful-paywall__heading {
- font-size: var(--wp--preset--font-size--medium, 1.25rem);
-}
-
-.memberful-paywall--banner .memberful-paywall__subheading {
- max-width: 32rem;
-}
-
-.memberful-paywall--banner .memberful-paywall__signin-link {
- color: #72aee6;
-}
-
-.memberful-paywall--banner .memberful-paywall__button--primary {
- padding: 0.75rem 2rem;
-}
-
/* Teaser fade. */
.memberful-global-teaser-content[class*="--memberful-"] {
position: relative;
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 659e94ef..4d407891 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -15,15 +15,15 @@
- >
+ >
-
+
-
-
+
+
@@ -39,19 +39,6 @@
-
- >
-
-
-
-
-
-
-
-
-
-
-
diff --git a/wordpress/wp-content/plugins/memberful-wp/webpack.config.js b/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
index 96e70f6d..9a57145c 100644
--- a/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
+++ b/wordpress/wp-content/plugins/memberful-wp/webpack.config.js
@@ -5,7 +5,6 @@ module.exports = {
entry: {
...defaultConfig.entry,
"editor-scripts": "./js/src/editor-scripts.js",
- "paywall-banner": "./js/src/paywall-banner.js",
"paywall-builder": "./js/src/paywall-builder.js",
},
};
From efe0fb15ab19ebc6ea5baab1d70c614ce2361615 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 13 May 2026 17:17:34 +1000
Subject: [PATCH 22/28] Adjust layout, removing heading selector
---
.../memberful-wp/js/src/paywall-builder.js | 1 -
.../memberful-wp/src/paywall/config.php | 2 -
.../memberful-wp/src/paywall/renderer.php | 6 +-
.../memberful-wp/src/paywall/sanitizer.php | 1 -
.../memberful-wp/stylesheets/admin.css | 16 ++-
.../memberful-wp/stylesheets/paywall.css | 10 +-
.../views/paywall/builder-panel.php | 100 ++++++++----------
7 files changed, 60 insertions(+), 76 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index 72ec8641..291598dc 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -45,7 +45,6 @@ jQuery(function ($) {
mode: $('input[name="memberful_paywall[mode]"]:checked').val() || 'builder',
layout: $('input[name="memberful_paywall[layout]"]:checked').val() || 'card',
heading: $('#memberful-paywall-heading').val() || '',
- heading_tag: $('#memberful-paywall-heading-tag').val() || 'h2',
subheading: $('#memberful-paywall-subheading').val() || '',
features: $('#memberful-paywall-features').val() || '',
button_label: $('#memberful-paywall-button-label').val() || '',
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index db7e939c..b779a552 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -13,7 +13,6 @@ class Memberful_Paywall_Config {
const MODES = array( 'builder', 'custom_html' );
const LAYOUTS = array( 'inline', 'card' );
- const HEADING_TAGS = array( 'h1', 'h2', 'h3' );
const BUTTON_SHAPES = array( 'pill', 'rounded', 'square' );
/**
@@ -26,7 +25,6 @@ public static function defaults(): array {
'mode' => 'builder',
'layout' => 'card',
'heading' => __( 'Subscribe to keep reading', 'memberful' ),
- 'heading_tag' => 'h2',
'subheading' => __( 'This post is for paying subscribers.', 'memberful' ),
'features' => array(),
'button_label' => __( 'Subscribe', 'memberful' ),
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index faed3682..8e063095 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -163,17 +163,15 @@ private static function render_inner( array $config ): string {
}
/**
- * Heading element with the configured tag.
+ * Heading element.
*
* @param array $config Sanitized config.
*
* @return string
*/
private static function heading_block( array $config ): string {
- $tag = in_array( $config['heading_tag'], Memberful_Paywall_Config::HEADING_TAGS, true ) ? $config['heading_tag'] : 'h2';
return sprintf(
- '<%1$s class="memberful-paywall__heading">%2$s%1$s>',
- tag_escape( $tag ),
+ '%s ',
esc_html( $config['heading'] )
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
index 32a7eb2b..967a6024 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
@@ -24,7 +24,6 @@ public static function sanitize( array $input, array $defaults ): array {
$enums = array(
'mode' => Memberful_Paywall_Config::MODES,
'layout' => Memberful_Paywall_Config::LAYOUTS,
- 'heading_tag' => Memberful_Paywall_Config::HEADING_TAGS,
'button_shape' => Memberful_Paywall_Config::BUTTON_SHAPES,
);
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index c6315aa1..1509996a 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -441,17 +441,25 @@ Paywall Builder
line-height: 1.35;
}
-/* Two-column split: customize | preview */
-.memberful-paywall-builder__split {
+/* Two-column layout: settings | preview */
+.memberful-paywall-builder__panel[data-panel="builder"] {
align-items: start;
display: grid;
gap: 32px;
- grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+ grid-template-columns: minmax(0, 540px) minmax(0, 1fr);
+}
+.memberful-paywall-builder__preview {
+ align-self: start;
+ position: sticky;
+ top: 32px;
}
@media (max-width: 900px) {
- .memberful-paywall-builder__split {
+ .memberful-paywall-builder__panel[data-panel="builder"] {
grid-template-columns: minmax(0, 1fr);
}
+ .memberful-paywall-builder__preview {
+ position: static;
+ }
}
.memberful-paywall-builder__customize .memberful-paywall-builder__field {
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index caef459e..1efadb33 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -41,18 +41,10 @@
margin: 0 0 0.5rem;
}
-h1.memberful-paywall__heading {
- font-size: var(--wp--preset--font-size--large, 1.5rem);
-}
-
-h2.memberful-paywall__heading {
+.memberful-paywall__heading {
font-size: var(--wp--preset--font-size--medium, 1.125rem);
}
-h3.memberful-paywall__heading {
- font-size: var(--wp--preset--font-size--small, 1rem);
-}
-
.memberful-paywall__subheading {
color: var(--memberful-muted);
font-size: var(--wp--preset--font-size--small, 0.875rem);
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 4d407891..27ee75ca 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -11,55 +11,45 @@
$features_textarea = implode( "\n", (array) $paywall_config['features'] );
?>
>
-
-
-
-
- >
-
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
-
- >H1
- >H2
- >H3
-
-
-
+
+
+
+
@@ -104,16 +94,16 @@
+
-
-
-
-
+
+
+
-
+
\ No newline at end of file
From ad9e596b023f4c00d81e40d971d75e28775a2c8d Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 13 May 2026 19:48:32 +1000
Subject: [PATCH 23/28] Itemised benefits functionality
---
.../memberful-wp/js/src/paywall-builder.js | 33 ++++++++++-
.../memberful-wp/stylesheets/admin.css | 56 ++++++++++++++++++-
.../memberful-wp/stylesheets/paywall.css | 7 +--
.../views/paywall/builder-panel.php | 36 ++++++++++--
4 files changed, 118 insertions(+), 14 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index 291598dc..3cdc66f0 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -46,7 +46,9 @@ jQuery(function ($) {
layout: $('input[name="memberful_paywall[layout]"]:checked').val() || 'card',
heading: $('#memberful-paywall-heading').val() || '',
subheading: $('#memberful-paywall-subheading').val() || '',
- features: $('#memberful-paywall-features').val() || '',
+ features: $('#memberful-paywall-benefits .memberful-paywall-builder__benefit-input').map(function () {
+ return $(this).val();
+ }).get(),
button_label: $('#memberful-paywall-button-label').val() || '',
subscribe_url: $('#memberful-paywall-subscribe-url').val() || '',
sign_in_url: $('#memberful-paywall-signin-url').val() || '',
@@ -69,7 +71,14 @@ jQuery(function ($) {
const config = collectConfig();
Object.keys(config).forEach(function (key) {
- body.append('config[' + key + ']', config[key]);
+ const value = config[key];
+ if (Array.isArray(value)) {
+ value.forEach(function (item) {
+ body.append('config[' + key + '][]', item);
+ });
+ } else {
+ body.append('config[' + key + ']', value);
+ }
});
fetch(preview.ajaxUrl, {
@@ -107,6 +116,26 @@ jQuery(function ($) {
$form.on('input', 'input[type="text"], input[type="url"], textarea', scheduleRefresh);
$form.on('change', 'input[type="radio"], select', refreshPreview);
+ $form.on('click', '.memberful-paywall-builder__benefit-add', function (e) {
+ e.preventDefault();
+
+ const template = document.getElementById('memberful-paywall-benefit-template');
+ const list = document.getElementById('memberful-paywall-benefits');
+ if (!template || !list) {
+ return;
+ }
+
+ list.appendChild(template.content.cloneNode(true));
+ $(list).find('.memberful-paywall-builder__benefit-input').last().trigger('focus');
+ refreshPreview();
+ });
+
+ $form.on('click', '.memberful-paywall-builder__benefit-remove', function (e) {
+ e.preventDefault();
+ $(this).closest('.memberful-paywall-builder__benefit').remove();
+ refreshPreview();
+ });
+
if (($modeInputs.filter(':checked').val() || 'builder') === 'builder') {
refreshPreview();
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 1509996a..e2ae6c47 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -463,12 +463,10 @@ Paywall Builder
}
.memberful-paywall-builder__customize .memberful-paywall-builder__field {
- display: block;
margin: 0 0 1rem;
}
.memberful-paywall-builder__customize label {
display: block;
- font-size: 13px;
font-weight: 600;
margin-bottom: 4px;
}
@@ -500,6 +498,60 @@ Paywall Builder
min-width: 80px;
}
+/* Benefits */
+.memberful-paywall-builder__benefits-label {
+ font-weight: 600;
+ margin-bottom: 8px;
+ padding: 0;
+}
+.memberful-paywall-builder__benefit-list {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 8px;
+}
+.memberful-paywall-builder__benefit {
+ align-items: center;
+ display: flex;
+ gap: 8px;
+}
+.memberful-paywall-builder__benefit-icon {
+ color: var(--wp-admin-theme-color);
+ flex: 0 0 auto;
+}
+.memberful-paywall-builder__customize .memberful-paywall-builder__benefit-label {
+ flex: 1;
+ margin: 0;
+}
+.memberful-paywall-builder__benefit-remove {
+ background: none;
+ border: 0;
+ color: #646970;
+ cursor: pointer;
+ display: inline-flex;
+ opacity: 0;
+ padding: 4px;
+ transition: opacity 120ms ease;
+}
+.memberful-paywall-builder__benefit:hover .memberful-paywall-builder__benefit-remove,
+.memberful-paywall-builder__benefit-remove:focus-visible {
+ opacity: 1;
+}
+.memberful-paywall-builder__benefit-remove:hover {
+ color: #1d2327;
+}
+.memberful-paywall-builder__benefit-add {
+ background: none;
+ border: 0;
+ color: var(--wp-admin-theme-color);
+ cursor: pointer;
+ font-weight: 500;
+ padding: 4px 0;
+}
+.memberful-paywall-builder__benefit-add:hover {
+ color: var(--wp-admin-theme-color-darker-10);
+}
+
.memberful-paywall-builder__preview-frame {
background: #fff;
border: 1px solid #dcdcde;
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index 1efadb33..e75b81ed 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -54,14 +54,13 @@
.memberful-paywall__features {
color: var(--memberful-muted);
- column-gap: var(--wp--preset--spacing--40, 1.5rem);
- display: inline-grid;
+ display: inline-flex;
+ flex-direction: column;
font-size: 0.9375rem;
- grid-template-columns: repeat(2, auto);
+ gap: 0.375rem;
list-style: none;
margin: 0 0 1.25rem;
padding: 0;
- row-gap: 0.375rem;
text-align: left;
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 27ee75ca..1a22111e 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -8,7 +8,6 @@
* @var bool $is_active
*/
-$features_textarea = implode( "\n", (array) $paywall_config['features'] );
?>
>
@@ -71,11 +70,36 @@
-
-
-
-
-
+
+
+
+ +
+
+
+
+
From 45faa2cdc91abff006b751834d0eb05914946d0b Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Wed, 13 May 2026 19:55:24 +1000
Subject: [PATCH 24/28] Minor style adjustments
---
.../memberful-wp/stylesheets/admin.css | 9 ++++--
.../views/paywall/builder-panel.php | 32 +++++++++----------
2 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index e2ae6c47..6f8c04ca 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -465,10 +465,14 @@ Paywall Builder
.memberful-paywall-builder__customize .memberful-paywall-builder__field {
margin: 0 0 1rem;
}
-.memberful-paywall-builder__customize label {
+.memberful-paywall-builder__customize label,
+.memberful-paywall-builder__benefits-label {
+ color: #6b7280;
display: block;
- font-weight: 600;
+ font-size: 13px;
+ font-weight: 500;
margin-bottom: 4px;
+ text-transform: uppercase;
}
.memberful-paywall-builder__customize input[type="text"],
.memberful-paywall-builder__customize input[type="url"],
@@ -500,7 +504,6 @@ Paywall Builder
/* Benefits */
.memberful-paywall-builder__benefits-label {
- font-weight: 600;
margin-bottom: 8px;
padding: 0;
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 1a22111e..9e7df61f 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -55,21 +55,6 @@
-
-
-
-
-
-
-
-
- >
- >
- >
-
-
-
-
@@ -101,6 +86,21 @@
+
+
+
+
+
+
+
+
+ >
+ >
+ >
+
+
+
+
@@ -130,4 +130,4 @@ class="memberful-paywall-builder__preview-frame"
sandbox
>
-
\ No newline at end of file
+
From 16f45d8f68207aac737bc76616987392effe11ac Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Thu, 14 May 2026 08:55:27 +1000
Subject: [PATCH 25/28] Adjust accent and background colors functionality
---
.../memberful-wp/js/src/paywall-builder.js | 62 +++++++++------
.../plugins/memberful-wp/src/paywall.php | 1 +
.../memberful-wp/src/paywall/color.php | 76 +++++++++++++++++++
.../memberful-wp/src/paywall/config.php | 21 ++---
.../memberful-wp/src/paywall/renderer.php | 6 ++
.../memberful-wp/src/paywall/sanitizer.php | 10 ++-
.../memberful-wp/stylesheets/admin.css | 12 +++
.../memberful-wp/stylesheets/paywall.css | 12 ++-
.../views/paywall/builder-panel.php | 42 +++++++++-
9 files changed, 195 insertions(+), 47 deletions(-)
create mode 100644 wordpress/wp-content/plugins/memberful-wp/src/paywall/color.php
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index 3cdc66f0..57fced03 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -1,9 +1,9 @@
jQuery(function ($) {
- const $form = $('.memberful-paywall-builder__panel[data-panel="builder"]');
- const $modeInputs = $('input[name="memberful_paywall[mode]"]');
- const $panels = $('.memberful-paywall-builder__panel');
- const $preview = $('#memberful-paywall-preview');
- const $colorInput = $('.memberful-paywall-builder__color');
+ const $form = $('.memberful-paywall-builder__panel[data-panel="builder"]');
+ const $modeInputs = $('input[name="memberful_paywall[mode]"]');
+ const $panels = $('.memberful-paywall-builder__panel');
+ const $preview = $('#memberful-paywall-preview');
+ const $colorInputs = $('.memberful-paywall-builder__color');
const preview = window.memberfulPaywallPreview || {};
const DEBOUNCE_MS = 250;
@@ -11,13 +11,30 @@ jQuery(function ($) {
let debounceTimer = null;
let requestSeq = 0;
- $colorInput.wpColorPicker({
- change: function () {
- setTimeout(scheduleRefresh, 0);
- },
- clear: function () {
- setTimeout(scheduleRefresh, 0);
- },
+ $colorInputs.each(function () {
+ const $input = $(this);
+ let palettes = true;
+ const palettesAttr = $input.attr('data-palettes');
+ if (palettesAttr) {
+ try {
+ const parsed = JSON.parse(palettesAttr);
+ if (Array.isArray(parsed) && parsed.length) {
+ palettes = parsed;
+ }
+ } catch (e) {
+ // Fall back to wpColorPicker's default palette.
+ }
+ }
+
+ $input.wpColorPicker({
+ palettes: palettes,
+ change: function () {
+ setTimeout(scheduleRefresh, 0);
+ },
+ clear: function () {
+ setTimeout(scheduleRefresh, 0);
+ },
+ });
});
function applyMode(mode) {
@@ -42,18 +59,19 @@ jQuery(function ($) {
function collectConfig() {
return {
- mode: $('input[name="memberful_paywall[mode]"]:checked').val() || 'builder',
- layout: $('input[name="memberful_paywall[layout]"]:checked').val() || 'card',
- heading: $('#memberful-paywall-heading').val() || '',
- subheading: $('#memberful-paywall-subheading').val() || '',
- features: $('#memberful-paywall-benefits .memberful-paywall-builder__benefit-input').map(function () {
+ mode: $('input[name="memberful_paywall[mode]"]:checked').val() || 'builder',
+ layout: $('input[name="memberful_paywall[layout]"]:checked').val() || 'card',
+ heading: $('#memberful-paywall-heading').val() || '',
+ subheading: $('#memberful-paywall-subheading').val() || '',
+ features: $('#memberful-paywall-benefits .memberful-paywall-builder__benefit-input').map(function () {
return $(this).val();
}).get(),
- button_label: $('#memberful-paywall-button-label').val() || '',
- subscribe_url: $('#memberful-paywall-subscribe-url').val() || '',
- sign_in_url: $('#memberful-paywall-signin-url').val() || '',
- brand_color: $colorInput.val() || '',
- button_shape: $('#memberful-paywall-button-shape').val() || 'rounded',
+ button_label: $('#memberful-paywall-button-label').val() || '',
+ subscribe_url: $('#memberful-paywall-subscribe-url').val() || '',
+ sign_in_url: $('#memberful-paywall-signin-url').val() || '',
+ brand_color: $('#memberful-paywall-brand-color').val() || '',
+ background_color: $('#memberful-paywall-background-color').val() || '',
+ button_shape: $('#memberful-paywall-button-shape').val() || 'rounded',
};
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
index 366ac454..03e81e75 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall.php
@@ -8,6 +8,7 @@
/**
* Load required classes.
*/
+require_once MEMBERFUL_DIR . '/src/paywall/color.php';
require_once MEMBERFUL_DIR . '/src/paywall/config.php';
require_once MEMBERFUL_DIR . '/src/paywall/preview.php';
require_once MEMBERFUL_DIR . '/src/paywall/renderer.php';
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/color.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/color.php
new file mode 100644
index 00000000..14f87b09
--- /dev/null
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/color.php
@@ -0,0 +1,76 @@
+ self::LUMINANCE_THRESHOLD ? self::TEXT_DARK : self::TEXT_LIGHT;
+ }
+
+ /**
+ * Parse a 3- or 6-digit hex colour into [r, g, b] of 0–255 integers.
+ *
+ * @param string $hex Hex colour input.
+ *
+ * @return array{0:int,1:int,2:int}|null
+ */
+ private static function hex_to_rgb( string $hex ): ?array {
+ $hex = ltrim( trim( $hex ), '#' );
+
+ if ( 3 === strlen( $hex ) ) {
+ $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
+ }
+
+ if ( 6 !== strlen( $hex ) || ! ctype_xdigit( $hex ) ) {
+ return null;
+ }
+
+ return array(
+ (int) hexdec( substr( $hex, 0, 2 ) ),
+ (int) hexdec( substr( $hex, 2, 2 ) ),
+ (int) hexdec( substr( $hex, 4, 2 ) ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
index b779a552..884290b8 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/config.php
@@ -22,16 +22,17 @@ class Memberful_Paywall_Config {
*/
public static function defaults(): array {
return array(
- 'mode' => 'builder',
- 'layout' => 'card',
- 'heading' => __( 'Subscribe to keep reading', 'memberful' ),
- 'subheading' => __( 'This post is for paying subscribers.', 'memberful' ),
- 'features' => array(),
- 'button_label' => __( 'Subscribe', 'memberful' ),
- 'subscribe_url' => '',
- 'sign_in_url' => '',
- 'brand_color' => '',
- 'button_shape' => 'square',
+ 'mode' => 'builder',
+ 'layout' => 'card',
+ 'heading' => __( 'Subscribe to keep reading', 'memberful' ),
+ 'subheading' => __( 'This post is for paying subscribers.', 'memberful' ),
+ 'features' => array(),
+ 'button_label' => __( 'Subscribe', 'memberful' ),
+ 'subscribe_url' => '',
+ 'sign_in_url' => '',
+ 'brand_color' => '',
+ 'background_color' => '',
+ 'button_shape' => 'square',
);
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
index 8e063095..a7b12be7 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/renderer.php
@@ -273,6 +273,12 @@ private static function wrapper_style( array $config ): string {
$parts[] = '--memberful-brand:' . $brand_color;
}
+ $background_color = isset( $config['background_color'] ) ? sanitize_hex_color( (string) $config['background_color'] ) : '';
+ if ( ! empty( $background_color ) ) {
+ $parts[] = '--memberful-surface:' . $background_color;
+ $parts[] = '--memberful-text:' . Memberful_Paywall_Color::contrast_text_color( $background_color );
+ }
+
return implode( ';', $parts ) . ';';
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
index 967a6024..eab188dc 100644
--- a/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
+++ b/wordpress/wp-content/plugins/memberful-wp/src/paywall/sanitizer.php
@@ -57,10 +57,12 @@ public static function sanitize( array $input, array $defaults ): array {
}
}
- if ( isset( $input['brand_color'] ) ) {
- $color = sanitize_hex_color( (string) $input['brand_color'] );
- if ( null !== $color && '' !== $color ) {
- $clean['brand_color'] = $color;
+ foreach ( array( 'brand_color', 'background_color' ) as $color_key ) {
+ if ( isset( $input[ $color_key ] ) ) {
+ $color = sanitize_hex_color( (string) $input[ $color_key ] );
+ if ( null !== $color && '' !== $color ) {
+ $clean[ $color_key ] = $color;
+ }
}
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 6f8c04ca..27786634 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -502,6 +502,18 @@ Paywall Builder
min-width: 80px;
}
+/* Accent + Background colours on one row */
+.memberful-paywall-builder__colors-row {
+ align-items: start;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 32px;
+}
+
+.memberful-paywall-builder__colors-row .wp-color-result.button {
+ margin: 0;
+}
+
/* Benefits */
.memberful-paywall-builder__benefits-label {
margin-bottom: 8px;
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index e75b81ed..6643e57c 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -5,11 +5,11 @@
.memberful-paywall {
--memberful-border: #e5e5e5;
--memberful-brand: var(--wp--preset--color--primary, var(--wp--preset--color--accent, var(--wp-admin-theme-color, #2271b1)));
- --memberful-muted: #666;
- --memberful-muted-soft: #999;
--memberful-radius: 0;
--memberful-surface: #fff;
--memberful-text: #1a1a1a;
+ --memberful-muted: color-mix(in srgb, var(--memberful-text) 60%, transparent);
+ --memberful-muted-soft: color-mix(in srgb, var(--memberful-text) 40%, transparent);
box-sizing: border-box;
color: var(--memberful-text);
@@ -137,11 +137,9 @@
border-top: 2px solid var(--memberful-brand);
}
-/* Card — centred white card on muted page. */
+/* Card - centred card surface on a fixed muted page. */
.memberful-paywall--card {
- --memberful-surface: #f8f8f8;
-
- background: var(--memberful-surface);
+ background: #f8f8f8;
}
.memberful-paywall--card .memberful-paywall__inner {
@@ -149,7 +147,7 @@
}
.memberful-paywall--card .memberful-paywall__card {
- background: #fff;
+ background: var(--memberful-surface);
border: 1px solid #e0e0e0;
border-radius: 0.5rem;
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.1);
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 9e7df61f..04e0359a 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -101,10 +101,44 @@
-
-
-
-
+
+
From 66ede34c5d0593c0dbd4cd79b551425bfa6072c9 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Thu, 14 May 2026 09:11:18 +1000
Subject: [PATCH 26/28] Adjust advanced settings
---
.../memberful-wp/stylesheets/admin.css | 25 +++++++++++++++++
.../views/paywall/builder-panel.php | 27 ++++++++++++-------
2 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 27786634..14ea2d70 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -567,6 +567,31 @@ Paywall Builder
color: var(--wp-admin-theme-color-darker-10);
}
+/* Advanced settings */
+.memberful-paywall-builder__advanced {
+ border-top: 1px solid var(--wp-editor-canvas-background);
+ margin-top: 2rem;
+ padding-top: 1rem;
+}
+.memberful-paywall-builder__advanced-summary {
+ align-items: center;
+ cursor: pointer;
+ display: flex;
+ font-weight: 600;
+ justify-content: space-between;
+ margin-bottom: 1rem;
+}
+.memberful-paywall-builder__advanced-summary::-webkit-details-marker {
+ display: none;
+}
+.memberful-paywall-builder__advanced-chevron {
+ color: #646970;
+ transition: transform 150ms ease;
+}
+.memberful-paywall-builder__advanced[open] .memberful-paywall-builder__advanced-chevron {
+ transform: rotate(180deg);
+}
+
.memberful-paywall-builder__preview-frame {
background: #fff;
border: 1px solid #dcdcde;
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 04e0359a..998d3b7c 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -140,17 +140,24 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
From 1c291585b09894ffe915b317dd4262b647efbc27 Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Thu, 14 May 2026 09:42:03 +1000
Subject: [PATCH 27/28] Adjust layout to be closer to the prototype
---
.../memberful-wp/js/src/paywall-builder.js | 9 +-
.../memberful-wp/stylesheets/admin.css | 92 ++++++++++++++-----
.../views/paywall/builder-panel.php | 70 ++++++++------
3 files changed, 122 insertions(+), 49 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
index 57fced03..03230a33 100644
--- a/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
+++ b/wordpress/wp-content/plugins/memberful-wp/js/src/paywall-builder.js
@@ -71,7 +71,7 @@ jQuery(function ($) {
sign_in_url: $('#memberful-paywall-signin-url').val() || '',
brand_color: $('#memberful-paywall-brand-color').val() || '',
background_color: $('#memberful-paywall-background-color').val() || '',
- button_shape: $('#memberful-paywall-button-shape').val() || 'rounded',
+ button_shape: $('input[name="memberful_paywall[button_shape]"]:checked').val() || 'rounded',
};
}
@@ -154,6 +154,13 @@ jQuery(function ($) {
refreshPreview();
});
+ const $headingInput = $('#memberful-paywall-heading');
+ const $headingCounter = $('[data-counter-for="memberful-paywall-heading"]');
+ const headingMax = parseInt($headingCounter.attr('data-max'), 10) || 60;
+ $headingInput.on('input', function () {
+ $headingCounter.text(this.value.length + '/' + headingMax);
+ });
+
if (($modeInputs.filter(':checked').val() || 'builder') === 'builder') {
refreshPreview();
}
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index 14ea2d70..d2b9b5ea 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -299,7 +299,7 @@ Paywall Builder
}
.memberful-paywall-builder__mode-tabs {
background: #fff;
- border: 1px solid #c3c4c7;
+ border: 1px solid var(--wp-editor-canvas-background);
border-radius: 4px;
display: inline-flex;
overflow: hidden;
@@ -316,7 +316,7 @@ Paywall Builder
}
.memberful-paywall-builder__mode-tab span {
background: #fff;
- border-right: 1px solid #c3c4c7;
+ border-right: 1px solid var(--wp-editor-canvas-background);
color: #2c3338;
display: inline-block;
font-size: 13px;
@@ -328,11 +328,11 @@ Paywall Builder
border-right: 0;
}
.memberful-paywall-builder__mode-tab input[type="radio"]:checked + span {
- background: #2271b1;
+ background: var(--wp-admin-theme-color);
color: #fff;
}
.memberful-paywall-builder__mode-tab input[type="radio"]:focus-visible + span {
- box-shadow: inset 0 0 0 2px #2271b1;
+ box-shadow: inset 0 0 0 2px var(--wp-admin-theme-color);
}
/* Template picker cards */
@@ -371,11 +371,11 @@ Paywall Builder
border-color: #8c8f94;
}
.memberful-paywall-builder__template-card input[type="radio"]:checked + .memberful-paywall-builder__template-card-inner {
- border-color: #2271b1;
- box-shadow: 0 0 0 1px #2271b1;
+ border-color: var(--wp-admin-theme-color);
+ box-shadow: 0 0 0 1px var(--wp-admin-theme-color);
}
.memberful-paywall-builder__template-card input[type="radio"]:checked + .memberful-paywall-builder__template-card-inner::after {
- background: #2271b1 url("data:image/svg+xml;utf8, ") center/12px no-repeat;
+ background: var(--wp-admin-theme-color) url("data:image/svg+xml;utf8, ") center/12px no-repeat;
border-radius: 50%;
content: "";
height: 20px;
@@ -385,7 +385,7 @@ Paywall Builder
width: 20px;
}
.memberful-paywall-builder__template-card input[type="radio"]:focus-visible + .memberful-paywall-builder__template-card-inner {
- box-shadow: 0 0 0 2px #2271b1;
+ box-shadow: 0 0 0 2px var(--wp-admin-theme-color);
}
.memberful-paywall-builder__template-thumb {
@@ -406,19 +406,19 @@ Paywall Builder
background: #f0f0f1;
}
.memberful-paywall-builder__thumb-line {
- background: #c3c4c7;
+ background: var(--wp-editor-canvas-background);
border-radius: 2px;
height: 3px;
width: 100px;
}
.memberful-paywall-builder__thumb-button {
- background: #2271b1;
+ background: var(--wp-admin-theme-color);
border-radius: 2px;
height: 10px;
width: 48px;
}
.memberful-paywall-builder__thumb-lock {
- background: #2271b1 url("data:image/svg+xml;utf8, ") center/18px no-repeat;
+ background: var(--wp-admin-theme-color) url("data:image/svg+xml;utf8, ") center/18px no-repeat;
border-radius: 50%;
height: 26px;
width: 26px;
@@ -466,7 +466,8 @@ Paywall Builder
margin: 0 0 1rem;
}
.memberful-paywall-builder__customize label,
-.memberful-paywall-builder__benefits-label {
+.memberful-paywall-builder__benefits-label,
+.memberful-paywall-builder__button-shape-label {
color: #6b7280;
display: block;
font-size: 13px;
@@ -487,6 +488,19 @@ Paywall Builder
font-size: 12px;
margin-top: 4px;
}
+.memberful-paywall-builder__field-row {
+ align-items: baseline;
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 4px;
+}
+.memberful-paywall-builder__field-row label {
+ margin: 0;
+}
+.memberful-paywall-builder__counter {
+ color: #646970;
+ font-size: 12px;
+}
.memberful-paywall-builder__customize .memberful-paywall-builder__field--paired {
align-items: start;
display: grid;
@@ -502,20 +516,54 @@ Paywall Builder
min-width: 80px;
}
-/* Accent + Background colours on one row */
-.memberful-paywall-builder__colors-row {
- align-items: start;
- display: flex;
- flex-wrap: wrap;
- gap: 32px;
+/* Accent + Background colours */
+.memberful-paywall-builder__field .wp-color-result.button {
+ margin: 0;
}
-.memberful-paywall-builder__colors-row .wp-color-result.button {
- margin: 0;
+/* Button shape controls */
+.memberful-paywall-builder__segmented {
+ border: 1px solid var(--wp-editor-canvas-background);
+ border-radius: 6px;
+ display: inline-flex;
+ padding: 4px;
+}
+.memberful-paywall-builder__segmented-option {
+ cursor: pointer;
+ margin: 0 !important;
+}
+.memberful-paywall-builder__segmented-option input[type="radio"] {
+ opacity: 0;
+ pointer-events: none;
+ position: absolute;
+}
+.memberful-paywall-builder__segmented-option-inner {
+ align-items: center;
+ border-radius: 4px;
+ color: #646970;
+ display: inline-flex;
+ font-size: 12px;
+ gap: 6px;
+ padding: 5px 14px 5px 10px;
+ transition: background-color 120ms ease, color 120ms ease;
+}
+.memberful-paywall-builder__segmented-option:hover .memberful-paywall-builder__segmented-option-inner {
+ color: #1d2327;
+}
+.memberful-paywall-builder__segmented-option input[type="radio"]:checked + .memberful-paywall-builder__segmented-option-inner {
+ background: var(--wp-admin-theme-color);
+ color: #fff;
+}
+.memberful-paywall-builder__segmented-option input[type="radio"]:focus-visible + .memberful-paywall-builder__segmented-option-inner {
+ box-shadow: 0 0 0 2px var(--wp-admin-theme-color);
+}
+.memberful-paywall-builder__segmented-option-inner svg {
+ flex: 0 0 auto;
}
/* Benefits */
-.memberful-paywall-builder__benefits-label {
+.memberful-paywall-builder__benefits-label,
+.memberful-paywall-builder__button-shape-label {
margin-bottom: 8px;
padding: 0;
}
@@ -570,7 +618,7 @@ Paywall Builder
/* Advanced settings */
.memberful-paywall-builder__advanced {
border-top: 1px solid var(--wp-editor-canvas-background);
- margin-top: 2rem;
+ margin-top: 1.5rem;
padding-top: 1rem;
}
.memberful-paywall-builder__advanced-summary {
diff --git a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
index 998d3b7c..f3dc5c18 100644
--- a/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
+++ b/wordpress/wp-content/plugins/memberful-wp/views/paywall/builder-panel.php
@@ -46,8 +46,11 @@
-
-
+
+
+ /60
+
+
@@ -86,20 +89,37 @@
-
-
-
-
-
-
-
-
- >
- >
- >
-
-
-
+
+
+
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+ >
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
From 3ad49f9763d45bd6ceeb7e031ad978cec35f7a3b Mon Sep 17 00:00:00 2001
From: Anton Vanyukov
Date: Thu, 14 May 2026 09:53:58 +1000
Subject: [PATCH 28/28] Final adjustments
---
.../plugins/memberful-wp/stylesheets/admin.css | 2 +-
.../plugins/memberful-wp/stylesheets/paywall.css | 15 +++++----------
2 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
index d2b9b5ea..31e93b07 100755
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/admin.css
@@ -564,7 +564,7 @@ Paywall Builder
/* Benefits */
.memberful-paywall-builder__benefits-label,
.memberful-paywall-builder__button-shape-label {
- margin-bottom: 8px;
+ margin-bottom: 6px;
padding: 0;
}
.memberful-paywall-builder__benefit-list {
diff --git a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
index 6643e57c..1981f55f 100644
--- a/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
+++ b/wordpress/wp-content/plugins/memberful-wp/stylesheets/paywall.css
@@ -9,7 +9,6 @@
--memberful-surface: #fff;
--memberful-text: #1a1a1a;
--memberful-muted: color-mix(in srgb, var(--memberful-text) 60%, transparent);
- --memberful-muted-soft: color-mix(in srgb, var(--memberful-text) 40%, transparent);
box-sizing: border-box;
color: var(--memberful-text);
@@ -42,7 +41,7 @@
}
.memberful-paywall__heading {
- font-size: var(--wp--preset--font-size--medium, 1.125rem);
+ font-size: var(--wp--preset--font-size--large, 1.375rem);
}
.memberful-paywall__subheading {
@@ -109,7 +108,7 @@
}
.memberful-paywall__signin {
- color: var(--memberful-muted-soft);
+ color: var(--memberful-muted);
font-size: 0.75rem;
margin: var(--wp--preset--spacing--30, 1rem) 0 0 !important; /* fixes compatibility with some themes */
}
@@ -138,10 +137,6 @@
}
/* Card - centred card surface on a fixed muted page. */
-.memberful-paywall--card {
- background: #f8f8f8;
-}
-
.memberful-paywall--card .memberful-paywall__inner {
padding: var(--wp--preset--spacing--40, 1.5rem);
}
@@ -149,10 +144,10 @@
.memberful-paywall--card .memberful-paywall__card {
background: var(--memberful-surface);
border: 1px solid #e0e0e0;
- border-radius: 0.5rem;
- box-shadow: 0 10px 25px rgba(15, 23, 42, 0.1);
+ border-radius: 1rem;
+ box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.1) 0px 4px 6px -4px;
margin: 0 auto;
- max-width: 24rem;
+ max-width: 34rem;
padding: var(--wp--preset--spacing--50, 2rem);
}