Skip to content

magento/magento2#33031: Add product images import mode for CSV gallery replace - #41088

Open
swnsma wants to merge 4 commits into
magento:2.4-developfrom
swnsma:magento/magento2#33031
Open

magento/magento2#33031: Add product images import mode for CSV gallery replace#41088
swnsma wants to merge 4 commits into
magento:2.4-developfrom
swnsma:magento/magento2#33031

Conversation

@swnsma

@swnsma swnsma commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes #33031 by adding an opt-in Product Images Import Mode (add | replace, default add) on product CSV Add/Update imports, plus an optional Delete Unused Image Files checkbox for Replace mode.

Product Images Import Mode

  • Add (default): append-only; existing gallery images are kept (backward compatible).
  • Replace: when the default-store row includes additional_images, gallery images not listed in that column are unlinked from the product.
  • Image roles (base / small / thumbnail / swatch) are kept when those role columns are omitted from the file.
  • If additional_images is omitted, replace does not drop gallery images.
  • Orphan main gallery rows (catalog_product_entity_media_gallery without product links) are always removed after unlink (core never rebinds them).
  • Physical media files are kept by default after replace.

Delete Unused Image Files (Replace only)

  • Admin checkbox visible only when Products + Add/Update + image mode Replace.
  • When checked: after unlink, media files no longer referenced by any product are deleted from disk, including resized cache via the same RemoveDeletedImagesFromCache path as Admin product image remove.
  • Shared files still used by other products are not deleted.

Implementation notes:

  • Replace planning is deferred across bunches (MediaGalleryReplaceCoordinator + role plan).
  • Gallery removal lives in MediaGalleryCleanup (processor delegates).
  • On media load failure for a SKU, replace unlinks are skipped for that SKU so a re-import can complete safely.

Fixed Issues (if relevant)

  1. Fixes Changing additional_images does not remove old images in csv import (Images not getting replaced) #33031

Manual testing scenarios

  1. Import a product with additional_images (Add/Update, mode Add) → image present.
  2. Re-import the same SKU with a different additional_images value and mode Add → old and new images both present (legacy behaviour).
  3. Re-import with mode Replace and only the new additional image listed → only listed additional remains; role images remain; orphan main gallery rows for unlinked images are removed; physical files remain when Delete Unused Image Files is off.
  4. Same as (3) with Delete Unused Image Files checked → unlinked, unused physical files (and cache) are deleted.
  5. Import with mode Replace but without an additional_images column → gallery extras are not removed.
  6. Admin Import UI: Product Images Import Mode visible only for Products + Add/Update; Delete Unused Image Files visible only when that mode is Replace.

Automated tests / test matrix

Layer What Status (local)
Unit MediaGalleryCleanupTest, MediaGalleryProcessorTest, MediaGalleryReplaceCoordinatorTest Pass (20 tests)
Jasmine product-image-import-mode.test.js (mode + delete-unused visibility helpers) Present
Integration ProductImagesTest (full class, 20 tests) Pass (278 assertions)
MFTF AdminProductImageImportModeVisibilityTest, AdminProductImageDeleteUnusedVisibilityTest Pass
PHPCS Touched production PHP Clean

Edge cases covered

  • Empty additional_images under Replace keeps image roles only; non-role extras unlinked.
  • Missing additional_images column under Replace does not drop gallery.
  • Multi-row / multi-bunch default additional_images keep-set is the union across rows/bunches.
  • Store-scoped role reassignment protects the new role path; old role path can drop when unused.
  • Store-only additional_images without a default-store replace register does not wipe default gallery.
  • External-video gallery entries are not removed by replace.
  • Upload failure on a listed image skips deferred unlinks for that SKU (partial adds may remain; re-import can finish replace).
  • Shared image still linked to another product is not deleted from disk when delete-unused is on.
  • Delete-unused off: physical file remains; orphan main gallery row is still removed.
  • Delete-unused checkbox hidden when image mode is Add, when import behavior is Replace/Delete, or for non-product entities.

Questions or comments

  • Default replace remains file-safe (no filesystem delete) unless the operator opts into Delete Unused Image Files.
  • GraphQL media gallery cache is invalidated via media_gallery_removed_skus on pure-replace removals.

Add an opt-in Import job setting (add|replace, default add) so Add/Update
product imports can remove gallery images not listed in additional_images
without changing default append behaviour. Roles stay protected when their
columns are omitted; replace is a no-op when additional_images is absent.
Includes GraphQL cache invalidation, admin UI visibility, and tests.
@m2-assistant

m2-assistant Bot commented Aug 5, 2026

Copy link
Copy Markdown

Hi @swnsma. Thank you for your contribution!
Here are some useful tips on how you can test your changes using Magento test environment.
❗ Automated tests can be triggered manually with an appropriate comment:

  • @magento run all tests - run or re-run all required tests against the PR changes
  • @magento run <test-build(s)> - run or re-run specific test build(s)
    For example: @magento run Unit Tests

<test-build(s)> is a comma-separated list of build names.

Allowed build names are:
  1. Database Compare
  2. Functional Tests CE
  3. Functional Tests EE
  4. Functional Tests B2B
  5. Integration Tests
  6. Magento Health Index
  7. Sample Data Tests CE
  8. Sample Data Tests EE
  9. Sample Data Tests B2B
  10. Static Tests
  11. Unit Tests
  12. WebAPI Tests
  13. Semantic Version Checker

You can find more information about the builds here
ℹ️ Run only required test builds during development. Run all test builds before sending your pull request for review.


For more details, review the Code Contributions documentation.
Join Magento Community Engineering Slack and ask your questions in #github channel.

Move replace-mode gallery reconciliation into MediaGalleryReplaceCoordinator
and MediaGalleryReplaceRolePlan so Product import only drives the lifecycle
(configure, plan roles, warm cache, register, keep paths, deferred removals).

Protect multi-store image roles using pre-import values and CSV reassignments,
accumulate keep/plan across bunches, unlink only after the full save loop, and
skip unlinks for a SKU when any image upload fails so re-import can complete
safely. Expand unit, integration, and MFTF coverage for these behaviours.
@swnsma
swnsma force-pushed the magento/magento2#33031 branch from d9f3335 to 5f24e0d Compare August 7, 2026 05:48
…port

When Product Images Import Mode is Replace, always drop orphan main gallery
rows after unlinking. Optionally delete unused media files and resized cache
via a Replace-only admin checkbox, matching Admin product image removal.
@swnsma
swnsma marked this pull request as ready for review August 7, 2026 07:11
Batch gallery path usage counts, reject path traversal before filesystem
delete, and catch FileSystemException only when removing unused media.
@engcom-Bravo engcom-Bravo added the Priority: P2 A defect with this priority could have functionality issues which are not to expectations. label Aug 7, 2026
@github-project-automation github-project-automation Bot moved this to Pending Review in Pull Requests Dashboard Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority: P2 A defect with this priority could have functionality issues which are not to expectations. Progress: pending review

Projects

Status: Pending Review

Development

Successfully merging this pull request may close these issues.

Changing additional_images does not remove old images in csv import (Images not getting replaced)

2 participants