Plugin Directory

Changeset 3790302


Ignore:
Timestamp:
11/08/2026 12:34:07 AM (40 hours ago)
Author:
visitoratlas
Message:

Release VisitorAtlas Core 2.6.25: local reporting and presentation features in Core, simplified reports, and voluntary donation support.

Location:
visitoratlas/trunk
Files:
11 added
4 edited

Legend:

Unmodified
Added
Removed
  • visitoratlas/trunk/includes/acquisition-dashboard.php

    r3757704 r3790302  
    5454    public function acquisition_report_context( $input = null ) {
    5555        $input = is_array( $input ) ? $input : $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only dashboard filters.
    56         $sites = $this->acquisition_sites();
    5756        $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' => '' );
    6259        $timezone = wp_timezone();
    6360        $today = new DateTimeImmutable( 'today', $timezone );
    6461        $oldest = $this->acquisition_date( $this->acquisition_installed_on( $site ), $timezone ) ?: $today;
    6562        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';
    6764        $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;
    7773        } 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;
    8178        if ( $start < $oldest ) { $start = $oldest; }
    8279        if ( $start > $today ) { $start = $today; }
    … …  
    8885        if ( ! in_array( $channel, $channels, true ) ) { $channel = ''; }
    8986        return array(
    90             'sites' => $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' ),
    9188            'preset' => $preset, 'from' => $start->format( 'Y-m-d' ), 'to' => $end->format( 'Y-m-d' ),
    9289            'from_utc' => $start->setTimezone( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d H:i:s' ),
    … …  
    152149    public function render_acquisition_dashboard() {
    153150        $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         }
    158151        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         }
    168152        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 ) {
    170154            echo '<option value="' . esc_attr( $value ) . '" ' . selected( $context['preset'], $value, false ) . '>' . esc_html( $label ) . '</option>';
    171155        }
    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>';
    173157        echo '<label for="va-report-channel">Channel</label><select id="va-report-channel" name="channel"><option value="">All channels</option>';
    174158        foreach ( $context['channels'] as $channel ) { echo '<option value="' . esc_attr( $channel ) . '" ' . selected( $context['channel'], $channel, false ) . '>' . esc_html( $channel ) . '</option>'; }
    … …  
    178162        echo '</div>';
    179163        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         }
    183164    }
    184165
  • visitoratlas/trunk/includes/presentation.php

    r3790089 r3790302  
    5555        echo '</div><p>© 2026 Ultimate Liberty Ventures Trust · VisitorAtlas ' . esc_html( self::VERSION ) . '</p></div>';
    5656    }
    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     }
    6257}
  • visitoratlas/trunk/readme.txt

    r3790089 r3790302  
    11=== VisitorAtlas ===
    22Contributors: visitoratlas
     3Donate link: https://visitoratlas.io/donate.php
    34Tags: analytics, visitors, globe, acquisition, statistics
    45Requires at least: 6.5
    56Tested up to: 7.1
    67Requires PHP: 7.4
    7 Stable tag: 2.6.24
     8Stable tag: 2.6.25
    89License: GPLv2 or later
    910License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    157158== Changelog ==
    158159
     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
    159165= 2.6.24 =
    160166* 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  
    33 * Plugin Name: VisitorAtlas
    44 * Description: Interactive live visitor Globe and Polar Azimuthal maps with configurable rotation, zoom, daylight, and touch/mouse controls.
    5  * Version: 2.6.24
     5 * Version: 2.6.25
    66 * Author: VisitorAtlas SI
    77 * Author URI: https://profiles.wordpress.org/visitoratlas/
    … …  
    2828require_once __DIR__ . '/includes/reporting.php';
    2929require_once __DIR__ . '/includes/acquisition-dashboard.php';
     30require_once __DIR__ . '/includes/advanced-reporting.php';
     31require_once __DIR__ . '/includes/conversion-integrations.php';
     32require_once __DIR__ . '/includes/newsletter-confirmation.php';
     33require_once __DIR__ . '/includes/core-enhancements.php';
    3034require_once __DIR__ . '/includes/weekly-email-report.php';
    3135require_once __DIR__ . '/includes/sites.php';
    … …  
    4145    use VisitorAtlas_Reporting;
    4246    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;
    4351    use VisitorAtlas_Weekly_Email_Report;
    4452    use VisitorAtlas_Tracked_Sites;
    45     const VERSION = '2.6.24';
     53    const VERSION = '2.6.25';
    4654    const DB_VERSION = '1.11.1';
    4755    const OPTION  = 'ps_lvg_settings';
    … …  
    6876        add_action( 'init', array( $this, 'ready' ), 3 );
    6977        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 );
    7092        add_action( 'init', array( $this, 'maybe_schedule_mailpoet_reconciliation' ), 20 );
    7193        add_action( 'visitoratlas_mailpoet_reconcile', array( $this, 'reconcile_mailpoet_pending' ) );
    … …  
    292314            'geolocation_enabled' => 0,
    293315            'custom_texture_id' => 0,
     316            'multi_page_color' => '#f59e0b',
    294317            'button_background_color' => '#132335',
    295318            'button_border_weight' => 1,
    … …  
    10531076        add_submenu_page( 'visitoratlas', 'UTM Links', 'UTM Links', 'manage_options', 'visitoratlas-utm-links', array( $this, 'utm_links_page' ) );
    10541077        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' ) );
    10581079        do_action( 'visitoratlas_admin_menu', $this );
    10591080    }
    10601081
    10611082    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; }
    10631084        $base_url = plugin_dir_url( __FILE__ );
    10641085        wp_register_style( 'visitoratlas-admin-menu', $base_url . 'assets/admin-menu.css', array(), self::VERSION );
    … …  
    10681089    }
    10691090
    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 
    10751091    public function redirect_legacy_globe_settings() {
    10761092        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only admin navigation parameter used solely for a safe redirect.
    … …  
    10781094        wp_safe_redirect( admin_url( 'admin.php?page=visitoratlas' ) );
    10791095        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>';
    10801101    }
    10811102
Note: See TracChangeset for help on using the changeset viewer.