PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.8
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.10.8, at vendor/codeinwp/themeisle-sdk/src/Modules/Announcements.php

380 lines 10.7 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 namespace ThemeisleSDK\Modules;
14
15 use ThemeisleSDK\Common\Abstract_Module;
16 use ThemeisleSDK\Loader;
17 use ThemeisleSDK\Product;
18
19 /**
20 * Announcement module for the ThemeIsle SDK.
21 */
22 class Announcements extends Abstract_Module {
23
24 /**
25 * Holds the timeline for the announcements.
26 *
27 * @var array
28 */
29 private static $timeline = array(
30 'black_friday' => array(
31 'start' => '2024-11-25 00:00:00',
32 'end' => '2024-12-03 23:59:59',
33 'rendered' => false,
34 ),
35 );
36
37 /**
38 * Holds the option prefix for the announcements.
39 *
40 * This is used to store the dismiss date for each announcement.
41 *
42 * @var string
43 */
44 public $option_prefix = 'themeisle_sdk_announcement_';
45
46 /**
47 * Holds the time for the current request.
48 *
49 * @var string
50 */
51 public $time = '';
52
53 /**
54 * Check if the module can be loaded.
55 *
56 * @param Product $product Product data.
57 *
58 * @return bool
59 */
60 public function can_load( $product ) {
61 if ( $this->is_from_partner( $product ) ) {
62 return false;
63 }
64
65 return true;
66 }
67
68 /**
69 * Load the module for the selected product.
70 *
71 * @param Product $product Product data.
72 *
73 * @return void
74 */
75 public function load( $product ) {
76 if ( ! current_user_can( 'install_plugins' ) ) {
77 return;
78 }
79
80 $this->product = $product;
81
82 add_action( 'admin_init', array( $this, 'load_announcements' ) );
83 add_filter( 'themeisle_sdk_active_announcements', array( $this, 'get_active_announcements' ) );
84 add_filter( 'themeisle_sdk_announcements', array( $this, 'get_announcements_for_plugins' ) );
85 }
86
87 /**
88 * Load all valid announcements.
89 *
90 * @return void
91 */
92 public function load_announcements() {
93 $active = $this->get_active_announcements();
94
95 if ( empty( $active ) ) {
96 return;
97 }
98
99 foreach ( $active as $announcement ) {
100
101 $method = $announcement . '_notice_render';
102
103 if ( method_exists( $this, $method ) ) {
104 add_action( 'admin_notices', array( $this, $method ) );
105 }
106 }
107
108 // Load the ajax handler.
109 add_action( 'wp_ajax_themeisle_sdk_dismiss_announcement', array( $this, 'disable_notification_ajax' ) );
110 }
111
112 /**
113 * Get all active announcements.
114 *
115 * @return array List of active announcements.
116 */
117 public function get_active_announcements() {
118 $active = array();
119
120 foreach ( self::$timeline as $announcement_slug => $dates ) {
121 if ( $this->is_active( $dates ) && $this->can_show( $announcement_slug, $dates ) ) {
122 $active[] = $announcement_slug;
123 }
124 }
125
126 return $active;
127 }
128
129 /**
130 * Get all announcements along with plugin specific data.
131 *
132 * @return array List of announcements.
133 */
134 public function get_announcements_for_plugins() {
135
136 $announcements = array();
137
138 foreach ( self::$timeline as $announcement => $dates ) {
139 $announcements[ $announcement ] = $dates;
140
141 if ( false !== strpos( $announcement, 'black_friday' ) ) {
142 $announcements[ $announcement ]['active'] = $this->is_active( $dates );
143
144 // Dashboard banners URLs.
145 $announcements[ $announcement ]['feedzy_dashboard_url'] = tsdk_utmify( 'https://themeisle.com/plugins/feedzy-rss-feeds/blackfriday/', 'bfcm24', 'dashboard' );
146 $announcements[ $announcement ]['neve_dashboard_url'] = tsdk_utmify( 'https://themeisle.com/themes/neve/blackfriday/', 'bfcm24', 'dashboard' );
147 $announcements[ $announcement ]['otter_dashboard_url'] = tsdk_utmify( 'https://themeisle.com/plugins/otter-blocks/blackfriday/', 'bfcm24', 'dashboard' );
148
149 // Customizer banners URLs.
150 $announcements[ $announcement ]['hestia_customizer_url'] = tsdk_utmify( 'https://themeisle.com/black-friday/', 'bfcm24', 'hestiacustomizer' );
151 $announcements[ $announcement ]['neve_customizer_url'] = tsdk_utmify( 'https://themeisle.com/black-friday/', 'bfcm24', 'nevecustomizer' );
152
153 // Banners urgency text.
154 $remaining_time = $this->get_remaining_time_for_event( $dates['end'] );
155 $announcements[ $announcement ]['remaining_time'] = $remaining_time;
156 $announcements[ $announcement ]['urgency_text'] = ! empty( $remaining_time ) ? 'Hurry up! Only ' . $remaining_time . ' left.' : '';
157 }
158 }
159
160 return apply_filters( 'themeisle_sdk_announcements_data', $announcements );
161 }
162
163 /**
164 * Get the announcement data.
165 *
166 * @param string $announcement The announcement to get the data for.
167 *
168 * @return array
169 */
170 public function get_announcement_data( $announcement ) {
171 return ! empty( $announcement ) && is_string( $announcement ) && isset( self::$timeline[ $announcement ] ) ? self::$timeline[ $announcement ] : array();
172 }
173
174 /**
175 * Check if the announcement has an active timeline.
176 *
177 * @param array $dates The announcement to check.
178 *
179 * @return bool
180 */
181 public function is_active( $dates ) {
182
183 if ( empty( $this->time ) ) {
184 $this->time = current_time( 'Y-m-d' );
185 }
186
187 $start = isset( $dates['start'] ) ? $dates['start'] : null;
188 $end = isset( $dates['end'] ) ? $dates['end'] : null;
189
190 if ( $start && $end ) {
191 return $start <= $this->time && $this->time <= $end;
192 } elseif ( $start ) {
193 return $this->time >= $start;
194 } elseif ( $end ) {
195 return $this->time <= $end;
196 }
197
198 return false;
199 }
200
201 /**
202 * Get the remaining time for the event in a human readable format.
203 *
204 * @param string $end_date The end date for event.
205 * @return string Remaining time for the event.
206 */
207 public function get_remaining_time_for_event( $end_date ) {
208 if ( empty( $end_date ) || ! is_string( $end_date ) ) {
209 return '';
210 }
211
212 try {
213 $end_date = new \DateTime( $end_date, new \DateTimeZone( 'GMT' ) );
214 $current_date = new \DateTime( 'now', new \DateTimeZone( 'GMT' ) );
215 $diff = $end_date->diff( $current_date );
216
217 if ( $diff->days > 0 ) {
218 return $diff->days === 1 ? $diff->format( '%a day' ) : $diff->format( '%a days' );
219 }
220
221 if ( $diff->h > 0 ) {
222 return $diff->h === 1 ? $diff->format( '%h hour' ) : $diff->format( '%h hours' );
223 }
224
225 if ( $diff->i > 0 ) {
226 return $diff->i === 1 ? $diff->format( '%i minute' ) : $diff->format( '%i minutes' );
227 }
228
229 return $diff->s === 1 ? $diff->format( '%s second' ) : $diff->format( '%s seconds' );
230 } catch ( \Exception $e ) {
231 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
232 error_log( $e->getMessage() ); // phpcs:ignore
233 }
234 }
235
236 return '';
237 }
238
239 /**
240 * Check if the announcement can be shown.
241 *
242 * @param string $announcement_slug The announcement to check.
243 * @param array $dates The announcement to check.
244 *
245 * @return bool
246 */
247 public function can_show( $announcement_slug, $dates ) {
248 $dismiss_date = get_option( $this->option_prefix . $announcement_slug, false );
249
250 if ( false === $dismiss_date ) {
251 return true;
252 }
253
254 // If the start date is after the dismiss date, show the notice.
255 $start = isset( $dates['start'] ) ? $dates['start'] : null;
256 if ( $start && $dismiss_date < $start ) {
257 return true;
258 }
259
260 return false;
261 }
262
263 /**
264 * Disable the notification via ajax.
265 *
266 * @return void
267 */
268 public function disable_notification_ajax() {
269 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), 'dismiss_themeisle_event_notice' ) ) {
270 wp_die( 'Invalid nonce! Refresh the page and try again.' );
271 }
272
273 if ( ! isset( $_POST['announcement'] ) || ! is_string( $_POST['announcement'] ) ) {
274 wp_die( 'Invalid announcement! Refresh the page and try again.' );
275 }
276
277 $announcement = sanitize_key( $_POST['announcement'] );
278
279 update_option( $this->option_prefix . $announcement, current_time( 'Y-m-d' ) );
280 wp_die( 'success' );
281 }
282
283 /**
284 * Render the Black Friday notice.
285 *
286 * @return void
287 */
288 public function black_friday_notice_render() {
289
290 // Prevent the notice from being rendered twice.
291 if ( self::$timeline['black_friday']['rendered'] ) {
292 return;
293 }
294 self::$timeline['black_friday']['rendered'] = true;
295
296 $product_names = array();
297
298 foreach ( Loader::get_products() as $product ) {
299 $slug = $product->get_slug();
300
301 // Do not add if the contains the string 'pro'.
302 if ( strpos( $slug, 'pro' ) !== false ) {
303 continue;
304 }
305
306 $product_names[] = $product->get_name();
307 }
308
309 // Randomize the products and get only 4.
310 shuffle( $product_names );
311 $product_names = array_slice( $product_names, 0, 4 );
312
313 ?>
314 <style>
315 .themeisle-sale {
316 display: flex;
317 }
318 </style>
319 <div class="themeisle-sale notice notice-info is-dismissible" data-announcement="black_friday">
320 <img src="<?php echo esc_url_raw( $this->get_sdk_uri() . 'assets/images/themeisle-logo.svg' ); ?>" />
321 <p>
322 <strong>Themeisle Black Friday Sale is Live!</strong> - Enjoy Maximum Savings on <?php echo esc_html( implode( ', ', $product_names ) ); ?>.
323 <a href="<?php echo esc_url_raw( tsdk_utmify( 'https://themeisle.com/blackfriday/', 'bfcm24', 'globalnotice' ) ); ?>" target="_blank">Learn more</a>
324 <span class="themeisle-sale-error"></span>
325 </p>
326 </div>
327 <script type="text/javascript" data-origin="themeisle-sdk">
328 window.document.addEventListener('DOMContentLoaded', () => {
329 const observer = new MutationObserver((mutationsList, observer) => {
330 for(let mutation of mutationsList) {
331 if (mutation.type === 'childList') {
332 const container = document.querySelector('.themeisle-sale.notice');
333 const button = container?.querySelector('button');
334 if ( button ) {
335 button.addEventListener('click', e => {
336 e.preventDefault();
337 fetch('<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', {
338 method: 'POST',
339 headers: {
340 'Content-Type': 'application/x-www-form-urlencoded'
341 },
342 body: new URLSearchParams({
343 action: 'themeisle_sdk_dismiss_announcement',
344 nonce: '<?php echo esc_attr( wp_create_nonce( 'dismiss_themeisle_event_notice' ) ); ?>',
345 announcement: container.dataset.announcement
346 })
347 })
348 .then(response => response.text())
349 .then(response => {
350 if (!response?.includes('success')) {
351 document.querySelector('.themeisle-sale-error').innerHTML = response;
352 return;
353 }
354
355 document.querySelectorAll('.themeisle-sale.notice').forEach(el => {
356 el.classList.add('hidden');
357 setTimeout(() => {
358 el.remove();
359 }, 800);
360 });
361 })
362 .catch(error => {
363 console.error('Error:', error);
364 document.querySelector('.themeisle-sale-error').innerHTML = error;
365 });
366 });
367 observer.disconnect();
368 break;
369 }
370 }
371 }
372 });
373
374 observer.observe(document.body, { childList: true, subtree: true });
375 });
376 </script>
377 <?php
378 }
379 }
380