| Server IP : 3.147.158.171 / Your IP : 216.73.216.216 Web Server : Apache/2.4.67 (Amazon Linux) OpenSSL/3.5.5 System : Linux ip-172-31-2-178.us-east-2.compute.internal 6.1.172-216.329.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 20 06:31:34 UTC 2026 x86_64 User : ec2-user ( 1000) PHP Version : 8.4.21 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /tsai/repo/plugins/tsai-compliance/includes/ |
Upload File : |
<?php
/**
* Consent state, settings, GPC detection, and consent logging.
*/
if (!defined('ABSPATH')) {
exit;
}
class TSAI_Compliance_Consent {
const OPTION = 'tsai_compliance_settings';
const COOKIE = 'tsai_consent';
/**
* Default settings. The category keys ('analytics', 'marketing') are the
* values used in data-tsai-category="..." attributes on gated scripts.
*/
public static function defaults() {
return array(
// 'optin' = full consent banner + per-category script gating (required once
// you load analytics/marketing trackers).
// 'notice' = slim dismissible notice, no gating (right when a site sets only
// functional cookies — comments, logins, saved preferences).
'mode' => 'notice',
'notice_text' => 'This site uses cookies that are necessary for it to function (comments, logins, and saved preferences). We do not use advertising or third-party tracking cookies.',
'privacy_policy_url' => '',
'do_not_sell_url' => '', // optional; blank = use the preferences modal
'banner_heading' => 'We value your privacy',
'banner_text' => 'We use cookies to analyze traffic and improve your experience. You can accept all, reject non-essential cookies, or choose which categories to allow. Necessary cookies are always on.',
'cookie_days' => 180,
'consent_version' => 1,
'enable_logging' => 1,
'categories' => array(
'analytics' => array(
'label' => 'Analytics',
'description' => 'Helps us understand how visitors use the site (e.g. page views, traffic sources).',
),
'marketing' => array(
'label' => 'Marketing',
'description' => 'Used to personalize content and measure advertising.',
),
),
// One external script per line, format: category|https://example.com/script.js
'gated_scripts' => '',
);
}
public static function settings() {
$saved = get_option(self::OPTION, array());
if (!is_array($saved)) {
$saved = array();
}
$s = wp_parse_args($saved, self::defaults());
// categories must merge per-key so partial saves don't drop defaults
if (empty($s['categories']) || !is_array($s['categories'])) {
$s['categories'] = self::defaults()['categories'];
}
return $s;
}
public static function init() {
add_action('rest_api_init', array(__CLASS__, 'register_routes'));
}
public static function activate() {
self::create_table();
if (get_option(self::OPTION) === false) {
add_option(self::OPTION, self::defaults());
}
}
/**
* Global Privacy Control: a Sec-GPC: 1 request header is a legally
* recognized opt-out signal under California's CPRA. When present we treat
* all non-necessary categories as denied.
*/
public static function is_gpc() {
return isset($_SERVER['HTTP_SEC_GPC']) && $_SERVER['HTTP_SEC_GPC'] === '1';
}
/**
* Returns the decoded consent cookie as an array, or null if absent/invalid.
*/
public static function get_consent() {
if (empty($_COOKIE[self::COOKIE])) {
return null;
}
$raw = wp_unslash($_COOKIE[self::COOKIE]);
$data = json_decode($raw, true);
if (!is_array($data)) {
return null;
}
return $data;
}
/**
* Server-side check usable in PHP/templates:
* if (TSAI_Compliance_Consent::has_consent('analytics')) { ... }
*/
public static function has_consent($category) {
if ($category === 'necessary') {
return true;
}
if (self::is_gpc()) {
return false;
}
$c = self::get_consent();
return $c && !empty($c[$category]);
}
/* ---------- Consent logging ---------- */
public static function table_name() {
global $wpdb;
return $wpdb->prefix . 'tsai_consent_log';
}
public static function create_table() {
global $wpdb;
$table = self::table_name();
$charset = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
consent_id VARCHAR(64) NOT NULL,
categories TEXT NOT NULL,
gpc TINYINT(1) NOT NULL DEFAULT 0,
ip_hash CHAR(64) NOT NULL,
user_agent VARCHAR(255) NOT NULL DEFAULT '',
consent_version INT NOT NULL DEFAULT 1,
created_at DATETIME NOT NULL,
PRIMARY KEY (id),
KEY consent_id (consent_id)
) $charset;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
public static function register_routes() {
register_rest_route('tsai-compliance/v1', '/consent', array(
'methods' => 'POST',
'callback' => array(__CLASS__, 'record_consent'),
'permission_callback' => '__return_true', // public: visitors record their own consent
));
}
public static function record_consent($request) {
$s = self::settings();
if (empty($s['enable_logging'])) {
return new WP_REST_Response(array('logged' => false), 200);
}
global $wpdb;
$params = $request->get_json_params();
$categories = isset($params['categories']) && is_array($params['categories']) ? $params['categories'] : array();
$consent_id = isset($params['consent_id']) ? sanitize_text_field($params['consent_id']) : wp_generate_uuid4();
// Hash the IP rather than storing it raw — it's a compliance tool, after all.
$ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
$ip_hash = $ip ? hash('sha256', $ip . wp_salt()) : '';
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? substr(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])), 0, 255) : '';
$wpdb->insert(self::table_name(), array(
'consent_id' => substr($consent_id, 0, 64),
'categories' => wp_json_encode($categories),
'gpc' => self::is_gpc() ? 1 : 0,
'ip_hash' => $ip_hash,
'user_agent' => $ua,
'consent_version' => intval($s['consent_version']),
'created_at' => current_time('mysql', true),
));
return new WP_REST_Response(array('logged' => true, 'consent_id' => $consent_id), 200);
}
}