File: /var/www/Gosuryaid/wp/wp-content/themes/my-listing/includes/extensions/visits/visits.php
<?php
namespace MyListing\Ext\Visits;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
class Visits {
private $table_version, $current_version;
public static function boot() {
new self;
}
public function __construct() {
$this->table_version = '0.67';
$this->current_version = get_option( 'mylisting_visits_table_version' );
// Setup DB.
$this->setup_tables();
// Count visits.
add_action( 'template_redirect', [ $this, 'maybe_add_visit' ] );
// Get visit stats.
add_filter( 'mylisting/stats/user', [ $this, 'set_user_visit_stats' ], 10, 2 );
add_filter( 'mylisting/stats/listing', [ $this, 'set_listing_visit_stats' ], 10, 2 );
add_filter( 'mylisting/stats/admin', [ $this, 'set_admin_visit_stats' ], 10 );
// Cleanup visit stats table.
add_action( 'mylisting/schedule:twicedaily', [ $this, 'cleanup_visits_table' ], 30 );
}
public function maybe_add_visit() {
if ( ! is_singular( 'job_listing' ) ) {
return;
}
global $post;
$cookie_key = md5( sprintf( 'mylisting_recent_visit_%s', $post->ID ) );
// If this user already viewed this listing recently, then skip
// to prevent visit spamming. See mylisting/stats/visit-throttle filter.
if ( \MyListing\get_cookie( $cookie_key ) === 'yes' ) {
return;
}
// Set recent visit cookie.
\MyListing\set_cookie( $cookie_key, 'yes', time() + ( (int) apply_filters( 'mylisting/stats/visit-throttle', 3 ) ) );
// Get visitor data and insert visit.
$visitor = \MyListing\Src\Visitor::instance();
$ref = $visitor->get_referrer();
$os = $visitor->get_os();
$location = $visitor->get_location();
$this->add_visit( [
'listing_id' => $post->ID,
'fingerprint' => $visitor->get_fingerprint(),
'ip_address' => $visitor->get_ip(),
'language' => $visitor->get_language(),
'ref_url' => $ref ? $ref['url'] : null,
'ref_domain' => $ref ? $ref['domain'] : null,
'os' => $os ? $os['os'] : null,
'device' => $os ? $os['device'] : null,
'browser' => $visitor->get_browser(),
'http_user_agent' => $visitor->get_user_agent(),
'country_code' => $location ?: null,
'city' => null,
] );
}
public function add_visit( $args ) {
global $wpdb;
if ( empty( $args['fingerprint'] ) || empty( $args['listing_id'] ) ) {
return;
}
// Get values.
$values = array_filter( [
'listing_id' => $args['listing_id'],
'fingerprint' => $args['fingerprint'],
'ip_address' => ! empty( $args['ip_address'] ) ? $args['ip_address'] : null,
'language' => ! empty( $args['language'] ) ? $args['language'] : null,
'ref_url' => ! empty( $args['ref_url'] ) ? $args['ref_url'] : null,
'ref_domain' => ! empty( $args['ref_domain'] ) ? $args['ref_domain'] : null,
'os' => ! empty( $args['os'] ) ? $args['os'] : null,
'device' => ! empty( $args['device'] ) ? $args['device'] : null,
'browser' => ! empty( $args['browser'] ) ? $args['browser'] : null,
'http_user_agent' => ! empty( $args['http_user_agent'] ) ? $args['http_user_agent'] : null,
'country_code' => ! empty( $args['country_code'] ) ? $args['country_code'] : null,
'city' => ! empty( $args['city'] ) ? $args['city'] : null,
] );
$values['time'] = gmdate('Y-m-d H:i:s');
// Insert visit to db.
$wpdb->insert( $wpdb->prefix.'mylisting_visits', $values );
}
public function setup_tables() {
if ( $this->table_version === $this->current_version ) {
return;
}
global $wpdb;
$table_name = $wpdb->prefix . 'mylisting_visits';
$sql = "CREATE TABLE $table_name (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
listing_id bigint(20) unsigned NOT NULL,
time datetime NOT NULL,
fingerprint varchar(64) NOT NULL,
ip_address varchar(32),
language varchar(32),
ref_url varchar(512),
ref_domain varchar(256),
os varchar(32),
device varchar(32),
browser varchar(32),
http_user_agent varchar(512),
country_code varchar(32),
city varchar(64),
PRIMARY KEY (id)
);";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
$wpdb->query( "
ALTER TABLE {$wpdb->prefix}mylisting_visits
ADD CONSTRAINT FK_VIEWS_LISTING_ID
FOREIGN KEY (listing_id)
REFERENCES {$wpdb->posts}(ID) ON DELETE CASCADE;
" );
$wpdb->query( "ALTER TABLE {$wpdb->prefix}mylisting_visits ADD INDEX `fingerprint` (`fingerprint`)" );
$wpdb->query( "ALTER TABLE {$wpdb->prefix}mylisting_visits ADD INDEX `time` (`time`)" );
$wpdb->query( "ALTER TABLE {$wpdb->prefix}mylisting_visits ADD INDEX `ref_domain` (`ref_domain`)" );
$wpdb->query( "ALTER TABLE {$wpdb->prefix}mylisting_visits ADD INDEX `ref_url` (`ref_url`)" );
update_option( 'mylisting_visits_table_version', $this->table_version );
}
public function _apply_query_rules( $sql, $args ) {
global $wpdb;
// Get stats for a single author.
if ( ! empty( $args['user_id'] ) ) {
$sql[] = sprintf( " AND {$wpdb->posts}.post_author = %d ", $args['user_id'] );
}
// Get stats for a single listing.
if ( ! empty( $args['listing_id'] ) ) {
$sql[] = sprintf( " AND {$wpdb->prefix}mylisting_visits.listing_id = %d ", $args['listing_id'] );
}
// Limit visit timeframe.
if ( ! empty( $args['time'] ) && in_array( $args['time'], ['lastday', 'lastweek', 'lastmonth', 'lasthalfyear', 'lastyear'] ) ) {
$time_modifiers = [ 'lastday' => '-1 day', 'lastweek' => '-7 days', 'lastmonth' => '-30 days', 'lasthalfyear' => '-182 days', 'lastyear' => '-365 days' ];
$sql[] = sprintf(
" AND {$wpdb->prefix}mylisting_visits.time >= '%s' ",
c27()->utc()->modify( $time_modifiers[ $args['time'] ] )->format('Y-m-d H:i:s')
);
}
return $sql;
}
public function get_visits( $args = [] ) {
global $wpdb;
$args = wp_parse_args( $args, [
'listing_id' => false,
'user_id' => false,
'time' => false,
'unique' => false,
] );
$sql = [];
if ( $args['unique'] ) {
$sql[] = "SELECT COUNT( DISTINCT( {$wpdb->prefix}mylisting_visits.fingerprint ) ) AS count";
} else {
$sql[] = "SELECT COUNT( {$wpdb->prefix}mylisting_visits.id ) AS count";
}
$sql[] = "FROM {$wpdb->prefix}mylisting_visits";
$sql[] = "INNER JOIN {$wpdb->posts} ON ( {$wpdb->posts}.ID = {$wpdb->prefix}mylisting_visits.listing_id )";
$sql[] = "WHERE {$wpdb->posts}.post_status = 'publish'";
$sql = $this->_apply_query_rules( $sql, $args );
$sql = join( "\n", $sql );
$query = $wpdb->get_row( $sql, OBJECT );
return is_object( $query ) && ! empty( $query->count ) ? (int) $query->count : 0;
}
public function get_grouped_visits( $args = [] ) {
global $wpdb;
$args = wp_parse_args( $args, [
'listing_id' => false,
'user_id' => false,
'time' => false,
'group_by' => 'day',
] );
$groups = [
'hour' => [
'query' => "DATE_FORMAT( {$wpdb->prefix}mylisting_visits.time, '%Y-%m-%d %H:00:00' )",
'modifier' => '-1 hour',
'id' => 'Y-m-d H:00:00',
'count' => function( $c ) { return $c * 24; },
'label' => function( $date ) {
return date_i18n(
apply_filters( 'mylisting/stats/hour-label', 'H:00' ),
$date->getTimestamp()
);
},
],
'day' => [
'query' => "DATE( {$wpdb->prefix}mylisting_visits.time )",
'modifier' => '-1 day',
'id' => 'Y-m-d',
'count' => function( $c ) { return $c; },
'label' => function( $date ) { return $date->format( 'M j' ); },
'label' => function( $date ) {
return date_i18n(
apply_filters( 'mylisting/stats/day-label', 'M j' ),
$date->getTimestamp()
);
},
],
'week' => [
'query' => "DATE_FORMAT( {$wpdb->prefix}mylisting_visits.time, '%x-%v' )",
'modifier' => '-1 week',
'count' => function( $c ) { return $c / 7; },
'id' => 'o-W',
'label' => function( $date ) {
return date_i18n(
apply_filters( 'mylisting/stats/week-label', 'M j' ),
$date->getTimestamp()
);
},
],
'month' => [
'query' => "DATE_FORMAT( {$wpdb->prefix}mylisting_visits.time, '%Y-%m-01' )",
'modifier' => '-1 month',
'id' => 'Y-m-01',
'count' => function( $c ) { return $c / 31; },
'label' => function( $date ) {
return date_i18n(
apply_filters( 'mylisting/stats/month-label', 'M' ),
$date->getTimestamp()
);
},
],
];
$visits = [];
$group = isset( $groups[ $args['group_by'] ] ) ? $groups[ $args['group_by'] ] : $groups['day'];
$sql = [];
$sql[] = "
SELECT
COUNT( {$wpdb->prefix}mylisting_visits.id ) AS views,
COUNT( DISTINCT( {$wpdb->prefix}mylisting_visits.fingerprint ) ) AS unique_views,
{$group['query']} as date
";
$sql[] = "FROM {$wpdb->prefix}mylisting_visits";
$sql[] = "INNER JOIN {$wpdb->posts} ON ( {$wpdb->posts}.ID = {$wpdb->prefix}mylisting_visits.listing_id )";
$sql[] = "WHERE {$wpdb->posts}.post_status = 'publish'";
$sql = $this->_apply_query_rules( $sql, $args );
$sql[] = "GROUP BY date";
$sql = join( "\n", $sql );
$query = $wpdb->get_results( $sql, OBJECT );
if ( ! is_array( $query ) || empty( $query ) ) {
$query = [];
}
$date = c27()->utc();
$counts = [
'lastyear' => 365,
'lasthalfyear' => 182,
'lastmonth' => 30,
'lastweek' => 7,
'lastday' => 1,
];
// lastmonth -> 30 days, lastweek -> 7 days, lastday -> 1 day.
$count = isset( $counts[ $args['time'] ] ) ? $counts[ $args['time'] ] : 1;
// apply the count modifier of $group
// if the `group_by` param is set to `hour`, multiply count by 24 for hours.
$count = $group['count']( $count );
// Prepare $visits array.
for ( $i = 0; $i < $count; $i++ ) {
$id = $date->format( $group['id'] );
$visits[ $id ] = [
'views' => 0,
'unique_views' => 0,
'date' => $id,
'label' => $group['label']( $date ),
'formatted' => [
'views' => _x( 'No views', 'User dashboard', 'my-listing' ),
'unique_views' => _x( 'No views', 'User dashboard', 'my-listing' ),
],
];
$date->modify( $group['modifier'] );
}
// Merge $query results to $visits array.
foreach ( $query as $day ) {
if ( empty( $day ) || ! isset( $visits[ $day->date ] ) ) {
continue;
}
$format = _x( '%1$s visit(s) on %2$s', 'User dashboard', 'my-listing' );
$day->views = isset( $day->views ) ? $day->views : 0;
$day->unique_views = isset( $day->unique_views ) ? $day->unique_views : 0;
$visits[ $day->date ]['views'] = $day->views;
$visits[ $day->date ]['unique_views'] = $day->unique_views;
$visits[ $day->date ]['formatted']['views'] = sprintf( _n( '%s view', '%s views', $day->views, 'my-listing' ), number_format_i18n( $day->views ) );
$visits[ $day->date ]['formatted']['unique_views'] = sprintf( _n( '%s unique view', '%s unique views', $day->unique_views, 'my-listing' ), number_format_i18n( $day->unique_views ) );
}
return array_reverse( $visits );
}
public function get_referrers( $args = [] ) {
global $wpdb;
$referrers = [];
$args = wp_parse_args( $args, [
'listing_id' => false,
'user_id' => false,
'time' => false,
] );
$sql = [];
$sql[] = "
SELECT
{$wpdb->prefix}mylisting_visits.ref_domain AS ref_domain,
COUNT( {$wpdb->prefix}mylisting_visits.ref_domain ) AS ref_domain_count
FROM {$wpdb->prefix}mylisting_visits
INNER JOIN {$wpdb->posts} ON ( {$wpdb->posts}.ID = {$wpdb->prefix}mylisting_visits.listing_id )
WHERE
{$wpdb->posts}.post_status = 'publish'
AND {$wpdb->prefix}mylisting_visits.ref_domain IS NOT NULL
AND {$wpdb->prefix}mylisting_visits.ref_url IS NOT NULL
";
$sql = $this->_apply_query_rules( $sql, $args );
$sql[] = "GROUP BY ref_domain";
$sql[] = "ORDER BY ref_domain_count DESC";
$sql[] = "LIMIT 15";
$sql = join( "\n", $sql );
$query = $wpdb->get_results( $sql, OBJECT );
if ( ! is_array( $query ) || empty( $query ) ) {
return $referrers;
}
foreach ( $query as $domain ) {
if ( empty( $domain->ref_domain ) || empty( $domain->ref_domain_count ) ) {
continue;
}
$referrers[] = [
'domain' => $domain->ref_domain,
'count' => $domain->ref_domain_count,
'subrefs' => $this->get_referrers_for_domain( $domain->ref_domain, $args ),
];
}
return $referrers;
}
public function get_countries( $args = [] ) {
$rows = $this->get_column_count( 'country_code', $args );
$countries = [];
foreach ( $rows as $country ) {
if ( empty( $country->country_code ) || empty( $country->country_code_count ) ) {
continue;
}
$countries[] = [
'code' => $country->country_code,
'name' => \MyListing\get_country_name_by_code( $country->country_code ),
'count' => $country->country_code_count,
];
}
return $countries;
}
public function get_browsers( $args = [] ) {
$rows = $this->get_column_count( 'browser', $args );
$browsers = [];
foreach ( $rows as $browser ) {
if ( empty( $browser->browser ) || empty( $browser->browser_count ) ) {
continue;
}
$browsers[] = [
'name' => $browser->browser,
'count' => $browser->browser_count,
];
}
if ( ! empty( $_GET['dummy_data'] ) ) {
$browsers = array_map( function( $browser ) {
return [
'name' => $browser,
'count' => rand( 30, 240 ),
];
}, [ 'Internet Explorer', 'Firefox', 'Safari', 'Chrome', 'Edge', 'Opera', 'Handheld Browser' ] );
}
return $browsers;
}
public function get_devices( $args = [] ) {
$rows = $this->get_column_count( 'device', $args );
$devices = [];
foreach ( $rows as $device ) {
if ( empty( $device->device ) || empty( $device->device_count ) ) {
continue;
}
$label = $device->device === 'desktop'
? _x( 'Desktop', 'User dashboard', 'my-listing' )
: _x( 'Mobile', 'User dashboard', 'my-listing' );
$devices[] = [
'name' => $device->device,
'label' => $label,
'count' => $device->device_count,
];
}
return $devices;
}
public function get_platforms( $args = [] ) {
$rows = $this->get_column_count( 'os', $args );
$platforms = [];
foreach ( $rows as $platform ) {
if ( empty( $platform->os ) || empty( $platform->os_count ) ) {
continue;
}
$platforms[] = [
'name' => $platform->os,
'count' => $platform->os_count,
];
}
if ( ! empty( $_GET['dummy_data'] ) ) {
$platforms = array_map( function( $os ) {
return [
'name' => $os,
'count' => rand( 30, 240 ),
];
}, [ 'Windows 10', 'Windows 8', 'Windows 7', 'macOS', 'Linux', 'Ubuntu', 'iOS', 'Android', 'webOS' ] );
}
return $platforms;
}
public function get_column_count( $column, $args = [] ) {
global $wpdb;
$args = wp_parse_args( $args, [
'listing_id' => false,
'user_id' => false,
'time' => false,
] );
$sql = [];
$sql[] = "
SELECT
{$wpdb->prefix}mylisting_visits.{$column} AS {$column},
COUNT( {$wpdb->prefix}mylisting_visits.{$column} ) AS {$column}_count
FROM {$wpdb->prefix}mylisting_visits
INNER JOIN {$wpdb->posts} ON ( {$wpdb->posts}.ID = {$wpdb->prefix}mylisting_visits.listing_id )
WHERE
{$wpdb->posts}.post_status = 'publish'
AND {$wpdb->prefix}mylisting_visits.{$column} IS NOT NULL
";
$sql = $this->_apply_query_rules( $sql, $args );
$sql[] = "GROUP BY {$column}";
$sql[] = "ORDER BY {$column}_count DESC";
$sql[] = "LIMIT 15";
$sql = join( "\n", $sql );
$query = $wpdb->get_results( $sql, OBJECT );
if ( ! is_array( $query ) || empty( $query ) ) {
return [];
}
return $query;
}
public function get_referrers_for_domain( $domain, $args = [] ) {
global $wpdb;
$refs = [];
$args = wp_parse_args( $args, [
'listing_id' => false,
'user_id' => false,
'time' => false,
] );
if ( empty( $domain ) ) {
return $refs;
}
$sql[] = "
SELECT
{$wpdb->prefix}mylisting_visits.ref_url AS ref_url,
COUNT( {$wpdb->prefix}mylisting_visits.ref_url ) AS ref_url_count
FROM {$wpdb->prefix}mylisting_visits
INNER JOIN {$wpdb->posts} ON ( {$wpdb->posts}.ID = {$wpdb->prefix}mylisting_visits.listing_id )
WHERE
{$wpdb->posts}.post_status = 'publish'
AND {$wpdb->prefix}mylisting_visits.ref_domain = %s
AND {$wpdb->prefix}mylisting_visits.ref_url IS NOT NULL
";
$sql = $this->_apply_query_rules( $sql, $args );
$sql[] = "GROUP BY ref_url";
$sql[] = "ORDER BY ref_url_count DESC";
$sql[] = "LIMIT 10";
$sql = join( "\n", $sql );
$query = $wpdb->get_results( $wpdb->prepare( $sql, $domain ), OBJECT );
if ( ! is_array( $query ) || empty( $query ) ) {
return $refs;
}
foreach ( $query as $domain ) {
if ( empty( $domain->ref_url ) || empty( $domain->ref_url_count ) ) {
continue;
}
$refs[] = [
'url' => $domain->ref_url,
'count' => $domain->ref_url_count,
];
}
return $refs;
}
public function get_grouped_stats( $args = [] ) {
$stats = [];
$user_id = ! empty( $args['user_id'] ) ? $args['user_id'] : false;
$listing_id = ! empty( $args['listing_id'] ) ? $args['listing_id'] : false;
$stats['views'] = [
'lastday' => $this->get_visits( [ 'user_id' => $user_id, 'listing_id' => $listing_id, 'time' => 'lastday' ] ),
'lastweek' => $this->get_visits( [ 'user_id' => $user_id, 'listing_id' => $listing_id, 'time' => 'lastweek' ] ),
'lastmonth' => $this->get_visits( [ 'user_id' => $user_id, 'listing_id' => $listing_id, 'time' => 'lastmonth' ] ),
];
$stats['unique_views'] = [
'lastday' => $this->get_visits( [ 'user_id' => $user_id, 'listing_id' => $listing_id, 'time' => 'lastday', 'unique' => true ] ),
'lastweek' => $this->get_visits( [ 'user_id' => $user_id, 'listing_id' => $listing_id, 'time' => 'lastweek', 'unique' => true ] ),
'lastmonth' => $this->get_visits( [ 'user_id' => $user_id, 'listing_id' => $listing_id, 'time' => 'lastmonth', 'unique' => true ] ),
];
$stats['referrers'] = $this->get_referrers( [ 'user_id' => $user_id, 'listing_id' => $listing_id ] );
$stats['countries'] = $this->get_countries( [ 'user_id' => $user_id, 'listing_id' => $listing_id ] );
$stats['browsers'] = $this->get_browsers( [ 'user_id' => $user_id, 'listing_id' => $listing_id ] );
$stats['platforms'] = $this->get_platforms( [ 'user_id' => $user_id, 'listing_id' => $listing_id ] );
$stats['devices'] = $this->get_devices( [ 'user_id' => $user_id, 'listing_id' => $listing_id ] );
$stats['charts'] = [];
$chart_categories = mylisting()->get( 'stats.chart_categories' );
if ( in_array( 'lastday', $chart_categories ) ) {
$stats['charts']['lastday'] = $this->get_grouped_visits( [ 'time' => 'lastday', 'group_by' => 'hour', 'user_id' => $user_id, 'listing_id' => $listing_id ] );
}
if ( in_array( 'lastweek', $chart_categories ) ) {
$stats['charts']['lastweek'] = $this->get_grouped_visits( [ 'time' => 'lastweek', 'group_by' => 'day', 'user_id' => $user_id, 'listing_id' => $listing_id ] );
}
if ( in_array( 'lastmonth', $chart_categories ) ) {
$stats['charts']['lastmonth'] = $this->get_grouped_visits( [ 'time' => 'lastmonth', 'group_by' => 'day', 'user_id' => $user_id, 'listing_id' => $listing_id ] );
}
if ( in_array( 'lasthalfyear', $chart_categories ) ) {
$stats['charts']['lasthalfyear'] = $this->get_grouped_visits( [ 'time' => 'lasthalfyear', 'group_by' => 'week', 'user_id' => $user_id, 'listing_id' => $listing_id ] );
}
if ( in_array( 'lastyear', $chart_categories ) ) {
$stats['charts']['lastyear'] = $this->get_grouped_visits( [ 'time' => 'lastyear', 'group_by' => 'month', 'user_id' => $user_id, 'listing_id' => $listing_id ] );
}
return $stats;
}
public function set_user_visit_stats( $stats, $user_id ) {
$stats['visits'] = $this->get_grouped_stats( [
'user_id' => $user_id,
] );
return $stats;
}
public function set_listing_visit_stats( $stats, $listing_id ) {
$stats['visits'] = $this->get_grouped_stats( [
'listing_id' => $listing_id,
] );
return $stats;
}
public function set_admin_visit_stats( $stats ) {
$stats['visits'] = $this->get_grouped_stats();
return $stats;
}
/**
* Check for, and remove old stats.
*
* @since 2.0
*/
public function cleanup_visits_table() {
global $wpdb;
$delete_after = mylisting()->get( 'stats.db_time' );
if ( empty( $delete_after ) || $delete_after < 1 ) {
return;
}
$time_modifier = sprintf( '-%d days', absint( $delete_after ) );
$sql = sprintf(
"DELETE FROM {$wpdb->prefix}mylisting_visits WHERE {$wpdb->prefix}mylisting_visits.time < '%s'",
c27()->utc()->modify( $time_modifier )->format('Y-m-d H:i:s')
);
// Execute delete query.
$wpdb->query( $sql );
}
}