Commit f3feeb81b91 for woocommerce
commit f3feeb81b9170d76cb10032f78c9a13c7511bd60
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date: Wed Jul 15 11:34:08 2026 +0300
Fix CSV import dropping gallery images after a leading empty value (#66586)
* Fix CSV import dropping gallery images after a leading empty value
* Add changelog entry for CSV import gallery fix
* fix: treat separators-only images cell as empty to preserve gallery
diff --git a/plugins/woocommerce/changelog/66583-fix-csv-import-leading-empty-gallery-image b/plugins/woocommerce/changelog/66583-fix-csv-import-leading-empty-gallery-image
new file mode 100644
index 00000000000..74c2cdb791e
--- /dev/null
+++ b/plugins/woocommerce/changelog/66583-fix-csv-import-leading-empty-gallery-image
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Import gallery images from a CSV Images cell whose first value is empty (e.g. a value starting with a separator) instead of silently dropping them and leaving the previous gallery in place.
diff --git a/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php b/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php
index 0e506672e9b..4d2c76e1e0e 100644
--- a/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php
+++ b/plugins/woocommerce/includes/import/class-wc-product-csv-importer.php
@@ -884,14 +884,24 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
if ( isset( $data['images'] ) ) {
$images = $data['images'];
$data['raw_image_id'] = array_shift( $images );
+ $gallery = array_filter(
+ $images,
+ function ( $image ) {
+ return '' !== $image;
+ }
+ );
- // When a featured image is provided, treat the remaining values as the full
+ // When any image value is provided, treat the remaining values as the full
// gallery. Setting the key even when no gallery images remain ensures that
// reducing the number of images in the CSV clears previously imported gallery
- // images instead of leaving them in place.
- // See https://github.com/woocommerce/woocommerce/issues/34839.
- if ( ! empty( $data['raw_image_id'] ) ) {
- $data['raw_gallery_image_ids'] = $images;
+ // images instead of leaving them in place, while a fully empty images cell
+ // (including one containing only separators) still leaves existing images
+ // untouched. Gating on the gallery values too keeps them imported when the
+ // featured-image slot is empty (e.g. a cell starting with a separator).
+ // See https://github.com/woocommerce/woocommerce/issues/34839
+ // and https://github.com/woocommerce/woocommerce/issues/66583.
+ if ( ! empty( $data['raw_image_id'] ) || ! empty( $gallery ) ) {
+ $data['raw_gallery_image_ids'] = $gallery;
}
unset( $data['images'] );
}
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/importer/product.php b/plugins/woocommerce/tests/legacy/unit-tests/importer/product.php
index 0254d59ed8c..33436c6dbc9 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/importer/product.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/importer/product.php
@@ -245,7 +245,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
$product->set_gallery_image_ids( array( $gallery_id ) );
$product->save();
- $importer = new WC_Product_CSV_Importer( $this->csv_file, array( 'parse' => false ) );
+ $importer = $this->get_importer();
$expanded = $this->invoke_protected( $importer, 'expand_data', array( array( 'images' => array( $new_url ) ) ) );
$this->invoke_protected( $importer, 'set_image_data', array( &$product, $expanded ) );
@@ -268,7 +268,7 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
$product->set_gallery_image_ids( array( $gallery_id ) );
$product->save();
- $importer = new WC_Product_CSV_Importer( $this->csv_file, array( 'parse' => false ) );
+ $importer = $this->get_importer();
$expanded = $this->invoke_protected( $importer, 'expand_data', array( array( 'images' => array() ) ) );
$this->invoke_protected( $importer, 'set_image_data', array( &$product, $expanded ) );
@@ -276,6 +276,53 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
$this->assertEquals( array( $gallery_id ), $product->get_gallery_image_ids(), 'Existing gallery should be preserved.' );
}
+ /**
+ * @testdox A leading empty value in the images column still imports the remaining gallery images.
+ *
+ * Regression test for https://github.com/woocommerce/woocommerce/issues/66583: an Images cell
+ * like ",gallery.jpg" parses to an empty featured-image value followed by gallery URLs. The
+ * gallery URLs must still be imported (and replace the existing gallery) even though the
+ * featured-image slot is empty.
+ */
+ public function test_leading_empty_image_value_still_imports_gallery_images() {
+ $gallery_url = 'http://example.com/gallery.jpg';
+ $gallery_id = $this->create_sourced_attachment( $gallery_url );
+ $stale_id = $this->create_sourced_attachment( 'http://example.com/stale.jpg' );
+
+ $product = WC_Helper_Product::create_simple_product();
+ $product->set_image_id( $this->create_sourced_attachment( 'http://example.com/featured.jpg' ) );
+ $product->set_gallery_image_ids( array( $stale_id ) );
+ $product->save();
+
+ $importer = $this->get_importer();
+ $expanded = $this->invoke_protected( $importer, 'expand_data', array( array( 'images' => array( '', $gallery_url ) ) ) );
+ $this->invoke_protected( $importer, 'set_image_data', array( &$product, $expanded ) );
+
+ $this->assertEmpty( $product->get_image_id(), 'An empty featured-image value should clear the featured image.' );
+ $this->assertEquals( array( $gallery_id ), $product->get_gallery_image_ids(), 'Gallery images after a leading empty value should replace the existing gallery.' );
+ }
+
+ /**
+ * @testdox An images cell containing only separators leaves the existing gallery untouched.
+ *
+ * An Images cell like "," parses to an array of empty strings. It carries no image values,
+ * so it must behave like a fully empty cell and not wipe the existing gallery.
+ */
+ public function test_separators_only_images_cell_preserves_existing_gallery() {
+ $gallery_id = $this->create_sourced_attachment( 'http://example.com/gallery.jpg' );
+
+ $product = WC_Helper_Product::create_simple_product();
+ $product->set_image_id( $this->create_sourced_attachment( 'http://example.com/featured.jpg' ) );
+ $product->set_gallery_image_ids( array( $gallery_id ) );
+ $product->save();
+
+ $importer = $this->get_importer();
+ $expanded = $this->invoke_protected( $importer, 'expand_data', array( array( 'images' => array( '', '' ) ) ) );
+ $this->invoke_protected( $importer, 'set_image_data', array( &$product, $expanded ) );
+
+ $this->assertEquals( array( $gallery_id ), $product->get_gallery_image_ids(), 'A separators-only images cell should leave the existing gallery untouched.' );
+ }
+
/**
* Create an attachment whose source URL is recorded, so the importer can resolve it by URL.
*