PluginProbe
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress / 9.1.0
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress v9.1.0
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 / legacy / admin / classes / class-wpvr-first-tour-banner.php

class-wpvr-first-tour-banner.php in WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress 9.1.0, at legacy/admin/classes/class-wpvr-first-tour-banner.php

357 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * WPVR First Tour Banner Class
5 *
6 * This class handles the display of an encouragement banner for users
7 * who haven't published their first tour yet.
8 *
9 * @since 8.5.44
10 */
11 class WPVR_First_Tour_Banner {
12
13 /**
14 * Option name for storing dismissal timestamp
15 *
16 * @var string
17 */
18 private $dismissal_option = 'wpvr_tour_banner_dismissed';
19
20 /**
21 * Banner ID for tracking
22 *
23 * @var string
24 */
25 private $banner_id = 'wpvr-tour-banner';
26
27 /**
28 * Constructor
29 */
30 public function __construct() {
31 add_action('admin_init', array($this, 'initialize'));
32 }
33
34 /**
35 * Initialize the banner after WordPress has finished loading plugins.
36 */
37 public function initialize() {
38 // Only initialize if banner should be shown
39 if ($this->should_show_banner()) {
40 // add_action('admin_notices', array($this, 'display_banner'));
41 add_action('admin_head', array($this, 'add_styles'));
42 add_action('wp_ajax_wpvr_dismiss_tour_banner', array($this, 'handle_dismiss_banner'));
43 }
44 }
45
46 /**
47 * Check if banner should be shown
48 *
49 * @return bool
50 */
51 private function should_show_banner() {
52 // Check if banner was dismissed (7 days ago or less)
53 $dismissed_time = get_option($this->dismissal_option, 0);
54 if ($dismissed_time && (time() - $dismissed_time) < (7 * DAY_IN_SECONDS)) {
55 return false;
56 }
57
58 // Check if user has published tours (excluding demo tours)
59 $published_tours = $this->get_published_tours_count();
60
61 // Show banner if no published tours exist, or if tours exist but Pro is not active
62 if ($published_tours === 0) {
63 return true;
64 } elseif ($published_tours > 0 && !defined('WPVR_PRO_VERSION')) {
65 return true;
66 }
67
68 return false;
69 }
70
71 /**
72 * Get count of published tours (excluding demo tours)
73 *
74 * @return int
75 */
76 private function get_published_tours_count() {
77 $args = array(
78 'post_type' => 'wpvr_item',
79 'post_status' => 'publish',
80 'posts_per_page' => -1,
81 'fields' => 'ids',
82 'meta_query' => array(
83 array(
84 'key' => 'wpvr_is_demo_tour',
85 'compare' => 'NOT EXISTS', // Exclude demo tours
86 ),
87 ),
88 );
89
90 $tours = get_posts($args);
91 return count($tours);
92 }
93
94 /**
95 * Display the banner
96 */
97 public function display_banner() {
98 // Only render if we're on a WPVR admin page
99 if (!$this->is_wpvr_admin_page()) {
100 return;
101 }
102 $this->render_banner_html();
103 }
104
105 /**
106 * Render banner HTML
107 */
108 private function render_banner_html() {
109 $create_tour_url = admin_url('post-new.php?post_type=wpvr_item');
110 $upgrade_url = 'https://rextheme.com/wpvr/wpvr-pricing/';
111 $published_tours = $this->get_published_tours_count();
112
113 // Determine which message to show
114 if ($published_tours === 0) {
115 // No tours published yet
116 $message = sprintf(
117 wp_kses(
118 /* translators: %s: url to publish first tour */
119 __('You haven\'t published your first tour yet. <a href="%s">Publish your first tour</a> and see it live in minutes!', 'wpvr'),
120 array(
121 'a' => array(
122 'href' => array(),
123 'class' => array()
124 )
125 )
126 ),
127 esc_url($create_tour_url)
128 );
129 } else {
130 // Tours published but Pro not active
131 $message = sprintf(
132 wp_kses(
133 /* translators: %s: url to upgrade to pro */
134 __('You\'ve published your first tour! <a href="%s" target="_blank" rel="noopener">Upgrade to Pro</a> for unlimited tours, hotspots, and advanced features!', 'wpvr'),
135 array(
136 'a' => array(
137 'href' => array(),
138 'target' => array(),
139 'rel' => array(),
140 'class' => array()
141 )
142 )
143 ),
144 esc_url($upgrade_url)
145 );
146 }
147 ?>
148 <div id="<?php echo esc_attr($this->banner_id); ?>" class="wpvr-tour-banner notice notice-info">
149 <div class="wpvr-tour-banner__content">
150 <div class="wpvr-tour-banner__text">
151 <p><?php echo wp_kses_post($message); ?></p>
152 </div>
153 </div>
154 <button type="button" class="wpvr-tour-banner__close" aria-label="Dismiss this notice">
155 <svg width="23" height="23" viewBox="0 0 23 23" fill="none" xmlns="http://www.w3.org/2000/svg">
156 <path d="M23 11.5C23 17.8513 17.8513 23 11.5 23C5.14873 23 0 17.8513 0 11.5C0 5.14873 5.14873 0 11.5 0C17.8513 0 23 5.14873 23 11.5Z" fill="#FFECE2"/>
157 <path d="M16 8.63687L14.3631 7L11.5 9.86313L8.63687 7L7 8.63687L9.86313 11.5L7 14.3631L8.63687 16L11.5 13.1369L14.3631 16L16 14.3631L13.1369 11.5L16 8.63687Z" fill="#E56829"/>
158 </svg>
159 </button>
160 </div>
161
162 <script>
163 jQuery(document).ready(function($) {
164 $(document).on('click', '#<?php echo esc_attr($this->banner_id); ?> .wpvr-tour-banner__close', function() {
165 wpvr_dismiss_tour_banner();
166 });
167
168 function wpvr_dismiss_tour_banner() {
169 // Hide the banner immediately with fade effect
170 var $banner = $('#<?php echo esc_attr($this->banner_id); ?>');
171
172 $banner.fadeOut(300, function() {
173 $(this).remove();
174 });
175
176 // Send AJAX request to store dismissal in database
177 $.ajax({
178 url: ajaxurl,
179 type: 'POST',
180 data: {
181 action: 'wpvr_dismiss_tour_banner',
182 nonce: '<?php echo esc_attr( wp_create_nonce('wpvr_dismiss_banner') ); ?>'
183 },
184 success: function(response) {
185 console.log('Banner dismissed successfully');
186 },
187 error: function(xhr, status, error) {
188 console.error('Error dismissing banner:', error);
189 // Even if AJAX fails, banner is already hidden from user perspective
190 }
191 });
192 }
193 });
194 </script>
195 <?php
196 }
197
198 /**
199 * Add banner styles
200 */
201 public function add_styles() {
202 ?>
203 <style id="wpvr-tour-banner-styles" type="text/css">
204 .wpvr-tour-banner {
205 position: relative;
206 padding: 16px 46px 16px 20px;
207 border: 1px solid #3F04FE;
208 border-radius: 4px;
209 background: #fff;
210 margin: 20px 20px 0 0;
211 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
212 }
213
214 .wpvr-tour-banner__content {
215 display: flex;
216 align-items: center;
217 justify-content: center;
218 text-align: center;
219 position: relative;
220 }
221
222 .wpvr-tour-banner__text {
223 flex: 1;
224 text-align: center;
225 }
226
227 .wpvr-tour-banner__text p {
228 margin: 0;
229 font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
230 font-weight: 600;
231 font-style: normal;
232 font-size: 16px;
233 leading-trim: none;
234 line-height: 100%;
235 letter-spacing: 0%;
236 text-align: center;
237 color: #3c434a;
238 }
239
240 .wpvr-tour-banner__text a {
241 color: #3F04FE;
242 text-decoration: none;
243 font-weight: 500;
244 font-style: normal;
245 border-bottom: 1px solid #3F04FE;
246 }
247
248 /* Position dismiss button in the middle right */
249 .wpvr-tour-banner__close {
250 position: absolute;
251 top: 50%;
252 right: 12px;
253 transform: translateY(-50%);
254 background: none;
255 border: none;
256 padding: 0;
257 cursor: pointer;
258 width: 23px;
259 height: 23px;
260 display: flex;
261 align-items: center;
262 justify-content: center;
263 transition: opacity 0.2s ease;
264 }
265
266 .wpvr-tour-banner__close:hover {
267 opacity: 0.8;
268 }
269
270 .wpvr-tour-banner__close:active {
271 transform: translateY(-50%) scale(0.95);
272 }
273
274 .wpvr-tour-banner__close svg {
275 display: block;
276 width: 23px;
277 height: 23px;
278 }
279
280 /* Hide banner if dismissed via JavaScript */
281 .wpvr-tour-banner.dismissed {
282 display: none;
283 }
284
285 /* Responsive styles */
286 @media screen and (max-width: 782px) {
287 .wpvr-tour-banner {
288 padding: 16px 46px 16px 16px;
289 margin: 16px 16px 0 0;
290 }
291
292 .wpvr-tour-banner__content {
293 flex-direction: column;
294 text-align: center;
295 justify-content: center;
296 }
297
298 .wpvr-tour-banner__close {
299 right: 8px;
300 }
301 }
302 </style>
303 <?php
304 }
305
306 /**
307 * Check if current screen is a WPVR admin page
308 *
309 * @return bool
310 */
311 private function is_wpvr_admin_page() {
312 // Check if we're in admin and the function is available
313 if (!is_admin() || !function_exists('get_current_screen')) {
314 return false;
315 }
316
317 $screen = get_current_screen();
318 if (!$screen) {
319 return false;
320 }
321
322 $wpvr_pages = array(
323 'toplevel_page_wpvr',
324 'wp-vr_page_wpvr-setting',
325 'edit-wpvr_item',
326 'wpvr_item',
327 'wp-vr_page_wpvr-setup-wizard',
328 'wp-vr_page_wpvr-analytics',
329 'wp-vr_page_wpvrpro',
330 'admin_page_rex-wpvr-setup-wizard',
331 'dashboard_page_rex-wpvr-setup-wizard',
332 );
333
334 return in_array($screen->id, $wpvr_pages, true);
335 }
336
337 /**
338 * Handle AJAX request to dismiss banner
339 */
340 public function handle_dismiss_banner() {
341 // Verify nonce
342 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'wpvr_dismiss_banner')) {
343 wp_die('Invalid nonce');
344 }
345
346 // Check user capabilities
347 if (!current_user_can('manage_options')) {
348 wp_die('Insufficient permissions');
349 }
350
351 // Store dismissal timestamp
352 update_option($this->dismissal_option, time());
353
354 wp_send_json_success(array('message' => 'Banner dismissed successfully'));
355 }
356 }
357