ABMsense to configure the plugin.
Version: 4.0.14
Author: Ashley Smith
Requires PHP: 7.2
Author URI: https://www.abmsense.com
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
add_action('init', function() {
if (!headers_sent() && !session_id()) {
session_start();
}
}, 1);
// Define plugin prefix
define('ABMSENSE_PREFIX', 'abmsense_');
if (!defined('ABMSENSE_VERSION')) {
define('ABMSENSE_VERSION', '4.0.14');
}
// PHP version check
if (version_compare(PHP_VERSION, '7.2', '<')) {
deactivate_plugins(plugin_basename(__FILE__));
add_action('admin_notices', function() {
?>
admin_url('admin-ajax.php'),
'security' => wp_create_nonce(ABMSENSE_PREFIX . 'ajax_nonce')
));
wp_register_script(ABMSENSE_PREFIX . 'consent', plugin_dir_url(__FILE__) . 'js/abmsense_consent.js', array(), '1.0', true);
}
}
// Function to run the plugin
function abmsense_run() {
ob_start();
$exclude_keywords = array('/wp-admin/', '/yoast/', '/wp-json/yoast/v1/wincher/', '/google-site-kit/', '/plugins/', '/admin-ajax.php', '/wp-login.php', '/wp-cron.php', '/xmlrpc.php', '/wp-content/plugins/abmsense/');
$script_name = isset($_SERVER['SCRIPT_NAME']) ? sanitize_text_field(wp_unslash($_SERVER['SCRIPT_NAME'])) : '';
foreach ($exclude_keywords as $keyword) {
if (strpos($script_name, $keyword) !== false) {
ob_end_clean();
return;
}
}
$our_customer = isset($_SERVER['SERVER_NAME']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_NAME'])) : '';
$is_consent_enabled = abmsense_check_consent_enabled($our_customer);
$is_consent_given = isset($_SESSION[ABMSENSE_PREFIX . 'consent']) && $_SESSION[ABMSENSE_PREFIX . 'consent'] === true;
if ($is_consent_given || !$is_consent_enabled) {
require_once plugin_dir_path(__FILE__) . 'visitor_tracking.php';
}
ob_end_flush();
}
// Function to enqueue consent script if consent is enabled
function abmsense_maybe_enqueue_consent_script() {
$our_customer = isset($_SERVER['SERVER_NAME']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_NAME'])) : '';
$is_consent_given = isset($_SESSION[ABMSENSE_PREFIX . 'consent']) && $_SESSION[ABMSENSE_PREFIX . 'consent'] === true;
if ($is_consent_given) {
abmsense_log("Consent already given for {$our_customer}", 'info');
return;
}
$is_consent_enabled = abmsense_check_consent_enabled($our_customer);
if ($is_consent_enabled) {
wp_enqueue_script(
ABMSENSE_PREFIX . 'consent',
plugin_dir_url(__FILE__) . 'js/abmsense_consent.js',
array(), // dependencies
'1.0.0', // version number
true // in footer
);
}
}
// Function to add defer attribute to the abmsense_consent script
function abmsense_add_defer_attribute($tag, $handle) {
if (ABMSENSE_PREFIX . 'consent' === $handle) {
return str_replace(' src', ' defer="defer" src', $tag);
}
return $tag;
}
add_filter('script_loader_tag', 'abmsense_add_defer_attribute', 10, 2);
// Function to check consent status (1-day cache)
function abmsense_check_consent_enabled($customer_name) {
$cache_key = 'abmsense_consent_' . md5($customer_name);
$cache_duration = 86400; // Cache for 1 day
// Check cache first
$cached_value = get_transient($cache_key);
if ($cached_value !== false) {
return $cached_value === '1';
}
// Get API configuration
$api_config = include('db_config.php');
$api_url = $api_config['check_consent_enabled'];
$public_key = $api_config['public_key'];
// Prepare and encrypt data
$api_data = array('customer_name' => $customer_name);
$encrypted_data = abmsense_encrypt_data($api_data, $public_key);
if (!$encrypted_data) {
abmsense_log("Encryption failed in abmsense_check_consent_enabled", 'error');
return false;
}
$json_data = wp_json_encode($encrypted_data);
if (false === $json_data) {
abmsense_log("Failed to encode data to JSON in consent check", 'error');
return false;
}
// Use our new SSL-safe request system
$args = abmsense_get_api_request_args($json_data);
$response = abmsense_safe_api_request($api_url, $args);
if (is_wp_error($response)) {
abmsense_log('API request to check consent failed: ' . $response->get_error_message(), 'error');
// If API call fails, use cached value if available, otherwise default to false
$last_known_value = get_option('abmsense_last_known_consent_' . md5($customer_name), null);
if ($last_known_value !== null) {
abmsense_log('Using last known consent value due to API failure', 'warning');
return $last_known_value === '1';
}
return false;
}
$response_body = wp_remote_retrieve_body($response);
$result = json_decode($response_body, true);
if (!isset($result['success']) || $result['success'] !== true) {
$error_message = isset($result['error']) ? $result['error'] : 'Unknown error';
abmsense_log('API response indicates failure: ' . $error_message, 'error');
return false;
}
$is_consent_enabled = isset($result['is_consent_enabled']) ? $result['is_consent_enabled'] : false;
// Cache the result
set_transient($cache_key, $is_consent_enabled ? '1' : '0', $cache_duration);
// Store as last known good value
update_option('abmsense_last_known_consent_' . md5($customer_name), $is_consent_enabled ? '1' : '0');
return $is_consent_enabled;
}
// Function to get visitor information using IP-API
function abmsense_get_visitor_info($ip) {
$url = 'http://ip-api.com/csv/' . urlencode($ip) . '?fields=city,country,org';
$response = wp_remote_get($url);
if (is_wp_error($response)) {
return false;
}
$body = wp_remote_retrieve_body($response);
if (empty($body)) {
return false;
}
$data = str_getcsv($body);
return array(
'city' => isset($data[1]) ? sanitize_text_field($data[1]) : '',
'country' => isset($data[0]) ? sanitize_text_field($data[0]) : '',
'company' => isset($data[2]) ? sanitize_text_field($data[2]) : ''
);
}
// Function to get real IP address from headers
function abmsense_get_real_ip_from_headers() {
$headers = [
'HTTP_X_FORWARDED_FOR',
'HTTP_CLIENT_IP',
'HTTP_X_CLIENT_IP',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'HTTP_CF_CONNECTING_IP',
'HTTP_X_REAL_IP',
'HTTP_X_FORWARDED',
'HTTP_X_FORWARDED_HOST',
'HTTP_X_FORWARDED_SERVER',
'HTTP_TRUE_CLIENT_IP',
'HTTP_X_ANONYMOUS_IP',
'HTTP_X_ORIGINAL_FOR',
'HTTP_FASTLY_CLIENT_IP',
'HTTP_X_AZURE_CLIENTIP'
];
foreach ($headers as $header) {
if (isset($_SERVER[$header])) {
$ip = sanitize_text_field(wp_unslash($_SERVER[$header]));
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
if ($header === 'HTTP_X_FORWARDED_FOR') {
$ipArray = explode(',', $ip);
$ip = trim($ipArray[0]);
}
return $ip;
}
}
}
return '';
}
// Function to save temporary data using mysqli
function abmsense_temp_save() {
global $wpdb;
check_ajax_referer(ABMSENSE_PREFIX . 'ajax_nonce', 'security');
if (!empty($_POST['hits'])) {
$hits = json_decode(sanitize_text_field(wp_unslash($_POST['hits'])), true);
$account_id = !empty($_POST['account_id']) ? sanitize_text_field(wp_unslash($_POST['account_id'])) : '';
$visitor_ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : 'UNKNOWN';
$header_ip = abmsense_get_real_ip_from_headers();
// Cache visitor info
$cache_key = ABMSENSE_PREFIX . 'visitor_info_' . md5($visitor_ip);
$visitor_info = wp_cache_get($cache_key);
if (false === $visitor_info) {
$visitor_info = abmsense_get_visitor_info($visitor_ip) ?: array('city' => '', 'country' => '', 'company' => '');
wp_cache_set($cache_key, $visitor_info, '', 3600); // Cache for 1 hour
}
$table_name = "{$wpdb->prefix}" . ABMSENSE_PREFIX . 'temp_data';
$today_date = current_time('Y-m-d');
foreach ($hits as $hit) {
$last_update = DateTime::createFromFormat('m-d-Y', sanitize_text_field($hit['last_update']));
$last_update_formatted = $last_update ? $last_update->format('Y-m-d') : '';
if ($last_update_formatted !== $today_date) {
continue;
}
// Generate a unique cache key
$cache_key = ABMSENSE_PREFIX . 'existing_record_' . md5($hit['page_title'] . $visitor_ip . $today_date);
// Try to get the existing record from cache
$existing_record = wp_cache_get($cache_key, ABMSENSE_PREFIX . 'records');
if (false === $existing_record) {
// If not in cache, query the database
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$existing_record = $wpdb->get_row($wpdb->prepare(
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
"SELECT * FROM `" . $wpdb->prefix . ABMSENSE_PREFIX . "temp_data` WHERE page_title = %s AND visitor_ip = %s AND last_update = %s",
$hit['page_title'], $visitor_ip, $today_date
));
// Cache the result for future use (cache for 1 hour)
wp_cache_set($cache_key, $existing_record, ABMSENSE_PREFIX, HOUR_IN_SECONDS);
}
if ($existing_record && !empty($account_id) && $existing_record->account_id == $account_id) {
// Update existing record
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->update(
$table_name,
array(
'time_spent' => $hit['time_spent'],
'page_view' => $hit['page_view'],
'last_update' => $last_update_formatted
),
array('id' => $existing_record->id),
array('%d', '%d', '%s'),
array('%d')
);
// Delete the cache after update
wp_cache_delete(ABMSENSE_PREFIX . 'local_data_' . md5($wpdb->prefix . ABMSENSE_PREFIX . 'temp_data'), ABMSENSE_PREFIX . 'local_data');
} elseif (!$existing_record) {
// Insert new record
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert(
$table_name,
array(
'page_title' => $hit['page_title'],
'time_spent' => $hit['time_spent'],
'page_view' => $hit['page_view'],
'visitor_ip' => $visitor_ip,
'header_ip' => $header_ip,
'visitor_city' => $visitor_info['city'],
'visitor_country' => $visitor_info['country'],
'visitor_company' => $visitor_info['company'],
'account_id' => $account_id,
'last_update' => $last_update_formatted
),
array('%s', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s')
);
// Delete the cache after insert
wp_cache_delete(ABMSENSE_PREFIX . 'local_data_' . md5($wpdb->prefix . ABMSENSE_PREFIX . 'temp_data'), ABMSENSE_PREFIX . 'local_data');
}
if ($wpdb->last_error) {
abmsense_log('Database operation failed: ' . $wpdb->last_error, 'error');
}
}
wp_send_json_success();
} else {
wp_send_json_error('No hits data received');
}
}
// Function to transfer data from temp table to main table
function abmsense_transfer_data_to_main_db() {
global $wpdb;
$local_table_name = $wpdb->prefix . ABMSENSE_PREFIX . 'temp_data';
$customer_name = isset($_SERVER['SERVER_NAME']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_NAME'])) : '';
$api_config = include('db_config.php');
$api_upsert_url = $api_config['upsert_abmsense_main_data'];
$public_key = $api_config['public_key'];
// Generate a unique cache key
$cache_key = 'abmsense_local_data_' . md5($local_table_name);
// Try to get the data from cache
$local_data = wp_cache_get($cache_key);
if (false === $local_data) {
// If not in cache, query the database
$sql = $wpdb->prepare("SELECT * FROM %i", $local_table_name);
$local_data = $wpdb->get_results($sql, ARRAY_A);
// Cache the result
wp_cache_set($cache_key, $local_data, '', HOUR_IN_SECONDS);
}
$total_records = count($local_data);
if ($total_records === 0) {
return;
}
$successful_transfers = 0;
$failed_transfers = 0;
foreach ($local_data as $index => $record) {
$data = abmsense_prepare_data_for_transfer($record, $customer_name);
$encrypted_data = abmsense_encrypt_data($data, $public_key);
if (!$encrypted_data) {
abmsense_log("Encryption failed for record " . ($index + 1), 'error');
$failed_transfers++;
continue;
}
$json_body = wp_json_encode($encrypted_data);
if (false === $json_body) {
abmsense_log("JSON encoding failed for API request", 'error');
$failed_transfers++;
continue;
}
// Use our SSL-safe request system
$args = abmsense_get_api_request_args($json_body);
$response = abmsense_safe_api_request($api_upsert_url, $args);
if (is_wp_error($response)) {
abmsense_log('API request failed for record ' . ($index + 1) . ': ' . $response->get_error_message(), 'error');
$failed_transfers++;
continue;
}
$response_code = wp_remote_retrieve_response_code($response);
$response_body = wp_remote_retrieve_body($response);
$result = json_decode($response_body, true);
if ($response_code === 200 && isset($result['success']) && $result['success'] === true) {
$successful_transfers++;
} else {
$failed_transfers++;
$error_message = isset($result['error']) ? $result['error'] : 'Unknown error';
$response_status = $response_code ?? 'unknown status';
abmsense_log(sprintf(
"Failed to transfer record %d. Status: %s, Error: %s, Response: %s",
$index + 1,
$response_status,
$error_message,
wp_json_encode($result)
), 'error');
}
}
// Handle cleanup based on transfer results
if ($successful_transfers === $total_records) {
// All transfers successful, truncate the table
$table_name = $wpdb->prefix . 'abmsense_temp_data';
if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name))) {
$wpdb->query("TRUNCATE TABLE {$wpdb->prefix}abmsense_temp_data");
}
} else {
abmsense_log(sprintf(
"Transfer completed with %d successes and %d failures out of %d total records.",
$successful_transfers,
$failed_transfers,
$total_records
), 'warning');
}
// Clear the cache after transfer attempt
wp_cache_delete($cache_key);
}
// Function to prepare data for transfer
function abmsense_prepare_data_for_transfer($record, $customer_name) {
return array(
'customer_name' => $customer_name,
'page_title' => mb_convert_encoding(html_entity_decode($record['page_title'], ENT_QUOTES, 'UTF-8'), 'UTF-8', 'UTF-8'),
'time_spent' => intval($record['time_spent']),
'page_view' => intval($record['page_view']),
'visitor_ip' => $record['visitor_ip'],
'visitor_city' => mb_convert_encoding($record['visitor_city'], 'UTF-8', 'UTF-8'),
'visitor_country' => mb_convert_encoding($record['visitor_country'], 'UTF-8', 'UTF-8'),
'visitor_company' => mb_convert_encoding($record['visitor_company'], 'UTF-8', 'UTF-8'),
'account_id' => $record['account_id'],
'last_update' => current_time('Y-m-d'),
'header_ip' => $record['header_ip'] ?: null,
);
}
// Function to add custom intervals for cron jobs
function abmsense_add_custom_intervals($schedules) {
$schedules['one_hour'] = array(
'interval' => 3600, // 1 hour in seconds
'display' => esc_html__('Every 1 Hour', 'abmsense')
);
return $schedules;
}
// Function to set consent in session
function abmsense_set_consent() {
$_SESSION['abmsense_consent'] = true;
wp_send_json_success('Consent recorded');
}
add_action('wp_enqueue_scripts', 'abmsense_enqueue_scripts');
add_action('init', 'abmsense_run');
add_action('wp_enqueue_scripts', 'abmsense_maybe_enqueue_consent_script', 999);
add_action('wp_ajax_abmsense_temp_save', 'abmsense_temp_save');
add_action('wp_ajax_nopriv_abmsense_temp_save', 'abmsense_temp_save');
add_action('abmsense_one_hour_data_transfer', 'abmsense_transfer_data_to_main_db');
add_filter('cron_schedules', 'abmsense_add_custom_intervals');
add_action('wp_ajax_abmsense_set_consent', 'abmsense_set_consent');
add_action('wp_ajax_nopriv_abmsense_set_consent', 'abmsense_set_consent');
if (!wp_next_scheduled('abmsense_one_hour_data_transfer')) {
wp_schedule_event(time(), 'one_hour', 'abmsense_one_hour_data_transfer');
}
register_activation_hook(__FILE__, 'abmsense_activate');