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

638 lines 17.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 2.1.14 Free
5 *
6 * @author FirePlugins <info@fireplugins.com>
7 * @link https://www.fireplugins.com
8 * @copyright Copyright © 2024 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 echo \FPFramework\Helpers\HTML::renderFPToggle([
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
115 public function column_views($item)
116 {
117 return isset($item['analytics']['views']) ? $item['analytics']['views'] : '';
118 }
119
120 public function column_conversions($item)
121 {
122 return isset($item['analytics']['conversions']) ? $item['analytics']['conversions'] : '';
123 }
124
125 public function column_conversionrate($item)
126 {
127 return isset($item['analytics']['conversionrate']) && $item['analytics']['conversionrate'] ? number_format($item['analytics']['conversionrate'], 1) . '%' : 'n/a';
128 }
129
130 /**
131 * Column "title" output.
132 *
133 * @param object $item
134 *
135 * @return void
136 */
137 public function column_title($item)
138 {
139 $url = admin_url('post.php?post=' . $item['ID'] . '&action=edit');
140
141 return '<a href="' . $url . '">' . $item['label'] . '</a>';
142 }
143
144 /**
145 * Processes the bulk actions.
146 *
147 * @return void
148 */
149 public function process_bulk_action()
150 {
151 // Ensure we have an action
152 if (!$action = $this->current_action())
153 {
154 return;
155 }
156
157 // Ensure its a valid action
158 $allowed_actions = $this->get_bulk_actions();
159 if (!array_key_exists($action, $allowed_actions))
160 {
161 return;
162 }
163
164 // Ensure we have IDs
165 $ids = isset($_GET['id']) ? array_map('intval', $_GET['id']) : [];
166 if (!$ids)
167 {
168 return;
169 }
170
171 // Get nonce
172 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : '';
173 if (!$nonce)
174 {
175 return;
176 }
177
178 // Verify nonce
179 $nonce_action = 'bulk-' . $this->_args['plural'];
180 if (!wp_verify_nonce($nonce, $nonce_action))
181 {
182 return;
183 }
184
185 switch ($action)
186 {
187 case 'publish':
188 $this->publishPosts($ids);
189 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_PUBLISHED'), count($ids)));
190 break;
191
192 case 'unpublish':
193 $this->unpublishPosts($ids);
194 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_UNPUBLISHED'), count($ids)));
195 break;
196
197 case 'delete':
198 $this->deletePosts($ids);
199 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_DELETED'), count($ids)));
200 break;
201
202 case 'reset_stats':
203 BoxHelper::resetBoxStats($ids);
204 \FPFramework\Libs\AdminNotice::displaySuccess(sprintf(firebox()->_('FB_X_CAMPAIGNS_HAVE_BEEN_RESET'), count($ids)));
205 break;
206 }
207 }
208
209 private function publishPosts($ids = [])
210 {
211 if (!$ids)
212 {
213 return;
214 }
215
216 foreach ($ids as $id)
217 {
218 wp_update_post([
219 'ID' => $id,
220 'post_status' => 'publish'
221 ]);
222 }
223 }
224
225 private function unpublishPosts($ids = [])
226 {
227 if (!$ids)
228 {
229 return;
230 }
231
232 foreach ($ids as $id)
233 {
234 wp_update_post([
235 'ID' => $id,
236 'post_status' => 'draft'
237 ]);
238 }
239 }
240
241 private function deletePosts($ids = [])
242 {
243 if (!$ids)
244 {
245 return;
246 }
247
248 foreach ($ids as $id)
249 {
250 wp_delete_post($id);
251 }
252 }
253
254 protected function handle_row_actions($item, $column_name, $primary)
255 {
256 if ($primary !== $column_name)
257 {
258 return '';
259 }
260
261 // Restores the more descriptive, specific name for use within this method.
262 $post = $item;
263 $post_type_object = get_post_type_object( $post['post_type'] );
264 $can_edit_post = current_user_can( 'edit_post', $post['ID'] );
265 $actions = array();
266 $title = _draft_or_post_title();
267
268 if ( $can_edit_post && 'trash' !== $post['post_status'] ) {
269 $actions['edit'] = sprintf(
270 '<a href="%s" aria-label="%s">%s</a>',
271 get_edit_post_link( $post['ID'] ),
272 /* translators: %s: Post title. */
273 esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $title ) ),
274 __( 'Edit' )
275 );
276 }
277
278 if ( current_user_can( 'delete_post', $post['ID'] ) ) {
279 if ( 'trash' === $post['post_status'] ) {
280 $actions['untrash'] = sprintf(
281 '<a href="%s" aria-label="%s">%s</a>',
282 wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post['ID'] ) ), 'untrash-post_' . $post['ID'] ),
283 /* translators: %s: Post title. */
284 esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash' ), $title ) ),
285 __( 'Restore' )
286 );
287 } elseif ( EMPTY_TRASH_DAYS ) {
288 $actions['trash'] = sprintf(
289 '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
290 get_delete_post_link( $post['ID'] ),
291 /* translators: %s: Post title. */
292 esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $title ) ),
293 _x( 'Trash', 'verb' )
294 );
295 }
296
297 if ( 'trash' === $post['post_status'] || ! EMPTY_TRASH_DAYS ) {
298 $actions['delete'] = sprintf(
299 '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
300 get_delete_post_link( $post['ID'], '', true ),
301 /* translators: %s: Post title. */
302 esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $title ) ),
303 __( 'Delete Permanently' )
304 );
305 }
306 }
307
308 if ( is_post_type_viewable( $post_type_object ) ) {
309 if ( in_array( $post['post_status'], array( 'pending', 'draft', 'future' ), true ) ) {
310 if ( $can_edit_post ) {
311 $preview_link = get_preview_post_link( $post['ID'] );
312 $actions['view'] = sprintf(
313 '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
314 esc_url( $preview_link ),
315 /* translators: %s: Post title. */
316 esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ),
317 __( 'Preview' )
318 );
319 }
320 }
321 }
322
323 // Add 'Duplicate' action
324 $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>';
325
326 // Analytics
327 $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>';
328
329 /**
330 * Check if cookie has been set
331 */
332 if ((new \FireBox\Core\FB\Cookie(firebox()->box->get($post['ID'])))->exist())
333 {
334 $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>';
335 }
336
337 return $this->row_actions( $actions );
338 }
339
340 /**
341 * Returns the views.
342 *
343 * @return array
344 */
345 public function get_views()
346 {
347 $current = $this->getCampaignStatus();
348 $base_url = $this->get_base_url();
349
350 // Base URL
351 $remove = ['status', 'paged', '_wpnonce'];
352 $url = remove_query_arg($remove, $base_url);
353
354 $count = '&nbsp;<span class="count">(%d)</span>';
355
356 $published = (int) $this->total_campaigns_data['publish'];
357 $drafts = (int) $this->total_campaigns_data['draft'];
358 $total_items = $published + $drafts;
359
360 // All
361 $all_class = in_array($current, ['', 'all'], true) ? ' class="current"' : '';
362 $all_count = sprintf($count, esc_attr($total_items));
363 $all_label = fpframework()->_('FPF_ALL') . $all_count;
364
365 // Mine
366 $m_class = in_array($current, ['mine'], true) ? ' class="current"' : '';
367 $m_count = sprintf($count, esc_attr($this->getMineCount()));
368 $m_label = fpframework()->_('FPF_MINE') . $m_count;
369
370 // Published
371 $p_class = in_array($current, ['published'], true) ? ' class="current"' : '';
372 $p_count = sprintf($count, esc_attr($published));
373 $p_label = fpframework()->_('FPF_PUBLISHED') . $p_count;
374
375 // Drafts
376 $d_class = in_array($current, ['drafts'], true) ? ' class="current"' : '';
377 $d_count = sprintf($count, esc_attr($drafts));
378 $d_label = fpframework()->_('FPF_DRAFTS') . $d_count;
379
380 $views = [
381 'all' => sprintf('<a href="%s"%s>%s</a>', esc_url($url), $all_class, $all_label),
382 'mine' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'mine', $base_url)), $m_class, $m_label),
383 'published' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'published', $base_url)), $p_class, $p_label),
384 'drafts' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'drafts', $base_url)), $d_class, $d_label),
385 ];
386
387 if ($this->total_campaigns_data['trash'])
388 {
389 $t_class = in_array($current, ['trash'], true) ? ' class="current"' : '';
390 $t_count = sprintf($count, esc_attr((int) $this->total_campaigns_data['trash']));
391 $t_label = fpframework()->_('FPF_TRASH') . $t_count;
392
393 $views['trash'] = sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'trash', $base_url)), $t_class, $t_label);
394 }
395
396 $views['import'] = '<a href="admin.php?page=firebox-import">' . fpframework()->_('FPF_IMPORT') . '</a>';
397
398 return $views;
399 }
400
401 private function getMineCount()
402 {
403 $query = new \WP_Query(
404 [
405 'post_type' => 'firebox',
406 'author' => get_current_user_id()
407 ]
408 );
409
410 return $query->found_posts;
411 }
412
413 protected function get_bulk_actions()
414 {
415 return [
416 'publish' => __( 'Publish', 'firebox' ),
417 'unpublish' => __( 'Unpublish', 'firebox' ),
418 'delete' => __( 'Delete', 'firebox' ),
419 'fb_export' => __( 'Export', 'firebox' ),
420 'reset_stats' => __( 'Reset Views', 'firebox' ),
421 ];
422 }
423
424 /**
425 * Column "id" output.
426 *
427 * @param object $item
428 *
429 * @return void
430 */
431 public function column_id($item)
432 {
433 $url = admin_url('post.php?post=' . $item['ID'] . '&action=edit');
434
435 return '<a href="' . $url . '">' . $item['ID'] . '</a>';
436 }
437
438 private function getCampaignStatus()
439 {
440 return isset($_GET['status']) ? sanitize_key($_GET['status']) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
441 }
442
443 function prepare_items()
444 {
445 $status = $this->getCampaignStatus();
446 $per_page = $this->per_page;
447 $current_page = $this->get_pagenum();
448
449 $hidden = [];
450 $columns = $this->get_columns();
451 $sortable = $this->get_sortable_columns();
452
453 $this->_column_headers = [$columns, $hidden, $sortable];
454
455 $data = [];
456 $args = [
457 'post_type' => 'firebox',
458 'posts_per_page' => $per_page,
459 'paged' => $current_page
460 ];
461
462 if (isset($_GET['s'])) //phpcs:ignore WordPress.Security.NonceVerification.Recommended
463 {
464 $search_term = sanitize_text_field($_GET['s']); //phpcs:ignore WordPress.Security.NonceVerification.Recommended
465 $args['s'] = $search_term;
466 }
467
468 switch ($status)
469 {
470 case 'mine':
471 $args['author'] = get_current_user_id();
472 break;
473 case 'published':
474 $args['post_status'] = 'publish';
475 break;
476 case 'drafts':
477 $args['post_status'] = 'draft';
478 break;
479 case 'trash':
480 $args['post_status'] = 'trash';
481 break;
482 }
483
484 $this->apply_initial_sorting($args);
485
486 $query = new \WP_Query($args);
487 if ($query->have_posts())
488 {
489 foreach ($query->posts as $post)
490 {
491 $data[] = [
492 'ID' => $post->ID,
493 'label' => $post->post_title,
494 'post_type' => $post->post_type,
495 'post_status' => $post->post_status,
496 'date' => $post->post_modified_gmt,
497 'analytics' => $this->getCampaignAnalytics($post->ID),
498 ];
499 }
500
501 $this->apply_secondary_sorting($data);
502 }
503
504 $total_items = $query->post_count;
505
506 if ($query->found_posts || $this->get_pagenum() === 1)
507 {
508 $total_items = $query->found_posts;
509 }
510 else
511 {
512 if (isset($_REQUEST['post_status']) && in_array($_REQUEST['post_status'], $avail_post_stati, true)) //phpcs:ignore WordPress.Security.NonceVerification.Recommended
513 {
514 $total_items = $this->total_campaigns_data[$_REQUEST['post_status']]; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
515 }
516 elseif (isset($_REQUEST['show_sticky']) && $_REQUEST['show_sticky']) //phpcs:ignore WordPress.Security.NonceVerification.Recommended
517 {
518 $total_items = $this->sticky_posts_count;
519 }
520 elseif (isset($_GET['author']) && get_current_user_id() === (int) $_GET['author']) //phpcs:ignore WordPress.Security.NonceVerification.Recommended
521 {
522 $total_items = $this->user_posts_count;
523 }
524 else
525 {
526 $total_items = array_sum($this->total_campaigns_data);
527
528 // Subtract post types that are not included in the admin all list.
529 foreach (get_post_stati(['show_in_admin_all_list' => false]) as $state)
530 {
531 $total_items -= $this->total_campaigns_data[$state];
532 }
533 }
534 }
535
536 $this->set_pagination_args([
537 'total_items' => $total_items,
538 'per_page' => $per_page
539 ]);
540
541 $this->items = $data;
542 }
543
544 private function getCampaignAnalytics($campaign_id = null)
545 {
546 if (!$campaign_id)
547 {
548 return;
549 }
550
551 $data = new \FireBox\Core\Analytics\Data();
552
553 $metrics = [
554 'views',
555 'conversions',
556 'conversionrate'
557 ];
558 $data->setMetrics($metrics);
559
560 $filters = [
561 'campaign' => [
562 'value' => [$campaign_id]
563 ]
564 ];
565 $data->setFilters($filters);
566
567 return $data->getData('count');
568 }
569
570 private function apply_initial_sorting(&$args = [])
571 {
572 $order = isset($_GET['order']) ? sanitize_key($_GET['order']) : 'desc'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
573 $orderby = isset($_GET['orderby']) ? sanitize_key($_GET['orderby']) : 'id'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
574
575 switch ($orderby)
576 {
577 case 'id':
578 $args['orderby'] = 'ID';
579 $args['order'] = $order;
580 break;
581 case 'title':
582 $args['orderby'] = 'title';
583 $args['order'] = $order;
584 break;
585 }
586 }
587
588 private function apply_secondary_sorting(&$data)
589 {
590 $orderby = isset($_GET['orderby']) ? sanitize_key($_GET['orderby']) : 'id'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
591
592 $allowed_orderby = ['views', 'conversions', 'conversionrate'];
593 if (!in_array($orderby, $allowed_orderby, true))
594 {
595 return;
596 }
597
598 $order = isset($_GET['order']) ? sanitize_key($_GET['order']) : 'desc'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
599
600 usort($data, function ($a, $b) use ($order, $orderby) {
601 if ($order === 'asc')
602 {
603 return strtolower($a['analytics'][$orderby]) > strtolower($b['analytics'][$orderby]) ? 1 : -1;
604 }
605 else
606 {
607 return strtolower($a['analytics'][$orderby]) < strtolower($b['analytics'][$orderby]) ? 1 : -1;
608 }
609 });
610 }
611
612 private function getTotalItems()
613 {
614 $status = $this->getCampaignStatus();
615 $total = 0;
616
617 switch ($status)
618 {
619 case 'mine':
620 $total = $this->getMineCount();
621 break;
622 case 'published':
623 $total = $this->total_campaigns_data['publish'];
624 break;
625 case 'drafts':
626 $total = $this->total_campaigns_data['draft'];
627 break;
628 case 'trash':
629 $total = $this->total_campaigns_data['trash'];
630 break;
631 default:
632 $total = $this->total_campaigns_data['publish'] + $this->total_campaigns_data['draft'];
633 break;
634 }
635
636 return $total;
637 }
638 }