| @@ -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(); |