0) {
/* translators: 1: minutes, 2: seconds */
return sprintf(__('%1$d min %2$d s', 'statinform'), $m, $s);
}
/* translators: %d: number of minutes */
return sprintf(__('%d min', 'statinform'), $m);
}
$h = floor($m / 60);
$m = $m % 60;
/* translators: 1: hours, 2: minutes */
return sprintf(__('%1$d h %2$d min', 'statinform'), $h, $m);
}
}
/**
* Render the analytics dashboard page.
*/
function eistat_render_dashboard() {
global $wpdb;
$eistat_p = $wpdb->prefix . 'eistat_';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$view = isset($_GET['view']) ? sanitize_key(wp_unslash($_GET['view'])) : 'main';
// --- Aggregation result notice ---
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if (isset($_GET['eistat_updated']) && $_GET['eistat_updated'] === '1') {
$ms = get_option('eistat_last_manual_run_ms');
$result = get_option('eistat_last_manual_run_result', -2);
if ($result === -2) {
echo '
'
. esc_html__('Could not retrieve the aggregation result.', 'statinform')
. '
';
} elseif ($result === -1) {
echo ''
. esc_html__('The aggregator is busy with another process.', 'statinform')
. ' ' . esc_html__('Please try again in a few seconds.', 'statinform')
. '
';
} elseif ($result === 0) {
echo ''
. esc_html__('The aggregator ran but found no events to process.', 'statinform')
. '
';
} else {
echo ''
. esc_html__('Aggregation completed.', 'statinform') . ' ';
if ($ms !== false) {
/* translators: %d: milliseconds */
echo esc_html(sprintf(__('Time: %d ms.', 'statinform'), (int) $ms)) . ' ';
}
/* translators: %d: number of processed events */
echo esc_html(sprintf(__('Events processed: %d.', 'statinform'), (int) $result));
echo '
';
}
}
eistat_render_freshness();
eistat_render_size_notice($eistat_p);
// --- Router ---
switch ($view) {
case 'day': require EISTAT_DIR . 'admin/views/day.php'; break;
case 'session': require EISTAT_DIR . 'admin/views/session.php'; break;
case 'source': require EISTAT_DIR . 'admin/views/source.php'; break;
case 'paths': require EISTAT_DIR . 'admin/views/paths.php'; break;
case 'entry-exit': require EISTAT_DIR . 'admin/views/entry-exit.php'; break;
case 'links': require EISTAT_DIR . 'admin/views/links.php'; break;
case 'archives': require EISTAT_DIR . 'admin/views/archives.php'; break;
default: require EISTAT_DIR . 'admin/views/main.php'; break;
}
}
eistat_render_dashboard();
/**
* Render a "data freshness" indicator.
*/
function eistat_render_freshness() {
$last = get_option('eistat_last_aggregation_ts', 0);
if (!$last) {
echo ''
. ''
. esc_html__('Aggregation has never run yet', 'statinform')
. '
';
return;
}
$age = time() - $last;
if ($age < 60) {
$age_str = sprintf(
/* translators: %d: number of seconds */
_n('%d second ago', '%d seconds ago', $age, 'statinform'),
$age
);
} elseif ($age < 3600) {
$mins = (int) round($age / 60);
$age_str = sprintf(
/* translators: %d: number of minutes */
_n('%d minute ago', '%d minutes ago', $mins, 'statinform'),
$mins
);
} else {
$hours = (int) round($age / 3600);
$age_str = sprintf(
/* translators: %d: number of hours */
_n('%d hour ago', '%d hours ago', $hours, 'statinform'),
$hours
);
}
$class = 'eistat-fresh';
$note = '';
if ($age > 3600) {
$class .= ' stale';
$note = ' (' . __('cron may not be running', 'statinform') . ')';
}
if ($age > 6 * 3600) {
$class = 'eistat-fresh stale-hard';
$note = ' (' . __('please check cron', 'statinform') . ')';
}
echo ''
. ''
/* translators: %s: human-readable time since last aggregation */
. esc_html(sprintf(__('Data updated %s', 'statinform'), $age_str))
. esc_html($note)
. '
';
}
/**
* Show a notice when the analytics DB grows large.
*
* @param string $p Table prefix for analytics tables.
*/
function eistat_render_size_notice($p) {
$info = get_transient('eistat_db_size_info');
if ($info === false) {
global $wpdb;
$tables = ['sessions', 'pageviews', 'sources', 'daily', 'transitions', 'events'];
$total_mb = 0;
$total_rows = 0;
foreach ($tables as $t) {
$full = $p . $t;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
$row = $wpdb->get_row($wpdb->prepare("SHOW TABLE STATUS LIKE %s", $full));
if ($row) {
$total_mb += ($row->Data_length + $row->Index_length) / 1024 / 1024;
$total_rows += (int) $row->Rows;
}
}
$info = ['mb' => round($total_mb, 1), 'rows' => $total_rows];
set_transient('eistat_db_size_info', $info, HOUR_IN_SECONDS);
}
if ($info['rows'] > 100000 || $info['mb'] > 200) {
$url = admin_url('admin.php?page=eistat-dashboard&view=archives');
echo ''
. 'ℹ️ '
. sprintf(
/* translators: 1: number of rows, 2: size in MB */
esc_html__('The analytics database has grown to %1$s records (about %2$s MB). Consider archiving and deleting data older than a year.', 'statinform'),
'
' . esc_html(number_format_i18n($info['rows'])) . '',
'
' . esc_html($info['mb']) . ''
)
. '
'
. esc_html__('Go to management →', 'statinform')
. ' ';
}
}