Add a WordPress admin dashboard widget that displays click statistics at a glance. Shows total clicks across multiple time ranges, a visual sparkline chart of daily clicks, your top 10 most-clicked links, and top referrers — all without leaving the WordPress dashboard.
Requirements
- GT Link Manager 1.5+ installed and active
- The Click Log: Custom Database Table snippet must be installed first (creates the
wp_gtlm_click_logtable) - User must have the
edit_postscapability (filterable viagtlm_capabilities)
Installation
- Install the Click Log: Custom Database Table snippet first if you haven’t already
- Add the snippet below to your theme’s
functions.phpor a site-specific plugin - Visit your WordPress Dashboard — the widget appears automatically
The Code
<?php
/**
* GT Link Manager - Click Stats Dashboard Widget
*
* Adds a WordPress admin dashboard widget showing:
* - Total clicks (today, 7 days, 30 days, all time)
* - Top 10 clicked links (last 30 days)
* - Clicks-per-day sparkline chart (last 30 days)
* - Top referrers
*
* Requires: click-log-db.php snippet (creates the wp_gtlm_click_log table).
*/
defined( 'ABSPATH' ) || exit;
add_action( 'wp_dashboard_setup', 'gtlm_stats_register_widget' );
function gtlm_stats_register_widget(): void {
if ( ! current_user_can( apply_filters( 'gtlm_capabilities', 'edit_posts', 'dashboard_widget' ) ) ) {
return;
}
wp_add_dashboard_widget(
'gtlm_click_stats',
'GT Link Manager - Click Stats',
'gtlm_stats_render_widget'
);
}
function gtlm_stats_render_widget(): void {
global $wpdb;
$table = $wpdb->prefix . 'gtlm_click_log';
// Check if click log table exists.
$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
if ( ! $table_exists ) {
echo '<p>No click data yet. Install the <strong>click-log-db.php</strong> snippet to start logging clicks.</p>';
return;
}
$now = current_time( 'mysql', true );
// ── Summary counts ───────────────────────────────────────────────────────
$today = gmdate( 'Y-m-d 00:00:00' );
$week = gmdate( 'Y-m-d 00:00:00', strtotime( '-7 days' ) );
$month = gmdate( 'Y-m-d 00:00:00', strtotime( '-30 days' ) );
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$count_today = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE created_at >= %s", $today ) );
$count_week = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE created_at >= %s", $week ) );
$count_month = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE created_at >= %s", $month ) );
$count_all = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );
echo '<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px;text-align:center;">';
printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">Today</div></div>', number_format_i18n( $count_today ) );
printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">7 Days</div></div>', number_format_i18n( $count_week ) );
printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">30 Days</div></div>', number_format_i18n( $count_month ) );
printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">All Time</div></div>', number_format_i18n( $count_all ) );
echo '</div>';
// ── Daily clicks chart (last 30 days) ────────────────────────────────────
$daily = $wpdb->get_results( $wpdb->prepare(
"SELECT DATE(created_at) as day, COUNT(*) as clicks FROM {$table} WHERE created_at >= %s GROUP BY DATE(created_at) ORDER BY day ASC",
$month
) );
if ( $daily ) {
$max_clicks = max( array_column( $daily, 'clicks' ) );
$days_map = [];
foreach ( $daily as $row ) {
$days_map[ $row->day ] = (int) $row->clicks;
}
echo '<div style="margin-bottom:20px;">';
echo '<h4 style="margin:0 0 8px;font-size:13px;color:#646970;">Clicks per Day (30 days)</h4>';
echo '<div style="display:flex;align-items:end;gap:2px;height:60px;">';
for ( $i = 29; $i >= 0; $i-- ) {
$day = gmdate( 'Y-m-d', strtotime( "-{$i} days" ) );
$clicks = $days_map[ $day ] ?? 0;
$height = $max_clicks > 0 ? max( 2, round( ( $clicks / $max_clicks ) * 60 ) ) : 2;
$color = $clicks > 0 ? '#2271b1' : '#dcdcde';
printf(
'<div title="%s: %s clicks" style="flex:1;height:%dpx;background:%s;border-radius:2px 2px 0 0;min-width:4px;"></div>',
esc_attr( $day ),
number_format_i18n( $clicks ),
$height,
$color
);
}
echo '</div></div>';
}
// ── Top 10 links ─────────────────────────────────────────────────────────
$top_links = $wpdb->get_results( $wpdb->prepare(
"SELECT slug, COUNT(*) as clicks FROM {$table} WHERE created_at >= %s GROUP BY slug ORDER BY clicks DESC LIMIT 10",
$month
) );
if ( $top_links ) {
echo '<h4 style="margin:0 0 8px;font-size:13px;color:#646970;">Top Links (30 days)</h4>';
echo '<table style="width:100%;border-collapse:collapse;font-size:13px;">';
$top_max = (int) $top_links[0]->clicks;
foreach ( $top_links as $i => $row ) {
$bar_width = $top_max > 0 ? round( ( (int) $row->clicks / $top_max ) * 100 ) : 0;
$bg = $i % 2 === 0 ? '#f6f7f7' : '#fff';
printf(
'<tr style="background:%s;"><td style="padding:4px 8px;"><code>%s</code></td><td style="padding:4px 8px;text-align:right;white-space:nowrap;">%s</td><td style="padding:4px 8px;width:40%%;"><div style="height:8px;background:#2271b1;border-radius:4px;width:%d%%;"></div></td></tr>',
$bg,
esc_html( $row->slug ),
number_format_i18n( (int) $row->clicks ),
$bar_width
);
}
echo '</table>';
}
// ── Top referrers ────────────────────────────────────────────────────────
$referrers = $wpdb->get_results( $wpdb->prepare(
"SELECT referrer, COUNT(*) as clicks FROM {$table} WHERE created_at >= %s AND referrer != '' GROUP BY referrer ORDER BY clicks DESC LIMIT 5",
$month
) );
if ( $referrers ) {
echo '<h4 style="margin:16px 0 8px;font-size:13px;color:#646970;">Top Referrers (30 days)</h4>';
echo '<ol style="margin:0;padding-left:20px;font-size:13px;">';
foreach ( $referrers as $row ) {
$host = wp_parse_url( $row->referrer, PHP_URL_HOST ) ?: $row->referrer;
printf(
'<li style="margin-bottom:4px;">%s <span style="color:#646970;">(%s)</span></li>',
esc_html( $host ),
number_format_i18n( (int) $row->clicks )
);
}
echo '</ol>';
}
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
How It Works
- Registers a dashboard widget via
wp_dashboard_setupwith permission checking - Displays four summary counters: Today, 7 Days, 30 Days, and All Time click totals
- Renders a 30-day sparkline bar chart showing daily click volume using inline CSS (no external JS libraries needed)
- Lists the top 10 most-clicked link slugs with click counts and visual bar indicators
- Shows the top 5 referrer domains with click counts
- Gracefully handles missing click log table with an informative message
Configuration Notes
The widget respects the gtlm_capabilities filter, so you can control which user roles see the stats. By default, any user with edit_posts can view the widget.
All queries use WordPress’s $wpdb with prepared statements for security. The widget is rendered with inline styles for compatibility across WordPress admin themes.