' . esc_html__( 'Support (Telegram)', 'visitrix' ) . ''; $links[] = '' . esc_html__( 'Donate', 'visitrix' ) . ''; } return $links; } /** * Menu entries. */ public static function menu() { $cap = Visitrix_Settings::view_capability(); self::$hook = add_menu_page( __( 'Visitrix Statistics', 'visitrix' ), __( 'Statistics', 'visitrix' ), $cap, 'visitrix', array( __CLASS__, 'render_dashboard' ), 'dashicons-chart-area' ); add_submenu_page( 'visitrix', __( 'Visitrix Statistics', 'visitrix' ), __( 'Summary', 'visitrix' ), $cap, 'visitrix', array( __CLASS__, 'render_dashboard' ) ); self::$maps_hook = (string) add_submenu_page( 'visitrix', __( 'Scroll map', 'visitrix' ), __( 'Scroll map', 'visitrix' ), $cap, 'visitrix-maps', array( __CLASS__, 'render_maps' ) ); self::$settings_hook = (string) add_submenu_page( 'visitrix', __( 'Visitrix Settings', 'visitrix' ), __( 'Settings', 'visitrix' ), 'manage_options', 'visitrix-settings', array( __CLASS__, 'render_settings' ) ); } /** * Plugins list links. * * @param array $links Links. * @return array */ public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Statistics', 'visitrix' ) . '', '' . esc_html__( 'Settings', 'visitrix' ) . '' ); return $links; } /** * Enqueues dashboard assets only on the plugin page. * * @param string $hook Current admin page. */ public static function assets( $hook ) { if ( $hook === self::$settings_hook ) { wp_enqueue_script( 'visitrix-settings', VISITRIX_URL . 'assets/js/settings.js', array(), VISITRIX_VERSION, true ); wp_localize_script( 'visitrix-settings', 'visitrixSettings', array( 'domain' => Visitrix_Tracker::cookie_domain(), 'counted' => __( 'Visits from this browser are counted.', 'visitrix' ), 'ignored' => __( 'Visits from this browser are not counted.', 'visitrix' ), 'ignore' => __( 'Do not count this browser', 'visitrix' ), 'resume' => __( 'Count this browser again', 'visitrix' ), ) ); wp_enqueue_script( 'visitrix-storage', VISITRIX_URL . 'assets/js/storage.js', array(), VISITRIX_VERSION, true ); wp_localize_script( 'visitrix-storage', 'visitrixStorage', Visitrix_Admin_Storage::script_data() ); return; } if ( $hook === self::$maps_hook ) { wp_enqueue_style( 'visitrix-admin', VISITRIX_URL . 'assets/css/admin.css', array(), VISITRIX_VERSION ); wp_enqueue_script( 'visitrix-maps', VISITRIX_URL . 'assets/js/maps.js', array(), VISITRIX_VERSION, true ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only page filter. $path = isset( $_GET['path'] ) ? sanitize_text_field( wp_unslash( $_GET['path'] ) ) : ''; wp_localize_script( 'visitrix-maps', 'visitrixMaps', array( 'rest' => esc_url_raw( rest_url( 'visitrix/v1/scrollmap' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'today' => current_time( 'Y-m-d' ), 'locale' => str_replace( '_', '-', get_user_locale() ), 'home' => esc_url_raw( home_url() ), 'path' => ( '' !== $path && '/' === $path[0] ) ? $path : '', 'i18n' => array( 'title' => __( 'Scroll map', 'visitrix' ), 'page' => __( 'Page', 'visitrix' ), 'period' => __( 'Period', 'visitrix' ), 'days7' => __( 'Last 7 days', 'visitrix' ), 'days30' => __( 'Last 30 days', 'visitrix' ), 'days90' => __( 'Last 90 days', 'visitrix' ), 'all' => __( 'All devices', 'visitrix' ), 'desktop' => __( 'PC', 'visitrix' ), 'mobile' => __( 'Smartphones', 'visitrix' ), 'tablet' => __( 'Tablets', 'visitrix' ), 'reached' => __( 'scrolled to here', 'visitrix' ), 'reachedEnd' => __( 'read to the end:', 'visitrix' ), 'basedOn' => __( 'Pageviews:', 'visitrix' ), 'legend' => __( 'share of visitors who scrolled to this part of the page', 'visitrix' ), 'noData' => __( 'No scroll data for this page and period yet. Data is collected starting from version 1.3.', 'visitrix' ), 'frameBlocked' => __( 'The page cannot be shown inside the admin (it forbids framing). The bands are drawn on an approximate height.', 'visitrix' ), 'error' => __( 'Could not load statistics.', 'visitrix' ), ), ) ); return; } if ( $hook !== self::$hook ) { return; } wp_enqueue_style( 'visitrix-admin', VISITRIX_URL . 'assets/css/admin.css', array(), VISITRIX_VERSION ); wp_enqueue_script( 'visitrix-admin', VISITRIX_URL . 'assets/js/admin.js', array(), VISITRIX_VERSION, true ); wp_localize_script( 'visitrix-admin', 'visitrixAdmin', array( 'rest' => esc_url_raw( rest_url( 'visitrix/v1/stats' ) ), 'online' => esc_url_raw( rest_url( 'visitrix/v1/online' ) ), 'maps' => esc_url_raw( admin_url( 'admin.php?page=visitrix-maps' ) ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'today' => current_time( 'Y-m-d' ), 'locale' => str_replace( '_', '-', get_user_locale() ), 'home' => esc_url_raw( home_url() ), 'i18n' => self::js_strings(), ) ); } /** * Translatable strings used by admin.js. * * @return array */ private static function js_strings() { return array( 'title' => __( 'Summary', 'visitrix' ), 'today' => __( 'Today', 'visitrix' ), 'yesterday' => __( 'Yesterday', 'visitrix' ), 'days3' => __( '3 days', 'visitrix' ), 'week' => __( 'Week', 'visitrix' ), 'month' => __( 'Month', 'visitrix' ), 'custom' => __( 'Custom range', 'visitrix' ), 'apply' => __( 'Apply', 'visitrix' ), 'refresh' => __( 'Refresh', 'visitrix' ), 'visits' => __( 'Visits', 'visitrix' ), 'visitors' => __( 'Visitors', 'visitrix' ), 'pageviews' => __( 'Pageviews', 'visitrix' ), 'bounce' => __( 'Bounce rate', 'visitrix' ), 'depth' => __( 'Page depth', 'visitrix' ), 'duration' => __( 'Time on site', 'visitrix' ), 'newVisitors' => __( 'New visitors', 'visitrix' ), 'views' => __( 'Views', 'visitrix' ), 'newReturning' => __( 'New and returning visitors', 'visitrix' ), 'visitorType' => __( 'Visitor type', 'visitrix' ), 'returning' => __( 'Returning visitors', 'visitrix' ), 'loyalty' => __( 'Visits per visitor (all time)', 'visitrix' ), 'loyaltyVisits' => __( 'visits', 'visitrix' ), 'anonNote' => __( 'Visits counted in anonymous mode (without consent) are not included here:', 'visitrix' ), 'online' => __( 'online now', 'visitrix' ), 'goals' => __( 'Goals', 'visitrix' ), 'goalsConverted' => __( 'visits with goals', 'visitrix' ), 'conversion' => __( 'Conversion', 'visitrix' ), 'goal' => __( 'Goal', 'visitrix' ), 'reaches' => __( 'Reached', 'visitrix' ), 'goalSources' => __( 'Conversion by traffic source', 'visitrix' ), 'noGoals' => __( 'Goals appear automatically after the first clicks on phone numbers, e-mails, messengers, files, outbound links and form submissions.', 'visitrix' ), 'g_form' => __( 'Form submission', 'visitrix' ), 'g_tel' => __( 'Phone call', 'visitrix' ), 'g_email' => __( 'E-mail', 'visitrix' ), 'g_messenger' => __( 'Messenger', 'visitrix' ), 'g_download' => __( 'File download', 'visitrix' ), 'g_outbound' => __( 'Outbound link', 'visitrix' ), 'content' => __( 'Content', 'visitrix' ), 'posts' => __( 'Posts', 'visitrix' ), 'categories' => __( 'Categories', 'visitrix' ), 'tags' => __( 'Tags', 'visitrix' ), 'authors' => __( 'Authors', 'visitrix' ), 'name' => __( 'Name', 'visitrix' ), 'scrollDepth' => __( 'Read depth', 'visitrix' ), 'scrollMap' => __( 'Open the scroll map', 'visitrix' ), 'onlineHint' => __( 'Visitors active during the last 5 minutes', 'visitrix' ), 'share' => __( 'Share', 'visitrix' ), 'vsPrev' => __( 'compared to the previous period', 'visitrix' ), 'bounceHint' => __( 'Share of visits with one pageview lasting less than 15 seconds', 'visitrix' ), 'depthHint' => __( 'Average number of pages viewed per visit', 'visitrix' ), 'durationHint' => __( 'Average visit duration', 'visitrix' ), 'newHint' => __( 'Share of visitors who came for the first time', 'visitrix' ), 'traffic' => __( 'Traffic', 'visitrix' ), 'byHour' => __( 'by hour', 'visitrix' ), 'byDay' => __( 'by day', 'visitrix' ), 'sources' => __( 'Traffic sources', 'visitrix' ), 'sourceDetails' => __( 'Sites, search engines and social networks', 'visitrix' ), 'search' => __( 'Search engines', 'visitrix' ), 'devices' => __( 'Device type', 'visitrix' ), 'browsers' => __( 'Browsers', 'visitrix' ), 'os' => __( 'Operating systems', 'visitrix' ), 'pages' => __( 'Popular pages', 'visitrix' ), 'landing' => __( 'Landing pages', 'visitrix' ), 'utm' => __( 'UTM campaigns', 'visitrix' ), 'source' => __( 'Source', 'visitrix' ), 'page' => __( 'Page', 'visitrix' ), 'noData' => __( 'No data for the selected period', 'visitrix' ), 'loading' => __( 'Loading…', 'visitrix' ), 'error' => __( 'Could not load statistics.', 'visitrix' ), 'other' => __( 'Other', 'visitrix' ), 'total' => __( 'Total', 'visitrix' ), 'src_direct' => __( 'Direct traffic', 'visitrix' ), 'src_search' => __( 'Search engine traffic', 'visitrix' ), 'src_social' => __( 'Social network traffic', 'visitrix' ), 'src_messenger' => __( 'Messenger traffic', 'visitrix' ), 'src_ai' => __( 'AI assistant traffic', 'visitrix' ), 'src_referral' => __( 'Link traffic', 'visitrix' ), 'src_ad' => __( 'Ad traffic', 'visitrix' ), 'src_internal' => __( 'Internal traffic', 'visitrix' ), 'dev_desktop' => __( 'PC', 'visitrix' ), 'dev_mobile' => __( 'Smartphones', 'visitrix' ), 'dev_tablet' => __( 'Tablets', 'visitrix' ), 'updated' => __( 'Updated', 'visitrix' ), ); } /** * Dashboard page (rendered by admin.js). */ public static function render_dashboard() { echo '
'; echo esc_html( number_format_i18n( Visitrix_Stats::online() ) . ' ' . __( 'online now', 'visitrix' ) ); echo '' . esc_html__( 'Open full report →', 'visitrix' ) . '
'; } /** * Suggested privacy policy text. */ public static function privacy_content() { if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) { return; } $modes = array( 'storage' => __( 'A random anonymous identifier is stored in your browser’s localStorage to distinguish new and returning visitors.', 'visitrix' ), 'cookie' => __( 'A random anonymous identifier is stored in the first-party cookie "visitrix_vid" (valid for 400 days) and in your browser’s localStorage to distinguish new and returning visitors.', 'visitrix' ), 'anonymous' => __( 'Nothing is stored in your browser. To group pageviews into visits, a hash of your IP address and browser is used; it changes every day and the IP address itself is not stored.', 'visitrix' ), ); $mode = Visitrix_Settings::get()['id_mode']; $text = '' . esc_html__( 'This site uses a self-hosted statistics tool (Visitrix). When you visit pages, we record the page address, the referring site, UTM tags, your browser, operating system and device type, as well as the time spent on the site.', 'visitrix' ) . ' ' . esc_html__( 'We also record how far pages are scrolled and clicks on phone numbers, e-mail addresses, messengers, files and external links, as well as form submissions (without the contents of the forms).', 'visitrix' ) . ' ' . esc_html( isset( $modes[ $mode ] ) ? $modes[ $mode ] : $modes['storage'] ) . ' ' . esc_html__( 'IP addresses are not stored. The data is kept on our server only and is not shared with third parties.', 'visitrix' ) . '
'; wp_add_privacy_policy_content( 'Visitrix', wp_kses_post( $text ) ); } /** * Floating "Donate" button on the plugin's own admin pages. */ public static function donate_button() { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || ! in_array( $screen->id, array( self::$hook, self::$maps_hook, self::$settings_hook ), true ) ) { return; } ?>