PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / trunk
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar vtrunk
3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 All 156 releases
notificationx / includes / FrontEnd / FrontEnd.php

FrontEnd.php in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar trunk, at includes/FrontEnd/FrontEnd.php

1,032 lines 48.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * FrontEnd Class
5 *
6 * @package NotificationX\FrontEnd
7 */
8
9 namespace NotificationX\FrontEnd;
10
11 use NotificationX\Admin\Entries;
12 use NotificationX\Admin\Settings;
13 use NotificationX\Core\Analytics;
14 use NotificationX\Core\Database;
15 use NotificationX\Core\GetData;
16 use NotificationX\NotificationX;
17 use NotificationX\Core\Locations;
18 use NotificationX\Core\PostType;
19 use NotificationX\Core\REST;
20 use NotificationX\GetInstance;
21 use NotificationX\Extensions\PressBar\PressBar;
22 use NotificationX\Core\Helper;
23 use NotificationX\Extensions\ExtensionFactory;
24 use NotificationX\Types\GDPR;
25
26 /**
27 * This class is responsible for all Front-End actions.
28 * @method static FrontEnd get_instance($args = null)
29 */
30 class FrontEnd {
31 /**
32 * Instance of FrontEnd
33 *
34 * @var FrontEnd
35 */
36 use GetInstance;
37 /**
38 * Assets Path and URL
39 */
40 const ASSET_URL = NOTIFICATIONX_ASSETS . 'public/';
41 const ASSET_PATH = NOTIFICATIONX_ASSETS_PATH . 'public/';
42 protected $notificationXArr = [];
43
44 /**
45 * Initially Invoked
46 * when its initialized.
47 */
48 public function __construct() {
49 Analytics::get_instance();
50 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reviewed for the NotificationX codebase: acceptable in this context.
51 if (!is_admin() || !empty($_GET['frontend'])) {
52 add_action('init', [$this, 'init'], 10);
53 }
54 add_filter('nx_frontend_localize_data', [$this, 'get_localize_data']);
55 Preview::get_instance();
56 }
57
58 /**
59 * This method is reponsible for Admin Menu of
60 * NotificationX
61 *
62 * @return void
63 */
64 public function init() {
65 add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts'], 10);
66 add_filter('nx_fallback_data', [$this, 'fallback_data'], 10, 3);
67 add_filter('nx_filtered_data', [$this, 'filtered_data'], 9999, 3);
68 add_filter('nx_filtered_post', [$this, 'filtered_post'], 9999, 2);
69 add_action('wp_print_footer_scripts', [$this, 'footer_scripts']);
70 add_filter('body_class', [ $this, 'nx_add_body_class' ] );
71
72 }
73
74 /**
75 * This method is responsible for enqueueing scripts for public use.
76 *
77 * @return void
78 */
79 public function enqueue_scripts() {
80 $custom_css = $this->generate_custom_css();
81 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
82 wp_register_script('notificationx-public', Helper::file('public/js/frontend.js', true), [], apply_filters('nx_frontend_js_version', NOTIFICATIONX_VERSION ), true);
83 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
84 wp_register_style('notificationx-public', Helper::file('public/css/frontend.css', true), [], apply_filters('nx_frontend_css_version', NOTIFICATIONX_VERSION ), 'all');
85 // wp_register_style('notificationx-icon-pack', Helper::file('public/icon/style.css', true), [], NOTIFICATIONX_VERSION, 'all');
86 // Localize scripts for frontend
87 wp_localize_script(
88 'notificationx-public',
89 'notificationxPublic',
90 array(
91 'necessary_tab_info' => [
92 'title' => __('Necessary', 'notificationx'),
93 'desc' => __('Necessary cookies are needed to ensure the basic functions of this site, like allowing secure log-ins and managing your consent settings. These cookies do not collect any personal information.', 'notificationx'),
94 ],
95 'functional_tab_info' => [
96 'title' => __('Functional', 'notificationx'),
97 'desc' => __('Functional cookies assist in performing tasks like sharing website content on social media, collecting feedback, and enabling other third-party features.', 'notificationx'),
98 ],
99 'analytics_tab_info' => [
100 'title' => __('Analytics', 'notificationx'),
101 'desc' => __('Analytical cookies help us understand how visitors use the website. They provide data on metrics like the number of visitors, bounce rate, traffic sources etc.', 'notificationx'),
102 ],
103 'performance_tab_info' => [
104 'title' => __('Performance', 'notificationx'),
105 'desc' => __("Performance cookies help analyze the website's key performance indicators, which in turn helps improve the user experience for visitors.", 'notificationx'),
106 ],
107 'advertising_tab_info' => [
108 'title' => __('Advertisement', 'notificationx'),
109 'desc' => __("Advertisement cookies help analyze the website's key advertising indicators, which in turn helps improve the user experience for visitors.", 'notificationx'),
110 ],
111 'uncategorized_tab_info' => [
112 'title' => __('Uncategorized', 'notificationx'),
113 'desc' => __("Uncategorized cookies are those that don't fall into any specific category but may still be used for various purposes on the site. These cookies help us improve user experience by tracking interactions that don't fit into other cookie types.", 'notificationx'),
114 ],
115 'is_enabled_wp_consent_api' => is_plugin_active('wp-consent-api/wp-consent-api.php'),
116 )
117 );
118
119 $exit = false;
120 $referer = isset($_SERVER['HTTP_REFERER']) ? esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER'])) : '';
121 if($referer && strpos($referer, 'wp-admin/widgets.php') !== false){
122 $exit = ['total' => 0];
123 }
124
125 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
126 $exit = apply_filters('nx_before_enqueue_scripts', $exit);
127 if(!empty($exit)){
128 $this->notificationXArr = $exit;
129 return;
130 }
131
132 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reviewed for the NotificationX codebase: acceptable in this context.
133 if (!$exit && empty($_GET['elementor-preview'])) {
134 $this->notificationXArr = $this->get_notifications_ids();
135 if ($this->notificationXArr['total'] > 0) {
136 $lang = get_locale();
137 $lang = str_replace('_', '-', $lang);
138 $lang = strtolower($lang);
139 $_lang = explode('-', $lang);
140 if ($lang !== "en" && $lang !== "en-us") {
141 $script = Helper::file("public/locale/$lang.js", false);
142 if (file_exists($script)) {
143 wp_enqueue_script('notificationx-moment-locale', Helper::file("public/locale/$lang.js", true), [], NOTIFICATIONX_VERSION, true);
144 } else if (!empty($_lang[1])) {
145 $lang = $_lang[0];
146 $script = Helper::file("public/locale/$lang.js", false);
147 if (file_exists($script)) {
148 wp_enqueue_script('notificationx-moment-locale', Helper::file("public/locale/$lang.js", true), [], NOTIFICATIONX_VERSION, true);
149 }
150 }
151 }
152
153 wp_enqueue_style('notificationx-public');
154 wp_enqueue_script('notificationx-public');
155 wp_enqueue_style('dashicons');
156 do_action('notificationx_scripts', $this->notificationXArr);
157 wp_add_inline_style( 'notificationx-public', $custom_css );
158 }
159 } else {
160 // @todo maybe elementor edit mode CSS. to move to top.
161 // LATER
162 }
163 }
164
165 public function nx_add_body_class($classes) {
166 $classes[] = 'has-notificationx';
167 return $classes;
168 }
169
170 private function separate_css($css) {
171 $media_css = '';
172 $normal_css = '';
173
174 // Match @media blocks
175 preg_match_all('/@media[^{]*{([^}]*{[^}]*})*[^}]*}/', $css, $media_matches);
176
177 // Extract normal CSS
178 $normal_css = preg_replace('/@media[^{]*{([^}]*{[^}]*})*[^}]*}/', '', $css);
179
180 // Extract @media CSS
181 if (!empty($media_matches[0])) {
182 $media_css = implode("\n", $media_matches[0]);
183 }
184
185 // Clean up normal CSS (remove extra whitespace)
186 $normal_css = trim($normal_css);
187
188 return [
189 'normal_css' => $normal_css,
190 'media_css' => $media_css
191 ];
192 }
193
194 public function generate_custom_css() {
195 $posts = Database::get_instance()->get_posts(Database::$table_posts, '*', ['enabled' => true] );
196 $combine_css = "";
197 foreach ($posts as $post) {
198 if( !empty( $post['data']['add_custom_css'] ) && !empty( $post['nx_id'] ) ) {
199 $separatedCss = $this->separate_css($post['data']['add_custom_css']);
200 if( !empty( $post['data']['source'] ) && $post['data']['source'] == 'press_bar' ) {
201 $combine_css .= "{$separatedCss['normal_css']} {$separatedCss['media_css']} ";
202 } else if( !empty( $post['data']['source'] ) && $post['data']['source'] == 'gdpr_notification' ) {
203 $combine_css .= "{$separatedCss['normal_css']} {$separatedCss['media_css']} ";
204 }
205 else {
206 $combine_css .= "{$separatedCss['normal_css']} {$separatedCss['media_css']} ";
207 }
208 }
209 }
210 return $combine_css;
211 }
212
213 public function footer_scripts() {
214 if (!empty($this->notificationXArr['total']) && $this->notificationXArr['total'] > 0) {
215 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
216 $this->notificationXArr = apply_filters('nx_frontend_localize_data', $this->notificationXArr);
217 ?>
218 <script data-no-optimize="1">
219 (function() {
220 window.notificationXArr = window.notificationXArr || [];
221 window.notificationXArr.push(<?php echo json_encode($this->notificationXArr); ?>);
222 })();
223 </script>
224 <?php
225 }
226 }
227
228 // @todo deprecated. use get_localize_data instead
229 public function localizeScripts() {
230 return [];
231 }
232
233 public function get_localize_data($data) {
234 $data['rest'] = REST::get_instance()->rest_data(false);
235 $data['assets'] = self::ASSET_URL;
236 $data['is_pro'] = false;
237 $data['gmt_offset'] = get_option('gmt_offset');
238 $data['lang'] = get_locale();
239 $data['common_assets'] = NOTIFICATIONX_COMMON_URL;
240 $data['extra'] = [
241 'is_singular' => is_singular(),
242 'query' => $GLOBALS['wp_query']->query,
243 'queried_id' => get_queried_object_id(),
244 'pid' => !empty($GLOBALS['post']->ID) ? $GLOBALS['post']->ID : 0,
245 ];
246 $data['localeData'] = load_script_textdomain('notificationx-public', 'notificationx');
247 return $data;
248 }
249
250 public function get_notifications_data($params) {
251 $_params = $params;
252 $result = [
253 'global' => [],
254 'active' => [],
255 'pressbar' => [],
256 'gdpr' => [],
257 'shortcode' => [],
258 'popup' => [],
259 'exit_intent' => [],
260 ];
261 if (!empty($_params['all_active'])) {
262 $params = $this->get_notifications_ids();
263 }
264 $params = wp_parse_args(
265 $params,
266 [
267 'global' => [],
268 'active' => [],
269 'pressbar' => [],
270 'gdpr' => [],
271 'popup' => [],
272 'exit_intent' => [],
273 'shortcode' => [],
274 'inline_shortcode' => false,
275 ]
276 );
277 $global = $params['global'];
278 $active = $params['active'];
279 $pressbar = $params['pressbar'];
280 $gdpr = $params['gdpr'];
281 $popup = $params['popup'];
282 $exit_intent = $params['exit_intent'];
283 $shortcode = $params['shortcode'];
284 $all = array_merge($global, $active, $shortcode);
285 $_defaults = array(
286 'none' => '',
287 'name' => __('Someone', 'notificationx'),
288 'first_name' => __('Someone', 'notificationx'),
289 'last_name' => __('Someone', 'notificationx'),
290 'anonymous_title' => __('Anonymous Title', 'notificationx'),
291 'sometime' => __('Some time ago', 'notificationx'),
292 );
293
294 // foreach (['global', 'active'] as $key => $type) {
295 // foreach ($params[$type] as $id) {
296 // $result[$type][$id] = [];
297 // }
298 // }
299
300 $device = isset($params['deviceType']) && !empty($params['deviceType']) ? $params['deviceType'] : '';
301 if (!empty($all)) {
302 $notifications = $this->get_notifications($all, $device);
303 $entries = $this->get_entries($all, $notifications, $params);
304
305 foreach ($entries as $entry) {
306 $nx_id = $entry['nx_id'];
307 $settings = $notifications[$nx_id];
308
309 // check if notification is enabled or not.
310 if (!$settings['enabled']) {
311 continue;
312 }
313
314 $type = $settings['type'];
315 $source = $settings['source'];
316
317 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
318 $should_continue = apply_filters("nx_entry_show_on_frontend_$source", false, $entry, $settings);
319 if ( $should_continue ) {
320 continue;
321 }
322
323 if (!empty($entry['timestamp'])) {
324 $timestamp = $entry['timestamp'];
325 $display_from = !empty($settings['display_from']) ? $settings['display_from'] : 2;
326 $display_from = Helper::generate_time_string($settings);
327 if (!is_numeric($timestamp)) {
328 $entry['timestamp'] = $timestamp = is_string($timestamp) ? strtotime($timestamp) : false;
329 }
330 if ($timestamp && $display_from > $timestamp) {
331 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
332 if (apply_filters("nx_entry_display_$source", true, $entry, $settings)) {
333 continue;
334 }
335 }
336 }
337
338 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
339 $defaults = apply_filters("nx_fallback_data_$source", $_defaults, $entry, $settings);
340 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
341 $defaults = apply_filters('nx_fallback_data', $defaults, $entry, $settings);
342
343 $entry = $this->apply_defaults($entry, $defaults);
344 $entry['image_data'] = $this->get_image_url($entry, $settings);
345 if (!empty($entry['title'])) {
346 $entry['title'] = wp_strip_all_tags(html_entity_decode($entry['title']));
347 }
348
349 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
350 $entry = apply_filters("nx_filtered_entry_$type", $entry, $settings);
351 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
352 $entry = apply_filters("nx_filtered_entry_$source", $entry, $settings);
353 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
354 $entry = apply_filters('nx_filtered_entry', $entry, $settings);
355 $entry = $this->link_url($entry, $settings, $params);
356
357 // @todo shortcode
358 // @todo check if the current page have shortcode.
359 if (in_array($nx_id, $shortcode)) {
360 $position = $settings['position'];
361 $settings['position'] = "notificationx-shortcode-$nx_id";
362 if (empty($result['shortcode'][$nx_id]['post'])) {
363 $result['shortcode'][$nx_id]['post'] = $settings;
364 }
365 $result['shortcode'][$nx_id]['entries'][] = $entry;
366 $settings['position'] = $position;
367 if ($settings['show_on'] === 'only_shortcode' || 'inline' === $settings['type'] || 'woocommerce_sales_inline' == $settings['source']) {
368 continue;
369 }
370 if (empty($global) && empty($active)) {
371 continue;
372 }
373 }
374
375 if (!empty($settings['global_queue'])) {
376 if (empty($result['global'][$nx_id]['post'])) {
377 $result['global'][$nx_id]['post'] = $settings;
378 }
379 $result['global'][$nx_id]['entries'][] = $entry;
380 } else {
381 if (empty($result['active'][$nx_id]['post'])) {
382 $result['active'][$nx_id]['post'] = $settings;
383 }
384 $result['active'][$nx_id]['entries'][] = $entry;
385 }
386 }
387
388 foreach ($result as &$group) {
389 foreach ($group as &$value) {
390 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
391 $value['entries'] = apply_filters("nx_filtered_data_{$value['post']['type']}", $value['entries'], $value['post'], $params);
392 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
393 $value['entries'] = apply_filters("nx_filtered_data_{$value['post']['source']}", $value['entries'], $value['post'], $params);
394 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
395 $value['entries'] = apply_filters('nx_filtered_data', $value['entries'], $value['post'], $params);
396 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
397 $value['post'] = apply_filters('nx_filtered_post', $value['post'], $params);
398 }
399 }
400 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
401 $result = apply_filters('nx_filtered_notice', $result, $params);
402 }
403
404 if (!empty($pressbar)) {
405 $notifications = $this->get_notifications($pressbar, $device);
406 foreach ($notifications as $key => $settings) {
407 $_nx_id = $settings['nx_id'];
408 // check if position is bottom_left then modify it to top
409 if($settings['position'] == 'bottom_left'){
410 $settings['position'] = 'top';
411 }
412 $elementor_post_id = isset($settings['elementor_id']) ? $settings['elementor_id'] : '';
413 if ($elementor_post_id == '' || get_post_status($elementor_post_id) !== 'publish' | !class_exists('\Elementor\Plugin')) {
414 $settings['elementor_id'] = false;
415 }
416 if ( ( !empty( $_params['all_active']) && $elementor_post_id ) || !$settings['enabled'] ) {
417 continue;
418 }
419
420 // $settings['button_url'] = apply_filters("nx_notification_link_{$settings['source']}", $settings['button_url'], $settings);
421 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
422 $settings['button_url'] = apply_filters('nx_notification_link', $settings['button_url'], $settings);
423 if (!empty($settings['button_url']) && strpos($settings['button_url'], '//') === false && strpos($settings['button_url'], './') === false) {
424 $settings['button_url'] = "//{$settings['button_url']}";
425 }
426 $bar_content = $this->get_bar_content($settings, false, $params);
427 if ($bar_content !== '&nbsp;' || !empty($settings['enable_countdown'])) {
428 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
429 $settings = apply_filters('nx_filtered_post', $settings, $params);
430 $result['pressbar'][$_nx_id]['post'] = $settings;
431 $result['pressbar'][$_nx_id]['content'] = $bar_content;
432 }
433
434 unset($_nx_id);
435 }
436 }
437
438 if (!empty($gdpr)) {
439 $notifications = $this->get_notifications($gdpr);
440 foreach ($notifications as $key => $settings) {
441 $_nx_id = $settings['nx_id'];
442 if (!empty($_params['all_active'])) {
443 continue;
444 }
445
446 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
447 $settings = apply_filters('nx_filtered_post', $settings, $params);
448
449 $result['gdpr'][$_nx_id]['post'] = $settings;
450 $result['gdpr'][$_nx_id]['content'] = "";
451 unset($_nx_id);
452 }
453 }
454
455 // Popup Notification
456 if (!empty($popup)) {
457 $popup_notifications = $this->get_notifications($popup);
458 foreach ($popup_notifications as $key => $settings) {
459 $_nx_id = $settings['nx_id'];
460 if ( !$settings['enabled'] ) {
461 continue;
462 }
463
464 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
465 $settings = apply_filters('nx_filtered_post', $settings, $params);
466
467 $result['popup'][$_nx_id]['post'] = $settings;
468 $result['popup'][$_nx_id]['content'] = "";
469 unset($_nx_id);
470 }
471 }
472
473 // Exit Intent Notification
474 if (!empty($exit_intent)) {
475 $exit_intent_notifications = $this->get_notifications($exit_intent);
476 foreach ($exit_intent_notifications as $key => $settings) {
477 $_nx_id = $settings['nx_id'];
478 if ( !$settings['enabled'] ) {
479 continue;
480 }
481 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
482 $settings = apply_filters('nx_filtered_post', $settings, $params);
483 $result['exit_intent'][$_nx_id]['post'] = $settings;
484 $result['exit_intent'][$_nx_id]['content'] = "";
485 unset($_nx_id);
486 }
487 }
488
489 $result['settings'] = $this->get_settings();
490 return $result;
491 }
492
493 public function get_settings(){
494
495 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
496 $branding_url = apply_filters('nx_branding_url', NOTIFICATIONX_PLUGIN_URL . '?utm_source=' . esc_url(home_url()) . '&utm_medium=notificationx');
497 $settings = [
498 'disable_powered_by' => Settings::get_instance()->get('settings.disable_powered_by'),
499 'affiliate_link' => $branding_url,
500 'enable_analytics' => Settings::get_instance()->get('settings.enable_analytics', true),
501 'analytics_from' => Settings::get_instance()->get('settings.analytics_from'),
502 'delay_before' => Settings::get_instance()->get('settings.delay_before', 5),
503 'display_for' => Settings::get_instance()->get('settings.display_for', 5),
504 'delay_between' => Settings::get_instance()->get('settings.delay_between', 5),
505 'loop' => Settings::get_instance()->get('settings.loop', 5),
506 'random' => Settings::get_instance()->get('settings.random', 5),
507 'analytics_nonce' => wp_create_nonce('analytics_nonce'),
508 ];
509 return $settings;
510 }
511
512 public function get_notifications_ids($return_posts = false, $args = []) {
513 $args = wp_parse_args($args, [
514 'enabled' => true,
515 'updated_at' => ['<=', Helper::mysql_time()],
516 // 'global_queue' => true,
517 ]);
518 $notifications = PostType::get_instance()->get_posts($args);
519
520 $active_notifications = $global_notifications = $bar_notifications = $gdpr_notification = $popup_notifications = $exit_intent_notifications = array();
521
522 foreach ($notifications as $key => $settings) {
523 // $settings = NotificationX::get_instance()->normalize_post($post);
524
525 // IF PRESSBAR TIME RE_CONFIG THEN REMOVE COOKIE
526 // if( $settings['source'] == 'press_bar'){
527 // if( $_settings->enable_countdown && ( Helper::current_timestamp($_settings->countdown_start_date) < time() || Helper::current_timestamp($_settings->countdown_end_date) > time() ) ) {
528 // unset( $_COOKIE["notificationx_{$settings['nx_id']}"] );
529 // \setcookie("notificationx_{$settings['nx_id']}", null);
530 // }
531 // }
532
533 /**
534 * Check if it's a pro source and pro plugin is disabled.
535 */
536 if(!NotificationX::is_pro()){
537 $ext = ExtensionFactory::get_instance()->get($settings['source']);
538 if($ext && $ext->is_pro){
539 continue;
540 }
541 }
542
543 $countdown_rand = !empty($settings['countdown_rand']) ? "-{$settings['countdown_rand']}" : '';
544
545 if (!empty($_COOKIE["notificationx_{$settings['nx_id']}$countdown_rand"]) && $_COOKIE["notificationx_{$settings['nx_id']}$countdown_rand"] == true) {
546 unset($notifications[$key]);
547 continue;
548 }
549
550 if($this->is_logged_in($settings['show_on_display'])){
551 continue;
552 }
553
554 $locations = isset($settings['all_locations']) ? $settings['all_locations'] : [];
555 $custom_ids = isset($settings['custom_ids']) ? $settings['custom_ids'] : [];
556 $taxonomy_ids = isset($settings['taxonomy_ids']) ? $settings['taxonomy_ids'] : [];
557
558 if($this->check_show_on($locations, $custom_ids, $settings['show_on'], $taxonomy_ids)){
559 continue;
560 }
561
562 /**
563 * Check for hiding in mobile device
564 */
565 // if ($settings['hide_on_mobile'] && wp_is_mobile()) {
566 // continue;
567 // }
568
569 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
570 $show_on_exclude = apply_filters('nx_show_on_exclude', false, $settings);
571 if ($show_on_exclude) {
572 continue;
573 }
574
575 // excluding pressBar on rest request. PressBar is printed directly in head.
576 // if((empty($args['source']) || $args['source'] !== 'press_bar') && $settings['source'] == 'press_bar'){
577 // continue;
578 // }
579
580 $active_global_queue = boolval($settings['global_queue']);
581 if ($settings['source'] == 'press_bar') {
582 // if(
583 // !$_settings->elementor_id &&
584 // $_settings->enable_countdown &&
585 // !$_settings->evergreen_timer &&
586 // (
587 // strtotime($_settings->countdown_start_date) > time() ||
588 // strtotime($_settings->countdown_end_date) < time()
589 // )
590 // ){
591 // continue;
592 // }
593
594 $bar_notifications[] = $return_posts ? $settings : $settings['nx_id'];
595 if (!empty($settings['elementor_id']) && class_exists('\Elementor\Plugin')) {
596 // @todo Find a function to only load css instead of building content.
597 \Elementor\Plugin::$instance->frontend->get_builder_content($settings['elementor_id'], false);
598 }
599 } elseif($settings['source'] == 'gdpr_notification') {
600 $gdpr_notification[] = $return_posts ? $settings : $settings['nx_id'];
601 } elseif($settings['source'] == 'popup_notification') {
602 $popup_notifications[] = $settings['nx_id'];
603 } elseif($settings['source'] == 'exit_intent_custom') {
604 $exit_intent_notifications[] = $settings['nx_id'];
605 // Force Elementor's frontend runtime + per-widget assets onto the
606 // page (elementor-frontend.js, the document CSS, and each widget's
607 // get_style_depends()/get_script_depends() — e.g. nx-countdown).
608 // The popup HTML itself is rendered later via REST, where these
609 // enqueues would be discarded, so the countdown widget would lose
610 // its layout CSS and timer JS. Mirrors the press_bar branch above.
611 if (!empty($settings['elementor_id']) && class_exists('\Elementor\Plugin')) {
612 \Elementor\Plugin::$instance->frontend->get_builder_content($settings['elementor_id'], false);
613 }
614 } elseif ($active_global_queue && NotificationX::is_pro()) {
615 $global_notifications[] = $return_posts ? $settings : $settings['nx_id'];
616 } else {
617 $active_notifications[] = $return_posts ? $settings : $settings['nx_id'];
618 }
619
620 unset($notifications[$key]);
621 }
622 // do_action('nx_active_notificationx', $notifications);
623
624 // @todo maybe combine two hooks.
625
626 return apply_filters(
627 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
628 'get_notifications_ids',
629 [
630 'global' => $global_notifications,
631 'active' => $active_notifications,
632 'pressbar' => $bar_notifications,
633 'gdpr' => $gdpr_notification,
634 'popup' => $popup_notifications,
635 'exit_intent' => $exit_intent_notifications,
636 'total' => (count($global_notifications) + count($active_notifications) + count($bar_notifications) + count($gdpr_notification) + count($popup_notifications) + count($exit_intent_notifications)),
637 ],
638 $notifications
639 );
640 }
641
642 public function is_logged_in($show_on_display){
643 $logged_in = is_user_logged_in();
644
645 if (($logged_in && 'logged_out_user' == $show_on_display) || (!$logged_in && 'logged_in_user' == $show_on_display)) {
646 return true;
647 }
648 return false;
649 }
650
651 /**
652 * @todo filter is not extensive enough.
653 */
654 public function check_show_on($locations, $custom_ids, $show_on, $taxonomy_ids = ''){
655 $check_location = false;
656
657 if ($locations == 'is_custom' || is_array($locations) && in_array('is_custom', $locations)) {
658 $custom_ids = !empty($custom_ids) ? $custom_ids : [];
659 }
660 if (!empty($locations)) {
661 // @todo need to pass url.
662 $check_location = Locations::get_instance()->check_location($locations, $custom_ids, $taxonomy_ids);
663 }
664
665 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
666 $check_location = apply_filters('nx_check_location', $check_location, $custom_ids, $show_on);
667
668 if ($show_on == 'on_selected') {
669 // show if the page is on selected
670 if (!$check_location) {
671 return true;
672 }
673 } elseif ($show_on == 'hide_on_selected') {
674 // hide if the page is on selected
675 if ($check_location) {
676 return true;
677 }
678 } elseif ($show_on === 'only_shortcode') {
679 return true;
680 }
681 return false;
682 }
683
684 public function get_notifications($ids, $device = '') {
685 $results = [];
686 $notifications = PostType::get_instance()->get_posts_by_ids($ids);
687
688 foreach ($notifications as $key => $value) {
689 /**
690 * Check for hiding in mobile device
691 */
692 if (empty($value['hide_on_mobile']) && $device === 'mobile') {
693 continue;
694 }
695
696 /**
697 * Check for hiding in tablet device
698 */
699 if (empty($value['hide_on_tab']) && $device === 'tablet') {
700 continue;
701 }
702
703 /**
704 * Check for hiding in desktop device
705 */
706 if (empty($value['hide_on_desktop']) && $device === 'desktop') {
707 continue;
708 }
709
710 /**
711 * Check if it's a pro source and pro plugin is disabled.
712 */
713 if(!NotificationX::is_pro()){
714 $ext = ExtensionFactory::get_instance()->get($value['source']);
715 if($ext && $ext->is_pro){
716 continue;
717 }
718 }
719
720 $results[$value['nx_id']] = $value;
721 }
722 return $results;
723 }
724
725 public function get_entries($ids, $notifications,$params) {
726 $entries = [];
727 if (!empty($ids) && is_array($ids)) {
728 $query = [];
729 foreach ($ids as $id) {
730 if (!empty($notifications[$id])) {
731 $post = $notifications[$id];
732 $global_query = " nx_id = " . absint($id) . " AND source = '" . esc_sql($post['source']) . "'";
733 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
734 $_q = apply_filters("nx_get_entries_query_part_{$notifications[$id]['source']}",$global_query, $notifications[$id], $params );
735 $query[$id] = " (" . $_q . ")";
736 }
737 }
738 if (!empty($query)) {
739 $entries = Entries::get_instance()->get_entries('WHERE' . implode(' OR ', $query));
740 foreach ($entries as $key => $value) {
741 if (!empty($value['data'])) {
742 $entries[$key] = array_merge($value, $value['data']);
743 unset($entries[$key]['data']);
744 }
745 }
746 }
747 }
748 if (!is_array($entries)) {
749 $entries = [];
750 }
751 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
752 $entries = apply_filters('nx_frontend_get_entries', $entries, $ids, $notifications,$params);
753 return $entries;
754 }
755
756 /**
757 * This function is responsible for make ready the link for notifications.
758 *
759 * @param array $data
760 * @return void
761 */
762 public static function link_url($entry, $post, $params = []) {
763 if (empty($entry)) {
764 return false;
765 }
766 $link = isset($entry['link']) ? $entry['link'] : '';
767 // removing link if link type is none.
768 if (empty($post['link_type']) || $post['link_type'] === 'none') {
769 $link = '';
770 }
771
772 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
773 $link = apply_filters("nx_notification_link_{$post['source']}", $link, $post, $entry, $params);
774 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
775 $entry['link'] = apply_filters('nx_notification_link', $link, $post, $entry, $params);
776 return $entry;
777 }
778
779 /**
780 * This function is responsible for getting the image url
781 * using Product ID or from default image settings.
782 *
783 * @param array $data
784 * @param array $settings
785 * @return array of image data, contains url and title as alt text
786 */
787 public function get_image_url($data, $settings) {
788 $source = $settings['source'];
789 $alt_title = isset($data['name']) ? $data['name'] : '';
790 $image_type = isset($settings['show_notification_image']) ? $settings['show_notification_image'] : false;
791 if (empty($alt_title)) {
792 $alt_title = isset($data['title']) ? $data['title'] : '';
793 }
794 $image_data = [
795 'url' => '',
796 'alt' => $alt_title,
797 ];
798
799 if ($settings['show_default_image']) {
800 if (!empty($settings['image_url']['url'])) {
801 $image_size = (string) Settings::get_instance()->get('settings.notification_image_size', '100_100');
802 $image_size = explode('_', $image_size);
803 $width = 100;
804 $height = 100;
805 if( !empty( $image_size[0] ) && !empty( $image_size[1] ) ) {
806 $width = $image_size[0];
807 $height = $image_size[1];
808 }
809 $image = wp_get_attachment_image_src($settings['image_url']['id'], [$width, $height], true);
810 $image_data['url'] = $image[0];
811 } else {
812 $default_avatar = $settings['default_avatar'];
813 $image_data['url'] = NOTIFICATIONX_PUBLIC_URL . 'image/icons/' . $default_avatar;
814 }
815 } else {
816 if ($image_type === 'gravatar') {
817 $_data = array_change_key_case($data);
818 if (isset($_data['email'])) {
819 $image_data['url'] = get_avatar_url($_data['email'], ['size' => '100']);
820 }
821 }
822 }
823
824 $image_data['classes'] = $image_type;
825 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
826 $image_data = apply_filters("nx_notification_image_$source", $image_data, $data, $settings);
827 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
828 $image_data = apply_filters('nx_notification_image', $image_data, $data, $settings);
829
830 if (!empty($image_data['url'])) {
831 return $image_data;
832 }
833
834 return false;
835 }
836
837 public function apply_defaults($entry, $defaults) {
838
839 foreach ($defaults as $key => $value) {
840 // @todo mukul remove `|| empty(trim($entry[$key]))`
841 if (empty($entry[$key]) || (is_string($entry[$key]) && empty(trim($entry[$key])))) {
842 $entry[$key] = $value;
843 }
844 }
845
846 return $entry;
847 }
848
849 public function fallback_data($data, $saved_data, $settings) {
850 if ((empty($saved_data['name']) || $data['name'] == __('Someone', 'notificationx')) && isset($saved_data['first_name']) || isset($saved_data['last_name'])) {
851 $first_name = isset($saved_data['first_name']) ? $saved_data['first_name'] : '';
852 $last_name = isset($saved_data['last_name']) ? $saved_data['last_name'] : '';
853 $data['name'] = Helper::name($first_name, $last_name);
854 }
855 if (!empty($saved_data['name']) && empty($saved_data['first_name']) && empty($saved_data['last_name'])) {
856 $data['first_name'] = $saved_data['name'];
857 }
858 $data['title'] = isset($saved_data['post_title']) ? $saved_data['post_title'] : (isset($data['title']) ? $data['title'] : '');
859 return $data;
860 }
861
862 /**
863 * Add NotificationX in Footer
864 *
865 * @return void
866 */
867 public function filtered_data($entries, $post, $params) {
868 if (is_array($entries) && (!defined('NX_DEBUG') || !NX_DEBUG)) {
869 if (!empty($post['display_last']) && !in_array($post['source'], ['google', 'woo_inline', 'edd_inline', 'tutor_inline', 'learndash_inline', 'google_reviews', 'youtube','woocommerce_sales_inline','fluentcart_inline'])) {
870 $entries = array_slice($entries, 0, $post['display_last']);
871 }
872 foreach ($entries as $index => $entry) {
873 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
874 $_entry = apply_filters("nx_frontend_keep_entry_{$post['source']}", [
875 'nx_id' => $entry['nx_id'],
876 'timestamp' => isset($entry['timestamp']) ? $entry['timestamp'] : Helper::current_timestamp($entry['updated_at']),
877 'updated_at' => isset( $entry['updated_at'] ) ? $entry['updated_at'] : '',
878 'image_data' => $entry['image_data'],
879 'link' => $entry['link'],
880 // Location is rendered by themes (e.g. conv-theme-fifteen) via the
881 // hardcoded {{city_country}} tag, not through a notification-template
882 // param, so the template loop below never whitelists it. Keep the
883 // raw parts here so the frontend can compose "City, Country".
884 'city' => $entry['city'] ?? '',
885 'country' => $entry['country'] ?? '',
886 ], $entry, $post, $params);
887 if (!empty($params['inline_shortcode']) && isset($entry['product_id'])) {
888 $_entry['product_id'] = $entry['product_id'];
889 }
890
891 // `notification-template` can be absent for some posts (e.g. advanced-
892 // template notifications or imported/migrated posts), so guard against
893 // passing null to array_values() — that is a fatal TypeError on PHP 8.
894 $template_arr = ( ! empty( $post['notification-template'] ) && is_array( $post['notification-template'] ) )
895 ? array_values( $post['notification-template'] )
896 : [];
897 if ( ! empty( $post['template_adv'] ) ) {
898 $adv_template = isset( $post['advanced_template'] ) ? $post['advanced_template'] : '';
899 $pattern = "/{{(.+?)}}/i";
900 if (preg_match_all($pattern, $adv_template, $matches)) {
901 $template_arr = $matches[1];
902 }
903 }
904 if (is_array($template_arr)) {
905 foreach ($template_arr as $entry_key) {
906 $_entry_key = $entry_key;
907 if ($entry_key == 'tag_siteview' || $entry_key == 'tag_realtime_siteview') {
908 $entry_key = 'views';
909 } elseif ($entry_key == 'ga_title' || $entry_key == 'ga_page_title') {
910 $entry_key = 'title';
911 } elseif (strpos($entry_key, 'tag_product_') === 0) {
912 $entry_key = str_replace('tag_product_', '', $entry_key);
913 } elseif (strpos($entry_key, 'tag_') === 0) {
914 $entry_key = str_replace('tag_', '', $entry_key);
915 } elseif (strpos($entry_key, 'product_') === 0) {
916 $entry_key = str_replace('product_', '', $entry_key);
917 }
918
919 if (isset($entry[$entry_key])) {
920 $_entry[$entry_key] = $entry[$entry_key];
921 }
922 if (isset($entry[$_entry_key])) {
923 $_entry[$_entry_key] = $entry[$_entry_key];
924 }
925 }
926 $entries[$index] = $_entry;
927 }
928 }
929 }
930 return $entries;
931 }
932
933 /**
934 * Remove unnecessary props from post in frontend.
935 *
936 * @return void
937 */
938 public function filtered_post($post, $params = null) {
939 if (is_array($post) && empty($params['inline_shortcode']) && (!defined('NX_DEBUG') || !NX_DEBUG)) {
940 $ignore_props = [
941 'all_locations',
942 'category_list',
943 'combine_multiorder_display',
944 'combine_multiorder_text',
945 'content_trim_length',
946 'convertkit_form',
947 'currentTab',
948 'custom_contents',
949 'custom_ids',
950 'default_avatar',
951 'elementor_edit_link',
952 'enabled',
953 'exclude_categories',
954 'exclude_products',
955 'form_list',
956 'freemius_item_type',
957 'freemius_plugins',
958 'freemius_themes',
959 'give_form_list',
960 'give_forms_control',
961 'image_url',
962 'inline_location',
963 'is_confirmed',
964 'is_elementor',
965 'is_inline',
966 'ld_course_list',
967 'ld_product_control',
968 'activecampaign_form',
969 'brevo_list',
970 'mailchimp_list',
971 'max_stock',
972 'nx-bar_with_elementor',
973 'nx-bar_with_elementor-remove',
974 'nx-bar_with_elementor_install',
975 'order_status',
976 'press_content',
977 'preview',
978 'product_control',
979 'product_exclude_by',
980 'product_list',
981 'rest_route',
982 'show_default_image',
983 'show_notification_image',
984 'show_on',
985 'show_on_display',
986 'source_error',
987 'utm_campaign',
988 'utm_medium',
989 'utm_source',
990 'wp_reviews_product_type',
991 'wp_reviews_slug',
992 'wp_stats_product_type',
993 'wp_stats_slug',
994 '_locale',
995 '__product_list',
996 '__form_list',
997 '__ld_course_list',
998 '__give_form_list',
999 ];
1000 foreach ($ignore_props as $prop) {
1001 if (isset($post[$prop])) {
1002 unset($post[$prop]);
1003 }
1004 }
1005 }
1006 if (isset($post['template_adv'], $post['advanced_template'])) {
1007 $post['advanced_template'] = do_shortcode($post['advanced_template']);
1008 }
1009 if( !empty( $post['notification-template'] ) ) {
1010 $post['notification-template'] = array_map( 'esc_html', $post['notification-template'] );
1011 }
1012 return $post;
1013 }
1014
1015 public function get_bar_content($settings, $suppress_filters = false, $params = []){
1016 $bar_content = PressBar::get_instance()->print_bar_notice($settings);
1017 if(!$suppress_filters){
1018 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
1019 $bar_content = apply_filters("nx_filtered_data_{$settings['source']}", $bar_content, $settings, $params);
1020 }
1021
1022 // checking if content is empty
1023 $_bar_content = str_replace(array("\r\n", "\n", "\r"), '', $bar_content);
1024 $_bar_content = trim(wp_strip_all_tags($_bar_content));
1025 if (empty($_bar_content) && !empty($settings['enable_countdown'])) {
1026 $bar_content = '&nbsp;';
1027 }
1028 return $bar_content;
1029 }
1030
1031 }
1032