From b4c06428e592ff503f89855e1ac5b54251fae95b Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Tue, 2 Jan 2024 11:31:31 +0100
Subject: [PATCH] [BUGFIX] Respect empty alt attribute in ImageViewhelper

The ImageViewhelper does by default add the
`alternative` property as `alt` attribute to
the img tag if no non-empty string is set.

However, there might be valid use cases where
an empty `alt` attribute should be set for such
image - even if the property exists. Such case
is e.g. a thumbnail of such image.

The ImageViewhelper does therefore now render
an empty `alt` attribute if the `alt` argument
is explicitly set to an empty string.

Resolves: #102739
Releases: main, 12.4
Change-Id: Id197c3d2f839fbbc37744a2ae356c8e3ae35f0c4
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/82251
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
---
 .../Classes/ViewHelpers/ImageViewHelper.php   |  8 +++-
 .../ViewHelpers/ImageViewHelperTest.php       | 41 +++++++++++++++++++
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/ImageViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/ImageViewHelper.php
index 66502f417a92..2fab5aa383e9 100644
--- a/typo3/sysext/fluid/Classes/ViewHelpers/ImageViewHelper.php
+++ b/typo3/sysext/fluid/Classes/ViewHelpers/ImageViewHelper.php
@@ -203,8 +203,12 @@ final class ImageViewHelper extends AbstractTagBasedViewHelper
             $this->tag->addAttribute('width', $processedImage->getProperty('width'));
             $this->tag->addAttribute('height', $processedImage->getProperty('height'));
 
-            // The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty
-            if (empty($this->arguments['alt'])) {
+            if (is_string($this->arguments['alt'] ?? false) && $this->arguments['alt'] === '') {
+                // In case the "alt" attribute is explicitly set to an empty string, respect
+                // this to allow excluding it from screen readers, improving accessibility.
+                $this->tag->addAttribute('alt', '');
+            } elseif (empty($this->arguments['alt'])) {
+                // The alt-attribute is mandatory to have valid html-code, therefore use "alternative" property or empty
                 $this->tag->addAttribute('alt', $image->hasProperty('alternative') ? $image->getProperty('alternative') : '');
             }
             // Add title-attribute from property if not already set and the property is not an empty string
diff --git a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php
index 22a4a988f66a..a970a94f1367 100644
--- a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php
+++ b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/ImageViewHelperTest.php
@@ -19,8 +19,13 @@ namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers;
 
 use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder;
 use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\Index\MetaDataRepository;
+use TYPO3\CMS\Core\Resource\ResourceFactory;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Mvc\ExtbaseRequestParameters;
 use TYPO3\CMS\Extbase\Mvc\Request;
+use TYPO3\CMS\Extbase\Service\ImageService;
 use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextFactory;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
@@ -182,4 +187,40 @@ final class ImageViewHelperTest extends FunctionalTestCase
         $context->getTemplatePaths()->setTemplateSource($template);
         self::assertMatchesRegularExpression($expected, (new TemplateView($context))->render());
     }
+
+    /**
+     * @test
+     */
+    public function renderReturnsCorrectAltAttribute(): void
+    {
+        $imageServiceMock = $this->createMock(ImageService::class);
+        $imageServiceMock->method('getImage')->willReturn(new File([], $this->get(ResourceFactory::class)->getDefaultStorage()));
+
+        $metaDataRepository = $this->createMock(MetaDataRepository::class);
+        $metaDataRepository->method('findByFileUid')->willReturn(['alternative' => 'alt text']);
+
+        GeneralUtility::setSingletonInstance(ImageService::class, $imageServiceMock);
+        GeneralUtility::setSingletonInstance(MetaDataRepository::class, $metaDataRepository);
+
+        $context = $this->get(RenderingContextFactory::class)->create();
+
+        // No alt attribute given - use metadata
+        $context->getTemplatePaths()->setTemplateSource(
+            '<f:image src="EXT:fluid/Tests/Functional/Fixtures/ViewHelpers/ImageViewHelperTest.jpg" />'
+        );
+        self::assertStringContainsString(
+            'alt="alt text"',
+            (new TemplateView($context))->render()
+        );
+
+        // Enforce empty alt attribute - omit metadata fallback
+        $context->getTemplatePaths()->setTemplateSource(
+            '<f:image src="EXT:fluid/Tests/Functional/Fixtures/ViewHelpers/ImageViewHelperTest.jpg" alt="" />'
+        );
+        self::assertStringContainsString(
+            'alt=""',
+            (new TemplateView($context))->render()
+        );
+
+    }
 }
-- 
GitLab