feat(access-control): make Audience Management an explicit prerequisite (NPPD-1846) - #765
Open
adekbadek wants to merge 3 commits into
Open
Conversation
…te (NPPD-1846) Access Control appeared whenever NEWSPACK_CONTENT_GATES was defined and never checked Reader Activation. Gates restrict content perfectly well without it, but everything a gate hands the reader off to is gated on it, so a registration wall built without Audience Management locked readers out and gave them no way in. Both gate-editing surfaces now refuse to work without it, via a shared Audience_Management_Dependency trait: gate creation and duplication are guarded at the REST layer, and the screens are replaced by a prerequisite state. The guard is applied at the wizard router so every section inherits it, rather than only the gate list. Conversely, Audience Management can no longer be switched off while any gate exists. That keeps "Audience Management off" and "no gates exist" the same state, which is what makes replacing the screens safe: without it, a site that disabled Audience Management would keep serving live gates with no admin surface left to unpublish them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWDEh5hvsTEmMiSkfbrVuS
adekbadek
force-pushed
the
nppd-1846-make-audience-management-dependency-explicit-for-access
branch
from
July 29, 2026 08:47
ada4cfd to
c500bd8
Compare
adekbadek
marked this pull request as ready for review
July 29, 2026 09:23
…ce-management-dependency-explicit-for-access # Conflicts: # plugins/newspack-plugin/includes/wizards/audience/class-audience-content-gates.php # plugins/newspack-plugin/src/wizards/types/window.d.ts
…ce-management-dependency-explicit-for-access # Conflicts: # plugins/newspack-plugin/includes/wizards/audience/class-audience-content-gates.php # plugins/newspack-plugin/includes/wizards/newsletters/class-premium-newsletters-wizard.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
All Submissions:
Changes proposed in this Pull Request:
Access Control appears whenever
NEWSPACK_CONTENT_GATESis defined and never checked Reader Activation. The ticket describes the symptom as broken UI in/my-account, but that undersells it:class-content-gate.phphas no Reader Activation dependency at all, so gates restrict content perfectly well — while everything a gate hands the reader off to is gated on Audience Management:class-reader-registration.php:28class-magic-link.php, 7 guardsclass-my-account.php:116class-reader-activation-emails.php:39class-session-hydration.php:102sync/class-sync.php:76A registration wall built without Audience Management locks readers out and then gives them no way in.
This lands two halves that are only correct together.
1. The gate-editing screens refuse to work without Audience Management. A shared
Audience_Management_Dependencytrait — consumed by both surfaces, so the dependency can't be enforced on one and forgotten on the other — provides the check, the localized script data, and a REST permission callback guarding gate creation and duplication. The screens are replaced by a prerequisite state built on the existing Institutions empty-state pattern. The submenu item deliberately stays visible: hiding it would turn "is this required?" into "where did it go?", which is the opposite of making the dependency explicit.The guard is applied at the wizard router, so all 7 Access Control sections and both Premium Newsletters sections inherit it. Guarding only the gate list would have left
#/edit/new/allreachable by bookmark or browser history, with a working Save — the publisher fills in an entire gate and only learns on save.Premium Newsletters is included because the dependency there is real, and harder.
filter_subscription_lists()is hooked unconditionally, so restricted lists vanish from every signup form whether or not Audience Management is on; meanwhile access is granted only via thereader_verified/product_subscription_changed/donation_subscription_changeddata events, andreader_verifiedtraces toset_reader_verified(), whose every trigger hook is registered insideif ( self::is_enabled() ). With Audience Management off, a premium newsletter is hidden from readers with no path to unhide it.2. Audience Management can no longer be switched off while any gate exists.
Content_Gate::has_any_gates()plus a 409 refusal inapi_update_reader_activation_settings(), and a toggle that explains the lock instead of letting the publisher confirm a destructive dialog and then meet a REST error.This second half is what makes the first one safe. The ticket's premise that nothing in the admin turns Audience Management off is wrong — Audience → Configuration → Reader Revenue Platform → Change → toggle calls
saveConfig( { enabled: false } )behind a confirm dialog. Without the disable guard, a site that used it would keep serving live gates whileContent_Gate::redirect_cpt()bounced the gate CPT list back to the now-blocked wizard andfilter_edit_post_link()rewrote every gate edit link to a blocked route — live paywalls with no admin surface left to lift them. The guard gives the invariant Audience Management off ⇒ no gates exist, which is exactly what the wholesale screen replacement relies on.The guard counts drafts and premium newsletter gates (a draft is publishable; a newsletter gate restricts by the same mechanism) but not trashed gates — a gate the publisher already deleted must not hold the toggle hostage.
Closes NPPD-1846.
Supersedes the prototype in #737, now closed.
How to test the changes in this Pull Request:
define( 'NEWSPACK_CONTENT_GATES', true );inwp-config.php, ensure Audience Management is off (wp option update newspack_reader_activation_enabled 0).#/edit/new/all,#/edit/new/custom,#/institutions,#/settings/countdown-banner,#/settings/content-gifting. All should show the prerequisite state.POSTto/wp-json/newspack/v1/wizard/newspack-audience-access-controlwith a valid nonce returns 403newspack_audience_management_required, and no gate is created. Same for.../newspack-premium-newsletters.POSTof{ enabled: false }to/wp-json/newspack/v1/wizard/newspack-audience/audience-managementreturns 409newspack_audience_management_required_by_gates.Verification
Audience Management off — Access Control and Newsletters → Premium:
The gate editor route is covered too, not just the list:
Audience Management on — nothing is over-blocked:
The disable guard, locked while a gate exists and released once it is gone:
Automated:
Both regression tests were confirmed to actually bite: reverting a single
permission_callbacktoken turns the route-wiring test red, and un-wrapping one router section turns the section-completeness test red.Other information:
Rollout safety. Every one of the 19 production sites that currently has Audience Management off was checked directly: all have zero content gates, so no live site meets the newly-blocked condition and nothing needs migrating.
NEWSPACK_CONTENT_GATESis awp-config.phpconstant marked@status draft, so Access Control only exists where someone deliberately defined it.Cross-repo note.
includes/content-gate/class-content-gate.phpis in thecontent-gatecontract consumed bynewspack-manager. The change there is purely additive (one new static method,has_any_gates()); no existing signature or behaviour changes, so no coordinated PR is needed.Deliberately not included. The Access Control help doc still doesn't list Audience Management as a dependency — the question that started the thread. Not a code change, and still unowned.
Reviewer note. Codex was unavailable during review (timed out twice without producing a review body), so that reviewer slot is degraded rather than clean.