HEX
Server: Apache
System: Linux vps.teamads.com 4.18.0-553.126.1.el8_10.x86_64 #1 SMP Thu May 28 06:44:09 EDT 2026 x86_64
User: teamadsc (1024)
PHP: 8.1.34
Disabled: NONE
Upload Files
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">&times;</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);
}