420 lines
15 KiB
PHP
420 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Booster for WooCommerce - Module - User Tracking
|
|
*
|
|
* @version 3.6.0
|
|
* @since 3.1.3
|
|
* @author Algoritmika Ltd.
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
|
|
|
if ( ! class_exists( 'WCJ_User_Tracking' ) ) :
|
|
|
|
class WCJ_User_Tracking extends WCJ_Module {
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @version 3.6.0
|
|
* @since 3.1.3
|
|
* @todo (maybe) if `wcj_track_users_enabled` set to `yes`, check if "General" module is also enabled (when upgrading from version 3.1.2)
|
|
*/
|
|
function __construct() {
|
|
|
|
$this->id = 'track_users';
|
|
$this->short_desc = __( 'User Tracking', 'woocommerce-jetpack' );
|
|
$this->desc = __( 'Track your users in WooCommerce.', 'woocommerce-jetpack' );
|
|
$this->link_slug = 'woocommerce-user-tracking';
|
|
parent::__construct();
|
|
|
|
// By country scopes
|
|
$this->track_users_scopes = array(
|
|
'1' => __( 'Last 24 hours', 'woocommerce-jetpack' ),
|
|
'7' => __( 'Last 7 days', 'woocommerce-jetpack' ),
|
|
'28' => __( 'Last 28 days', 'woocommerce-jetpack' ),
|
|
'all_time' => __( 'All time', 'woocommerce-jetpack' ),
|
|
);
|
|
|
|
if ( $this->is_enabled() ) {
|
|
// User tracking
|
|
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_track_users_script' ) );
|
|
add_action( 'wp_ajax_' . 'wcj_track_users', array( $this, 'track_users' ) );
|
|
add_action( 'wp_ajax_nopriv_' . 'wcj_track_users', array( $this, 'track_users' ) );
|
|
// Stats in dashboard widgets
|
|
if ( 'yes' === get_option( 'wcj_track_users_by_country_widget_enabled', 'yes' ) ) {
|
|
add_action( 'wp_dashboard_setup', array( $this, 'add_track_users_dashboard_widgets' ) );
|
|
add_action( 'admin_init', array( $this, 'maybe_delete_track_users_stats' ) );
|
|
add_action( 'admin_init', array( $this, 'track_users_update_county_stats' ) );
|
|
}
|
|
// Order tracking
|
|
if ( 'yes' === apply_filters( 'booster_option', 'no', get_option( 'wcj_track_users_save_order_http_referer_enabled', 'no' ) ) ) {
|
|
add_action( 'woocommerce_new_order', array( $this, 'add_http_referer_to_order' ) );
|
|
add_action( 'add_meta_boxes', array( $this, 'add_http_referer_order_meta_box' ) );
|
|
}
|
|
// Cron
|
|
add_action( 'init', array( $this, 'track_users_schedule_the_event' ) );
|
|
add_action( 'admin_init', array( $this, 'track_users_schedule_the_event' ) );
|
|
add_action( 'wcj_track_users_generate_stats', array( $this, 'track_users_generate_stats_cron' ) );
|
|
// Orders columns
|
|
if (
|
|
'yes' === get_option( 'wcj_track_users_shop_order_columns_referer', 'no' ) ||
|
|
'yes' === get_option( 'wcj_track_users_shop_order_columns_referer_type', 'no' )
|
|
) {
|
|
add_filter( 'manage_edit-shop_order_columns', array( $this, 'add_order_columns' ), PHP_INT_MAX - 2 );
|
|
add_action( 'manage_shop_order_posts_custom_column', array( $this, 'render_order_columns' ), PHP_INT_MAX );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* add_order_columns.
|
|
*
|
|
* @version 3.6.0
|
|
* @since 3.6.0
|
|
*/
|
|
function add_order_columns( $columns ) {
|
|
if ( 'yes' === get_option( 'wcj_track_users_shop_order_columns_referer', 'no' ) ) {
|
|
$columns['wcj_track_users_referer'] = __( 'Referer', 'woocommerce-jetpack' );
|
|
}
|
|
if ( 'yes' === get_option( 'wcj_track_users_shop_order_columns_referer_type', 'no' ) ) {
|
|
$columns['wcj_track_users_referer_type'] = __( 'Referer Type', 'woocommerce-jetpack' );
|
|
}
|
|
return $columns;
|
|
}
|
|
|
|
/**
|
|
* render_order_columns.
|
|
*
|
|
* @param string $column
|
|
* @version 3.6.0
|
|
* @since 3.6.0
|
|
*/
|
|
function render_order_columns( $column ) {
|
|
if ( 'wcj_track_users_referer' === $column || 'wcj_track_users_referer_type' === $column ) {
|
|
$order_id = get_the_ID();
|
|
$referer = get_post_meta( $order_id, '_wcj_track_users_http_referer', true );
|
|
switch ( $column ) {
|
|
case 'wcj_track_users_referer':
|
|
echo $referer;
|
|
break;
|
|
case 'wcj_track_users_referer_type':
|
|
echo $this->get_referer_type( $referer );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* track_users_update_county_stats.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
* @todo (maybe) `wp_nonce`
|
|
*/
|
|
function track_users_update_county_stats() {
|
|
if ( isset( $_GET['wcj_track_users_update_county_stats'] ) ) {
|
|
$this->track_users_generate_stats_cron();
|
|
wp_safe_redirect( remove_query_arg( 'wcj_track_users_update_county_stats' ) );
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* track_users_schedule_the_event.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
* @todo (maybe) customizable interval
|
|
* @todo (maybe) separate events for all time, last 28 days, last 7 days, last 24 hours
|
|
*/
|
|
function track_users_schedule_the_event() {
|
|
$event_timestamp = wp_next_scheduled( 'wcj_track_users_generate_stats', array( 'hourly' ) );
|
|
update_option( 'wcj_track_users_cron_time_schedule', $event_timestamp );
|
|
if ( ! $event_timestamp ) {
|
|
wp_schedule_event( time(), 'hourly', 'wcj_track_users_generate_stats', array( 'hourly' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* track_users_generate_stats_cron.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
*/
|
|
function track_users_generate_stats_cron( $interval ) {
|
|
update_option( 'wcj_track_users_cron_time_last_run', time() );
|
|
$stats = get_option( 'wcj_track_users_stats_by_country', array() );
|
|
foreach ( $this->track_users_scopes as $scope => $scope_title ) {
|
|
$stats[ $scope ] = $this->generate_track_users_stats_by_country( $scope );
|
|
}
|
|
update_option( 'wcj_track_users_stats_by_country', $stats );
|
|
}
|
|
|
|
/**
|
|
* add_http_referer_order_meta_box.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
*/
|
|
function add_http_referer_order_meta_box() {
|
|
add_meta_box(
|
|
'wc-jetpack-' . $this->id,
|
|
__( 'Booster', 'woocommerce-jetpack' ) . ': ' . __( 'Acquisition Source', 'woocommerce-jetpack' ),
|
|
array( $this, 'create_http_referer_order_meta_box' ),
|
|
'shop_order',
|
|
'side',
|
|
'low'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* get_referer_type.
|
|
*
|
|
* @version 3.6.0
|
|
* @since 2.9.1
|
|
* @todo group hosts by type (i.e. "Search Engines", "Social" etc.)
|
|
*/
|
|
function get_referer_type( $http_referer ) {
|
|
if ( '' != $http_referer && 'N/A' != $http_referer ) {
|
|
if ( ( $http_referer_info = parse_url( $http_referer ) ) && isset( $http_referer_info['host'] ) ) {
|
|
return $http_referer_info['host'];
|
|
}
|
|
}
|
|
return 'N/A';
|
|
}
|
|
|
|
/**
|
|
* create_http_referer_order_meta_box.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
*/
|
|
function create_http_referer_order_meta_box() {
|
|
if ( '' == ( $http_referer = get_post_meta( get_the_ID(), '_wcj_track_users_http_referer', true ) ) ) {
|
|
$http_referer = 'N/A';
|
|
}
|
|
echo '<p>' . __( 'URL:', 'woocommerce-jetpack' ) . ' ' . $http_referer . '</p>';
|
|
echo '<p>' . __( 'Type:', 'woocommerce-jetpack' ) . ' ' . $this->get_referer_type( $http_referer ) . '</p>';
|
|
}
|
|
|
|
/**
|
|
* add_http_referer_to_order.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
* @todo add "all orders by referer type" stats
|
|
*/
|
|
function add_http_referer_to_order( $order_id ) {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'wcj_track_users';
|
|
$http_referer = 'N/A';
|
|
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) === $table_name ) {
|
|
$user_ip = ( class_exists( 'WC_Geolocation' ) ? WC_Geolocation::get_ip_address() : wcj_get_the_ip() );
|
|
$result = $wpdb->get_row( "SELECT * FROM $table_name WHERE ip = '$user_ip' ORDER BY time DESC" );
|
|
if ( $result ) {
|
|
$http_referer = $result->referer;
|
|
}
|
|
}
|
|
update_post_meta( $order_id, '_wcj_track_users_http_referer', $http_referer );
|
|
}
|
|
|
|
/**
|
|
* maybe_delete_track_users_stats.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
* @todo (maybe) wp_nonce
|
|
*/
|
|
function maybe_delete_track_users_stats() {
|
|
if ( isset( $_GET['wcj_delete_track_users_stats'] ) /* && is_super_admin() */ ) {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'wcj_track_users';
|
|
$sql = "DROP TABLE IF EXISTS $table_name";
|
|
$wpdb->query( $sql );
|
|
delete_option( 'wcj_track_users_stats_by_country' );
|
|
delete_option( 'wcj_track_users_cron_time_last_run' );
|
|
wp_safe_redirect( remove_query_arg( 'wcj_delete_track_users_stats' ) );
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a widgets to the dashboard.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
*/
|
|
function add_track_users_dashboard_widgets() {
|
|
wp_add_dashboard_widget(
|
|
'wcj_track_users_dashboard_widget',
|
|
__( 'Booster', 'woocommerce-jetpack' ) . ': ' . sprintf( __( 'Top %d countries by visits', 'woocommerce-jetpack' ),
|
|
get_option( 'wcj_track_users_by_country_widget_top_count', 10 ) ),
|
|
array( $this, 'track_users_by_country_dashboard_widget' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* get_saved_track_users_stats_by_country.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
*/
|
|
function get_saved_track_users_stats_by_country( $scope ) {
|
|
$stats = get_option( 'wcj_track_users_stats_by_country', array() );
|
|
return ( isset( $stats[ $scope ] ) ? $stats[ $scope ] : array() );
|
|
}
|
|
|
|
/**
|
|
* generate_track_users_stats_by_country.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
*/
|
|
function generate_track_users_stats_by_country( $scope ) {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'wcj_track_users';
|
|
switch ( $scope ) {
|
|
case 'all_time':
|
|
$select_query = "SELECT * FROM $table_name";
|
|
break;
|
|
default: // '28', '7', '1'
|
|
$time_expired = date( 'Y-m-d H:i:s', ( current_time( 'timestamp' ) - $scope * 24 * 60 * 60 ) );
|
|
$select_query = "SELECT * FROM $table_name WHERE time > '" . $time_expired . "'";
|
|
break;
|
|
}
|
|
$totals = array();
|
|
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) === $table_name && ( $results = $wpdb->get_results( $select_query ) ) ) {
|
|
foreach ( $results as $result ) {
|
|
if ( ! isset( $totals[ $result->country ] ) ) {
|
|
$totals[ $result->country ] = 1;
|
|
} else {
|
|
$totals[ $result->country ]++;
|
|
}
|
|
}
|
|
arsort( $totals );
|
|
}
|
|
return $totals;
|
|
}
|
|
|
|
/**
|
|
* track_users_by_country_dashboard_widget.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
* @todo (maybe) display all info (IP, referer etc.) on country click
|
|
* @todo (maybe) display stats by day and/or month
|
|
* @todo (maybe) display stats by state
|
|
*/
|
|
function track_users_by_country_dashboard_widget( $post, $args ) {
|
|
$top_count = get_option( 'wcj_track_users_by_country_widget_top_count', 10 );
|
|
foreach ( $this->track_users_scopes as $scope => $scope_title ) {
|
|
if ( ! in_array( $scope, get_option( 'wcj_track_users_by_country_widget_scopes', array( '1', '28' ) ) ) ) {
|
|
continue;
|
|
}
|
|
$totals = $this->get_saved_track_users_stats_by_country( $scope );
|
|
if ( ! empty( $totals ) ) {
|
|
$totals = array_slice( $totals, 0, $top_count );
|
|
$table_data = array();
|
|
$table_data[] = array( '', __( 'Country', 'woocommerce-jetpack' ), __( 'Visits', 'woocommerce-jetpack' ) );
|
|
$i = 0;
|
|
foreach ( $totals as $country_code => $visits ) {
|
|
$i++;
|
|
$country_info = ( '' != $country_code ? wcj_get_country_flag_by_code( $country_code ) . ' ' . wcj_get_country_name_by_code( $country_code ) : 'N/A' );
|
|
$table_data[] = array( $i, $country_info, $visits );
|
|
}
|
|
echo '<strong>' . $scope_title . '</strong>';
|
|
echo wcj_get_table_html( $table_data, array( 'table_class' => 'widefat striped', 'table_heading_type' => 'horizontal' ) );
|
|
} else {
|
|
echo '<p>' . '<em>' . __( 'No stats yet.', 'woocommerce-jetpack' ) . '</em>' . '</p>';
|
|
}
|
|
}
|
|
echo '<p>' .
|
|
'<a class="button-primary" href="' . add_query_arg( 'wcj_delete_track_users_stats', '1' ) . '" ' .
|
|
'onclick="return confirm(\'' . __( 'Are you sure?', 'woocommerce-jetpack' ) . '\')"' .
|
|
'>' . __( 'Delete all tracking data', 'woocommerce-jetpack' ) . '</a>' .
|
|
'</p>';
|
|
$cron_last_run = ( '' != ( $_time = get_option( 'wcj_track_users_cron_time_last_run', '' ) ) ? date( 'Y-m-d H:i:s', $_time ) : '-' );
|
|
$cron_next_schedule = ( '' != ( $_time = get_option( 'wcj_track_users_cron_time_schedule', '' ) ) ? date( 'Y-m-d H:i:s', $_time ) : '-' );
|
|
echo '<p>' .
|
|
sprintf( __( 'Stats generated at %s. Next update is scheduled at %s.', 'woocommerce-jetpack' ), $cron_last_run, $cron_next_schedule ) . ' ' .
|
|
'<a href="' . add_query_arg( 'wcj_track_users_update_county_stats', '1' ) . '">' . __( 'Update now', 'woocommerce-jetpack' ) . '</a>.' .
|
|
'</p>';
|
|
}
|
|
|
|
/**
|
|
* enqueue_track_users_script.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
*/
|
|
function enqueue_track_users_script() {
|
|
wp_enqueue_script( 'wcj-track-users', trailingslashit( wcj_plugin_url() ) . 'includes/js/wcj-track-users.js', array( 'jquery' ), WCJ()->version, true );
|
|
wp_localize_script( 'wcj-track-users', 'track_users_ajax_object', array(
|
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
|
'http_referer' => ( isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : 'N/A' ),
|
|
'user_ip' => ( class_exists( 'WC_Geolocation' ) ? WC_Geolocation::get_ip_address() : wcj_get_the_ip() ),
|
|
) );
|
|
}
|
|
|
|
/**
|
|
* track_users.
|
|
*
|
|
* @version 2.9.1
|
|
* @since 2.9.1
|
|
* @todo (maybe) customizable `$time_expired`
|
|
* @todo (maybe) optionally do not track selected user roles (e.g. admin)
|
|
*/
|
|
function track_users() {
|
|
if ( ! isset( $_POST['wcj_user_ip'] ) ) {
|
|
die();
|
|
}
|
|
$user_ip = $_POST['wcj_user_ip'];
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'wcj_track_users';
|
|
if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
|
|
// Create DB table
|
|
$charset_collate = $wpdb->get_charset_collate();
|
|
$sql = "CREATE TABLE $table_name (
|
|
id int NOT NULL AUTO_INCREMENT,
|
|
time datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
|
|
country tinytext NOT NULL,
|
|
state tinytext NOT NULL,
|
|
ip text NOT NULL,
|
|
referer text NOT NULL,
|
|
PRIMARY KEY (id)
|
|
) $charset_collate;";
|
|
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
|
dbDelta( $sql );
|
|
} else {
|
|
// Check if already tracked recently
|
|
$time_expired = date( 'Y-m-d H:i:s', strtotime( '-1 day', current_time( 'timestamp' ) ) );
|
|
$result = $wpdb->get_row( "SELECT * FROM $table_name WHERE ip = '$user_ip' AND time > '$time_expired'" );
|
|
if ( $result ) {
|
|
return;
|
|
}
|
|
}
|
|
// Country by IP
|
|
$location = ( class_exists( 'WC_Geolocation' ) ? WC_Geolocation::geolocate_ip( $user_ip ) : array( 'country' => '', 'state' => '' ) );
|
|
// HTTP referrer
|
|
$http_referer = ( isset( $_POST['wcj_http_referer'] ) ? $_POST['wcj_http_referer'] : 'N/A' );
|
|
// Add row to DB table
|
|
$wpdb->insert(
|
|
$table_name,
|
|
array(
|
|
'time' => current_time( 'mysql' ),
|
|
'country' => $location['country'],
|
|
'state' => $location['state'],
|
|
'ip' => $user_ip,
|
|
'referer' => $http_referer,
|
|
)
|
|
);
|
|
die();
|
|
}
|
|
|
|
}
|
|
|
|
endif;
|
|
|
|
return new WCJ_User_Tracking();
|