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

721 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 1.1.0 Free
5 *
6 * @author FirePlugins <info@fireplugins.com>
7 * @link https://www.fireplugins.com
8 * @copyright Copyright © 2022 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;
161 }
162
163 $fbox = $this->box;
164
165 $this->prepare();
166
167 $css = $this->getCustomCSS();
168
169 add_action('wp_enqueue_scripts', function() use ($fbox, $css) {
170 // Loads all media files.
171 $this->loadBoxMedia($fbox);
172
173 // Load CSS
174 if ($css)
175 {
176 wp_add_inline_style('firebox', $css);
177 }
178 });
179
180 /**
181 * Runs before rendering the box.
182 */
183 do_action('firebox/box/before_render', $this->box);
184
185 // payload
186 $payload = [
187 'box' => $this->box,
188 'params' => $this->params,
189 ];
190
191 // return box template
192 add_action('wp_footer', function() use ($payload) {
193 echo firebox()->renderer->public->render('box', $payload, true);
194 });
195 }
196
197 /**
198 * Gets the Custom CSS of the popup.
199 *
200 * @return string
201 */
202 private function getCustomCSS()
203 {
204 return $this->box->params->get('customcss', '');
205 }
206
207 /**
208 * Send a helpful object to JavaScript files
209 *
210 * @return void
211 */
212 public static function setJSObject()
213 {
214 if (self::$loadedLocalizedScript)
215 {
216 return;
217 }
218 self::$loadedLocalizedScript = true;
219
220 $data = array(
221 'ajax_url' => admin_url('admin-ajax.php'),
222 'nonce' => wp_create_nonce('fbox_js_nonce'),
223 'site_url' => site_url('/'),
224 'referrer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''
225 );
226
227 wp_localize_script('firebox', 'fbox_js_object', $data);
228 }
229
230 /**
231 * Load Box Media
232 *
233 * @return void
234 */
235 public function loadBoxMedia($box)
236 {
237 $box = new Registry($box);
238
239 // Add polyfills for Internet Explorer
240 $browser = $this->factory->getBrowser();
241 if ($browser && is_array($browser) && array_key_exists('name', $browser) && $browser['name'] === 'ie')
242 {
243 wp_enqueue_script(
244 'firebox-ie11-polyfill',
245 'https://polyfill.io/v3/polyfill.min.js?features=NodeList.prototype.forEach%2CElement.prototype.closest%2CArray.prototype.forEach%2CArray.prototype.find%2CIntersectionObserver%2CIntersectionObserverEntry',
246 [],
247 FBOX_VERSION,
248 true
249 );
250 }
251
252 /**
253 * Velocity
254 */
255 if ($this->params->get('loadVelocity', true))
256 {
257 wp_enqueue_script(
258 'firebox-velocity',
259 FBOX_MEDIA_PUBLIC_URL . 'js/vendor/velocity.js',
260 [],
261 FBOX_VERSION,
262 true
263 );
264 wp_enqueue_script(
265 'firebox-velocity-ui',
266 FBOX_MEDIA_PUBLIC_URL . 'js/vendor/velocity.ui.js',
267 [],
268 FBOX_VERSION,
269 true
270 );
271
272 /**
273 * Animations
274 */
275 if (strpos($box->get('params.data.animationin'), 'firebox') !== false || strpos($box->get('params.data.animationout'), 'firebox') !== false)
276 {
277 wp_enqueue_script(
278 'firebox-animations',
279 FBOX_MEDIA_PUBLIC_URL . 'js/animations.js',
280 [],
281 FBOX_VERSION,
282 true
283 );
284 }
285 }
286
287 /**
288 * FireBox JS
289 */
290 wp_enqueue_script(
291 'firebox',
292 FBOX_MEDIA_PUBLIC_URL . 'js/firebox.js',
293 [],
294 FBOX_VERSION,
295 true
296 );
297
298 // run above the main JS script to run only once
299 self::setJSObject();
300
301 /**
302 * FireBox CSS
303 */
304 if ($this->params->get('loadCSS', true))
305 {
306 wp_enqueue_style(
307 'firebox',
308 FBOX_MEDIA_PUBLIC_URL . 'css/firebox.css',
309 [],
310 FBOX_VERSION
311 );
312 }
313
314 /**
315 * Page Slide mode JS
316 */
317 if ($box->get('params.data.mode') == 'pageslide')
318 {
319 wp_enqueue_script(
320 'firebox-pageslide-mode',
321 FBOX_MEDIA_PUBLIC_URL . 'js/pageslide_mode.js',
322 [],
323 FBOX_VERSION,
324 true
325 );
326 }
327
328
329
330 $this->loadThemeCSSOverrides();
331 }
332
333 /**
334 * Some themes require overrides to preserve as much as we can the styling of the popups.
335 *
336 * @return void
337 */
338 private function loadThemeCSSOverrides()
339 {
340 $active_theme = wp_get_theme();
341 $theme = $active_theme->template;
342
343 /**
344 * This is the listed of the themes that we have created overrides
345 */
346 $themes = [
347 'twentytwentyone'
348 ];
349
350 if (!in_array($theme, $themes))
351 {
352 return;
353 }
354
355 wp_enqueue_style(
356 'firebox-theme-' . $theme . '-override',
357 FBOX_MEDIA_PUBLIC_URL . 'css/themes/' . $theme . '.css',
358 [],
359 FBOX_VERSION
360 );
361 }
362
363 /**
364 * Prepares the box before rendering
365 *
366 * @return void
367 */
368 public function prepare()
369 {
370 $this->css = new Styling\CSS($this->box);
371
372 global $post;
373 $original_post = $post;
374
375 $GLOBALS['post'] = $this->box;
376 setup_postdata($GLOBALS['post']);
377
378 $cParam = BoxHelper::getParams();
379 $cParam = new Registry($cParam);
380
381 $this->box->post_content = apply_filters('the_content', $this->box->post_content);
382
383 $position = $this->box->params->get('position', '');
384 $position = !is_string($position) ? '' : $position;
385
386 /* Classes */
387 $css_class = [
388 $this->box->ID,
389 $position
390 ];
391
392 $rtl = $this->box->params->get('rtl', '0');
393 if ($rtl == '1')
394 {
395 $css_class[] = 'rtl';
396 }
397
398 self::prefixCSSClasses($css_class);
399
400 // Class suffix
401 $classSuffix = $this->box->params->get('classsuffix', '');
402 $classSuffix = is_string($classSuffix) ? $classSuffix : '';
403
404 $css_class[] = $classSuffix;
405
406 $this->box->classes = $css_class;
407
408 // Box shadow
409 $boxshadow = (is_string($this->box->params->get('boxshadow', '1')) || is_int($this->box->params->get('boxshadow', '1'))) ? $this->box->params->get('boxshadow', '1') : '0';
410
411 $dialog_css_classes = [
412 $boxshadow != '0' ? 'shd' . $boxshadow : null
413 ];
414
415 // Align Content
416 $aligncontent = is_string($this->box->params->get('aligncontent')) ? explode(' ', $this->box->params->get('aligncontent')) : [];
417 $dialog_css_classes = array_merge($dialog_css_classes, $aligncontent);
418
419 self::prefixCSSClasses($dialog_css_classes);
420 $this->box->dialog_classes = $dialog_css_classes;
421
422 $trigger_point_methods = [
423 'pageheight' => 'onScrollDepth',
424 'element' => 'onElementVisibility',
425 'pageready' => 'onPageReady',
426 'pageload' => 'onPageLoad',
427 'userleave' => 'onExit',
428 'onclick' => 'onClick',
429 'elementHover' => 'onHover',
430 'ondemand' => 'onDemand'
431 ];
432
433 /* Other Settings */
434 $scroll_depth = $this->box->params->get('scroll_depth', 'percentage');
435 $scroll_depth = is_string($scroll_depth) ? $scroll_depth : '';
436
437 $animation_duration = $this->box->params->get('duration') ? (float) $this->box->params->get('duration') : 0;
438
439 // Use Namespaced classes for each trigger point and let them manipulate the settings dynamicaly.
440 $this->box->settings = [
441 'trigger' => (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'),
442 'trigger_selector' => $this->box->params->get('triggerelement'),
443 'delay' => (int) $this->box->params->get('triggerdelay') * 1000,
444 'scroll_depth' => $scroll_depth,
445 'scroll_depth_value' => $scroll_depth == 'percentage' ? (int) $this->box->params->get('triggerpercentage') : (int) $this->box->params->get('scroll_pixel'),
446 'firing_frequency' => (int) $this->box->params->get('firing_frequency', 1),
447 'reverse_scroll_close' => (bool) $this->box->params->get('autohide'),
448 'threshold' => (float) $this->box->params->get('threshold', 0) / 100,
449 'close_out_viewport' => (bool) $this->box->params->get('close_out_viewport', false),
450 'exit_timer' => (int) $this->box->params->get('exittimer') * 1000,
451 'idle_time' => (int) $this->box->params->get('idle_time') * 1000,
452 'animation_open' => $this->box->params->get('animationin'),
453 'animation_close' => $this->box->params->get('animationout'),
454 'animation_duration' => (float) $animation_duration * 1000,
455 'prevent_default' => (bool) $this->box->params->get('preventdefault', true),
456 'backdrop' => (bool) $this->box->params->get('overlay'),
457 'backdrop_color' => $this->box->params->get('overlay_color'),
458 'backdrop_click' => (bool) $this->box->params->get('overlayclick'),
459 'disable_page_scroll' => (bool) $this->box->params->get('preventpagescroll'),
460 'test_mode' => (bool) $this->box->params->get('testmode'),
461 'debug' => (bool) $cParam->get('debug', false),
462 'ga_tracking' => (bool) $cParam->get('gaTrack', 0),
463 'ga_tracking_id' => $cParam->get('gaID', 0),
464 'ga_tracking_label' => $cParam->get('gaCategory'),
465 'auto_focus' => (bool) $this->box->params->get('autofocus', false)
466 ];
467
468 // Apply Popup CSS
469 $this->box->params->set('customcss', $this->box->params->get('customcss') . $this->css->getCSS());
470
471 $this->replaceBoxSmartTags();
472
473 $GLOBALS['post'] = $original_post;
474 wp_reset_postdata();
475 }
476
477 /**
478 * Replaces all box smart tags
479 *
480 * @return object
481 */
482 public function replaceBoxSmartTags()
483 {
484 $tags = new \FPFramework\Base\SmartTags\SmartTags();
485
486 // register FB Smart Tags
487 $tags->register('\FireBox\Core\SmartTags', FBOX_BASE_FOLDER . '/Inc/Core/SmartTags', $this->box);
488
489 $this->box = $tags->replace($this->box);
490 }
491
492 /**
493 * Checks if a box passes assignments
494 *
495 * @return boolean
496 */
497 public function pass()
498 {
499 if (!$this->box || !is_object($this->box))
500 {
501 return false;
502 }
503
504 // Prepare boxes that mirror other boxes assignments
505 if ($this->box->params->get('mirror', false) && $mirror_box_id = $this->box->params->get('mirror_box'))
506 {
507 $this->box->params->merge($this->getAssignmentsForMirroring($mirror_box_id));
508 }
509
510 // Check first local assignments
511 if (!$this->passLocalAssignments())
512 {
513 return false;
514 }
515
516 // If testmode is enabled disable the User Groups assignment
517 if ($this->box->params->get('testmode'))
518 {
519 $this->box->params->set('assignments.assign_grouplevel.selection', '0');
520 }
521
522 $globalAssignments = $this->passGlobalAssignments();
523
524 // Check framework based assignments
525 return $globalAssignments;
526 }
527
528 /**
529 * Passes framework based global assignments
530 *
531 * @return boolean
532 */
533 private function passGlobalAssignments()
534 {
535 $assignments = new \FPFramework\Base\Assignments($this->factory);
536 $pass = $assignments->passAll($this->box, $this->box->params->get('assignmentMatchingMethod', 'and'));
537 return $pass;
538 }
539
540 /**
541 * Check if a box passes local assignments
542 *
543 * @return boolean
544 */
545 private function passLocalAssignments()
546 {
547 $localAssignments = new \FireBox\Core\FB\Assignments($this, $this->factory);
548 return $localAssignments->passAll();
549 }
550
551 /**
552 * Gets assignments of mirrored box
553 *
554 * @param int $box_id
555 *
556 * @return object
557 */
558 private function getAssignmentsForMirroring($box_id)
559 {
560 $payload = [
561 'where' => [
562 'ID' => ' = ' . intval($box_id),
563 'post_status' => " = 'publish'",
564 'post_type' => " = 'firebox'"
565 ]
566 ];
567
568 // Load box
569 if (!$box = firebox()->tables->box->getResults($payload))
570 {
571 return;
572 }
573
574 $box = $box[0];
575
576 // get meta options for box
577 $meta = get_post_meta($box_id, 'fpframework_meta_settings', true);
578 $box->params = $meta;
579
580 // To prevent user frustration, we ignore the following assignments because they are not displayed in the Publishing Assignments.
581 $params_to_ignore = [
582 'assign_impressions'
583 ];
584
585 $assignments = [];
586
587 // Gather params to merge
588 foreach ($box->params as $param_key => $param_value)
589 {
590 if (strpos($param_key, 'assign') === false || in_array($param_key, $params_to_ignore))
591 {
592 continue;
593 }
594
595 $assignments[$param_key] = $param_value;
596 }
597
598 return new Registry($assignments);
599 }
600
601 /**
602 * Prefixes the CSS classes
603 *
604 * @param array $classes
605 * @param string $prefix
606 *
607 * @return void
608 */
609 private static function prefixCSSClasses(&$classes, $prefix = 'fb-')
610 {
611 $classes = array_filter($classes);
612
613 if (empty($classes))
614 {
615 return;
616 }
617
618 foreach ($classes as &$class)
619 {
620 $class = $prefix . $class;
621 }
622 }
623
624 /**
625 * Track box open
626 *
627 * @param integer $box_id
628 * @param string $page
629 * @param string $referrer
630 *
631 * @return void
632 */
633 public function logOpenEvent($box_id, $page = null, $referrer = null)
634 {
635 $box = $this->get($box_id);
636
637 // Do not track if statistics option is disabled
638 $track_open_event = (bool) (is_null($box->params->get('stats', null)) ? BoxHelper::getParams()->get('stats', 1) : $box->params->get('stats'));
639 if (!$track_open_event)
640 {
641 return;
642 }
643
644 return firebox()->log->track($box_id, 1, null, $page, $referrer);
645 }
646
647 /**
648 * Track box close
649 *
650 * @param integer $box_id
651 * @param integer $box_log_id
652 *
653 * @return void
654 */
655 public function logCloseEvent($box_id, $box_log_id)
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)) ? BoxHelper::getParams()->get('stats', 1) : $box->params->get('stats'));
661 if (!$track_open_event)
662 {
663 return null;
664 }
665
666 firebox()->log->track($box_id, 2, $box_log_id);
667 }
668
669 /**
670 * Get total box impressions
671 *
672 * @param array $payload
673 *
674 * @return array
675 */
676 public function getTotalImpressions($payload)
677 {
678 $impressions = firebox()->tables->boxlog->getResults($payload);
679
680 return count($impressions);
681 }
682
683 /**
684 * Returns the cookie instance.
685 *
686 * @return mixed
687 */
688 public function getCookie()
689 {
690 if (!$this->box)
691 {
692 return;
693 }
694
695 return new Cookie($this->box->id);
696 }
697
698 /**
699 * Returns the box.
700 *
701 * @return object
702 */
703 public function getBox()
704 {
705 return $this->box;
706 }
707
708 /**
709 * Sets the box.
710 *
711 * @param object $box
712 *
713 * @return Box
714 */
715 public function setBox($box)
716 {
717 $this->box = $box;
718
719 return $this;
720 }
721 }