From a243bdc41cfa9189bb6b3d45e568f2bd44f89d25 Mon Sep 17 00:00:00 2001 From: Andreas Date: Fri, 17 Apr 2026 23:56:44 +0200 Subject: [PATCH] Sync folders --- ansico-stat-plugin-v1.0.0.9.zip | Bin 33908 -> 0 bytes ansico-stat-plugin-v1.1.0.1.zip | Bin 0 -> 39599 bytes ...o-stat-plugin-v1.1.0.1.zip:Zone.Identifier | Bin 0 -> 25 bytes ansico-stat-plugin/ansico-stat-plugin.php | 1283 ++++++++++++++++- .../assets/css/ansico-stat-admin.css | 111 ++ ansico-stat-plugin/readme.txt | 37 +- 6 files changed, 1412 insertions(+), 19 deletions(-) delete mode 100644 ansico-stat-plugin-v1.0.0.9.zip create mode 100644 ansico-stat-plugin-v1.1.0.1.zip create mode 100644 ansico-stat-plugin-v1.1.0.1.zip:Zone.Identifier diff --git a/ansico-stat-plugin-v1.0.0.9.zip b/ansico-stat-plugin-v1.0.0.9.zip deleted file mode 100644 index 0c61d376f69d586cf1ac5d874b4495ad61d86116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33908 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 j<40{jBdl#JyUFr831@K2xget_referer_data(); if ($page_context['kind'] === 'singular') { - $this->increment_post_views((int) $page_context['post_id'], $referer_data); + $this->increment_post_views((int) $page_context['post_id'], $referer_data, $is_unique_visitor); } else { - $this->increment_non_singular_views($page_context, $referer_data); + $this->increment_non_singular_views($page_context, $referer_data, $is_unique_visitor); } if (!headers_sent()) { - setcookie($cookie_name, '1', time() + $this->get_cookie_ttl(), COOKIEPATH ?: '/', COOKIE_DOMAIN, is_ssl(), true); - $_COOKIE[$cookie_name] = '1'; + setcookie($view_cookie_name, '1', time() + $this->get_cookie_ttl(), COOKIEPATH ?: '/', COOKIE_DOMAIN, is_ssl(), true); + $_COOKIE[$view_cookie_name] = '1'; + + if ($is_unique_visitor) { + $expires = strtotime('tomorrow', current_time('timestamp')); + if ($expires <= time()) { + $expires = time() + DAY_IN_SECONDS; + } + setcookie($unique_cookie_name, '1', $expires, COOKIEPATH ?: '/', COOKIE_DOMAIN, is_ssl(), true); + $_COOKIE[$unique_cookie_name] = '1'; + } } } @@ -681,7 +698,7 @@ if (!class_exists('Ansico_Stat_Plugin')) { return null; } - protected function increment_post_views(int $post_id, array $referer_data = []) { + protected function increment_post_views(int $post_id, array $referer_data = [], bool $is_unique_visitor = false) { global $wpdb; $today = current_time('Y-m-d'); @@ -701,11 +718,12 @@ if (!class_exists('Ansico_Stat_Plugin')) { )); $wpdb->query($wpdb->prepare( - "INSERT INTO {$post_daily_table} (post_id, stat_date, views) - VALUES (%d, %s, 1) - ON DUPLICATE KEY UPDATE views = views + 1", + "INSERT INTO {$post_daily_table} (post_id, stat_date, views, unique_visitors) + VALUES (%d, %s, 1, %d) + ON DUPLICATE KEY UPDATE views = views + 1, unique_visitors = unique_visitors + VALUES(unique_visitors)", $post_id, - $today + $today, + $is_unique_visitor ? 1 : 0 )); $category = isset($referer_data['category']) ? sanitize_key($referer_data['category']) : 'direct'; @@ -725,7 +743,7 @@ if (!class_exists('Ansico_Stat_Plugin')) { $this->increment_dimension_views($today); } - protected function increment_non_singular_views(array $page_context, array $referer_data = []) { + protected function increment_non_singular_views(array $page_context, array $referer_data = [], bool $is_unique_visitor = false) { global $wpdb; $today = current_time('Y-m-d'); @@ -747,15 +765,16 @@ if (!class_exists('Ansico_Stat_Plugin')) { $url = isset($page_context['url']) ? esc_url_raw((string) $page_context['url']) : ''; $wpdb->query($wpdb->prepare( - "INSERT INTO {$page_daily_table} (page_key, page_type, object_id, page_label, page_url, stat_date, views) - VALUES (%s, %s, %s, %s, %s, %s, 1) - ON DUPLICATE KEY UPDATE views = views + 1, page_label = VALUES(page_label), page_url = VALUES(page_url)", + "INSERT INTO {$page_daily_table} (page_key, page_type, object_id, page_label, page_url, stat_date, views, unique_visitors) + VALUES (%s, %s, %s, %s, %s, %s, 1, %d) + ON DUPLICATE KEY UPDATE views = views + 1, unique_visitors = unique_visitors + VALUES(unique_visitors), page_label = VALUES(page_label), page_url = VALUES(page_url)", $page_key, $page_type, $object_id, $label, $url, - $today + $today, + $is_unique_visitor ? 1 : 0 )); $category = isset($referer_data['category']) ? sanitize_key($referer_data['category']) : 'direct'; @@ -921,6 +940,15 @@ if (!class_exists('Ansico_Stat_Plugin')) { [$this, 'render_yearly_stats_page'] ); + add_submenu_page( + self::MENU_SLUG_STATS, + __('Single page statistics', 'ansico-stat-plugin'), + __('Single page statistics', 'ansico-stat-plugin'), + 'manage_options', + self::MENU_SLUG_SINGLE, + [$this, 'render_single_page_stats_page'] + ); + add_submenu_page( self::MENU_SLUG_STATS, __('Settings', 'ansico-stat-plugin'), @@ -936,6 +964,7 @@ if (!class_exists('Ansico_Stat_Plugin')) { 'index.php', 'toplevel_page_' . self::MENU_SLUG_STATS, 'ansico-stat-plugin_page_' . self::MENU_SLUG_YEARLY, + 'ansico-stat-plugin_page_' . self::MENU_SLUG_SINGLE, 'ansico-stat-plugin_page_' . self::MENU_SLUG_SETTINGS, ], true)) { return; @@ -1962,6 +1991,1146 @@ if (!class_exists('Ansico_Stat_Plugin')) { date_days_ago(29); + + $start = isset($_GET['start_date']) ? $this->sanitize_stats_date((string) wp_unslash($_GET['start_date'])) : ''; + $end = isset($_GET['end_date']) ? $this->sanitize_stats_date((string) wp_unslash($_GET['end_date'])) : ''; + + if ($start === '') { + $start = $default_start; + } + if ($end === '') { + $end = $default_end; + } + if (strtotime($start) > strtotime($end)) { + [$start, $end] = [$end, $start]; + } + + return [ + 'start' => $start, + 'end' => $end, + 'days' => max(1, (int) floor((strtotime($end) - strtotime($start)) / DAY_IN_SECONDS) + 1), + 'is_custom' => isset($_GET['start_date']) || isset($_GET['end_date']), + ]; + } + + protected function build_single_stats_admin_url(string $target_url, string $start_date = '', string $end_date = '') { + $args = [ + 'page' => self::MENU_SLUG_SINGLE, + 'target_url' => $target_url !== '' ? $target_url : home_url('/'), + ]; + if ($start_date !== '') { + $args['start_date'] = $start_date; + } + if ($end_date !== '') { + $args['end_date'] = $end_date; + } + return add_query_arg($args, admin_url('admin.php')); + } + + public function register_frontend_admin_bar_link($wp_admin_bar) { + if (is_admin() || !is_admin_bar_showing() || !current_user_can('manage_options') || !$this->is_trackable_request()) { + return; + } + + $page_context = $this->get_current_page_context(); + if (empty($page_context) || empty($page_context['url'])) { + return; + } + + $target_url = $this->normalize_target_url((string) $page_context['url']); + if ($target_url === '') { + return; + } + + $wp_admin_bar->add_node([ + 'id' => 'ansico-stat-single-page', + 'parent' => 'top-secondary', + 'title' => '' . esc_html__('Page stats', 'ansico-stat-plugin') . '', + 'href' => esc_url($this->build_single_stats_admin_url($target_url)), + 'meta' => [ + 'class' => 'ansico-stat-admin-bar-link', + 'title' => esc_attr__('Open statistics for this page', 'ansico-stat-plugin'), + ], + ]); + } + + protected function resolve_single_stats_target(string $url) { + global $wpdb; + + $normalized_url = $this->normalize_target_url($url); + if ($normalized_url === '') { + return [ + 'success' => false, + 'message' => __('Please enter a valid URL.', 'ansico-stat-plugin'), + 'requested_url' => $url, + 'normalized_url' => '', + ]; + } + + $site_host = strtolower((string) wp_parse_url(home_url('/'), PHP_URL_HOST)); + $target_host = strtolower((string) wp_parse_url($normalized_url, PHP_URL_HOST)); + if ($site_host === '' || $target_host === '' || preg_replace('/^www\./', '', $site_host) !== preg_replace('/^www\./', '', $target_host)) { + return [ + 'success' => false, + 'message' => __('The URL must belong to this WordPress site.', 'ansico-stat-plugin'), + 'requested_url' => $url, + 'normalized_url' => $normalized_url, + ]; + } + + $post_id = url_to_postid($normalized_url); + if ($post_id > 0) { + $post = get_post($post_id); + if ($this->is_trackable_post($post)) { + return [ + 'success' => true, + 'requested_url' => $url, + 'normalized_url' => $normalized_url, + 'target' => [ + 'kind' => 'singular', + 'post_id' => (int) $post_id, + 'post_type' => sanitize_key((string) $post->post_type), + 'page_key' => 'post:' . (int) $post_id, + 'page_type' => sanitize_key((string) $post->post_type), + 'object_id' => (int) $post_id, + 'label' => get_the_title($post_id) ?: __('(no title)', 'ansico-stat-plugin'), + 'url' => get_permalink($post_id) ?: $normalized_url, + 'resolved_via' => 'post', + ], + ]; + } + } + + $front_url = $this->normalize_target_url(home_url('/')); + if ($normalized_url === $front_url) { + return [ + 'success' => true, + 'requested_url' => $url, + 'normalized_url' => $normalized_url, + 'target' => [ + 'kind' => 'archive', + 'page_key' => 'front_page', + 'page_type' => 'front_page', + 'object_id' => 0, + 'label' => __('Front page', 'ansico-stat-plugin'), + 'url' => home_url('/'), + 'resolved_via' => 'front_page', + ], + ]; + } + + $posts_page_id = (int) get_option('page_for_posts'); + if ($posts_page_id > 0) { + $posts_page_url = $this->normalize_target_url((string) get_permalink($posts_page_id)); + if ($posts_page_url !== '' && $normalized_url === $posts_page_url) { + return [ + 'success' => true, + 'requested_url' => $url, + 'normalized_url' => $normalized_url, + 'target' => [ + 'kind' => 'archive', + 'page_key' => 'home', + 'page_type' => 'home', + 'object_id' => $posts_page_id, + 'label' => __('Posts page', 'ansico-stat-plugin'), + 'url' => get_permalink($posts_page_id) ?: $normalized_url, + 'resolved_via' => 'posts_page', + ], + ]; + } + } + + $page_daily_table = self::page_daily_table_name(); + $url_variants = array_values(array_unique(array_filter([ + $normalized_url, + untrailingslashit($normalized_url), + trailingslashit($normalized_url), + ]))); + + if (!empty($url_variants)) { + $placeholders = implode(',', array_fill(0, count($url_variants), '%s')); + $sql = "SELECT page_key, page_type, object_id, page_label, page_url\n FROM {$page_daily_table}\n WHERE page_url IN ({$placeholders})\n ORDER BY stat_date DESC\n LIMIT 1"; + $row = $wpdb->get_row($wpdb->prepare($sql, $url_variants), ARRAY_A); + if (is_array($row) && !empty($row['page_key'])) { + return [ + 'success' => true, + 'requested_url' => $url, + 'normalized_url' => $normalized_url, + 'target' => [ + 'kind' => 'archive', + 'page_key' => (string) $row['page_key'], + 'page_type' => sanitize_key((string) ($row['page_type'] ?? 'archive')), + 'object_id' => !empty($row['object_id']) ? (int) $row['object_id'] : 0, + 'label' => sanitize_text_field((string) ($row['page_label'] ?? $normalized_url)), + 'url' => !empty($row['page_url']) ? esc_url_raw((string) $row['page_url']) : $normalized_url, + 'resolved_via' => 'page_daily', + ], + ]; + } + } + + return [ + 'success' => false, + 'message' => __('No tracked page or post was found for that URL yet. Try opening the page on the site first so the plugin can register it, and then update this report again.', 'ansico-stat-plugin'), + 'requested_url' => $url, + 'normalized_url' => $normalized_url, + ]; + } + + protected function get_target_lifetime_views(array $target) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + return (int) get_post_meta((int) $target['post_id'], self::TOTAL_META_KEY, true); + } + + $table = self::page_daily_table_name(); + $views = $wpdb->get_var($wpdb->prepare( + "SELECT SUM(views) FROM {$table} WHERE page_key = %s", + (string) $target['page_key'] + )); + + return (int) $views; + } + + + protected function get_target_lifetime_unique_visitors(array $target) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $uniques = $wpdb->get_var($wpdb->prepare( + "SELECT SUM(unique_visitors) FROM {$table} WHERE post_id = %d", + (int) $target['post_id'] + )); + return (int) $uniques; + } + + $table = self::page_daily_table_name(); + $uniques = $wpdb->get_var($wpdb->prepare( + "SELECT SUM(unique_visitors) FROM {$table} WHERE page_key = %s", + (string) $target['page_key'] + )); + return (int) $uniques; + } + + protected function get_target_period_unique_visitors(array $target, string $start, string $end) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $uniques = $wpdb->get_var($wpdb->prepare( + "SELECT SUM(unique_visitors) FROM {$table} WHERE post_id = %d AND stat_date BETWEEN %s AND %s", + (int) $target['post_id'], + $start, + $end + )); + return (int) $uniques; + } + + $table = self::page_daily_table_name(); + $uniques = $wpdb->get_var($wpdb->prepare( + "SELECT SUM(unique_visitors) FROM {$table} WHERE page_key = %s AND stat_date BETWEEN %s AND %s", + (string) $target['page_key'], + $start, + $end + )); + return (int) $uniques; + } + + protected function get_target_daily_chart_data_between(array $target, string $start, string $end, string $metric = 'views') { + global $wpdb; + + $metric = $metric === 'unique_visitors' ? 'unique_visitors' : 'views'; + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT stat_date, {$metric} AS metric_value FROM {$table} WHERE post_id = %d AND stat_date BETWEEN %s AND %s ORDER BY stat_date ASC", + (int) $target['post_id'], + $start, + $end + ), ARRAY_A); + } else { + $table = self::page_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT stat_date, {$metric} AS metric_value FROM {$table} WHERE page_key = %s AND stat_date BETWEEN %s AND %s ORDER BY stat_date ASC", + (string) $target['page_key'], + $start, + $end + ), ARRAY_A); + } + + $indexed = []; + foreach ((array) $rows as $row) { + $indexed[(string) $row['stat_date']] = (int) $row['metric_value']; + } + + $data = []; + $cursor = strtotime($start); + $end_ts = strtotime($end); + while ($cursor <= $end_ts) { + $date = gmdate('Y-m-d', $cursor); + $data[] = [ + 'date' => $date, + 'views' => $indexed[$date] ?? 0, + ]; + $cursor = strtotime('+1 day', $cursor); + } + + return $data; + } + + protected function get_target_monthly_chart_data_between(array $target, string $start, string $end) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT DATE_FORMAT(stat_date, '%%Y-%%m') AS month_key, SUM(views) AS total_views FROM {$table} WHERE post_id = %d AND stat_date BETWEEN %s AND %s GROUP BY month_key ORDER BY month_key ASC", + (int) $target['post_id'], + $start, + $end + ), ARRAY_A); + } else { + $table = self::page_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT DATE_FORMAT(stat_date, '%%Y-%%m') AS month_key, SUM(views) AS total_views FROM {$table} WHERE page_key = %s AND stat_date BETWEEN %s AND %s GROUP BY month_key ORDER BY month_key ASC", + (string) $target['page_key'], + $start, + $end + ), ARRAY_A); + } + + $indexed = []; + foreach ((array) $rows as $row) { + $indexed[(string) $row['month_key']] = (int) $row['total_views']; + } + + $data = []; + $cursor = strtotime(gmdate('Y-m-01', strtotime($start))); + $end_month = strtotime(gmdate('Y-m-01', strtotime($end))); + while ($cursor <= $end_month) { + $month_key = gmdate('Y-m', $cursor); + $data[] = [ + 'label' => wp_date('M Y', $cursor), + 'views' => $indexed[$month_key] ?? 0, + ]; + $cursor = strtotime('+1 month', $cursor); + } + + return $data; + } + + protected function get_target_weekday_breakdown_between(array $target, string $start, string $end) { + $daily_data = $this->get_target_daily_chart_data_between($target, $start, $end, 'views'); + $weekday_rows = [ + __('Mon', 'ansico-stat-plugin') => 0, + __('Tue', 'ansico-stat-plugin') => 0, + __('Wed', 'ansico-stat-plugin') => 0, + __('Thu', 'ansico-stat-plugin') => 0, + __('Fri', 'ansico-stat-plugin') => 0, + __('Sat', 'ansico-stat-plugin') => 0, + __('Sun', 'ansico-stat-plugin') => 0, + ]; + $weekday_keys = array_keys($weekday_rows); + + foreach ($daily_data as $row) { + $timestamp = strtotime((string) ($row['date'] ?? '')); + if (!$timestamp) { + continue; + } + $weekday_index = (int) gmdate('N', $timestamp) - 1; + if (isset($weekday_keys[$weekday_index])) { + $weekday_rows[$weekday_keys[$weekday_index]] += (int) ($row['views'] ?? 0); + } + } + + $data = []; + foreach ($weekday_rows as $label => $views) { + $data[] = [ + 'label' => $label, + 'views' => (int) $views, + ]; + } + + return $data; + } + + protected function render_dual_chart_markup(array $views_data, array $unique_data, string $chart_id, string $aria_label = '') { + if ($aria_label === '') { + $aria_label = __('Views and unique visitors chart', 'ansico-stat-plugin'); + } + + $count = max(count($views_data), count($unique_data)); + if ($count < 1) { + return '

' . esc_html__('No chart data available.', 'ansico-stat-plugin') . '

'; + } + + $values = []; + foreach ($views_data as $row) { + $values[] = (int) ($row['views'] ?? 0); + } + foreach ($unique_data as $row) { + $values[] = (int) ($row['views'] ?? 0); + } + $max = max(1, !empty($values) ? max($values) : 1); + $width = 920; + $height = 240; + $chart_top = 20; + $chart_bottom = 185; + $chart_left = 26; + $chart_right = 830; + $stepX = $count > 1 ? ($chart_right - $chart_left) / ($count - 1) : ($chart_right - $chart_left); + + $build_points = static function(array $series, string $series_label) use ($chart_top, $chart_bottom, $chart_left, $stepX, $max) { + $points = []; + foreach (array_values($series) as $index => $row) { + $value = (int) ($row['views'] ?? 0); + $x = $chart_left + ($stepX * $index); + $y = $value > 0 ? $chart_top + (($chart_bottom - $chart_top) * (1 - ($value / $max))) : $chart_bottom; + $label = (string) ($row['label'] ?? $row['date'] ?? ''); + $points[] = [ + 'x' => $x, + 'y' => $y, + 'label' => $label, + 'value' => $value, + 'series_label' => $series_label, + ]; + } + return $points; + }; + + $view_points = $build_points($views_data, __('Views', 'ansico-stat-plugin')); + $unique_points = $build_points($unique_data, __('Unique visitors', 'ansico-stat-plugin')); + $view_path = implode(' ', array_map(static function($point) { + return $point['x'] . ',' . $point['y']; + }, $view_points)); + $unique_path = implode(' ', array_map(static function($point) { + return $point['x'] . ',' . $point['y']; + }, $unique_points)); + + ob_start(); + ?> +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + date_days_ago($days - 1); + $end = current_time('Y-m-d'); + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT stat_date, unique_visitors FROM {$table} WHERE post_id = %d AND stat_date BETWEEN %s AND %s ORDER BY stat_date ASC", + (int) $target['post_id'], + $start, + $end + ), ARRAY_A); + } else { + $table = self::page_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT stat_date, unique_visitors FROM {$table} WHERE page_key = %s AND stat_date BETWEEN %s AND %s ORDER BY stat_date ASC", + (string) $target['page_key'], + $start, + $end + ), ARRAY_A); + } + + $indexed = []; + foreach ((array) $rows as $row) { + $indexed[(string) $row['stat_date']] = (int) $row['unique_visitors']; + } + + $data = []; + $cursor = strtotime($start); + $end_ts = strtotime($end); + while ($cursor <= $end_ts) { + $date = gmdate('Y-m-d', $cursor); + $data[] = [ + 'date' => $date, + 'views' => $indexed[$date] ?? 0, + ]; + $cursor = strtotime('+1 day', $cursor); + } + + return $data; + } + + protected function get_target_period_views(array $target, string $start, string $end) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $views = $wpdb->get_var($wpdb->prepare( + "SELECT SUM(views) FROM {$table} WHERE post_id = %d AND stat_date BETWEEN %s AND %s", + (int) $target['post_id'], + $start, + $end + )); + return (int) $views; + } + + $table = self::page_daily_table_name(); + $views = $wpdb->get_var($wpdb->prepare( + "SELECT SUM(views) FROM {$table} WHERE page_key = %s AND stat_date BETWEEN %s AND %s", + (string) $target['page_key'], + $start, + $end + )); + return (int) $views; + } + + protected function get_target_daily_chart_data(array $target, int $days = 60) { + global $wpdb; + + $days = max(1, $days); + $start = $this->date_days_ago($days - 1); + $end = current_time('Y-m-d'); + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT stat_date, views FROM {$table} WHERE post_id = %d AND stat_date BETWEEN %s AND %s ORDER BY stat_date ASC", + (int) $target['post_id'], + $start, + $end + ), ARRAY_A); + } else { + $table = self::page_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT stat_date, views FROM {$table} WHERE page_key = %s AND stat_date BETWEEN %s AND %s ORDER BY stat_date ASC", + (string) $target['page_key'], + $start, + $end + ), ARRAY_A); + } + + $indexed = []; + foreach ((array) $rows as $row) { + $indexed[(string) $row['stat_date']] = (int) $row['views']; + } + + $data = []; + $cursor = strtotime($start); + $end_ts = strtotime($end); + while ($cursor <= $end_ts) { + $date = gmdate('Y-m-d', $cursor); + $data[] = [ + 'date' => $date, + 'views' => $indexed[$date] ?? 0, + ]; + $cursor = strtotime('+1 day', $cursor); + } + + return $data; + } + + protected function get_target_monthly_chart_data(array $target, int $months = 12) { + global $wpdb; + + $months = max(1, $months); + $current_month = strtotime(wp_date('Y-m-01', current_time('timestamp'))); + $start_month = strtotime('-' . ($months - 1) . ' months', $current_month); + $start = wp_date('Y-m-01', $start_month); + $end = wp_date('Y-m-t', current_time('timestamp')); + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT DATE_FORMAT(stat_date, '%%Y-%%m') AS month_key, SUM(views) AS total_views\n FROM {$table}\n WHERE post_id = %d AND stat_date BETWEEN %s AND %s\n GROUP BY month_key\n ORDER BY month_key ASC", + (int) $target['post_id'], + $start, + $end + ), ARRAY_A); + } else { + $table = self::page_daily_table_name(); + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT DATE_FORMAT(stat_date, '%%Y-%%m') AS month_key, SUM(views) AS total_views\n FROM {$table}\n WHERE page_key = %s AND stat_date BETWEEN %s AND %s\n GROUP BY month_key\n ORDER BY month_key ASC", + (string) $target['page_key'], + $start, + $end + ), ARRAY_A); + } + + $indexed = []; + foreach ((array) $rows as $row) { + $indexed[(string) $row['month_key']] = (int) $row['total_views']; + } + + $data = []; + $cursor = $start_month; + while ($cursor <= $current_month) { + $month_key = wp_date('Y-m', $cursor); + $data[] = [ + 'label' => wp_date('M Y', $cursor), + 'views' => $indexed[$month_key] ?? 0, + ]; + $cursor = strtotime('+1 month', $cursor); + } + + return $data; + } + + protected function get_target_best_day(array $target) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $row = $wpdb->get_row($wpdb->prepare( + "SELECT stat_date, views FROM {$table} WHERE post_id = %d ORDER BY views DESC, stat_date ASC LIMIT 1", + (int) $target['post_id'] + ), ARRAY_A); + } else { + $table = self::page_daily_table_name(); + $row = $wpdb->get_row($wpdb->prepare( + "SELECT stat_date, views FROM {$table} WHERE page_key = %s ORDER BY views DESC, stat_date ASC LIMIT 1", + (string) $target['page_key'] + ), ARRAY_A); + } + + return is_array($row) ? $row : []; + } + + protected function get_target_first_tracked_date(array $target) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $date = $wpdb->get_var($wpdb->prepare( + "SELECT MIN(stat_date) FROM {$table} WHERE post_id = %d", + (int) $target['post_id'] + )); + } else { + $table = self::page_daily_table_name(); + $date = $wpdb->get_var($wpdb->prepare( + "SELECT MIN(stat_date) FROM {$table} WHERE page_key = %s", + (string) $target['page_key'] + )); + } + + return is_string($date) ? $date : ''; + } + + + protected function get_target_total_tracked_days(array $target) { + global $wpdb; + + if (($target['kind'] ?? '') === 'singular') { + $table = self::post_daily_table_name(); + $count = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM {$table} WHERE post_id = %d", + (int) $target['post_id'] + )); + } else { + $table = self::page_daily_table_name(); + $count = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM {$table} WHERE page_key = %s", + (string) $target['page_key'] + )); + } + + return (int) $count; + } + + + protected function get_target_average_unique_visitors_per_day(array $target, int $days) { + $days = max(1, $days); + $uniques = $this->get_target_period_unique_visitors($target, $this->date_days_ago($days - 1), current_time('Y-m-d')); + return $uniques / $days; + } + + protected function get_target_unique_period_comparison(array $target, int $days) { + $days = max(1, $days); + $current_end = current_time('Y-m-d'); + $current_start = $this->date_days_ago($days - 1); + $previous_end = $this->date_days_ago($days); + $previous_start = $this->date_days_ago(($days * 2) - 1); + + $current_uniques = $this->get_target_period_unique_visitors($target, $current_start, $current_end); + $previous_uniques = $this->get_target_period_unique_visitors($target, $previous_start, $previous_end); + $change = $current_uniques - $previous_uniques; + $percent_change = null; + if ($previous_uniques > 0) { + $percent_change = (($current_uniques - $previous_uniques) / $previous_uniques) * 100; + } elseif ($current_uniques > 0) { + $percent_change = 100.0; + } + + return [ + 'days' => $days, + 'current_views' => (int) $current_uniques, + 'previous_views' => (int) $previous_uniques, + 'change' => (int) $change, + 'percent_change' => $percent_change, + 'current_start' => $current_start, + 'current_end' => $current_end, + 'previous_start' => $previous_start, + 'previous_end' => $previous_end, + ]; + } + + protected function get_target_average_views_per_day(array $target, int $days) { + $days = max(1, $days); + $views = $this->get_target_period_views($target, $this->date_days_ago($days - 1), current_time('Y-m-d')); + return $views / $days; + } + + protected function get_target_period_comparison(array $target, int $days) { + $days = max(1, $days); + $current_end = current_time('Y-m-d'); + $current_start = $this->date_days_ago($days - 1); + $previous_end = $this->date_days_ago($days); + $previous_start = $this->date_days_ago(($days * 2) - 1); + + $current_views = $this->get_target_period_views($target, $current_start, $current_end); + $previous_views = $this->get_target_period_views($target, $previous_start, $previous_end); + $change = $current_views - $previous_views; + $percent_change = null; + if ($previous_views > 0) { + $percent_change = (($current_views - $previous_views) / $previous_views) * 100; + } elseif ($current_views > 0) { + $percent_change = 100.0; + } + + return [ + 'days' => $days, + 'current_views' => (int) $current_views, + 'previous_views' => (int) $previous_views, + 'change' => (int) $change, + 'percent_change' => $percent_change, + 'current_start' => $current_start, + 'current_end' => $current_end, + 'previous_start' => $previous_start, + 'previous_end' => $previous_end, + ]; + } + + protected function format_change_percent($percent_change) { + if ($percent_change === null) { + return __('New data', 'ansico-stat-plugin'); + } + + $prefix = $percent_change > 0 ? '+' : ''; + return $prefix . number_format_i18n((float) $percent_change, 1) . '%'; + } + + protected function get_target_weekday_breakdown(array $target, int $days = 90) { + $days = max(1, $days); + $daily_data = $this->get_target_daily_chart_data($target, $days); + $weekday_rows = [ + __('Mon', 'ansico-stat-plugin') => 0, + __('Tue', 'ansico-stat-plugin') => 0, + __('Wed', 'ansico-stat-plugin') => 0, + __('Thu', 'ansico-stat-plugin') => 0, + __('Fri', 'ansico-stat-plugin') => 0, + __('Sat', 'ansico-stat-plugin') => 0, + __('Sun', 'ansico-stat-plugin') => 0, + ]; + $weekday_keys = array_keys($weekday_rows); + + foreach ($daily_data as $row) { + $timestamp = strtotime((string) ($row['date'] ?? '')); + if (!$timestamp) { + continue; + } + $weekday_index = (int) gmdate('N', $timestamp) - 1; + if (!isset($weekday_keys[$weekday_index])) { + continue; + } + $weekday_rows[$weekday_keys[$weekday_index]] += (int) ($row['views'] ?? 0); + } + + $data = []; + foreach ($weekday_rows as $label => $views) { + $data[] = [ + 'label' => $label, + 'views' => (int) $views, + ]; + } + + return $data; + } + + protected function build_single_export_url(string $target_url, string $start_date = '', string $end_date = '') { + $args = [ + 'action' => 'ansico_stat_export_single_csv', + 'target_url' => $target_url, + ]; + if ($start_date !== '') { + $args['start_date'] = $start_date; + } + if ($end_date !== '') { + $args['end_date'] = $end_date; + } + + return wp_nonce_url( + add_query_arg($args, admin_url('admin-post.php')), + 'ansico_stat_export_single_csv' + ); + } + + protected function render_single_stats_summary_cards(array $items) { + echo '
'; + foreach ($items as $item) { + echo '
'; + echo '
' . esc_html((string) ($item['label'] ?? '')) . '
'; + echo '
' . esc_html((string) ($item['value'] ?? '')) . '
'; + if (!empty($item['description'])) { + echo '
' . esc_html((string) $item['description']) . '
'; + } + echo '
'; + } + echo '
'; + } + + public function render_single_page_stats_page() { + if (!current_user_can('manage_options')) { + wp_die(esc_html__('You do not have permission to access this page.', 'ansico-stat-plugin')); + } + + $requested_url = $this->get_requested_single_stats_url(); + $date_range = $this->get_single_stats_date_range(); + $range_start = (string) $date_range['start']; + $range_end = (string) $date_range['end']; + $range_days = (int) $date_range['days']; + $result = $this->resolve_single_stats_target($requested_url); + $target = !empty($result['success']) ? (array) $result['target'] : []; + $daily_chart = !empty($target) ? $this->get_target_daily_chart_data_between($target, $range_start, $range_end, 'views') : []; + $unique_daily_chart = !empty($target) ? $this->get_target_daily_chart_data_between($target, $range_start, $range_end, 'unique_visitors') : []; + $monthly_chart = !empty($target) ? $this->get_target_monthly_chart_data_between($target, $range_start, $range_end) : []; + $weekday_chart = !empty($target) ? $this->get_target_weekday_breakdown_between($target, $range_start, $range_end) : []; + $lifetime_views = !empty($target) ? $this->get_target_lifetime_views($target) : 0; + $lifetime_unique_visitors = !empty($target) ? $this->get_target_lifetime_unique_visitors($target) : 0; + $views_today = !empty($target) ? $this->get_target_period_views($target, current_time('Y-m-d'), current_time('Y-m-d')) : 0; + $unique_today = !empty($target) ? $this->get_target_period_unique_visitors($target, current_time('Y-m-d'), current_time('Y-m-d')) : 0; + $range_views = !empty($target) ? $this->get_target_period_views($target, $range_start, $range_end) : 0; + $range_unique = !empty($target) ? $this->get_target_period_unique_visitors($target, $range_start, $range_end) : 0; + $best_day = !empty($target) ? $this->get_target_best_day($target) : []; + $first_tracked = !empty($target) ? $this->get_target_first_tracked_date($target) : ''; + $tracked_days = !empty($target) ? $this->get_target_total_tracked_days($target) : 0; + $avg_range = $range_days > 0 ? ($range_views / $range_days) : 0; + $avg_unique_range = $range_days > 0 ? ($range_unique / $range_days) : 0; + $compare_range = !empty($target) ? $this->get_target_period_comparison($target, $range_days) : []; + $unique_compare_range = !empty($target) ? $this->get_target_unique_period_comparison($target, $range_days) : []; + $views_7 = !empty($target) ? $this->get_target_period_views($target, $this->date_days_ago(6), current_time('Y-m-d')) : 0; + $views_30 = !empty($target) ? $this->get_target_period_views($target, $this->date_days_ago(29), current_time('Y-m-d')) : 0; + $unique_7 = !empty($target) ? $this->get_target_period_unique_visitors($target, $this->date_days_ago(6), current_time('Y-m-d')) : 0; + $unique_30 = !empty($target) ? $this->get_target_period_unique_visitors($target, $this->date_days_ago(29), current_time('Y-m-d')) : 0; + $export_url = !empty($target) ? $this->build_single_export_url($requested_url, $range_start, $range_end) : ''; + $reset_range_url = $this->build_single_stats_admin_url($requested_url); + $post_type_label = ''; + if (!empty($target) && ($target['kind'] ?? '') === 'singular') { + $post_type_obj = get_post_type_object((string) ($target['post_type'] ?? '')); + $post_type_label = $post_type_obj && !empty($post_type_obj->labels->singular_name) ? (string) $post_type_obj->labels->singular_name : (string) ($target['post_type'] ?? ''); + } + ?> +
+

+

+ +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+

+
+
+ + +
+

+
+ +
+
+
+

+

+

+
+
+ + +
+
+ render_single_stats_summary_cards([ + [ + 'label' => __('Views in selected period', 'ansico-stat-plugin'), + 'value' => number_format_i18n($range_views), + 'description' => sprintf(__('Avg. %s per day', 'ansico-stat-plugin'), number_format_i18n($avg_range, 1)), + ], + [ + 'label' => __('Unique visitors in selected period', 'ansico-stat-plugin'), + 'value' => number_format_i18n($range_unique), + 'description' => sprintf(__('Avg. %s per day', 'ansico-stat-plugin'), number_format_i18n($avg_unique_range, 1)), + ], + [ + 'label' => __('Views today', 'ansico-stat-plugin'), + 'value' => number_format_i18n($views_today), + 'description' => __('Today only', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Unique visitors today', 'ansico-stat-plugin'), + 'value' => number_format_i18n($unique_today), + 'description' => __('Today only', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Views last 7 days', 'ansico-stat-plugin'), + 'value' => number_format_i18n($views_7), + 'description' => __('Rolling 7-day total', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Unique visitors last 7 days', 'ansico-stat-plugin'), + 'value' => number_format_i18n($unique_7), + 'description' => __('Rolling 7-day total', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Views last 30 days', 'ansico-stat-plugin'), + 'value' => number_format_i18n($views_30), + 'description' => __('Rolling 30-day total', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Unique visitors last 30 days', 'ansico-stat-plugin'), + 'value' => number_format_i18n($unique_30), + 'description' => __('Rolling 30-day total', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Lifetime views', 'ansico-stat-plugin'), + 'value' => number_format_i18n($lifetime_views), + 'description' => $first_tracked !== '' ? sprintf(__('Tracked since %s', 'ansico-stat-plugin'), wp_date(get_option('date_format'), strtotime($first_tracked))) : '', + ], + [ + 'label' => __('Lifetime unique visitors', 'ansico-stat-plugin'), + 'value' => number_format_i18n($lifetime_unique_visitors), + 'description' => __('Stored from version 1.0.0.6 and forward', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Best day', 'ansico-stat-plugin'), + 'value' => !empty($best_day['views']) ? number_format_i18n((int) $best_day['views']) : '0', + 'description' => !empty($best_day['stat_date']) ? wp_date(get_option('date_format'), strtotime((string) $best_day['stat_date'])) : __('No tracked data yet', 'ansico-stat-plugin'), + ], + [ + 'label' => __('Tracked days with views', 'ansico-stat-plugin'), + 'value' => number_format_i18n($tracked_days), + 'description' => __('Days stored in the daily statistics table', 'ansico-stat-plugin'), + ], + ]); ?> +

+
+ +
+

+ render_dual_chart_markup($daily_chart, $unique_daily_chart, 'ansico-single-target-dual-chart', __('Views and unique visitors for selected page', 'ansico-stat-plugin')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
+ +
+

+ render_chart_markup($daily_chart, 'ansico-single-target-daily-chart'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
+ +
+

+ render_chart_markup($unique_daily_chart, 'ansico-single-target-unique-daily-chart'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
+ +
+

+ render_bar_chart_markup($monthly_chart, 'ansico-single-target-monthly-chart', __('Monthly totals for selected page', 'ansico-stat-plugin')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
+ +
+

+ render_bar_chart_markup($weekday_chart, 'ansico-single-target-weekday-chart', __('Views by weekday for selected page', 'ansico-stat-plugin')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> +
+ +
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
format_change_percent($compare_range['percent_change'] ?? null)); ?>
format_change_percent($unique_compare_range['percent_change'] ?? null)); ?>
+
+ +
+ resolve_single_stats_target($requested_url); + if (empty($result['success']) || empty($result['target'])) { + wp_die(esc_html__('No tracked page could be resolved for export.', 'ansico-stat-plugin')); + } + + $target = (array) $result['target']; + $start_date = isset($_GET['start_date']) ? $this->sanitize_stats_date((string) wp_unslash($_GET['start_date'])) : ''; + $end_date = isset($_GET['end_date']) ? $this->sanitize_stats_date((string) wp_unslash($_GET['end_date'])) : ''; + if ($start_date === '') { + $start_date = $this->date_days_ago(29); + } + if ($end_date === '') { + $end_date = current_time('Y-m-d'); + } + if (strtotime($start_date) > strtotime($end_date)) { + [$start_date, $end_date] = [$end_date, $start_date]; + } + $daily_rows = $this->get_target_daily_chart_data_between($target, $start_date, $end_date, 'views'); + $range_days = max(1, (int) floor((strtotime($end_date) - strtotime($start_date)) / DAY_IN_SECONDS) + 1); + $compare_7 = $this->get_target_period_comparison($target, 7); + $compare_30 = $this->get_target_period_comparison($target, 30); + $compare_range = $this->get_target_period_comparison($target, $range_days); + $unique_compare_7 = $this->get_target_unique_period_comparison($target, 7); + $unique_compare_30 = $this->get_target_unique_period_comparison($target, 30); + $unique_compare_range = $this->get_target_unique_period_comparison($target, $range_days); + $filename_slug = sanitize_title((string) ($target['label'] ?? 'page-stats')); + if ($filename_slug === '') { + $filename_slug = 'page-stats'; + } + $filename = 'ansico-single-page-' . $filename_slug . '-' . wp_date('Y-m-d-H-i-s') . '.csv'; + + nocache_headers(); + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename=' . $filename); + + $output = fopen('php://output', 'w'); + fputcsv($output, ['Selected URL', (string) ($target['url'] ?? '')]); + fputcsv($output, ['Label', (string) ($target['label'] ?? '')]); + fputcsv($output, ['Type', (string) (($target['kind'] ?? '') === 'singular' ? ($target['post_type'] ?? 'post') : ($target['page_type'] ?? 'page'))]); + fputcsv($output, ['Selected period start', $start_date]); + fputcsv($output, ['Selected period end', $end_date]); + fputcsv($output, ['Selected period views', (int) $this->get_target_period_views($target, $start_date, $end_date)]); + fputcsv($output, ['Selected period unique visitors', (int) $this->get_target_period_unique_visitors($target, $start_date, $end_date)]); + fputcsv($output, ['Previous comparable period views', (int) ($compare_range['previous_views'] ?? 0)]); + fputcsv($output, ['Previous comparable period unique visitors', (int) ($unique_compare_range['previous_views'] ?? 0)]); + fputcsv($output, ['Lifetime views', (int) $this->get_target_lifetime_views($target)]); + fputcsv($output, ['Views today', (int) $this->get_target_period_views($target, current_time('Y-m-d'), current_time('Y-m-d'))]); + fputcsv($output, ['Unique visitors today', (int) $this->get_target_period_unique_visitors($target, current_time('Y-m-d'), current_time('Y-m-d'))]); + fputcsv($output, ['Views last 7 days', (int) ($compare_7['current_views'] ?? 0)]); + fputcsv($output, ['Unique visitors last 7 days', (int) ($unique_compare_7['current_views'] ?? 0)]); + fputcsv($output, ['Previous 7 days', (int) ($compare_7['previous_views'] ?? 0)]); + fputcsv($output, ['Previous unique visitors 7 days', (int) ($unique_compare_7['previous_views'] ?? 0)]); + fputcsv($output, ['Views last 30 days', (int) ($compare_30['current_views'] ?? 0)]); + fputcsv($output, ['Unique visitors last 30 days', (int) ($unique_compare_30['current_views'] ?? 0)]); + fputcsv($output, ['Previous 30 days', (int) ($compare_30['previous_views'] ?? 0)]); + fputcsv($output, ['Previous unique visitors 30 days', (int) ($unique_compare_30['previous_views'] ?? 0)]); + fputcsv($output, []); + fputcsv($output, ['Date', 'Views', 'Unique visitors']); + foreach ($daily_rows as $row) { + fputcsv($output, [ + (string) ($row['date'] ?? ''), + (int) ($row['views'] ?? 0), + (int) $this->get_target_period_unique_visitors($target, (string) ($row['date'] ?? ''), (string) ($row['date'] ?? '')), + ]); + } + fclose($output); + exit; + } + public function handle_export_csv() { if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to export statistics.', 'ansico-stat-plugin')); diff --git a/ansico-stat-plugin/assets/css/ansico-stat-admin.css b/ansico-stat-plugin/assets/css/ansico-stat-admin.css index 9c3ef4f..d3f56ca 100644 --- a/ansico-stat-plugin/assets/css/ansico-stat-admin.css +++ b/ansico-stat-plugin/assets/css/ansico-stat-admin.css @@ -351,3 +351,114 @@ box-shadow: 0 4px 16px rgba(0,0,0,.15); max-width: min(260px, calc(100% - 16px)); } + + +.ansico-stat-url-form-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 12px; + align-items: end; +} + +.ansico-stat-url-field { + min-width: 0; +} + +.ansico-stat-url-action .button { + min-width: 110px; +} + +.ansico-stat-summary-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 14px; + margin-top: 16px; +} + +.ansico-stat-summary-card { + border: 1px solid #dcdcde; + border-radius: 8px; + padding: 14px; + background: #f9f9f9; +} + +.ansico-stat-summary-label { + font-size: 12px; + line-height: 1.4; + color: #50575e; + margin-bottom: 6px; +} + +.ansico-stat-summary-value { + font-size: 28px; + line-height: 1.2; + font-weight: 600; +} + +.ansico-stat-summary-description { + font-size: 12px; + line-height: 1.4; + color: #50575e; + margin-top: 6px; +} + +.ansico-stat-target-badge { + display: inline-flex; + align-items: center; + padding: 6px 10px; + border-radius: 999px; + background: #eef4fb; + color: #1d4f82; + font-size: 12px; + font-weight: 600; +} + +@media (max-width: 782px) { + .ansico-stat-url-form-row { + grid-template-columns: 1fr; + } +} + + +.ansico-stat-url-form-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 160px 160px auto; + gap: 12px; + align-items: end; +} + +.ansico-stat-date-field input { + width: 100%; +} + +.ansico-stat-dual-chart-legend { + display: flex; + gap: 18px; + flex-wrap: wrap; + margin-top: 12px; + font-size: 13px; +} + +.ansico-stat-dual-chart-legend span { + display: inline-flex; + align-items: center; + gap: 8px; +} + +.ansico-stat-dual-chart-swatch { + display: inline-block; + width: 22px; + height: 0; + border-top: 3px solid #2271b1; +} + +.ansico-stat-dual-chart-swatch-unique { + border-top-color: #8c8f94; + border-top-style: dashed; +} + +@media (max-width: 960px) { + .ansico-stat-url-form-row { + grid-template-columns: 1fr; + } +} diff --git a/ansico-stat-plugin/readme.txt b/ansico-stat-plugin/readme.txt index 18e6a2e..6c6531e 100644 --- a/ansico-stat-plugin/readme.txt +++ b/ansico-stat-plugin/readme.txt @@ -5,7 +5,7 @@ Tags: analytics, statistics, views, post views, dashboard Requires at least: 6.0 Tested up to: 6.9.4 Requires PHP: 7.4 -Stable tag: 1.0.0 +Stable tag: 1.1.0.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -27,6 +27,7 @@ Features include: * Device, browser, and operating system breakdowns * Bot exclusion and logged-in user exclusion rules * Reset statistics from the settings page +* Single page statistics page with URL-based lookup, date range filters, per-page charts, unique visitor tracking from version 1.0.0.6 forward, and a frontend admin bar shortcut Support and documentation: https://ansico.dk/Ansico/Ansico-Stat-plugin @@ -58,6 +59,28 @@ https://ansico.dk/Ansico/Ansico-Stat-plugin == Changelog == += 1.1.0.1 = +- Added visible data points to the combined views and unique visitors chart. +- Added hover tooltips to the combined chart so each point can be read directly. +- Updated the plugin version to 1.1.0.1. + += 1.0.0.7 = +* Added date range filters and a combined views/unique visitors chart on the single page statistics screen. +* Added a frontend admin bar shortcut that opens statistics for the current page directly in wp-admin. +* Updated single-page CSV export to include the selected date range. + += 1.0.0.6 = +* Added true per-page unique visitor tracking stored per day for posts, pages, and tracked non-singular pages. +* Extended the single page statistics screen and CSV export with unique visitor metrics and charts. + += 1.0.0.5 = +* Expanded the single page statistics screen with more page-specific KPIs, weekday distribution, and period-over-period comparisons. +* Added CSV export for the selected page URL so the report can be downloaded directly from the single page statistics screen. + += 1.0.0.4 = +* Added a single page statistics admin page where you can enter a site URL and view statistics for that specific tracked page or post. +* Added per-page daily and monthly charts plus lifetime and recent-period summaries. + = 1.0.0 = * First public release. * Added tooltips to line, bar, and pie charts. @@ -66,5 +89,17 @@ https://ansico.dk/Ansico/Ansico-Stat-plugin == Upgrade Notice == += 1.0.0.7 = +Adds custom date ranges, a combined page chart, and a frontend admin bar shortcut for the current page report. + += 1.0.0.6 = +Adds true per-page unique visitor tracking for new data and shows those metrics on the single page statistics screen. + += 1.0.0.5 = +Adds more page-specific KPIs and CSV export on the single page statistics screen. + += 1.0.0.4 = +Adds a single page statistics screen with URL-based lookup. + = 1.0.0 = First public release.