diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-importers.php b/plugins/woocommerce/includes/admin/class-wc-admin-importers.php index 6dfec075b89..3c16a0b07d9 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-importers.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-importers.php @@ -215,114 +215,12 @@ class WC_Admin_Importers { * Ajax callback for importing one batch of products from a CSV. */ public function do_ajax_product_import() { - global $wpdb; - - check_ajax_referer( 'wc-product-import', 'security' ); - - if ( ! $this->import_allowed() || ! isset( $_POST['file'] ) ) { // PHPCS: input var ok. + if ( ! $this->import_allowed() ) { wp_send_json_error( array( 'message' => __( 'Insufficient privileges to import products.', 'woocommerce' ) ) ); } include_once WC_ABSPATH . 'includes/admin/importers/class-wc-product-csv-importer-controller.php'; - include_once WC_ABSPATH . 'includes/import/class-wc-product-csv-importer.php'; - - $file = wc_clean( wp_unslash( $_POST['file'] ) ); // PHPCS: input var ok. - $params = array( - 'delimiter' => ! empty( $_POST['delimiter'] ) ? wc_clean( wp_unslash( $_POST['delimiter'] ) ) : ',', // PHPCS: input var ok. - 'start_pos' => isset( $_POST['position'] ) ? absint( $_POST['position'] ) : 0, // PHPCS: input var ok. - 'mapping' => isset( $_POST['mapping'] ) ? (array) wc_clean( wp_unslash( $_POST['mapping'] ) ) : array(), // PHPCS: input var ok. - 'update_existing' => isset( $_POST['update_existing'] ) ? (bool) $_POST['update_existing'] : false, // PHPCS: input var ok. - 'character_encoding' => isset( $_POST['character_encoding'] ) ? wc_clean( wp_unslash( $_POST['character_encoding'] ) ) : '', - - /** - * Batch size for the product import process. - * - * @param int $size Batch size. - * - * @since 3.1.0 - */ - 'lines' => apply_filters( 'woocommerce_product_import_batch_size', 30 ), - 'parse' => true, - ); - - // Log failures. - if ( 0 !== $params['start_pos'] ) { - $error_log = array_filter( (array) get_user_option( 'product_import_error_log' ) ); - } else { - $error_log = array(); - } - - $importer = WC_Product_CSV_Importer_Controller::get_importer( $file, $params ); - $results = $importer->import(); - $percent_complete = $importer->get_percent_complete(); - $error_log = array_merge( $error_log, $results['failed'], $results['skipped'] ); - - update_user_option( get_current_user_id(), 'product_import_error_log', $error_log ); - - if ( 100 === $percent_complete ) { - // @codingStandardsIgnoreStart. - $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_original_id' ) ); - $wpdb->delete( $wpdb->posts, array( - 'post_type' => 'product', - 'post_status' => 'importing', - ) ); - $wpdb->delete( $wpdb->posts, array( - 'post_type' => 'product_variation', - 'post_status' => 'importing', - ) ); - // @codingStandardsIgnoreEnd. - - // Clean up orphaned data. - $wpdb->query( - " - DELETE {$wpdb->posts}.* FROM {$wpdb->posts} - LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->posts}.post_parent - WHERE wp.ID IS NULL AND {$wpdb->posts}.post_type = 'product_variation' - " - ); - $wpdb->query( - " - DELETE {$wpdb->postmeta}.* FROM {$wpdb->postmeta} - LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->postmeta}.post_id - WHERE wp.ID IS NULL - " - ); - // @codingStandardsIgnoreStart. - $wpdb->query( " - DELETE tr.* FROM {$wpdb->term_relationships} tr - LEFT JOIN {$wpdb->posts} wp ON wp.ID = tr.object_id - LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id - WHERE wp.ID IS NULL - AND tt.taxonomy IN ( '" . implode( "','", array_map( 'esc_sql', get_object_taxonomies( 'product' ) ) ) . "' ) - " ); - // @codingStandardsIgnoreEnd. - - // Send success. - wp_send_json_success( - array( - 'position' => 'done', - 'percentage' => 100, - 'url' => add_query_arg( array( '_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ) ), admin_url( 'edit.php?post_type=product&page=product_importer&step=done' ) ), - 'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0, - 'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0, - 'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0, - 'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0, - 'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0, - ) - ); - } else { - wp_send_json_success( - array( - 'position' => $importer->get_file_position(), - 'percentage' => $percent_complete, - 'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0, - 'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0, - 'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0, - 'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0, - 'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0, - ) - ); - } + WC_Product_CSV_Importer_Controller::dispatch_ajax(); } /** diff --git a/plugins/woocommerce/includes/admin/importers/class-wc-product-csv-importer-controller.php b/plugins/woocommerce/includes/admin/importers/class-wc-product-csv-importer-controller.php index e5fac1cecb9..87b705e9ef3 100644 --- a/plugins/woocommerce/includes/admin/importers/class-wc-product-csv-importer-controller.php +++ b/plugins/woocommerce/includes/admin/importers/class-wc-product-csv-importer-controller.php @@ -274,6 +274,121 @@ class WC_Product_CSV_Importer_Controller { $this->output_footer(); } + /** + * Processes AJAX requests related to a product CSV import. + * + * @since 9.3.0 + */ + public static function dispatch_ajax() { + global $wpdb; + + check_ajax_referer( 'wc-product-import', 'security' ); + + try { + $file = wc_clean( wp_unslash( $_POST['file'] ?? '' ) ); // PHPCS: input var ok. + $params = array( + 'delimiter' => ! empty( $_POST['delimiter'] ) ? wc_clean( wp_unslash( $_POST['delimiter'] ) ) : ',', // PHPCS: input var ok. + 'start_pos' => isset( $_POST['position'] ) ? absint( $_POST['position'] ) : 0, // PHPCS: input var ok. + 'mapping' => isset( $_POST['mapping'] ) ? (array) wc_clean( wp_unslash( $_POST['mapping'] ) ) : array(), // PHPCS: input var ok. + 'update_existing' => isset( $_POST['update_existing'] ) ? (bool) $_POST['update_existing'] : false, // PHPCS: input var ok. + 'character_encoding' => isset( $_POST['character_encoding'] ) ? wc_clean( wp_unslash( $_POST['character_encoding'] ) ) : '', + + /** + * Batch size for the product import process. + * + * @param int $size Batch size. + * + * @since 3.1.0 + */ + 'lines' => apply_filters( 'woocommerce_product_import_batch_size', 1 ), + 'parse' => true, + ); + + // Log failures. + if ( 0 !== $params['start_pos'] ) { + $error_log = array_filter( (array) get_user_option( 'product_import_error_log' ) ); + } else { + $error_log = array(); + } + + include_once WC_ABSPATH . 'includes/import/class-wc-product-csv-importer.php'; + + $importer = self::get_importer( $file, $params ); + $results = $importer->import(); + $percent_complete = $importer->get_percent_complete(); + $error_log = array_merge( $error_log, $results['failed'], $results['skipped'] ); + + update_user_option( get_current_user_id(), 'product_import_error_log', $error_log ); + + if ( 100 === $percent_complete ) { + // @codingStandardsIgnoreStart. + $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_original_id' ) ); + $wpdb->delete( $wpdb->posts, array( + 'post_type' => 'product', + 'post_status' => 'importing', + ) ); + $wpdb->delete( $wpdb->posts, array( + 'post_type' => 'product_variation', + 'post_status' => 'importing', + ) ); + // @codingStandardsIgnoreEnd. + + // Clean up orphaned data. + $wpdb->query( + " + DELETE {$wpdb->posts}.* FROM {$wpdb->posts} + LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->posts}.post_parent + WHERE wp.ID IS NULL AND {$wpdb->posts}.post_type = 'product_variation' + " + ); + $wpdb->query( + " + DELETE {$wpdb->postmeta}.* FROM {$wpdb->postmeta} + LEFT JOIN {$wpdb->posts} wp ON wp.ID = {$wpdb->postmeta}.post_id + WHERE wp.ID IS NULL + " + ); + // @codingStandardsIgnoreStart. + $wpdb->query( " + DELETE tr.* FROM {$wpdb->term_relationships} tr + LEFT JOIN {$wpdb->posts} wp ON wp.ID = tr.object_id + LEFT JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id + WHERE wp.ID IS NULL + AND tt.taxonomy IN ( '" . implode( "','", array_map( 'esc_sql', get_object_taxonomies( 'product' ) ) ) . "' ) + " ); + // @codingStandardsIgnoreEnd. + + // Send success. + wp_send_json_success( + array( + 'position' => 'done', + 'percentage' => 100, + 'url' => add_query_arg( array( '_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ) ), admin_url( 'edit.php?post_type=product&page=product_importer&step=done' ) ), + 'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0, + 'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0, + 'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0, + 'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0, + 'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0, + ) + ); + } else { + wp_send_json_success( + array( + 'position' => $importer->get_file_position(), + 'percentage' => $percent_complete, + 'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0, + 'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0, + 'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0, + 'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0, + 'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0, + ) + ); + } + } catch ( \Exception $e ) { + wp_send_json_error( array( 'message' => $e->getMessage() ) ); + } + } + /** * Output information about the uploading process. */