PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.1.7
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.1.7
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 / Form / Tables / Submissions.php

Submissions.php in FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment 3.1.7, at Inc/Core/Form/Tables/Submissions.php

612 lines 14.8 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.7 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\Form\Tables;
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\Form\Form;
26
27 class Submissions extends \WP_List_Table
28 {
29 /**
30 * Total items.
31 *
32 * @var array
33 */
34 private $total_items;
35
36 /**
37 * Total items per page.
38 *
39 * @var int
40 */
41 private $per_page = 20;
42
43 /**
44 * The selected form id.
45 *
46 * @var string
47 */
48 private $selected_form_id = null;
49
50 /**
51 * The selected form.
52 *
53 * @var array
54 */
55 private $selected_form = null;
56
57 public function __construct($args = [])
58 {
59 parent::__construct($args);
60
61 $forms = Form::getForms();
62 usort($forms, function($a, $b) {
63 return strcmp(strtolower($a['name']), strtolower($b['name']));
64 });
65 $this->selected_form_id = isset($_GET['form_id']) ? sanitize_key($_GET['form_id']) : (count($forms) ? $forms[0]['id'] : false); //phpcs:ignore WordPress.Security.NonceVerification.Recommended
66 $this->selected_form = Form::getFormByID($this->selected_form_id, true);
67 }
68
69 /**
70 * This is a default column renderer.
71 *
72 * @param Object $item
73 * @param string $column_name
74 *
75 * @return string
76 */
77 public function column_default($item, $column_name)
78 {
79 // Find field related values
80 if (strpos($column_name, 'field_') === 0)
81 {
82 $form_fields = $this->getFormFields();
83
84 foreach ($item->meta as $meta_item)
85 {
86 if ('field_' . $meta_item->meta_key !== $column_name)
87 {
88 continue;
89 }
90
91 $field = current(array_filter($form_fields, function($field) use ($meta_item) {
92 return $field->getOptionValue('id') === $meta_item->meta_key;
93 }));
94
95 if ($field)
96 {
97 return $field->prepareValueHTML($meta_item->meta_value);
98 }
99 else
100 {
101 return nl2br($meta_item->meta_value);
102 }
103 }
104
105 return '';
106 }
107
108 return $item->$column_name;
109 }
110
111 /**
112 * Gets a list of CSS classes for the WP_List_Table table tag.
113 *
114 * @since 3.1.0
115 *
116 * @return string[] Array of CSS classes for the table tag.
117 */
118 protected function get_table_classes() {
119 $mode = get_user_setting( 'posts_list_mode', 'list' );
120
121 $mode_class = esc_attr( 'table-view-' . $mode );
122
123 return array( 'widefat', 'striped', $mode_class, $this->_args['plural'] );
124 }
125
126 /**
127 * Returns the form fields.
128 *
129 * @return array
130 */
131 private function getFormFields()
132 {
133 return isset($this->selected_form['fields']) ? $this->selected_form['fields'] : [];
134 }
135
136 /**
137 * Column "state" output.
138 *
139 * @param object $item
140 *
141 * @return void
142 */
143 public function column_state($item)
144 {
145 $payload = [
146 'input_class' => ['fb-toggle-form-state'],
147 'name' => 'fb_submission_state_' . $item->id,
148 'extra_atts' => [
149 'data-submission-id' => $item->id
150 ],
151 'value' => $item->state === '1' ? 1 : 0
152 ];
153
154 echo \FPFramework\Helpers\HTML::renderFPToggle($payload); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
155 }
156
157 /**
158 * Column "created at" output.
159 *
160 * @param object $item
161 *
162 * @return void
163 */
164 public function column_created_at($item)
165 {
166 return get_date_from_gmt($item->created_at);
167 }
168
169 /**
170 * Column "id" output.
171 *
172 * @param object $item
173 *
174 * @return void
175 */
176 public function column_id($item)
177 {
178 $url = admin_url('admin.php?page=firebox-submissions&task=edit&id=' . $item->id . '&_wpnonce=' . wp_create_nonce('edit-firebox-submission'));
179
180 return '<a href="' . $url . '">' . $item->id . '</a>';
181 }
182
183 /**
184 * Column "user id" output.
185 *
186 * @param object $item
187 *
188 * @return void
189 */
190 public function column_user_id($item)
191 {
192 if ($item->user_id === '0')
193 {
194 return '';
195 }
196
197 return '<a href="' . get_edit_user_link($item->user_id) . '">' . $item->user_id . '</a>';
198 }
199
200 /**
201 * Column "user name" output.
202 *
203 * @param object $item
204 *
205 * @return void
206 */
207 public function column_user_name($item)
208 {
209 if ($item->user_id === '0')
210 {
211 return '';
212 }
213
214 $user = get_user_by('id', $item->user_id);
215
216 return '<a href="' . get_edit_user_link($item->user_id) . '">' . $user->display_name . '</a>';
217 }
218
219 /**
220 * Renders a checkbox.
221 *
222 * @param object $item
223 *
224 * @return string
225 */
226 public function column_cb($item)
227 {
228 return sprintf('<input type="checkbox" name="id[]" value="%s" />', esc_attr($item->id));
229 }
230
231 /**
232 * Returns all table columns.
233 *
234 * @return array
235 */
236 function get_columns()
237 {
238 $columns = [
239 'cb' => '<input type="checkbox" />',
240 'state' => fpframework()->_('FPF_STATUS'),
241 'id' => fpframework()->_('FPF_ID'),
242 'created_at' => firebox()->_('FB_DATE_SUBMITTED')
243 ];
244
245 if ($fields = $this->getFormFields())
246 {
247 foreach ($fields as $field)
248 {
249 $columns = array_merge($columns, [
250 'field_' . $field->getOptionValue('id') => !empty($field->getLabel()) ? $field->getLabel() : $field->getOptionValue('name')
251 ]);
252 }
253 }
254
255 $columns = array_merge($columns, [
256 'user_id' => fpframework()->_('FPF_USER_ID'),
257 'user_name' => fpframework()->_('FPF_USER_NAME'),
258 'visitor_id' => fpframework()->_('FPF_VISITOR_ID'),
259 ]);
260
261 return $columns;
262 }
263
264 /**
265 * [OPTIONAL] This method return columns that may be used to sort table
266 * all strings in array - is column names
267 * notice that true on name column means that its default sort
268 *
269 * @return array
270 */
271 public function get_sortable_columns()
272 {
273 return [
274 'state' => ['state', false],
275 'id' => ['id', true],
276 'created_at' => ['created_at', false],
277 'user_id' => ['user_id', false],
278 'visitor_id' => ['visitor_id', false],
279 ];
280 }
281
282 /**
283 * Returns the allowed sortable columns keys.
284 *
285 * @return array
286 */
287 private function getAllowedOrders()
288 {
289 return array_keys($this->get_sortable_columns());
290 }
291
292 /**
293 * Set bulk actions.
294 *
295 * @return array
296 */
297 public function get_bulk_actions()
298 {
299 return [
300 'publish' => fpframework()->_('FPF_PUBLISH'),
301 'unpublish' => fpframework()->_('FPF_UNPUBLISH'),
302 'delete' => fpframework()->_('FPF_DELETE')
303 ];
304 }
305
306 /**
307 * Processes the bulk actions.
308 *
309 * @return void
310 */
311 public function process_bulk_action()
312 {
313 if (!isset($_GET['form_id']))
314 {
315 return;
316 }
317
318 // Ensure we have an action
319 if (!$action = $this->current_action())
320 {
321 return;
322 }
323
324 // Ensure its a valid action
325 $allowed_actions = $this->get_bulk_actions();
326 if (!array_key_exists($action, $allowed_actions))
327 {
328 return;
329 }
330
331 // Get nonce
332 $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
333 if (!$nonce)
334 {
335 return;
336 }
337
338 // Validate nonce
339 if (!wp_verify_nonce($nonce, 'bulk-firebox_page_firebox-submissions'))
340 {
341 wp_die(esc_html(fpframework()->_('FPF_CANNOT_VALIDATE_REQUEST')));
342 }
343
344 unset($_GET['fpframework_fields']);
345
346 // Ensure we have IDs
347 $ids = isset($_GET['id']) ? array_map('intval', array_map('sanitize_text_field', wp_unslash($_GET['id']))) : [];
348 if (!$ids)
349 {
350 return;
351 }
352
353 // Publish or Unpublish action
354 if (in_array($action, ['publish', 'unpublish']))
355 {
356 $new_state = $action === 'publish' ? 1 : 0;
357
358 foreach ($ids as $id)
359 {
360 if (!\FireBox\Core\Helpers\Form\Submission::updateState($id, $new_state))
361 {
362 wp_die(esc_html(firebox()->_('FB_CANNOT_UPDATE_SUBMISSION')));
363 }
364 }
365
366 \FPFramework\Libs\AdminNotice::displaySuccess(firebox()->_('FB_SUBMISSIONS_UPDATED'));
367 }
368 // Delete action
369 else if ($action === 'delete')
370 {
371 $idsPlaceholders = array_fill(0, count($ids), '%s');
372 $placeholdersForIds = implode(', ', $idsPlaceholders);
373
374 global $wpdb;
375
376 // Find all box log ids for each submission
377 $meta_table_name = firebox()->tables->submissionmeta->getFullTableName();
378
379 $box_log_ids = $wpdb->get_results(
380 $wpdb->prepare(
381 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
382 "SELECT DISTINCT(meta_value) FROM $meta_table_name WHERE meta_key = %s AND submission_id IN ($placeholdersForIds)",
383 array_merge(['box_log_id'], $ids)
384 ),
385 ARRAY_A
386 );
387
388 // Delete the form conversions for these submissions
389 if ($box_log_ids)
390 {
391 $logIdsPlaceholders = array_fill(0, count($box_log_ids), '%s');
392 $placeholdersForLogIds = implode(', ', $logIdsPlaceholders);
393
394 $box_log_table_name = firebox()->tables->boxlogdetails->getFullTableName();
395
396 // Prepare the box log ids
397 $box_log_ids = array_map(function($box_log_id) {
398 return $box_log_id['meta_value'];
399 }, $box_log_ids);
400
401 $wpdb->query(
402 $wpdb->prepare(
403 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
404 "DELETE FROM $box_log_table_name WHERE event = 'conversion' AND event_source = 'form' AND log_id IN ($placeholdersForLogIds)",
405 ...$box_log_ids
406 )
407 );
408 }
409
410 // Delete submission
411 $s_table_name = firebox()->tables->submission->getFullTableName();
412 $wpdb->query(
413 $wpdb->prepare(
414 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
415 "DELETE FROM $s_table_name WHERE id IN ($placeholdersForIds)",
416 ...$ids
417 )
418 );
419
420 // Also delete all submission meta associated with this submission
421 $wpdb->query(
422 $wpdb->prepare(
423 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
424 "DELETE FROM $meta_table_name WHERE submission_id IN ($placeholdersForIds)",
425 ...$ids
426 )
427 );
428
429 \FPFramework\Libs\AdminNotice::displaySuccess(firebox()->_('FB_SUBMISSIONS_UPDATED'));
430 }
431 }
432
433 /**
434 * Returns the views.
435 *
436 * @return array
437 */
438 public function get_views()
439 {
440 $current = isset($_GET['status']) ? sanitize_key($_GET['status']) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
441 $base_url = $this->get_base_url();
442
443 // Base URL
444 $remove = ['status', 'paged', '_wpnonce'];
445 $url = remove_query_arg($remove, $base_url);
446
447 $count = '&nbsp;<span class="count">(%d)</span>';
448
449 // All
450 $all_class = in_array($current, ['', 'all'], true) ? ' class="current"' : '';
451 $all_count = sprintf($count, esc_attr(count($this->total_items)));
452 $all_label = fpframework()->_('FPF_ALL') . $all_count;
453
454 // Published
455 $p_class = in_array($current, ['published'], true) ? ' class="current"' : '';
456 $p_count = sprintf($count, esc_attr($this->getPublishedCount()));
457 $p_label = fpframework()->_('FPF_PUBLISHED') . $p_count;
458
459 // Unpublished
460 $t_class = in_array($current, ['unpublished'], true) ? ' class="current"' : '';
461 $t_count = sprintf($count, esc_attr($this->getUnpublishedCount()));
462 $t_label = fpframework()->_('FPF_UNPUBLISHED') . $t_count;
463
464 $views = [
465 'all' => sprintf('<a href="%s"%s>%s</a>', esc_url($url), $all_class, $all_label),
466 'published' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'published', $base_url)), $p_class, $p_label),
467 'unpublished' => sprintf('<a href="%s"%s>%s</a>', esc_url(add_query_arg('status', 'unpublished', $base_url)), $t_class, $t_label)
468 ];
469
470 if ($this->total_items)
471 {
472 $views['export'] = sprintf('<a href="%s">%s</a>', esc_url(add_query_arg(['task' => 'export', 'form_id' => $this->selected_form_id], $base_url)), firebox()->_('FB_EXPORT_SUBMISSIONS'));
473 }
474
475 return $views;
476 }
477
478 /**
479 * Returns how many published items we have.
480 *
481 * @return int
482 */
483 private function getPublishedCount()
484 {
485 return count(array_filter($this->total_items, function($submission) {
486 return $submission->state === '1';
487 }));
488 }
489
490 /**
491 * Returns how many unpublished items we have.
492 *
493 * @return int
494 */
495 private function getUnpublishedCount()
496 {
497 return count(array_filter($this->total_items, function($submission) {
498 return $submission->state === '0';
499 }));
500 }
501
502 /**
503 * When no items, display this message.
504 *
505 * @return void
506 */
507 public function no_items()
508 {
509 echo esc_html(firebox()->_('FB_NO_SUBMISSIONS_FOUND'));
510 }
511
512 /**
513 * Prepares items to be shown in table.
514 *
515 * @return void
516 */
517 function prepare_items()
518 {
519 $columns = $this->get_columns();
520
521 $sortable = $this->get_sortable_columns();
522
523 $this->_column_headers = [$columns, [], $sortable];
524
525 $offset = isset($_GET['paged']) ? max(0, intval($_GET['paged'] - 1) * $this->per_page) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
526
527 $orderby = isset($_GET['orderby']) && in_array($_GET['orderby'], $this->getAllowedOrders()) ? sanitize_key($_GET['orderby']) : 'created_at'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
528 $order = isset($_GET['order']) && in_array($_GET['order'], ['asc', 'desc']) ? sanitize_key($_GET['order']) : 'desc'; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
529 $orderby = $orderby . ' ' . $order;
530
531 $payload = [
532 'where' => [
533 'form_id' => " = '" . sanitize_key($this->selected_form_id) . "'"
534 ],
535 'offset' => $offset,
536 'limit' => $this->per_page,
537 'orderby' => $orderby
538 ];
539
540 $status = isset($_GET['status']) ? sanitize_key($_GET['status']) : false; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
541 if ($status)
542 {
543 $payload['where'] = array_merge($payload['where'], [
544 'state = ' => "'" . sanitize_key($status === 'published' ? 1 : 0) . "'"
545 ]);
546 }
547
548 $this->total_items = firebox()->tables->submission->getResults([
549 'where' => [
550 'form_id' => " = '" . sanitize_key($this->selected_form_id) . "'"
551 ]
552 ], true);
553
554 $total_items_count = count($this->total_items);
555
556 $this->items = $total_items_count ? firebox()->tables->submission->getResults($payload) : [];
557
558 if ($this->items)
559 {
560 // Set submission fields values
561 foreach ($this->items as &$item)
562 {
563 // Find field values
564 $meta = firebox()->tables->submissionmeta->getResults([
565 'where' => [
566 'submission_id' => " = " . sanitize_key($item->id)
567 ]
568 ]);
569 $item->meta = $meta;
570 }
571
572 $this->set_pagination_args([
573 'total_items' => $total_items_count,
574 'per_page' => $this->per_page,
575 'total_pages' => ceil($total_items_count / $this->per_page),
576 ]);
577 }
578 }
579
580 /**
581 * Generates the table navigation above or below the table
582 *
583 * Overriden.
584 *
585 * @param string $which
586 *
587 * @return void
588 */
589 protected function display_tablenav($which)
590 {
591 if ('top' === $which)
592 {
593 wp_nonce_field('bulk-' . $this->_args['plural'], '_wpnonce', false, true);
594 }
595 ?>
596 <div class="tablenav <?php echo esc_attr($which); ?>">
597
598 <?php if ($this->has_items()): ?>
599 <div class="alignleft actions bulkactions">
600 <?php $this->bulk_actions($which); ?>
601 </div>
602 <?php
603 endif;
604 $this->extra_tablenav($which);
605 $this->pagination($which);
606 ?>
607
608 <br class="clear" />
609 </div>
610 <?php
611 }
612 }