PluginProbe
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster / 2.7.0
SellKit – Funnel builder and checkout optimizer for WooCommerce to sell more, faster v2.7.0
2.7.0 2.6.0 trunk 1.1.0 1.1.4 1.2.1 1.2.2 1.2.3 1.2.5 1.2.9 1.3.1 1.3.2 1.5.0 1.5.1 1.5.4 1.5.7 1.5.8 1.5.9 1.6.2 1.6.5 1.6.8 1.7.2 1.7.4 1.7.5 1.7.9 All 43 releases
← All changes | includes/admin/components/class-list-table.php +147 -2 1.6.22.7.0 View file →
@@ -3,8 +3,9 @@
3 3 defined( 'ABSPATH' ) || die();
4 4
5 5 use Elementor\Plugin;
6 6 use Sellkit\Admin\Funnel\Importer\Ajax_Handler;
7 +use Sellkit\Global_Checkout\Checkout;
7 8
8 9 /**
9 10 * List Table component class.
10 11 *
@@ -9,8 +10,9 @@
9 10 * List Table component class.
10 11 *
11 12 * @since 1.1.0
12 13 * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
14 + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
13 15 */
14 16 class Sellkit_Admin_List_Table {
15 17
16 18 /**
@@ -58,8 +60,13 @@
58 60 $post_type = sellkit_htmlspecialchars( INPUT_POST, 'post_type' );
59 61 $paged = filter_input( INPUT_POST, 'paged', FILTER_SANITIZE_NUMBER_INT );
60 62 $posts_per_page = filter_input( INPUT_POST, 'posts_per_page', FILTER_SANITIZE_NUMBER_INT );
61 63 $search_args = filter_input( INPUT_POST, 'args', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
64 + $page = filter_input( INPUT_POST, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
65 + $list_status = isset( $_POST['list_status'] ) ? sanitize_text_field( wp_unslash( $_POST['list_status'] ) ) : 'all';
66 + if ( ! in_array( $list_status, [ 'all', 'publish', 'draft' ], true ) ) {
67 + $list_status = 'all';
68 + }
62 69
63 70 /**
64 71 * Filter List Table query arguments.
65 72 *
@@ -80,8 +87,22 @@
80 87 $args[ $search_arg['param'] ] = $search_arg['value'];
81 88 }
82 89 }
83 90
91 + $not_in = $this->get_list_table_post__not_in( $post_type, $page );
92 + if ( ! empty( $not_in ) ) {
93 + // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- Exclude global checkout funnel from list.
94 + $args['post__not_in'] = $not_in;
95 + }
96 +
97 + if ( 'publish' === $list_status ) {
98 + $args['post_status'] = 'publish';
99 + } elseif ( 'draft' === $list_status ) {
100 + $args['post_status'] = 'draft';
101 + } else {
102 + $args['post_status'] = [ 'publish', 'draft', 'pending', 'private', 'future' ];
103 + }
104 +
84 105 // Query.
85 106 $query = new \WP_Query( $args );
86 107
87 108 /**
@@ -91,8 +112,9 @@
91 112 *
92 113 * @param array $args The taxonomy arguments.
93 114 */
94 115 $posts = apply_filters( "sellkit_list_table_{$post_type}_posts", $query->posts );
116 + $posts = $this->prepare_posts_for_list_table( $posts );
95 117
96 118 /**
97 119 * Filter List Table columns.
98 120 *
@@ -104,17 +126,109 @@
104 126 'labels' => [],
105 127 'values' => [],
106 128 ], $posts );
107 129
130 + $list_counts = $this->get_list_status_counts( $post_type, $page );
131 +
108 132 // Send response.
109 133 wp_send_json_success( [
110 134 'posts' => $posts,
111 135 'max_num_pages' => $query->max_num_pages,
112 136 'columns' => $columns,
137 + 'list_counts' => $list_counts,
113 138 ] );
114 139 }
115 140
116 141 /**
142 + * Post IDs excluded from list queries (e.g. global checkout funnel).
143 + *
144 + * @since 2.4.0
145 + *
146 + * @param string $post_type Post type.
147 + * @param string $page Admin page slug context.
148 + * @return int[]
149 + */
150 + private function get_list_table_post__not_in( $post_type, $page ) {
151 + if ( 'sellkit-funnels' === $post_type && 'checkout' !== $page ) {
152 + $global_checkout_id = (int) get_option( Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 );
153 + if ( $global_checkout_id > 0 ) {
154 + return [ $global_checkout_id ];
155 + }
156 + }
157 + return [];
158 + }
159 +
160 + /**
161 + * Counts for All / Active / Inactive list filters (respects post__not_in).
162 + *
163 + * @since 2.4.0
164 + *
165 + * @param string $post_type Post type.
166 + * @param string $page Admin page slug context.
167 + * @return array{all:int,publish:int,draft:int}
168 + */
169 + private function get_list_status_counts( $post_type, $page ) {
170 + $not_in = $this->get_list_table_post__not_in( $post_type, $page );
171 + $base = [
172 + 'post_type' => $post_type,
173 + 'posts_per_page' => -1,
174 + 'fields' => 'ids',
175 + 'no_found_rows' => false,
176 + ];
177 + if ( ! empty( $not_in ) ) {
178 + // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in
179 + $base['post__not_in'] = $not_in;
180 + }
181 +
182 + $statuses_all = [ 'publish', 'draft', 'pending', 'private', 'future' ];
183 + $q_all = new \WP_Query( array_merge( $base, [ 'post_status' => $statuses_all ] ) );
184 + $q_pub = new \WP_Query( array_merge( $base, [ 'post_status' => 'publish' ] ) );
185 + $q_dra = new \WP_Query( array_merge( $base, [ 'post_status' => 'draft' ] ) );
186 +
187 + return [
188 + 'all' => (int) $q_all->found_posts,
189 + 'publish' => (int) $q_pub->found_posts,
190 + 'draft' => (int) $q_dra->found_posts,
191 + ];
192 + }
193 +
194 + /**
195 + * Add formatted published / modified dates for list table rows (JSON).
196 + *
197 + * @since 2.4.0
198 + *
199 + * @param array $posts Posts from WP_Query.
200 + * @return array
201 + */
202 + private function prepare_posts_for_list_table( $posts ) {
203 + if ( empty( $posts ) || ! is_array( $posts ) ) {
204 + return $posts;
205 + }
206 +
207 + $date_time_format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
208 +
209 + foreach ( $posts as $key => $post ) {
210 + if ( ! $post instanceof \WP_Post ) {
211 + continue;
212 + }
213 +
214 + $row = array_merge(
215 + $post->to_array(),
216 + [
217 + 'custom_date' => get_the_date( 'M d, Y', $post ),
218 + 'custom_date_with_time' => mysql2date( $date_time_format, $post->post_date ),
219 + 'custom_modified_date' => get_the_modified_date( 'M d, Y', $post ),
220 + 'custom_modified_date_with_time' => mysql2date( $date_time_format, $post->post_modified ),
221 + ]
222 + );
223 +
224 + $posts[ $key ] = (object) $row;
225 + }
226 +
227 + return $posts;
228 + }
229 +
230 + /**
117 231 * Handle post actions.
118 232 *
119 233 * @since 1.1.0
120 234 */
@@ -124,8 +238,12 @@
124 238 // Sanitize.
125 239 $post = filter_input( INPUT_POST, 'post', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
126 240 $sub_action = sellkit_htmlspecialchars( INPUT_POST, 'sub_action' );
127 241
242 + if ( is_array( $post ) && count( $post ) < 3 ) {
243 + $post = (array) get_post( $post['ID'] );
244 + }
245 +
128 246 call_user_func( [ $this, "handle_post_{$sub_action}" ], $post );
129 247 }
130 248
131 249 /**
@@ -262,8 +380,14 @@
262 380 if ( 'step_data' === $key ) {
263 381 $data[0] = str_replace( $post_id, $new_funnel_id, $data[0] );
264 382 }
265 383
384 + if ( '_elementor_data' === $key && '[' === substr( $data[0], 0, 1 ) ) {
385 + update_post_meta( $new_id, $key, maybe_unserialize( wp_slash( $data[0] ) ) );
386 +
387 + continue;
388 + }
389 +
266 390 update_post_meta( $new_id, $key, maybe_unserialize( $data[0] ) );
267 391 }
268 392
269 393 if ( class_exists( 'Elementor\Plugin' ) ) {
@@ -333,8 +457,12 @@
333 457 return $data;
334 458 }
335 459
336 460 foreach ( $data['funnel']['sellkit_steps'] as $step ) {
461 + if ( ! array_key_exists( 'page_id', $step ) ) {
462 + continue;
463 + }
464 +
337 465 $id = $step['page_id'];
338 466 $post = get_post( $id );
339 467 $export_data = get_post_meta( $id );
340 468
@@ -396,9 +524,15 @@
396 524 if ( empty( $posts ) ) {
397 525 wp_send_json_error();
398 526 }
399 527
528 + $global_checkout_id = intval( get_option( Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, 0 ) );
529 +
400 530 foreach ( $posts as $post ) {
531 + if ( $post->ID === $global_checkout_id ) {
532 + continue;
533 + }
534 +
401 535 $post = (array) $post;
402 536
403 537 $data['data'][] = $this->export_single_funnel( $post );
404 538 }
@@ -412,16 +546,21 @@
412 546 * @since 1.5.0
413 547 */
414 548 private function handle_post_import_funnel() {
415 549 $json = filter_input( INPUT_POST, 'funnel', FILTER_DEFAULT );
550 + $page = filter_input( INPUT_POST, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
416 551 $data = json_decode( $json );
417 552
553 + if ( 'multi' === $data->type && 'checkout' === $page ) {
554 + wp_send_json_error( esc_html__( 'You can not import multiple funnels to the global checkout.', 'sellkit' ) );
555 + }
556 +
418 557 $ajax_handler = new Ajax_Handler();
419 558
420 559 if ( 'single' === $data->type ) {
421 560 $funnel_data = $data->data->funnel;
422 561 $steps_data = $data->data->step;
423 - $ajax_handler->import_funnel_by_data( $funnel_data, $steps_data );
562 + $funnel_id = $ajax_handler->import_funnel_by_data( $funnel_data, $steps_data );
424 563 }
425 564
426 565 if ( 'multi' === $data->type ) {
427 566 foreach ( $data->data as $item ) {
@@ -426,11 +565,17 @@
426 565 if ( 'multi' === $data->type ) {
427 566 foreach ( $data->data as $item ) {
428 567 $funnel_data = $item->funnel;
429 568 $steps_data = $item->step;
430 - $ajax_handler->import_funnel_by_data( $funnel_data, $steps_data );
569 + $funnel_id = $ajax_handler->import_funnel_by_data( $funnel_data, $steps_data );
431 570 }
432 571 }
572 +
573 + if ( 'checkout' === $page && (int) $funnel_id > 0 ) {
574 + update_option( Checkout::SELLKIT_GLOBAL_CHECKOUT_OPTION, $funnel_id );
575 + }
576 +
577 + wp_send_json_success();
433 578 }
434 579 }
435 580
436 581 Sellkit_Admin_List_Table::get_instance();