diff --git a/code/Model/ThumbnailGenerator.php b/code/Model/ThumbnailGenerator.php index 29dc6b2e9..87b5596f1 100644 --- a/code/Model/ThumbnailGenerator.php +++ b/code/Model/ThumbnailGenerator.php @@ -4,9 +4,11 @@ use LogicException; use SilverStripe\Assets\File; +use SilverStripe\Assets\Image_Backend; use SilverStripe\Assets\Storage\AssetContainer; use SilverStripe\Assets\Storage\AssetStore; use SilverStripe\Assets\Storage\DBFile; +use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Configurable; /** @@ -65,6 +67,8 @@ class ThumbnailGenerator */ private static $method = 'FitMax'; + private bool $allowsAnimation = false; + /** * Generate thumbnail and return the "src" property for this thumbnail * @@ -107,9 +111,33 @@ public function generateThumbnail(AssetContainer $file, $width, $height) $file = $file->existingOnly(); } - // Make large thumbnail - $method = $this->config()->get('method'); - return $file->$method($width, $height); + // Disable animation while generating thumbnail + $origAllowAnimation = null; + if (!$this->getAllowsAnimation()) { + if (ClassInfo::hasMethod($file, 'getImageBackend')) { + /** @var Image_Backend $backend */ + $backend = $file->getImageBackend(); + $origAllowAnimation = $backend->getAllowsAnimationInManipulations(); + $backend->setAllowsAnimationInManipulations(false); + } elseif ($file->getIsAnimated() && ClassInfo::hasMethod($file, 'RemoveAnimation')) { + $noAnimation = $file->RemoveAnimation(); + if ($noAnimation) { + $file = $noAnimation; + } + } + } + + try { + // Make large thumbnail + $method = $this->config()->get('method'); + $thumbnail = $file->$method($width, $height); + } finally { + if ($origAllowAnimation !== null) { + $backend->setAllowsAnimationInManipulations($origAllowAnimation); + } + } + + return $thumbnail; } /** @@ -173,4 +201,15 @@ public function setGenerates($generates) $this->generates = $generates; return $this; } + + public function getAllowsAnimation(): bool + { + return $this->allowsAnimation; + } + + public function setAllowsAnimation(bool $allows): static + { + $this->allowsAnimation = $allows; + return $this; + } } diff --git a/tests/php/Forms/fixtures/animated.gif b/tests/php/Forms/fixtures/animated.gif new file mode 100644 index 000000000..e8963f17f Binary files /dev/null and b/tests/php/Forms/fixtures/animated.gif differ diff --git a/tests/php/Model/ThumbnailGeneratorTest.php b/tests/php/Model/ThumbnailGeneratorTest.php index 139d135a6..259e96713 100644 --- a/tests/php/Model/ThumbnailGeneratorTest.php +++ b/tests/php/Model/ThumbnailGeneratorTest.php @@ -116,4 +116,29 @@ public function testGenerateLink() $thumbnail = $generator->generateThumbnailLink($image, 100, 200); $this->assertEquals('/assets/906835357d/TestImage__FitMaxWzEwMCwyMDBd.png', $thumbnail); } + + public function provideAnimatedThumbnail(): array + { + return [ + [true], + [false], + ]; + } + + /** + * @dataProvider provideAnimatedThumbnail + */ + public function testAnimatedThumbnail(bool $allowAnimation): void + { + $image = new Image(); + $image->setFromLocalFile(__DIR__ . '/../Forms/fixtures/animated.gif', 'animated.gif'); + $image->write(); + + ThumbnailGenerator::config()->set('method', 'Fit'); + $generator = new ThumbnailGenerator(); + $generator->setAllowsAnimation($allowAnimation); + $thumbnail = $generator->generateThumbnail($image, 100, 100); + + $this->assertSame($allowAnimation, $thumbnail->getIsAnimated()); + } }