Changeset 3790302
- Timestamp:
- 11/08/2026 12:34:07 AM (40 hours ago)
- Location:
- visitoratlas/trunk
- Files:
-
- 11 added
- 4 edited
-
assets/dark.jpg (added)
-
assets/light.jpg (added)
-
assets/newsletter-confirmation.js (added)
-
assets/political.jpg (added)
-
assets/report-columns.js (added)
-
assets/report-refresh.js (added)
-
assets/texture-picker.js (added)
-
includes/acquisition-dashboard.php (modified) (4 diffs)
-
includes/advanced-reporting.php (added)
-
includes/conversion-integrations.php (added)
-
includes/core-enhancements.php (added)
-
includes/newsletter-confirmation.php (added)
-
includes/presentation.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
-
visitoratlas.php (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
visitoratlas/trunk/includes/acquisition-dashboard.php
r3757704 r3790302 54 54 public function acquisition_report_context( $input = null ) { 55 55 $input = is_array( $input ) ? $input : $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only dashboard filters. 56 $sites = $this->acquisition_sites();57 56 $primary = self::primary_site_id(); 58 $site_id = isset( $input['site_id'] ) ? absint( $input['site_id'] ) : $primary; 59 $site_unavailable = $site_id && ! isset( $sites[$site_id] ); 60 if ( $site_unavailable ) { $site_id = $primary; } 61 $site = $sites[$site_id] ?? array( 'id' => $primary, 'site_name' => get_bloginfo( 'name' ), 'site_url' => home_url( '/' ), 'created_at' => '' ); 57 $site_id = $primary; 58 $site = array( 'id' => $primary, 'site_name' => get_bloginfo( 'name' ), 'site_url' => home_url( '/' ), 'created_at' => '' ); 62 59 $timezone = wp_timezone(); 63 60 $today = new DateTimeImmutable( 'today', $timezone ); 64 61 $oldest = $this->acquisition_date( $this->acquisition_installed_on( $site ), $timezone ) ?: $today; 65 62 if ( $oldest > $today ) { $oldest = $today; } 66 $preset = isset( $input['range'] ) && is_string( $input['range'] ) ? sanitize_key( $input['range'] ) : ' month';63 $preset = isset( $input['range'] ) && is_string( $input['range'] ) ? sanitize_key( $input['range'] ) : 'last_30_days'; 67 64 $start = $today; 68 if ( 'week' === $preset ) { 69 $weekday = (int) $today->format( 'w' ); 70 $start = $today->modify( '-' . ( ( $weekday - (int) get_option( 'start_of_week', 0 ) + 7 ) % 7 ) . ' days' ); 71 } elseif ( 'month' === $preset ) { 72 $start = $today->modify( 'first day of this month' ); 73 } elseif ( 'year' === $preset ) { 74 $start = $today->setDate( (int) $today->format( 'Y' ), 1, 1 ); 75 } elseif ( 'custom' === $preset ) { 76 $start = $this->acquisition_date( $input['from'] ?? null, $timezone ) ?: $today; 65 if ( 'last_7_days' === $preset ) { 66 $start = $today->modify( '-6 days' ); 67 } elseif ( 'last_30_days' === $preset ) { 68 $start = $today->modify( '-29 days' ); 69 } elseif ( 'last_90_days' === $preset ) { 70 $start = $today->modify( '-89 days' ); 71 } elseif ( 'all_retained_data' === $preset ) { 72 $start = $oldest; 77 73 } else { 78 $preset = 'today'; 79 } 80 $end = 'custom' === $preset ? ( $this->acquisition_date( $input['to'] ?? null, $timezone ) ?: $today ) : $today; 74 $preset = 'last_30_days'; 75 $start = $today->modify( '-29 days' ); 76 } 77 $end = $today; 81 78 if ( $start < $oldest ) { $start = $oldest; } 82 79 if ( $start > $today ) { $start = $today; } … … 88 85 if ( ! in_array( $channel, $channels, true ) ) { $channel = ''; } 89 86 return array( 90 'site s' => $sites, 'site' => $site, 'site_id' => $site_id, 'site_unavailable' => $site_unavailable, 'oldest' => $oldest->format( 'Y-m-d' ),87 'site' => $site, 'site_id' => $site_id, 'oldest' => $oldest->format( 'Y-m-d' ), 91 88 'preset' => $preset, 'from' => $start->format( 'Y-m-d' ), 'to' => $end->format( 'Y-m-d' ), 92 89 'from_utc' => $start->setTimezone( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d H:i:s' ), … … 152 149 public function render_acquisition_dashboard() { 153 150 $context = $this->acquisition_report_context(); 154 if ( ! empty( $context['site_unavailable'] ) ) {155 echo '<div class="notice notice-error"><p>The selected site is unavailable or could not be authenticated. No report was loaded.</p></div>';156 return;157 }158 151 echo '<form class="va-acquisition-filters" method="get" action="' . esc_url( admin_url( 'admin.php' ) ) . '"><input type="hidden" name="page" value="ps-lvg-acquisition">'; 159 if ( count( $context['sites'] ) > 1 ) {160 echo '<label for="va-report-site">Site</label><select id="va-report-site" name="site_id">';161 foreach ( $context['sites'] as $id => $site ) {162 echo '<option value="' . esc_attr( $id ) . '" ' . selected( $context['site_id'], $id, false ) . '>' . esc_html( $site['site_name'] ?: $site['site_url'] ) . '</option>';163 }164 echo '</select>';165 } else {166 echo '<span class="va-acquisition-site"><strong>Site:</strong> ' . esc_html( $context['site']['site_name'] ?: $context['site']['site_url'] ) . '</span>';167 }168 152 echo '<label for="va-report-range">Date range</label><select id="va-report-range" name="range">'; 169 foreach ( array( ' today' => 'Today', 'week' => 'This Week', 'month' => 'This Month', 'year' => 'This Year', 'custom' => 'Custom' ) as $value => $label ) {153 foreach ( array( 'last_7_days' => 'Last 7 days', 'last_30_days' => 'Last 30 days', 'last_90_days' => 'Last 90 days', 'all_retained_data' => 'All retained data' ) as $value => $label ) { 170 154 echo '<option value="' . esc_attr( $value ) . '" ' . selected( $context['preset'], $value, false ) . '>' . esc_html( $label ) . '</option>'; 171 155 } 172 echo '</select> <span class="va-acquisition-custom"><label for="va-report-from">From</label><input id="va-report-from" type="date" name="from" min="' . esc_attr( $context['oldest'] ) . '" max="' . esc_attr( wp_date( 'Y-m-d' ) ) . '" value="' . esc_attr( $context['from'] ) . '"><label for="va-report-to">To</label><input id="va-report-to" type="date" name="to" min="' . esc_attr( $context['oldest'] ) . '" max="' . esc_attr( wp_date( 'Y-m-d' ) ) . '" value="' . esc_attr( $context['to'] ) . '"></span>';156 echo '</select>'; 173 157 echo '<label for="va-report-channel">Channel</label><select id="va-report-channel" name="channel"><option value="">All channels</option>'; 174 158 foreach ( $context['channels'] as $channel ) { echo '<option value="' . esc_attr( $channel ) . '" ' . selected( $context['channel'], $channel, false ) . '>' . esc_html( $channel ) . '</option>'; } … … 178 162 echo '</div>'; 179 163 echo '<p class="description">Dates use the site timezone. Reports show retained visits only; the earliest selectable date is the first known installation or recorded visit date. Visitors are deduplicated by a salted browser identifier. WordPress signups use account creation dates and each account\'s earliest retained human visit for channel attribution.</p>'; 180 if ( has_action( 'visitoratlas_acquisition_reports' ) ) {181 echo '<p class="notice notice-info inline" style="padding:8px 12px">Additional Pro reports below use their own retained-history period and do not yet follow these Core filters.</p>';182 }183 164 } 184 165 -
visitoratlas/trunk/includes/presentation.php
r3790089 r3790302 55 55 echo '</div><p>© 2026 Ultimate Liberty Ventures Trust · VisitorAtlas ' . esc_html( self::VERSION ) . '</p></div>'; 56 56 } 57 public function integrations_page() {58 if ( ! current_user_can( 'manage_options' ) ) { return; }59 if ( has_action( 'visitoratlas_integrations_page' ) ) { do_action( 'visitoratlas_integrations_page', $this ); return; }60 echo '<div class="wrap"><h1>Conversion Integrations</h1><p>VisitorAtlas Pro supplies configurable provider adapters for Elementor Forms, MailPoet forms, Mailchimp for WordPress, Gravity Forms, and WPForms.</p><p>Install and activate the separate VisitorAtlas Pro add-on to track conversions from these providers. Existing settings and conversion records remain intact.</p></div>';61 }62 57 } -
visitoratlas/trunk/readme.txt
r3790089 r3790302 1 1 === VisitorAtlas === 2 2 Contributors: visitoratlas 3 Donate link: https://visitoratlas.io/donate.php 3 4 Tags: analytics, visitors, globe, acquisition, statistics 4 5 Requires at least: 6.5 5 6 Tested up to: 7.1 6 7 Requires PHP: 7.4 7 Stable tag: 2.6.2 48 Stable tag: 2.6.25 8 9 License: GPLv2 or later 9 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 157 158 == Changelog == 158 159 160 = 2.6.25 = 161 * Core now includes local advanced reporting, conversion integrations, newsletter confirmation-page tracking, additional map styles, custom textures, page-flow detail, and bot reporting. 162 * Simplified the acquisition report date selector to last 7, 30, or 90 days, or all retained data; reports now apply to this WordPress installation only. 163 * Added an optional donation link to help fund ongoing development. 164 159 165 = 2.6.24 = 160 166 * Arrange Core settings in two columns with the existing live map preview beside the settings and a single-column layout on narrower screens. No settings behaviour changes. -
visitoratlas/trunk/visitoratlas.php
r3790089 r3790302 3 3 * Plugin Name: VisitorAtlas 4 4 * Description: Interactive live visitor Globe and Polar Azimuthal maps with configurable rotation, zoom, daylight, and touch/mouse controls. 5 * Version: 2.6.2 45 * Version: 2.6.25 6 6 * Author: VisitorAtlas SI 7 7 * Author URI: https://profiles.wordpress.org/visitoratlas/ … … 28 28 require_once __DIR__ . '/includes/reporting.php'; 29 29 require_once __DIR__ . '/includes/acquisition-dashboard.php'; 30 require_once __DIR__ . '/includes/advanced-reporting.php'; 31 require_once __DIR__ . '/includes/conversion-integrations.php'; 32 require_once __DIR__ . '/includes/newsletter-confirmation.php'; 33 require_once __DIR__ . '/includes/core-enhancements.php'; 30 34 require_once __DIR__ . '/includes/weekly-email-report.php'; 31 35 require_once __DIR__ . '/includes/sites.php'; … … 41 45 use VisitorAtlas_Reporting; 42 46 use VisitorAtlas_Acquisition_Dashboard; 47 use VisitorAtlas_Core_Advanced_Reporting; 48 use VisitorAtlas_Core_Conversion_Integrations; 49 use VisitorAtlas_Core_Newsletter_Confirmation; 50 use VisitorAtlas_Core_Enhancements; 43 51 use VisitorAtlas_Weekly_Email_Report; 44 52 use VisitorAtlas_Tracked_Sites; 45 const VERSION = '2.6.2 4';53 const VERSION = '2.6.25'; 46 54 const DB_VERSION = '1.11.1'; 47 55 const OPTION = 'ps_lvg_settings'; … … 68 76 add_action( 'init', array( $this, 'ready' ), 3 ); 69 77 add_action( 'init', array( $this, 'boot_mailpoet_pending_confirmations' ), 4 ); 78 add_action( 'init', array( $this, 'boot_conversion_integrations' ), 4 ); 79 $this->boot_newsletter_confirmation(); 80 add_filter( 'visitoratlas_globe_styles', array( $this, 'core_extended_globe_styles' ) ); 81 add_filter( 'visitoratlas_globe_config', array( $this, 'page_flow_globe_config' ), 10, 2 ); 82 add_filter( 'visitoratlas_globe_point', array( $this, 'point_data' ), 10, 2 ); 83 add_action( 'visitoratlas_globe_settings_fields', array( $this, 'texture_field' ) ); 84 add_action( 'visitoratlas_globe_color_settings_rows', array( $this, 'multi_page_color_field' ) ); 85 add_filter( 'visitoratlas_sanitize_presentation_settings', array( $this, 'sanitize_texture' ), 10, 2 ); 86 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_texture_picker' ) ); 87 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_acquisition_columns' ), 20 ); 88 add_action( 'visitoratlas_acquisition_reports', array( $this, 'render_pro_reports' ) ); 89 add_action( 'visitoratlas_acquisition_page_init', array( $this, 'handle_report_action' ) ); 90 add_action( 'wp_ajax_visitoratlas_advanced_acquisition_report', array( $this, 'ajax_pro_acquisition_report' ) ); 91 add_filter( 'visitoratlas_dashboard_counts', array( $this, 'add_bot_dashboard_count' ), 10, 2 ); 70 92 add_action( 'init', array( $this, 'maybe_schedule_mailpoet_reconciliation' ), 20 ); 71 93 add_action( 'visitoratlas_mailpoet_reconcile', array( $this, 'reconcile_mailpoet_pending' ) ); … … 292 314 'geolocation_enabled' => 0, 293 315 'custom_texture_id' => 0, 316 'multi_page_color' => '#f59e0b', 294 317 'button_background_color' => '#132335', 295 318 'button_border_weight' => 1, … … 1053 1076 add_submenu_page( 'visitoratlas', 'UTM Links', 'UTM Links', 'manage_options', 'visitoratlas-utm-links', array( $this, 'utm_links_page' ) ); 1054 1077 add_submenu_page( 'visitoratlas', 'Conversion Integrations', 'Conversion Integrations', 'manage_options', 'ps-lvg-integrations', array( $this, 'integrations_page' ) ); 1055 if ( ! isset( $GLOBALS['visitoratlas_pro'] ) ) { 1056 add_submenu_page( 'visitoratlas', 'Upgrade to Pro', 'Upgrade to Pro', 'manage_options', 'visitoratlas-upgrade-pro', array( $this, 'upgrade_to_pro_page' ) ); 1057 } 1078 add_submenu_page( 'visitoratlas', 'Help fund ongoing development', 'Help fund ongoing development', 'manage_options', 'visitoratlas-donate', array( $this, 'donation_page' ) ); 1058 1079 do_action( 'visitoratlas_admin_menu', $this ); 1059 1080 } 1060 1081 1061 1082 public function enqueue_admin_menu_assets() { 1062 if ( ! current_user_can( 'manage_options' ) || isset( $GLOBALS['visitoratlas_pro'] )) { return; }1083 if ( ! current_user_can( 'manage_options' ) ) { return; } 1063 1084 $base_url = plugin_dir_url( __FILE__ ); 1064 1085 wp_register_style( 'visitoratlas-admin-menu', $base_url . 'assets/admin-menu.css', array(), self::VERSION ); … … 1068 1089 } 1069 1090 1070 public function upgrade_to_pro_page() {1071 if ( ! current_user_can( 'manage_options' ) ) { return; }1072 echo '<div class="wrap"><h1>Upgrade to Pro</h1><p>Explore VisitorAtlas Pro plans and choose the coverage for your WordPress sites.</p><p><a class="button button-primary" href="https://visitoratlas.io/#pricing" target="_blank" rel="noopener noreferrer">View Pro plans</a></p></div>';1073 }1074 1075 1091 public function redirect_legacy_globe_settings() { 1076 1092 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin navigation parameter used solely for a safe redirect. … … 1078 1094 wp_safe_redirect( admin_url( 'admin.php?page=visitoratlas' ) ); 1079 1095 exit; 1096 } 1097 1098 public function donation_page() { 1099 if ( ! current_user_can( 'manage_options' ) ) { return; } 1100 echo '<div class="wrap"><h1>Help fund ongoing development</h1><p>VisitorAtlas Core is free software. Voluntary contributions help support maintenance, privacy work, compatibility testing, and documentation.</p><p>A contribution does not purchase a licence, feature, support level, Cloud Sites access, credit, or any other product or service.</p><p><a class="button button-secondary" href="https://visitoratlas.io/donate.php" target="_blank" rel="noopener noreferrer">Help fund ongoing development</a></p></div>'; 1080 1101 } 1081 1102
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)