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.
944 lines
32 KiB
PHP
944 lines
32 KiB
PHP
<?php
|
|
/**
|
|
* Data Deletion Whisker for TigerStyle Whiskers
|
|
*
|
|
* Data deletion with surgical precision - like a cat covering its tracks
|
|
* Implements GDPR Right to Erasure and data retention policies
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class TigerStyleWhiskers_DataDeletion {
|
|
|
|
/**
|
|
* Single instance
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Deletion request statuses
|
|
*/
|
|
private $deletion_statuses = array(
|
|
'pending' => 'Pending Review',
|
|
'processing' => 'Processing Deletion',
|
|
'completed' => 'Deletion Completed',
|
|
'rejected' => 'Request Rejected',
|
|
'verified' => 'Identity Verified'
|
|
);
|
|
|
|
/**
|
|
* Data sources that can be deleted
|
|
*/
|
|
private $deletable_sources = array(
|
|
'user_account' => array(
|
|
'name' => 'User Account Data',
|
|
'description' => 'Profile, preferences, and account information',
|
|
'complexity' => 'medium',
|
|
'dependencies' => array('comments', 'orders')
|
|
),
|
|
'comments' => array(
|
|
'name' => 'Comments and Reviews',
|
|
'description' => 'All comments and review content',
|
|
'complexity' => 'low',
|
|
'dependencies' => array()
|
|
),
|
|
'orders' => array(
|
|
'name' => 'Order History',
|
|
'description' => 'E-commerce orders and transactions',
|
|
'complexity' => 'high',
|
|
'dependencies' => array('accounting_records')
|
|
),
|
|
'analytics' => array(
|
|
'name' => 'Analytics Data',
|
|
'description' => 'Tracking and behavior data',
|
|
'complexity' => 'medium',
|
|
'dependencies' => array()
|
|
),
|
|
'marketing' => array(
|
|
'name' => 'Marketing Data',
|
|
'description' => 'Email lists and marketing preferences',
|
|
'complexity' => 'low',
|
|
'dependencies' => array()
|
|
),
|
|
'logs' => array(
|
|
'name' => 'Access Logs',
|
|
'description' => 'Login and activity logs',
|
|
'complexity' => 'low',
|
|
'dependencies' => array()
|
|
)
|
|
);
|
|
|
|
/**
|
|
* Get instance
|
|
*/
|
|
public static function instance() {
|
|
if (is_null(self::$instance)) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
$this->init_deletion_whisker();
|
|
}
|
|
|
|
/**
|
|
* Initialize data deletion whisker
|
|
*/
|
|
private function init_deletion_whisker() {
|
|
// Frontend hooks for deletion requests
|
|
add_action('wp_ajax_tigerstyle_whiskers_request_deletion', array($this, 'handle_deletion_request'));
|
|
add_action('wp_ajax_nopriv_tigerstyle_whiskers_request_deletion', array($this, 'handle_deletion_request'));
|
|
|
|
// Admin hooks for managing deletion requests
|
|
if (is_admin()) {
|
|
add_action('admin_post_process_deletion_request', array($this, 'admin_process_deletion'));
|
|
add_action('admin_post_export_user_data', array($this, 'export_user_data'));
|
|
}
|
|
|
|
// Automated deletion hooks
|
|
add_action('tigerstyle_whiskers_daily_cleanup', array($this, 'process_automated_deletions'));
|
|
add_action('tigerstyle_whiskers_retention_cleanup', array($this, 'enforce_retention_policies'));
|
|
|
|
// Schedule automated cleanup if not already scheduled
|
|
if (!wp_next_scheduled('tigerstyle_whiskers_daily_cleanup')) {
|
|
wp_schedule_event(time(), 'daily', 'tigerstyle_whiskers_daily_cleanup');
|
|
}
|
|
|
|
// User deletion hooks
|
|
add_action('delete_user', array($this, 'handle_user_deletion'));
|
|
add_action('wp_privacy_personal_data_erased', array($this, 'handle_privacy_erasure'));
|
|
|
|
// Integration with WordPress privacy tools
|
|
add_filter('wp_privacy_personal_data_erasers', array($this, 'register_privacy_erasers'));
|
|
add_filter('wp_privacy_personal_data_exporters', array($this, 'register_privacy_exporters'));
|
|
|
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
|
error_log('TigerStyle Whiskers: Data deletion whisker is ready to cover tracks with precision!');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle deletion request from frontend
|
|
*/
|
|
public function handle_deletion_request() {
|
|
// Verify nonce
|
|
if (!wp_verify_nonce($_POST['nonce'], 'tigerstyle_whiskers_deletion')) {
|
|
wp_send_json_error(__('Security check failed', 'tigerstyle-whiskers'));
|
|
}
|
|
|
|
$email = sanitize_email($_POST['email'] ?? '');
|
|
$request_type = sanitize_text_field($_POST['request_type'] ?? 'full_deletion');
|
|
$reason = sanitize_textarea_field($_POST['reason'] ?? '');
|
|
$data_sources = array_map('sanitize_text_field', $_POST['data_sources'] ?? array());
|
|
|
|
if (empty($email) || !is_email($email)) {
|
|
wp_send_json_error(__('Please provide a valid email address', 'tigerstyle-whiskers'));
|
|
}
|
|
|
|
// Create deletion request
|
|
$request_id = $this->create_deletion_request(array(
|
|
'email' => $email,
|
|
'request_type' => $request_type,
|
|
'reason' => $reason,
|
|
'data_sources' => $data_sources,
|
|
'ip_address' => $this->get_user_ip(),
|
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
|
'status' => 'pending',
|
|
'created_at' => current_time('mysql')
|
|
));
|
|
|
|
if ($request_id) {
|
|
// Send confirmation email
|
|
$this->send_deletion_confirmation_email($email, $request_id);
|
|
|
|
// Notify administrators
|
|
$this->notify_admin_of_deletion_request($request_id);
|
|
|
|
wp_send_json_success(array(
|
|
'message' => __('Your deletion request has been received. You will receive a confirmation email shortly.', 'tigerstyle-whiskers'),
|
|
'request_id' => $request_id
|
|
));
|
|
} else {
|
|
wp_send_json_error(__('Failed to process deletion request. Please try again.', 'tigerstyle-whiskers'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create deletion request record
|
|
*/
|
|
private function create_deletion_request($request_data) {
|
|
global $wpdb;
|
|
|
|
// Create custom table if it doesn't exist
|
|
$this->create_deletion_requests_table();
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_whiskers_deletion_requests';
|
|
|
|
$result = $wpdb->insert(
|
|
$table_name,
|
|
array(
|
|
'email' => $request_data['email'],
|
|
'request_type' => $request_data['request_type'],
|
|
'reason' => $request_data['reason'],
|
|
'data_sources' => json_encode($request_data['data_sources']),
|
|
'ip_address_hash' => hash('sha256', $request_data['ip_address']),
|
|
'user_agent_hash' => hash('sha256', $request_data['user_agent']),
|
|
'status' => $request_data['status'],
|
|
'created_at' => $request_data['created_at'],
|
|
'verification_token' => wp_generate_password(32, false)
|
|
),
|
|
array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')
|
|
);
|
|
|
|
return $result ? $wpdb->insert_id : false;
|
|
}
|
|
|
|
/**
|
|
* Create deletion requests table
|
|
*/
|
|
private function create_deletion_requests_table() {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_whiskers_deletion_requests';
|
|
|
|
$charset_collate = $wpdb->get_charset_collate();
|
|
|
|
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
|
|
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
|
email varchar(255) NOT NULL,
|
|
request_type varchar(50) NOT NULL,
|
|
reason text,
|
|
data_sources text,
|
|
ip_address_hash varchar(64),
|
|
user_agent_hash varchar(64),
|
|
status varchar(20) DEFAULT 'pending',
|
|
verification_token varchar(32),
|
|
verified_at datetime DEFAULT NULL,
|
|
processed_at datetime DEFAULT NULL,
|
|
completed_at datetime DEFAULT NULL,
|
|
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
|
notes text,
|
|
PRIMARY KEY (id),
|
|
KEY email (email),
|
|
KEY status (status),
|
|
KEY created_at (created_at)
|
|
) $charset_collate;";
|
|
|
|
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
|
dbDelta($sql);
|
|
}
|
|
|
|
/**
|
|
* Send deletion confirmation email
|
|
*/
|
|
private function send_deletion_confirmation_email($email, $request_id) {
|
|
$request = $this->get_deletion_request($request_id);
|
|
if (!$request) return;
|
|
|
|
$verification_url = add_query_arg(array(
|
|
'action' => 'verify_deletion',
|
|
'request_id' => $request_id,
|
|
'token' => $request->verification_token
|
|
), home_url());
|
|
|
|
$subject = sprintf(__('[%s] Data Deletion Request Confirmation', 'tigerstyle-whiskers'), get_bloginfo('name'));
|
|
|
|
$message = sprintf(__('Hello,
|
|
|
|
We have received a request to delete your personal data from %s.
|
|
|
|
Request Details:
|
|
- Request ID: %s
|
|
- Request Type: %s
|
|
- Date: %s
|
|
|
|
To verify this request and proceed with deletion, please click the following link:
|
|
%s
|
|
|
|
If you did not make this request, please ignore this email or contact us immediately.
|
|
|
|
This verification link will expire in 48 hours for security reasons.
|
|
|
|
Best regards,
|
|
The %s Team
|
|
|
|
---
|
|
This is an automated message from TigerStyle Whiskers GDPR Compliance System.', 'tigerstyle-whiskers'),
|
|
get_bloginfo('name'),
|
|
$request_id,
|
|
ucwords(str_replace('_', ' ', $request->request_type)),
|
|
date_i18n(get_option('date_format'), strtotime($request->created_at)),
|
|
$verification_url,
|
|
get_bloginfo('name')
|
|
);
|
|
|
|
$headers = array('Content-Type: text/plain; charset=UTF-8');
|
|
|
|
wp_mail($email, $subject, $message, $headers);
|
|
}
|
|
|
|
/**
|
|
* Notify admin of deletion request
|
|
*/
|
|
private function notify_admin_of_deletion_request($request_id) {
|
|
$admin_email = get_option('admin_email');
|
|
$request = $this->get_deletion_request($request_id);
|
|
|
|
if (!$request) return;
|
|
|
|
$subject = sprintf(__('[%s] New Data Deletion Request #%s', 'tigerstyle-whiskers'), get_bloginfo('name'), $request_id);
|
|
|
|
$admin_url = admin_url('admin.php?page=tigerstyle-whiskers&tab=deletion&request_id=' . $request_id);
|
|
|
|
$message = sprintf(__('A new data deletion request has been submitted.
|
|
|
|
Request Details:
|
|
- Request ID: %s
|
|
- Email: %s
|
|
- Request Type: %s
|
|
- Reason: %s
|
|
- Date: %s
|
|
|
|
Please review this request in the admin panel:
|
|
%s
|
|
|
|
The request requires verification by the user before processing can begin.
|
|
|
|
---
|
|
TigerStyle Whiskers GDPR Compliance System', 'tigerstyle-whiskers'),
|
|
$request_id,
|
|
$request->email,
|
|
ucwords(str_replace('_', ' ', $request->request_type)),
|
|
$request->reason ?: 'Not specified',
|
|
date_i18n(get_option('date_format'), strtotime($request->created_at)),
|
|
$admin_url
|
|
);
|
|
|
|
wp_mail($admin_email, $subject, $message);
|
|
}
|
|
|
|
/**
|
|
* Get deletion request by ID
|
|
*/
|
|
private function get_deletion_request($request_id) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_whiskers_deletion_requests';
|
|
|
|
return $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM $table_name WHERE id = %d",
|
|
$request_id
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Process deletion request (admin action)
|
|
*/
|
|
public function admin_process_deletion() {
|
|
if (!current_user_can('manage_options')) {
|
|
wp_die(__('You do not have sufficient permissions', 'tigerstyle-whiskers'));
|
|
}
|
|
|
|
if (!wp_verify_nonce($_POST['deletion_nonce'], 'process_deletion')) {
|
|
wp_die(__('Security check failed', 'tigerstyle-whiskers'));
|
|
}
|
|
|
|
$request_id = intval($_POST['request_id']);
|
|
$action = sanitize_text_field($_POST['deletion_action']);
|
|
$notes = sanitize_textarea_field($_POST['admin_notes'] ?? '');
|
|
|
|
$request = $this->get_deletion_request($request_id);
|
|
if (!$request) {
|
|
wp_die(__('Invalid deletion request', 'tigerstyle-whiskers'));
|
|
}
|
|
|
|
switch ($action) {
|
|
case 'approve':
|
|
$this->execute_deletion($request_id, $notes);
|
|
break;
|
|
case 'reject':
|
|
$this->reject_deletion($request_id, $notes);
|
|
break;
|
|
case 'verify':
|
|
$this->verify_deletion_request($request_id);
|
|
break;
|
|
}
|
|
|
|
wp_redirect(admin_url('admin.php?page=tigerstyle-whiskers&tab=deletion&message=request_processed'));
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Execute deletion with surgical precision
|
|
*/
|
|
private function execute_deletion($request_id, $admin_notes = '') {
|
|
$request = $this->get_deletion_request($request_id);
|
|
if (!$request || $request->status !== 'verified') {
|
|
return false;
|
|
}
|
|
|
|
// Update status to processing
|
|
$this->update_deletion_request_status($request_id, 'processing', $admin_notes);
|
|
|
|
$data_sources = json_decode($request->data_sources, true) ?: array();
|
|
$deletion_log = array();
|
|
|
|
// Process each data source with feline precision
|
|
foreach ($data_sources as $source) {
|
|
$result = $this->delete_data_source($request->email, $source);
|
|
$deletion_log[$source] = $result;
|
|
}
|
|
|
|
// If full deletion requested, delete everything
|
|
if ($request->request_type === 'full_deletion') {
|
|
foreach ($this->deletable_sources as $source => $config) {
|
|
if (!in_array($source, $data_sources)) {
|
|
$result = $this->delete_data_source($request->email, $source);
|
|
$deletion_log[$source] = $result;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store deletion log
|
|
$this->store_deletion_log($request_id, $deletion_log);
|
|
|
|
// Update status to completed
|
|
$this->update_deletion_request_status($request_id, 'completed', 'Deletion completed successfully');
|
|
|
|
// Send completion notification
|
|
$this->send_deletion_completion_email($request->email, $request_id, $deletion_log);
|
|
|
|
// Log for audit trail
|
|
TigerStyleWhiskers_AuditTrail::log_event(array(
|
|
'event_type' => 'data_deletion_completed',
|
|
'request_id' => $request_id,
|
|
'email_hash' => hash('sha256', $request->email),
|
|
'data_sources_deleted' => array_keys($deletion_log),
|
|
'admin_notes' => $admin_notes,
|
|
'timestamp' => current_time('timestamp')
|
|
));
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Delete specific data source
|
|
*/
|
|
private function delete_data_source($email, $source) {
|
|
$result = array(
|
|
'source' => $source,
|
|
'success' => false,
|
|
'items_deleted' => 0,
|
|
'errors' => array(),
|
|
'timestamp' => current_time('mysql')
|
|
);
|
|
|
|
try {
|
|
switch ($source) {
|
|
case 'user_account':
|
|
$result = $this->delete_user_account_data($email);
|
|
break;
|
|
case 'comments':
|
|
$result = $this->delete_comment_data($email);
|
|
break;
|
|
case 'orders':
|
|
$result = $this->delete_order_data($email);
|
|
break;
|
|
case 'analytics':
|
|
$result = $this->delete_analytics_data($email);
|
|
break;
|
|
case 'marketing':
|
|
$result = $this->delete_marketing_data($email);
|
|
break;
|
|
case 'logs':
|
|
$result = $this->delete_log_data($email);
|
|
break;
|
|
default:
|
|
$result['errors'][] = 'Unknown data source: ' . $source;
|
|
}
|
|
} catch (Exception $e) {
|
|
$result['errors'][] = $e->getMessage();
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Delete user account data
|
|
*/
|
|
private function delete_user_account_data($email) {
|
|
$user = get_user_by('email', $email);
|
|
$result = array(
|
|
'source' => 'user_account',
|
|
'success' => false,
|
|
'items_deleted' => 0,
|
|
'errors' => array()
|
|
);
|
|
|
|
if (!$user) {
|
|
$result['errors'][] = 'User not found';
|
|
return $result;
|
|
}
|
|
|
|
// Before deleting, handle dependencies
|
|
$this->anonymize_user_content($user->ID);
|
|
|
|
// Delete user account
|
|
$deleted = wp_delete_user($user->ID);
|
|
|
|
if ($deleted) {
|
|
$result['success'] = true;
|
|
$result['items_deleted'] = 1;
|
|
} else {
|
|
$result['errors'][] = 'Failed to delete user account';
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Delete comment data
|
|
*/
|
|
private function delete_comment_data($email) {
|
|
$comments = get_comments(array('author_email' => $email));
|
|
$result = array(
|
|
'source' => 'comments',
|
|
'success' => true,
|
|
'items_deleted' => 0,
|
|
'errors' => array()
|
|
);
|
|
|
|
foreach ($comments as $comment) {
|
|
$deleted = wp_delete_comment($comment->comment_ID, true);
|
|
if ($deleted) {
|
|
$result['items_deleted']++;
|
|
} else {
|
|
$result['errors'][] = 'Failed to delete comment ID: ' . $comment->comment_ID;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Delete order data (WooCommerce)
|
|
*/
|
|
private function delete_order_data($email) {
|
|
$result = array(
|
|
'source' => 'orders',
|
|
'success' => true,
|
|
'items_deleted' => 0,
|
|
'errors' => array()
|
|
);
|
|
|
|
if (!class_exists('WooCommerce')) {
|
|
$result['errors'][] = 'WooCommerce not active';
|
|
return $result;
|
|
}
|
|
|
|
// Get orders by email
|
|
$orders = wc_get_orders(array(
|
|
'billing_email' => $email,
|
|
'limit' => -1
|
|
));
|
|
|
|
foreach ($orders as $order) {
|
|
// Check if order can be deleted (accounting requirements)
|
|
if ($this->can_delete_order($order)) {
|
|
$order->delete(true);
|
|
$result['items_deleted']++;
|
|
} else {
|
|
// Anonymize instead of delete
|
|
$this->anonymize_order($order);
|
|
$result['items_deleted']++;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Delete analytics data
|
|
*/
|
|
private function delete_analytics_data($email) {
|
|
// This would integrate with Google Analytics Data Deletion API
|
|
// For now, we'll remove local analytics data
|
|
$result = array(
|
|
'source' => 'analytics',
|
|
'success' => true,
|
|
'items_deleted' => 0,
|
|
'errors' => array()
|
|
);
|
|
|
|
// Remove from local analytics logs
|
|
$logs = get_option('tigerstyle_whiskers_processing_log', array());
|
|
$original_count = count($logs);
|
|
|
|
$logs = array_filter($logs, function($log) use ($email) {
|
|
$email_hash = hash('sha256', $email);
|
|
return !isset($log['email_hash']) || $log['email_hash'] !== $email_hash;
|
|
});
|
|
|
|
update_option('tigerstyle_whiskers_processing_log', $logs);
|
|
|
|
$result['items_deleted'] = $original_count - count($logs);
|
|
|
|
// Note: Real implementation would also request deletion from Google Analytics
|
|
// via their User Deletion API
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Delete marketing data
|
|
*/
|
|
private function delete_marketing_data($email) {
|
|
$result = array(
|
|
'source' => 'marketing',
|
|
'success' => true,
|
|
'items_deleted' => 0,
|
|
'errors' => array()
|
|
);
|
|
|
|
// Remove from newsletter subscriptions
|
|
// This would integrate with MailChimp, ConvertKit, etc.
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Delete log data
|
|
*/
|
|
private function delete_log_data($email) {
|
|
$result = array(
|
|
'source' => 'logs',
|
|
'success' => true,
|
|
'items_deleted' => 0,
|
|
'errors' => array()
|
|
);
|
|
|
|
// Remove from access logs and audit trails
|
|
$email_hash = hash('sha256', $email);
|
|
|
|
// Clean up audit trail
|
|
if (class_exists('TigerStyleWhiskers_AuditTrail')) {
|
|
$deleted = TigerStyleWhiskers_AuditTrail::delete_user_events($email_hash);
|
|
$result['items_deleted'] = $deleted;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Anonymize user content instead of deleting (for dependencies)
|
|
*/
|
|
private function anonymize_user_content($user_id) {
|
|
// Anonymize comments
|
|
global $wpdb;
|
|
|
|
$wpdb->update(
|
|
$wpdb->comments,
|
|
array(
|
|
'comment_author' => 'Anonymous',
|
|
'comment_author_email' => 'deleted@privacy.local',
|
|
'comment_author_url' => '',
|
|
'comment_author_IP' => '0.0.0.0'
|
|
),
|
|
array('user_id' => $user_id),
|
|
array('%s', '%s', '%s', '%s'),
|
|
array('%d')
|
|
);
|
|
|
|
// Anonymize posts if necessary
|
|
$wpdb->update(
|
|
$wpdb->posts,
|
|
array('post_author' => 0),
|
|
array('post_author' => $user_id),
|
|
array('%d'),
|
|
array('%d')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if order can be deleted (accounting requirements)
|
|
*/
|
|
private function can_delete_order($order) {
|
|
// Orders must be kept for 7 years for accounting in most jurisdictions
|
|
$order_date = $order->get_date_created();
|
|
$seven_years_ago = new DateTime('-7 years');
|
|
|
|
return $order_date < $seven_years_ago;
|
|
}
|
|
|
|
/**
|
|
* Anonymize order instead of deleting
|
|
*/
|
|
private function anonymize_order($order) {
|
|
$order->set_billing_first_name('Anonymous');
|
|
$order->set_billing_last_name('Customer');
|
|
$order->set_billing_email('deleted@privacy.local');
|
|
$order->set_billing_phone('');
|
|
$order->set_billing_address_1('Deleted');
|
|
$order->set_billing_address_2('');
|
|
$order->set_billing_city('Privacy');
|
|
$order->set_billing_postcode('00000');
|
|
|
|
$order->set_shipping_first_name('Anonymous');
|
|
$order->set_shipping_last_name('Customer');
|
|
$order->set_shipping_address_1('Deleted');
|
|
$order->set_shipping_address_2('');
|
|
$order->set_shipping_city('Privacy');
|
|
$order->set_shipping_postcode('00000');
|
|
|
|
$order->save();
|
|
}
|
|
|
|
/**
|
|
* Store deletion log
|
|
*/
|
|
private function store_deletion_log($request_id, $deletion_log) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_whiskers_deletion_requests';
|
|
|
|
$wpdb->update(
|
|
$table_name,
|
|
array('deletion_log' => json_encode($deletion_log)),
|
|
array('id' => $request_id),
|
|
array('%s'),
|
|
array('%d')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Update deletion request status
|
|
*/
|
|
private function update_deletion_request_status($request_id, $status, $notes = '') {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_whiskers_deletion_requests';
|
|
|
|
$update_data = array('status' => $status);
|
|
$update_format = array('%s');
|
|
|
|
if (!empty($notes)) {
|
|
$update_data['notes'] = $notes;
|
|
$update_format[] = '%s';
|
|
}
|
|
|
|
switch ($status) {
|
|
case 'processing':
|
|
$update_data['processed_at'] = current_time('mysql');
|
|
$update_format[] = '%s';
|
|
break;
|
|
case 'completed':
|
|
$update_data['completed_at'] = current_time('mysql');
|
|
$update_format[] = '%s';
|
|
break;
|
|
case 'verified':
|
|
$update_data['verified_at'] = current_time('mysql');
|
|
$update_format[] = '%s';
|
|
break;
|
|
}
|
|
|
|
$wpdb->update(
|
|
$table_name,
|
|
$update_data,
|
|
array('id' => $request_id),
|
|
$update_format,
|
|
array('%d')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Send deletion completion email
|
|
*/
|
|
private function send_deletion_completion_email($email, $request_id, $deletion_log) {
|
|
$subject = sprintf(__('[%s] Data Deletion Completed', 'tigerstyle-whiskers'), get_bloginfo('name'));
|
|
|
|
$deleted_sources = array();
|
|
$total_items = 0;
|
|
|
|
foreach ($deletion_log as $source => $result) {
|
|
if ($result['success']) {
|
|
$deleted_sources[] = ucwords(str_replace('_', ' ', $source)) . ' (' . $result['items_deleted'] . ' items)';
|
|
$total_items += $result['items_deleted'];
|
|
}
|
|
}
|
|
|
|
$message = sprintf(__('Hello,
|
|
|
|
Your data deletion request #%s has been completed successfully.
|
|
|
|
Data Sources Processed:
|
|
%s
|
|
|
|
Total Items Deleted: %s
|
|
|
|
Your personal data has been removed from our systems with feline precision. Some anonymized data may remain for legal or security purposes as outlined in our privacy policy.
|
|
|
|
If you have any questions about this deletion, please contact us.
|
|
|
|
Best regards,
|
|
The %s Team
|
|
|
|
---
|
|
TigerStyle Whiskers GDPR Compliance System', 'tigerstyle-whiskers'),
|
|
$request_id,
|
|
implode("\n", $deleted_sources),
|
|
$total_items,
|
|
get_bloginfo('name')
|
|
);
|
|
|
|
wp_mail($email, $subject, $message);
|
|
}
|
|
|
|
/**
|
|
* Get user IP (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
|
|
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
|
$ip_parts = explode('.', $ip);
|
|
$ip_parts[3] = '0';
|
|
$ip = implode('.', $ip_parts);
|
|
}
|
|
|
|
return $ip;
|
|
}
|
|
|
|
/**
|
|
* Register privacy erasers for WordPress privacy tools
|
|
*/
|
|
public function register_privacy_erasers($erasers) {
|
|
$erasers['tigerstyle-whiskers'] = array(
|
|
'eraser_friendly_name' => __('TigerStyle Whiskers Data', 'tigerstyle-whiskers'),
|
|
'callback' => array($this, 'privacy_eraser_callback'),
|
|
);
|
|
|
|
return $erasers;
|
|
}
|
|
|
|
/**
|
|
* Privacy eraser callback
|
|
*/
|
|
public function privacy_eraser_callback($email_address, $page = 1) {
|
|
$response = array(
|
|
'items_removed' => 0,
|
|
'items_retained' => 0,
|
|
'messages' => array(),
|
|
'done' => true,
|
|
);
|
|
|
|
// Erase TigerStyle Whiskers specific data
|
|
$logs = get_option('tigerstyle_whiskers_processing_log', array());
|
|
$email_hash = hash('sha256', $email_address);
|
|
$original_count = count($logs);
|
|
|
|
$logs = array_filter($logs, function($log) use ($email_hash) {
|
|
return !isset($log['email_hash']) || $log['email_hash'] !== $email_hash;
|
|
});
|
|
|
|
update_option('tigerstyle_whiskers_processing_log', $logs);
|
|
|
|
$items_removed = $original_count - count($logs);
|
|
$response['items_removed'] = $items_removed;
|
|
|
|
if ($items_removed > 0) {
|
|
$response['messages'][] = sprintf(
|
|
__('Removed %d TigerStyle Whiskers processing log entries.', 'tigerstyle-whiskers'),
|
|
$items_removed
|
|
);
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Render admin page for deletion management
|
|
*/
|
|
public function render_admin_page() {
|
|
?>
|
|
<div class="tigerstyle-whiskers-admin-section">
|
|
<h2><?php _e('Data Deletion & Right to Erasure', 'tigerstyle-whiskers'); ?></h2>
|
|
<p class="description">
|
|
<?php _e('Manage data deletion requests with surgical precision - like a cat covering its tracks completely.', 'tigerstyle-whiskers'); ?>
|
|
</p>
|
|
|
|
<?php $this->render_deletion_requests_table(); ?>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Render deletion requests table
|
|
*/
|
|
private function render_deletion_requests_table() {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'tigerstyle_whiskers_deletion_requests';
|
|
$requests = $wpdb->get_results("SELECT * FROM $table_name ORDER BY created_at DESC LIMIT 50");
|
|
|
|
?>
|
|
<div class="tw-deletion-requests">
|
|
<h3><?php _e('Recent Deletion Requests', 'tigerstyle-whiskers'); ?></h3>
|
|
|
|
<?php if (!empty($requests)): ?>
|
|
<table class="wp-list-table widefat fixed striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php _e('ID', 'tigerstyle-whiskers'); ?></th>
|
|
<th><?php _e('Email', 'tigerstyle-whiskers'); ?></th>
|
|
<th><?php _e('Type', 'tigerstyle-whiskers'); ?></th>
|
|
<th><?php _e('Status', 'tigerstyle-whiskers'); ?></th>
|
|
<th><?php _e('Created', 'tigerstyle-whiskers'); ?></th>
|
|
<th><?php _e('Actions', 'tigerstyle-whiskers'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($requests as $request): ?>
|
|
<tr>
|
|
<td><?php echo esc_html($request->id); ?></td>
|
|
<td><?php echo esc_html($request->email); ?></td>
|
|
<td><?php echo esc_html(ucwords(str_replace('_', ' ', $request->request_type))); ?></td>
|
|
<td>
|
|
<span class="tw-status-<?php echo esc_attr($request->status); ?>">
|
|
<?php echo esc_html($this->deletion_statuses[$request->status] ?? $request->status); ?>
|
|
</span>
|
|
</td>
|
|
<td><?php echo esc_html(date_i18n(get_option('date_format'), strtotime($request->created_at))); ?></td>
|
|
<td>
|
|
<a href="<?php echo admin_url('admin.php?page=tigerstyle-whiskers&tab=deletion&action=view&request_id=' . $request->id); ?>"
|
|
class="button button-small">
|
|
<?php _e('View', 'tigerstyle-whiskers'); ?>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php else: ?>
|
|
<div class="notice notice-info">
|
|
<p><?php _e('No deletion requests found. The whiskers are keeping a clean house!', 'tigerstyle-whiskers'); ?></p>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|