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-first-tour-banner.php

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

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