PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.2.0
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.2.0
1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
xspeed / includes / modules / AIPrivacy / AIPrivacyModule.php

AIPrivacyModule.php in xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN 1.2.0, at includes/modules/AIPrivacy/AIPrivacyModule.php

116 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AI Privacy module — the GDPR off-switch FEATURES.md §AI row 6
4 * mandates ship in Free, regardless of whether the user has Pro
5 * installed.
6 *
7 * Why this lives in Free even though every AI feature is Pro: privacy
8 * is a fundamental user right, not a paid feature. If a site activates
9 * xSpeed Pro and starts collecting navigation patterns / Web Vitals
10 * for AI analysis, the GDPR off-switch must already be present and
11 * configured — not gated behind a license. So this module ships with
12 * Free and exposes a public filter that every Pro AI feature checks
13 * before recording any data point.
14 *
15 * Pro consumes this via:
16 * if ( ! apply_filters( 'xspeed_ai_can_collect_data', true ) ) return;
17 *
18 * When Pro is not installed, the module still works (the toggle just
19 * has no consumer) — same pattern as our other Free/Pro contracts.
20 *
21 * @package XSpeed
22 */
23
24 declare(strict_types=1);
25
26 namespace XSpeed\Modules\AIPrivacy;
27
28 defined( 'ABSPATH' ) || exit;
29
30 use XSpeed\Module;
31 use XSpeed\Settings_Manager;
32
33 final class AIPrivacyModule extends Module {
34
35 public const SLUG = 'ai-privacy';
36 public const TIER = self::TIER_FREE;
37 public const VERSION = '1.0.0';
38
39 /**
40 * Cookie name a consent banner sets when the visitor accepts AI
41 * data collection. Banner ships separately (UI work) — defining the
42 * key here means Pro modules and any third-party banner agree on a
43 * single source of truth.
44 */
45 public const CONSENT_COOKIE = 'xspeed_ai_consent';
46
47 public function ui_metadata(): array {
48 return array(
49 'label' => 'AI Privacy',
50 'icon' => 'Shield',
51 'description' => 'Govern whether AI-powered features (Pro) may collect data from your visitors. The off-switch is here in Free because privacy is a fundamental right, not a paid feature.',
52 );
53 }
54
55 public function settings_schema(): array {
56 return array(
57 'gdpr_consent_required' => array(
58 'type' => 'bool',
59 'default' => true,
60 'label' => 'Require consent before AI data collection',
61 'description' => 'When ON, AI-powered features only record data after a visitor accepts the consent banner. When OFF, they collect from every visitor — only legal in regions without GDPR-style consent rules. The setting applies even if Pro is not installed (so a later Pro upgrade respects whichever choice you made).',
62 ),
63 'consent_banner_text' => array(
64 'type' => 'string',
65 'default' => 'We collect anonymized navigation and performance data to make this site faster. Accept to help us optimize your experience.',
66 'label' => 'Consent banner text',
67 'description' => 'Shown in the cookie consent banner. Keep it factual — what you collect (page navigation, performance metrics) and why.',
68 ),
69 );
70 }
71
72 /**
73 * The canonical "may we collect data right now?" decision. Pro AI
74 * features should ALWAYS go through the `xspeed_ai_can_collect_data`
75 * filter (which this method backs by default) rather than calling
76 * this directly — so a site owner can override the decision with
77 * their own filter callback (e.g., a custom CMP integration).
78 */
79 public static function can_collect( bool $default = true ): bool {
80 $opts = Settings_Manager::get( self::SLUG );
81
82 // Privacy mode off → fall back to whatever the caller proposed.
83 // Most callers proposed true; an upstream filter that already
84 // returned false gets preserved.
85 if ( empty( $opts['gdpr_consent_required'] ) ) {
86 return $default;
87 }
88
89 // Privacy mode on → require explicit consent cookie. Absence of
90 // the cookie means the visitor hasn't accepted the banner yet
91 // (or rejected it) and we record nothing.
92 return ! empty( $_COOKIE[ self::CONSENT_COOKIE ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
93 }
94
95 public function boot(): void {
96 add_filter( 'xspeed_ai_can_collect_data', array( self::class, 'can_collect' ), 10, 1 );
97 }
98
99 public function cli_commands(): array {
100 return array(
101 array(
102 'name' => 'xspeed ai-privacy',
103 'callback' => array( $this, 'cli_handler' ),
104 'shortdesc' => 'Show whether AI data collection is allowed for the current request context (CLI = no cookie).',
105 'synopsis' => array(),
106 ),
107 );
108 }
109
110 public function cli_handler( array $args, array $assoc ): void {
111 $opts = $this->get_settings();
112 \WP_CLI::log( sprintf( '%-26s %s', 'gdpr_consent_required', ! empty( $opts['gdpr_consent_required'] ) ? 'on' : 'off' ) );
113 \WP_CLI::log( sprintf( '%-26s %s', 'can_collect (no cookie)', self::can_collect() ? 'yes' : 'no' ) );
114 }
115 }
116