525 lines
14 KiB
PHP
525 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Class WPConsent_Usage_Tracking - The abstract class for the usage tracking.
|
|
*
|
|
* @package WPConsent
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* The abstract class for the usage tracking.
|
|
*/
|
|
abstract class WPConsent_Usage_Tracking {
|
|
/**
|
|
* Returns the current plugin version type ("lite" or "pro").
|
|
*
|
|
* @return string The version type.
|
|
* @since 1.0.0
|
|
*/
|
|
abstract public function get_type();
|
|
|
|
/**
|
|
* Is the usage tracking enabled?
|
|
*
|
|
* @return bool
|
|
* @since 1.0.0
|
|
*/
|
|
abstract public function is_enabled();
|
|
|
|
/**
|
|
* Usage Tracking endpoint.
|
|
*
|
|
* @since 1.0.0
|
|
*
|
|
* @var string
|
|
*/
|
|
private $url = 'https://usage.wpconsent.com/v1/track';
|
|
|
|
/**
|
|
* Option name to store the timestamp of the last run.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
const LAST_RUN = 'wpconsent_send_usage_last_run';
|
|
|
|
/**
|
|
* Class Constructor.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function __construct() {
|
|
add_action( 'init', array( $this, 'init' ), 2 );
|
|
add_action( 'wpconsent_usage_tracking_cron', array( $this, 'process' ) );
|
|
}
|
|
|
|
/**
|
|
* Initiate the usage tracking cron.
|
|
*
|
|
* @return void
|
|
* @since 1.0.0
|
|
*/
|
|
public function init() {
|
|
if ( ! wp_next_scheduled( 'wpconsent_usage_tracking_cron' ) ) {
|
|
$tracking = array();
|
|
$tracking['day'] = wp_rand( 0, 6 );
|
|
$tracking['hour'] = wp_rand( 0, 23 );
|
|
$tracking['minute'] = wp_rand( 0, 59 );
|
|
$tracking['second'] = wp_rand( 0, 59 );
|
|
$tracking['offset'] = ( $tracking['day'] * DAY_IN_SECONDS ) + ( $tracking['hour'] * HOUR_IN_SECONDS ) + ( $tracking['minute'] * MINUTE_IN_SECONDS ) + $tracking['second'];
|
|
$tracking['initsend'] = strtotime( 'next sunday' ) + $tracking['offset'];
|
|
|
|
wp_schedule_event( $tracking['initsend'], 'weekly', 'wpconsent_usage_tracking_cron' );
|
|
update_option( 'wpconsent_usage_tracking_config', $tracking, false );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes the usage tracking.
|
|
*
|
|
* @return void
|
|
* @since 1.0.0
|
|
*/
|
|
public function process() {
|
|
if ( ! $this->is_enabled() ) {
|
|
return;
|
|
}
|
|
|
|
$last_run = get_option( self::LAST_RUN );
|
|
|
|
// Make sure we do not run it more than once a day.
|
|
if ( false !== $last_run && ( time() - $last_run ) < DAY_IN_SECONDS ) {
|
|
return;
|
|
}
|
|
|
|
wp_remote_post(
|
|
$this->get_url(),
|
|
array(
|
|
'timeout' => 10,
|
|
'headers' => array(
|
|
'Content-Type' => 'application/json; charset=utf-8',
|
|
),
|
|
'user-agent' => $this->get_user_agent(),
|
|
'body' => wp_json_encode( $this->get_data() ),
|
|
)
|
|
);
|
|
|
|
// If we have completed successfully, recheck in 1 week.
|
|
update_option( self::LAST_RUN, time() );
|
|
}
|
|
|
|
/**
|
|
* Gets the URL for the usage tracking api.
|
|
*
|
|
* @return string The URL to use for the api requests.
|
|
* @since 1.0.0
|
|
*/
|
|
private function get_url() {
|
|
if ( defined( 'WPCONSENT_USAGE_TRACKING_URL' ) ) {
|
|
return WPCONSENT_USAGE_TRACKING_URL;
|
|
}
|
|
|
|
return $this->url;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the data to send to the usage tracking api.
|
|
*
|
|
* @return array
|
|
* @since 1.0.0
|
|
*/
|
|
public function get_data() {
|
|
global $wpdb;
|
|
|
|
$theme_data = wp_get_theme();
|
|
$activated = get_option( 'wpconsent_activated', array() );
|
|
$installed_date = isset( $activated['wpconsent'] ) ? $activated['wpconsent'] : null;
|
|
|
|
$data = array(
|
|
// Generic data (environment).
|
|
'url' => home_url(),
|
|
'php_version' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
|
|
'wp_version' => get_bloginfo( 'version' ),
|
|
'mysql_version' => $wpdb->db_version(),
|
|
'server_version' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
|
|
'is_ssl' => is_ssl(),
|
|
'is_multisite' => is_multisite(),
|
|
'sites_count' => function_exists( 'get_blog_count' ) ? (int) get_blog_count() : 1,
|
|
'active_plugins' => $this->get_active_plugins(),
|
|
'theme_name' => $theme_data->name,
|
|
'theme_version' => $theme_data->version,
|
|
'user_count' => function_exists( 'get_user_count' ) ? get_user_count() : null,
|
|
'locale' => get_locale(),
|
|
'timezone_offset' => wp_timezone_string(),
|
|
// WPConsent specific data.
|
|
'wpconsent_version' => WPCONSENT_VERSION,
|
|
'wpconsent_license_key' => null,
|
|
'wpconsent_license_type' => null,
|
|
'wpconsent_is_pro' => false,
|
|
'wpconsent_lite_installed_date' => $installed_date,
|
|
);
|
|
|
|
// Add WPConsent settings and data.
|
|
$data = array_merge( $data, $this->get_wpconsent_data() );
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Return a list of active plugins.
|
|
*
|
|
* @return array An array of active plugin data.
|
|
* @since 1.0.0
|
|
*/
|
|
private function get_active_plugins() {
|
|
if ( ! function_exists( 'get_plugins' ) ) {
|
|
include ABSPATH . '/wp-admin/includes/plugin.php';
|
|
}
|
|
$active = get_option( 'active_plugins', array() );
|
|
$plugins = array_intersect_key( get_plugins(), array_flip( $active ) );
|
|
|
|
return array_map(
|
|
static function ( $plugin ) {
|
|
if ( isset( $plugin['Version'] ) ) {
|
|
return $plugin['Version'];
|
|
}
|
|
|
|
return 'Not Set';
|
|
},
|
|
$plugins
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the User Agent string that will be sent to the API.
|
|
*
|
|
* @return string
|
|
* @since 1.0.0
|
|
*/
|
|
public function get_user_agent() {
|
|
return 'WPConsent/' . WPCONSENT_VERSION . '; ' . get_bloginfo( 'url' );
|
|
}
|
|
|
|
/**
|
|
* Get the WPConsent data - settings, languages, geo rules, DNT, etc.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_wpconsent_data() {
|
|
return array_merge(
|
|
$this->get_wpconsent_stats(),
|
|
$this->get_settings_data(),
|
|
$this->get_geo_rules_data(),
|
|
$this->get_languages_data()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the WPConsent settings data, filtered to remove sensitive/unnecessary data.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function get_settings_data() {
|
|
$settings = wpconsent()->settings->get_options();
|
|
|
|
return array(
|
|
'wpconsent_settings' => $this->filter_settings( $settings ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Filter settings by removing ignored fields and processing special cases.
|
|
*
|
|
* @param array $settings The raw settings array.
|
|
* @return array Filtered settings.
|
|
*/
|
|
protected function filter_settings( $settings ) {
|
|
// Get the list of fields to ignore.
|
|
$ignored_fields = $this->get_ignored_setting_fields();
|
|
|
|
// Start with all settings.
|
|
$filtered = $settings;
|
|
|
|
foreach ( $ignored_fields as $field ) {
|
|
unset( $filtered[ $field ] );
|
|
}
|
|
|
|
$filtered = $this->remove_language_content( $filtered );
|
|
|
|
unset( $filtered['geolocation_groups'] );
|
|
|
|
unset( $filtered['enabled_languages'] );
|
|
|
|
$filtered = $this->sanitize_settings( $filtered );
|
|
|
|
// Allow filtering.
|
|
return apply_filters( 'wpconsent_usage_tracking_filtered_settings', $filtered, $settings );
|
|
}
|
|
|
|
/**
|
|
* Get the list of setting fields to ignore from tracking.
|
|
*
|
|
* @return array Array of setting keys to exclude.
|
|
*/
|
|
protected function get_ignored_setting_fields() {
|
|
$ignored = array(
|
|
// Banner content - we don't want to track user's custom messages.
|
|
'banner_message',
|
|
'accept_button_text',
|
|
'cancel_button_text',
|
|
'preferences_button_text',
|
|
'preferences_panel_title',
|
|
'preferences_panel_description',
|
|
'cookie_policy_title',
|
|
'cookie_policy_text',
|
|
'save_preferences_button_text',
|
|
'close_button_text',
|
|
'content_blocking_placeholder_text',
|
|
|
|
// Banner styling - purely aesthetic, not useful for analytics.
|
|
'banner_background_color',
|
|
'banner_text_color',
|
|
'banner_accept_bg',
|
|
'banner_accept_color',
|
|
'banner_cancel_bg',
|
|
'banner_cancel_color',
|
|
'banner_preferences_bg',
|
|
'banner_preferences_color',
|
|
'button_order',
|
|
|
|
// Banner logo - sensitive/personal.
|
|
'banner_logo',
|
|
|
|
// Cookie table headers - content.
|
|
'cookie_table_header_name',
|
|
'cookie_table_header_description',
|
|
'cookie_table_header_duration',
|
|
'cookie_table_header_service_url',
|
|
|
|
// Internal flags.
|
|
'has_auto_populated_pages',
|
|
|
|
// Do Not Track settings.
|
|
'dnt_submit_text',
|
|
'dnt_field_first_name_label',
|
|
'dnt_field_last_name_label',
|
|
'dnt_field_email_label',
|
|
'dnt_field_address_label',
|
|
'dnt_field_zip_label',
|
|
'dnt_field_city_label',
|
|
'dnt_field_state_label',
|
|
'dnt_field_country_label',
|
|
'dnt_field_phone_label',
|
|
);
|
|
|
|
/**
|
|
* Filter the list of ignored setting fields.
|
|
*
|
|
* @param array $ignored Array of setting keys to exclude from tracking.
|
|
*/
|
|
return apply_filters( 'wpconsent_usage_tracking_ignored_settings', $ignored );
|
|
}
|
|
|
|
/**
|
|
* Remove language-specific content arrays from settings.
|
|
* Language codes can be 2-5 characters (e.g., 'en', 'sq', 'pt_BR', 'zh_CN').
|
|
*
|
|
* @param array $settings The settings array.
|
|
* @return array Settings with language content removed.
|
|
*/
|
|
protected function remove_language_content( $settings ) {
|
|
foreach ( $settings as $key => $value ) {
|
|
// Language codes are typically 2-5 chars, contain letters, underscores, and hyphens.
|
|
// and contain translation keys like 'banner_message', 'accept_button_text', etc.
|
|
if ( $this->is_language_content_key( $key, $value ) ) {
|
|
unset( $settings[ $key ] );
|
|
}
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* Check if a settings key represents language content.
|
|
*
|
|
* @param string $key The setting key.
|
|
* @param mixed $value The setting value.
|
|
* @return bool True if this is language content.
|
|
*/
|
|
protected function is_language_content_key( $key, $value ) {
|
|
// Handle numeric keys - these could be language content arrays.
|
|
if ( is_numeric( $key ) ) {
|
|
return true;
|
|
}
|
|
|
|
// Check if key looks like a language code 2-5 chars, letters/underscores (e.g., 'en', 'sq', 'pt_BR', 'zh_CN').
|
|
if ( preg_match( '/^[a-z]{2}([_-][a-z]{2,3})?$/i', $key ) ) {
|
|
return true;
|
|
}
|
|
|
|
// Check if the value contains typical translation keys.
|
|
return $this->array_contains_translation_keys( $value );
|
|
}
|
|
|
|
/**
|
|
* Check if an array contains translation keys.
|
|
*
|
|
* @param array $data The array to check.
|
|
* @return bool True if the array contains translation keys.
|
|
*/
|
|
protected function array_contains_translation_keys( $data ) {
|
|
if ( ! is_array( $data ) ) {
|
|
return false;
|
|
}
|
|
|
|
// Check if the value contains typical translation keys.
|
|
$translation_keys = array(
|
|
// Banner content.
|
|
'banner_message',
|
|
'accept_button_text',
|
|
'cancel_button_text',
|
|
'preferences_button_text',
|
|
'preferences_panel_title',
|
|
'preferences_panel_description',
|
|
'content_blocking_placeholder_text',
|
|
'cookie_policy_title',
|
|
);
|
|
|
|
foreach ( $translation_keys as $trans_key ) {
|
|
if ( isset( $data[ $trans_key ] ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Sanitize setting values for tracking.
|
|
*
|
|
* @param array $settings The filtered settings.
|
|
* @return array Sanitized settings.
|
|
*/
|
|
protected function sanitize_settings( $settings ) {
|
|
foreach ( $settings as $key => $value ) {
|
|
// Convert page IDs to boolean (just track if configured).
|
|
if ( in_array( $key, array( 'cookie_policy_page', 'dnt_page_id' ), true ) ) {
|
|
$settings[ $key ] = ! empty( $value ) ? 1 : 0;
|
|
continue;
|
|
}
|
|
|
|
// Convert manual_scan_pages array to count (don't track actual page IDs).
|
|
if ( 'manual_scan_pages' === $key && is_array( $value ) ) {
|
|
$settings[ $key ] = count( $value );
|
|
continue;
|
|
}
|
|
|
|
// Handle any other arrays (convert to JSON).
|
|
if ( is_array( $value ) ) {
|
|
$settings[ $key ] = wp_json_encode( $value );
|
|
}
|
|
}
|
|
|
|
return $settings;
|
|
}
|
|
|
|
/**
|
|
* Get languages data for tracking.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function get_languages_data() {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Get geolocation rules data for tracking.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function get_geo_rules_data() {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Track WPConsent-specific data.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_wpconsent_stats() {
|
|
$wpconsent_data = array(
|
|
'wpconsent_total_categories' => 0,
|
|
'wpconsent_total_services' => 0,
|
|
'wpconsent_total_cookies' => 0,
|
|
);
|
|
|
|
// Get categories and count them along with their services and cookies.
|
|
$categories = wpconsent()->cookies->get_categories();
|
|
$total_categories = 0;
|
|
$total_services = 0;
|
|
$total_cookies = 0;
|
|
|
|
foreach ( $categories as $category_slug => $category ) {
|
|
$category_id = $category['id'];
|
|
|
|
// Count this category (only if it has a valid ID).
|
|
if ( $category_id > 0 ) {
|
|
++$total_categories;
|
|
}
|
|
|
|
// Get cookies for this category.
|
|
$cookies = wpconsent()->cookies->get_cookies_by_category( $category_id );
|
|
|
|
// Skip if get_cookies_by_category returned an error.
|
|
if ( ! is_array( $cookies ) ) {
|
|
continue;
|
|
}
|
|
|
|
// Get services for this category.
|
|
$services = wpconsent()->cookies->get_services_by_category( $category_id );
|
|
|
|
// Skip if get_services_by_category returned an error.
|
|
if ( ! is_array( $services ) ) {
|
|
$services = array();
|
|
}
|
|
|
|
// Count cookies directly attached to this category.
|
|
foreach ( $cookies as $cookie ) {
|
|
// Ensure categories is an array before using it.
|
|
if ( ! isset( $cookie['categories'] ) || ! is_array( $cookie['categories'] ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( count( $cookie['categories'] ) === 1 && $cookie['categories'][0] === $category_id ) {
|
|
++$total_cookies;
|
|
}
|
|
}
|
|
|
|
// Count services and their cookies.
|
|
foreach ( $services as $service ) {
|
|
++$total_services;
|
|
|
|
// Count cookies under this service.
|
|
foreach ( $cookies as $cookie ) {
|
|
// Ensure categories is an array before using it.
|
|
if ( ! isset( $cookie['categories'] ) || ! is_array( $cookie['categories'] ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( in_array( $service['id'], $cookie['categories'], true ) ) {
|
|
++$total_cookies;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$wpconsent_data['wpconsent_total_categories'] = $total_categories;
|
|
$wpconsent_data['wpconsent_total_services'] = $total_services;
|
|
$wpconsent_data['wpconsent_total_cookies'] = $total_cookies;
|
|
|
|
return $wpconsent_data;
|
|
}
|
|
}
|