PluginProbe
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress / 8.5.71
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress v8.5.71
9.1.2 9.1.1 9.1.0 9.0.3 9.0.2 9.0.1 9.0.0 8.5.79 8.5.78 8.5.77 8.5.76 8.5.75 8.5.74 8.5.73 8.5.72 8.5.71 8.5.70 8.5.69 8.5.68 8.5.35 8.5.36 8.5.37 8.5.38 8.5.39 8.5.4 All 221 releases
wpvr / includes / class-wpvr-telemetry.php

class-wpvr-telemetry.php in WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress 8.5.71, at includes/class-wpvr-telemetry.php

807 lines 28.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Rex_WPVR_Telemetry
4 *
5 * Handles telemetry tracking for the WPVR plugin.
6 *
7 * @since 8.5.48
8 */
9
10 class Rex_WPVR_Telemetry {
11
12 /**
13 * Rex_WPVR_Telemetry constructor.
14 *
15 * Initialize telemetry hooks for the plugin.
16 *
17 * @since 8.5.48
18 */
19 public function __construct() {
20 add_action( 'wpvr_plugin_activated', array( $this, 'track_plugin_activation' ));
21 add_action( 'wpvr_plugin_deactivated', array( $this, 'track_plugin_deactivation' ));
22 add_action('setup_wizard_before_onboarding_content', array( $this, 'track_setup_wizard_start_event' ));
23 add_action( 'wp_ajax_wpvr_setup_wizard_completed', array($this, 'track_setup_wizard_completed' ) );
24 add_action( 'wp_ajax_wpvr_first_strike_completed', array($this, 'track_first_strike_completed' ) );
25
26
27 add_action( 'transition_post_status', array( $this, 'track_first_tour_published' ), 10, 3);
28 add_action( 'rex_wpvr_tour_created', array( $this, 'track_tour_created' ), 10, 1);
29 add_action( 'current_screen', array( $this, 'track_page_view' ) );
30 add_action( 'rex_wpvr_embadded_tour', array( $this, 'track_embadded_tour' ), 10, 1 );
31 add_action( 'rex_wpvr_tour_saved', array( $this, 'track_detailed_tour_events' ), 10, 1 );
32
33 add_action( 'wp_ajax_wpvr_track_telemetry_event', array( $this, 'track_telemetry_event' ) );
34 }
35
36 /**
37 * Track setup wizard started event
38 * Checks if already tracked before sending
39 *
40 * @since 8.5.48
41 */
42 public function track_setup_wizard_start_event() {
43 if ( ! current_user_can( 'manage_options' ) ) {
44 wp_send_json_error( array( 'message' => 'Unauthorized user' ), 403 );
45 return;
46 }
47
48 $setup_started = get_option('wpvr_setup_wizard_started', false);
49 if ( $setup_started ) {
50 return;
51 }
52
53 update_option('wpvr_setup_wizard_started', true);
54 if ( function_exists( 'coderex_telemetry_track' ) && defined( 'WPVR_FILE' ) ) {
55 coderex_telemetry_update_last_action( WPVR_FILE, 'setup_wizard_started' );
56 coderex_telemetry_track(
57 WPVR_FILE,
58 'setup_started',
59 array(
60 'time' => current_time('mysql'),
61 )
62 );
63 }
64 }
65
66
67 /**
68 * Track setup wizard completed event
69 * Checks if already tracked before sending
70 *
71 * @since 8.5.48
72 */
73 public function track_setup_wizard_completed() {
74 if ( ! current_user_can( 'manage_options' ) ) {
75 wp_send_json_error( array( 'message' => 'Unauthorized user' ), 403 );
76 return;
77 }
78
79 $nonce = isset($_POST['security']) ? sanitize_text_field($_POST['security']) : '';
80 if ( !wp_verify_nonce( $nonce, 'wpvr' ) ) {
81 wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 );
82 return;
83 }
84
85 $setup_completed = get_option('wpvr_setup_wizard_completed', false);
86 if ( $setup_completed ) {
87 wp_send_json_success( array( 'message' => 'Setup already tracked', 'already_tracked' => true ) );
88 return;
89 }
90
91 update_option('wpvr_setup_wizard_completed', true);
92
93 $industry = isset($_POST['industry']) ? sanitize_text_field($_POST['industry']) : '';
94
95 if ( function_exists( 'coderex_telemetry_track' ) && defined( 'WPVR_FILE' ) ) {
96 coderex_telemetry_update_last_action( WPVR_FILE, 'setup_wizard_completed' );
97 coderex_telemetry_track(
98 WPVR_FILE,
99 'setup_completed',
100 array(
101 'industry' => $industry,
102 'time' => current_time('mysql'),
103 )
104 );
105 }
106
107 do_action( 'wpvr_setup_wizard_completed_event', $industry );
108
109 wp_send_json_success( array( 'message' => 'Setup wizard completed tracked' ) );
110 }
111
112 /**
113 * Track first strike completed event
114 * Checks if already tracked before sending
115 *
116 * @since 8.5.48
117 */
118 public function track_first_strike_completed() {
119 if ( ! current_user_can( 'manage_options' ) ) {
120 wp_send_json_error( array( 'message' => 'Unauthorized user' ), 403 );
121 return;
122 }
123
124 $nonce = isset($_POST['security']) ? sanitize_text_field($_POST['security']) : '';
125 if ( !wp_verify_nonce( $nonce, 'wpvr' ) ) {
126 wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 );
127 return;
128 }
129
130 $first_strike_completed = get_option('wpvr_first_strike_completed', false);
131 if ( $first_strike_completed ) {
132 wp_send_json_success( array( 'message' => 'First strike already tracked', 'already_tracked' => true ) );
133 return;
134 }
135
136 update_option('wpvr_first_strike_completed', true);
137
138 $industry = isset($_POST['industry']) ? sanitize_text_field($_POST['industry']) : '';
139
140 if ( function_exists( 'coderex_telemetry_track' ) && defined( 'WPVR_FILE' ) ) {
141 coderex_telemetry_update_last_action( WPVR_FILE, 'first_strike_completed' );
142 coderex_telemetry_track(
143 WPVR_FILE,
144 'first_strike_completed',
145 array(
146 'industry' => $industry,
147 'time' => current_time('mysql'),
148 )
149 );
150 }
151
152 wp_send_json_success( array( 'message' => 'First strike completed tracked' ) );
153 }
154
155 /**
156 * Track generic telemetry event via AJAX
157 *
158 * @since 8.5.48
159 */
160 public function track_telemetry_event() {
161 if ( ! isset( $_POST['security'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['security'] ) ), 'wpvr' ) ) {
162 wp_send_json_error( array( 'message' => 'Nonce verification failed' ) );
163 }
164
165 if ( ! current_user_can( 'manage_options' ) ) {
166 wp_send_json_error( array( 'message' => 'Permission denied' ) );
167 }
168
169 $event_name = isset( $_POST['event_name'] ) ? sanitize_text_field( wp_unslash( $_POST['event_name'] ) ) : '';
170
171 if ( empty( $event_name ) ) {
172 wp_send_json_error( array( 'message' => 'Event name is required' ) );
173 }
174
175 $properties = isset( $_POST['properties'] ) ? (array) $_POST['properties'] : array();
176
177 // Sanitize properties
178 $properties = map_deep( wp_unslash( $properties ), 'sanitize_text_field' );
179
180 $default_properties = array(
181 'time' => current_time( 'mysql' ),
182 );
183
184 $final_properties = array_merge( $default_properties, $properties );
185
186 // Update last core action
187 if ( function_exists( 'coderex_telemetry_update_last_action' ) ) {
188 coderex_telemetry_update_last_action( WPVR_FILE, $event_name );
189 }
190
191 if ( function_exists( 'coderex_telemetry_track' ) ) {
192 coderex_telemetry_track(
193 WPVR_FILE,
194 $event_name,
195 $final_properties
196 );
197 }
198
199 wp_send_json_success( array( 'message' => 'Event tracked successfully' ) );
200 }
201
202 /**
203 * Track detailed tour events on save/published
204 *
205 * @param int $post_id
206 * @since 8.5.48
207 */
208 public function track_detailed_tour_events( $post_id ) {
209 $this->track_tour_type_event( $post_id );
210 $this->track_feature_used_events( $post_id );
211 }
212
213 /**
214 * Track tour type
215 *
216 * @param int $post_id
217 */
218 private function track_tour_type_event( $post_id ) {
219 $tour_type = $this->get_tour_type( $post_id );
220
221 // Update last core action
222 if ( function_exists( 'coderex_telemetry_update_last_action' ) ) {
223 coderex_telemetry_update_last_action( WPVR_FILE, 'tour_type_set' );
224 }
225 if ( function_exists( 'coderex_telemetry_track' ) ) {
226 coderex_telemetry_track(
227 WPVR_FILE,
228 'tour_type',
229 array(
230 'tour_type' => $tour_type,
231 )
232 );
233 }
234 }
235
236 /**
237 * Get tour type from post meta
238 *
239 * @param int $post_id
240 * @return string
241 */
242 private function get_tour_type( $post_id ) {
243 $panodata = get_post_meta( $post_id, 'panodata', true );
244
245 $post_panovideo = isset( $_POST['panovideo'] ) ? sanitize_text_field( wp_unslash( $_POST['panovideo'] ) ) : '';
246 $post_streetview = isset( $_POST['streetview'] ) ? sanitize_text_field( wp_unslash( $_POST['streetview'] ) ) : '';
247
248 if ( $post_panovideo === 'on' || ( isset( $panodata['vidid'] ) && ! empty( $panodata['vidid'] ) ) ) {
249 return '360 Video Tour';
250 }
251
252 if ( $post_streetview === 'on' || ( isset( $panodata['streetview'] ) && ! empty( $panodata['streetview'] ) ) ) {
253 return 'Google Street View';
254 }
255
256 if ( ! empty( $panodata['panodata']['scene-list'] ) ) {
257 foreach ( $panodata['panodata']['scene-list'] as $scene ) {
258 if ( isset( $scene['scene-type'] ) && $scene['scene-type'] === 'cubemap' ) {
259 return 'Cubemap Tour';
260 }
261 }
262 }
263
264 // Default to image tour
265 return '360 Image Tour';
266 }
267
268 /**
269 * Track feature used events
270 *
271 * @param int $post_id
272 */
273 private function track_feature_used_events( $post_id ) {
274 $features = $this->get_enabled_features( $post_id );
275
276 foreach ( $features as $feature_name ) {
277 if ( function_exists( 'coderex_telemetry_track' ) ) {
278 coderex_telemetry_track(
279 WPVR_FILE,
280 'feature_used',
281 array(
282 'feature_name' => $feature_name,
283 )
284 );
285 }
286 }
287 }
288
289 /**
290 * Identify all enabled features for a tour
291 *
292 * @param int $post_id
293 * @return array
294 */
295 private function get_enabled_features( $post_id ) {
296 $enabled_features = array();
297 $panodata = get_post_meta( $post_id, 'panodata', true );
298
299 $post_panovideo = isset( $_POST['panovideo'] ) ? sanitize_text_field( wp_unslash( $_POST['panovideo'] ) ) : '';
300 $post_streetview = isset( $_POST['wpvrStreetView'] ) ? sanitize_text_field( wp_unslash( $_POST['wpvrStreetView'] ) ) : '';
301 $is_video_tour = $post_panovideo === 'on';
302 $is_street_view = $post_streetview === 'on';
303
304 // Feature Mapping Detection
305 $feature_map = array(
306 'Auto Rotation' => 'autorotation',
307 'Tour Autoload' => 'autoload',
308 'Control Buttons' => 'control',
309 'Gyroscope Control' => 'gyro',
310 'Auto Gyroscope' => 'deviceorientationcontrol',
311 'Scene Gallery' => 'vrgallery',
312 'Scene Navigation Menu' => 'vrscene_navigation',
313 'Compass' => 'compass',
314 'Background Music' => 'bg_music',
315 'Company Logo' => 'cpLogoSwitch',
316 'Explainer Video' => 'explainerSwitch',
317 'Background Tour' => 'wpvr_bg_tour_enabler',
318 'Generic Form (Contact)' => 'genericform',
319 'Call To Action Button' => 'button_enable',
320 'Custom CSS' => 'custom_css_switch',
321 'Global Zoom Limit' => 'gzoom',
322 'Floor Plan' => 'wpvr_floor_plan_enabler',
323 );
324
325 foreach ( $feature_map as $readable_name => $post_key ) {
326 if ( $is_street_view && 'autoload' === $post_key ) {
327 continue;
328 }
329
330 if ( isset( $_POST[$post_key] ) ) {
331 $val = sanitize_text_field( wp_unslash( $_POST[$post_key] ) );
332 if ( $val === 'on' || $val === '1' ) {
333 $enabled_features[] = $readable_name;
334 }
335 }
336 }
337
338 // Global Scene Transition
339 if ( isset( $_POST['scenefadeduration'] ) ) {
340 $scenefadeduration = floatval( wp_unslash( $_POST['scenefadeduration'] ) );
341 if ( $scenefadeduration > 0 ) {
342 $enabled_features[] = 'Scene Transition (Fade)';
343 }
344 }
345
346 // Add Video Specific Features
347 if ( $is_video_tour ) {
348 if ( isset( $_POST['autoplay'] ) && sanitize_text_field( wp_unslash( $_POST['autoplay'] ) ) === 'on' ) {
349 $enabled_features[] = 'Video Autoplay';
350 }
351 if ( isset( $_POST['loop'] ) && sanitize_text_field( wp_unslash( $_POST['loop'] ) ) === 'on' ) {
352 $enabled_features[] = 'Video Loop';
353 }
354
355 // Detect Video Source
356 if ( isset( $_POST['videourl'] ) && ! empty( $_POST['videourl'] ) ) {
357 $video_url = esc_url_raw( wp_unslash( $_POST['videourl'] ) );
358 $video_source = 'Self-hosted Video'; // Default
359
360 if ( strpos( $video_url, 'youtube.com' ) !== false || strpos( $video_url, 'youtu.be' ) !== false ) {
361 $video_source = 'YouTube Video';
362 } elseif ( strpos( $video_url, 'vimeo.com' ) !== false ) {
363 $video_source = 'Vimeo Video';
364 } elseif ( strpos( $video_url, 'instagram.com' ) !== false ) {
365 $video_source = 'Instagram Video';
366 } elseif ( strpos( $video_url, 'facebook.com' ) !== false || strpos( $video_url, 'fb.watch' ) !== false ) {
367 $video_source = 'Facebook Video';
368 }
369
370 $enabled_features[] = $video_source;
371 }
372 }
373
374 // Layouts (Skip for Video Tour and Street View)
375 if ( ! $is_video_tour && ! $is_street_view && isset( $_POST['tourLayout'] ) ) {
376 $tourLayout = sanitize_text_field( wp_unslash( $_POST['tourLayout'] ) );
377 if ( $tourLayout === 'default' ) {
378 $enabled_features[] = 'Classic Layout';
379 } elseif ( $tourLayout === 'layout1' ) {
380 $enabled_features[] = 'Modern Layout';
381 }
382 }
383
384 // Detailed JSON data checks
385 if ( ! empty( $_POST['panodata'] ) ) {
386 $post_panodata = json_decode( wp_unslash( $_POST['panodata'] ), true );
387 if ( ! empty( $post_panodata['scene-list'] ) ) {
388 foreach ( $post_panodata['scene-list'] as $scene ) {
389 // Hotspots (Skip for Video Tour and Street View)
390 if ( ! $is_video_tour && ! $is_street_view && ! empty( $scene['hotspot-list'] ) ) {
391 foreach ( $scene['hotspot-list'] as $hotspot ) {
392 $type = isset( $hotspot['hotspot-type'] ) ? $hotspot['hotspot-type'] : '';
393 if ( $type === 'info' ) {
394 $enabled_features[] = 'Info Hotspot';
395 } elseif ( $type === 'wc_product' ) {
396 $enabled_features[] = 'WooCommerce Product Hotspot';
397 } elseif ( $type === 'fluent_form' ) {
398 $enabled_features[] = 'Fluent Form Hotspot';
399 } else {
400 $enabled_features[] = 'Scene Type Hotspot';
401 }
402
403 if ( ! empty( $hotspot['hotspot-customclass'] ) || ! empty( $hotspot['hotspot-custom-icon'] ) ) {
404 $enabled_features[] = 'Custom Hotspot Icon';
405 }
406 }
407 }
408
409 // Scene specific settings from images (Usually skip for Video Tour as it has separate logic)
410 if ( ! $is_video_tour ) {
411 if ( ! empty( $scene['maxHfov'] ) || ! empty( $scene['minHfov'] ) || ! empty( $scene['maxPitch'] ) || ! empty( $scene['minPitch'] ) ) {
412 $enabled_features[] = 'Custom Viewing Limits';
413 }
414
415 if ( ! empty( $scene['haov'] ) || ! empty( $scene['vaov'] ) ) {
416 $enabled_features[] = 'Custom Default View';
417 }
418
419 if ( ! empty( $scene['latitude'] ) || ! empty( $scene['longitude'] ) ) {
420 $enabled_features[] = 'Map Location';
421 }
422 }
423 }
424 }
425 }
426
427 return array_unique( $enabled_features );
428 }
429
430 /**
431 * Track plugin activation
432 *
433 * Sends telemetry event when the plugin is activated.
434 *
435 * @since 8.5.48
436 */
437 public function track_plugin_activation() {
438
439 if ( function_exists( 'coderex_telemetry_track' ) ) {
440 coderex_telemetry_track(
441 WPVR_FILE,
442 'plugin_activation',
443 array(
444 'plugin_version' => defined('WPVR_VERSION') ? WPVR_VERSION : 'unknown',
445 'activation_time' => current_time( 'c' ),
446 )
447 );
448 }
449 }
450
451 /**
452 * Track plugin deactivation
453 *
454 * Sends telemetry event when the plugin is deactivated.
455 *
456 * @since 8.5.48
457 */
458 public function track_plugin_deactivation() {
459 // Calculate usage duration
460 $activation_time = get_option( 'wpvr_plugin_installed', 0 );
461 $usage_duration = 'Not available';
462 if ( $activation_time > 0 ) {
463 $duration_seconds = time() - $activation_time;
464 $usage_duration = $this->format_duration( $duration_seconds );
465 }
466
467 // Get last core action
468 $last_core_action = get_option( 'wpvr_last_core_action', '' );
469
470 // Track deactivation event
471 if ( function_exists( 'coderex_telemetry_track' ) ) {
472 coderex_telemetry_track(
473 WPVR_FILE,
474 'plugin_deactivated',
475 array(
476 'plugin_version' => defined('WPVR_VERSION') ? WPVR_VERSION : 'unknown',
477 'deactivation_time' => current_time( 'c' ),
478 'usage_duration' => $usage_duration,
479 'last_core_action' => $last_core_action,
480 )
481 );
482 }
483
484 // Clean up activation time option
485 delete_option( 'wpvr_plugin_installed' );
486 }
487
488 /**
489 * Track the first published tour
490 *
491 * Sends telemetry when the first tour is published for the plugin.
492 *
493 * @param string $new_status The new post status
494 * @param string $old_status The previous post status
495 * @param object $post \WP_Post The post object
496 * @since 8.5.48
497 */
498 public function track_first_tour_published( $new_status, $old_status, $post ) {
499 if ($post->post_type !== 'wpvr_item') {
500 return;
501 }
502
503 // Skip demo/sample tours created during plugin installation
504 $is_demo_tour = get_post_meta($post->ID, 'wpvr_is_demo_tour', true);
505 if ($is_demo_tour === '1') {
506 return;
507 }
508
509 if ($new_status === 'publish' && in_array($old_status, ['auto-draft', 'draft', 'new', ''])) {
510 // Count only non-demo tours
511 $args = array(
512 'post_type' => 'wpvr_item',
513 'post_status' => array('publish', 'draft'),
514 'posts_per_page' => -1,
515 'fields' => 'ids',
516 'meta_query' => array(
517 'relation' => 'OR',
518 array(
519 'key' => 'wpvr_is_demo_tour',
520 'compare' => 'NOT EXISTS',
521 ),
522 array(
523 'key' => 'wpvr_is_demo_tour',
524 'value' => '1',
525 'compare' => '!=',
526 ),
527 ),
528 );
529
530 $user_tours = get_posts($args);
531 $total_user_tours = is_array($user_tours) ? count($user_tours) : 0;
532
533 do_action('rex_wpvr_tour_created', $post->post_title);
534
535 // Detailed tracking for first publish
536 $this->track_detailed_tour_events( $post->ID );
537
538 if (1 === $total_user_tours) {
539 // Update last core action
540 if ( function_exists( 'coderex_telemetry_update_last_action' ) ) {
541 coderex_telemetry_update_last_action( WPVR_FILE, 'first_tour_published' );
542 }
543 if ( function_exists( 'coderex_telemetry_track' ) ) {
544 coderex_telemetry_track(
545 WPVR_FILE,
546 'first_tour_published',
547 array(
548 'tour_title' => $post->post_title,
549 'time' => current_time('mysql'),
550 )
551 );
552 }
553
554 do_action( 'wpvr_first_tour_published_event', $post->ID, $post->post_title );
555
556 // Also track first_strike (first successful value moment)
557 if ( function_exists( 'coderex_telemetry_update_last_action' ) ) {
558 coderex_telemetry_update_last_action( WPVR_FILE, 'first_strike' );
559 }
560 if ( function_exists( 'coderex_telemetry_track' ) ) {
561 coderex_telemetry_track(
562 WPVR_FILE,
563 'first_strike',
564 array(
565 'tour_title' => $post->post_title,
566 'time' => current_time('mysql'),
567 )
568 );
569 }
570 }
571 } elseif ($new_status === 'publish' && $old_status === 'publish') {
572 // Update last core action
573 if ( function_exists( 'coderex_telemetry_update_last_action' ) ) {
574 coderex_telemetry_update_last_action( WPVR_FILE, 'tour_updated' );
575 }
576 if ( function_exists( 'coderex_telemetry_track' ) ) {
577 coderex_telemetry_track(
578 WPVR_FILE,
579 'tour_updated',
580 array(
581 'tour_title' => $post->post_title,
582 'time' => current_time('mysql'),
583 )
584 );
585 }
586 // Detailed tracking on update
587 $this->track_detailed_tour_events( $post->ID );
588 }
589 }
590 /**
591 * Track tour creation
592 *
593 * Sends telemetry when a new tour is created.
594 *
595 * @param string $post_title The title of the created tour
596 * @since 8.5.48
597 */
598 public function track_tour_created( $post_title ) {
599 // Update last core action
600 if ( function_exists( 'coderex_telemetry_update_last_action' ) ) {
601 coderex_telemetry_update_last_action( WPVR_FILE, 'tour_created' );
602 }
603 if ( function_exists( 'coderex_telemetry_track' ) ) {
604 coderex_telemetry_track(
605 WPVR_FILE,
606 'tour_created',
607 array(
608 'tour_title' => isset( $post_title) ? $post_title : '',
609 'time' => current_time('mysql'),
610 )
611 );
612 }
613 }
614
615
616 /**
617 * Track page views
618 *
619 * Sends telemetry when specific admin pages for the plugin are viewed.
620 *
621 * @param WP_Screen $screen Current admin screen object
622 * @return void
623 * @since 8.5.48
624 */
625 public function track_page_view( $screen ) {
626 if ( ! is_admin() || empty( $screen->id ) ) {
627 return;
628 }
629
630 $page_map = array(
631 '/wp-admin/edit.php?post_type=wpvr_item' => 'Tour list',
632 '/wp-admin/admin.php?page=wpvr-settings' => 'Settings',
633 '/wp-admin/admin.php?page=wpvr-analytics' => 'Analytics',
634 '/wp-admin/admin.php?page=rex-wpvr-setup-wizard' => 'Setup wizard',
635 '/wp-admin/admin.php?page=wpvrpro' => 'License',
636 );
637
638 $current_page = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( $_SERVER['REQUEST_URI'] ) : '';
639 if ( '' === $current_page ) {
640 return;
641 }
642
643 $page_name = null;
644 foreach ( $page_map as $fragment => $name ) {
645 if ( strpos( $current_page, $fragment ) !== false ) {
646 $page_name = $name;
647 break;
648 }
649 }
650
651
652 if ( null === $page_name &&
653 strpos( $current_page, '/wp-admin/post.php' ) !== false &&
654 strpos( $current_page, 'action=edit' ) !== false ) {
655 if ( isset( $screen->post_type ) && 'wpvr_item' === $screen->post_type ) {
656 $page_name = 'Tour edit';
657 }
658 }
659
660 if ( null === $page_name ) {
661 return;
662 }
663
664 $current_user = wp_get_current_user();
665 if ( ! $current_user->exists() ) {
666 return;
667 }
668
669 if ( function_exists( 'coderex_telemetry_track' ) ) {
670 coderex_telemetry_track(
671 WPVR_FILE,
672 'page_view',
673 array(
674 'page' => $current_page,
675 'page_name' => $page_name,
676 'time' => current_time( 'mysql' ),
677 )
678 );
679 }
680 }
681
682
683 /**
684 * Track embedded tour event
685 *
686 * Sends telemetry when a tour is embedded.
687 *
688 * @param int $tour_id The ID of the tour being embedded.
689 * @return void
690 * @since 8.5.48
691 */
692 public function track_embadded_tour( $tour_id ) {
693 $this->track_weekly_unique_views_kui( $tour_id );
694
695 // Check if tracking is already done for this tour
696 if ( get_post_meta( $tour_id, '_wpvr_tour_embedded_tracked', true ) ) {
697 return;
698 }
699
700 $tour = get_post( $tour_id );
701
702 if ( ! $tour || $tour->post_type !== 'wpvr_item' ) {
703 return;
704 }
705
706 // Update last core action
707 if ( function_exists( 'coderex_telemetry_update_last_action' ) ) {
708 coderex_telemetry_update_last_action( WPVR_FILE, 'tour_embedded' );
709 }
710 if ( function_exists( 'coderex_telemetry_track' ) ) {
711 coderex_telemetry_track(
712 WPVR_FILE,
713 'tour_embedded',
714 array(
715 'title' => $tour->post_title,
716 )
717 );
718 }
719
720 // Mark as tracked
721 update_post_meta( $tour_id, '_wpvr_tour_embedded_tracked', true );
722 }
723
724 /**
725 * Track rolling 7-day unique views across all active tours for KUI.
726 *
727 * @param int $tour_id Current viewed tour ID.
728 * @return void
729 */
730 private function track_weekly_unique_views_kui( $tour_id ) {
731 $tour_id = absint( $tour_id );
732 if ( $tour_id <= 0 ) {
733 return;
734 }
735
736 $tour = get_post( $tour_id );
737 if ( ! $tour || 'wpvr_item' !== $tour->post_type || 'publish' !== $tour->post_status ) {
738 return;
739 }
740
741 $option_key = 'wpvr_kui_unique_views_7d';
742 $last_count_key = 'wpvr_kui_unique_views_7d_last_count';
743 $window_seconds = 7 * DAY_IN_SECONDS;
744 $now = time();
745
746 $tracked_views = get_option( $option_key, array() );
747 if ( ! is_array( $tracked_views ) ) {
748 $tracked_views = array();
749 }
750
751 foreach ( $tracked_views as $tracked_tour_id => $timestamp ) {
752 $tracked_tour_id = absint( $tracked_tour_id );
753 $timestamp = absint( $timestamp );
754
755 if (
756 $tracked_tour_id <= 0 ||
757 $timestamp <= 0 ||
758 ( $now - $timestamp ) > $window_seconds ||
759 'publish' !== get_post_status( $tracked_tour_id )
760 ) {
761 unset( $tracked_views[ $tracked_tour_id ] );
762 }
763 }
764
765 $tracked_views[ $tour_id ] = $now;
766 update_option( $option_key, $tracked_views );
767
768 $unique_views = count( $tracked_views );
769 $last_sent_count = (int) get_option( $last_count_key, -1 );
770
771 if ( $unique_views !== $last_sent_count ) {
772 update_option( $last_count_key, $unique_views );
773 do_action( 'wpvr_kui_unique_views_updated', $unique_views, $tour_id );
774 }
775 }
776
777 /**
778 * Format duration in seconds to human-readable format
779 *
780 * @param int $seconds Duration in seconds
781 * @return string Human-readable duration
782 * @since 8.5.48
783 */
784 private function format_duration( $seconds ) {
785 if ( $seconds < 60 ) {
786 return $seconds . ' seconds';
787 }
788
789 $minutes = floor( $seconds / 60 );
790 if ( $minutes < 60 ) {
791 return $minutes . ' minutes';
792 }
793
794 $hours = floor( $minutes / 60 );
795 if ( $hours < 24 ) {
796 $remaining_minutes = $minutes % 60;
797 return $hours . ' hours' . ( $remaining_minutes > 0 ? ', ' . $remaining_minutes . ' minutes' : '' );
798 }
799
800 $days = floor( $hours / 24 );
801 $remaining_hours = $hours % 24;
802 return $days . ' days' . ( $remaining_hours > 0 ? ', ' . $remaining_hours . ' hours' : '' );
803 }
804
805 }
806
807 //new Rex_WPVR_Telemetry();