PluginProbe ʕ •ᴥ•ʔ
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI / 3.6.0
Everest Forms – Contact Form, Payment Form, Quiz, Survey & Custom Form Builder with AI v3.6.0
3.6.0 3.5.3 3.5.2 3.5.1 3.5.0 3.4.8 3.4.7 3.4.6 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5.1 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.10 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.6.1 1.6.7 1.7.0 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.2 1.7.2.1 1.7.2.2 1.7.3 1.7.4 1.7.5 1.7.5.1 1.7.5.2 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.8.0 1.8.0.1 1.8.1 1.8.2 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.0.1 1.9.1 1.9.2 1.9.3 1.9.4 1.9.4.1 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.0.1 2.0.1 2.0.2 2.0.3 2.0.3.1 2.0.4 2.0.4.1 2.0.5 2.0.6 2.0.7 2.0.8 2.0.8.1 2.0.9 3.0.0 3.0.0.1 3.0.1 3.0.2 3.0.3 3.0.3.1 3.0.4 3.0.4.1 3.0.4.2 3.0.5 3.0.5.1 3.0.5.2 3.0.6 3.0.6.1 3.0.7.1 3.0.8 3.0.8.1 3.0.9 3.0.9.1 3.0.9.2 3.0.9.3 3.0.9.4 3.0.9.5 3.1.0 3.1.1 3.1.2 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.4.0 3.4.1 3.4.2 3.4.2.1 3.4.3 3.4.4 3.4.5 trunk 1.0 1.0.1 1.0.2 1.0.3
everest-forms / vendor / themegrill / themegrill-sdk / src / Modules / Notification.php
everest-forms / vendor / themegrill / themegrill-sdk / src / Modules Last commit date
Announcements.php 6 months ago Licenser.php 1 month ago Logger.php 6 months ago Notification.php 6 months ago Rollback.php 6 months ago ScriptLoader.php 6 months ago UninstallFeedback.php 6 months ago Updater.php 1 month ago
Notification.php
514 lines
1 <?php
2 /**
3 * The notification model class for ThemeGrill SDK
4 *
5 * @package ThemeGrillSDK
6 * @subpackage Modules
7 * @copyright Copyright (c) 2025, ThemeGrill
8 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
9 * @since 1.0.0
10 */
11
12 namespace ThemeGrillSDK\Modules;
13
14 use ThemeGrillSDK\Common\AbstractModule;
15 use ThemeGrillSDK\Product;
16
17 // Exit if accessed directly.
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 /**
23 * Notification module for ThemeGrill SDK.
24 */
25 class Notification extends AbstractModule {
26 /**
27 * Show notifications only after the user has the product installed after this amount of time, in hours.
28 */
29 const MIN_INSTALL_TIME = 100;
30 /**
31 * How much time should we show the notification, in days.
32 */
33 const MAX_TIME_TO_LIVE = 7;
34
35 /**
36 * Number of days between notifications.
37 */
38 const TIME_BETWEEN_NOTIFICATIONS = 5;
39
40 /**
41 * Holds a possible notification list.
42 *
43 * @var array Notifications list.
44 */
45 private static $notifications = [];
46
47 /**
48 * Show notification data.
49 */
50 public static function show_notification() {
51
52 $current_notification = self::get_last_notification();
53
54 $notification_details = [];
55 // Check if the saved notification is still present among the possible ones.
56 if ( ! empty( $current_notification ) ) {
57 $notification_details = self::get_notification_details( $current_notification );
58 if ( empty( $notification_details ) ) {
59 $current_notification = [];
60 }
61 }
62 // Check if the notification is expired.
63 if ( ! empty( $current_notification ) && self::is_notification_expired( $current_notification ) ) {
64 update_option( $current_notification['id'], 'no' );
65 self::set_last_active_notification_timestamp();
66 $current_notification = [];
67 }
68 // If we don't have any saved notification, get a new one.
69 if ( empty( $current_notification ) ) {
70 $notification_details = self::get_random_notification();
71 if ( empty( $notification_details ) ) {
72 return;
73 }
74 self::set_active_notification(
75 [
76 'id' => $notification_details['id'],
77 'display_at' => time(),
78 ]
79 );
80 }
81 if ( empty( $notification_details ) ) {
82 return;
83 }
84 $notification_html = self::get_notification_html( $notification_details );
85 do_action( $notification_details['id'] . '_before_render' );
86
87 echo $notification_html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped, already escaped internally.
88
89 do_action( $notification_details['id'] . '_after_render' );
90 self::render_snippets();
91 }
92
93 /**
94 * Get last notification details.
95 *
96 * @return array Last notification details.
97 */
98 private static function get_last_notification() {
99 $notification = self::get_notifications_metadata();
100
101 return isset( $notification['last_notification'] ) ? $notification['last_notification'] : [];
102 }
103
104 /**
105 * Get notification center details.
106 *
107 * @return array Notification center details.
108 */
109 private static function get_notifications_metadata() {
110
111 $data = get_option(
112 'themeGrill_sdk_notifications',
113 [
114 'last_notification' => [],
115 'last_notification_active' => 0,
116 ]
117 );
118
119 return $data;
120 }
121
122 /**
123 * Check if the notification is still possible.
124 *
125 * @param array $notification Notification to check.
126 *
127 * @return array Either is still active or not.
128 */
129 private static function get_notification_details( $notification ) {
130 $notifications = array_filter(
131 self::$notifications,
132 function ( $value ) use ( $notification ) {
133 if ( isset( $value['id'] ) && isset( $notification['id'] ) && $value['id'] === $notification['id'] ) {
134 return true;
135 }
136
137 return false;
138 }
139 );
140
141 return ! empty( $notifications ) ? reset( $notifications ) : [];
142 }
143
144 /**
145 * Check if the notification is expired.
146 *
147 * @param array $notification Notification to check.
148 *
149 * @return bool Either the notification is due.
150 */
151 private static function is_notification_expired( $notification ) {
152 if ( ! isset( $notification['display_at'] ) ) {
153 return true;
154 }
155
156 $notifications = array_filter(
157 self::$notifications,
158 function ( $value ) use ( $notification ) {
159 if ( isset( $value['id'] ) && isset( $notification['id'] ) && $value['id'] === $notification['id'] ) {
160 return true;
161 }
162
163 return false;
164 }
165 );
166
167 if ( empty( $notifications ) ) {
168 return true;
169 }
170 $notification_definition = reset( $notifications );
171
172 $when_to_expire = isset( $notification_definition['expires_at'] )
173 ? $notification_definition['expires_at'] :
174 ( isset( $notification_definition['expires'] )
175 ? ( $notification['display_at'] + $notification_definition['expires'] ) :
176 ( $notification['display_at'] + self::MAX_TIME_TO_LIVE * DAY_IN_SECONDS )
177 );
178
179 return ( $when_to_expire - time() ) < 0;
180 }
181
182 /**
183 * Set last notification details.
184 */
185 private static function set_last_active_notification_timestamp() {
186 $metadata = self::get_notifications_metadata();
187 $metadata['last_notification_active'] = time();
188 update_option( 'themeGrill_sdk_notifications', $metadata );
189 }
190
191 /**
192 * Return notification to show.
193 *
194 * @return array Notification data.
195 */
196 public static function get_random_notification() {
197 if ( ( time() - self::get_last_active_notification_timestamp() ) < self::TIME_BETWEEN_NOTIFICATIONS * DAY_IN_SECONDS ) {
198 return [];
199 }
200
201 $notifications = self::$notifications;
202 $notifications = array_filter(
203 $notifications,
204 function ( $value ) {
205 if ( isset( $value['sticky'] ) && true === $value['sticky'] ) {
206 return true;
207 }
208
209 return false;
210 }
211 );
212 // No priority notifications, use all.
213 if ( empty( $notifications ) ) {
214 $notifications = self::$notifications;
215 }
216 if ( empty( $notifications ) ) {
217 return [];
218 }
219 $notifications = array_values( $notifications );
220
221 return $notifications[ array_rand( $notifications, 1 ) ];
222 }
223
224 /**
225 * Get last notification details.
226 *
227 * @return int Last notification details.
228 */
229 private static function get_last_active_notification_timestamp() {
230 $notification = self::get_notifications_metadata();
231
232 return isset( $notification['last_notification_active'] ) ? $notification['last_notification_active'] : 0;
233 }
234
235 /**
236 * Get last notification details.
237 *
238 * @param array $notification Notification data.
239 */
240 private static function set_active_notification( $notification ) {
241 $metadata = self::get_notifications_metadata();
242 $metadata['last_notification'] = $notification;
243 update_option( 'themeGrill_sdk_notifications', $metadata );
244 }
245
246 /**
247 * Get notification html.
248 *
249 * @param array $notification_details Notification details.
250 *
251 * @return string Html for notice.
252 */
253 public static function get_notification_html( $notification_details ) {
254 $default = [
255 'id' => '',
256 'heading' => '',
257 'img_src' => '',
258 'message' => '',
259 'ctas' => [
260 'confirm' => [
261 'link' => '#',
262 'text' => '',
263 ],
264 'cancel' => [
265 'link' => '#',
266 'text' => '',
267 ],
268 ],
269 'type' => 'success',
270 ];
271 $notification_details = wp_parse_args( $notification_details, $default );
272 global $pagenow;
273 $type = in_array( $notification_details['type'], [ 'success', 'info', 'warning', 'error' ], true ) ? $notification_details['type'] : 'success';
274 $notification_details['ctas']['cancel']['link'] = wp_nonce_url( add_query_arg( [ 'nid' => $notification_details['id'] ], admin_url( $pagenow ) ), $notification_details['id'], 'tsdk_dismiss_nonce' );
275 $notification_html = '<div class="notice notice-' . $type . ' is-dismissible themegrill-sdk-notice" data-notification-id="' . esc_attr( $notification_details['id'] ) . '" id="' . esc_attr( $notification_details['id'] ) . '-notification"> <div class="themegrill-sdk-notification-box">';
276
277 if ( ! empty( $notification_details['heading'] ) ) {
278 $notification_html .= sprintf( '<h4>%s</h4>', wp_kses_post( $notification_details['heading'] ) );
279 }
280 if ( ! empty( $notification_details['img_src'] ) ) {
281 $notification_html .= '<div class="wrap-flex">';
282 $notification_html .= sprintf( '<img src="%s" alt="%s" />', esc_attr( $notification_details['img_src'] ), esc_attr( $notification_details['heading'] ) );
283 }
284 if ( ! empty( $notification_details['message'] ) ) {
285 $notification_html .= wp_kses_post( $notification_details['message'] );
286 if ( ! empty( $notification_details['img_src'] ) ) {
287 $notification_html .= '</div>';
288 }
289 }
290 $notification_html .= '<div class="actions">';
291
292 if ( ! empty( $notification_details['ctas']['confirm']['text'] ) ) {
293 $notification_html .= sprintf(
294 '<a href="%s" target="_blank" class=" button button-primary %s" data-confirm="yes" >%s</a>',
295 esc_url( $notification_details['ctas']['confirm']['link'] ),
296 esc_attr( $notification_details['id'] . '_confirm' ),
297 wp_kses_post( $notification_details['ctas']['confirm']['text'] )
298 );
299 }
300
301 if ( ! empty( $notification_details['ctas']['cancel']['text'] ) ) {
302 $notification_html .= sprintf(
303 '<a href="%s" class=" button %s" data-confirm="no">%s</a>',
304 esc_url( $notification_details['ctas']['cancel']['link'] ),
305 esc_attr( $notification_details['id'] ) . '_cancel',
306 wp_kses_post( $notification_details['ctas']['cancel']['text'] )
307 );
308 }
309
310 $notification_html .= '</div>';
311 $notification_html .= ' </div>';
312 $notification_html .= ' </div>';
313
314 return $notification_html;
315 }
316
317 /**
318 * Adds js snippet for hiding the notice.
319 */
320 public static function render_snippets() {
321
322 ?>
323 <style type="text/css">
324 .themegrill-sdk-notification-box {
325 padding: 3px;
326 }
327
328 .themegrill-sdk-notification-box .wrap-flex {
329 display: flex;
330 align-items: center;
331 justify-content: start;
332 gap: 12px;
333 }
334
335 .themegrill-sdk-notification-box .wrap-flex img {
336 width: 42px;
337 object-fit: cover;
338 }
339
340 .themegrill-sdk-notification-box .actions {
341 margin-top: 6px;
342 margin-bottom: 4px;
343 }
344
345 .themegrill-sdk-notification-box .button {
346 margin-right: 5px;
347 }
348 </style>
349 <script type="text/javascript">
350 (function ($) {
351 $(document).ready(function () {
352 $('#wpbody-content').on('click', ".themegrill-sdk-notice a.button, .themegrill-sdk-notice .notice-dismiss", function (e) {
353
354 var container = $('.themegrill-sdk-notice');
355 var link = $(this);
356 var notification_id = container.attr('data-notification-id');
357 var confirm = link.attr('data-confirm');
358 if (typeof confirm === "undefined") {
359 confirm = 'no';
360 }
361 $.post(
362 ajaxurl,
363 {
364 'nonce': '<?php echo esc_attr( wp_create_nonce( (string) __CLASS__ ) ); ?>',
365 'action': 'themeGrill_sdk_dismiss_notice',
366 'id': notification_id,
367 'confirm': confirm,
368 },
369 ).fail(function() {
370 location.href = encodeURI(link.attr('href'));
371 });
372 if (confirm === 'yes') {
373 $(this).trigger('themegrill-sdk:confirmed');
374 } else {
375 $(this).trigger('themegrill-sdk:canceled');
376 }
377 container.hide();
378 if (confirm === 'no' || link.attr('href') === '#') {
379 return false;
380 }
381 });
382 });
383 })(jQuery);
384 </script>
385 <?php
386 }
387
388 /**
389 * Dismiss the notification.
390 */
391 public static function dismiss() {
392 check_ajax_referer( (string) __CLASS__, 'nonce' );
393
394 $id = isset( $_POST['id'] ) ? sanitize_text_field( $_POST['id'] ) : '';
395 $confirm = isset( $_POST['confirm'] ) ? sanitize_text_field( $_POST['confirm'] ) : 'no';
396
397 if ( empty( $id ) ) {
398 wp_send_json( [] );
399 }
400 self::setup_notifications();
401 $ids = wp_list_pluck( self::$notifications, 'id' );
402 if ( ! in_array( $id, $ids, true ) ) {
403 wp_send_json( [] );
404 }
405 self::set_last_active_notification_timestamp();
406 update_option( $id, $confirm );
407 do_action( $id . '_process_confirm', $confirm );
408 wp_send_json( [] );
409 }
410 /**
411 * Dismiss the notification.
412 */
413 public static function dismiss_get() {
414 $is_nonce_dismiss = sanitize_text_field( isset( $_GET['tsdk_dismiss_nonce'] ) ? $_GET['tsdk_dismiss_nonce'] : '' );
415 if ( strlen( $is_nonce_dismiss ) < 5 ) {
416 return;
417 }
418 $id = sanitize_text_field( isset( $_GET['nid'] ) ? $_GET['nid'] : '' );
419 if ( empty( $id ) ) {
420 return;
421 }
422 $nonce = wp_verify_nonce( sanitize_text_field( $_GET['tsdk_dismiss_nonce'] ), $id );
423 if ( $nonce !== 1 ) {
424 return;
425 }
426 $ids = wp_list_pluck( self::$notifications, 'id' );
427 if ( ! in_array( $id, $ids, true ) ) {
428 return;
429 }
430 $confirm = 'no';
431 self::set_last_active_notification_timestamp();
432 update_option( $id, $confirm );
433 do_action( $id . '_process_confirm', $confirm );
434 }
435
436 /**
437 * Check if we should load the notification module.
438 *
439 * @param Product $product Product to check.
440 *
441 * @return bool Should we load this?
442 */
443 public function can_load( $product ) {
444
445 if ( $this->is_from_partner( $product ) ) {
446 return false;
447 }
448 if ( ! current_user_can( 'manage_options' ) ) {
449 return false;
450 }
451 if ( ( time() - $product->get_install_time() ) < ( self::MIN_INSTALL_TIME * HOUR_IN_SECONDS ) ) {
452 return false;
453 }
454
455 return true;
456 }
457
458 /**
459 * Setup notifications queue.
460 */
461 public static function setup_notifications() {
462 $notifications = apply_filters( 'themeGrill_sdk_registered_notifications', [] );
463 $notifications = array_filter(
464 $notifications,
465 function ( $value ) {
466 if ( ! isset( $value['id'] ) ) {
467 return false;
468 }
469 if ( get_option( $value['id'], '' ) !== '' ) {
470 return false;
471 }
472
473 return apply_filters( $value['id'] . '_should_show', true );
474 }
475 );
476 self::$notifications = $notifications;
477 }
478 /**
479 * Load the module logic.
480 *
481 * @param Product $product Product to load the module for.
482 *
483 * @return Notification Module instance.
484 */
485 public function load( $product ) {
486 if ( apply_filters( 'themeGrill_sdk_hide_notifications', false ) ) {
487 return;
488 }
489 $this->product = $product;
490
491 $notifications = apply_filters( 'themeGrill_sdk_registered_notifications', [] );
492 $notifications = array_filter(
493 $notifications,
494 function ( $value ) {
495 if ( ! isset( $value['id'] ) ) {
496 return false;
497 }
498 if ( get_option( $value['id'], '' ) !== '' ) {
499 return false;
500 }
501
502 return apply_filters( $value['id'] . '_should_show', true );
503 }
504 );
505 self::$notifications = $notifications;
506 add_action( 'admin_notices', array( __CLASS__, 'show_notification' ) );
507 add_action( 'wp_ajax_themeGrill_sdk_dismiss_notice', array( __CLASS__, 'dismiss' ) );
508 add_action( 'admin_head', array( __CLASS__, 'dismiss_get' ) );
509 add_action( 'admin_head', array( __CLASS__, 'setup_notifications' ) );
510
511 return $this;
512 }
513 }
514