PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.1.14
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.1.14
3.1.14 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 All 123 releases
firebox / Inc / Core / Analytics / Ajax / Main.php

Main.php in FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment 3.1.14, at Inc/Core/Analytics/Ajax/Main.php

642 lines 16.7 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.14 Free
5 *
6 * @author FirePlugins <[email protected]>
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\Analytics\Ajax;
13
14 if (!defined('ABSPATH'))
15 {
16 exit; // Exit if accessed directly.
17 }
18
19 class Main
20 {
21 use Shared;
22
23 public function __construct()
24 {
25 add_action('wp_ajax_firebox_analytics_stats', [$this, 'firebox_analytics_stats']);
26
27 add_action('wp_ajax_firebox_analytics_get_campaigns', [$this, 'firebox_analytics_get_campaigns']);
28
29 add_action('wp_ajax_firebox_analytics_get_dropdown_campaigns', [$this, 'firebox_analytics_get_dropdown_campaigns']);
30
31 add_action('wp_ajax_firebox_delete_campaign', [$this, 'firebox_delete_campaign']);
32
33 add_action('wp_ajax_firebox_duplicate_campaign', [$this, 'firebox_duplicate_campaign']);
34
35 add_action('wp_ajax_firebox_analytics_get_charts_data', [$this, 'firebox_analytics_get_charts_data']);
36
37 add_action('wp_ajax_firebox_analytics_get_trending_templates', [$this, 'firebox_analytics_get_trending_templates']);
38 }
39
40 /**
41 * Analytics Counts
42 *
43 * @return void
44 */
45 public function firebox_analytics_stats()
46 {
47 if (!current_user_can('read_fireboxes'))
48 {
49 return;
50 }
51
52 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
53
54 // verify nonce
55 if (!$verify = wp_verify_nonce($nonce, 'fpf_js_nonce'))
56 {
57 return false;
58 }
59
60 $date_period = isset($_POST['date_period']) ? sanitize_text_field(wp_unslash($_POST['date_period'])) : '';
61 $start_date = isset($_POST['start_date']) ? sanitize_text_field(wp_unslash($_POST['start_date'])) : '';
62 $end_date = isset($_POST['end_date']) ? sanitize_text_field(wp_unslash($_POST['end_date'])) : '';
63
64 if (!$start_date || $start_date === 'false' || !$end_date || $end_date === 'false')
65 {
66 return;
67 }
68
69 $selected_campaign = isset($_POST['selected_campaign']) && !empty($_POST['selected_campaign']) ? intval(wp_unslash($_POST['selected_campaign'])) : '';
70
71 $metrics = [
72 'views',
73 'clicks',
74 'conversions',
75 'conversionrate',
76 'revenue'
77 ];
78
79 $filters = [];
80 if ($selected_campaign)
81 {
82 $filters['campaign'] = [
83 'value' => [$selected_campaign]
84 ];
85 }
86
87 $cache_key = 'firebox_analytics_' . md5(serialize([
88 'stats',
89 $metrics,
90 $filters,
91 $start_date,
92 $end_date
93 ]));
94
95 $cache_ttl = $this->getAnalyticsCacheTtl($end_date);
96
97 if ($cache_ttl)
98 {
99 $cached = get_transient($cache_key);
100 if ($cached !== false)
101 {
102 echo wp_json_encode($cached);
103 wp_die();
104 }
105 }
106
107 $data = new \FireBox\Core\Analytics\Data($start_date, $end_date);
108
109 // Use new fluent interface
110 $data->metrics($metrics)->filters($filters);
111
112 // Get current period data
113 $currentData = $data->getData('count');
114
115
116
117 // Calculate previous period data
118 $start_date_ts = strtotime($start_date);
119 $end_date_ts = strtotime($end_date);
120 $days_between = ceil(abs($end_date_ts - $start_date_ts) / 86400);
121
122 $previousPeriodData = [
123 'views' => 0,
124 'clicks' => 0,
125 'conversions' => 0,
126 'conversionrate' => 0,
127 'revenue' => 0,
128 'view_through_revenue' => 0,
129 'conversion_through_revenue' => 0
130 ];
131
132 if ($previousPeriodDates = $this->getPreviousPeriodDates($start_date, $days_between))
133 {
134 $previousData = new \FireBox\Core\Analytics\Data($previousPeriodDates[0], $previousPeriodDates[1]);
135
136 $previousMetrics = [
137 'views',
138 'conversions',
139 'conversionrate',
140
141 ];
142
143 // Use new fluent interface
144 $previousData->metrics($previousMetrics)->filters($filters);
145
146 $previousPeriodData = $previousData->getData('count');
147
148
149 }
150
151 $response = [
152 'current' => $currentData,
153 'previous' => $previousPeriodData
154 ];
155
156 if ($cache_ttl)
157 {
158 set_transient($cache_key, $response, $cache_ttl);
159 }
160
161 echo wp_json_encode($response);
162 wp_die();
163 }
164
165 /**
166 * Analytics Get All Dropdown Campaigns
167 *
168 * @return void
169 */
170 public function firebox_analytics_get_dropdown_campaigns()
171 {
172 if (!current_user_can('read_fireboxes'))
173 {
174 return;
175 }
176
177 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
178
179 // verify nonce
180 if (!$verify = wp_verify_nonce($nonce, 'fpf_js_nonce'))
181 {
182 return false;
183 }
184
185 if (!current_user_can('read_fireboxes'))
186 {
187 echo wp_json_encode([
188 'permission_denied' => true
189 ]);
190 wp_die();
191 }
192
193 $boxes = \FireBox\Core\Helpers\BoxHelper::getAllBoxes(['draft', 'publish']);
194 $boxes = $boxes->posts;
195
196 if (!count($boxes))
197 {
198 echo wp_json_encode([]);
199 wp_die();
200 }
201
202 $data = [
203 [
204 'id' => null,
205 'label' => 'All Campaigns'
206 ]
207 ];
208
209 foreach ($boxes as $box)
210 {
211 $data[] = [
212 'id' => $box->ID,
213 'label' => $box->post_title
214 ];
215 }
216
217 echo wp_json_encode($data);
218 wp_die();
219 }
220
221 /**
222 * Analytics Get Chart Data
223 *
224 * @return void
225 */
226 public function firebox_analytics_get_charts_data()
227 {
228 if (!current_user_can('read_fireboxes'))
229 {
230 return;
231 }
232
233 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
234
235 // verify nonce
236 if (!$verify = wp_verify_nonce($nonce, 'fpf_js_nonce'))
237 {
238 return false;
239 }
240
241 $start_date = isset($_POST['start_date']) ? sanitize_text_field(wp_unslash($_POST['start_date'])) : '';
242 $end_date = isset($_POST['end_date']) ? sanitize_text_field(wp_unslash($_POST['end_date'])) : '';
243
244 if (!$start_date || $start_date === 'false' || !$end_date || $end_date === 'false')
245 {
246 return;
247 }
248
249 $filter = isset($_POST['filter']) ? sanitize_text_field(wp_unslash($_POST['filter'])) : 'list';
250
251 $start_date_ts = strtotime($start_date);
252 $end_date_ts = strtotime($end_date);
253 $days_between = ceil(abs($end_date_ts - $start_date_ts) / 86400);
254
255 // prepare labels
256 $labels = [];
257
258 // We are fetching data for a single day
259 if ($days_between == 1)
260 {
261 for ($hour = 0; $hour < 24; $hour++)
262 {
263 $start_hour = sprintf('%02d', $hour);
264 $labels[] = $start_hour . ':00';
265 }
266 }
267 // Multiple days
268 else if ($days_between > 1)
269 {
270 switch ($filter)
271 {
272 case 'list':
273 default:
274 $tmp_start_date_ts = $start_date_ts;
275 for ($i = 0; $i < $days_between; $i++)
276 {
277 $labels[] = gmdate('Y-m-d', $tmp_start_date_ts);
278 $tmp_start_date_ts = strtotime("+1 day", $tmp_start_date_ts);
279 }
280 break;
281
282 case 'weekly':
283 $startDate = new \DateTime();
284 $startDate->setTimestamp($start_date_ts);
285
286 $endDate = new \DateTime();
287 $endDate->setTimestamp($end_date_ts);
288
289 // Find the nearest Monday to the start date
290 while ($startDate->format('N') != 1)
291 {
292 $startDate->modify('-1 day');
293 }
294
295 while ($startDate <= $endDate)
296 {
297 $labels[] = $startDate->format('d M y');
298 $startDate->modify('+1 week');
299 }
300 break;
301
302 case 'monthly':
303 $startDate = new \DateTime();
304 $startDate->setTimestamp($start_date_ts);
305
306 $endDate = new \DateTime();
307 $endDate->setTimestamp($end_date_ts);
308
309 // Set the start date to the first day of the month
310 $startDate->modify('first day of this month');
311
312 while ($startDate <= $endDate)
313 {
314 $labels[] = $startDate->format('M Y');
315 $startDate->modify('+1 month');
316 }
317 break;
318 }
319 }
320
321 $selected_campaign = isset($_POST['selected_campaign']) && !empty($_POST['selected_campaign']) ? intval(wp_unslash($_POST['selected_campaign'])) : '';
322
323 $metrics = [
324 'views',
325 'clicks',
326 'conversions',
327 'conversionrate',
328 'revenue'
329 ];
330
331 $filters = [];
332 if ($selected_campaign)
333 {
334 $filters = [
335 'campaign' => [
336 'value' => [$selected_campaign]
337 ]
338 ];
339 }
340
341 $cache_key = 'firebox_analytics_' . md5(serialize([
342 'charts',
343 $metrics,
344 $filters,
345 $filter,
346 $start_date,
347 $end_date
348 ]));
349
350 $cache_ttl = $this->getAnalyticsCacheTtl($end_date);
351
352 if ($cache_ttl)
353 {
354 $cached = get_transient($cache_key);
355 if ($cached !== false)
356 {
357 echo wp_json_encode($cached);
358 wp_die();
359 }
360 }
361
362 $data = new \FireBox\Core\Analytics\Data($start_date, $end_date);
363
364 // Use new fluent interface
365 $data->metrics($metrics)->filters($filters);
366
367 $chartData = $data->getData($filter);
368
369 $revenueBreakdownByLabel = [];
370
371
372
373 $response = [
374 'labels' => $labels,
375 'data' => $chartData,
376 'revenue_breakdown' => $revenueBreakdownByLabel
377 ];
378
379 if ($cache_ttl)
380 {
381 set_transient($cache_key, $response, $cache_ttl);
382 }
383
384 echo wp_json_encode($response);
385 wp_die();
386 }
387
388 /**
389 * Analytics Get Latest 5 Campaigns
390 *
391 * @return void
392 */
393 public function firebox_analytics_get_campaigns()
394 {
395 if (!current_user_can('read_fireboxes'))
396 {
397 return;
398 }
399
400 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
401
402 // verify nonce
403 if (!$verify = wp_verify_nonce($nonce, 'fpf_js_nonce'))
404 {
405 return false;
406 }
407
408 if (!current_user_can('read_fireboxes'))
409 {
410 echo wp_json_encode([
411 'permission_denied' => true
412 ]);
413 wp_die();
414 }
415
416 $boxes = \FireBox\Core\Analytics\Helpers\Campaigns::getRecentCampaignsList(5);
417
418 if (!count($boxes->posts))
419 {
420 echo wp_json_encode([]);
421 wp_die();
422 }
423
424 $start_date = isset($_POST['start_date']) ? sanitize_text_field(wp_unslash($_POST['start_date'])) : '';
425 $end_date = isset($_POST['end_date']) ? sanitize_text_field(wp_unslash($_POST['end_date'])) : '';
426
427 $data = [];
428
429 foreach ($boxes->posts as $box)
430 {
431 $meta = \FireBox\Core\Helpers\BoxHelper::getMeta($box->ID);
432
433 $data[] = [
434 'id' => $box->ID,
435 'label' => $box->post_title,
436 'status' => $box->post_status,
437 'trigger' => isset($meta['triggermethod']) ? $meta['triggermethod'] : '',
438 'position' => isset($meta['position']) ? $meta['position'] : '',
439 'totals' => $this->getBoxAnalyticsTotals($box->ID, $start_date, $end_date)
440 ];
441 }
442
443 echo wp_json_encode($data);
444 wp_die();
445 }
446
447 /**
448 * Delete a campaign by ID.
449 *
450 * @return void
451 */
452 public function firebox_delete_campaign()
453 {
454 if (!current_user_can('delete_fireboxes'))
455 {
456 return;
457 }
458
459 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
460
461 // verify nonce
462 if (!$verify = wp_verify_nonce($nonce, 'fpf_js_nonce'))
463 {
464 return false;
465 }
466
467 $id = isset($_POST['id']) ? intval($_POST['id']) : '';
468 if (!$id)
469 {
470 return;
471 }
472
473 /**
474 * "delete_fireboxes" is a global capability, so on its own it would authorise
475 * deleting any post in the site. Confirm the target really is a campaign and
476 * that this user may delete this specific one.
477 */
478 if (!self::userCanActOnCampaign($id, 'delete_post'))
479 {
480 echo wp_json_encode([
481 'error' => true
482 ]);
483 wp_die();
484 }
485
486 $error = false;
487
488 if (!wp_delete_post($id, true))
489 {
490 $error = true;
491 }
492
493 echo wp_json_encode([
494 'error' => $error
495 ]);
496 wp_die();
497 }
498
499 /**
500 * Duplicate a campaign by ID.
501 *
502 * @return void
503 */
504 public function firebox_duplicate_campaign()
505 {
506 if (!current_user_can('edit_fireboxes'))
507 {
508 return;
509 }
510
511 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
512
513 // verify nonce
514 if (!$verify = wp_verify_nonce($nonce, 'fpf_js_nonce'))
515 {
516 return false;
517 }
518
519 $id = isset($_POST['id']) ? intval($_POST['id']) : '';
520 if (!$id)
521 {
522 return;
523 }
524
525 /**
526 * Same reasoning as firebox_delete_campaign(): without this a global capability
527 * would allow duplicating — and therefore reading — any post in the site.
528 */
529 if (!self::userCanActOnCampaign($id, 'edit_post'))
530 {
531 echo wp_json_encode([
532 'error' => true
533 ]);
534 wp_die();
535 }
536
537 $error = false;
538
539 if (!\FireBox\Core\Helpers\BoxHelper::duplicateBox($id))
540 {
541 $error = true;
542 }
543
544 echo wp_json_encode([
545 'error' => $error
546 ]);
547 wp_die();
548 }
549
550 /**
551 * Returns whether the given ID is a campaign the current user may act on.
552 *
553 * @param int $id Post ID.
554 * @param string $capability Meta capability to check, i.e. edit_post/delete_post.
555 *
556 * @return bool
557 */
558 private static function userCanActOnCampaign($id, $capability)
559 {
560 $id = (int) $id;
561
562 if (!$id || get_post_type($id) !== 'firebox')
563 {
564 return false;
565 }
566
567 return current_user_can($capability, $id);
568 }
569
570 /**
571 * Retrieve trending templates.
572 *
573 * @return void
574 */
575 public function firebox_analytics_get_trending_templates()
576 {
577 if (!current_user_can('read_fireboxes'))
578 {
579 return;
580 }
581
582 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
583
584 // verify nonce
585 if (!$verify = wp_verify_nonce($nonce, 'fpf_js_nonce'))
586 {
587 return false;
588 }
589
590 $templates = \FPFramework\Helpers\Templates::getTemplates('firebox');
591
592 if (!isset($templates->templates))
593 {
594 echo wp_json_encode([
595 'templates' => []
596 ]);
597 wp_die();
598 }
599
600 $templates = $templates->templates;
601
602 // Find top trending templates
603 usort($templates, function($a, $b) {
604 return $b->sort->trending - $a->sort->trending;
605 });
606
607 // First top 6 trending templates
608 $templates = array_slice($templates, 0, 6);
609
610 echo wp_json_encode($templates);
611 wp_die();
612 }
613
614 private function getBoxAnalyticsTotals($id = null, $start_date = null, $end_date = null)
615 {
616 if (!$id || !$start_date || !$end_date)
617 {
618 return;
619 }
620
621 $data = new \FireBox\Core\Analytics\Data($start_date, $end_date);
622
623 $metrics = [
624 'views',
625 'clicks',
626 'conversions',
627 'conversionrate'
628 ];
629
630 $filters = [
631 'campaign' => [
632 'value' => [$id]
633 ]
634 ];
635
636 // Use new fluent interface
637 $data->metrics($metrics)->filters($filters);
638
639 return $data->getData('count');
640 }
641 }
642