File: /home/teamadsc/public_html/wp-content/plugins/visitors/includes/admin-columns.php
<?php
// Add columns to posts and pages tables
function ahcpro_add_hits_column($columns)
{
$columns['hits'] = 'Hits';
unset($columns['views'], $columns['pageviews']); // Remove old ones
return $columns;
}
add_filter('manage_posts_columns', 'ahcpro_add_hits_column');
add_filter('manage_pages_columns', 'ahcpro_add_hits_column');
// Make columns sortable
function ahcpro_sortable_hits_column($columns)
{
$columns['hits'] = 'hits';
return $columns;
}
add_filter('manage_edit-post_sortable_columns', 'ahcpro_sortable_hits_column');
add_filter('manage_edit-page_sortable_columns', 'ahcpro_sortable_hits_column');
// Handle the sorting
function ahcpro_sort_hits_column($query)
{
if (!is_admin()) return;
$orderby = $query->get('orderby');
if ($orderby === 'hits') {
$query->set('meta_key', '_ahcpro_total_views'); // sort by visitors
$query->set('orderby', 'meta_value_num');
}
}
add_action('pre_get_posts', 'ahcpro_sort_hits_column');
// Populate the columns with data
function ahcpro_populate_hits_column($column, $post_id)
{
if ($column !== 'hits') return;
global $wpdb;
// Fetch hits from ahc_hits table
$results = $wpdb->get_row($wpdb->prepare(
"SELECT
COUNT(*) as total_hits,
COUNT(DISTINCT hit_ip_address) as unique_visitors
FROM ahc_hits
WHERE hit_page_id = %d",
$post_id
));
$total_hits = $results ? intval($results->total_hits) : 0;
$unique_visitors = $results ? intval($results->unique_visitors) : 0;
// Store in post meta for sorting
update_post_meta($post_id, '_ahcpro_total_views', $total_hits);
update_post_meta($post_id, '_ahcpro_unique_visitors', $unique_visitors);
$page_title = htmlspecialchars(get_the_title($post_id), ENT_QUOTES, 'UTF-8');
echo '<div class="ahc-stats-cell">';
echo '<a href="#" class="ahc-stats-number" data-post-id="' . $post_id . '" data-page-title="' . $page_title . '">';
echo '<span class="dashicons ahc-icon"></span> ';
echo '<span class="stat-number">' . number_format($total_hits) . ' hits</span>';
echo '<span class="stat-visitors">' . number_format($unique_visitors) . ' visitors</span>';
echo '</a>';
echo '</div>';
// Add styles and modal (only once)
static $modal_added = false;
if (!$modal_added) {
echo '<style>
.column-hits { width: 120px !important; text-align: center; }
.ahc-icon:before {
content: "\\f185";
color: #1DAE22;
font-family: dashicons;
position: relative;
top: 3px;
}
.ahc-stats-number {
display: flex;
flex-direction: column;
align-items: center;
text-decoration: none;
color: #2271b1;
gap: 2px;
padding: 4px;
border-radius: 3px;
transition: background-color 0.2s;
}
.ahc-stats-number:hover {
background-color: rgba(0,0,0,0.05);
}
.stat-number { font-size: 13px; font-weight: 500; }
.stat-visitors { font-size: 11px; color: #666; }
.ahc-modal {
display: none;
position: fixed;
z-index: 999999;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.4);
}
.ahc-modal-content {
background-color: #fff;
margin: 5% auto;
padding: 20px;
border-radius: 4px;
width: 80%;
max-width: 800px;
max-height: 80vh;
overflow-y: auto;
}
.ahc-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}
.ahc-modal-close {
cursor: pointer;
font-size: 20px;
padding: 5px;
}
.ahc-chart-container {
position: relative;
height: 400px;
margin-top: 20px;
}
.ahc-time-range {
margin-bottom: 15px;
}
.ahc-stats-summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-bottom: 20px;
padding: 15px;
background: #f9f9f9;
border-radius: 4px;
}
.ahc-stat-item {
text-align: center;
}
.ahc-stat-number {
font-size: 24px;
font-weight: bold;
color: #1DAE22;
display: block;
}
.ahc-stat-label {
font-size: 12px;
color: #666;
text-transform: uppercase;
}
</style>';
echo '<div id="ahcHitsModal" class="ahc-modal">
<div class="ahc-modal-content">
<div class="ahc-modal-header">
<h2 id="ahcModalTitle">Page Statistics</h2>
<span class="ahc-modal-close">×</span>
</div>
<div id="ahcStatsSummary" class="ahc-stats-summary"></div>
<div class="ahc-time-range">
<label for="ahcTimeRange"><strong>Time Range:</strong></label>
<select id="ahcTimeRange">
<option value="7">Last 7 days</option>
<option value="14" selected>Last 14 days</option>
<option value="30">Last 30 days</option>
<option value="this_month">This month</option>
</select>
</div>
<div class="ahc-chart-container">
<canvas id="ahcHitsChart"></canvas>
</div>
</div>
</div>';
echo '<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
jQuery(document).ready(function($) {
var modal = $("#ahcHitsModal");
var chart;
var chartCanvas = document.getElementById("ahcHitsChart");
$(".ahc-stats-number").on("click", function(e) {
e.preventDefault();
var postId = $(this).data("post-id");
var pageTitle = $(this).data("page-title");
$("#ahcModalTitle").text("Statistics - " + pageTitle);
$("#ahcHitsModal").data("post-id", postId);
$("#ahcTimeRange").val("14");
fetchChartData(postId, "14");
modal.show();
});
$(".ahc-modal-close").on("click", function() {
modal.hide();
if (chart) { chart.destroy(); }
});
$(window).on("click", function(e) {
if (e.target === modal[0]) {
modal.hide();
if (chart) { chart.destroy(); }
}
});
$("#ahcTimeRange").on("change", function() {
var postId = $("#ahcHitsModal").data("post-id");
var range = $(this).val();
fetchChartData(postId, range);
});
function fetchChartData(postId, range) {
$.ajax({
url: ajaxurl,
method: "POST",
data: {
action: "ahcpro_get_hits_data",
post_id: postId,
range: range,
nonce: "' . wp_create_nonce('ahcpro_hits_nonce') . '"
},
beforeSend: function() {
$(".ahc-chart-container").html("<p style=\"text-align:center;padding:50px;\">Loading...</p>");
},
success: function(response) {
if (!response.success) {
$(".ahc-chart-container").html("<p style=\"text-align:center;padding:50px;color:red;\">Error loading data</p>");
return;
}
var data = response.data;
// Update summary stats
updateSummaryStats(data.summary);
// Restore chart container
$(".ahc-chart-container").html("<canvas id=\"ahcHitsChart\"></canvas>");
chartCanvas = document.getElementById("ahcHitsChart");
// Destroy existing chart
if (chart) chart.destroy();
// Create new chart
var labels = data.chart.map(item => item.date);
var hits = data.chart.map(item => item.hits);
var visitors = data.chart.map(item => item.visitors);
chart = new Chart(chartCanvas.getContext("2d"), {
type: "line",
data: {
labels: labels,
datasets: [
{
label: "Total Hits",
data: hits,
borderColor: "#1DAE22",
backgroundColor: "rgba(29, 174, 34, 0.1)",
fill: true,
tension: 0.4
},
{
label: "Unique Visitors",
data: visitors,
borderColor: "#0073aa",
backgroundColor: "rgba(0, 115, 170, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
intersect: false,
mode: "index"
},
plugins: {
title: { display: false },
legend: {
position: "top",
align: "center"
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
precision: 0,
callback: function(value) {
return Math.round(value);
}
}
},
x: {
display: true,
title: {
display: true,
text: "Date"
}
}
}
}
});
},
error: function() {
$(".ahc-chart-container").html("<p style=\"text-align:center;padding:50px;color:red;\">Failed to load data</p>");
}
});
}
function updateSummaryStats(summary) {
var html = `
<div class="ahc-stat-item">
<span class="ahc-stat-number">${summary.total_hits.toLocaleString()}</span>
<span class="ahc-stat-label">Total Hits</span>
</div>
<div class="ahc-stat-item">
<span class="ahc-stat-number">${summary.unique_visitors.toLocaleString()}</span>
<span class="ahc-stat-label">Unique Visitors</span>
</div>
<div class="ahc-stat-item">
<span class="ahc-stat-number">${summary.avg_daily_hits.toLocaleString()}</span>
<span class="ahc-stat-label">Avg Daily Hits</span>
</div>
<div class="ahc-stat-item">
<span class="ahc-stat-number">${summary.peak_day_hits.toLocaleString()}</span>
<span class="ahc-stat-label">Peak Day</span>
</div>
`;
$("#ahcStatsSummary").html(html);
}
});
</script>';
$modal_added = true;
}
}
add_action('manage_posts_custom_column', 'ahcpro_populate_hits_column', 10, 2);
add_action('manage_pages_custom_column', 'ahcpro_populate_hits_column', 10, 2);
// Add CSS for column widths
function ahcpro_admin_head()
{
global $pagenow;
if ($pagenow == 'edit.php') {
echo '<style>
.column-hits {
width: 120px !important;
text-align: center !important;
}
.fixed .column-hits {
vertical-align: middle;
}
.wp-list-table thead th.column-hits {
text-align: center !important;
}
.wp-list-table thead th.column-hits a {
display: inline-flex !important;
align-items: center;
justify-content: center;
}
</style>';
}
}
add_action('admin_head', 'ahcpro_admin_head');
// AJAX handler for getting hits data
add_action('wp_ajax_ahcpro_get_hits_data', 'ahcpro_get_hits_data');
function ahcpro_get_hits_data()
{
// Verify nonce
if (!wp_verify_nonce($_POST['nonce'], 'ahcpro_hits_nonce')) {
wp_send_json_error('Invalid nonce');
}
global $wpdb;
$post_id = intval($_POST['post_id']);
$range = sanitize_text_field($_POST['range']);
// Determine date range
switch ($range) {
case '7':
case '14':
case '30':
$start = date('Y-m-d', strtotime("-" . intval($range) . " days"));
$end = date('Y-m-d');
$group_format = '%Y-%m-%d';
$date_format = 'Y-m-d';
$step = '+1 day';
break;
case 'this_month':
$start = date('Y-m-01');
$end = date('Y-m-t');
$group_format = '%Y-%m-%d';
$date_format = 'Y-m-d';
$step = '+1 day';
break;
default:
$start = date('Y-m-d', strtotime('-14 days'));
$end = date('Y-m-d');
$group_format = '%Y-%m-%d';
$date_format = 'Y-m-d';
$step = '+1 day';
break;
}
// Query from ahc_hits table
$results = $wpdb->get_results($wpdb->prepare(
"SELECT
DATE_FORMAT(hit_date, %s) as date,
COUNT(*) as hits,
COUNT(DISTINCT hit_ip_address) as visitors
FROM ahc_hits
WHERE hit_page_id = %d
AND hit_date BETWEEN %s AND %s
GROUP BY date
ORDER BY date ASC",
$group_format,
$post_id,
$start,
$end
));
// Get summary statistics
$summary_stats = $wpdb->get_row($wpdb->prepare(
"SELECT
COUNT(*) as total_hits,
COUNT(DISTINCT hit_ip_address) as unique_visitors
FROM ahc_hits
WHERE hit_page_id = %d
AND hit_date BETWEEN %s AND %s",
$post_id,
$start,
$end
));
// Build date range with 0 fallback
$dates = [];
$cursor = strtotime($start);
$end_ts = strtotime($end);
while ($cursor <= $end_ts) {
$key = date($date_format, $cursor);
$dates[$key] = [
'hits' => 0,
'visitors' => 0
];
$cursor = strtotime($step, $cursor);
}
// Populate with actual data
foreach ($results as $row) {
$key = $row->date;
if (isset($dates[$key])) {
$dates[$key]['hits'] = intval($row->hits);
$dates[$key]['visitors'] = intval($row->visitors);
}
}
// Calculate additional summary stats
$total_days = count($dates);
$daily_hits = array_column($dates, 'hits');
$avg_daily_hits = $total_days > 0 ? round(array_sum($daily_hits) / $total_days) : 0;
$peak_day_hits = $total_days > 0 ? max($daily_hits) : 0;
// Format chart data
$chart_data = [];
foreach ($dates as $date => $data) {
$chart_data[] = [
'date' => $date,
'hits' => $data['hits'],
'visitors' => $data['visitors']
];
}
// Prepare response
$response = [
'chart' => $chart_data,
'summary' => [
'total_hits' => intval($summary_stats->total_hits),
'unique_visitors' => intval($summary_stats->unique_visitors),
'avg_daily_hits' => $avg_daily_hits,
'peak_day_hits' => $peak_day_hits,
'date_range' => $start . ' to ' . $end
]
];
wp_send_json_success($response);
}