PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / trunk
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment vtrunk
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 / CampaignsList.php

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

545 lines 15.2 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.13 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 // Load WP_List_Table if not loaded
20 if (!class_exists('WP_List_Table'))
21 {
22 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
23 }
24
25 use FireBox\Core\Helpers\BoxHelper;
26
27 class CampaignsList extends \WP_List_Table
28 {
29 private $per_page = 30;
30
31 private $total_campaigns_data = [];
32
33 function __construct()
34 {
35 $this->total_campaigns_data = (array) wp_count_posts('firebox', 'readable');
36
37 parent::__construct([
38 'singular' => 'firebox',
39 'plural' => 'fireboxes',
40 'ajax' => false
41 ]);
42 }
43
44 /**
45 * Gets a list of CSS classes for the WP_List_Table table tag.
46 *
47 * @since 3.1.0
48 *
49 * @return string[] Array of CSS classes for the table tag.
50 */
51 protected function get_table_classes()
52 {
53 $mode = get_user_setting('posts_list_mode', 'list');
54
55 $mode_class = esc_attr('table-view-' . $mode);
56
57 return ['widefat', 'striped', $mode_class, $this->_args['plural']];
58 }
59
60 protected function get_primary_column_name()
61 {
62 return 'title';
63 }
64
65 function get_columns()
66 {
67 return array(
68 'cb' => '<input type="checkbox" />',
69 'status' => 'Status',
70 'title' => 'Title',
71 'views' => 'Views',
72 'conversions' => 'Conversions',
73 'conversionrate' => 'Conversion Rate',
74 'id' => 'ID'
75 );
76 }
77
78 function get_sortable_columns()
79 {
80 $sortable_columns = [
81 'id' => [ 'id', false ],
82 'title' => [ 'title', false ],
83 'views' => [ 'views', false ],
84 'conversions' => [ 'conversions', false ],
85 'conversionrate' => [ 'conversionrate', false ],
86 ];
87
88 return $sortable_columns;
89 }
90
91 /**
92 * Renders a checkbox.
93 *
94 * @param object $item
95 *
96 * @return string
97 */
98 public function column_cb($item)
99 {
100 return sprintf('<input type="checkbox" name="id[]" value="%s" />', esc_attr($item['ID']));
101 }
102
103 public function column_status($item)
104 {
105 $payload = [
106 'input_class' => ['fpf-toggle-post-status', 'size-small'],
107 'name' => 'fb_toggle_post_' . $item['ID'],
108 'extra_atts' => [
109 'data-post-id' => $item['ID']
110 ],
111 'value' => get_post_status($item['ID']) == 'publish' ? 1 : 0
112 ];
113
114 echo \FPFramework\Helpers\HTML::renderFPToggle($payload); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
115 }
116
117 public function column_views($item)
118 {
119 return isset($item['analytics']['views']) ? number_format($item['analytics']['views']) : '';
120 }
121
122 public function column_conversions($item)
123 {
124 return isset($item['analytics']['conversions']) ? number_format($item['analytics']['conversions']) : '';
125 }
126
127 public function column_conversionrate($item)
128 {
129 return isset($item['analytics']['conversionrate']) && $item['analytics']['conversionrate'] ? number_format($item['analytics']['conversionrate'], 1) . '%' : 'n/a';
130 }
131
132 /**
133 * Column "title" output.
134 *
135 * @param object $item
136 *
137 * @return void
138 */
139 public function column_title($item)
140 {
141 $url = admin_url('post.php?post=' . $item['ID'] . '&action=edit');
142
143 return '<a href="' . $url . '">' . $item['label'] . '</a>';
144 }
145
146 /**
147 * Processes the bulk actions.
148 *
149 * @return void
150 */
151 public function process_bulk_action()
152 {
153 // Ensure we have an action
154 if (!$action = $this->current_action())
155 {
156 return;
157 }
158
159 // Ensure its a valid action
160 $allowed_actions = $this->get_bulk_actions();
161 if (!array_key_exists($action, $allowed_actions))
162 {
163 return;
164 }
165
166 // Ensure we have IDs
167 $ids = isset($_GET['id']) ? array_map('intval', $_GET['id']) : [];
168 if (!$ids)
169 {
170 return;
171 }
172
173 // Get nonce
174 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
175 if (!$nonce)
176 {
177 return;
178 }
179
180 // Verify nonce
181 $nonce_action = 'bulk-' . $this->_args['plural'];
182 if (!wp_verify_nonce($nonce, $nonce_action))
183 {
184 return;
185 }
186
187 switch ($action)
188 {
189 case 'publish':
190 $this->publishPosts($ids);
191 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_PUBLISHED'), count($ids)));
192 break;
193
194 case 'unpublish':
195 $this->unpublishPosts($ids);
196 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_UNPUBLISHED'), count($ids)));
197 break;
198
199 case 'delete':
200 $this->deletePosts($ids);
201 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_DELETED'), count($ids)));
202 break;
203
204 case 'reset_stats':
205 BoxHelper::resetBoxStats($ids);
206 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_RESET'), count($ids)));
207 break;
208 }
209 }
210
211 private function publishPosts($ids = [])
212 {
213 if (!$ids)
214 {
215 return;
216 }
217
218 foreach ($ids as $id)
219 {
220 wp_update_post([
221 'ID' => $id,
222 'post_status' => 'publish'
223 ]);
224 }
225 }
226
227 private function unpublishPosts($ids = [])
228 {
229 if (!$ids)
230 {
231 return;
232 }
233
234 foreach ($ids as $id)
235 {
236 wp_update_post([
237 'ID' => $id,
238 'post_status' => 'draft'
239 ]);
240 }
241 }
242
243 private function deletePosts($ids = [])
244 {
245 if (!$ids)
246 {
247 return;
248 }
249
250 foreach ($ids as $id)
251 {
252 wp_delete_post($id);
253 }
254 }
255
256 protected function handle_row_actions($item, $column_name, $primary)
257 {
258 if ($primary !== $column_name)
259 {
260 return '';
261 }
262
263 // Restores the more descriptive, specific name for use within this method.
264 $post = $item;
265 $post_type_object = get_post_type_object( $post['post_type'] );
266 $can_edit_post = current_user_can( 'edit_post', $post['ID'] );
267 $actions = array();
268 $title = _draft_or_post_title();
269
270 if ( $can_edit_post && 'trash' !== $post['post_status'] ) {
271 $actions['edit'] = sprintf(
272 '<a href="%s" aria-label="%s">%s</a>',
273 get_edit_post_link( $post['ID'] ),
274 /* translators: %s: Post title. */
275 esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;', 'firebox' ), $title ) ),
276 __( 'Edit', 'firebox' )
277 );
278 }
279
280 if ( current_user_can( 'delete_post', $post['ID'] ) ) {
281 if ( 'trash' === $post['post_status'] ) {
282 $actions['untrash'] = sprintf(
283 '<a href="%s" aria-label="%s">%s</a>',
284 wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post['ID'] ) ), 'untrash-post_' . $post['ID'] ),
285 /* translators: %s: Post title. */
286 esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash', 'firebox' ), $title ) ),
287 __( 'Restore', 'firebox' )
288 );
289 } elseif ( EMPTY_TRASH_DAYS ) {
290 $actions['trash'] = sprintf(
291 '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
292 get_delete_post_link( $post['ID'] ),
293 /* translators: %s: Post title. */
294 esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash', 'firebox' ), $title ) ),
295 _x( 'Trash', 'verb', 'firebox' )
296 );
297 }
298
299 if ( 'trash' === $post['post_status'] || ! EMPTY_TRASH_DAYS ) {
300 $actions['delete'] = sprintf(
301 '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
302 get_delete_post_link( $post['ID'], '', true ),
303 /* translators: %s: Post title. */
304 esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently', 'firebox' ), $title ) ),
305 __( 'Delete Permanently', 'firebox' )
306 );
307 }
308 }
309
310 if ( is_post_type_viewable( $post_type_object ) ) {
311 if ( in_array( $post['post_status'], array( 'pending', 'draft', 'future' ), true ) ) {
312 if ( $can_edit_post ) {
313 $preview_link = get_preview_post_link( $post['ID'] );
314 $actions['view'] = sprintf(
315 '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
316 esc_url( $preview_link ),
317 /* translators: %s: Post title. */
318 esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;', 'firebox' ), $title ) ),
319 __( 'Preview', 'firebox' )
320 );
321 }
322 }
323 }
324
325 // Add 'Duplicate' action
326 $actions['duplicate'] = '<a href="admin.php?action=fb_duplicate_post_as_draft&post=' . $post['ID'] . '&_wpnonce=' . wp_create_nonce('duplicate-firebox-campaign') . '" title="' . firebox()->_('FB_DUPLICATE_CAMPAIGN') . '" rel="permalink">' . fpframework()->_('FPF_DUPLICATE') . '</a>';
327
328 // Analytics
329 $actions['analytics'] = '<a href="admin.php?page=firebox-analytics&campaign=' . $post['ID'] . '" title="' . firebox()->_('FB_VIEW_ANALYTICS_OF_CAMPAIGN') . '" rel="permalink">' . fpframework()->_('FPF_ANALYTICS') . '</a>';
330
331 /**
332 * Check if cookie has been set
333 */
334 if ((new \FireBox\Core\FB\Cookie(firebox()->box->get($post['ID'])))->exist())
335 {
336 $actions['clear_cookie'] = '<a class="firebox_red_text_color" href="admin.php?action=fb_clear_cookie&post=' . $post['ID'] . '&_wpnonce=' . wp_create_nonce('clearcookie-firebox-campaign') . '" title="' . firebox()->_('FB_CLEAR_COOKIE') . '" rel="permalink">' . firebox()->_('FB_HIDDEN_BY_COOKIE') . '</a>';
337 }
338
339 return $this->row_actions( $actions );
340 }
341
342 /**
343 * Returns the views.
344 *
345 * @return array
346 */
347 public function get_views()
348 {
349 $current = $this->getCampaignStatus();
350 $base_url = admin_url('admin.php?page=firebox-campaigns');
351
352 // Base URL
353 $remove = ['status', 'paged', '_wpnonce'];
354 $url = remove_query_arg($remove, $base_url);
355
356 $count = '&nbsp;<span class="count">(%d)</span>';
357
358 $published = (int) $this->total_campaigns_data['publish'];
359 $drafts = (int) $this->total_campaigns_data['draft'];
360 $total_items = $published + $drafts;
361
362 // All
363 $all_class = in_array($current, ['', 'all'], true) ? ' class="current"' : '';
364 $all_count = sprintf($count, esc_attr($total_items));
365 $all_label = fpframework()->_('FPF_ALL') . $all_count;
366
367 // Mine
368 $m_class = in_array($current, ['mine'], true) ? ' class="current"' : '';
369 $m_count = sprintf($count, esc_attr($this->getMineCount()));
370 $m_label = fpframework()->_('FPF_MINE') . $m_count;
371
372 // Published
373 $p_class = in_array($current, ['published'], true) ? ' class="current"' : '';
374 $p_count = sprintf($count, esc_attr($published));
375 $p_label = fpframework()->_('FPF_PUBLISHED') . $p_count;
376
377 // Drafts
378 $d_class = in_array($current, ['drafts'], true) ? ' class="current"' : '';
379 $d_count = sprintf($count, esc_attr($drafts));
380 $d_label = fpframework()->_('FPF_DRAFTS') . $d_count;
381
382 $views = [
383 'all' => sprintf('<a href="%s"%s>%s</a>', esc_url($url), $all_class, $all_label),
384 'mine' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'mine', $base_url)), $m_class, $m_label),
385 'published' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'published', $base_url)), $p_class, $p_label),
386 'drafts' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'drafts', $base_url)), $d_class, $d_label),
387 ];
388
389 if ($this->total_campaigns_data['trash'])
390 {
391 $t_class = in_array($current, ['trash'], true) ? ' class="current"' : '';
392 $t_count = sprintf($count, esc_attr((int) $this->total_campaigns_data['trash']));
393 $t_label = fpframework()->_('FPF_TRASH') . $t_count;
394
395 $views['trash'] = sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'trash', $base_url)), $t_class, $t_label);
396 }
397
398 $views['import'] = '<a href="admin.php?page=firebox-import">' . fpframework()->_('FPF_IMPORT') . '</a>';
399
400 return $views;
401 }
402
403 private function getMineCount()
404 {
405 return (new CampaignsQuery())->getMineCount();
406 }
407
408 protected function get_bulk_actions()
409 {
410 return [
411 'publish' => __( 'Publish', 'firebox' ),
412 'unpublish' => __( 'Unpublish', 'firebox' ),
413 'delete' => __( 'Delete', 'firebox' ),
414 'fb_export' => __( 'Export', 'firebox' ),
415 'reset_stats' => __( 'Reset Statistics', 'firebox' ),
416 ];
417 }
418
419 /**
420 * Column "id" output.
421 *
422 * @param object $item
423 *
424 * @return void
425 */
426 public function column_id($item)
427 {
428 $url = admin_url('post.php?post=' . $item['ID'] . '&action=edit');
429
430 return '<a href="' . $url . '">' . $item['ID'] . '</a>';
431 }
432
433 private function getCampaignStatus()
434 {
435 return isset($_GET['status']) ? sanitize_key($_GET['status']) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
436 }
437
438 function prepare_items()
439 {
440 $status = $this->mapLegacyStatus($this->getCampaignStatus());
441 $per_page = $this->per_page;
442 $current_page = $this->get_pagenum();
443
444 $hidden = [];
445 $columns = $this->get_columns();
446 $sortable = $this->get_sortable_columns();
447
448 $this->_column_headers = [$columns, $hidden, $sortable];
449
450 $search = isset($_GET['s']) ? sanitize_text_field(wp_unslash($_GET['s'])) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
451 $order = isset($_GET['order']) ? sanitize_key($_GET['order']) : 'desc'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
452 $orderby = isset($_GET['orderby']) ? sanitize_key($_GET['orderby']) : 'id'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
453
454 $result = (new CampaignsQuery())->getItems([
455 'status' => $status,
456 'search' => $search,
457 'orderby' => $orderby,
458 'order' => $order,
459 'page' => $current_page,
460 'per_page' => $per_page,
461 ]);
462
463 $data = $result['items'];
464
465 $total_items = $result['found_posts'];
466
467 if (!$total_items && $this->get_pagenum() !== 1)
468 {
469 if (isset($_REQUEST['post_status']) && in_array($_REQUEST['post_status'], get_post_stati(), true)) //phpcs:ignore WordPress.Security.NonceVerification.Recommended
470 {
471 $total_items = $this->total_campaigns_data[sanitize_key(wp_unslash($_REQUEST['post_status']))]; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
472 }
473 elseif (isset($_GET['author']) && get_current_user_id() === (int) $_GET['author']) //phpcs:ignore WordPress.Security.NonceVerification.Recommended
474 {
475 $total_items = $this->getMineCount();
476 }
477 else
478 {
479 $total_items = array_sum($this->total_campaigns_data);
480
481 // Subtract post types that are not included in the admin all list.
482 foreach (get_post_stati(['show_in_admin_all_list' => false]) as $state)
483 {
484 $total_items -= $this->total_campaigns_data[$state];
485 }
486 }
487 }
488
489 $this->set_pagination_args([
490 'total_items' => $total_items,
491 'per_page' => $per_page
492 ]);
493
494 $this->items = $data;
495 }
496
497 /**
498 * Maps the legacy `status` query-arg values used by this list table's
499 * URLs (published/drafts) to the CampaignsQuery status enum (publish/draft).
500 *
501 * @param string $status
502 *
503 * @return string
504 */
505 private function mapLegacyStatus($status)
506 {
507 $map = [
508 '' => 'all',
509 'all' => 'all',
510 'mine' => 'mine',
511 'published' => 'publish',
512 'drafts' => 'draft',
513 'trash' => 'trash',
514 ];
515
516 return $map[$status] ?? 'all';
517 }
518
519 private function getTotalItems()
520 {
521 $status = $this->getCampaignStatus();
522 $total = 0;
523
524 switch ($status)
525 {
526 case 'mine':
527 $total = $this->getMineCount();
528 break;
529 case 'published':
530 $total = $this->total_campaigns_data['publish'];
531 break;
532 case 'drafts':
533 $total = $this->total_campaigns_data['draft'];
534 break;
535 case 'trash':
536 $total = $this->total_campaigns_data['trash'];
537 break;
538 default:
539 $total = $this->total_campaigns_data['publish'] + $this->total_campaigns_data['draft'];
540 break;
541 }
542
543 return $total;
544 }
545 }