PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.10
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.10
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / codeinwp / themeisle-sdk / src / Modules / Announcements.php

Announcements.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.11.10, at vendor/codeinwp/themeisle-sdk/src/Modules/Announcements.php

498 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * File responsible for announcements.
4 *
5 * This is used to display information about limited events, such as Black Friday.
6 *
7 * @package ThemeIsleSDK
8 * @subpackage Modules
9 * @copyright Copyright (c) 2017, Marius Cristea
10 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
11 * @since 3.3.0
12 */
13
14 namespace ThemeisleSDK\Modules;
15
16 use ThemeisleSDK\Common\Abstract_Module;
17 use ThemeisleSDK\Loader;
18 use ThemeisleSDK\Product;
19
20 /**
21 * Announcement module for the ThemeIsle SDK.
22 */
23 class Announcements extends Abstract_Module {
24
25 /**
26 * Holds the timeline for the announcements.
27 *
28 * @var array
29 */
30 private static $timeline = array(
31 'black_friday' => array(
32 'start' => '2024-11-25 00:00:00',
33 'end' => '2024-12-03 23:59:59',
34 'rendered' => false,
35 ),
36 );
37
38 /**
39 * Mark is a banner for a product was already loaded.
40 *
41 * @var array
42 */
43 private static $banner_loaded = array();
44
45 const PLUGIN_PAGE = 'https://themeisle.com/plugins';
46 const THEME_PAGE = 'https://themeisle.com/themes';
47 const REVIVE_SOCIAL = 'https://revive.social/plugins';
48
49 /**
50 * Holds the option prefix for the announcements.
51 *
52 * This is used to store the dismiss date for each announcement.
53 *
54 * @var string
55 */
56 public $option_prefix = 'themeisle_sdk_announcement_';
57
58 /**
59 * Holds the time for the current request.
60 *
61 * @var string
62 */
63 public $time = '';
64
65 /**
66 * Constructor for the Announcements module.
67 *
68 * @param array $timeline Optional. An array representing the timeline of announcements. Default is an empty array.
69 */
70 public function __construct( $timeline = array() ) {
71 if ( is_array( $timeline ) && ! empty( $timeline ) ) {
72 self::$timeline = $timeline;
73 }
74 }
75
76 /**
77 * Check if the module can be loaded.
78 *
79 * @param Product $product Product data.
80 *
81 * @return bool
82 */
83 public function can_load( $product ) {
84 if ( $this->is_from_partner( $product ) ) {
85 return false;
86 }
87
88 return true;
89 }
90
91 /**
92 * Load the module for the selected product.
93 *
94 * @param Product $product Product data.
95 *
96 * @return void
97 */
98 public function load( $product ) {
99 if ( ! current_user_can( 'install_plugins' ) ) {
100 return;
101 }
102
103 $this->product = $product;
104
105 add_action( 'admin_init', array( $this, 'load_announcements' ) );
106 add_filter( 'themeisle_sdk_active_announcements', array( $this, 'get_active_announcements' ) );
107 add_filter( 'themeisle_sdk_announcements', array( $this, 'get_announcements_for_plugins' ) );
108 add_action( 'themeisle_sdk_load_banner', array( $this, 'load_dashboard_banner_renderer' ) );
109 }
110
111 /**
112 * Load all valid announcements.
113 *
114 * @return void
115 */
116 public function load_announcements() {
117 $active = $this->get_active_announcements();
118
119 if ( empty( $active ) ) {
120 return;
121 }
122
123 foreach ( $active as $announcement ) {
124
125 $method = $announcement . '_notice_render';
126
127 if ( method_exists( $this, $method ) ) {
128 add_action( 'admin_notices', array( $this, $method ) );
129 }
130 }
131
132 // Load the ajax handler.
133 add_action( 'wp_ajax_themeisle_sdk_dismiss_announcement', array( $this, 'disable_notification_ajax' ) );
134 }
135
136 /**
137 * Get all active announcements.
138 *
139 * @return array List of active announcements.
140 */
141 public function get_active_announcements() {
142 $active = array();
143
144 foreach ( self::$timeline as $announcement_slug => $dates ) {
145 if ( $this->is_active( $dates ) && $this->can_show( $announcement_slug, $dates ) ) {
146 $active[] = $announcement_slug;
147 }
148 }
149
150 return $active;
151 }
152
153 /**
154 * Get all announcements along with plugin specific data.
155 *
156 * @return array List of announcements.
157 */
158 public function get_announcements_for_plugins() {
159
160 $announcements = array();
161
162 foreach ( self::$timeline as $announcement => $dates ) {
163 $announcements[ $announcement ] = $dates;
164
165 if ( false !== strpos( $announcement, 'black_friday' ) ) {
166 $announcements[ $announcement ]['active'] = $this->is_active( $dates );
167
168 // Dashboard banners URLs.
169 $announcements[ $announcement ]['neve_dashboard_url'] = tsdk_utmify( self::THEME_PAGE . '/neve/blackfriday/', 'bfcm24', 'dashboard' );
170 $announcements[ $announcement ]['hestia_dashboard_url'] = tsdk_utmify( self::THEME_PAGE . '/hestia/blackfriday/', 'bfcm24', 'dashboard' );
171 $announcements[ $announcement ]['feedzy_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/feedzy-rss-feeds/blackfriday/', 'bfcm24', 'dashboard' );
172 $announcements[ $announcement ]['otter_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/otter-blocks/blackfriday/', 'bfcm24', 'dashboard' );
173 $announcements[ $announcement ]['mpg_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/multi-pages-generator/blackfriday', 'bfcm24', 'dashboard' );
174 $announcements[ $announcement ]['ppom_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/ppom-pro/blackfriday/', 'bfcm24', 'dashboard' );
175 $announcements[ $announcement ]['rfc7r_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/wpcf7-redirect/blackfriday/', 'bfcm24', 'dashboard' );
176 $announcements[ $announcement ]['hyve_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/hyve/', 'bfcm24', 'dashboard' );
177 $announcements[ $announcement ]['spc_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/super-page-cache-pro/blackfriday/', 'bfcm24', 'dashboard' );
178 $announcements[ $announcement ]['visualizer_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/visualizer-charts-and-graphs/blackfriday/', 'bfcm24', 'dashboard' );
179 $announcements[ $announcement ]['feedzy_dashboard_url'] = tsdk_utmify( self::PLUGIN_PAGE . '/feedzy-rss-feeds/blackfriday/', 'bfcm24', 'dashboard' );
180 $announcements[ $announcement ]['rop_dashboard_url'] = tsdk_utmify( self::REVIVE_SOCIAL . '/revive-old-post/', 'bfcm24', 'dashboard' );
181
182 // Customizer banners URLs.
183 $announcements[ $announcement ]['hestia_customizer_url'] = tsdk_utmify( 'https://themeisle.com/black-friday/', 'bfcm24', 'hestiacustomizer' );
184 $announcements[ $announcement ]['neve_customizer_url'] = tsdk_utmify( 'https://themeisle.com/black-friday/', 'bfcm24', 'nevecustomizer' );
185
186 // Banners urgency text.
187 $remaining_time = $this->get_remaining_time_for_event( $dates['end'] );
188 $announcements[ $announcement ]['remaining_time'] = $remaining_time;
189 $announcements[ $announcement ]['urgency_text'] = ! empty( $remaining_time ) ? sprintf( Loader::$labels['announcements']['hurry_up'], $remaining_time ) : '';
190
191 $announcements[ $announcement ]['feedzy_banner_src'] = defined( 'FEEDZY_ABSURL' ) ? FEEDZY_ABSURL . 'img/black-friday.jpg' : '';
192 $announcements[ $announcement ]['visualizer_banner_src'] = defined( 'VISUALIZER_ABSURL' ) ? VISUALIZER_ABSURL . 'images/black-friday.jpg' : '';
193 $announcements[ $announcement ]['ppom_banner_src'] = defined( 'PPOM_URL' ) ? PPOM_URL . '/images/black-friday.jpg' : '';
194 $announcements[ $announcement ]['mpg_banner_src'] = defined( 'MPG_BASE_IMG_PATH' ) ? MPG_BASE_IMG_PATH . '/black-friday.jpg' : '';
195 $announcements[ $announcement ]['spc_banner_src'] = defined( 'SWCFPC_PLUGIN_URL' ) ? SWCFPC_PLUGIN_URL . 'assets/img/black-friday.jpg' : '';
196 $announcements[ $announcement ]['hestia_banner_src'] = defined( 'HESTIA_ASSETS_URL' ) ? HESTIA_ASSETS_URL . 'img/black-friday.jpg' : '';
197 $announcements[ $announcement ]['hyve_banner_src'] = defined( 'HYVE_LITE_URL' ) ? HYVE_LITE_URL . 'assets/images/black-friday.jpg' : '';
198 $announcements[ $announcement ]['rfc7r_banner_src'] = defined( 'WPCF7_PRO_REDIRECT_ASSETS_PATH' ) ? WPCF7_PRO_REDIRECT_ASSETS_PATH . 'images/black-friday.jpg' : '';
199 $announcements[ $announcement ]['rop_banner_src'] = defined( 'ROP_LITE_URL' ) ? ROP_LITE_URL . 'assets/img/black-friday.jpg' : '';
200
201 foreach ( $announcements[ $announcement ] as $key => $value ) {
202 if ( strpos( $key, '_url' ) !== false ) {
203 $announcements[ $announcement ][ $key ] = tsdk_translate_link( $value );
204 }
205 }
206 }
207 }
208
209 return apply_filters( 'themeisle_sdk_announcements_data', $announcements );
210 }
211
212 /**
213 * Get the announcement data.
214 *
215 * @param string $announcement The announcement to get the data for.
216 *
217 * @return array
218 */
219 public function get_announcement_data( $announcement ) {
220 return ! empty( $announcement ) && is_string( $announcement ) && isset( self::$timeline[ $announcement ] ) ? self::$timeline[ $announcement ] : array();
221 }
222
223 /**
224 * Check if the announcement has an active timeline.
225 *
226 * @param array $dates The announcement to check.
227 *
228 * @return bool
229 */
230 public function is_active( $dates ) {
231
232 if ( empty( $this->time ) ) {
233 $this->time = current_time( 'Y-m-d' );
234 }
235
236 $start = isset( $dates['start'] ) ? $dates['start'] : null;
237 $end = isset( $dates['end'] ) ? $dates['end'] : null;
238
239 if ( $start && $end ) {
240 return $start <= $this->time && $this->time <= $end;
241 } elseif ( $start ) {
242 return $this->time >= $start;
243 } elseif ( $end ) {
244 return $this->time <= $end;
245 }
246
247 return false;
248 }
249
250 /**
251 * Get the remaining time for the event in a human readable format.
252 *
253 * @param string $end_date The end date for event.
254 *
255 * @return string Remaining time for the event.
256 */
257 public function get_remaining_time_for_event( $end_date ) {
258 if ( empty( $end_date ) || ! is_string( $end_date ) ) {
259 return '';
260 }
261
262 return human_time_diff( time(), strtotime( $end_date ) );
263
264 }
265
266 /**
267 * Check if the announcement can be shown.
268 *
269 * @param string $announcement_slug The announcement to check.
270 * @param array $dates The announcement to check.
271 *
272 * @return bool
273 */
274 public function can_show( $announcement_slug, $dates ) {
275 $dismiss_date = get_option( $this->option_prefix . $announcement_slug, false );
276
277 if ( false === $dismiss_date ) {
278 return true;
279 }
280
281 // If the start date is after the dismiss date, show the notice.
282 $start = isset( $dates['start'] ) ? $dates['start'] : null;
283 if ( $start && $dismiss_date < $start ) {
284 return true;
285 }
286
287 return false;
288 }
289
290 /**
291 * Disable the notification via ajax.
292 *
293 * @return void
294 */
295 public function disable_notification_ajax() {
296 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), 'dismiss_themeisle_event_notice' ) ) {
297 wp_die( 'Invalid nonce! Refresh the page and try again.' );
298 }
299
300 if ( ! isset( $_POST['announcement'] ) || ! is_string( $_POST['announcement'] ) ) {
301 wp_die( 'Invalid announcement! Refresh the page and try again.' );
302 }
303
304 $announcement = sanitize_key( $_POST['announcement'] );
305
306 update_option( $this->option_prefix . $announcement, current_time( 'Y-m-d' ) );
307 wp_die( 'success' );
308 }
309
310 /**
311 * Render the Black Friday notice.
312 *
313 * @return void
314 */
315 public function black_friday_notice_render() {
316
317 // Prevent the notice from being rendered twice.
318 if ( self::$timeline['black_friday']['rendered'] ) {
319 return;
320 }
321 self::$timeline['black_friday']['rendered'] = true;
322
323 $product_names = array();
324
325 foreach ( Loader::get_products() as $product ) {
326 $slug = $product->get_slug();
327
328 // NOTE: No notice if the user has at least one Pro product.
329 if ( $product->requires_license() ) {
330 return;
331 }
332
333 $product_names[] = $product->get_name();
334 }
335
336 // Randomize the products and get only 4.
337 shuffle( $product_names );
338 $product_names = array_slice( $product_names, 0, 4 );
339
340 ?>
341 <style>
342 .themeisle-sale {
343 display: flex;
344 align-items: center;
345 }
346 </style>
347 <div class="themeisle-sale notice notice-info is-dismissible" data-announcement="black_friday">
348 <img width="24" src="<?php echo esc_url_raw( $this->get_sdk_uri() . 'assets/images/themeisle-logo.png' ); ?>"/>
349 <p>
350 <strong><?php echo esc_html( Loader::$labels['announcements']['sale_live'] ); ?></strong>
351 - <?php echo sprintf( esc_html( Loader::$labels['announcements']['max_savings'] ), esc_html( implode( ', ', $product_names ) ) ); ?>.
352 <a href="<?php echo esc_url_raw( tsdk_utmify( 'https://themeisle.com/blackfriday/', 'bfcm24', 'globalnotice' ) ); ?>"
353 target="_blank"><?php echo esc_html( Loader::$labels['announcements']['learn_more'] ); ?></a>
354 <span class="themeisle-sale-error"></span>
355 </p>
356 </div>
357 <script type="text/javascript" data-origin="themeisle-sdk">
358 window.document.addEventListener('DOMContentLoaded', () => {
359 const observer = new MutationObserver((mutationsList, observer) => {
360 for (let mutation of mutationsList) {
361 if (mutation.type === 'childList') {
362 const container = document.querySelector('.themeisle-sale.notice');
363 const button = container?.querySelector('button');
364 if (button) {
365 button.addEventListener('click', e => {
366 e.preventDefault();
367 fetch('<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', {
368 method: 'POST',
369 headers: {
370 'Content-Type': 'application/x-www-form-urlencoded'
371 },
372 body: new URLSearchParams({
373 action: 'themeisle_sdk_dismiss_announcement',
374 nonce: '<?php echo esc_attr( wp_create_nonce( 'dismiss_themeisle_event_notice' ) ); ?>',
375 announcement: container.dataset.announcement
376 })
377 })
378 .then(response => response.text())
379 .then(response => {
380 if (!response?.includes('success')) {
381 document.querySelector('.themeisle-sale-error').innerHTML = response;
382 return;
383 }
384
385 document.querySelectorAll('.themeisle-sale.notice').forEach(el => {
386 el.classList.add('hidden');
387 setTimeout(() => {
388 el.remove();
389 }, 800);
390 });
391 })
392 .catch(error => {
393 console.error('Error:', error);
394 document.querySelector('.themeisle-sale-error').innerHTML = error;
395 });
396 });
397 observer.disconnect();
398 break;
399 }
400 }
401 }
402 });
403
404 observer.observe(document.body, {childList: true, subtree: true});
405 });
406 </script>
407 <?php
408 }
409
410 /**
411 * Load the dashboard banner renderer.
412 *
413 * @param string $product_key The product key.
414 *
415 * @return void
416 */
417 public function load_dashboard_banner_renderer( $product_key ) {
418
419 $banner_handler = apply_filters( 'themeisle_sdk_dependency_script_handler', 'banner' );
420
421 if ( empty( $banner_handler ) ) {
422 return;
423 }
424
425 if ( isset( self::$banner_loaded[ $product_key ] ) && true === self::$banner_loaded[ $product_key ] ) {
426 return;
427 }
428 self::$banner_loaded[ $product_key ] = true;
429
430 $banner_data = array();
431
432 // Get the first active banner.
433 foreach ( $this->get_announcements_for_plugins() as $announcement ) {
434 if ( false === $announcement['active'] ) {
435 continue;
436 }
437
438 $cta_key = $product_key . '_dashboard_url';
439 $banner_src_key = $product_key . '_banner_src';
440
441 if (
442 ! isset( $announcement[ $cta_key ] ) ||
443 ! isset( $announcement[ $banner_src_key ] ) ||
444 empty( $announcement[ $banner_src_key ] ) ||
445 ! isset( $announcement['urgency_text'] )
446 ) {
447 continue;
448 }
449
450 $banner_data = array(
451 'content' => $this->render_banner(
452 array(
453 'cta_url' => $announcement[ $cta_key ],
454 'img_src' => $announcement[ $banner_src_key ],
455 'urgency_text' => $announcement['urgency_text'],
456 )
457 ),
458 );
459
460 break;
461 }
462
463 if ( empty( $banner_data ) ) {
464 return;
465 }
466
467 do_action( 'themeisle_sdk_dependency_enqueue_script', 'banner' );
468 wp_localize_script( $banner_handler, 'tsdk_banner_data', $banner_data );
469 }
470
471 /**
472 * Renders a banner with the provided settings.
473 *
474 * @param array $settings {
475 * Optional. An array of settings for the banner.
476 *
477 * @type string $cta_url The URL for the call-to-action link.
478 * @type string $img_src The source URL for the banner image.
479 * @type string $urgency_text The urgency text to display on the banner.
480 * }
481 * @return string The HTML output of the banner.
482 */
483 public function render_banner( $settings = array() ) {
484 if ( empty( $settings ) ) {
485 return '';
486 }
487
488 return wp_kses_post(
489 wp_sprintf(
490 '<a href="%s" target="_blank" class="tsdk-banner-cta"><img src="%s" class="tsdk-banner-img"><div class="tsdk-banner-urgency-text">%s</div></a>',
491 esc_url_raw( $settings['cta_url'] ),
492 esc_url_raw( $settings['img_src'] ),
493 sanitize_text_field( $settings['urgency_text'] )
494 )
495 );
496 }
497 }
498