Skip to content

MR-11: Shortcodes to use BeaverBuilder shared rows just don't work - #25

Open
av3nger wants to merge 5 commits into
mainfrom
feature/MR-11-beaver-builder-support
Open

MR-11: Shortcodes to use BeaverBuilder shared rows just don't work#25
av3nger wants to merge 5 commits into
mainfrom
feature/MR-11-beaver-builder-support

Conversation

@av3nger

@av3nger av3nger commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes a request-scoped side effect in the content protection filter that silently broke Beaver Builder layouts: when a protected post was rendered anywhere in a request (archive loop, widget, related-posts block), Memberful permanently removed Beaver Builder's the_content hook, so every BB layout rendered later in that same request disappeared and visitors saw raw fallback content instead.

While tracing that, several smaller defects in the same code path were found and fixed: a duplicate protection filter being registered while building the global marketing teaser, block-emitted shortcodes never executing inside marketing content, an empty teaser wrapper (plus its CSS) being output for posts with no "teasable" content, and the Sensei integration failing to unhook content protection at all.

What's included

  • Beaver Builder hook restoration (src/content_filter.php): the gated branch still disables FLBuilder::render_content while the marketing/paywall content is built (so the protected layout can't leak into it), but now captures the hook's priority via has_filter() and re-registers it before returning. Posts rendered later in the same request get their layouts back.
  • Duplicate filter removal (src/global_marketing.php): the teaser builder ran a stale remove_action / add_action pair at priority -10 around its nested the_content render. The removal was a silent no-op (the hook lives at priority 100), and the re-add registered a second protection filter at -10 for the rest of the request. Both lines are gone; recursion is already prevented by the existing doing_filter( 'memberful_wp_protect_content' ) guard.
  • Block/shortcode ordering (src/content_filter.php): do_blocks on the marketing-content filter chain moved from priority 15 to 9, matching core's the_content ordering (blocks → wpautop → shortcodes). Shortcodes emitted by blocks in marketing content now execute instead of printing literally.
  • Empty teaser suppression (src/global_marketing.php): $has_teaser was effectively always true, so protected posts with no extractable paragraphs emitted an empty memberful-global-teaser-content div plus the teaser gradient CSS. The teaser wrapper and CSS are now only output when there is actual teaser content.
  • Sensei integration fix (src/contrib/woothemes-sensei.php): its remove_action( 'the_content', 'memberful_wp_protect_content' ) omitted the priority argument, making the removal a silent no-op since the hook is registered at priority 100. The priority is now passed.

Scope and behaviour

  • The Beaver Builder change alters when the hook is absent, not what protected visitors see: the marketing/paywall content for the gated post itself renders exactly as before; only subsequent posts in the same request regain their layouts. The same capture/restore applies whatever priority BB registered at.
  • No behaviour change for accessible posts, for admins (who bypass protection), or for sites without Beaver Builder - has_filter() returns false and both branches are skipped.
  • The -10 cleanup and the $has_teaser fix only affect installs with global snippets enabled (memberful_use_global_snippets).
  • The Sensei change makes the integration's intended existing behaviour (deferring protection to a custom course-aware filter) actually take effect; sites without Sensei are unaffected.

Test plan

All flows tested logged out on a site running Beaver Builder Pro, with global marketing + snippets enabled.

  • Render a protected post followed by a public Beaver Builder page in the same request (e.g. an archive loop or a nested
    render). Before: BB's the_content hook is gone after the protected render and the BB page shows its raw fallback content. After: the hook survives (has_filter returns its original priority) and the BB layout renders.
  • Protected BB page (regression): view a protected Beaver Builder page whose marketing content pulls in a BB shared row via [fl_builder_insert_layout] and a custom access-check shortcode. Logged out: teaser + marketing content + styled shared row, unchanged from before the fix. As a subscriber: full layout, no paywall markup.
  • Empty teaser: view a protected post with no paragraph content. Before: page source contains an empty memberful-global-teaser-content div and the teaser <style> block. After: only the memberful-global-marketing-content div.
  • Duplicate hook: after rendering a gated post with snippets enabled, dump the_content filter priorities. Before: memberful_wp_protect_content appears at both -10 and 100. After: only 100.
  • Shortcodes in marketing content: marketing content containing a block that emits a shortcode now renders the shortcode output instead of the literal [shortcode] text.

Note

Medium Risk
Changes run on every protected the_content pass and global-marketing teaser builds; incorrect hook restore could still leak BB layout into paywalls or break later renders, though behavior is intended to match pre-fix output for the gated post itself.

Overview
Fixes a request-scoped bug where gating a protected post removed Beaver Builder’s the_content hook without putting it back, so later posts in the same request (archives, widgets, related content) lost their BB layouts. The paywall path still strips FLBuilder::render_content while marketing content is built, but now captures priority, restores the hook (including a late the_content callback when needed), and unifies paywall/divider return paths.

Adds a new Beaver Builder integration (beaver-builder.php): Memberful visibility on row/column/module Advanced settings (aligned with block editor), runtime node hiding by login/plan, and stripping restricted modules from BB’s post_content fallback on save.

Global marketing / filter chain: drops a broken remove_action/add_action at priority -10 that duplicated memberful_wp_protect_content; moves do_blocks to priority 9 on the marketing filter so block-emitted shortcodes run; only outputs teaser wrapper/CSS when there is real teaser text. Sensei: passes priority 100 when unhooking content protection so that integration actually works.

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

Comment thread wordpress/wp-content/plugins/memberful-wp/src/content_filter.php Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

Fix All in Cursor

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

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e23bb5e. Configure here.

}
}

return TRUE;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Settings match can strip public modules

Medium Severity

memberful_wp_beaver_builder_settings_match treats a saved module as a match when its settings are only a subset of the exporter's merged settings. Unrelated public modules with the same type and overlapping defaults (spacers, separators, duplicate headings/buttons) can therefore be emptied from the fallback whenever an identical module sits under a Memberful-restricted row or column.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by team rule: Code Review Guidelines

Reviewed by Cursor Bugbot for commit e23bb5e. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It will only strip exact-duplicate modules from the search/excerpt fallback (never the rendered page, never leaking restricted content), and it's an unavoidable tradeoff since BB's export path drops node IDs.

# Conflicts:
#	wordpress/wp-content/plugins/memberful-wp/readme.txt
#	wordpress/wp-content/plugins/memberful-wp/src/content_filter.php
#	wordpress/wp-content/plugins/memberful-wp/src/global_marketing.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants