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 / API / Routes / Open / Campaigns.php

Campaigns.php in FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment 3.1.14, at Inc/Core/API/Routes/Open/Campaigns.php

466 lines 11.3 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
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\API\Routes\Open;
13
14 if (!defined('ABSPATH'))
15 {
16 exit; // Exit if accessed directly.
17 }
18
19 use \FireBox\Core\API\EndpointController;
20 use FireBox\Core\FB\CampaignsQuery;
21 use FireBox\Core\FB\Cookie;
22 use FireBox\Core\Helpers\BoxHelper;
23 use WP_REST_Server;
24 use WP_Error;
25
26 /**
27 * REST endpoints backing the DataViews Campaigns admin page.
28 */
29 class Campaigns extends EndpointController
30 {
31 const ALLOWED_STATUSES = ['all', 'mine', 'publish', 'draft', 'trash'];
32 const ALLOWED_ORDERBY = ['id', 'title', 'views', 'conversions', 'conversionrate', 'date'];
33
34 /**
35 * Endpoint name
36 *
37 * @return string
38 */
39 public function get_name()
40 {
41 return '';
42 }
43
44 /**
45 * Register routes
46 *
47 * @return void
48 */
49 public function register()
50 {
51 $this->register_route('/campaigns/list', WP_REST_Server::READABLE, [$this, 'get_list'], [], [$this, 'read_permission_callback']);
52 $this->register_route('/campaigns/status', WP_REST_Server::CREATABLE, [$this, 'update_status'], [], [$this, 'edit_permission_callback']);
53 $this->register_route('/campaigns/trash', WP_REST_Server::CREATABLE, [$this, 'trash'], [], [$this, 'edit_permission_callback']);
54 $this->register_route('/campaigns/restore', WP_REST_Server::CREATABLE, [$this, 'restore'], [], [$this, 'edit_permission_callback']);
55 $this->register_route('/campaigns/delete', WP_REST_Server::CREATABLE, [$this, 'delete'], [], [$this, 'edit_permission_callback']);
56 $this->register_route('/campaigns/duplicate', WP_REST_Server::CREATABLE, [$this, 'duplicate'], [], [$this, 'edit_permission_callback']);
57 $this->register_route('/campaigns/export', WP_REST_Server::CREATABLE, [$this, 'export'], [], [$this, 'edit_permission_callback']);
58 $this->register_route('/campaigns/reset-stats', WP_REST_Server::CREATABLE, [$this, 'reset_stats'], [], [$this, 'edit_permission_callback']);
59 $this->register_route('/campaigns/clear-cookie', WP_REST_Server::CREATABLE, [$this, 'clear_cookie'], [], [$this, 'edit_permission_callback']);
60 }
61
62 public function read_permission_callback($request)
63 {
64 return current_user_can('read_fireboxes');
65 }
66
67 public function edit_permission_callback($request)
68 {
69 return current_user_can('edit_fireboxes');
70 }
71
72 /**
73 * GET /campaigns/list
74 */
75 public function get_list($request)
76 {
77 $page = max(1, (int) $request->get_param('page'));
78 $per_page = (int) $request->get_param('per_page');
79 $per_page = $per_page > 0 ? min(100, $per_page) : 30;
80
81 $search = sanitize_text_field((string) $request->get_param('search'));
82
83 $status = sanitize_key((string) $request->get_param('status'));
84 if (!in_array($status, self::ALLOWED_STATUSES, true))
85 {
86 $status = 'all';
87 }
88
89 $orderby = sanitize_key((string) $request->get_param('orderby'));
90 if (!in_array($orderby, self::ALLOWED_ORDERBY, true))
91 {
92 $orderby = 'id';
93 }
94
95 $order = strtolower(sanitize_key((string) $request->get_param('order')));
96 if (!in_array($order, ['asc', 'desc'], true))
97 {
98 $order = 'desc';
99 }
100
101 $query = new CampaignsQuery();
102
103 $result = $query->getItems([
104 'status' => $status,
105 'search' => $search,
106 'orderby' => $orderby,
107 'order' => $order,
108 'page' => $page,
109 'per_page' => $per_page,
110 ]);
111
112 return [
113 'items' => array_map([$this, 'prepareItemForResponse'], $result['items']),
114 'total' => $result['found_posts'],
115 'total_pages' => $per_page > 0 ? (int) ceil($result['found_posts'] / $per_page) : 1,
116 'status_counts' => $query->getStatusCounts(),
117 ];
118 }
119
120 /**
121 * POST /campaigns/status
122 */
123 public function update_status($request)
124 {
125 $ids = $this->sanitizeIds($request->get_param('ids'));
126 $status = sanitize_key((string) $request->get_param('status'));
127
128 if (!$ids)
129 {
130 return new WP_Error('firebox_missing_ids', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
131 }
132
133 if (!in_array($status, ['publish', 'draft'], true))
134 {
135 return new WP_Error('firebox_invalid_status', firebox()->_('FB_INVALID_STATUS'), ['status' => 400]);
136 }
137
138 if (is_wp_error($error = $this->validateFireboxIds($ids)))
139 {
140 return $error;
141 }
142
143 if (is_wp_error($error = $this->checkCapabilityForIds($ids, 'edit_post')))
144 {
145 return $error;
146 }
147
148 foreach ($ids as $id)
149 {
150 wp_update_post([
151 'ID' => $id,
152 'post_status' => $status,
153 ]);
154 }
155
156 return ['success' => true];
157 }
158
159 /**
160 * POST /campaigns/trash
161 */
162 public function trash($request)
163 {
164 $ids = $this->sanitizeIds($request->get_param('ids'));
165
166 if (!$ids)
167 {
168 return new WP_Error('firebox_missing_ids', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
169 }
170
171 if (is_wp_error($error = $this->validateFireboxIds($ids)))
172 {
173 return $error;
174 }
175
176 if (is_wp_error($error = $this->checkCapabilityForIds($ids, 'delete_post')))
177 {
178 return $error;
179 }
180
181 foreach ($ids as $id)
182 {
183 wp_trash_post($id);
184 }
185
186 return ['success' => true];
187 }
188
189 /**
190 * POST /campaigns/restore
191 */
192 public function restore($request)
193 {
194 $ids = $this->sanitizeIds($request->get_param('ids'));
195
196 if (!$ids)
197 {
198 return new WP_Error('firebox_missing_ids', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
199 }
200
201 if (is_wp_error($error = $this->validateFireboxIds($ids)))
202 {
203 return $error;
204 }
205
206 if (is_wp_error($error = $this->checkCapabilityForIds($ids, 'delete_post')))
207 {
208 return $error;
209 }
210
211 foreach ($ids as $id)
212 {
213 wp_untrash_post($id);
214 }
215
216 return ['success' => true];
217 }
218
219 /**
220 * POST /campaigns/delete
221 */
222 public function delete($request)
223 {
224 $ids = $this->sanitizeIds($request->get_param('ids'));
225
226 if (!$ids)
227 {
228 return new WP_Error('firebox_missing_ids', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
229 }
230
231 if (is_wp_error($error = $this->validateFireboxIds($ids)))
232 {
233 return $error;
234 }
235
236 if (is_wp_error($error = $this->checkCapabilityForIds($ids, 'delete_post')))
237 {
238 return $error;
239 }
240
241 foreach ($ids as $id)
242 {
243 wp_delete_post($id, true);
244 }
245
246 return ['success' => true];
247 }
248
249 /**
250 * POST /campaigns/duplicate
251 */
252 public function duplicate($request)
253 {
254 $id = absint($request->get_param('id'));
255
256 if (!$id)
257 {
258 return new WP_Error('firebox_missing_id', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
259 }
260
261 if (is_wp_error($error = $this->validateFireboxIds([$id])))
262 {
263 return $error;
264 }
265
266 if (is_wp_error($error = $this->checkCapabilityForIds([$id], 'edit_post')))
267 {
268 return $error;
269 }
270
271 if (!$new_id = BoxHelper::duplicateBox($id))
272 {
273 return new WP_Error('firebox_duplicate_failed', firebox()->_('FB_CAMPAIGN_DUPLICATION_FAILED'), ['status' => 500]);
274 }
275
276 $query = new CampaignsQuery();
277
278 $item = [
279 'ID' => $new_id,
280 'label' => get_the_title($new_id),
281 'post_type' => 'firebox',
282 'post_status' => get_post_status($new_id),
283 'date' => get_post_field('post_modified_gmt', $new_id),
284 'analytics' => $query->getCampaignAnalytics($new_id),
285 ];
286
287 return [
288 'success' => true,
289 'item' => $this->prepareItemForResponse($item),
290 ];
291 }
292
293 /**
294 * POST /campaigns/export
295 */
296 public function export($request)
297 {
298 $ids = $this->sanitizeIds($request->get_param('ids'));
299
300 if (!$ids)
301 {
302 return new WP_Error('firebox_missing_ids', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
303 }
304
305 if (is_wp_error($error = $this->validateFireboxIds($ids)))
306 {
307 return $error;
308 }
309
310 if (!$payload = BoxHelper::getExportPayload($ids))
311 {
312 return new WP_Error('firebox_export_failed', firebox()->_('FB_CAMPAIGN_EXPORT_FAILED'), ['status' => 500]);
313 }
314
315 return [
316 'filename' => $payload['filename'] . '.fbox',
317 'data' => $payload['exported'],
318 ];
319 }
320
321 /**
322 * POST /campaigns/reset-stats
323 */
324 public function reset_stats($request)
325 {
326 $ids = $this->sanitizeIds($request->get_param('ids'));
327
328 if (!$ids)
329 {
330 return new WP_Error('firebox_missing_ids', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
331 }
332
333 if (is_wp_error($error = $this->validateFireboxIds($ids)))
334 {
335 return $error;
336 }
337
338 if (is_wp_error($error = $this->checkCapabilityForIds($ids, 'edit_post')))
339 {
340 return $error;
341 }
342
343 BoxHelper::resetBoxStats($ids);
344
345 return ['success' => true];
346 }
347
348 /**
349 * POST /campaigns/clear-cookie
350 */
351 public function clear_cookie($request)
352 {
353 $id = absint($request->get_param('id'));
354
355 if (!$id)
356 {
357 return new WP_Error('firebox_missing_id', firebox()->_('FB_NO_CAMPAIGNS_SELECTED'), ['status' => 400]);
358 }
359
360 if (is_wp_error($error = $this->validateFireboxIds([$id])))
361 {
362 return $error;
363 }
364
365 if (is_wp_error($error = $this->checkCapabilityForIds([$id], 'edit_post')))
366 {
367 return $error;
368 }
369
370 if ($box = firebox()->box->get($id))
371 {
372 (new Cookie($box))->remove();
373 }
374
375 return ['success' => true];
376 }
377
378 /**
379 * Formats an internal item (as returned by CampaignsQuery) into the public REST shape.
380 *
381 * @param array $item
382 *
383 * @return array
384 */
385 private function prepareItemForResponse(array $item)
386 {
387 $id = (int) $item['ID'];
388 $analytics = $item['analytics'];
389
390 $preview_url = null;
391 if (in_array($item['post_status'], ['pending', 'draft', 'future'], true) && current_user_can('edit_post', $id))
392 {
393 $preview_url = get_preview_post_link($id);
394 }
395
396 $box = firebox()->box->get($id);
397
398 return [
399 'id' => $id,
400 'title' => sanitize_text_field($item['label']),
401 'status' => $item['post_status'],
402 'date' => $item['date'],
403 'views' => isset($analytics['views']) ? (int) $analytics['views'] : 0,
404 'conversions' => isset($analytics['conversions']) ? (int) $analytics['conversions'] : 0,
405 'conversion_rate' => !empty($analytics['conversionrate']) ? (float) $analytics['conversionrate'] : null,
406 'edit_url' => esc_url_raw(get_edit_post_link($id, 'raw')),
407 'preview_url' => $preview_url ? esc_url_raw($preview_url) : null,
408 'analytics_url' => esc_url_raw(admin_url('admin.php?page=firebox-analytics&campaign=' . $id)),
409 'can_clear_cookie' => $box ? (bool) (new Cookie($box))->exist() : false,
410 ];
411 }
412
413 /**
414 * Sanitizes a raw `ids` request param into a de-duplicated array of positive ints.
415 *
416 * @param mixed $value
417 *
418 * @return int[]
419 */
420 private function sanitizeIds($value)
421 {
422 return array_values(array_unique(array_filter(array_map('absint', (array) $value))));
423 }
424
425 /**
426 * Ensures every id is an existing `firebox` post, guarding against wrong-CPT abuse.
427 *
428 * @param int[] $ids
429 *
430 * @return true|WP_Error
431 */
432 private function validateFireboxIds(array $ids)
433 {
434 foreach ($ids as $id)
435 {
436 if (get_post_type($id) !== 'firebox')
437 {
438 return new WP_Error('firebox_invalid_id', sprintf(firebox()->_('FB_INVALID_CAMPAIGN_ID'), $id), ['status' => 400]);
439 }
440 }
441
442 return true;
443 }
444
445 /**
446 * Ensures the current user has the given per-post capability for every id.
447 *
448 * @param int[] $ids
449 * @param string $capability 'edit_post'|'delete_post'
450 *
451 * @return true|WP_Error
452 */
453 private function checkCapabilityForIds(array $ids, $capability)
454 {
455 foreach ($ids as $id)
456 {
457 if (!current_user_can($capability, $id))
458 {
459 return new WP_Error('firebox_forbidden', firebox()->_('FB_CAMPAIGNS_ACTION_FORBIDDEN'), ['status' => 403]);
460 }
461 }
462
463 return true;
464 }
465 }
466