PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.0.1
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.0.1
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.0.1, at Inc/Core/FB/Box.php

770 lines 16.6 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.0.1 Free
5 *
6 * @author FirePlugins <info@fireplugins.com>
7 * @link https://www.fireplugins.com
8 * @copyright Copyright © 2025 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' => ' = ' . esc_sql(intval($id)),
121 'post_type' => " = 'firebox'"
122 ]
123 ];
124
125 // apply status if given
126 if ($status)
127 {
128 $payload['where']['post_status'] = ' = \'' . esc_sql($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(
311 'firebox-main',
312 FBOX_MEDIA_PUBLIC_URL . 'js/firebox.js',
313 [],
314 FBOX_VERSION,
315 true
316 );
317
318 // Add Custom Javascript
319 $custom_code = $box->get('params.data.customcode', '');
320 if (is_string($custom_code) && !empty($custom_code))
321 {
322 $custom_code = html_entity_decode(stripslashes($custom_code));
323 wp_add_inline_script('firebox-main', $custom_code, 'after');
324 }
325
326 // run above the main JS script to run only once
327 self::setJSObject();
328
329 /**
330 * FireBox CSS
331 */
332 if ($this->params->get('loadCSS', true))
333 {
334 wp_enqueue_style(
335 'firebox',
336 FBOX_MEDIA_PUBLIC_URL . 'css/firebox.css',
337 [],
338 FBOX_VERSION
339 );
340 }
341
342 /**
343 * Page Slide mode JS
344 */
345 if ($box->get('params.data.mode') == 'pageslide')
346 {
347 wp_enqueue_script(
348 'firebox-pageslide-mode',
349 FBOX_MEDIA_PUBLIC_URL . 'js/pageslide_mode.js',
350 ['firebox-main'],
351 FBOX_VERSION,
352 true
353 );
354 }
355
356
357 }
358
359 /**
360 * Prepares the box before rendering
361 *
362 * @return void
363 */
364 public function prepare()
365 {
366 remove_filter('the_content', 'wptexturize');
367
368 $cParam = BoxHelper::getParams();
369 $cParam = new Registry($cParam);
370
371 $this->box->post_content = apply_filters('the_content', $this->box->post_content);
372
373 $mode = $this->box->params->get('mode');
374
375 /* Classes */
376 $css_class = [
377 $this->box->ID,
378 $mode
379 ];
380
381 if (in_array($mode, ['popup', 'stickybar', 'sidebar', 'floating', 'slide-in']))
382 {
383 $position = $this->box->params->get('position', '');
384 $position = !is_string($position) ? '' : $position;
385 if ($position)
386 {
387 $css_class[] = $position;
388 }
389 }
390 else if ($mode === 'fullscreen')
391 {
392 if ($center_content = $this->box->params->get('center_content', false)) {
393 $css_class[] = 'center-content';
394 }
395 }
396
397 self::prefixCSSClasses($css_class);
398
399 // Class suffix
400 $classSuffix = $this->box->params->get('classsuffix', '');
401 $classSuffix = is_string($classSuffix) ? $classSuffix : '';
402
403 $css_class[] = $classSuffix;
404
405 $this->box->classes = $css_class;
406
407 // Dialog CSS Classes
408 $dialog_css_classes = [
409 // Add Box shadow
410 $this->box->params->get('boxshadow') ? 'shdelevation' : null
411 ];
412
413 // Align Content
414 $aligncontent = is_string($this->box->params->get('aligncontent')) ? explode(' ', $this->box->params->get('aligncontent')) : [];
415 $dialog_css_classes = array_merge($dialog_css_classes, $aligncontent);
416
417 self::prefixCSSClasses($dialog_css_classes);
418 $this->box->dialog_classes = $dialog_css_classes;
419
420 $trigger_point_methods = [
421 'pageload' => 'onPageLoad',
422 'onclick' => 'onClick',
423 'elementHover' => 'onHover',
424 'ondemand' => 'onDemand',
425
426 ];
427
428 /* Other Settings */
429 $this->box->params->set('animation_duration', $this->box->params->get('animation_duration', 0.2));
430
431 $scroll_amount = $this->box->params->get('scroll_amount', '80%');
432
433 // Parse scroll_amount to extract unit and value
434 $scroll_amount_data = $this->parseScrollAmount($scroll_amount);
435
436 $delay = in_array($this->box->params->get('triggermethod'), ['floatingbutton', 'onexternallink']) ? 0 : (int) $this->box->params->get('triggerdelay') * 1000;
437
438 $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');
439
440 $trigger_element = is_scalar($this->box->params->get('triggerelement', '')) ? $this->box->params->get('triggerelement', '') : '';
441
442 // Use Namespaced classes for each trigger point and let them manipulate the settings dynamicaly.
443 $this->box->settings = [
444 'name' => $this->box->post_title,
445 'trigger' => $trigger_method,
446 'trigger_selector' => $trigger_method === 'onExternalLink' ? '' : rtrim($trigger_element, ','),
447 'delay' => $delay,
448
449 'close_on_esc' => (bool) $this->box->params->get('close_on_esc', false),
450 'animation_open' => $this->box->params->get('animationin'),
451 'animation_close' => $this->box->params->get('animationout'),
452 'animation_duration' => (float) $this->box->params->get('animation_duration') * 1000,
453 'prevent_default' => true,
454 'backdrop' => (bool) $this->box->params->get('overlay'),
455 'backdrop_color' => $this->box->params->get('overlay_color'),
456 'backdrop_click' => (bool) $this->box->params->get('overlayclick'),
457 'disable_page_scroll' => (bool) $this->box->params->get('preventpagescroll'),
458 'test_mode' => (bool) $this->box->params->get('testmode'),
459 'debug' => (bool) $cParam->get('debug', false),
460 'auto_focus' => (bool) $this->box->params->get('autofocus', false),
461 'mode' => $this->box->params->get('mode')
462 ];
463
464 $this->css = new Styling\CSS($this->box);
465
466 // Apply Popup CSS
467 $this->box->params->set('customcss', $this->box->params->get('customcss') . $this->css->getCSS());
468
469 $this->replaceBoxSmartTags();
470
471 add_filter('the_content', 'wptexturize');
472 }
473
474 /**
475 * Parses scroll_amount to extract unit and value
476 *
477 * @param mixed $scroll_amount
478 *
479 * @return array
480 */
481 private function parseScrollAmount($scroll_amount)
482 {
483 if (is_array($scroll_amount))
484 {
485 return [
486 'unit' => $scroll_amount['unit'] ?? '%',
487 'value' => $scroll_amount['value'] ?? 80
488 ];
489 }
490
491 if (is_string($scroll_amount) && preg_match('/^(\d+)(px|%)$/', $scroll_amount, $matches))
492 {
493 return [
494 'unit' => $matches[2],
495 'value' => $matches[1]
496 ];
497 }
498
499 return [
500 'unit' => '%',
501 'value' => 80
502 ];
503 }
504
505 /**
506 * Replaces all box smart tags
507 *
508 * @return object
509 */
510 public function replaceBoxSmartTags()
511 {
512 $tags = new \FPFramework\Base\SmartTags\SmartTags();
513
514 // register FB Smart Tags
515 $tags->register('\FireBox\Core\SmartTags', FBOX_BASE_FOLDER . '/Inc/Core/SmartTags', $this->box);
516
517 $this->box = $tags->replace($this->box);
518 }
519
520 /**
521 * Checks if a box passes assignments
522 *
523 * @return boolean
524 */
525 public function pass()
526 {
527 if (!$this->box || !is_object($this->box))
528 {
529 return false;
530 }
531
532 // Check first local assignments
533 if (!$this->passLocalAssignments())
534 {
535 return false;
536 }
537
538 $displayConditionsType = $this->box->params->get('display_conditions_type', '');
539
540 // If empty, display popup sitewide
541 if (empty($displayConditionsType) || $displayConditionsType === 'all')
542 {
543 return true;
544 }
545
546 // Mirror Display Conditions of another popup.
547 if ($displayConditionsType == 'mirror' && $mirror_box_id = $this->box->params->get('mirror_box'))
548 {
549 $this->box->params->merge(self::getAssignmentsForMirroring($mirror_box_id));
550 }
551
552 // Get a recursive array of all rules
553 $rules = $this->box->params->get('rules', []);
554 $rules = is_string($rules) ? json_decode($rules, true) : json_decode(wp_json_encode($rules), true);
555
556 // If testmode is enabled disable the User Groups condition
557 if ($this->box->params->get('testmode'))
558 {
559 foreach ($rules as $key => &$group)
560 {
561 foreach ($group['rules'] as $_key => &$rule)
562 {
563 if (!isset($rule['name']) || empty($rule['name']))
564 {
565 continue;
566 }
567
568 if ($rule['name'] === 'WP\UserGroup')
569 {
570 unset($group['rules'][$_key]);
571 }
572 }
573 }
574 }
575
576 // Check framework based conditions
577 return \FPFramework\Base\Conditions\ConditionBuilder::pass($rules, $this->factory);
578 }
579
580 /**
581 * Check if a box passes local conditions
582 *
583 * @return boolean
584 */
585 private function passLocalAssignments()
586 {
587 $localAssignments = new \FireBox\Core\FB\Assignments($this, $this->factory);
588 return $localAssignments->passAll();
589 }
590
591 /**
592 * Gets assignments of mirrored box
593 *
594 * @param int $box_id
595 *
596 * @return object
597 */
598 private function getAssignmentsForMirroring($box_id)
599 {
600 $payload = [
601 'where' => [
602 'ID' => ' = ' . intval($box_id),
603 'post_status' => " = 'publish'",
604 'post_type' => " = 'firebox'"
605 ]
606 ];
607
608 // Load box
609 if (!$box = firebox()->tables->box->getResults($payload))
610 {
611 return;
612 }
613
614 $box = $box[0];
615
616 // get meta options for box
617 $meta = BoxHelper::getMeta($box->ID);
618 $box->params = new Registry($meta);
619
620 return new Registry(['rules' => $box->params->get('rules')]);
621 }
622
623 /**
624 * Prefixes the CSS classes
625 *
626 * @param array $classes
627 * @param string $prefix
628 *
629 * @return void
630 */
631 private static function prefixCSSClasses(&$classes, $prefix = 'fb-')
632 {
633 $classes = array_filter($classes);
634
635 if (empty($classes))
636 {
637 return;
638 }
639
640 foreach ($classes as &$class)
641 {
642 $class = $prefix . $class;
643 }
644 }
645
646 /**
647 * Track box open
648 *
649 * @param integer $box_id
650 * @param string $page
651 * @param string $referrer
652 *
653 * @return void
654 */
655 public function logOpenEvent($box_id, $page = null, $referrer = null)
656 {
657 $box = $this->get($box_id);
658
659 // Do not track if statistics option is disabled
660 $track_open_event = (bool) (is_null($box->params->get('stats', null)) ? true : $box->params->get('stats'));
661 if (!$track_open_event)
662 {
663 return;
664 }
665
666 return firebox()->log->track($box_id, 1, null, $page, $referrer);
667 }
668
669 /**
670 * Track box close
671 *
672 * @param integer $box_id
673 * @param integer $box_log_id
674 *
675 * @return void
676 */
677 public function logCloseEvent($box_id, $box_log_id)
678 {
679 $box = $this->get($box_id);
680
681 // Do not track if statistics option is disabled
682 $track_open_event = (bool) (is_null($box->params->get('stats', null)) ? true : $box->params->get('stats'));
683 if (!$track_open_event)
684 {
685 return null;
686 }
687
688 firebox()->log->track($box_id, 2, $box_log_id);
689 }
690
691 /**
692 * Get total box impressions
693 *
694 * @param array $payload
695 *
696 * @return array
697 */
698 public function getTotalImpressions($payload)
699 {
700 $impressions = firebox()->tables->boxlog->getResults($payload);
701
702 return count($impressions);
703 }
704
705 /**
706 * Returns the cookie instance.
707 *
708 * @return mixed
709 */
710 public function getCookie()
711 {
712 if (!$this->box)
713 {
714 return;
715 }
716
717 return new Cookie($this->box);
718 }
719
720 /**
721 * Returns the box.
722 *
723 * @return object
724 */
725 public function getBox()
726 {
727 return $this->box;
728 }
729
730 /**
731 * Sets the box.
732 *
733 * @param object $box
734 *
735 * @return Box
736 */
737 public function setBox($box)
738 {
739 $this->box = $box;
740
741 return $this;
742 }
743
744 public function getParams()
745 {
746 return $this->params;
747 }
748
749 public function setParams($params)
750 {
751 $this->params = $params;
752
753 return $this;
754 }
755
756 public function getCampaignParams()
757 {
758 return isset($this->box->params) ? $this->box->params : null;
759 }
760
761 public function setCampaignParams($params)
762 {
763 if (isset($this->box->params))
764 {
765 $this->box->params = $params;
766 }
767
768 return $this;
769 }
770 }