Changeset 3378632
- Timestamp:
- 10/15/2025 06:39:31 AM (2 months ago)
- Location:
- koko-analytics
- Files:
-
- 14 added
- 2 deleted
- 44 edited
- 1 copied
-
tags/2.0.19 (copied) (copied from koko-analytics/trunk)
-
tags/2.0.19/CHANGELOG.md (modified) (1 diff)
-
tags/2.0.19/assets/dist/js/script.js (modified) (1 diff)
-
tags/2.0.19/data/referrer-blocklist (modified) (2 diffs)
-
tags/2.0.19/koko-analytics.php (modified) (6 diffs)
-
tags/2.0.19/readme.txt (modified) (3 diffs)
-
tags/2.0.19/src/Admin/Actions.php (modified) (1 diff)
-
tags/2.0.19/src/Admin/Admin.php (modified) (3 diffs)
-
tags/2.0.19/src/Admin/Data_Export.php (modified) (2 diffs)
-
tags/2.0.19/src/Dashboard.php (modified) (3 diffs)
-
tags/2.0.19/src/Dashboard_Standalone.php (added)
-
tags/2.0.19/src/Dashboard_Widget.php (modified) (2 diffs)
-
tags/2.0.19/src/Import (added)
-
tags/2.0.19/src/Import/Importer.php (added)
-
tags/2.0.19/src/Import/Jetpack_Importer.php (added)
-
tags/2.0.19/src/Import/Plausible_Importer.php (added)
-
tags/2.0.19/src/Jetpack_Importer.php (deleted)
-
tags/2.0.19/src/Pageview_Aggregator.php (modified) (3 diffs)
-
tags/2.0.19/src/Referrer_Repository.php (added)
-
tags/2.0.19/src/Resources/functions/functions.php (modified) (1 diff)
-
tags/2.0.19/src/Resources/functions/global.php (modified) (1 diff)
-
tags/2.0.19/src/Resources/views/dashboard-page.php (modified) (7 diffs)
-
tags/2.0.19/src/Resources/views/dashboard-widget.php (modified) (2 diffs)
-
tags/2.0.19/src/Resources/views/nav.php (modified) (2 diffs)
-
tags/2.0.19/src/Resources/views/settings-page.php (modified) (5 diffs)
-
tags/2.0.19/src/Resources/views/standalone.php (modified) (2 diffs)
-
tags/2.0.19/src/Rest.php (modified) (5 diffs)
-
tags/2.0.19/src/Router.php (added)
-
tags/2.0.19/src/Script_Loader.php (modified) (5 diffs)
-
tags/2.0.19/src/Shortcodes/Shortcode_Site_Counter.php (modified) (1 diff)
-
tags/2.0.19/src/Stats.php (modified) (8 diffs)
-
trunk/CHANGELOG.md (modified) (1 diff)
-
trunk/assets/dist/js/script.js (modified) (1 diff)
-
trunk/data/referrer-blocklist (modified) (2 diffs)
-
trunk/koko-analytics.php (modified) (6 diffs)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/src/Admin/Actions.php (modified) (1 diff)
-
trunk/src/Admin/Admin.php (modified) (3 diffs)
-
trunk/src/Admin/Data_Export.php (modified) (2 diffs)
-
trunk/src/Dashboard.php (modified) (3 diffs)
-
trunk/src/Dashboard_Standalone.php (added)
-
trunk/src/Dashboard_Widget.php (modified) (2 diffs)
-
trunk/src/Import (added)
-
trunk/src/Import/Importer.php (added)
-
trunk/src/Import/Jetpack_Importer.php (added)
-
trunk/src/Import/Plausible_Importer.php (added)
-
trunk/src/Jetpack_Importer.php (deleted)
-
trunk/src/Pageview_Aggregator.php (modified) (3 diffs)
-
trunk/src/Referrer_Repository.php (added)
-
trunk/src/Resources/functions/functions.php (modified) (1 diff)
-
trunk/src/Resources/functions/global.php (modified) (1 diff)
-
trunk/src/Resources/views/dashboard-page.php (modified) (7 diffs)
-
trunk/src/Resources/views/dashboard-widget.php (modified) (2 diffs)
-
trunk/src/Resources/views/nav.php (modified) (2 diffs)
-
trunk/src/Resources/views/settings-page.php (modified) (5 diffs)
-
trunk/src/Resources/views/standalone.php (modified) (2 diffs)
-
trunk/src/Rest.php (modified) (5 diffs)
-
trunk/src/Router.php (added)
-
trunk/src/Script_Loader.php (modified) (5 diffs)
-
trunk/src/Shortcodes/Shortcode_Site_Counter.php (modified) (1 diff)
-
trunk/src/Stats.php (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
koko-analytics/tags/2.0.19/CHANGELOG.md
r3366958 r3378632 1 1 # Changelog 2 3 ### 2.0.19 - Oct 15, 2025 4 5 - Print (< 500 bytes) tracking script inline in page HTML to save on an additional HTTP request and resolve overly aggressive cache issues. 6 - Add importer for Plausible. 7 - Change public dashboard URL to `/koko-analytics-dashboard/` if pretty permalinks are enabled. 8 - Exclude visits to post previews. 9 2 10 3 11 ### 2.0.18 - Sep 24, 2025 -
koko-analytics/tags/2.0.19/assets/dist/js/script.js
r3362953 r3378632 1 /*! For license information please see script.js.LICENSE.txt */ 2 !function(){var e=window,t="koko_analytics";e[t].trackPageview=function(){if("prerender"!=document.visibilityState&&!/bot|crawl|spider|seo|lighthouse|facebookexternalhit|preview/i.test(navigator.userAgent)){var a=e[t].path;a||(a=window.location.pathname+window.location.search);var o,r=0==document.referrer.indexOf(e[t].site_url)?"":document.referrer;o={pa:a,po:e[t].post_id,r:r},e[t].use_cookie?o.m="c":e[t].method&&(o.m=e[t].method[0]),navigator.sendBeacon(e[t].url,new URLSearchParams(o))}},e.addEventListener("load",function(){e[t].trackPageview()})}(); 1 !function(){var e=window,r="koko_analytics";function t(t){t.m=e[r].use_cookie?"c":e[r].method[0],navigator.sendBeacon(e[r].url,new URLSearchParams(t))}e[r].request=t,e[r].trackPageview=function(){if("prerender"!=document.visibilityState&&!/bot|crawl|spider|seo|lighthouse|facebookexternalhit|preview/i.test(navigator.userAgent)){var i=0==document.referrer.indexOf(e[r].site_url)?"":document.referrer;t({pa:e[r].path,po:e[r].post_id,r:i})}},e.addEventListener("load",function(){e[r].trackPageview()})}(); -
koko-analytics/tags/2.0.19/data/referrer-blocklist
r3359615 r3378632 841 841 getrichquick.ml 842 842 getrichquickly.info 843 gevciamst.online 843 844 gezlev.com.ua 844 845 ghazel.ru … … 1507 1508 pfrf-kabinet.ru 1508 1509 pharm--shop.ru 1510 phimarshcer.online 1509 1511 phimmakinhdi.com 1510 1512 photo-clip.ru -
koko-analytics/tags/2.0.19/koko-analytics.php
r3366958 r3378632 4 4 Plugin Name: Koko Analytics 5 5 Plugin URI: https://www.kokoanalytics.com/#utm_source=wp-plugin&utm_medium=koko-analytics&utm_campaign=plugins-page 6 Version: 2.0.1 86 Version: 2.0.19 7 7 Description: Privacy-friendly and efficient statistics for your WordPress site. 8 8 Author: ibericode … … 39 39 use KokoAnalytics\Widgets\Most_Viewed_Posts_Widget; 40 40 41 \define('KOKO_ANALYTICS_VERSION', '2.0.1 8');41 \define('KOKO_ANALYTICS_VERSION', '2.0.19'); 42 42 \define('KOKO_ANALYTICS_PLUGIN_FILE', __FILE__); 43 43 \define('KOKO_ANALYTICS_PLUGIN_DIR', __DIR__); … … 72 72 73 73 // script loader 74 add_action('wp_enqueue_scripts', [Script_Loader::class, 'maybe_enqueue_script'], 10, 0); 74 add_action('wp_head', [ Script_Loader::class , 'print_js_object' ], 1, 0); 75 add_action('wp_footer', [Script_Loader::class, 'maybe_print_script'], 10, 0); 75 76 add_action('amp_print_analytics', [Script_Loader::class, 'print_amp_analytics_tag'], 10, 0); 76 77 add_action('admin_bar_menu', [Admin\Bar::class, 'register'], 40, 1); … … 108 109 // maybe show standalone dashboard 109 110 add_action('wp', function () { 110 if (! isset($_GET['koko-analytics-dashboard'])) {111 if (!Router::is('dashboard-standalone')) { 111 112 return; 112 113 } … … 122 123 } 123 124 124 (new Dashboard())->show_standalone_dashboard_page(); 125 (new Dashboard_Standalone())->show(); 126 exit; 125 127 }, 10, 0); 126 128 … … 134 136 } 135 137 136 // on plugin update (but using old code) 137 // this breaks in 2.x because of the new file structure 138 // TODO: Reactivate once 2.x stabilises 139 // add_filter('upgrader_process_complete', function () { 140 // do_action('koko_analytics_aggregate_stats'); 141 // }); 138 // on plugin update (but using old code that's already in memory) 139 add_filter('upgrader_process_complete', function () { 140 do_action('koko_analytics_aggregate_stats'); 141 }); 142 142 143 143 // on plugin activation -
koko-analytics/tags/2.0.19/readme.txt
r3373525 r3378632 5 5 Requires at least: 6.0 6 6 Tested up to: 6.8 7 Stable tag: 2.0.1 87 Stable tag: 2.0.19 8 8 License: GPL-3.0-or-later 9 9 License URI: http://www.gnu.org/licenses/gpl-3.0.html … … 132 132 == Changelog == 133 133 134 ### 2.0.19 - Oct 15, 2025 135 136 - Print (< 500 bytes) tracking script inline in page HTML to save on an additional HTTP request and resolve overly aggressive cache issues. 137 - Add importer for Plausible. 138 - Change public dashboard URL to `/koko-analytics-dashboard/` if pretty permalinks are enabled. 139 - Exclude visits to post previews. 140 141 134 142 ### 2.0.18 - Sep 24, 2025 135 143 … … 444 452 #### 1.4.4 - Nov 4, 2024 445 453 446 - Add Jetpack Stats importer to import your historical analytics data into Koko Analytics. Go to the settings page (with Jetpack still enabled) to access it. 447 - Fix settings page showing proxy IP instead of client IP if using reverse proxy. 448 - Fix use of PHP 7.4 only feature in thousands separator in source ... 449 454 - Add Jetpack Stats ... 455 -
koko-analytics/tags/2.0.19/src/Admin/Actions.php
r3366958 r3378632 193 193 194 194 do { 195 $results = $wpdb->get_results($wpdb->prepare("SELECT post_id, path_id, p.path FROM {$wpdb->prefix}koko_analytics_post_stats s LEFT JOIN {$wpdb->prefix}koko_analytics_paths p ON p.id = s.path_id WHERE post_id IS NOT NULL AND post_id != 0 AND date <= '2025-08-29'GROUP BY post_id LIMIT %d OFFSET %d", [$limit, $offset]));195 $results = $wpdb->get_results($wpdb->prepare("SELECT post_id, path_id, p.path FROM {$wpdb->prefix}koko_analytics_post_stats s LEFT JOIN {$wpdb->prefix}koko_analytics_paths p ON p.id = s.path_id WHERE post_id IS NOT NULL AND post_id != 0 GROUP BY post_id LIMIT %d OFFSET %d", [$limit, $offset])); 196 196 $offset += $limit; 197 197 if (!$results) { -
koko-analytics/tags/2.0.19/src/Admin/Admin.php
r3366958 r3378632 9 9 namespace KokoAnalytics\Admin; 10 10 11 use KokoAnalytics\Jetpack_Importer; 11 use KokoAnalytics\Import\Jetpack_Importer; 12 use KokoAnalytics\Import\Plausible_Importer; 13 use KokoAnalytics\Router; 12 14 13 15 class Admin … … 41 43 add_action('koko_analytics_start_jetpack_import', [Jetpack_Importer::class, 'start_import'], 10, 0); 42 44 add_action('koko_analytics_jetpack_import_chunk', [Jetpack_Importer::class, 'import_chunk'], 10, 0); 45 46 // actions for plausible importer 47 add_action('koko_analytics_show_plausible_importer_page', [Plausible_Importer::class, 'show_page'], 10, 0); 48 add_action('koko_analytics_start_plausible_import', [Plausible_Importer::class, 'start_import'], 10, 0); 43 49 } 44 50 … … 58 64 public function add_plugin_settings_link($links): array 59 65 { 60 $href = admin_url('index.php?page=koko-analytics&tab=settings');66 $href = Router::url('settings-page'); 61 67 $label = esc_html__('Settings', 'koko-analytics'); 62 68 $settings_link = "<a href=\"{$href}\">{$label}</a>"; -
koko-analytics/tags/2.0.19/src/Admin/Data_Export.php
r3366958 r3378632 9 9 namespace KokoAnalytics\Admin; 10 10 11 use function KokoAnalytics\create_local_datetime;11 use DateTimeImmutable; 12 12 13 13 class Data_Export … … 36 36 { 37 37 // write to HTTP stream 38 $date = create_local_datetime('now')->format("Y-m-d");38 $date = (new DateTimeImmutable('now', wp_timezone()))->format("Y-m-d"); 39 39 $site_url = parse_url(get_site_url(), PHP_URL_HOST); 40 40 -
koko-analytics/tags/2.0.19/src/Dashboard.php
r3362953 r3378632 9 9 namespace KokoAnalytics; 10 10 11 use DateTimeImmutable; 12 11 13 class Dashboard 12 14 { 13 public function show_standalone_dashboard_page(): void 14 { 15 require KOKO_ANALYTICS_PLUGIN_DIR . '/src/Resources/views/standalone.php'; 16 exit; 17 } 18 19 public function show(): void 15 protected function get_base_url() 16 { 17 return Router::url('dashboard-embedded'); 18 } 19 20 public function show() 20 21 { 21 22 $settings = get_settings(); 22 23 $stats = new Stats(); 23 24 $items_per_page = (int) apply_filters('koko_analytics_items_per_page', 20); 24 $date Format = get_option('date_format', 'Y-m-d');25 $dashboard_url = remove_query_arg(['start_date', 'end_date', 'view', 'posts', 'referrers']);25 $date_format = get_option('date_format', 'Y-m-d'); 26 $dashboard_url = $this->get_base_url(); 26 27 27 28 // parse query params … … 33 34 $range = $settings['default_view']; 34 35 } 35 $now = create_local_datetime('now'); 36 $timezone = wp_timezone(); 37 $now = new DateTimeImmutable('now', $timezone); 36 38 $week_starts_on = (int) get_option('start_of_week', 0); 37 $date Range = $this->get_dates_for_range($now, $range, $week_starts_on);39 $date_range = $this->get_dates_for_range($now, $range, $week_starts_on); 38 40 $page = isset($_GET['p']) ? trim($_GET['p']) : 0; 39 41 40 42 try { 41 $date Start = isset($_GET['start_date']) ? create_local_datetime($_GET['start_date']) : $dateRange[0];43 $date_start = isset($_GET['start_date']) ? new DateTimeImmutable($_GET['start_date'], $timezone) : $date_range[0]; 42 44 } catch (\Exception $e) { 43 $date Start = $dateRange[0];45 $date_start = $date_range[0]; 44 46 } 45 47 try { 46 $date End = isset($_GET['end_date']) ? create_local_datetime($_GET['end_date']) : $dateRange[1];48 $date_end = isset($_GET['end_date']) ? new DateTimeImmutable($_GET['end_date'], $timezone) : $date_range[1]; 47 49 } catch (\Exception $e) { 48 $date End = $dateRange[1];50 $date_end = $date_range[1]; 49 51 } 50 52 … … 57 59 58 60 // calculate next and previous dates for datepicker component and comparison 59 $next Dates = $this->get_next_period($dateStart, $dateEnd, 1);60 $prev Dates = $this->get_next_period($dateStart, $dateEnd, -1);61 62 $date StartStr = $dateStart->format('Y-m-d');63 $date EndStr = $dateEnd->format('Y-m-d');64 65 $totals = $stats->get_totals($date StartStr, $dateEndStr, $page);66 $totals_previous = $stats->get_totals($prev Dates[0]->format('Y-m-d'), $prevDates[2]->format('Y-m-d'), $page);67 68 $posts = $stats->get_posts($date StartStr, $dateEndStr, $posts_offset, $posts_limit);69 $posts_count = $stats->count_posts($date StartStr, $dateEndStr);70 $referrers = $stats->get_referrers($date StartStr, $dateEndStr, $referrers_offset, $referrers_limit);71 $referrers_count = $stats->count_referrers($date StartStr, $dateEndStr);61 $next_dates = $this->get_next_period($date_start, $date_end, 1); 62 $prev_dates = $this->get_next_period($date_start, $date_end, -1); 63 64 $date_start_str = $date_start->format('Y-m-d'); 65 $date_end_str = $date_end->format('Y-m-d'); 66 67 $totals = $stats->get_totals($date_start_str, $date_end_str, $page); 68 $totals_previous = $stats->get_totals($prev_dates[0]->format('Y-m-d'), $prev_dates[2]->format('Y-m-d'), $page); 69 70 $posts = $stats->get_posts($date_start_str, $date_end_str, $posts_offset, $posts_limit); 71 $posts_count = $stats->count_posts($date_start_str, $date_end_str); 72 $referrers = $stats->get_referrers($date_start_str, $date_end_str, $referrers_offset, $referrers_limit); 73 $referrers_count = $stats->count_referrers($date_start_str, $date_end_str); 72 74 $realtime = get_realtime_pageview_count('-1 hour'); 73 75 74 76 if (isset($_GET['group']) && in_array($_GET['group'], ['day', 'week', 'month'])) { 75 $group ChartBy = $_GET['group'];76 } else { 77 $group ChartBy = $dateEnd->getTimestamp() - $dateStart->getTimestamp() >= 86400 * 90 ? 'month' : 'day';78 } 79 $chart_data = $stats->get_stats($date StartStr, $dateEndStr, $groupChartBy, $page);77 $group_chart_by = $_GET['group']; 78 } else { 79 $group_chart_by = $date_end->getTimestamp() - $date_start->getTimestamp() >= 86400 * 90 ? 'month' : 'day'; 80 } 81 $chart_data = $stats->get_stats($date_start_str, $date_end_str, $group_chart_by, $page); 80 82 81 83 require KOKO_ANALYTICS_PLUGIN_DIR . '/src/Resources/views/dashboard-page.php'; 82 84 } 83 85 84 public function get_next_period(\DateTimeImmutable $date Start, \DateTimeImmutable $dateEnd, int $dir = 1): array86 public function get_next_period(\DateTimeImmutable $date_start, \DateTimeImmutable $date_end, int $dir = 1): array 85 87 { 86 88 $now = new \DateTimeImmutable('now', wp_timezone()); 87 89 $modifier = $dir > 0 ? "+" : "-"; 88 90 89 if ($date Start->format('d') === "01" && $dateEnd->format('d') === $dateEnd->format('t')) {91 if ($date_start->format('d') === "01" && $date_end->format('d') === $date_end->format('t')) { 90 92 // cycling full months 91 $diffInMonths = 1 + ((int) $date End->format('Y') - (int) $dateStart->format('Y')) * 12 + (int) $dateEnd->format('m') - (int) $dateStart->format('m');92 $periodStart = $date Start->setDate((int) $dateStart->format('Y'), (int) $dateStart->format('m') + ($dir * $diffInMonths), 1);93 $periodEnd = $date End->setDate((int) $dateStart->format('Y'), (int) $dateEnd->format('m') + ($dir * $diffInMonths), 5);93 $diffInMonths = 1 + ((int) $date_end->format('Y') - (int) $date_start->format('Y')) * 12 + (int) $date_end->format('m') - (int) $date_start->format('m'); 94 $periodStart = $date_start->setDate((int) $date_start->format('Y'), (int) $date_start->format('m') + ($dir * $diffInMonths), 1); 95 $periodEnd = $date_end->setDate((int) $date_start->format('Y'), (int) $date_end->format('m') + ($dir * $diffInMonths), 5); 94 96 $periodEnd = $periodEnd->setDate((int) $periodEnd->format('Y'), (int) $periodEnd->format('m'), (int) $periodEnd->format('t')); 95 97 } else { 96 $diffInDays = $date End->diff($dateStart)->days + 1;97 $periodStart = $date Start->modify("{$modifier}{$diffInDays} days");98 $periodEnd = $date End->modify("{$modifier}{$diffInDays} days");99 } 100 101 if ($date End > $now) {98 $diffInDays = $date_end->diff($date_start)->days + 1; 99 $periodStart = $date_start->modify("{$modifier}{$diffInDays} days"); 100 $periodEnd = $date_end->modify("{$modifier}{$diffInDays} days"); 101 } 102 103 if ($date_end > $now) { 102 104 // limit end date to difference between now and start date, counting from start date 103 $days_diff = $now->diff($date Start)->days;105 $days_diff = $now->diff($date_start)->days; 104 106 $compareEnd = $periodStart->modify("+{$days_diff} days"); 105 107 } else { 106 108 $compareEnd = $periodEnd; 107 109 } 108 109 110 110 111 return [ $periodStart, $periodEnd, $compareEnd ]; -
koko-analytics/tags/2.0.19/src/Dashboard_Widget.php
r3350963 r3378632 8 8 9 9 namespace KokoAnalytics; 10 11 use DateTimeImmutable; 10 12 11 13 class Dashboard_Widget … … 27 29 28 30 $number_of_top_items = (int) apply_filters('koko_analytics_dashboard_widget_number_of_top_items', 5); 31 $timezone = wp_timezone(); 29 32 $stats = new Stats(); 30 $ dateToday = create_local_datetime('today, midnight')->format('Y-m-d');31 $totals = $stats->get_totals($ dateToday, $dateToday);33 $today = (new DateTimeImmutable('today, midnight', $timezone))->format('Y-m-d'); 34 $totals = $stats->get_totals($today, $today); 32 35 33 36 // get realtime pageviews, but limit it to number of total pageviews today in case viewing shortly after midnight 34 37 $realtime = min($totals->pageviews, get_realtime_pageview_count('-1 hour')); 35 38 36 $dateStart = create_local_datetime('-14 days'); 37 $dateEnd = create_local_datetime('now'); 38 $chart_data = $stats->get_stats($dateStart->format('Y-m-d'), $dateEnd->format('Y-m-d'), 'day'); 39 // get chart data 40 $date_start = new DateTimeImmutable('-14 days', $timezone); 41 $date_end = new DateTimeImmutable('now', $timezone); 42 $chart_data = $stats->get_stats($date_start->format('Y-m-d'), $date_end->format('Y-m-d'), 'day'); 39 43 40 44 if ($number_of_top_items > 0) { 41 $posts = $stats->get_posts($ dateToday, $dateToday, 0, $number_of_top_items);42 $referrers = $stats->get_referrers($ dateToday, $dateToday, 0, $number_of_top_items);45 $posts = $stats->get_posts($today, $today, 0, $number_of_top_items); 46 $referrers = $stats->get_referrers($today, $today, 0, $number_of_top_items); 43 47 } 44 48 -
koko-analytics/tags/2.0.19/src/Pageview_Aggregator.php
r3352477 r3378632 124 124 global $wpdb; 125 125 126 // insert referrerstats126 // insert page-specific stats 127 127 foreach ($this->post_stats as $date => $stats) { 128 $paths = array_keys($stats); 129 $path_map = Path_Repository::upsert($paths); 130 131 // insert referrer stats 128 $path_ids = Path_Repository::upsert(array_keys($stats)); 132 129 $values = []; 133 130 foreach ($stats as $path => $r) { 134 array_push($values, $date, $path_ map[$path], $r['post_id'], $r['visitors'], $r['pageviews']);131 array_push($values, $date, $path_ids[$path], $r['post_id'], $r['visitors'], $r['pageviews']); 135 132 } 136 133 $placeholders = rtrim(str_repeat('(%s,%d,%d,%d,%d),', count($stats)), ','); … … 147 144 // insert referrer stats 148 145 foreach ($this->referrer_stats as $date => $stats) { 149 // retrieve ID's for known referrer urls 150 $referrer_urls = array_keys($stats); 151 $placeholders = rtrim(str_repeat('%s,', count($referrer_urls)), ','); 152 $results = $wpdb->get_results($wpdb->prepare("SELECT id, url FROM {$wpdb->prefix}koko_analytics_referrer_urls r WHERE r.url IN({$placeholders})", $referrer_urls)); 153 foreach ($results as $r) { 154 $stats[ $r->url ]['id'] = $r->id; 155 } 156 157 // build query for new referrer urls 158 $new_referrer_urls = []; 146 $referrer_ids = Referrer_Repository::upsert(array_keys($stats)); 147 $values = []; 159 148 foreach ($stats as $url => $r) { 160 if (! isset($r['id'])) { 161 $new_referrer_urls[] = $url; 162 } 163 } 164 165 // insert new referrer urls and set ID in map 166 if (count($new_referrer_urls) > 0) { 167 $values = $new_referrer_urls; 168 $placeholders = rtrim(str_repeat('(%s),', count($values)), ','); 169 $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->prefix}koko_analytics_referrer_urls(url) VALUES {$placeholders}", $values)); 170 $last_insert_id = $wpdb->insert_id; 171 foreach ($values as $url) { 172 $stats[ $url ]['id'] = $last_insert_id++; 173 } 174 } 175 176 // insert referrer stats 177 $values = []; 178 foreach ($stats as $r) { 179 array_push($values, $date, $r['id'], $r['visitors'], $r['pageviews']); 149 array_push($values, $date, $referrer_ids[$url], $r['visitors'], $r['pageviews']); 180 150 } 181 151 $placeholders = rtrim(str_repeat('(%s,%d,%d,%d),', count($stats)), ','); … … 192 162 // remove all data older than 60 minutes 193 163 $one_hour_ago = \time() - 60 * 60; 194 foreach ($counts as $timestamp => $ v) {164 foreach ($counts as $timestamp => $unused) { 195 165 // delete all data older than one hour 196 166 if ((int) $timestamp < $one_hour_ago) { -
koko-analytics/tags/2.0.19/src/Resources/functions/functions.php
r3352698 r3378632 121 121 function get_realtime_pageview_count($since = null): int 122 122 { 123 if (is_numeric($since) || is_int($since)) {123 if (is_numeric($since)) { 124 124 $since = (int) $since; 125 } elseif (is_string($since)) {126 // $since is relative time string127 $since = strtotime($since);128 125 } else { 129 $since = strtotime( '-5 minutes');126 $since = strtotime($since ?? '-5 minutes'); 130 127 } 131 128 -
koko-analytics/tags/2.0.19/src/Resources/functions/global.php
r3350963 r3378632 16 16 function koko_analyics_tracking_script(): void 17 17 { 18 $script_loader = new KokoAnalytics\Script_Loader(); 19 $script_loader->maybe_enqueue_script(true); 18 KokoAnalytics\Script_Loader::maybe_print_script(); 20 19 } 21 20 -
koko-analytics/tags/2.0.19/src/Resources/views/dashboard-page.php
r3366958 r3378632 9 9 /** 10 10 * @var \KokoAnalytics\Dashboard $this 11 * @var \DateTimeInterface $date Start12 * @var \DateTimeInterface $date End11 * @var \DateTimeInterface $date_start 12 * @var \DateTimeInterface $date_end 13 13 * @var object $totals 14 14 * @var int $realtime 15 * @var string $date Format15 * @var string $date_format 16 16 * @var string $dashboard_url 17 17 * @var \KokoAnalytics\Dates $dates 18 18 * @var \KokoAnalytics\Stats $stats 19 * @var array $next_dates 20 * @var array $prev_dates 19 21 */ 20 22 … … 32 34 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar3 me-2" style="vertical-align: middle;" viewBox="0 0 16 16"><path d="M14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M1 3.857C1 3.384 1.448 3 2 3h12c.552 0 1 .384 1 .857v10.286c0 .473-.448.857-1 .857H2c-.552 0-1-.384-1-.857z"/> 33 35 <path d="M6.5 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2"/></svg> 34 <?php echo wp_date($date Format, $dateStart->getTimestamp()); ?> — <?php echo wp_date($dateFormat, $dateEnd->getTimestamp()); ?>36 <?php echo wp_date($date_format, $date_start->getTimestamp()); ?> — <?php echo wp_date($date_format, $date_end->getTimestamp()); ?> 35 37 </div> 36 38 … … 38 40 <div class="mb-3 bg-dark text-white p-3 rounded-top fw-bold d-flex justify-content-between"> 39 41 <?php // only output pagination for date ranges between reasonable dates... to prevent ever-crawling bots from going wild ?> 40 <?php if ($date Start > $total_start_date) { ?>41 <a class="js-quicknav-prev text-decoration-none text-white me-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $prev Dates[0]->format('Y-m-d'), 'end_date' => $prevDates[1]->format('Y-m-d')], $dashboard_url)); ?>">◂</a>42 <?php if ($date_start > $total_start_date) { ?> 43 <a class="js-quicknav-prev text-decoration-none text-white me-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $prev_dates[0]->format('Y-m-d'), 'end_date' => $prev_dates[1]->format('Y-m-d')], $dashboard_url)); ?>">◂</a> 42 44 <?php } else { ?> 43 45 <a class="text-decoration-none text-white me-2">◂</a> 44 46 <?php } ?> 45 <span><?php echo wp_date($date Format, $dateStart->getTimestamp()); ?> — <?php echo wp_date($dateFormat, $dateEnd->getTimestamp()); ?></span>46 <?php if ($date End < $total_end_date) { ?>47 <a class="js-quicknav-next text-decoration-none text-white ms-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $next Dates[0]->format('Y-m-d'), 'end_date' => $nextDates[1]->format('Y-m-d')], $dashboard_url)); ?>">▸</a>47 <span><?php echo wp_date($date_format, $date_start->getTimestamp()); ?> — <?php echo wp_date($date_format, $date_end->getTimestamp()); ?></span> 48 <?php if ($date_end < $total_end_date) { ?> 49 <a class="js-quicknav-next text-decoration-none text-white ms-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $next_dates[0]->format('Y-m-d'), 'end_date' => $next_dates[1]->format('Y-m-d')], $dashboard_url)); ?>">▸</a> 48 50 <?php } else { ?> 49 51 <a class="text-decoration-none text-white ms-2">▸</a> … … 69 71 <label for='ka-date-start' class="ka-label"><?php esc_html_e('Start date', 'koko-analytics'); ?></label> 70 72 <input name="start_date" id='ka-date-start' type="date" size="10" min="2000-01-01" max="2100-01-01" 71 value="<?php echo $date Start->format('Y-m-d'); ?>" class="ka-input">73 value="<?php echo $date_start->format('Y-m-d'); ?>" class="ka-input"> 72 74 </div> 73 75 <div class="mb-3"> 74 76 <label for='ka-date-end' class="ka-label"><?php esc_html_e('End date', 'koko-analytics'); ?></label> 75 77 <input name="end_date" id='ka-date-end' type="date" size="10" min="2000-01-01" max="2100-01-01" 76 value="<?php echo $date End->format('Y-m-d'); ?>" class="ka-input">78 value="<?php echo $date_end->format('Y-m-d'); ?>" class="ka-input"> 77 79 </div> 78 80 <div> … … 89 91 </div> 90 92 91 <?php do_action('koko_analytics_after_datepicker', $date Start, $dateEnd); ?>93 <?php do_action('koko_analytics_after_datepicker', $date_start, $date_end); ?> 92 94 </div> 93 95 … … 168 170 <?php if (count($chart_data) > 1) { ?> 169 171 <div class="ka-box mb-3 p-3"> 170 <?php new Chart_View($chart_data, $date Start, $dateEnd); ?>172 <?php new Chart_View($chart_data, $date_start, $date_end); ?> 171 173 </div> 172 174 <?php } ?> … … 262 264 263 265 <?php do_action_deprecated('koko_analytics_show_dashboard_components', [], '1.4', 'koko_analytics_after_dashboard_components'); ?> 264 <?php do_action('koko_analytics_after_dashboard_components', $date Start, $dateEnd); ?>266 <?php do_action('koko_analytics_after_dashboard_components', $date_start, $date_end); ?> 265 267 </div><?php // end div.ka-row ?> 266 268 -
koko-analytics/tags/2.0.19/src/Resources/views/dashboard-widget.php
r3352477 r3378632 8 8 * @var array $referrers 9 9 * @var stdClass $totals 10 * @var \DateTimeInterface $dateStart 11 * @var \DateTimeInterface $dateEnd 10 * @var \DateTimeInterface $today 11 * @var \DateTimeInterface $date_start 12 * @var \DateTimeInterface $date_end 12 13 */ 13 14 … … 31 32 <?php esc_html_e('Showing site visits over last 14 days', 'koko-analytics'); ?> 32 33 </h3> 33 <div class="">34 <?php new Chart_View($chart_data, $date Start, $dateEnd, 200, false); ?>34 <div> 35 <?php new Chart_View($chart_data, $date_start, $date_end, 200, false); ?> 35 36 </div> 36 37 </div> -
koko-analytics/tags/2.0.19/src/Resources/views/nav.php
r3350963 r3378632 1 1 <?php 2 2 3 /** 3 4 * @var string $tab 4 5 */ 6 7 use KokoAnalytics\Router; 8 5 9 ?> 6 10 <?php if (current_user_can('manage_koko_analytics')) { ?> … … 8 12 <?php if (current_user_can('view_koko_analytics')) : ?> 9 13 <ul class="list-inline m-0"> 10 <li class="list-inline-item m-0 ms-2"><a href="<? php echo esc_attr(add_query_arg(['koko-analytics-dashboard' => 1], home_url())); ?>" <?php echo isset($_GET['koko-analytics-dashboard']) ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard (full screen)', 'koko-analytics'); ?></a></li>14 <li class="list-inline-item m-0 ms-2"><a href="<?= Router::url('dashboard-standalone'); ?>" <?= Router::is('dashboard-standalone') ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard (full screen)', 'koko-analytics'); ?></a></li> 11 15 12 <li class="list-inline-item m-0 ms-2"><a href="<? php echo admin_url('index.php?page=koko-analytics'); ?>" <?php echo $tab === 'dashboard' && !isset($_GET['koko-analytics-dashboard']) ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard', 'koko-analytics'); ?></a></li>16 <li class="list-inline-item m-0 ms-2"><a href="<?= Router::url('dashboard-embedded') ?>" <?= Router::is('dashboard-embedded') ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard', 'koko-analytics'); ?></a></li> 13 17 <?php endif; ?> 14 18 <?php if (current_user_can('manage_koko_analytics')) : ?> 15 <li class="list-inline-item m-0 ms-2"><a href="<? php echo admin_url('index.php?page=koko-analytics&tab=settings'); ?>" <?php echo $tab === 'settings'? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Settings', 'koko-analytics'); ?></a></li>19 <li class="list-inline-item m-0 ms-2"><a href="<?= Router::url('settings-page') ?>" <?= Router::is('settings-page') ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Settings', 'koko-analytics'); ?></a></li> 16 20 <?php endif; ?> 17 21 </ul> -
koko-analytics/tags/2.0.19/src/Resources/views/settings-page.php
r3366958 r3378632 2 2 3 3 use KokoAnalytics\Endpoint_Installer; 4 use KokoAnalytics\Router; 4 5 5 6 defined('ABSPATH') or exit; … … 14 15 */ 15 16 $tab = 'settings'; 16 $public_dashboard_url = add_query_arg(['koko-analytics-dashboard' => 1], home_url());17 $public_dashboard_url = Router::url('dashboard-standalone'); 17 18 ?> 18 19 <div class="wrap koko-analytics" id="koko-analytics-admin"> … … 238 239 <ul class="ul-square"> 239 240 <li><a href="<?php echo esc_attr(add_query_arg(['tab' => 'jetpack_importer'])); ?>"><?php esc_html_e('Import from Jetpack Stats', 'koko-analytics'); ?></a></li> 241 <li><a href="<?php echo esc_attr(add_query_arg(['tab' => 'plausible_importer'])); ?>"><?php esc_html_e('Import from Plausible', 'koko-analytics'); ?></a></li> 240 242 </ul> 241 243 </div> … … 262 264 } else { 263 265 // store empty array to prevent doing an HTTP request on every page load 264 // we'll try again in 8hours266 // we'll try again in 24 hours 265 267 $posts = []; 266 268 } 267 set_transient('koko_analytics_remote_posts', $posts, HOUR_IN_SECONDS * 8);269 set_transient('koko_analytics_remote_posts', $posts, HOUR_IN_SECONDS * 24); 268 270 } 269 271 … … 283 285 284 286 <?php if (isset($_GET['notice'])) { ?> 285 <script>history.replaceState({}, null, '<?= admin_url('index.php?page=koko-analytics&tab=settings'); ?>');</script>287 <script>history.replaceState({}, null, "<?= Router::url('settings-page'); ?>");</script> 286 288 <?php } ?> -
koko-analytics/tags/2.0.19/src/Resources/views/standalone.php
r3350963 r3378632 11 11 <meta name="viewport" content="width=device-width, initial-scale=1"> 12 12 <meta name="referrer" content="no-referrer-when-downgrade"> 13 <meta name="robots" content="noindex,nofollow">14 13 <title>Koko Analytics</title> 15 14 <meta name="apple-mobile-web-app-capable" content="yes"> … … 19 18 <link rel="manifest" href="<?php echo plugins_url('assets/dist/manifest.json', KOKO_ANALYTICS_PLUGIN_FILE); ?>"> 20 19 <link rel="shortcut icon" href="<?php echo plugins_url('assets/dist/img/favicon.ico', KOKO_ANALYTICS_PLUGIN_FILE); ?>"> 20 <link rel="canonical" href="<?= site_url('?koko-analytics-dashboard'); ?>"> 21 <meta name="robots" content="nofollow, noindex"> 21 22 <meta name="theme-color" content="#B60205"> 22 23 </head> 23 24 <body class="koko-analytics"> 24 <?php $this->show(); ?>25 <?php parent::show(); ?> 25 26 <script> 26 27 if ('serviceWorker' in navigator) { -
koko-analytics/tags/2.0.19/src/Rest.php
r3350963 r3378632 8 8 9 9 namespace KokoAnalytics; 10 11 use DateTimeImmutable; 10 12 11 13 class Rest … … 129 131 public function get_stats(\WP_REST_Request $request): \WP_REST_Response 130 132 { 133 $timezone = wp_timezone(); 131 134 $params = $request->get_query_params(); 132 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');133 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');135 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 136 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 134 137 $group = ($params['monthly'] ?? false) ? 'month' : 'day'; 135 138 $page = $params['page'] ?? 0; … … 143 146 public function get_totals(\WP_REST_Request $request): \WP_REST_Response 144 147 { 148 $timezone = wp_timezone(); 145 149 $params = $request->get_query_params(); 146 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');147 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');150 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 151 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 148 152 $page = $params['page'] ?? 0; 149 153 $result = (new Stats())->get_totals($start_date, $end_date, $page); … … 156 160 public function get_posts(\WP_REST_Request $request): \WP_REST_Response 157 161 { 162 $timezone = wp_timezone(); 158 163 $params = $request->get_query_params(); 159 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');160 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');164 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 165 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 161 166 $offset = isset($params['offset']) ? absint($params['offset']) : 0; 162 167 $limit = isset($params['limit']) ? absint($params['limit']) : 10; … … 170 175 public function get_referrers(\WP_REST_Request $request): \WP_REST_Response 171 176 { 177 $timezone = wp_timezone(); 172 178 $params = $request->get_query_params(); 173 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');174 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');179 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 180 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 175 181 $offset = isset($params['offset']) ? absint($params['offset']) : 0; 176 182 $limit = isset($params['limit']) ? absint($params['limit']) : 10; -
koko-analytics/tags/2.0.19/src/Script_Loader.php
r3350963 r3378632 10 10 11 11 use KokoAnalytics\Normalizers\Normalizer; 12 use WP_User;13 12 14 13 class Script_Loader 15 14 { 16 /** 17 * @param bool $echo Whether to use the default WP script enqueue method or print the script tag directly 18 */ 19 public static function maybe_enqueue_script(bool $echo = false): void 15 public static function maybe_print_script(): void 20 16 { 21 17 $load_script = apply_filters('koko_analytics_load_tracking_script', true); … … 24 20 } 25 21 26 if (is_request_excluded() ) {22 if (is_request_excluded() || is_preview()) { 27 23 return; 28 24 } 29 25 30 // TODO: Handle "term" requests so we track both terms and post types. 31 add_filter('script_loader_tag', [ Script_Loader::class , 'add_async_attribute' ], 20, 2); 32 33 if (false === $echo) { 34 // Print configuration object early on in the HTML so scripts can modify it 35 if (did_action('wp_head')) { 36 self::print_js_object(); 37 } else { 38 add_action('wp_head', [ Script_Loader::class , 'print_js_object' ], 1, 0); 39 } 40 41 // Enqueue the actual tracking script (in footer, if possible) 42 wp_enqueue_script('koko-analytics', plugins_url('assets/dist/js/script.js', KOKO_ANALYTICS_PLUGIN_FILE), [], KOKO_ANALYTICS_VERSION, true); 43 } else { 44 self::print_js_object(); 45 echo '<script defer src="', plugins_url('assets/dist/js/script.js?ver=' . KOKO_ANALYTICS_VERSION, KOKO_ANALYTICS_PLUGIN_FILE), '"></script>'; 46 } 26 echo PHP_EOL . '<!-- Koko Analytics v' . KOKO_ANALYTICS_VERSION . ' - https://www.kokoanalytics.com/ -->' . PHP_EOL; 27 wp_print_inline_script_tag(file_get_contents(KOKO_ANALYTICS_PLUGIN_DIR . '/assets/dist/js/script.js')); 28 echo PHP_EOL; 47 29 } 48 30 … … 63 45 private static function get_tracker_url(): string 64 46 { 65 global $wp;66 67 47 // People can create their own endpoint and define it through this constant 68 48 if (\defined('KOKO_ANALYTICS_CUSTOM_ENDPOINT') && KOKO_ANALYTICS_CUSTOM_ENDPOINT) { … … 80 60 public static function get_request_path(): string 81 61 { 82 $path = trim($_SERVER["REQUEST_URI"] ?? ''); 83 return Normalizer::path($path); 62 return Normalizer::path(trim($_SERVER["REQUEST_URI"] ?? '')); 84 63 } 85 64 … … 132 111 echo '<amp-analytics><script type="application/json">', json_encode($config), '</script></amp-analytics>'; 133 112 } 134 135 /**136 * @param string $tag137 * @param string $handle138 */139 public static function add_async_attribute($tag, $handle)140 {141 if ($handle !== 'koko-analytics' || strpos($tag, ' defer') !== false) {142 return $tag;143 }144 145 return str_replace(' src=', ' defer src=', $tag);146 }147 113 } -
koko-analytics/tags/2.0.19/src/Shortcodes/Shortcode_Site_Counter.php
r3364374 r3378632 20 20 use KokoAnalytics\Normalizers\Normalizer; 21 21 use KokoAnalytics\Stats; 22 23 use function KokoAnalytics\create_local_datetime;24 22 25 23 class Shortcode_Site_Counter -
koko-analytics/tags/2.0.19/src/Stats.php
r3366958 r3378632 28 28 public function get_totals(string $start_date, string $end_date, $page = 0, $unused = null): object 29 29 { 30 /** @var wpdb $wpdb */30 /** @var \wpdb $wpdb */ 31 31 global $wpdb; 32 32 … … 78 78 public function get_stats(string $start_date, string $end_date, string $group = 'day', $page = ''): array 79 79 { 80 /** @var wpdb $wpdb */80 /** @var \wpdb $wpdb */ 81 81 global $wpdb; 82 82 … … 121 121 public function get_posts(string $start_date, string $end_date, int $offset = 0, int $limit = 10): array 122 122 { 123 /** @var wpdb $wpdb */123 /** @var \wpdb $wpdb */ 124 124 global $wpdb; 125 125 … … 128 128 FROM {$wpdb->prefix}koko_analytics_post_stats s 129 129 JOIN {$wpdb->prefix}koko_analytics_paths p ON p.id = s.path_id 130 LEFT JOIN {$wpdb->prefix}posts wp ON s.post_id = wp.ID130 LEFT JOIN {$wpdb->prefix}posts wp ON wp.ID = s.post_id 131 131 WHERE s.date >= %s AND s.date <= %s 132 132 GROUP BY p.path, s.post_id … … 138 138 return array_map(function ($row) { 139 139 $row->pageviews = (int) $row->pageviews; 140 $row->visitors = (int) $row->visitors;140 $row->visitors = max(1, (int) $row->visitors); 141 141 142 142 // for backwards compatibility with versions before 2.0 … … 151 151 public function count_posts(string $start_date, string $end_date): int 152 152 { 153 /** @var wpdb $wpdb */153 /** @var \wpdb $wpdb */ 154 154 global $wpdb; 155 155 return (int) $wpdb->get_var($wpdb->prepare( … … 169 169 public function get_referrers(string $start_date, string $end_date, int $offset = 0, int $limit = 10): array 170 170 { 171 /** @var wpdb $wpdb */ 172 global $wpdb; 173 return $wpdb->get_results($wpdb->prepare( 171 /** @var \wpdb $wpdb */ 172 global $wpdb; 173 174 return array_map(function ($row) { 175 $row->pageviews = (int) $row->pageviews; 176 $row->visitors = max(1, (int) $row->visitors); 177 return $row; 178 }, $wpdb->get_results($wpdb->prepare( 174 179 "SELECT s.id, url, SUM(visitors) As visitors, SUM(pageviews) AS pageviews 175 180 FROM {$wpdb->prefix}koko_analytics_referrer_stats s … … 180 185 LIMIT %d, %d", 181 186 [$start_date, $end_date, $offset, $limit] 182 )) ;187 ))); 183 188 } 184 189 185 190 public function count_referrers(string $start_date, string $end_date): int 186 191 { 187 /** @var wpdb $wpdb */192 /** @var \wpdb $wpdb */ 188 193 global $wpdb; 189 194 return (int) $wpdb->get_var($wpdb->prepare( -
koko-analytics/trunk/CHANGELOG.md
r3366958 r3378632 1 1 # Changelog 2 3 ### 2.0.19 - Oct 15, 2025 4 5 - Print (< 500 bytes) tracking script inline in page HTML to save on an additional HTTP request and resolve overly aggressive cache issues. 6 - Add importer for Plausible. 7 - Change public dashboard URL to `/koko-analytics-dashboard/` if pretty permalinks are enabled. 8 - Exclude visits to post previews. 9 2 10 3 11 ### 2.0.18 - Sep 24, 2025 -
koko-analytics/trunk/assets/dist/js/script.js
r3362953 r3378632 1 /*! For license information please see script.js.LICENSE.txt */ 2 !function(){var e=window,t="koko_analytics";e[t].trackPageview=function(){if("prerender"!=document.visibilityState&&!/bot|crawl|spider|seo|lighthouse|facebookexternalhit|preview/i.test(navigator.userAgent)){var a=e[t].path;a||(a=window.location.pathname+window.location.search);var o,r=0==document.referrer.indexOf(e[t].site_url)?"":document.referrer;o={pa:a,po:e[t].post_id,r:r},e[t].use_cookie?o.m="c":e[t].method&&(o.m=e[t].method[0]),navigator.sendBeacon(e[t].url,new URLSearchParams(o))}},e.addEventListener("load",function(){e[t].trackPageview()})}(); 1 !function(){var e=window,r="koko_analytics";function t(t){t.m=e[r].use_cookie?"c":e[r].method[0],navigator.sendBeacon(e[r].url,new URLSearchParams(t))}e[r].request=t,e[r].trackPageview=function(){if("prerender"!=document.visibilityState&&!/bot|crawl|spider|seo|lighthouse|facebookexternalhit|preview/i.test(navigator.userAgent)){var i=0==document.referrer.indexOf(e[r].site_url)?"":document.referrer;t({pa:e[r].path,po:e[r].post_id,r:i})}},e.addEventListener("load",function(){e[r].trackPageview()})}(); -
koko-analytics/trunk/data/referrer-blocklist
r3359615 r3378632 841 841 getrichquick.ml 842 842 getrichquickly.info 843 gevciamst.online 843 844 gezlev.com.ua 844 845 ghazel.ru … … 1507 1508 pfrf-kabinet.ru 1508 1509 pharm--shop.ru 1510 phimarshcer.online 1509 1511 phimmakinhdi.com 1510 1512 photo-clip.ru -
koko-analytics/trunk/koko-analytics.php
r3366958 r3378632 4 4 Plugin Name: Koko Analytics 5 5 Plugin URI: https://www.kokoanalytics.com/#utm_source=wp-plugin&utm_medium=koko-analytics&utm_campaign=plugins-page 6 Version: 2.0.1 86 Version: 2.0.19 7 7 Description: Privacy-friendly and efficient statistics for your WordPress site. 8 8 Author: ibericode … … 39 39 use KokoAnalytics\Widgets\Most_Viewed_Posts_Widget; 40 40 41 \define('KOKO_ANALYTICS_VERSION', '2.0.1 8');41 \define('KOKO_ANALYTICS_VERSION', '2.0.19'); 42 42 \define('KOKO_ANALYTICS_PLUGIN_FILE', __FILE__); 43 43 \define('KOKO_ANALYTICS_PLUGIN_DIR', __DIR__); … … 72 72 73 73 // script loader 74 add_action('wp_enqueue_scripts', [Script_Loader::class, 'maybe_enqueue_script'], 10, 0); 74 add_action('wp_head', [ Script_Loader::class , 'print_js_object' ], 1, 0); 75 add_action('wp_footer', [Script_Loader::class, 'maybe_print_script'], 10, 0); 75 76 add_action('amp_print_analytics', [Script_Loader::class, 'print_amp_analytics_tag'], 10, 0); 76 77 add_action('admin_bar_menu', [Admin\Bar::class, 'register'], 40, 1); … … 108 109 // maybe show standalone dashboard 109 110 add_action('wp', function () { 110 if (! isset($_GET['koko-analytics-dashboard'])) {111 if (!Router::is('dashboard-standalone')) { 111 112 return; 112 113 } … … 122 123 } 123 124 124 (new Dashboard())->show_standalone_dashboard_page(); 125 (new Dashboard_Standalone())->show(); 126 exit; 125 127 }, 10, 0); 126 128 … … 134 136 } 135 137 136 // on plugin update (but using old code) 137 // this breaks in 2.x because of the new file structure 138 // TODO: Reactivate once 2.x stabilises 139 // add_filter('upgrader_process_complete', function () { 140 // do_action('koko_analytics_aggregate_stats'); 141 // }); 138 // on plugin update (but using old code that's already in memory) 139 add_filter('upgrader_process_complete', function () { 140 do_action('koko_analytics_aggregate_stats'); 141 }); 142 142 143 143 // on plugin activation -
koko-analytics/trunk/readme.txt
r3373525 r3378632 5 5 Requires at least: 6.0 6 6 Tested up to: 6.8 7 Stable tag: 2.0.1 87 Stable tag: 2.0.19 8 8 License: GPL-3.0-or-later 9 9 License URI: http://www.gnu.org/licenses/gpl-3.0.html … … 132 132 == Changelog == 133 133 134 ### 2.0.19 - Oct 15, 2025 135 136 - Print (< 500 bytes) tracking script inline in page HTML to save on an additional HTTP request and resolve overly aggressive cache issues. 137 - Add importer for Plausible. 138 - Change public dashboard URL to `/koko-analytics-dashboard/` if pretty permalinks are enabled. 139 - Exclude visits to post previews. 140 141 134 142 ### 2.0.18 - Sep 24, 2025 135 143 … … 444 452 #### 1.4.4 - Nov 4, 2024 445 453 446 - Add Jetpack Stats importer to import your historical analytics data into Koko Analytics. Go to the settings page (with Jetpack still enabled) to access it. 447 - Fix settings page showing proxy IP instead of client IP if using reverse proxy. 448 - Fix use of PHP 7.4 only feature in thousands separator in source ... 449 454 - Add Jetpack Stats ... 455 -
koko-analytics/trunk/src/Admin/Actions.php
r3366958 r3378632 193 193 194 194 do { 195 $results = $wpdb->get_results($wpdb->prepare("SELECT post_id, path_id, p.path FROM {$wpdb->prefix}koko_analytics_post_stats s LEFT JOIN {$wpdb->prefix}koko_analytics_paths p ON p.id = s.path_id WHERE post_id IS NOT NULL AND post_id != 0 AND date <= '2025-08-29'GROUP BY post_id LIMIT %d OFFSET %d", [$limit, $offset]));195 $results = $wpdb->get_results($wpdb->prepare("SELECT post_id, path_id, p.path FROM {$wpdb->prefix}koko_analytics_post_stats s LEFT JOIN {$wpdb->prefix}koko_analytics_paths p ON p.id = s.path_id WHERE post_id IS NOT NULL AND post_id != 0 GROUP BY post_id LIMIT %d OFFSET %d", [$limit, $offset])); 196 196 $offset += $limit; 197 197 if (!$results) { -
koko-analytics/trunk/src/Admin/Admin.php
r3366958 r3378632 9 9 namespace KokoAnalytics\Admin; 10 10 11 use KokoAnalytics\Jetpack_Importer; 11 use KokoAnalytics\Import\Jetpack_Importer; 12 use KokoAnalytics\Import\Plausible_Importer; 13 use KokoAnalytics\Router; 12 14 13 15 class Admin … … 41 43 add_action('koko_analytics_start_jetpack_import', [Jetpack_Importer::class, 'start_import'], 10, 0); 42 44 add_action('koko_analytics_jetpack_import_chunk', [Jetpack_Importer::class, 'import_chunk'], 10, 0); 45 46 // actions for plausible importer 47 add_action('koko_analytics_show_plausible_importer_page', [Plausible_Importer::class, 'show_page'], 10, 0); 48 add_action('koko_analytics_start_plausible_import', [Plausible_Importer::class, 'start_import'], 10, 0); 43 49 } 44 50 … … 58 64 public function add_plugin_settings_link($links): array 59 65 { 60 $href = admin_url('index.php?page=koko-analytics&tab=settings');66 $href = Router::url('settings-page'); 61 67 $label = esc_html__('Settings', 'koko-analytics'); 62 68 $settings_link = "<a href=\"{$href}\">{$label}</a>"; -
koko-analytics/trunk/src/Admin/Data_Export.php
r3366958 r3378632 9 9 namespace KokoAnalytics\Admin; 10 10 11 use function KokoAnalytics\create_local_datetime;11 use DateTimeImmutable; 12 12 13 13 class Data_Export … … 36 36 { 37 37 // write to HTTP stream 38 $date = create_local_datetime('now')->format("Y-m-d");38 $date = (new DateTimeImmutable('now', wp_timezone()))->format("Y-m-d"); 39 39 $site_url = parse_url(get_site_url(), PHP_URL_HOST); 40 40 -
koko-analytics/trunk/src/Dashboard.php
r3362953 r3378632 9 9 namespace KokoAnalytics; 10 10 11 use DateTimeImmutable; 12 11 13 class Dashboard 12 14 { 13 public function show_standalone_dashboard_page(): void 14 { 15 require KOKO_ANALYTICS_PLUGIN_DIR . '/src/Resources/views/standalone.php'; 16 exit; 17 } 18 19 public function show(): void 15 protected function get_base_url() 16 { 17 return Router::url('dashboard-embedded'); 18 } 19 20 public function show() 20 21 { 21 22 $settings = get_settings(); 22 23 $stats = new Stats(); 23 24 $items_per_page = (int) apply_filters('koko_analytics_items_per_page', 20); 24 $date Format = get_option('date_format', 'Y-m-d');25 $dashboard_url = remove_query_arg(['start_date', 'end_date', 'view', 'posts', 'referrers']);25 $date_format = get_option('date_format', 'Y-m-d'); 26 $dashboard_url = $this->get_base_url(); 26 27 27 28 // parse query params … … 33 34 $range = $settings['default_view']; 34 35 } 35 $now = create_local_datetime('now'); 36 $timezone = wp_timezone(); 37 $now = new DateTimeImmutable('now', $timezone); 36 38 $week_starts_on = (int) get_option('start_of_week', 0); 37 $date Range = $this->get_dates_for_range($now, $range, $week_starts_on);39 $date_range = $this->get_dates_for_range($now, $range, $week_starts_on); 38 40 $page = isset($_GET['p']) ? trim($_GET['p']) : 0; 39 41 40 42 try { 41 $date Start = isset($_GET['start_date']) ? create_local_datetime($_GET['start_date']) : $dateRange[0];43 $date_start = isset($_GET['start_date']) ? new DateTimeImmutable($_GET['start_date'], $timezone) : $date_range[0]; 42 44 } catch (\Exception $e) { 43 $date Start = $dateRange[0];45 $date_start = $date_range[0]; 44 46 } 45 47 try { 46 $date End = isset($_GET['end_date']) ? create_local_datetime($_GET['end_date']) : $dateRange[1];48 $date_end = isset($_GET['end_date']) ? new DateTimeImmutable($_GET['end_date'], $timezone) : $date_range[1]; 47 49 } catch (\Exception $e) { 48 $date End = $dateRange[1];50 $date_end = $date_range[1]; 49 51 } 50 52 … … 57 59 58 60 // calculate next and previous dates for datepicker component and comparison 59 $next Dates = $this->get_next_period($dateStart, $dateEnd, 1);60 $prev Dates = $this->get_next_period($dateStart, $dateEnd, -1);61 62 $date StartStr = $dateStart->format('Y-m-d');63 $date EndStr = $dateEnd->format('Y-m-d');64 65 $totals = $stats->get_totals($date StartStr, $dateEndStr, $page);66 $totals_previous = $stats->get_totals($prev Dates[0]->format('Y-m-d'), $prevDates[2]->format('Y-m-d'), $page);67 68 $posts = $stats->get_posts($date StartStr, $dateEndStr, $posts_offset, $posts_limit);69 $posts_count = $stats->count_posts($date StartStr, $dateEndStr);70 $referrers = $stats->get_referrers($date StartStr, $dateEndStr, $referrers_offset, $referrers_limit);71 $referrers_count = $stats->count_referrers($date StartStr, $dateEndStr);61 $next_dates = $this->get_next_period($date_start, $date_end, 1); 62 $prev_dates = $this->get_next_period($date_start, $date_end, -1); 63 64 $date_start_str = $date_start->format('Y-m-d'); 65 $date_end_str = $date_end->format('Y-m-d'); 66 67 $totals = $stats->get_totals($date_start_str, $date_end_str, $page); 68 $totals_previous = $stats->get_totals($prev_dates[0]->format('Y-m-d'), $prev_dates[2]->format('Y-m-d'), $page); 69 70 $posts = $stats->get_posts($date_start_str, $date_end_str, $posts_offset, $posts_limit); 71 $posts_count = $stats->count_posts($date_start_str, $date_end_str); 72 $referrers = $stats->get_referrers($date_start_str, $date_end_str, $referrers_offset, $referrers_limit); 73 $referrers_count = $stats->count_referrers($date_start_str, $date_end_str); 72 74 $realtime = get_realtime_pageview_count('-1 hour'); 73 75 74 76 if (isset($_GET['group']) && in_array($_GET['group'], ['day', 'week', 'month'])) { 75 $group ChartBy = $_GET['group'];76 } else { 77 $group ChartBy = $dateEnd->getTimestamp() - $dateStart->getTimestamp() >= 86400 * 90 ? 'month' : 'day';78 } 79 $chart_data = $stats->get_stats($date StartStr, $dateEndStr, $groupChartBy, $page);77 $group_chart_by = $_GET['group']; 78 } else { 79 $group_chart_by = $date_end->getTimestamp() - $date_start->getTimestamp() >= 86400 * 90 ? 'month' : 'day'; 80 } 81 $chart_data = $stats->get_stats($date_start_str, $date_end_str, $group_chart_by, $page); 80 82 81 83 require KOKO_ANALYTICS_PLUGIN_DIR . '/src/Resources/views/dashboard-page.php'; 82 84 } 83 85 84 public function get_next_period(\DateTimeImmutable $date Start, \DateTimeImmutable $dateEnd, int $dir = 1): array86 public function get_next_period(\DateTimeImmutable $date_start, \DateTimeImmutable $date_end, int $dir = 1): array 85 87 { 86 88 $now = new \DateTimeImmutable('now', wp_timezone()); 87 89 $modifier = $dir > 0 ? "+" : "-"; 88 90 89 if ($date Start->format('d') === "01" && $dateEnd->format('d') === $dateEnd->format('t')) {91 if ($date_start->format('d') === "01" && $date_end->format('d') === $date_end->format('t')) { 90 92 // cycling full months 91 $diffInMonths = 1 + ((int) $date End->format('Y') - (int) $dateStart->format('Y')) * 12 + (int) $dateEnd->format('m') - (int) $dateStart->format('m');92 $periodStart = $date Start->setDate((int) $dateStart->format('Y'), (int) $dateStart->format('m') + ($dir * $diffInMonths), 1);93 $periodEnd = $date End->setDate((int) $dateStart->format('Y'), (int) $dateEnd->format('m') + ($dir * $diffInMonths), 5);93 $diffInMonths = 1 + ((int) $date_end->format('Y') - (int) $date_start->format('Y')) * 12 + (int) $date_end->format('m') - (int) $date_start->format('m'); 94 $periodStart = $date_start->setDate((int) $date_start->format('Y'), (int) $date_start->format('m') + ($dir * $diffInMonths), 1); 95 $periodEnd = $date_end->setDate((int) $date_start->format('Y'), (int) $date_end->format('m') + ($dir * $diffInMonths), 5); 94 96 $periodEnd = $periodEnd->setDate((int) $periodEnd->format('Y'), (int) $periodEnd->format('m'), (int) $periodEnd->format('t')); 95 97 } else { 96 $diffInDays = $date End->diff($dateStart)->days + 1;97 $periodStart = $date Start->modify("{$modifier}{$diffInDays} days");98 $periodEnd = $date End->modify("{$modifier}{$diffInDays} days");99 } 100 101 if ($date End > $now) {98 $diffInDays = $date_end->diff($date_start)->days + 1; 99 $periodStart = $date_start->modify("{$modifier}{$diffInDays} days"); 100 $periodEnd = $date_end->modify("{$modifier}{$diffInDays} days"); 101 } 102 103 if ($date_end > $now) { 102 104 // limit end date to difference between now and start date, counting from start date 103 $days_diff = $now->diff($date Start)->days;105 $days_diff = $now->diff($date_start)->days; 104 106 $compareEnd = $periodStart->modify("+{$days_diff} days"); 105 107 } else { 106 108 $compareEnd = $periodEnd; 107 109 } 108 109 110 110 111 return [ $periodStart, $periodEnd, $compareEnd ]; -
koko-analytics/trunk/src/Dashboard_Widget.php
r3350963 r3378632 8 8 9 9 namespace KokoAnalytics; 10 11 use DateTimeImmutable; 10 12 11 13 class Dashboard_Widget … … 27 29 28 30 $number_of_top_items = (int) apply_filters('koko_analytics_dashboard_widget_number_of_top_items', 5); 31 $timezone = wp_timezone(); 29 32 $stats = new Stats(); 30 $ dateToday = create_local_datetime('today, midnight')->format('Y-m-d');31 $totals = $stats->get_totals($ dateToday, $dateToday);33 $today = (new DateTimeImmutable('today, midnight', $timezone))->format('Y-m-d'); 34 $totals = $stats->get_totals($today, $today); 32 35 33 36 // get realtime pageviews, but limit it to number of total pageviews today in case viewing shortly after midnight 34 37 $realtime = min($totals->pageviews, get_realtime_pageview_count('-1 hour')); 35 38 36 $dateStart = create_local_datetime('-14 days'); 37 $dateEnd = create_local_datetime('now'); 38 $chart_data = $stats->get_stats($dateStart->format('Y-m-d'), $dateEnd->format('Y-m-d'), 'day'); 39 // get chart data 40 $date_start = new DateTimeImmutable('-14 days', $timezone); 41 $date_end = new DateTimeImmutable('now', $timezone); 42 $chart_data = $stats->get_stats($date_start->format('Y-m-d'), $date_end->format('Y-m-d'), 'day'); 39 43 40 44 if ($number_of_top_items > 0) { 41 $posts = $stats->get_posts($ dateToday, $dateToday, 0, $number_of_top_items);42 $referrers = $stats->get_referrers($ dateToday, $dateToday, 0, $number_of_top_items);45 $posts = $stats->get_posts($today, $today, 0, $number_of_top_items); 46 $referrers = $stats->get_referrers($today, $today, 0, $number_of_top_items); 43 47 } 44 48 -
koko-analytics/trunk/src/Pageview_Aggregator.php
r3352477 r3378632 124 124 global $wpdb; 125 125 126 // insert referrerstats126 // insert page-specific stats 127 127 foreach ($this->post_stats as $date => $stats) { 128 $paths = array_keys($stats); 129 $path_map = Path_Repository::upsert($paths); 130 131 // insert referrer stats 128 $path_ids = Path_Repository::upsert(array_keys($stats)); 132 129 $values = []; 133 130 foreach ($stats as $path => $r) { 134 array_push($values, $date, $path_ map[$path], $r['post_id'], $r['visitors'], $r['pageviews']);131 array_push($values, $date, $path_ids[$path], $r['post_id'], $r['visitors'], $r['pageviews']); 135 132 } 136 133 $placeholders = rtrim(str_repeat('(%s,%d,%d,%d,%d),', count($stats)), ','); … … 147 144 // insert referrer stats 148 145 foreach ($this->referrer_stats as $date => $stats) { 149 // retrieve ID's for known referrer urls 150 $referrer_urls = array_keys($stats); 151 $placeholders = rtrim(str_repeat('%s,', count($referrer_urls)), ','); 152 $results = $wpdb->get_results($wpdb->prepare("SELECT id, url FROM {$wpdb->prefix}koko_analytics_referrer_urls r WHERE r.url IN({$placeholders})", $referrer_urls)); 153 foreach ($results as $r) { 154 $stats[ $r->url ]['id'] = $r->id; 155 } 156 157 // build query for new referrer urls 158 $new_referrer_urls = []; 146 $referrer_ids = Referrer_Repository::upsert(array_keys($stats)); 147 $values = []; 159 148 foreach ($stats as $url => $r) { 160 if (! isset($r['id'])) { 161 $new_referrer_urls[] = $url; 162 } 163 } 164 165 // insert new referrer urls and set ID in map 166 if (count($new_referrer_urls) > 0) { 167 $values = $new_referrer_urls; 168 $placeholders = rtrim(str_repeat('(%s),', count($values)), ','); 169 $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->prefix}koko_analytics_referrer_urls(url) VALUES {$placeholders}", $values)); 170 $last_insert_id = $wpdb->insert_id; 171 foreach ($values as $url) { 172 $stats[ $url ]['id'] = $last_insert_id++; 173 } 174 } 175 176 // insert referrer stats 177 $values = []; 178 foreach ($stats as $r) { 179 array_push($values, $date, $r['id'], $r['visitors'], $r['pageviews']); 149 array_push($values, $date, $referrer_ids[$url], $r['visitors'], $r['pageviews']); 180 150 } 181 151 $placeholders = rtrim(str_repeat('(%s,%d,%d,%d),', count($stats)), ','); … … 192 162 // remove all data older than 60 minutes 193 163 $one_hour_ago = \time() - 60 * 60; 194 foreach ($counts as $timestamp => $ v) {164 foreach ($counts as $timestamp => $unused) { 195 165 // delete all data older than one hour 196 166 if ((int) $timestamp < $one_hour_ago) { -
koko-analytics/trunk/src/Resources/functions/functions.php
r3352698 r3378632 121 121 function get_realtime_pageview_count($since = null): int 122 122 { 123 if (is_numeric($since) || is_int($since)) {123 if (is_numeric($since)) { 124 124 $since = (int) $since; 125 } elseif (is_string($since)) {126 // $since is relative time string127 $since = strtotime($since);128 125 } else { 129 $since = strtotime( '-5 minutes');126 $since = strtotime($since ?? '-5 minutes'); 130 127 } 131 128 -
koko-analytics/trunk/src/Resources/functions/global.php
r3350963 r3378632 16 16 function koko_analyics_tracking_script(): void 17 17 { 18 $script_loader = new KokoAnalytics\Script_Loader(); 19 $script_loader->maybe_enqueue_script(true); 18 KokoAnalytics\Script_Loader::maybe_print_script(); 20 19 } 21 20 -
koko-analytics/trunk/src/Resources/views/dashboard-page.php
r3366958 r3378632 9 9 /** 10 10 * @var \KokoAnalytics\Dashboard $this 11 * @var \DateTimeInterface $date Start12 * @var \DateTimeInterface $date End11 * @var \DateTimeInterface $date_start 12 * @var \DateTimeInterface $date_end 13 13 * @var object $totals 14 14 * @var int $realtime 15 * @var string $date Format15 * @var string $date_format 16 16 * @var string $dashboard_url 17 17 * @var \KokoAnalytics\Dates $dates 18 18 * @var \KokoAnalytics\Stats $stats 19 * @var array $next_dates 20 * @var array $prev_dates 19 21 */ 20 22 … … 32 34 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar3 me-2" style="vertical-align: middle;" viewBox="0 0 16 16"><path d="M14 0H2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2M1 3.857C1 3.384 1.448 3 2 3h12c.552 0 1 .384 1 .857v10.286c0 .473-.448.857-1 .857H2c-.552 0-1-.384-1-.857z"/> 33 35 <path d="M6.5 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-9 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2m3 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2"/></svg> 34 <?php echo wp_date($date Format, $dateStart->getTimestamp()); ?> — <?php echo wp_date($dateFormat, $dateEnd->getTimestamp()); ?>36 <?php echo wp_date($date_format, $date_start->getTimestamp()); ?> — <?php echo wp_date($date_format, $date_end->getTimestamp()); ?> 35 37 </div> 36 38 … … 38 40 <div class="mb-3 bg-dark text-white p-3 rounded-top fw-bold d-flex justify-content-between"> 39 41 <?php // only output pagination for date ranges between reasonable dates... to prevent ever-crawling bots from going wild ?> 40 <?php if ($date Start > $total_start_date) { ?>41 <a class="js-quicknav-prev text-decoration-none text-white me-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $prev Dates[0]->format('Y-m-d'), 'end_date' => $prevDates[1]->format('Y-m-d')], $dashboard_url)); ?>">◂</a>42 <?php if ($date_start > $total_start_date) { ?> 43 <a class="js-quicknav-prev text-decoration-none text-white me-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $prev_dates[0]->format('Y-m-d'), 'end_date' => $prev_dates[1]->format('Y-m-d')], $dashboard_url)); ?>">◂</a> 42 44 <?php } else { ?> 43 45 <a class="text-decoration-none text-white me-2">◂</a> 44 46 <?php } ?> 45 <span><?php echo wp_date($date Format, $dateStart->getTimestamp()); ?> — <?php echo wp_date($dateFormat, $dateEnd->getTimestamp()); ?></span>46 <?php if ($date End < $total_end_date) { ?>47 <a class="js-quicknav-next text-decoration-none text-white ms-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $next Dates[0]->format('Y-m-d'), 'end_date' => $nextDates[1]->format('Y-m-d')], $dashboard_url)); ?>">▸</a>47 <span><?php echo wp_date($date_format, $date_start->getTimestamp()); ?> — <?php echo wp_date($date_format, $date_end->getTimestamp()); ?></span> 48 <?php if ($date_end < $total_end_date) { ?> 49 <a class="js-quicknav-next text-decoration-none text-white ms-2" href="<?php echo esc_attr(add_query_arg(['start_date' => $next_dates[0]->format('Y-m-d'), 'end_date' => $next_dates[1]->format('Y-m-d')], $dashboard_url)); ?>">▸</a> 48 50 <?php } else { ?> 49 51 <a class="text-decoration-none text-white ms-2">▸</a> … … 69 71 <label for='ka-date-start' class="ka-label"><?php esc_html_e('Start date', 'koko-analytics'); ?></label> 70 72 <input name="start_date" id='ka-date-start' type="date" size="10" min="2000-01-01" max="2100-01-01" 71 value="<?php echo $date Start->format('Y-m-d'); ?>" class="ka-input">73 value="<?php echo $date_start->format('Y-m-d'); ?>" class="ka-input"> 72 74 </div> 73 75 <div class="mb-3"> 74 76 <label for='ka-date-end' class="ka-label"><?php esc_html_e('End date', 'koko-analytics'); ?></label> 75 77 <input name="end_date" id='ka-date-end' type="date" size="10" min="2000-01-01" max="2100-01-01" 76 value="<?php echo $date End->format('Y-m-d'); ?>" class="ka-input">78 value="<?php echo $date_end->format('Y-m-d'); ?>" class="ka-input"> 77 79 </div> 78 80 <div> … … 89 91 </div> 90 92 91 <?php do_action('koko_analytics_after_datepicker', $date Start, $dateEnd); ?>93 <?php do_action('koko_analytics_after_datepicker', $date_start, $date_end); ?> 92 94 </div> 93 95 … … 168 170 <?php if (count($chart_data) > 1) { ?> 169 171 <div class="ka-box mb-3 p-3"> 170 <?php new Chart_View($chart_data, $date Start, $dateEnd); ?>172 <?php new Chart_View($chart_data, $date_start, $date_end); ?> 171 173 </div> 172 174 <?php } ?> … … 262 264 263 265 <?php do_action_deprecated('koko_analytics_show_dashboard_components', [], '1.4', 'koko_analytics_after_dashboard_components'); ?> 264 <?php do_action('koko_analytics_after_dashboard_components', $date Start, $dateEnd); ?>266 <?php do_action('koko_analytics_after_dashboard_components', $date_start, $date_end); ?> 265 267 </div><?php // end div.ka-row ?> 266 268 -
koko-analytics/trunk/src/Resources/views/dashboard-widget.php
r3352477 r3378632 8 8 * @var array $referrers 9 9 * @var stdClass $totals 10 * @var \DateTimeInterface $dateStart 11 * @var \DateTimeInterface $dateEnd 10 * @var \DateTimeInterface $today 11 * @var \DateTimeInterface $date_start 12 * @var \DateTimeInterface $date_end 12 13 */ 13 14 … … 31 32 <?php esc_html_e('Showing site visits over last 14 days', 'koko-analytics'); ?> 32 33 </h3> 33 <div class="">34 <?php new Chart_View($chart_data, $date Start, $dateEnd, 200, false); ?>34 <div> 35 <?php new Chart_View($chart_data, $date_start, $date_end, 200, false); ?> 35 36 </div> 36 37 </div> -
koko-analytics/trunk/src/Resources/views/nav.php
r3350963 r3378632 1 1 <?php 2 2 3 /** 3 4 * @var string $tab 4 5 */ 6 7 use KokoAnalytics\Router; 8 5 9 ?> 6 10 <?php if (current_user_can('manage_koko_analytics')) { ?> … … 8 12 <?php if (current_user_can('view_koko_analytics')) : ?> 9 13 <ul class="list-inline m-0"> 10 <li class="list-inline-item m-0 ms-2"><a href="<? php echo esc_attr(add_query_arg(['koko-analytics-dashboard' => 1], home_url())); ?>" <?php echo isset($_GET['koko-analytics-dashboard']) ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard (full screen)', 'koko-analytics'); ?></a></li>14 <li class="list-inline-item m-0 ms-2"><a href="<?= Router::url('dashboard-standalone'); ?>" <?= Router::is('dashboard-standalone') ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard (full screen)', 'koko-analytics'); ?></a></li> 11 15 12 <li class="list-inline-item m-0 ms-2"><a href="<? php echo admin_url('index.php?page=koko-analytics'); ?>" <?php echo $tab === 'dashboard' && !isset($_GET['koko-analytics-dashboard']) ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard', 'koko-analytics'); ?></a></li>16 <li class="list-inline-item m-0 ms-2"><a href="<?= Router::url('dashboard-embedded') ?>" <?= Router::is('dashboard-embedded') ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Dashboard', 'koko-analytics'); ?></a></li> 13 17 <?php endif; ?> 14 18 <?php if (current_user_can('manage_koko_analytics')) : ?> 15 <li class="list-inline-item m-0 ms-2"><a href="<? php echo admin_url('index.php?page=koko-analytics&tab=settings'); ?>" <?php echo $tab === 'settings'? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Settings', 'koko-analytics'); ?></a></li>19 <li class="list-inline-item m-0 ms-2"><a href="<?= Router::url('settings-page') ?>" <?= Router::is('settings-page') ? 'class="text-black" aria-current="page"' : ''; ?>><?php esc_html_e('Settings', 'koko-analytics'); ?></a></li> 16 20 <?php endif; ?> 17 21 </ul> -
koko-analytics/trunk/src/Resources/views/settings-page.php
r3366958 r3378632 2 2 3 3 use KokoAnalytics\Endpoint_Installer; 4 use KokoAnalytics\Router; 4 5 5 6 defined('ABSPATH') or exit; … … 14 15 */ 15 16 $tab = 'settings'; 16 $public_dashboard_url = add_query_arg(['koko-analytics-dashboard' => 1], home_url());17 $public_dashboard_url = Router::url('dashboard-standalone'); 17 18 ?> 18 19 <div class="wrap koko-analytics" id="koko-analytics-admin"> … … 238 239 <ul class="ul-square"> 239 240 <li><a href="<?php echo esc_attr(add_query_arg(['tab' => 'jetpack_importer'])); ?>"><?php esc_html_e('Import from Jetpack Stats', 'koko-analytics'); ?></a></li> 241 <li><a href="<?php echo esc_attr(add_query_arg(['tab' => 'plausible_importer'])); ?>"><?php esc_html_e('Import from Plausible', 'koko-analytics'); ?></a></li> 240 242 </ul> 241 243 </div> … … 262 264 } else { 263 265 // store empty array to prevent doing an HTTP request on every page load 264 // we'll try again in 8hours266 // we'll try again in 24 hours 265 267 $posts = []; 266 268 } 267 set_transient('koko_analytics_remote_posts', $posts, HOUR_IN_SECONDS * 8);269 set_transient('koko_analytics_remote_posts', $posts, HOUR_IN_SECONDS * 24); 268 270 } 269 271 … … 283 285 284 286 <?php if (isset($_GET['notice'])) { ?> 285 <script>history.replaceState({}, null, '<?= admin_url('index.php?page=koko-analytics&tab=settings'); ?>');</script>287 <script>history.replaceState({}, null, "<?= Router::url('settings-page'); ?>");</script> 286 288 <?php } ?> -
koko-analytics/trunk/src/Resources/views/standalone.php
r3350963 r3378632 11 11 <meta name="viewport" content="width=device-width, initial-scale=1"> 12 12 <meta name="referrer" content="no-referrer-when-downgrade"> 13 <meta name="robots" content="noindex,nofollow">14 13 <title>Koko Analytics</title> 15 14 <meta name="apple-mobile-web-app-capable" content="yes"> … … 19 18 <link rel="manifest" href="<?php echo plugins_url('assets/dist/manifest.json', KOKO_ANALYTICS_PLUGIN_FILE); ?>"> 20 19 <link rel="shortcut icon" href="<?php echo plugins_url('assets/dist/img/favicon.ico', KOKO_ANALYTICS_PLUGIN_FILE); ?>"> 20 <link rel="canonical" href="<?= site_url('?koko-analytics-dashboard'); ?>"> 21 <meta name="robots" content="nofollow, noindex"> 21 22 <meta name="theme-color" content="#B60205"> 22 23 </head> 23 24 <body class="koko-analytics"> 24 <?php $this->show(); ?>25 <?php parent::show(); ?> 25 26 <script> 26 27 if ('serviceWorker' in navigator) { -
koko-analytics/trunk/src/Rest.php
r3350963 r3378632 8 8 9 9 namespace KokoAnalytics; 10 11 use DateTimeImmutable; 10 12 11 13 class Rest … … 129 131 public function get_stats(\WP_REST_Request $request): \WP_REST_Response 130 132 { 133 $timezone = wp_timezone(); 131 134 $params = $request->get_query_params(); 132 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');133 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');135 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 136 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 134 137 $group = ($params['monthly'] ?? false) ? 'month' : 'day'; 135 138 $page = $params['page'] ?? 0; … … 143 146 public function get_totals(\WP_REST_Request $request): \WP_REST_Response 144 147 { 148 $timezone = wp_timezone(); 145 149 $params = $request->get_query_params(); 146 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');147 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');150 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 151 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 148 152 $page = $params['page'] ?? 0; 149 153 $result = (new Stats())->get_totals($start_date, $end_date, $page); … … 156 160 public function get_posts(\WP_REST_Request $request): \WP_REST_Response 157 161 { 162 $timezone = wp_timezone(); 158 163 $params = $request->get_query_params(); 159 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');160 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');164 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 165 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 161 166 $offset = isset($params['offset']) ? absint($params['offset']) : 0; 162 167 $limit = isset($params['limit']) ? absint($params['limit']) : 10; … … 170 175 public function get_referrers(\WP_REST_Request $request): \WP_REST_Response 171 176 { 177 $timezone = wp_timezone(); 172 178 $params = $request->get_query_params(); 173 $start_date = $params['start_date'] ?? create_local_datetime('first day of this month')->format('Y-m-d');174 $end_date = $params['end_date'] ?? create_local_datetime('now')->format('Y-m-d');179 $start_date = $params['start_date'] ?? (new DateTimeImmutable('first day of this month', $timezone))->format('Y-m-d'); 180 $end_date = $params['end_date'] ?? (new DateTimeImmutable('now', $timezone))->format('Y-m-d'); 175 181 $offset = isset($params['offset']) ? absint($params['offset']) : 0; 176 182 $limit = isset($params['limit']) ? absint($params['limit']) : 10; -
koko-analytics/trunk/src/Script_Loader.php
r3350963 r3378632 10 10 11 11 use KokoAnalytics\Normalizers\Normalizer; 12 use WP_User;13 12 14 13 class Script_Loader 15 14 { 16 /** 17 * @param bool $echo Whether to use the default WP script enqueue method or print the script tag directly 18 */ 19 public static function maybe_enqueue_script(bool $echo = false): void 15 public static function maybe_print_script(): void 20 16 { 21 17 $load_script = apply_filters('koko_analytics_load_tracking_script', true); … … 24 20 } 25 21 26 if (is_request_excluded() ) {22 if (is_request_excluded() || is_preview()) { 27 23 return; 28 24 } 29 25 30 // TODO: Handle "term" requests so we track both terms and post types. 31 add_filter('script_loader_tag', [ Script_Loader::class , 'add_async_attribute' ], 20, 2); 32 33 if (false === $echo) { 34 // Print configuration object early on in the HTML so scripts can modify it 35 if (did_action('wp_head')) { 36 self::print_js_object(); 37 } else { 38 add_action('wp_head', [ Script_Loader::class , 'print_js_object' ], 1, 0); 39 } 40 41 // Enqueue the actual tracking script (in footer, if possible) 42 wp_enqueue_script('koko-analytics', plugins_url('assets/dist/js/script.js', KOKO_ANALYTICS_PLUGIN_FILE), [], KOKO_ANALYTICS_VERSION, true); 43 } else { 44 self::print_js_object(); 45 echo '<script defer src="', plugins_url('assets/dist/js/script.js?ver=' . KOKO_ANALYTICS_VERSION, KOKO_ANALYTICS_PLUGIN_FILE), '"></script>'; 46 } 26 echo PHP_EOL . '<!-- Koko Analytics v' . KOKO_ANALYTICS_VERSION . ' - https://www.kokoanalytics.com/ -->' . PHP_EOL; 27 wp_print_inline_script_tag(file_get_contents(KOKO_ANALYTICS_PLUGIN_DIR . '/assets/dist/js/script.js')); 28 echo PHP_EOL; 47 29 } 48 30 … … 63 45 private static function get_tracker_url(): string 64 46 { 65 global $wp;66 67 47 // People can create their own endpoint and define it through this constant 68 48 if (\defined('KOKO_ANALYTICS_CUSTOM_ENDPOINT') && KOKO_ANALYTICS_CUSTOM_ENDPOINT) { … … 80 60 public static function get_request_path(): string 81 61 { 82 $path = trim($_SERVER["REQUEST_URI"] ?? ''); 83 return Normalizer::path($path); 62 return Normalizer::path(trim($_SERVER["REQUEST_URI"] ?? '')); 84 63 } 85 64 … … 132 111 echo '<amp-analytics><script type="application/json">', json_encode($config), '</script></amp-analytics>'; 133 112 } 134 135 /**136 * @param string $tag137 * @param string $handle138 */139 public static function add_async_attribute($tag, $handle)140 {141 if ($handle !== 'koko-analytics' || strpos($tag, ' defer') !== false) {142 return $tag;143 }144 145 return str_replace(' src=', ' defer src=', $tag);146 }147 113 } -
koko-analytics/trunk/src/Shortcodes/Shortcode_Site_Counter.php
r3364374 r3378632 20 20 use KokoAnalytics\Normalizers\Normalizer; 21 21 use KokoAnalytics\Stats; 22 23 use function KokoAnalytics\create_local_datetime;24 22 25 23 class Shortcode_Site_Counter -
koko-analytics/trunk/src/Stats.php
r3366958 r3378632 28 28 public function get_totals(string $start_date, string $end_date, $page = 0, $unused = null): object 29 29 { 30 /** @var wpdb $wpdb */30 /** @var \wpdb $wpdb */ 31 31 global $wpdb; 32 32 … … 78 78 public function get_stats(string $start_date, string $end_date, string $group = 'day', $page = ''): array 79 79 { 80 /** @var wpdb $wpdb */80 /** @var \wpdb $wpdb */ 81 81 global $wpdb; 82 82 … … 121 121 public function get_posts(string $start_date, string $end_date, int $offset = 0, int $limit = 10): array 122 122 { 123 /** @var wpdb $wpdb */123 /** @var \wpdb $wpdb */ 124 124 global $wpdb; 125 125 … … 128 128 FROM {$wpdb->prefix}koko_analytics_post_stats s 129 129 JOIN {$wpdb->prefix}koko_analytics_paths p ON p.id = s.path_id 130 LEFT JOIN {$wpdb->prefix}posts wp ON s.post_id = wp.ID130 LEFT JOIN {$wpdb->prefix}posts wp ON wp.ID = s.post_id 131 131 WHERE s.date >= %s AND s.date <= %s 132 132 GROUP BY p.path, s.post_id … … 138 138 return array_map(function ($row) { 139 139 $row->pageviews = (int) $row->pageviews; 140 $row->visitors = (int) $row->visitors;140 $row->visitors = max(1, (int) $row->visitors); 141 141 142 142 // for backwards compatibility with versions before 2.0 … … 151 151 public function count_posts(string $start_date, string $end_date): int 152 152 { 153 /** @var wpdb $wpdb */153 /** @var \wpdb $wpdb */ 154 154 global $wpdb; 155 155 return (int) $wpdb->get_var($wpdb->prepare( … … 169 169 public function get_referrers(string $start_date, string $end_date, int $offset = 0, int $limit = 10): array 170 170 { 171 /** @var wpdb $wpdb */ 172 global $wpdb; 173 return $wpdb->get_results($wpdb->prepare( 171 /** @var \wpdb $wpdb */ 172 global $wpdb; 173 174 return array_map(function ($row) { 175 $row->pageviews = (int) $row->pageviews; 176 $row->visitors = max(1, (int) $row->visitors); 177 return $row; 178 }, $wpdb->get_results($wpdb->prepare( 174 179 "SELECT s.id, url, SUM(visitors) As visitors, SUM(pageviews) AS pageviews 175 180 FROM {$wpdb->prefix}koko_analytics_referrer_stats s … … 180 185 LIMIT %d, %d", 181 186 [$start_date, $end_date, $offset, $limit] 182 )) ;187 ))); 183 188 } 184 189 185 190 public function count_referrers(string $start_date, string $end_date): int 186 191 { 187 /** @var wpdb $wpdb */192 /** @var \wpdb $wpdb */ 188 193 global $wpdb; 189 194 return (int) $wpdb->get_var($wpdb->prepare(
Note: See TracChangeset
for help on using the changeset viewer.