Merge pull request #15189 from woocommerce/feature/import-merges-2

Add "Skip existing products" option.
This commit is contained in:
Mike Jolley 2017-05-22 11:56:39 +01:00 committed by GitHub
commit 076eac48b5
8 changed files with 69 additions and 37 deletions

View File

@ -5,12 +5,13 @@
* productImportForm handles the import process. * productImportForm handles the import process.
*/ */
var productImportForm = function( $form ) { var productImportForm = function( $form ) {
this.$form = $form; this.$form = $form;
this.xhr = false; this.xhr = false;
this.mapping = wc_product_import_params.mapping; this.mapping = wc_product_import_params.mapping;
this.position = 0; this.position = 0;
this.file = wc_product_import_params.file; this.file = wc_product_import_params.file;
this.security = wc_product_import_params.import_nonce; this.skip_existing = wc_product_import_params.skip_existing;
this.security = wc_product_import_params.import_nonce;
// Number of import successes/failures. // Number of import successes/failures.
this.imported = 0; this.imported = 0;
@ -35,11 +36,12 @@
type: 'POST', type: 'POST',
url: ajaxurl, url: ajaxurl,
data: { data: {
action : 'woocommerce_do_ajax_product_import', action : 'woocommerce_do_ajax_product_import',
position : $this.position, position : $this.position,
mapping : $this.mapping, mapping : $this.mapping,
file : $this.file, file : $this.file,
security : $this.security skip_existing : $this.skip_existing,
security : $this.security
}, },
dataType: 'json', dataType: 'json',
success: function( response ) { success: function( response ) {

View File

@ -1 +1 @@
!function(a,b){var c=function(a){this.$form=a,this.xhr=!1,this.mapping=wc_product_import_params.mapping,this.position=0,this.file=wc_product_import_params.file,this.security=wc_product_import_params.import_nonce,this.imported=0,this.failed=0,this.$form.find(".woocommerce-importer-progress").val(0),this.run_import=this.run_import.bind(this),this.run_import()};c.prototype.run_import=function(){var c=this;a.ajax({type:"POST",url:ajaxurl,data:{action:"woocommerce_do_ajax_product_import",position:c.position,mapping:c.mapping,file:c.file,security:c.security},dataType:"json",success:function(a){a.success&&(c.position=a.data.position,c.imported+=a.data.imported,c.failed+=a.data.failed,c.$form.find(".woocommerce-importer-progress").val(a.data.percentage),"done"===a.data.position?b.location=a.data.url+"&imported="+parseInt(c.imported,10)+"&failed="+parseInt(c.failed,10):c.run_import())}}).fail(function(a){b.console.log(a)})},a.fn.wc_product_importer=function(){return new c(this),this},a(".woocommerce-importer").wc_product_importer()}(jQuery,window); !function(a,b){var c=function(a){this.$form=a,this.xhr=!1,this.mapping=wc_product_import_params.mapping,this.position=0,this.file=wc_product_import_params.file,this.skip_existing=wc_product_import_params.skip_existing,this.security=wc_product_import_params.import_nonce,this.imported=0,this.failed=0,this.$form.find(".woocommerce-importer-progress").val(0),this.run_import=this.run_import.bind(this),this.run_import()};c.prototype.run_import=function(){var c=this;a.ajax({type:"POST",url:ajaxurl,data:{action:"woocommerce_do_ajax_product_import",position:c.position,mapping:c.mapping,file:c.file,skip_existing:c.skip_existing,security:c.security},dataType:"json",success:function(a){a.success&&(c.position=a.data.position,c.imported+=a.data.imported,c.failed+=a.data.failed,c.$form.find(".woocommerce-importer-progress").val(a.data.percentage),"done"===a.data.position?b.location=a.data.url+"&imported="+parseInt(c.imported,10)+"&failed="+parseInt(c.failed,10):c.run_import())}}).fail(function(a){b.console.log(a)})},a.fn.wc_product_importer=function(){return new c(this),this},a(".woocommerce-importer").wc_product_importer()}(jQuery,window);

View File

@ -203,10 +203,11 @@ class WC_Admin_Importers {
$file = wc_clean( $_POST['file'] ); $file = wc_clean( $_POST['file'] );
$params = array( $params = array(
'start_pos' => isset( $_POST['position'] ) ? absint( $_POST['position'] ) : 0, 'start_pos' => isset( $_POST['position'] ) ? absint( $_POST['position'] ) : 0,
'mapping' => isset( $_POST['mapping'] ) ? (array) $_POST['mapping'] : array(), 'mapping' => isset( $_POST['mapping'] ) ? (array) $_POST['mapping'] : array(),
'lines' => apply_filters( 'woocommerce_product_import_batch_size', 10 ), 'skip_existing' => isset( $_POST['skip_existing'] ) ? (bool) $_POST['skip_existing'] : false,
'parse' => true, 'lines' => apply_filters( 'woocommerce_product_import_batch_size', 10 ),
'parse' => true,
); );
$importer = WC_Product_CSV_Importer_Controller::get_importer( $file, $params ); $importer = WC_Product_CSV_Importer_Controller::get_importer( $file, $params );

View File

@ -52,6 +52,13 @@ class WC_Product_CSV_Importer_Controller {
*/ */
protected $delimiter = ','; protected $delimiter = ',';
/**
* Whether to skip existing products.
*
* @var bool
*/
protected $skip_existing = false;
/** /**
* Get importer instance. * Get importer instance.
* *
@ -92,6 +99,7 @@ class WC_Product_CSV_Importer_Controller {
); );
$this->step = isset( $_REQUEST['step'] ) ? sanitize_key( $_REQUEST['step'] ) : current( array_keys( $this->steps ) ); $this->step = isset( $_REQUEST['step'] ) ? sanitize_key( $_REQUEST['step'] ) : current( array_keys( $this->steps ) );
$this->file = isset( $_REQUEST['file'] ) ? wc_clean( $_REQUEST['file'] ) : ''; $this->file = isset( $_REQUEST['file'] ) ? wc_clean( $_REQUEST['file'] ) : '';
$this->skip_existing = isset( $_REQUEST['skip_existing'] ) ? (bool) $_REQUEST['skip_existing'] : false;
} }
/** /**
@ -119,10 +127,11 @@ class WC_Product_CSV_Importer_Controller {
} }
$params = array( $params = array(
'step' => $keys[ $step_index + 1 ], 'step' => $keys[ $step_index + 1 ],
'file' => $this->file, 'file' => $this->file,
'delimiter' => $this->delimiter, 'delimiter' => $this->delimiter,
'_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ), // wp_nonce_url() escapes & to & breaking redirects. 'skip_existing' => $this->skip_existing,
'_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ), // wp_nonce_url() escapes & to & breaking redirects.
); );
return add_query_arg( $params ); return add_query_arg( $params );
@ -300,9 +309,10 @@ class WC_Product_CSV_Importer_Controller {
} }
wp_localize_script( 'wc-product-import', 'wc_product_import_params', array( wp_localize_script( 'wc-product-import', 'wc_product_import_params', array(
'import_nonce' => wp_create_nonce( 'wc-product-import' ), 'import_nonce' => wp_create_nonce( 'wc-product-import' ),
'mapping' => $mapping, 'mapping' => $mapping,
'file' => $this->file, 'file' => $this->file,
'skip_existing' => $this->skip_existing,
) ); ) );
wp_enqueue_script( 'wc-product-import' ); wp_enqueue_script( 'wc-product-import' );

View File

@ -54,6 +54,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<input type="submit" class="button button-primary button-next" value="<?php esc_attr_e( 'Run the importer', 'woocommerce' ); ?>" name="save_step" /> <input type="submit" class="button button-primary button-next" value="<?php esc_attr_e( 'Run the importer', 'woocommerce' ); ?>" name="save_step" />
<input type="hidden" name="file" value="<?php echo esc_attr( $this->file ); ?>" /> <input type="hidden" name="file" value="<?php echo esc_attr( $this->file ); ?>" />
<input type="hidden" name="delimiter" value="<?php echo esc_attr( $this->delimiter ); ?>" /> <input type="hidden" name="delimiter" value="<?php echo esc_attr( $this->delimiter ); ?>" />
<input type="hidden" name="skip_existing" value="<?php echo (int) $this->skip_existing; ?>" />
<?php wp_nonce_field( 'woocommerce-csv-importer' ); ?> <?php wp_nonce_field( 'woocommerce-csv-importer' ); ?>
</div> </div>
</form> </form>

View File

@ -56,6 +56,13 @@ if ( ! defined( 'ABSPATH' ) ) {
<th><label><?php _e( 'CSV Delimiter', 'woocommerce' ); ?></label><br/></th> <th><label><?php _e( 'CSV Delimiter', 'woocommerce' ); ?></label><br/></th>
<td><input type="text" name="delimiter" placeholder="," size="2" /></td> <td><input type="text" name="delimiter" placeholder="," size="2" /></td>
</tr> </tr>
<tr>
<th><label><?php _e( 'Skip existing products', 'woocommerce' ); ?></label><br/></th>
<td>
<input type="hidden" name="skip_existing" value="0" />
<input type="checkbox" name="skip_existing" value="1" />
</td>
</tr>
</tbody> </tbody>
</table> </table>
</section> </section>

View File

@ -138,12 +138,8 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
* @return WC_Product|WC_Error * @return WC_Product|WC_Error
*/ */
protected function process_item( $data ) { protected function process_item( $data ) {
// Ignore IDs and create new products.
// @todo Mike said that we should have something to force create.
$force_create = false;
try { try {
$object = $this->prepare_product( $data, $force_create ); $object = $this->prepare_product( $data );
if ( is_wp_error( $object ) ) { if ( is_wp_error( $object ) ) {
return $object; return $object;
@ -182,11 +178,10 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
* Prepare a single product for create or update. * Prepare a single product for create or update.
* *
* @param array $data Row data. * @param array $data Row data.
* @param bool $creating If should force create a new product.
* @return WC_Product|WP_Error * @return WC_Product|WP_Error
*/ */
protected function prepare_product( $data, $force_create = false ) { protected function prepare_product( $data ) {
$id = ! $force_create && isset( $data['id'] ) ? absint( $data['id'] ) : 0; $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. // Type is the most important part here because we need to be using the correct class and methods.
if ( isset( $data['type'] ) ) { if ( isset( $data['type'] ) ) {

View File

@ -31,12 +31,13 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
*/ */
public function __construct( $file, $params = array() ) { public function __construct( $file, $params = array() ) {
$default_args = array( $default_args = array(
'start_pos' => 0, // File pointer start. 'start_pos' => 0, // File pointer start.
'end_pos' => -1, // File pointer end. 'end_pos' => -1, // File pointer end.
'lines' => -1, // Max lines to read. 'lines' => -1, // Max lines to read.
'mapping' => array(), // Column mapping. csv_heading => schema_heading. 'mapping' => array(), // Column mapping. csv_heading => schema_heading.
'parse' => false, // Whether to sanitize and format data. 'parse' => false, // Whether to sanitize and format data.
'delimiter' => ',', // CSV delimiter. 'skip_existing' => false, // Whether to skip existing items.
'delimiter' => ',', // CSV delimiter.
); );
$this->params = wp_parse_args( $params, $default_args ); $this->params = wp_parse_args( $params, $default_args );
@ -270,6 +271,21 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
); );
foreach ( $this->parsed_data as $parsed_data ) { foreach ( $this->parsed_data as $parsed_data ) {
// Don't import products with IDs or SKUs that already exist if option is true.
if ( $this->params['skip_existing'] ) {
$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['failed'][] = new WP_Error( 'woocommerce_product_csv_importer_error', __( 'A product with this ID already exists.', 'woocommerce' ), array( 'id' => $id ) );
continue;
} elseif( $sku && wc_get_product_id_by_sku( $sku ) ) {
$data['failed'][] = new WP_Error( 'woocommerce_product_csv_importer_error', __( 'A product with this SKU already exists.', 'woocommerce' ), array( 'sku' => $sku ) );
continue;
}
}
$result = $this->process_item( $parsed_data ); $result = $this->process_item( $parsed_data );
if ( is_wp_error( $result ) ) { if ( is_wp_error( $result ) ) {