Merge remote-tracking branch 'origin/feature/product-csv-import-export' into HEAD
This commit is contained in:
commit
df06d6f942
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -673,10 +673,6 @@ table.wc_status_table {
|
|||
}
|
||||
}
|
||||
|
||||
.edit-php.post-type-product .top .bulkactions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#woocommerce-fields.inline-edit-col {
|
||||
clear: left;
|
||||
|
||||
|
@ -5635,10 +5631,6 @@ table.bar_chart {
|
|||
-webkit-appearance: none;
|
||||
border: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* All good till now. Now we'll style the background */
|
||||
progress::-webkit-progress-bar {
|
||||
background: #f5f5f5;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 4px;
|
||||
|
@ -5646,13 +5638,35 @@ table.bar_chart {
|
|||
box-shadow: 0 1px 0px 0 rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Now the value part */
|
||||
progress::-webkit-progress-bar {
|
||||
background: transparent none;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
progress::-webkit-progress-value {
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4);
|
||||
background:
|
||||
-webkit-linear-gradient(top, rgba(255, 255, 255, 0.25), rgba(0, 0, 0, 0.2)),
|
||||
#A46497;
|
||||
background: #A46497;
|
||||
background: linear-gradient( top, #A46497, #66405F ), #A46497;
|
||||
transition: width 1s ease;
|
||||
}
|
||||
|
||||
progress::-moz-progress-bar {
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4);
|
||||
background: #A46497;
|
||||
background: linear-gradient( top, #A46497, #66405F ), #A46497;
|
||||
transition: width 1s ease;
|
||||
}
|
||||
|
||||
progress::-ms-fill {
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4);
|
||||
background: #A46497;
|
||||
background: linear-gradient( to bottom, #A46497, #66405F ), #A46497;
|
||||
transition: width 1s ease;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
if ( 0 < $failed ) {
|
||||
$results [] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
_n( 'Failed to import %s product.', 'Failed to import %s products.', $failed, 'woocommerce' ),
|
||||
_n( 'Failed to import %s product', 'Failed to import %s products', $failed, 'woocommerce' ),
|
||||
'<strong>' . number_format_i18n( $failed ) . '</strong>'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -176,6 +176,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
'post_status' => $product->get_status( 'edit' ) ? $product->get_status( 'edit' ) : 'publish',
|
||||
'menu_order' => $product->get_menu_order( 'edit' ),
|
||||
'post_name' => $product->get_slug( 'edit' ),
|
||||
'post_type' => 'product',
|
||||
);
|
||||
if ( $product->get_date_created( 'edit' ) ) {
|
||||
$post_data['post_date'] = gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getOffsetTimestamp() );
|
||||
|
|
|
@ -153,6 +153,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
|
|||
'post_date_gmt' => gmdate( 'Y-m-d H:i:s', $product->get_date_created( 'edit' )->getTimestamp() ),
|
||||
'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',
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -135,22 +135,34 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
|||
* Process a single item and save.
|
||||
*
|
||||
* @param array $data Raw CSV data.
|
||||
* @return WC_Product|WC_Error
|
||||
* @return array|WC_Error
|
||||
*/
|
||||
protected function process_item( $data ) {
|
||||
try {
|
||||
$object = $this->prepare_product( $data );
|
||||
$object = $this->get_product_object( $data );
|
||||
$updating = false;
|
||||
|
||||
if ( is_wp_error( $object ) ) {
|
||||
return $object;
|
||||
}
|
||||
|
||||
if ( $object->get_id() && 'importing' !== $object->get_status() ) {
|
||||
$updating = true;
|
||||
}
|
||||
|
||||
if ( 'variation' === $object->get_type() ) {
|
||||
$object = $this->save_variation_data( $object, $data );
|
||||
} else {
|
||||
$object = $this->save_product_data( $object, $data );
|
||||
}
|
||||
|
||||
$object = apply_filters( 'woocommerce_product_import_pre_insert_product_object', $object, $data );
|
||||
$object->save();
|
||||
|
||||
// Clean cache for updated products.
|
||||
$this->clear_cache( $object );
|
||||
|
||||
return $object->get_id();
|
||||
return array(
|
||||
'id' => $object->get_id(),
|
||||
'updated' => $updating,
|
||||
);
|
||||
} catch ( WC_Data_Exception $e ) {
|
||||
return new WP_Error( $e->getErrorCode(), $e->getMessage(), $e->getErrorData() );
|
||||
} catch ( Exception $e ) {
|
||||
|
@ -158,29 +170,13 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear product cache.
|
||||
*
|
||||
* @param WC_Product $object Product instance.
|
||||
*/
|
||||
protected function clear_cache( $object ) {
|
||||
$id = $object->get_id();
|
||||
|
||||
if ( 'variation' === $object->get_type() ) {
|
||||
$id = $object->get_parent_id();
|
||||
}
|
||||
|
||||
wc_delete_product_transients( $id );
|
||||
wp_cache_delete( 'product-' . $id, 'products' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single product for create or update.
|
||||
*
|
||||
* @param array $data Row data.
|
||||
* @return WC_Product|WP_Error
|
||||
*/
|
||||
protected function prepare_product( $data ) {
|
||||
protected function get_product_object( $data ) {
|
||||
$id = isset( $data['id'] ) ? absint( $data['id'] ) : 0;
|
||||
|
||||
// Type is the most important part here because we need to be using the correct class and methods.
|
||||
|
@ -206,16 +202,10 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
|||
return new WP_Error( 'woocommerce_product_csv_importer_invalid_id', sprintf( __( 'Invalid product ID %d.', 'woocommerce' ), $id ), array( 'id' => $id, 'status' => 401 ) );
|
||||
}
|
||||
} else {
|
||||
$product = new WC_Product_Simple();
|
||||
$product = new WC_Product_Simple( $id );
|
||||
}
|
||||
|
||||
if ( 'variation' === $product->get_type() ) {
|
||||
$product = $this->save_variation_data( $product, $data );
|
||||
} else {
|
||||
$product = $this->save_product_data( $product, $data );
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_product_import_pre_insert_product_object', $product, $data );
|
||||
return apply_filters( 'woocommerce_product_import_get_product_object', $product, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -246,6 +236,8 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
|||
// Status.
|
||||
if ( isset( $data['published'] ) ) {
|
||||
$product->set_status( $data['published'] ? 'publish' : 'draft' );
|
||||
} elseif ( 'importing' === $product->get_status() ) {
|
||||
$product->set_status( 'publish' );
|
||||
}
|
||||
|
||||
// Slug.
|
||||
|
@ -572,8 +564,10 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
|||
}
|
||||
|
||||
// Status.
|
||||
if ( isset( $data['status'] ) ) {
|
||||
$variation->set_status( false === $data['status'] ? 'private' : 'publish' );
|
||||
if ( isset( $data['published'] ) ) {
|
||||
$variation->set_status( $data['published'] ? 'publish' : 'draft' );
|
||||
} elseif ( 'importing' === $variation->get_status() ) {
|
||||
$variation->set_status( 'publish' );
|
||||
}
|
||||
|
||||
// SKU.
|
||||
|
|
|
@ -94,7 +94,14 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
|||
|
||||
/**
|
||||
* Parse relative field and return product ID.
|
||||
* Handle `id:xx` and SKUs.
|
||||
*
|
||||
* Handles `id:xx` and SKUs.
|
||||
*
|
||||
* If mapping to an id: and the product ID does not exist, this link is not
|
||||
* valid.
|
||||
*
|
||||
* If mapping to a SKU and the product ID does not exist, a temporary object
|
||||
* will be created so it can be updated later.
|
||||
*
|
||||
* @param string $field Field value.
|
||||
* @return int|string
|
||||
|
@ -108,7 +115,25 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
|||
return intval( $matches[1] );
|
||||
}
|
||||
|
||||
return wc_get_product_id_by_sku( $field );
|
||||
if ( $id = wc_get_product_id_by_sku( $field ) ) {
|
||||
return $id;
|
||||
}
|
||||
|
||||
try {
|
||||
$product = new WC_Product_Simple();
|
||||
$product->set_name( 'Import placeholder for ' . $field );
|
||||
$product->set_status( 'importing' );
|
||||
$product->set_sku( $field );
|
||||
$id = $product->save();
|
||||
|
||||
if ( $id && ! is_wp_error( $id ) ) {
|
||||
return $id;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,6 +398,11 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
|||
protected function expand_data( $data ) {
|
||||
$data = apply_filters( 'woocommerce_product_importer_pre_expand_data', $data );
|
||||
|
||||
// Product ID and SKU mapping.
|
||||
if ( empty( $data['id'] ) && ! empty( $data['sku'] ) && ( $id = wc_get_product_id_by_sku( $data['sku'] ) ) ) {
|
||||
$data['id'] = $id;
|
||||
}
|
||||
|
||||
// Meta data.
|
||||
$data['meta_data'] = array();
|
||||
$data['attributes'] = array();
|
||||
|
@ -397,6 +427,7 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
|||
|
||||
// Type, virtual and downlodable.
|
||||
if ( isset( $data['type'] ) ) {
|
||||
$data['type'] = array_map( 'strtolower', $data['type'] );
|
||||
$data['virtual'] = in_array( 'virtual', $data['type'], true );
|
||||
$data['downloadable'] = in_array( 'downloadable', $data['type'], true );
|
||||
|
||||
|
@ -479,6 +510,10 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
|||
|
||||
// Parse the data.
|
||||
foreach ( $this->raw_data as $row ) {
|
||||
// Skip empty rows.
|
||||
if ( ! count( array_filter( $row ) ) ) {
|
||||
continue;
|
||||
}
|
||||
$data = array();
|
||||
|
||||
foreach ( $row as $id => $value ) {
|
||||
|
@ -533,18 +568,26 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
|||
);
|
||||
|
||||
foreach ( $this->parsed_data as $parsed_data_key => $parsed_data ) {
|
||||
|
||||
// Don't import products with IDs or SKUs that already exist if option is true.
|
||||
// Do not import products with IDs or SKUs that already exist if option
|
||||
// is true UNLESS this is a dummy product created during this import.
|
||||
if ( ! $this->params['update_existing'] ) {
|
||||
$id = isset( $parsed_data['id'] ) ? absint( $parsed_data['id'] ) : 0;
|
||||
$sku = isset( $parsed_data['sku'] ) ? esc_attr( $parsed_data['sku'] ) : '';
|
||||
$id = isset( $parsed_data['id'] ) ? absint( $parsed_data['id'] ) : 0;
|
||||
$sku = isset( $parsed_data['sku'] ) ? esc_attr( $parsed_data['sku'] ) : '';
|
||||
|
||||
if ( $id && wc_get_product( $id ) ) {
|
||||
$data['skipped'][] = new WP_Error( 'woocommerce_product_csv_importer_error', __( 'A product with this ID already exists.', 'woocommerce' ), array( 'id' => $id, 'row' => $this->get_row_id( $parsed_data ) ) );
|
||||
continue;
|
||||
} elseif ( $sku && wc_get_product_id_by_sku( $sku ) ) {
|
||||
$data['skipped'][] = new WP_Error( 'woocommerce_product_csv_importer_error', __( 'A product with this SKU already exists.', 'woocommerce' ), array( 'sku' => $sku, 'row' => $this->get_row_id( $parsed_data ) ) );
|
||||
continue;
|
||||
if ( $id ) {
|
||||
$product = wc_get_product( $id );
|
||||
|
||||
if ( $product && 'importing' !== $product->get_status() ) {
|
||||
$data['skipped'][] = new WP_Error( 'woocommerce_product_csv_importer_error', __( 'A product with this ID already exists.', 'woocommerce' ), array( 'id' => $id, 'row' => $this->get_row_id( $parsed_data ) ) );
|
||||
continue;
|
||||
}
|
||||
} elseif ( $sku && ( $id_from_sku = wc_get_product_id_by_sku( $sku ) ) ) {
|
||||
$product = wc_get_product( $id_from_sku );
|
||||
|
||||
if ( $product && 'importing' !== $product->get_status() ) {
|
||||
$data['skipped'][] = new WP_Error( 'woocommerce_product_csv_importer_error', __( 'A product with this SKU already exists.', 'woocommerce' ), array( 'sku' => $sku, 'row' => $this->get_row_id( $parsed_data ) ) );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,11 +595,11 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
|||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$result->add_data( array( 'row' => $this->get_row_id( $parsed_data ) ) );
|
||||
$data['failed'][] = $result;
|
||||
} elseif ( ! empty( $parsed_data['id'] ) ) {
|
||||
$data['updated'][] = $result;
|
||||
$data['failed'][] = $result['id'];
|
||||
} elseif ( $result['updated'] ) {
|
||||
$data['updated'][] = $result['id'];
|
||||
} else {
|
||||
$data['imported'][] = $result;
|
||||
$data['imported'][] = $result['id'];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
|
|||
|
||||
// Exclude imported products.
|
||||
foreach ( $results['imported'] as $id ) {
|
||||
wp_delete_post( $id );
|
||||
wp_delete_post( $id, true );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
|
|||
'type' => 'simple',
|
||||
'sku' => 'PRODUCT-01',
|
||||
'name' => 'Imported Product 1',
|
||||
'published' => 1,
|
||||
'published' => true,
|
||||
'regular_price' => '40',
|
||||
'meta_data' => array(),
|
||||
'attributes' => array(),
|
||||
|
@ -164,7 +164,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
|
|||
'type' => 'simple',
|
||||
'sku' => 'PRODUCT-02',
|
||||
'name' => 'Imported Product 2',
|
||||
'published' => 1,
|
||||
'published' => true,
|
||||
'regular_price' => '41',
|
||||
'meta_data' => array(),
|
||||
'attributes' => array(),
|
||||
|
@ -178,7 +178,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
|
|||
'type' => 'simple',
|
||||
'sku' => 'PRODUCT-03',
|
||||
'name' => 'Imported Product 3',
|
||||
'published' => 1,
|
||||
'published' => true,
|
||||
'regular_price' => '42',
|
||||
'meta_data' => array(),
|
||||
'attributes' => array(),
|
||||
|
|
Loading…
Reference in New Issue