PluginProbe
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress / 8.5.79
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress v8.5.79
9.1.3 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 All 222 releases
wpvr / admin / classes / class-wpvr-new-user-tour.php

class-wpvr-new-user-tour.php in WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress 8.5.79, at admin/classes/class-wpvr-new-user-tour.php

198 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPVR New User Guided Tour
4 *
5 * Automatically launches the Shepherd.js guided tour the first time a
6 * brand-new user opens the "Add New Virtual Tour" editor page.
7 *
8 * Visibility rules (ALL must be true):
9 * 1. No real (non-demo) wpvr_item has ever been created on this site.
10 * Tracked via `wpvr_has_ever_created_tour` (set by WPVR_Onboarding_Notice backfill).
11 * 2. The setup wizard has NOT been completed (`wpvr_wizard_onboarding_done`).
12 * 3. The guided tour has NOT been permanently dismissed
13 * (`wpvr_new_user_guided_tour_dismissed`).
14 * 4. Current admin screen is a NEW `wpvr_item` post (no post ID in the URL).
15 *
16 * @package WPVR
17 * @since 8.5.68
18 */
19
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit;
22 }
23
24 class WPVR_New_User_Tour {
25
26 /**
27 * Option: permanently set when the user dismisses or completes the tour.
28 */
29 const OPT_DISMISSED = 'wpvr_new_user_guided_tour_dismissed';
30
31 /**
32 * AJAX action for the dismiss request.
33 */
34 const AJAX_DISMISS = 'wpvr_dismiss_new_user_tour';
35
36 /**
37 * Constructor — wire up all hooks.
38 */
39 public function __construct() {
40 // Register AJAX handler (must be registered regardless of screen).
41 add_action( 'wp_ajax_' . self::AJAX_DISMISS, array( $this, 'handle_dismiss' ) );
42
43 // Conditionally enqueue scripts only on admin pages.
44 add_action( 'admin_enqueue_scripts', array( $this, 'maybe_enqueue' ) );
45 }
46
47 /* -----------------------------------------------------------------------
48 * Visibility check
49 * --------------------------------------------------------------------- */
50
51 /**
52 * Whether the auto-start tour should be launched.
53 *
54 * @return bool
55 */
56 private function should_launch() {
57 // 1. Only on admin screens.
58 if ( ! is_admin() || ! function_exists( 'get_current_screen' ) ) {
59 return false;
60 }
61
62 // 2. Must be the Single Tour editor screen (wpvr_item).
63 $screen = get_current_screen();
64 if ( ! $screen || 'wpvr_item' !== $screen->id ) {
65 return false;
66 }
67
68 // 3. Must be a NEW post (no post ID = creating, not editing).
69 // When editing an existing tour, $_GET['post'] is set.
70 if ( isset( $_GET['post'] ) && absint( wp_unslash($_GET['post']) ) > 0 ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
71 return false;
72 }
73
74 // 4. Admins only.
75 if ( ! current_user_can( 'manage_options' ) ) {
76 return false;
77 }
78
79 // 5. If a real tour was ever created on this site → existing user, skip.
80 if ( get_option( WPVR_Onboarding_Notice::OPT_HAS_EVER_CREATED ) ) {
81 return false;
82 }
83
84 // 6. If the setup wizard was completed → skip (they already built a tour).
85 if ( get_option( WPVR_Onboarding_Notice::OPT_WIZARD_DONE ) ) {
86 return false;
87 }
88
89 // 7. If the guided tour was already dismissed / completed → skip.
90 if ( get_option( self::OPT_DISMISSED ) ) {
91 return false;
92 }
93
94 return true;
95 }
96
97 /* -----------------------------------------------------------------------
98 * Asset enqueueing
99 * --------------------------------------------------------------------- */
100
101 /**
102 * Enqueue Shepherd assets and localize the auto-start flag when needed.
103 */
104 public function maybe_enqueue() {
105 if ( ! $this->should_launch() ) {
106 return;
107 }
108
109 $plugin_url = WPVR_ASSET_PATH; // already defined as plugin_url . 'admin/'
110
111 // --- CSS ---
112 wp_enqueue_style(
113 'wpvr-shepherd-css',
114 $plugin_url . 'lib/shepherd/css/shepherd-theme-arrows-plain-buttons.css',
115 array(),
116 WPVR_VERSION
117 );
118 wp_enqueue_style(
119 'wpvr-tour-guide-css',
120 $plugin_url . 'lib/shepherd/css/wpvr-tour-guide.min.css',
121 array(),
122 WPVR_VERSION
123 );
124
125 // --- JS ---
126 wp_enqueue_script(
127 'wpvr-tether-js',
128 $plugin_url . 'lib/shepherd/tether/tether.js',
129 array(),
130 WPVR_VERSION,
131 true
132 );
133 wp_enqueue_script(
134 'wpvr-shepherd-js',
135 $plugin_url . 'lib/shepherd/tether-shepherd/shepherd.js',
136 array( 'wpvr-tether-js' ),
137 WPVR_VERSION,
138 true
139 );
140 wp_enqueue_script(
141 'wpvr-tour-guide',
142 $plugin_url . 'js/wpvr-tour-guide.js',
143 array( 'jquery', 'wpvr-tether-js', 'wpvr-shepherd-js' ),
144 WPVR_VERSION,
145 true
146 );
147
148 // Translations already used by the tour guide script.
149 $tour_guide_translation = new WPVR_Tour_Guide_Translation();
150
151 wp_localize_script(
152 'wpvr-tour-guide',
153 'wpvr_tour_guide_obj',
154 array(
155 'Tour_Guide_Translation' => $tour_guide_translation->get_translatable_string(),
156 'step1_bg_image' => plugins_url( 'admin/icon/first-step-bg.png', WPVR_FILE ),
157 'next_button_arrow' => plugins_url( 'admin/icon/next-button-arrow.png', WPVR_FILE ),
158 )
159 );
160
161 // Auto-start config + dismiss endpoint.
162 wp_localize_script(
163 'wpvr-tour-guide',
164 'wpvr_new_user_tour',
165 array(
166 'autoStart' => true,
167 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
168 'nonce' => wp_create_nonce( self::AJAX_DISMISS ),
169 'action' => self::AJAX_DISMISS,
170 )
171 );
172 }
173
174 /* -----------------------------------------------------------------------
175 * AJAX handler
176 * --------------------------------------------------------------------- */
177
178 /**
179 * Permanently dismiss the guided tour so it never auto-fires again.
180 */
181 public function handle_dismiss() {
182 if ( ! current_user_can( 'manage_options' ) ) {
183 wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 );
184 return;
185 }
186
187 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
188 if ( ! wp_verify_nonce( $nonce, self::AJAX_DISMISS ) ) {
189 wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 );
190 return;
191 }
192
193 update_option( self::OPT_DISMISSED, '1', false );
194
195 wp_send_json_success( array( 'message' => 'Tour dismissed' ) );
196 }
197 }
198