tigerstyle-whiskers/includes/whiskers/class-data-mapper.php
Ryan Malloy adbdae19c8 Initial commit: TigerStyle Whiskers v1.0.0
Navigate privacy laws with feline precision — detect every boundary,
respect every territory! GDPR compliance and privacy protection for
WordPress.

- Cookie consent management
- Privacy boundary detection
- GDPR-compliant analytics gating
- Cross-plugin consent coordination (integrates with TigerStyle Heat)
- Visitor preference tracking
- Configurable cookie categories

Includes build.sh and .distignore for WordPress-installable release ZIPs.
2026-05-27 14:31:51 -06:00

697 lines
27 KiB
PHP

<?php
/**
* Data Mapper Whisker for TigerStyle Whiskers
*
* Track personal data like a cat tracks movement - precise, comprehensive, and alert
* Maps all data processing activities for GDPR compliance
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class TigerStyleWhiskers_DataMapper {
/**
* Single instance
*/
private static $instance = null;
/**
* Data processing activities registry
*/
private $processing_activities = array();
/**
* Personal data categories tracked
*/
private $data_categories = array(
'identity' => array(
'name' => 'Identity Data',
'description' => 'Name, username, title, date of birth',
'legal_basis' => 'consent',
'retention_period' => '2 years',
'sensitivity' => 'medium'
),
'contact' => array(
'name' => 'Contact Data',
'description' => 'Email, phone number, postal address',
'legal_basis' => 'legitimate_interest',
'retention_period' => '3 years',
'sensitivity' => 'medium'
),
'technical' => array(
'name' => 'Technical Data',
'description' => 'IP address, browser type, device info',
'legal_basis' => 'legitimate_interest',
'retention_period' => '13 months',
'sensitivity' => 'low'
),
'usage' => array(
'name' => 'Usage Data',
'description' => 'Pages visited, time spent, interactions',
'legal_basis' => 'consent',
'retention_period' => '25 months',
'sensitivity' => 'low'
),
'marketing' => array(
'name' => 'Marketing Data',
'description' => 'Preferences, communication history',
'legal_basis' => 'consent',
'retention_period' => '2 years',
'sensitivity' => 'medium'
),
'financial' => array(
'name' => 'Financial Data',
'description' => 'Payment info, transaction history',
'legal_basis' => 'contract',
'retention_period' => '7 years',
'sensitivity' => 'high'
)
);
/**
* Legal basis options
*/
private $legal_basis_options = array(
'consent' => 'User has given explicit consent',
'contract' => 'Processing is necessary for contract performance',
'legal_obligation' => 'Processing is necessary for legal compliance',
'vital_interests' => 'Processing is necessary to protect vital interests',
'public_task' => 'Processing is necessary for public interest',
'legitimate_interest' => 'Processing is necessary for legitimate interests'
);
/**
* Get instance
*/
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->init_data_mapping();
}
/**
* Initialize data mapping whisker
*/
private function init_data_mapping() {
// Hook into WordPress to detect data processing
add_action('init', array($this, 'scan_data_processing_activities'));
add_action('user_register', array($this, 'map_user_registration_data'));
add_action('wp_login', array($this, 'map_login_data'), 10, 2);
add_action('comment_post', array($this, 'map_comment_data'));
// WooCommerce hooks if available
if (class_exists('WooCommerce')) {
add_action('woocommerce_checkout_order_processed', array($this, 'map_order_data'));
add_action('woocommerce_new_customer_data', array($this, 'map_customer_data'));
}
// Contact form hooks
add_action('wpcf7_mail_sent', array($this, 'map_contact_form_data'));
add_action('gform_after_submission', array($this, 'map_gravity_form_data'), 10, 2);
// Analytics integration
add_action('wp_head', array($this, 'track_analytics_data_collection'));
// Admin hooks
if (is_admin()) {
add_action('admin_post_export_data_map', array($this, 'export_data_map'));
}
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('TigerStyle Whiskers: Data mapper whisker is tracking with precision!');
}
}
/**
* Scan for data processing activities
*/
public function scan_data_processing_activities() {
$activities = array();
// Core WordPress activities
$activities = array_merge($activities, $this->scan_wordpress_core_processing());
// Plugin-based activities
$activities = array_merge($activities, $this->scan_plugin_processing());
// Theme-based activities
$activities = array_merge($activities, $this->scan_theme_processing());
// Third-party service activities
$activities = array_merge($activities, $this->scan_third_party_processing());
// Store the complete map
$this->processing_activities = $activities;
update_option('tigerstyle_whiskers_data_processing_map', $activities);
return $activities;
}
/**
* Scan WordPress core data processing
*/
private function scan_wordpress_core_processing() {
$activities = array();
// User registration
if (get_option('users_can_register')) {
$activities['user_registration'] = array(
'purpose' => 'User account creation and management',
'data_categories' => array('identity', 'contact'),
'legal_basis' => 'consent',
'recipients' => array('website_administrators'),
'retention_period' => 'Until account deletion',
'processing_location' => 'EU/US',
'automated_decision_making' => false,
'data_source' => 'User input during registration',
'security_measures' => array('password_hashing', 'access_controls')
);
}
// Comments
if (comments_open()) {
$activities['comments'] = array(
'purpose' => 'Comment moderation and display',
'data_categories' => array('identity', 'contact', 'technical'),
'legal_basis' => 'legitimate_interest',
'recipients' => array('website_visitors', 'administrators'),
'retention_period' => 'Until comment deletion',
'processing_location' => 'EU/US',
'automated_decision_making' => true,
'decision_logic' => 'Spam detection and moderation',
'data_source' => 'User input in comment forms',
'security_measures' => array('spam_filtering', 'moderation_queue')
);
}
// Admin activities
$activities['admin_access'] = array(
'purpose' => 'Website administration and security',
'data_categories' => array('technical', 'usage'),
'legal_basis' => 'legitimate_interest',
'recipients' => array('administrators'),
'retention_period' => '13 months',
'processing_location' => 'EU/US',
'automated_decision_making' => false,
'data_source' => 'Server logs and user interactions',
'security_measures' => array('access_logs', 'login_protection')
);
return $activities;
}
/**
* Scan plugin-based data processing
*/
private function scan_plugin_processing() {
$activities = array();
// TigerStyle Heat Analytics
if (class_exists('TigerStyleSEO_Google_setup')) {
$analytics_id = get_option('google_analytics_id', '');
if (!empty($analytics_id)) {
$activities['analytics'] = array(
'purpose' => 'Website traffic analysis and optimization',
'data_categories' => array('technical', 'usage'),
'legal_basis' => 'consent',
'recipients' => array('Google Analytics', 'website_administrators'),
'retention_period' => '26 months (Google Analytics default)',
'processing_location' => 'US (Google servers)',
'automated_decision_making' => true,
'decision_logic' => 'Audience segmentation and behavior analysis',
'data_source' => 'Website visitor interactions',
'security_measures' => array('data_anonymization', 'secure_transmission'),
'third_party_processor' => 'Google Ireland Limited'
);
}
}
// WooCommerce
if (class_exists('WooCommerce')) {
$activities['ecommerce'] = array(
'purpose' => 'Order processing and customer management',
'data_categories' => array('identity', 'contact', 'financial'),
'legal_basis' => 'contract',
'recipients' => array('payment_processors', 'shipping_companies'),
'retention_period' => '7 years (accounting requirements)',
'processing_location' => 'EU/US',
'automated_decision_making' => false,
'data_source' => 'Customer checkout and account creation',
'security_measures' => array('payment_encryption', 'ssl_transmission')
);
}
// Contact Form 7
if (is_plugin_active('contact-form-7/wp-contact-form-7.php')) {
$activities['contact_forms'] = array(
'purpose' => 'Customer inquiries and support',
'data_categories' => array('identity', 'contact'),
'legal_basis' => 'legitimate_interest',
'recipients' => array('support_team', 'administrators'),
'retention_period' => '2 years',
'processing_location' => 'EU/US',
'automated_decision_making' => false,
'data_source' => 'Contact form submissions',
'security_measures' => array('spam_protection', 'secure_transmission')
);
}
// MailChimp
if (is_plugin_active('mailchimp-for-wp/mailchimp-for-wp.php')) {
$activities['email_marketing'] = array(
'purpose' => 'Email marketing and newsletters',
'data_categories' => array('identity', 'contact', 'marketing'),
'legal_basis' => 'consent',
'recipients' => array('Mailchimp', 'marketing_team'),
'retention_period' => 'Until unsubscribe',
'processing_location' => 'US (Mailchimp servers)',
'automated_decision_making' => true,
'decision_logic' => 'Email campaign optimization and segmentation',
'data_source' => 'Newsletter signup forms',
'security_measures' => array('double_opt_in', 'secure_api'),
'third_party_processor' => 'The Rocket Science Group LLC (Mailchimp)'
);
}
return $activities;
}
/**
* Scan theme-based data processing
*/
private function scan_theme_processing() {
$activities = array();
// Check for theme customizations that might process data
$theme = wp_get_theme();
// Custom theme analytics or tracking
if ($this->theme_has_custom_tracking()) {
$activities['theme_tracking'] = array(
'purpose' => 'Theme-specific user experience tracking',
'data_categories' => array('technical', 'usage'),
'legal_basis' => 'legitimate_interest',
'recipients' => array('theme_developers', 'administrators'),
'retention_period' => '13 months',
'processing_location' => 'EU/US',
'automated_decision_making' => false,
'data_source' => 'Theme interaction events',
'security_measures' => array('anonymized_tracking')
);
}
return $activities;
}
/**
* Scan third-party service data processing
*/
private function scan_third_party_processing() {
$activities = array();
// Check for external services
$external_services = $this->detect_external_services();
foreach ($external_services as $service => $details) {
$activities["third_party_{$service}"] = array(
'purpose' => $details['purpose'],
'data_categories' => $details['data_categories'],
'legal_basis' => $details['legal_basis'],
'recipients' => array($details['provider']),
'retention_period' => $details['retention_period'],
'processing_location' => $details['location'],
'automated_decision_making' => $details['automated_decisions'],
'data_source' => 'Website visitor interactions',
'security_measures' => $details['security_measures'],
'third_party_processor' => $details['processor_name']
);
}
return $activities;
}
/**
* Detect external services
*/
private function detect_external_services() {
$services = array();
// Google Fonts
if ($this->uses_google_fonts()) {
$services['google_fonts'] = array(
'purpose' => 'Font delivery and display optimization',
'data_categories' => array('technical'),
'legal_basis' => 'legitimate_interest',
'retention_period' => 'Up to 1 year',
'location' => 'US (Google servers)',
'automated_decisions' => false,
'security_measures' => array('https_delivery'),
'provider' => 'Google Fonts API',
'processor_name' => 'Google Ireland Limited'
);
}
// YouTube embeds
if ($this->has_youtube_embeds()) {
$services['youtube'] = array(
'purpose' => 'Video content delivery and analytics',
'data_categories' => array('technical', 'usage'),
'legal_basis' => 'consent',
'retention_period' => 'Various (see YouTube privacy policy)',
'location' => 'US (Google servers)',
'automated_decisions' => true,
'security_measures' => array('privacy_enhanced_mode'),
'provider' => 'YouTube',
'processor_name' => 'Google Ireland Limited'
);
}
// CDN services
$cdn_service = $this->detect_cdn_service();
if ($cdn_service) {
$services['cdn'] = array(
'purpose' => 'Content delivery and performance optimization',
'data_categories' => array('technical'),
'legal_basis' => 'legitimate_interest',
'retention_period' => '30-90 days',
'location' => 'Global CDN network',
'automated_decisions' => false,
'security_measures' => array('https_delivery', 'ddos_protection'),
'provider' => $cdn_service['name'],
'processor_name' => $cdn_service['processor']
);
}
return $services;
}
/**
* Map user registration data
*/
public function map_user_registration_data($user_id) {
$user = get_user_by('id', $user_id);
$this->log_data_processing_event(array(
'event_type' => 'user_registration',
'user_id' => $user_id,
'data_collected' => array(
'username' => $user->user_login,
'email' => $user->user_email,
'registration_date' => $user->user_registered,
'ip_address' => $this->get_user_ip(),
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? ''
),
'legal_basis' => 'consent',
'purpose' => 'User account creation',
'retention_period' => 'Until account deletion'
));
}
/**
* Map login data
*/
public function map_login_data($user_login, $user) {
$this->log_data_processing_event(array(
'event_type' => 'user_login',
'user_id' => $user->ID,
'data_collected' => array(
'login_time' => current_time('mysql'),
'ip_address' => $this->get_user_ip(),
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'login_method' => 'standard'
),
'legal_basis' => 'legitimate_interest',
'purpose' => 'Security and access control',
'retention_period' => '13 months'
));
}
/**
* Map comment data
*/
public function map_comment_data($comment_id) {
$comment = get_comment($comment_id);
$this->log_data_processing_event(array(
'event_type' => 'comment_submission',
'comment_id' => $comment_id,
'data_collected' => array(
'author_name' => $comment->comment_author,
'author_email' => $comment->comment_author_email,
'author_url' => $comment->comment_author_url,
'ip_address' => $comment->comment_author_IP,
'user_agent' => $comment->comment_agent,
'comment_date' => $comment->comment_date
),
'legal_basis' => 'legitimate_interest',
'purpose' => 'Comment display and moderation',
'retention_period' => 'Until comment deletion'
));
}
/**
* Map WooCommerce order data
*/
public function map_order_data($order_id) {
if (!class_exists('WooCommerce')) {
return;
}
$order = wc_get_order($order_id);
$this->log_data_processing_event(array(
'event_type' => 'order_processing',
'order_id' => $order_id,
'data_collected' => array(
'billing_details' => $order->get_billing_address(),
'shipping_details' => $order->get_shipping_address(),
'payment_method' => $order->get_payment_method(),
'order_total' => $order->get_total(),
'customer_id' => $order->get_customer_id(),
'order_date' => $order->get_date_created()
),
'legal_basis' => 'contract',
'purpose' => 'Order fulfillment and customer service',
'retention_period' => '7 years (accounting requirements)'
));
}
/**
* Track analytics data collection
*/
public function track_analytics_data_collection() {
if (!TigerStyleWhiskers_CookieConsent::instance()->has_analytics_consent()) {
return;
}
// Only track if analytics is active
if (class_exists('TigerStyleSEO_Google_setup')) {
$analytics_id = get_option('google_analytics_id', '');
if (!empty($analytics_id)) {
$this->log_data_processing_event(array(
'event_type' => 'analytics_tracking',
'data_collected' => array(
'page_url' => $_SERVER['REQUEST_URI'] ?? '',
'referrer' => $_SERVER['HTTP_REFERER'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'ip_address' => $this->get_user_ip(),
'timestamp' => current_time('mysql')
),
'legal_basis' => 'consent',
'purpose' => 'Website analytics and optimization',
'retention_period' => '26 months',
'third_party_processor' => 'Google Analytics'
));
}
}
}
/**
* Log data processing event
*/
private function log_data_processing_event($event_data) {
// Add common fields
$event_data['timestamp'] = current_time('timestamp');
$event_data['site_url'] = home_url();
$event_data['gdpr_applies'] = TigerStyleWhiskers_BoundaryDetector::is_gdpr_territory();
// Hash sensitive data
if (isset($event_data['data_collected']['ip_address'])) {
$event_data['data_collected']['ip_address_hash'] = hash('sha256', $event_data['data_collected']['ip_address']);
unset($event_data['data_collected']['ip_address']); // Remove raw IP
}
if (isset($event_data['data_collected']['user_agent'])) {
$event_data['data_collected']['user_agent_hash'] = hash('sha256', $event_data['data_collected']['user_agent']);
unset($event_data['data_collected']['user_agent']); // Remove raw user agent
}
// Store in database (consider using a custom table for better performance)
$existing_log = get_option('tigerstyle_whiskers_processing_log', array());
$existing_log[] = $event_data;
// Keep only last 1000 events to prevent database bloat
if (count($existing_log) > 1000) {
$existing_log = array_slice($existing_log, -1000);
}
update_option('tigerstyle_whiskers_processing_log', $existing_log);
// Also log to audit trail if available
if (class_exists('TigerStyleWhiskers_AuditTrail') && isset($event_data['event_type'])) {
$audit_trail = TigerStyleWhiskers_AuditTrail::instance();
$audit_trail->log_event($event_data['event_type'], $event_data);
}
}
/**
* Get user IP address (anonymized)
*/
private function get_user_ip() {
$ip = '';
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
// Anonymize IP for privacy (remove last octet for IPv4)
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$ip_parts = explode('.', $ip);
$ip_parts[3] = '0';
$ip = implode('.', $ip_parts);
}
return $ip;
}
/**
* Helper methods for detection
*/
private function theme_has_custom_tracking() {
// Check theme files for tracking code
return false; // Simplified for now
}
private function uses_google_fonts() {
// Check if Google Fonts are loaded
return strpos(file_get_contents(get_template_directory() . '/style.css'), 'fonts.googleapis.com') !== false;
}
private function has_youtube_embeds() {
// Check for YouTube embeds in content
return false; // Simplified for now
}
private function detect_cdn_service() {
// Detect CDN services
return false; // Simplified for now
}
/**
* Export data processing map
*/
public function export_data_map() {
if (!current_user_can('manage_options')) {
wp_die('You do not have sufficient permissions');
}
$data_map = array(
'site_info' => array(
'site_url' => home_url(),
'site_name' => get_bloginfo('name'),
'export_date' => current_time('mysql'),
'wordpress_version' => get_bloginfo('version'),
'whiskers_version' => TIGERSTYLE_WHISKERS_VERSION
),
'processing_activities' => $this->processing_activities,
'data_categories' => $this->data_categories,
'legal_basis_explanations' => $this->legal_basis_options,
'compliance_status' => TigerStyleWhiskers_ComplianceScanner::get_status()
);
// Set headers for download
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="data-processing-map-' . date('Y-m-d') . '.json"');
header('Cache-Control: no-cache, must-revalidate');
echo json_encode($data_map, JSON_PRETTY_PRINT);
exit;
}
/**
* Get processing activities for admin display
*/
public function get_processing_activities() {
return $this->processing_activities;
}
/**
* Get data categories
*/
public function get_data_categories() {
return $this->data_categories;
}
/**
* Render admin page
*/
public function render_admin_page() {
$activities = $this->get_processing_activities();
?>
<div class="tigerstyle-whiskers-admin-section">
<h2><?php _e('Data Processing Map', 'tigerstyle-whiskers'); ?></h2>
<p class="description">
<?php _e('Track personal data like a cat tracks movement - comprehensive mapping of all data processing activities on your website.', 'tigerstyle-whiskers'); ?>
</p>
<div class="tw-data-map-actions">
<a href="<?php echo admin_url('admin-post.php?action=export_data_map'); ?>"
class="button button-secondary">
<?php _e('Export Data Processing Map', 'tigerstyle-whiskers'); ?>
</a>
<button type="button" class="button button-primary" onclick="tigerstyleWhiskers.refreshDataMap()">
<?php _e('Refresh Scan', 'tigerstyle-whiskers'); ?>
</button>
</div>
<?php if (!empty($activities)): ?>
<div class="tw-processing-activities">
<h3><?php _e('Detected Processing Activities', 'tigerstyle-whiskers'); ?></h3>
<?php foreach ($activities as $activity_id => $activity): ?>
<div class="tw-activity-card">
<h4><?php echo esc_html(ucwords(str_replace('_', ' ', $activity_id))); ?></h4>
<div class="tw-activity-details">
<p><strong><?php _e('Purpose:', 'tigerstyle-whiskers'); ?></strong> <?php echo esc_html($activity['purpose']); ?></p>
<p><strong><?php _e('Legal Basis:', 'tigerstyle-whiskers'); ?></strong> <?php echo esc_html(ucwords($activity['legal_basis'])); ?></p>
<p><strong><?php _e('Data Categories:', 'tigerstyle-whiskers'); ?></strong> <?php echo esc_html(implode(', ', $activity['data_categories'])); ?></p>
<p><strong><?php _e('Retention Period:', 'tigerstyle-whiskers'); ?></strong> <?php echo esc_html($activity['retention_period']); ?></p>
<?php if (isset($activity['third_party_processor'])): ?>
<p><strong><?php _e('Third Party Processor:', 'tigerstyle-whiskers'); ?></strong> <?php echo esc_html($activity['third_party_processor']); ?></p>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="notice notice-info">
<p><?php _e('No data processing activities detected yet. The whiskers are still scanning...', 'tigerstyle-whiskers'); ?></p>
</div>
<?php endif; ?>
</div>
<?php
}
}