From 75fd8c483064c8b0e87c7d50d59a7eb7d88182ae Mon Sep 17 00:00:00 2001 From: Milorad Markovic Date: Wed, 25 Mar 2026 14:03:29 +0100 Subject: [PATCH] Fix: programmatic image uploads fail when ImageContentInterface::NAME has no extension --- Plugin/ImageProcessorRestrictExtensions.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Plugin/ImageProcessorRestrictExtensions.php b/Plugin/ImageProcessorRestrictExtensions.php index 8cd3431..97b3990 100644 --- a/Plugin/ImageProcessorRestrictExtensions.php +++ b/Plugin/ImageProcessorRestrictExtensions.php @@ -18,6 +18,13 @@ class ImageProcessorRestrictExtensions { private const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png']; + private const MIME_EXTENSION_MAP = [ + 'image/jpg' => 'jpg', + 'image/jpeg' => 'jpg', + 'image/gif' => 'gif', + 'image/png' => 'png', + ]; + /** * @var Uploader */ @@ -34,6 +41,13 @@ public function __construct(Uploader $uploader) /** * Before processImageContent, lock the uploader to image-only extensions. * + * Magento core stores ImageContentInterface::NAME as the filename without + * extension when images are added programmatically (e.g. CLI import via + * Product::addImageToMediaGallery). In this case the Uploader would reject + * the file because an empty extension is not in the allowlist. We derive the + * extension from the MIME type — which Magento has already validated — so that + * both programmatic and form-based uploads pass the extension check. + * * @param ImageProcessor $subject * @param string $entityType * @param ImageContentInterface $imageContent @@ -46,6 +60,13 @@ public function beforeProcessImageContent( $entityType, $imageContent ) { + if (!pathinfo($imageContent->getName(), PATHINFO_EXTENSION)) { + $extension = self::MIME_EXTENSION_MAP[$imageContent->getType()] ?? null; + if ($extension) { + $imageContent->setName($imageContent->getName() . '.' . $extension); + } + } + $this->uploader->setAllowedExtensions(self::ALLOWED_EXTENSIONS); return null; }