File: /var/www/kevin-demo/wp-content/plugins/allspice/includes/membership-ads.php
<?php
// includes/membership-ads.php
//
// Ad suppression for ad-free members, done server-side: PHP knows access before the page
// renders (signed cookie, includes/membership.php), so we emit what the ad networks
// actually consume:
// - Raptive/AdThrive: the `adthrive-disable-all` body class (their supported site-level
// switch; the old `window.adthriveDisableAds` global is not used)
// - Mediavine: a `#mediavine-settings` element with per-slot data-blocklist attributes
// Both are emitted with no provider detection. Each signal is inert on the other
// network's sites, so sniffing the installed network would only add a failure mode.
//
// Everything here is gated on the `adFree` benefit from a validated member session.
// Anonymous readers, non-members, members without that benefit, and invalid/expired
// cookies produce no output. Membership alone is not ad-free; the publisher has to
// enable the benefit.
if (!defined('ABSPATH')) exit;
/*
* Single gate for this module: a validated member session carrying the `adFree` benefit.
* `adFree` (camelCase) is the canonical id, the key in ALLSPICE_BUILTIN_BENEFITS and what
* the signed cookie stores. This once checked `ad_free`, which the cookie never contains,
* so ad-free members kept seeing ads. Compare against the stored id only.
*/
function allspice_member_is_ad_free(): bool {
return function_exists('allspice_member_has_benefit') && allspice_member_has_benefit('adFree');
}
/*
* Emergency per-network kill switch, not provider detection. Both default on; the filter
* exists so a broken interaction with one network can be turned off without a release.
*/
function allspice_ad_free_networks(): array {
$defaults = ['raptive' => true, 'mediavine' => true];
$networks = apply_filters('allspice_ad_free_networks', $defaults);
return is_array($networks) ? $networks : $defaults;
}
/*
* Body classes, server-side. `adthrive-disable-all` is Raptive's own switch; the two
* allspice-* classes are ours (styling hooks + the page bundle's "server-visible state"
* marker for its guarded one-time reload).
*/
add_filter('body_class', 'allspice_member_body_classes', 1);
function allspice_member_body_classes(array $classes): array {
if (!allspice_member_is_ad_free()) return $classes;
if (!empty(allspice_ad_free_networks()['raptive'])) {
$classes[] = 'adthrive-disable-all';
/* Raptive treats VIDEO as its own product (live finding, baking4happiness
2026-08-26): disable-all removes display units only, and their plugin gates the
Playlist + Sticky Outstream players on this separate class (it also feeds
window.adthriveCLS.videoDisabledFromPlugin from it). Without it, an ad-free
member lost the banners but kept the sponsored video in the post and the sticky
corner player. Auto-INSERTED players only: a video the publisher embedded by
hand is content, not an ad, and stays. */
$classes[] = 'adthrive-disable-video';
}
$classes[] = 'allspice-paid-member';
$classes[] = 'allspice-ad-free-member';
return $classes;
}
/*
* The Mediavine per-slot blocklist for ad-free members: every display slot Mediavine
* documents for `#mediavine-settings` is blocked. Universal-player attributes are held
* behind a filter until the intended video-player behavior is verified - flipping
* `allspice_mediavine_blocklist_universal_player` to true adds them, no release needed.
*/
function allspice_mediavine_settings_attributes(): array {
$attrs = [
'data-blocklist-leaderboard' => '1',
'data-blocklist-sidebar-atf' => '1',
'data-blocklist-sidebar-btf' => '1',
'data-blocklist-content-desktop' => '1',
'data-blocklist-content-mobile' => '1',
'data-blocklist-adhesion-mobile' => '1',
'data-blocklist-adhesion-tablet' => '1',
'data-blocklist-adhesion-desktop' => '1',
'data-blocklist-recipe' => '1',
'data-blocklist-auto-insert-sticky' => '1',
'data-blocklist-chicory' => '1',
'data-blocklist-zergnet' => '1',
'data-blocklist-interstitial-mobile' => '1',
'data-blocklist-interstitial-desktop' => '1',
];
if (apply_filters('allspice_mediavine_blocklist_universal_player', false)) {
$attrs['data-blocklist-universal-player-desktop'] = '1';
$attrs['data-blocklist-universal-player-mobile'] = '1';
}
return apply_filters('allspice_mediavine_settings_attributes', $attrs);
}
function allspice_mediavine_settings_element(): string {
if (!allspice_member_is_ad_free()) return '';
if (empty(allspice_ad_free_networks()['mediavine'])) return '';
$html = '<div id="mediavine-settings"';
foreach (allspice_mediavine_settings_attributes() as $name => $value) {
/* Attribute names come from our own fixed table (plus filter); sanitize both sides. */
$name = preg_replace('/[^a-z0-9\-]/', '', strtolower((string)$name));
if ($name === '') continue;
$html .= ' ' . $name . '="' . esc_attr((string)$value) . '"';
}
return $html . '></div>';
}
/*
* Emit early in <body> (wp_body_open) so the element exists before Mediavine's script
* initializes its slots, with a wp_footer fallback for the rare theme that never calls
* wp_body_open. The flag guarantees exactly one element.
*/
add_action('wp_body_open', 'allspice_member_print_mediavine_settings', 1);
add_action('wp_footer', 'allspice_member_print_mediavine_settings', 1);
function allspice_member_print_mediavine_settings(): void {
static $printed = false;
if ($printed) return;
$el = allspice_mediavine_settings_element();
if ($el === '') return;
$printed = true;
echo $el . "\n";
}