diff --git a/includes/admin/class-wc-admin-importers.php b/includes/admin/class-wc-admin-importers.php index 015fdde3525..8c02b9642cd 100644 --- a/includes/admin/class-wc-admin-importers.php +++ b/includes/admin/class-wc-admin-importers.php @@ -192,6 +192,8 @@ 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 ( ! current_user_can( 'edit_products' ) || ! isset( $_POST['file'] ) ) { @@ -226,6 +228,10 @@ class WC_Admin_Importers { update_user_option( get_current_user_id(), 'product_import_error_log', $error_log ); if ( 100 === $percent_complete ) { + // Clear temp meta. + $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_original_id' ) ); + + // Send success. wp_send_json_success( array( 'position' => 'done', 'percentage' => 100, diff --git a/includes/admin/importers/views/html-product-csv-import-form.php b/includes/admin/importers/views/html-product-csv-import-form.php index 08aa61af473..16525304fc4 100644 --- a/includes/admin/importers/views/html-product-csv-import-form.php +++ b/includes/admin/importers/views/html-product-csv-import-form.php @@ -49,7 +49,7 @@ if ( ! defined( 'ABSPATH' ) ) { - + diff --git a/includes/data-stores/class-wc-product-variation-data-store-cpt.php b/includes/data-stores/class-wc-product-variation-data-store-cpt.php index a82ab99b562..18351e29eb7 100644 --- a/includes/data-stores/class-wc-product-variation-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-variation-data-store-cpt.php @@ -38,23 +38,10 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl public function read( &$product ) { $product->set_defaults(); - if ( ! $product->get_id() || ! ( $post_object = get_post( $product->get_id() ) ) || 'product_variation' !== $post_object->post_type ) { + if ( ! $product->get_id() || ! ( $post_object = get_post( $product->get_id() ) ) || ! in_array( $post_object->post_type, array( 'product', 'product_variation' ) ) ) { return; } - $product->set_parent_id( $post_object->post_parent ); - $parent_id = $product->get_parent_id(); - - // The post doesn't have a parent id, therefore its invalid and we should prevent this being created. - if ( empty( $parent_id ) ) { - throw new Exception( sprintf( 'No parent product set for variation #%d', $product->get_id() ), 422 ); - } - - // The post parent is not a valid variable product so we should prevent this being created. - if ( 'product' !== get_post_type( $product->get_parent_id() ) ) { - throw new Exception( sprintf( 'Invalid parent for variation #%d', $product->get_id() ), 422 ); - } - $product->set_props( array( 'name' => $post_object->post_title, 'slug' => $post_object->post_name, @@ -63,8 +50,14 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl 'status' => $post_object->post_status, 'menu_order' => $post_object->menu_order, 'reviews_allowed' => 'open' === $post_object->comment_status, + 'parent_id' => $post_object->post_parent, ) ); + // The post parent is not a valid variable product so we should prevent this. + if ( $product->get_parent_id( 'edit' ) && 'product' !== get_post_type( $product->get_parent_id( 'edit' ) ) ) { + $product->set_parent_id( 0 ); + } + $this->read_downloads( $product ); $this->read_product_data( $product ); $this->read_extra_data( $product ); @@ -102,6 +95,11 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl $product->set_name( $new_title ); } + // The post parent is not a valid variable product so we should prevent this. + if ( $product->get_parent_id( 'edit' ) && 'product' !== get_post_type( $product->get_parent_id( 'edit' ) ) ) { + $product->set_parent_id( 0 ); + } + $id = wp_insert_post( apply_filters( 'woocommerce_new_product_variation_data', array( 'post_type' => 'product_variation', 'post_status' => $product->get_status() ? $product->get_status() : 'publish', @@ -114,6 +112,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl 'menu_order' => $product->get_menu_order(), 'post_date' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getOffsetTimestamp() ), 'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getTimestamp() ), + 'post_name' => $product->get_slug( 'edit' ), ) ), true ); if ( $id && ! is_wp_error( $id ) ) { @@ -150,6 +149,11 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl $product->set_name( $new_title ); } + // The post parent is not a valid variable product so we should prevent this. + if ( $product->get_parent_id( 'edit' ) && 'product' !== get_post_type( $product->get_parent_id( 'edit' ) ) ) { + $product->set_parent_id( 0 ); + } + $changes = $product->get_changes(); // Only update the post when the post data changes. @@ -165,6 +169,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl 'post_modified' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $product->get_date_modified( 'edit' )->getOffsetTimestamp() ) : current_time( 'mysql' ), 'post_modified_gmt' => isset( $changes['date_modified'] ) ? gmdate( 'Y-m-d H:i:s', $product->get_date_modified( 'edit' )->getTimestamp() ) : current_time( 'mysql', 1 ), 'post_type' => 'product_variation', + 'post_name' => $product->get_slug( 'edit' ), ); /** diff --git a/includes/import/abstract-wc-product-importer.php b/includes/import/abstract-wc-product-importer.php index 004bd46e4a4..ce660eb2dda 100644 --- a/includes/import/abstract-wc-product-importer.php +++ b/includes/import/abstract-wc-product-importer.php @@ -197,6 +197,11 @@ abstract class WC_Product_Importer implements WC_Importer_Interface { unset( $data['manage_stock'], $data['stock_status'], $data['backorders'] ); } + if ( 'importing' === $object->get_status() ) { + $object->set_status( 'publish' ); + $object->set_slug( '' ); + } + $result = $object->set_props( array_diff_key( $data, array_flip( array( 'meta_data', 'raw_image_id', 'raw_gallery_image_ids', 'raw_attributes' ) ) ) ); if ( is_wp_error( $result ) ) { @@ -212,10 +217,6 @@ abstract class WC_Product_Importer implements WC_Importer_Interface { $this->set_image_data( $object, $data ); $this->set_meta_data( $object, $data ); - if ( 'importing' === $object->get_status() ) { - $object->set_status( 'publish' ); - } - $object = apply_filters( 'woocommerce_product_import_pre_insert_product_object', $object, $data ); $object->save(); diff --git a/includes/import/class-wc-product-csv-importer.php b/includes/import/class-wc-product-csv-importer.php index 57b01ab68f5..d00b8473863 100644 --- a/includes/import/class-wc-product-csv-importer.php +++ b/includes/import/class-wc-product-csv-importer.php @@ -107,12 +107,17 @@ class WC_Product_CSV_Importer extends WC_Product_Importer { * @return int|string */ protected function parse_relative_field( $field ) { + global $wpdb; + if ( empty( $field ) ) { return ''; } if ( preg_match( '/^id:(\d+)$/', $field, $matches ) ) { - return intval( $matches[1] ); + $id = intval( $matches[1] ); + $original_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_original_id' AND meta_value = %s;", $id ) ); + + return $original_id ? $original_id : $id; } if ( $id = wc_get_product_id_by_sku( $field ) ) { @@ -136,6 +141,29 @@ class WC_Product_CSV_Importer extends WC_Product_Importer { return ''; } + /** + * Parse the ID field. + * + * If we're not doing an update, create a placeholder product so mapping works + * for rows following this one. + * + * @return int + */ + protected function parse_id_field( $field ) { + $field = absint( $field ); + + // Not updating? Make sure we have a new placeholder for this ID. + if ( $field && ! $this->params['update_existing'] ) { + $product = new WC_Product_Simple(); + $product->set_name( 'Import placeholder for ' . $field ); + $product->set_status( 'importing' ); + $product->add_meta_data( '_original_id', $field, true ); + $field = $product->save(); + } + + return $field && ! is_wp_error( $field ) ? $field : 0; + } + /** * Parse reletive comma-delineated field and return product ID. * @@ -357,7 +385,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer { * column_name => callback. */ $data_formatting = array( - 'id' => 'absint', + 'id' => array( $this, 'parse_id_field' ), 'type' => array( $this, 'parse_comma_field' ), 'published' => array( $this, 'parse_bool_field' ), 'featured' => array( $this, 'parse_bool_field' ),