Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4939c07
feat(reader-activation): remove RAS auto-enable and skip system
miguelpeixe May 25, 2026
fc2bd8e
feat(audience): add manual Audience Management toggle
miguelpeixe May 25, 2026
38cc2f1
fix(audience): drop campaign Skip button after removing skip system
miguelpeixe May 25, 2026
1deffd1
refactor(audience): make campaign completion view standalone
miguelpeixe May 25, 2026
84543e5
fix(audience): treat missing plugins as guidance, not blockers
miguelpeixe May 25, 2026
b2ab51a
fix(audience): gate Newsletters-dependent settings on plugin presence
miguelpeixe May 25, 2026
6c4621a
fix(reader-activation): account link works without WooCommerce
miguelpeixe May 26, 2026
b0baa20
fix(audience): hide Donations submenu without WooCommerce or NRH
miguelpeixe May 26, 2026
45d0b74
fix(audience): address Copilot review feedback on NPPD-1066
miguelpeixe May 26, 2026
5ce2887
feat(audience): add reader revenue platform-selected signal
miguelpeixe May 28, 2026
4c6cf40
feat(audience): expose platform_selected in payment data
miguelpeixe May 28, 2026
30d4c48
fix(audience): type platform_selected and note stripe migration
miguelpeixe May 28, 2026
98372d2
feat(audience): trim and reorder RAS setup prerequisites
miguelpeixe May 28, 2026
52f02a4
feat(audience): add reader revenue platform chooser component
miguelpeixe May 28, 2026
acd2867
fix(audience): don't advance platform chooser on failed save
miguelpeixe May 28, 2026
fea34af
feat(audience): gate payment tab and render platform chooser inline
miguelpeixe May 28, 2026
126d7d6
feat(audience): show platform summary with change action
miguelpeixe May 29, 2026
e7da6de
refactor(audience): drop inline platform selector from payment tab
miguelpeixe May 29, 2026
5caf72b
fix(audience): render platform chooser with wizard header and layout
miguelpeixe Jun 1, 2026
853ddb6
fix(audience): skip WC donation product write when WooCommerce inactive
miguelpeixe Jun 1, 2026
fd7577f
fix(audience): let platform chooser continue when a plugin can't install
miguelpeixe Jun 1, 2026
347c2f7
feat(audience): surface missing platform plugins on config page
miguelpeixe Jun 1, 2026
3a676bd
feat(audience): move enable toggle to platform screen, confirm on dis…
miguelpeixe Jun 1, 2026
c839bae
fix(audience): land on platform screen when audience management disabled
miguelpeixe Jun 1, 2026
d3bb5a6
feat(audience): badge the currently selected platform in the chooser
miguelpeixe Jun 1, 2026
c16cd33
fix(audience): address Copilot review feedback
miguelpeixe Jun 1, 2026
48cbfd1
fix(reader-activation): enable RAS in ras setup CLI command
miguelpeixe Jun 1, 2026
48f5530
fix(audience): guard payment settings against missing platform and WP…
miguelpeixe Jun 1, 2026
c9e1326
refactor(audience): remove dead code flagged in review
miguelpeixe Jun 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion includes/class-donations.php
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,6 @@ public static function update_donation_product( $args = [] ) {
* Remove all donation products from the cart.
*/
public static function remove_donations_from_cart() {
$donation_settings = self::get_donation_settings();
if ( ! self::is_platform_wc() || is_wp_error( self::is_woocommerce_suite_active() ) ) {
return;
}
Expand All @@ -696,6 +695,22 @@ public static function get_platform_slug() {
return $saved_slug;
}

/**
* Whether a reader revenue platform has been explicitly chosen.
*
* The platform option defaults to 'wc' and has no unset value, so first-run
* is detected by whether the option was ever saved.
*
* Note: get_platform_slug() persists 'wc' when migrating a legacy 'stripe'
* platform, which marks the option as saved. Such sites are treated as
* having selected Newspack.
*
* @return bool
*/
public static function is_platform_selected() {
return null !== get_option( self::NEWSPACK_READER_REVENUE_PLATFORM, null );
}

/**
* Set donation platform slug.
*
Expand Down
3 changes: 3 additions & 0 deletions includes/cli/class-ras.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public static function cli_setup_ras() {
exit;
}

// Enabling is no longer implied by configured prerequisites, so set it explicitly here.
Reader_Activation::update_setting( 'enabled', true );

WP_CLI::success( __( 'RAS enabled with default prompts.', 'newspack-plugin' ) );
}

Expand Down
226 changes: 45 additions & 181 deletions includes/reader-activation/class-reader-activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -539,11 +539,7 @@ public static function activate() {
return new \WP_Error( 'newspack_reader_activation_missing_dependencies', __( 'Newspack Campaigns plugin is required to activate Reader Activation features.', 'newspack-plugin' ) );
}

$activated = \Newspack_Popups_Presets::activate_ras_presets();
if ( $activated ) {
self::skip( 'ras_campaign', false );
}
return $activated;
return \Newspack_Popups_Presets::activate_ras_presets();
}

/**
Expand Down Expand Up @@ -739,27 +735,6 @@ public static function get_available_newsletter_lists( $email_address = '' ) {
return apply_filters( 'newspack_registration_newsletters_lists', $registration_lists );
}

/**
* Are all Reader Revenue features configured and ready to use?
* Platform must be "Newspack" and all donation settings must be configured.
*/
public static function is_reader_revenue_ready() {
$ready = false;
$donation_settings = Donations::get_donation_settings();

if ( \is_wp_error( $donation_settings ) ) {
return $ready;
}

if ( Donations::is_platform_wc() ) {
$ready = true;
} elseif ( Donations::is_platform_nrh() && NRH::get_setting( 'nrh_organization_id' ) && method_exists( '\Newspack_Popups_Settings', 'donor_landing_page' ) && \Newspack_Popups_Settings::donor_landing_page() ) {
$ready = true;
}

return $ready;
}

/**
* Get an array of required plugins for satisfying Reader Revenue prerequisites.
* WooCommerce and Woo Subscriptions are required for Newspack, but not for NRH.
Expand All @@ -780,22 +755,12 @@ public static function get_reader_revenue_required_plugins() {
* Are the Legal Pages settings configured?
* Allows for blank values.
*
* @param bool $skip Whether to skip the check.
*
* @return bool
*/
public static function is_terms_configured( $skip = false ) {
public static function is_terms_configured() {
$terms_text = \get_option( self::OPTIONS_PREFIX . 'terms_text', false );
$terms_url = \get_option( self::OPTIONS_PREFIX . 'terms_url', false );
$is_valid = is_string( $terms_text ) && is_string( $terms_url );
if ( $skip ) {
return $is_valid || self::is_skipped( 'terms_conditions' );
}
if ( $is_valid ) {
self::skip( 'terms_conditions', false );
}

return $is_valid;
return is_string( $terms_text ) && is_string( $terms_url );
}

/**
Expand All @@ -812,97 +777,19 @@ public static function is_transactional_email_configured() {
/**
* Is reCAPTCHA enabled?
*
* @param bool $skip Whether to skip the check.
*
* @return bool
*/
public static function is_recaptcha_enabled( $skip = false ) {
$is_valid = method_exists( '\Newspack\Recaptcha', 'can_use_captcha' ) && \Newspack\Recaptcha::can_use_captcha();
if ( $skip ) {
return $is_valid || self::is_skipped( 'recaptcha' );
}
if ( $is_valid ) {
self::skip( 'recaptcha', false );
}
return $is_valid;
public static function is_recaptcha_enabled() {
return method_exists( '\Newspack\Recaptcha', 'can_use_captcha' ) && \Newspack\Recaptcha::can_use_captcha();
}

/**
* Is the RAS campaign configured?
*
* @param bool $skip Whether to skip the check.
*
* @return bool
*/
public static function is_ras_campaign_configured( $skip = false ) {
$is_valid = class_exists( 'Newspack_Popups_Presets' ) && get_option( \Newspack_Popups_Presets::NEWSPACK_POPUPS_RAS_LAST_UPDATED, false );
if ( $skip ) {
return $is_valid || self::is_skipped( 'ras_campaign' );
}
if ( $is_valid ) {
self::skip( 'ras_campaign', false );
}
return $is_valid;
}

/**
* Are all prerequisites for Reader Activation complete?
*
* @return bool
*/
public static function is_ras_ready_to_configure() {
$is_ready = self::is_terms_configured( true ) && self::is_esp_configured() && self::is_transactional_email_configured() && self::is_recaptcha_enabled( true ) && self::is_woocommerce_active();

// If all requirements are met or skipped, and RAS isn't yet enabled, enable it.
if ( $is_ready && self::is_ras_campaign_configured( true ) && ! self::is_enabled() ) {
self::update_setting( 'enabled', true );
}
return $is_ready;
}

/**
* Has the given prerequisite been skipped?
*
* @param string $prerequisite The prerequisite to check.
*
* @return bool
*/
public static function is_skipped( $prerequisite ) {
// Legacy option name compabitility.
$legacy_is_skipped = false;
if ( 'ras_campaign' === $prerequisite ) {
$legacy_is_skipped = get_option( Audience_Wizard::SKIP_CAMPAIGN_SETUP_OPTION, false ) === '1';
}

return boolval( get_option( self::OPTIONS_PREFIX . $prerequisite . '_skipped', $legacy_is_skipped ) );
}

/**
* Skip or unskip the given prerequisite.
*
* @param string $prerequisite The prerequisite to skip.
* @param bool $skip If true, skip the prerequisite. If false, unskip it.
*
* @return bool True if updated, false if not.
*/
public static function skip( $prerequisite, $skip = true ) {
if ( ( $skip && self::is_skipped( $prerequisite ) ) || ( ! $skip && ! self::is_skipped( $prerequisite ) ) ) {
return true;
}

$updated = $skip ? update_option( self::OPTIONS_PREFIX . $prerequisite . '_skipped', '1' ) : delete_option( self::OPTIONS_PREFIX . $prerequisite . '_skipped' );

// Legacy option name compabitility.
if ( 'ras_campaign' === $prerequisite && ! $skip && ! $updated ) {
$updated = delete_option( Audience_Wizard::SKIP_CAMPAIGN_SETUP_OPTION );
}

// If all requirements are met or skipped, and RAS isn't yet enabled, enable it.
if ( $skip && self::is_ras_ready_to_configure() && self::is_ras_campaign_configured( true ) && ! self::is_enabled() ) {
self::update_setting( 'enabled', true );
}

return $updated;
public static function is_ras_campaign_configured() {
return class_exists( 'Newspack_Popups_Presets' ) && (bool) get_option( \Newspack_Popups_Presets::NEWSPACK_POPUPS_RAS_LAST_UPDATED, false );
}

/**
Expand All @@ -912,37 +799,6 @@ public static function skip( $prerequisite, $skip = true ) {
*/
public static function get_prerequisites_status() {
$prerequisites = [
'terms_conditions' => [
'active' => self::is_terms_configured(),
'label' => __( 'Legal Pages', 'newspack-plugin' ),
'description' => __( 'Displaying legal pages like Privacy Policy and Terms of Service on your site is recommended for allowing readers to register and access their account.', 'newspack-plugin' ),
'help_url' => 'https://help.newspack.com/engagement/audience-management-system/',
'warning' => __( 'Privacy policies that tell users how you collect and use their data are essential for running a trustworthy website. While rules and regulations can differ by country, certain legal pages might be required by law.', 'newspack-plugin' ),
'fields' => [
'terms_text' => [
'label' => __( 'Legal Pages Disclaimer Text', 'newspack-plugin' ),
'description' => __( 'Legal pages disclaimer text to display on registration.', 'newspack-plugin' ),
],
'terms_url' => [
'label' => __( 'Legal Pages URL', 'newspack-plugin' ),
'description' => __( 'URL to the page containing the privacy policy or terms of service.', 'newspack-plugin' ),
],
],
'skippable' => true,
'is_skipped' => self::is_skipped( 'terms_conditions' ),
],
'esp' => [
'active' => self::is_esp_configured(),
'plugins' => [
'newspack-newsletters' => class_exists( '\Newspack_Newsletters' ),
],
'label' => __( 'Email Service Provider (ESP)', 'newspack-plugin' ),
'description' => __( 'Connect to your ESP to register readers with their email addresses and send newsletters.', 'newspack-plugin' ),
'instructions' => __( 'Connect to your email service provider (ESP) and enable at least one subscription list.', 'newspack-plugin' ),
'help_url' => 'https://help.newspack.com/engagement/audience-management-system/',
'href' => \admin_url( 'edit.php?post_type=newspack_nl_cpt&page=newspack-newsletters' ),
'action_text' => __( 'ESP settings' ),
],
'emails' => [
'active' => self::is_transactional_email_configured(),
'label' => __( 'Transactional Emails', 'newspack-plugin' ),
Expand All @@ -963,6 +819,23 @@ public static function get_prerequisites_status() {
],
],
],
'terms_conditions' => [
'active' => self::is_terms_configured(),
'label' => __( 'Legal Pages', 'newspack-plugin' ),
'description' => __( 'Displaying legal pages like Privacy Policy and Terms of Service on your site is recommended for allowing readers to register and access their account.', 'newspack-plugin' ),
'help_url' => 'https://help.newspack.com/engagement/audience-management-system/',
'warning' => __( 'Privacy policies that tell users how you collect and use their data are essential for running a trustworthy website. While rules and regulations can differ by country, certain legal pages might be required by law.', 'newspack-plugin' ),
'fields' => [
'terms_text' => [
'label' => __( 'Legal Pages Disclaimer Text', 'newspack-plugin' ),
'description' => __( 'Legal pages disclaimer text to display on registration.', 'newspack-plugin' ),
],
'terms_url' => [
'label' => __( 'Legal Pages URL', 'newspack-plugin' ),
'description' => __( 'URL to the page containing the privacy policy or terms of service.', 'newspack-plugin' ),
],
],
],
'recaptcha' => [
'active' => self::is_recaptcha_enabled(),
'label' => __( 'reCAPTCHA', 'newspack-plugin' ),
Expand All @@ -971,36 +844,25 @@ public static function get_prerequisites_status() {
'help_url' => 'https://help.newspack.com/engagement/audience-management-system/',
'href' => \admin_url( '/admin.php?page=newspack-settings&scrollTo=newspack-settings-recaptcha' ),
'action_text' => __( 'reCAPTCHA settings' ),
'skippable' => true,
'is_skipped' => self::is_skipped( 'recaptcha' ),
],
'reader_revenue' => [
'active' => self::is_reader_revenue_ready(),
'plugins' => self::get_reader_revenue_required_plugins(),
'label' => __( 'Reader Revenue', 'newspack-plugin' ),
'description' => __( 'Setting suggested donation amounts is required for enabling a streamlined donation experience.', 'newspack-plugin' ),
'instructions' => __( 'Set platform to "Newspack" or "News Revenue Hub" and configure your default donation settings. If using News Revenue Hub, set an Organization ID and a Donor Landing Page in News Revenue Hub Settings.', 'newspack-plugin' ),
'help_url' => 'https://help.newspack.com/engagement/audience-management-system/',
'href' => \admin_url( '/admin.php?page=newspack-audience#/payment' ),
'action_text' => __( 'Reader Revenue settings' ),
],
'ras_campaign' => [
'active' => self::is_ras_campaign_configured(),
'plugins' => [
'newspack-popups' => class_exists( '\Newspack_Popups_Model' ),
],
'label' => __( 'Audience Management Campaign', 'newspack-plugin' ),
'description' => __( 'Building a set of prompts with default segments and settings allows for an improved experience optimized for audience management.', 'newspack-plugin' ),
'help_url' => 'https://help.newspack.com/engagement/audience-management-system/',
'href' => self::is_ras_campaign_configured() ? admin_url( '/admin.php?page=newspack-audience-campaigns' ) : admin_url( '/admin.php?page=newspack-audience#/campaign' ),
'action_enabled' => self::is_ras_ready_to_configure(),
'action_text' => __( 'Audience Management campaign', 'newspack-plugin' ),
'disabled_text' => __( 'Waiting for all settings to be ready', 'newspack-plugin' ),
'skippable' => true,
'is_skipped' => self::is_skipped( 'ras_campaign' ),
],
];

// ESP is only relevant when Newspack Newsletters is installed.
if ( class_exists( '\Newspack_Newsletters' ) ) {
$prerequisites['esp'] = [
'active' => self::is_esp_configured(),
'plugins' => [
'newspack-newsletters' => true,
],
'label' => __( 'Email Service Provider (ESP)', 'newspack-plugin' ),
'description' => __( 'Connect to your ESP to register readers with their email addresses and send newsletters.', 'newspack-plugin' ),
'instructions' => __( 'Connect to your email service provider (ESP) and enable at least one subscription list.', 'newspack-plugin' ),
'help_url' => 'https://help.newspack.com/engagement/audience-management-system/',
'href' => \admin_url( 'edit.php?post_type=newspack_nl_cpt&page=newspack-newsletters' ),
'action_text' => __( 'ESP settings' ),
];
}

return $prerequisites;
}

Expand Down Expand Up @@ -1372,9 +1234,11 @@ private static function get_element_class_name( ...$parts ) {
* Setup nav menu hooks.
*/
public static function setup_nav_menu() {
// Not checking if the whole WC suite is active (self::is_woocommerce_active()),
// because only the main WooCommerce plugin is actually required for this to work.
if ( ! self::get_setting( 'enabled_account_link' ) || ! function_exists( 'WC' ) ) {
// The account link works without WooCommerce: signed-out visitors get a JS-driven
// auth modal trigger. When WooCommerce is active, signed-in readers additionally
// get a "My Account" link to the Woo account page; without Woo, get_account_link()
// returns empty for signed-in users and no menu item is rendered.
if ( ! self::get_setting( 'enabled_account_link' ) ) {
return;
}

Expand Down
15 changes: 14 additions & 1 deletion includes/wizards/audience/class-audience-donations.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,15 @@ public function get_name() {

/**
* Add Donations page.
*
* The Donations wizard requires either the WooCommerce platform (with WC installed)
* or the News Revenue Hub platform. Without one of those the page cannot render
* anything meaningful, so the submenu entry is hidden entirely.
*/
public function add_page() {
if ( ! function_exists( 'WC' ) && ! Donations::is_platform_nrh() ) {
return;
}
add_submenu_page(
$this->parent_slug,
$this->get_name(),
Expand Down Expand Up @@ -167,11 +174,17 @@ public function update_donation_settings( $settings ) {
public function fetch_all_data() {
$platform = Donations::get_platform_slug();

// get_donation_settings() returns a WP_Error when the WooCommerce suite isn't
// fully active (e.g. platform is 'wc' but Subscriptions is missing). Surface an
// empty array in that case so the response doesn't carry a serialized WP_Error;
// missing plugins are reported separately via plugin_status below.
$donation_settings = Donations::get_donation_settings();

$args = [
'platform_data' => [
'platform' => $platform,
],
'donation_data' => Donations::get_donation_settings(),
'donation_data' => is_wp_error( $donation_settings ) ? [] : $donation_settings,
'donation_page' => Donations::get_donation_page_info(),
'product_validation' => $this->validate_donation_products(),
];
Expand Down
Loading
Loading