PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.1.7
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.1.7
3.1.13 3.1.12 3.1.11 3.1.10 3.1.9 3.1.8 3.1.7 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 All 122 releases
firebox / Inc / Core / FB / Box.php

Box.php in FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment 3.1.7, at Inc/Core/FB/Box.php

764 lines 16.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package FireBox
4 * @version 3.1.7 Free
5 *
6 * @author FirePlugins <info@fireplugins.com>
7 * @link https://www.fireplugins.com
8 * @copyright Copyright © 2026 FirePlugins All Rights Reserved
9 * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
10 */
11
12 namespace FireBox\Core\FB;
13
14 if (!defined('ABSPATH'))
15 {
16 exit; // Exit if accessed directly.
17 }
18
19 use FireBox\Core\Helpers\BoxHelper;
20 use FPFramework\Libs\Registry;
21 use FPFramework\Helpers\Fields\DimensionsHelper;
22 use FPFramework\Helpers\CSS;
23
24 class Box
25 {
26 /**
27 * Send useful JS snippet once in first box
28 *
29 * @var boolean
30 */
31 static $loadedLocalizedScript = false;
32
33 /**
34 * The box.
35 *
36 * @param object
37 */
38 private $box = null;
39
40 /**
41 * Factory
42 *
43 * @var Factory
44 */
45 private $factory = null;
46
47 /**
48 * FireBox settings.
49 *
50 * @var object
51 */
52 private $params = null;
53
54 /**
55 * Popup CSS.
56 *
57 * @var CSS
58 */
59 public $css = null;
60
61 /**
62 * Constructor.
63 *
64 * @param object $box
65 * @param object $factory
66 *
67 * @return void
68 */
69 public function __construct($box = null, $factory = null)
70 {
71 if ($box)
72 {
73 $this->box = $this->prepareConstructorBox($box);
74 }
75
76 if (!$factory)
77 {
78 $factory = new \FPFramework\Base\Factory();
79 }
80 $this->factory = $factory;
81
82 $this->params = new Registry(BoxHelper::getParams());
83 }
84
85 /**
86 * Allow to set either a box ID or box object
87 * and we then set the box object.
88 *
89 * @param mixed $box
90 *
91 * @return object
92 */
93 private function prepareConstructorBox($box)
94 {
95 if (!is_object($box))
96 {
97 $box = $this->get($box);
98 }
99
100 return $box;
101 }
102
103 /**
104 * Get a box.
105 *
106 * @param int $id
107 * @param string $status
108 *
109 * @return object
110 */
111 public function get($id = null, $status = null)
112 {
113 if (!$id)
114 {
115 return;
116 }
117
118 $payload = [
119 'where' => [
120 'ID' => ' = ' . intval($id),
121 'post_type' => " = 'firebox'"
122 ]
123 ];
124
125 // apply status if given
126 if ($status)
127 {
128 $payload['where']['post_status'] = ' = \'' . sanitize_key($status) . '\'';
129 }
130
131 if (!$box = firebox()->tables->box->getResults($payload))
132 {
133 return [];
134 }
135
136 if (!isset($box[0]))
137 {
138 return [];
139 }
140
141 $this->box = $box[0];
142
143 // get meta options for box
144 $meta = \FireBox\Core\Helpers\BoxHelper::getMeta($id);
145 $this->box->params = new Registry($meta);
146
147 return $this->box;
148 }
149
150 /**
151 * Renders the box.
152 *
153 * @return void
154 */
155 public function render()
156 {
157 // Check Publishing Assignments
158 if (!$this->pass())
159 {
160 return false;
161 }
162
163 $fbox = $this->box;
164
165 /**
166 * Runs before rendering the box.
167 */
168 $this->box = apply_filters('firebox/box/before_render', $this->box);
169
170 $this->prepare();
171
172 $css = $this->getCustomCSS();
173
174 add_action('wp_enqueue_scripts', function() use ($fbox, $css) {
175 // Loads all media files.
176 $this->loadBoxMedia($fbox);
177
178 // Load CSS
179 if ($css)
180 {
181 wp_add_inline_style('firebox', $css);
182 }
183
184
185 });
186
187 // Allow to manipulate the box before rendering
188 $this->box = apply_filters('firebox/box/edit', $this->box);
189
190 // payload
191 $payload = [
192 'box' => $this->box,
193 'params' => $this->params,
194 ];
195
196 // print campaign HTML
197 add_action('wp_footer', function() use ($payload) {
198 echo $this->getFinalCampaignHTML($payload); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
199 });
200
201 return true;
202 }
203
204 public function renderEmbed()
205 {
206 // Check Publishing Assignments
207 if (!$this->pass())
208 {
209 return false;
210 }
211
212 /**
213 * Runs before rendering the box.
214 */
215 $this->box = apply_filters('firebox/box/before_render', $this->box);
216
217 $this->prepare();
218
219 // Loads all media files.
220 $this->loadBoxMedia($this->box);
221
222 $css = $this->getCustomCSS();
223
224 wp_register_style('fireboxStyle', false);
225 wp_enqueue_style('fireboxStyle');
226
227 // Load CSS
228 if ($css)
229 {
230 wp_add_inline_style('fireboxStyle', $css);
231 }
232
233
234
235 // Allow to manipulate the box before rendering
236 $this->box = apply_filters('firebox/box/edit', $this->box);
237
238 // payload
239 $payload = [
240 'box' => $this->box,
241 'params' => $this->params,
242 ];
243
244 // return box template
245 return $this->getFinalCampaignHTML($payload);
246 }
247
248 public function getFinalCampaignHTML($payload)
249 {
250 $html = firebox()->renderer->public->render('box', $payload, true);
251
252 /**
253 * Runs after rendering the box.
254 */
255 return apply_filters('firebox/box/after_render', $html, $payload['box']);
256 }
257
258 /**
259 * Gets the Custom CSS of the popup.
260 *
261 * @return string
262 */
263 public function getCustomCSS()
264 {
265 return $this->box->params->get('customcss', '');
266 }
267
268 /**
269 * Send a helpful object to JavaScript files
270 *
271 * @return void
272 */
273 public static function setJSObject()
274 {
275 if (self::$loadedLocalizedScript)
276 {
277 return;
278 }
279 self::$loadedLocalizedScript = true;
280
281 $data = [
282 'ajax_url' => admin_url('admin-ajax.php'),
283 'nonce' => wp_create_nonce('fbox_js_nonce'),
284 'site_url' => site_url('/'),
285 'referrer' => isset($_SERVER['HTTP_REFERER']) ? sanitize_url(wp_unslash($_SERVER['HTTP_REFERER'])) : ''
286 ];
287
288 wp_add_inline_script('firebox-main', 'const fbox_js_object = ' . wp_json_encode($data), 'before');
289 }
290
291 /**
292 * Load Box Media
293 *
294 * @return void
295 */
296 public function loadBoxMedia($box)
297 {
298 $box = new Registry($box);
299
300 wp_enqueue_style(
301 'firebox-animations',
302 FBOX_MEDIA_PUBLIC_URL . 'css/vendor/animate.min.css',
303 [],
304 FBOX_VERSION
305 );
306
307 /**
308 * FireBox JS
309 */
310 wp_enqueue_script('firebox-main');
311
312
313
314 // Add Custom Javascript
315 $custom_code = $box->get('params.data.customcode', '');
316 if (is_string($custom_code) && !empty($custom_code))
317 {
318 $custom_code = html_entity_decode(stripslashes($custom_code));
319 wp_add_inline_script('firebox-main', $custom_code, 'after');
320 }
321
322 // run above the main JS script to run only once
323 self::setJSObject();
324
325 /**
326 * FireBox CSS
327 */
328 wp_enqueue_style(
329 'firebox',
330 FBOX_MEDIA_PUBLIC_URL . 'css/firebox.css',
331 [],
332 FBOX_VERSION
333 );
334
335 /**
336 * Page Slide mode JS
337 */
338 if ($box->get('params.data.mode') == 'pageslide')
339 {
340 wp_enqueue_script(
341 'firebox-pageslide-mode',
342 FBOX_MEDIA_PUBLIC_URL . 'js/pageslide_mode.js',
343 ['firebox-main'],
344 FBOX_VERSION,
345 true
346 );
347 }
348
349
350 }
351
352 /**
353 * Prepares the box before rendering
354 *
355 * @return void
356 */
357 public function prepare()
358 {
359 remove_filter('the_content', 'wptexturize');
360
361 $cParam = BoxHelper::getParams();
362 $cParam = new Registry($cParam);
363
364 $this->box->post_content = apply_filters('the_content', $this->box->post_content);
365
366 $mode = $this->box->params->get('mode');
367
368 /* Classes */
369 $css_class = [
370 $this->box->ID,
371 $mode
372 ];
373
374 if (in_array($mode, ['popup', 'stickybar', 'sidebar', 'floating', 'slide-in']))
375 {
376 $position = $this->box->params->get('position', '');
377 $position = !is_string($position) ? '' : $position;
378 if ($position)
379 {
380 $css_class[] = $position;
381 }
382 }
383 else if ($mode === 'fullscreen')
384 {
385 if ($center_content = $this->box->params->get('center_content', false)) {
386 $css_class[] = 'center-content';
387 }
388 }
389
390 self::prefixCSSClasses($css_class);
391
392 // Class suffix
393 $classSuffix = $this->box->params->get('classsuffix', '');
394 $classSuffix = is_string($classSuffix) ? $classSuffix : '';
395
396 $css_class[] = $classSuffix;
397
398 $this->box->classes = $css_class;
399
400 // Dialog CSS Classes
401 $dialog_css_classes = [
402 // Add Box shadow
403 $this->box->params->get('boxshadow') ? 'shdelevation' : null
404 ];
405
406 // Align Content
407 $aligncontent = is_string($this->box->params->get('aligncontent')) ? explode(' ', $this->box->params->get('aligncontent')) : [];
408 $dialog_css_classes = array_merge($dialog_css_classes, $aligncontent);
409
410 self::prefixCSSClasses($dialog_css_classes);
411 $this->box->dialog_classes = $dialog_css_classes;
412
413 $trigger_point_methods = [
414 'pageload' => 'onPageLoad',
415 'onclick' => 'onClick',
416 'elementHover' => 'onHover',
417 'ondemand' => 'onDemand',
418
419 ];
420
421 /* Other Settings */
422 $this->box->params->set('animation_duration', $this->box->params->get('animation_duration', 0.2));
423
424 $scroll_amount = $this->box->params->get('scroll_amount', '80%');
425
426 // Parse scroll_amount to extract unit and value
427 $scroll_amount_data = $this->parseScrollAmount($scroll_amount);
428
429 $delay = in_array($this->box->params->get('triggermethod'), ['floatingbutton', 'onexternallink']) ? 0 : (int) $this->box->params->get('triggerdelay') * 1000;
430
431 $trigger_method = (is_string($this->box->params->get('triggermethod'))) && array_key_exists($this->box->params->get('triggermethod'), $trigger_point_methods) ? $trigger_point_methods[$this->box->params->get('triggermethod')] : $this->box->params->get('triggermethod');
432
433 $trigger_element = is_scalar($this->box->params->get('triggerelement', '')) ? $this->box->params->get('triggerelement', '') : '';
434
435 // Use Namespaced classes for each trigger point and let them manipulate the settings dynamicaly.
436 $this->box->settings = [
437 'name' => $this->box->post_title,
438 'trigger' => $trigger_method,
439 'trigger_selector' => $trigger_method === 'onExternalLink' ? '' : rtrim($trigger_element, ','),
440 'delay' => $delay,
441
442 'close_on_esc' => (bool) $this->box->params->get('close_on_esc', false),
443 'animation_open' => $this->box->params->get('animationin'),
444 'animation_close' => $this->box->params->get('animationout'),
445 'animation_duration' => (float) $this->box->params->get('animation_duration') * 1000,
446 'prevent_default' => true,
447 'backdrop' => (bool) $this->box->params->get('overlay'),
448 'backdrop_color' => $this->box->params->get('overlay_color'),
449 'backdrop_click' => (bool) $this->box->params->get('overlayclick'),
450 'disable_page_scroll' => (bool) $this->box->params->get('preventpagescroll'),
451 'test_mode' => (bool) $this->box->params->get('testmode'),
452 'debug' => (bool) $cParam->get('debug', false),
453 'auto_focus' => (bool) $this->box->params->get('autofocus', false),
454 'mode' => $this->box->params->get('mode')
455 ];
456
457 $this->css = new Styling\CSS($this->box);
458
459 // Apply Popup CSS
460 $this->box->params->set('customcss', $this->box->params->get('customcss') . $this->css->getCSS());
461
462 $this->replaceBoxSmartTags();
463
464 add_filter('the_content', 'wptexturize');
465 }
466
467 /**
468 * Parses scroll_amount to extract unit and value
469 *
470 * @param mixed $scroll_amount
471 *
472 * @return array
473 */
474 private function parseScrollAmount($scroll_amount)
475 {
476 if (is_array($scroll_amount))
477 {
478 return [
479 'unit' => $scroll_amount['unit'] ?? '%',
480 'value' => $scroll_amount['value'] ?? 80
481 ];
482 }
483
484 if (is_string($scroll_amount) && preg_match('/^(\d+)(px|%)$/', $scroll_amount, $matches))
485 {
486 return [
487 'unit' => $matches[2],
488 'value' => $matches[1]
489 ];
490 }
491
492 return [
493 'unit' => '%',
494 'value' => 80
495 ];
496 }
497
498 /**
499 * Replaces all box smart tags
500 *
501 * @return object
502 */
503 public function replaceBoxSmartTags()
504 {
505 $tags = new \FPFramework\Base\SmartTags\SmartTags();
506
507 // register FB Smart Tags
508 $tags->register('\FireBox\Core\SmartTags', FBOX_BASE_FOLDER . '/Inc/Core/SmartTags', $this->box);
509
510 $this->box = $tags->replace($this->box);
511 }
512
513 /**
514 * Checks if a box passes assignments
515 *
516 * @return boolean
517 */
518 public function pass()
519 {
520 if (!$this->box || !is_object($this->box))
521 {
522 return false;
523 }
524
525 // Check first local assignments
526 if (!$this->passLocalAssignments())
527 {
528 return false;
529 }
530
531 $displayConditionsType = $this->box->params->get('display_conditions_type', '');
532
533 // If empty, display popup sitewide
534 if (empty($displayConditionsType) || $displayConditionsType === 'all')
535 {
536 return true;
537 }
538
539 // Mirror Display Conditions of another popup.
540 if ($displayConditionsType == 'mirror' && $mirror_box_id = $this->box->params->get('mirror_box'))
541 {
542 $this->box->params->merge(self::getAssignmentsForMirroring($mirror_box_id));
543 }
544
545 // Get a recursive array of all rules
546 $rules = $this->box->params->get('rules', []);
547 $rules = is_string($rules) ? json_decode($rules, true) : json_decode(wp_json_encode($rules), true);
548
549 // If testmode is enabled disable the User Groups condition
550 if ($this->box->params->get('testmode'))
551 {
552 foreach ($rules as $key => &$group)
553 {
554 foreach ($group['rules'] as $_key => &$rule)
555 {
556 if (!isset($rule['name']) || empty($rule['name']))
557 {
558 continue;
559 }
560
561 if ($rule['name'] === 'WP\UserGroup')
562 {
563 unset($group['rules'][$_key]);
564 }
565 }
566 }
567 }
568
569 // Check framework based conditions
570 return \FPFramework\Base\Conditions\ConditionBuilder::pass($rules, $this->factory);
571 }
572
573 /**
574 * Check if a box passes local conditions
575 *
576 * @return boolean
577 */
578 private function passLocalAssignments()
579 {
580 $localAssignments = new \FireBox\Core\FB\Assignments($this, $this->factory);
581 return $localAssignments->passAll();
582 }
583
584 /**
585 * Gets assignments of mirrored box
586 *
587 * @param int $box_id
588 *
589 * @return object
590 */
591 private function getAssignmentsForMirroring($box_id)
592 {
593 $payload = [
594 'where' => [
595 'ID' => ' = ' . intval($box_id),
596 'post_status' => " = 'publish'",
597 'post_type' => " = 'firebox'"
598 ]
599 ];
600
601 // Load box
602 if (!$box = firebox()->tables->box->getResults($payload))
603 {
604 return;
605 }
606
607 $box = $box[0];
608
609 // get meta options for box
610 $meta = BoxHelper::getMeta($box->ID);
611 $box->params = new Registry($meta);
612
613 return new Registry(['rules' => $box->params->get('rules')]);
614 }
615
616 /**
617 * Prefixes the CSS classes
618 *
619 * @param array $classes
620 * @param string $prefix
621 *
622 * @return void
623 */
624 private static function prefixCSSClasses(&$classes, $prefix = 'fb-')
625 {
626 $classes = array_filter($classes);
627
628 if (empty($classes))
629 {
630 return;
631 }
632
633 foreach ($classes as &$class)
634 {
635 $class = $prefix . $class;
636 }
637 }
638
639 /**
640 * Track box open
641 *
642 * @param integer $box_id
643 * @param string $page
644 * @param string $referrer
645 *
646 * @return void
647 */
648 public function logOpenEvent($box_id, $page = null, $referrer = null)
649 {
650 $box = $this->get($box_id);
651
652 // Do not track if statistics option is disabled
653 $track_open_event = (bool) (is_null($box->params->get('stats', null)) ? true : $box->params->get('stats'));
654 if (!$track_open_event)
655 {
656 return;
657 }
658
659 return firebox()->log->track($box_id, 1, null, $page, $referrer);
660 }
661
662 /**
663 * Track box close
664 *
665 * @param integer $box_id
666 * @param integer $box_log_id
667 *
668 * @return void
669 */
670 public function logCloseEvent($box_id, $box_log_id)
671 {
672 $box = $this->get($box_id);
673
674 // Do not track if statistics option is disabled
675 $track_open_event = (bool) (is_null($box->params->get('stats', null)) ? true : $box->params->get('stats'));
676 if (!$track_open_event)
677 {
678 return null;
679 }
680
681 firebox()->log->track($box_id, 2, $box_log_id);
682 }
683
684 /**
685 * Get total box impressions
686 *
687 * @param array $payload
688 *
689 * @return array
690 */
691 public function getTotalImpressions($payload)
692 {
693 $impressions = firebox()->tables->boxlog->getResults($payload);
694
695 return count($impressions);
696 }
697
698 /**
699 * Returns the cookie instance.
700 *
701 * @return mixed
702 */
703 public function getCookie()
704 {
705 if (!$this->box)
706 {
707 return;
708 }
709
710 return new Cookie($this->box);
711 }
712
713 /**
714 * Returns the box.
715 *
716 * @return object
717 */
718 public function getBox()
719 {
720 return $this->box;
721 }
722
723 /**
724 * Sets the box.
725 *
726 * @param object $box
727 *
728 * @return Box
729 */
730 public function setBox($box)
731 {
732 $this->box = $box;
733
734 return $this;
735 }
736
737 public function getParams()
738 {
739 return $this->params;
740 }
741
742 public function setParams($params)
743 {
744 $this->params = $params;
745
746 return $this;
747 }
748
749 public function getCampaignParams()
750 {
751 return isset($this->box->params) ? $this->box->params : null;
752 }
753
754 public function setCampaignParams($params)
755 {
756 if (isset($this->box->params))
757 {
758 $this->box->params = $params;
759 }
760
761 return $this;
762 }
763 }
764