From 080985dd1a19499210b0336f8328c1fa784c616b Mon Sep 17 00:00:00 2001
From: Guido Schmechel <guido.schmechel@brandung.de>
Date: Mon, 19 Jul 2021 01:55:12 +0200
Subject: [PATCH] [BUGFIX] Render hreflang only for translated pages
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

If a language in the SiteConfig is set to the fallbackType
free and there is no translated page for this language, then
there is now no generated hreflang.

Previously, a link was always generated with default language
slug, which lead to 404 page not found.

Resolves: #94270
Releases: main, 11.5, 10.4
Change-Id: Ifb261cf388fccbaabbf2bcae9ac805b143c9b42f
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69919
Tested-by: core-ci <typo3@b13.com>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../seo/Classes/HrefLang/HrefLangGenerator.php     |  6 +++++-
 .../Tests/Functional/Fixtures/HrefLangScenario.yml |  5 +++++
 .../Functional/HrefLang/HrefLangGeneratorTest.php  | 14 ++++++++++----
 3 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/typo3/sysext/seo/Classes/HrefLang/HrefLangGenerator.php b/typo3/sysext/seo/Classes/HrefLang/HrefLangGenerator.php
index 741129faa95f..a77c254d4bcf 100644
--- a/typo3/sysext/seo/Classes/HrefLang/HrefLangGenerator.php
+++ b/typo3/sysext/seo/Classes/HrefLang/HrefLangGenerator.php
@@ -70,8 +70,12 @@ class HrefLangGenerator
         foreach ($languages['languagemenu'] as $language) {
             if (!empty($language['link']) && $language['hreflang']) {
                 $page = $this->getTranslatedPageRecord($pageId, $language['languageId'], $site);
+                // do not set hreflang if a page is not translated explicitly
+                if (empty($page)) {
+                    continue;
+                }
+                // do not set hreflang when canonical is set explicitly
                 if (!empty($page['canonical_link'])) {
-                    // do not set hreflang when canonical is set
                     continue;
                 }
 
diff --git a/typo3/sysext/seo/Tests/Functional/Fixtures/HrefLangScenario.yml b/typo3/sysext/seo/Tests/Functional/Fixtures/HrefLangScenario.yml
index 881af1154fa5..e987d52e2fb1 100644
--- a/typo3/sysext/seo/Tests/Functional/Fixtures/HrefLangScenario.yml
+++ b/typo3/sysext/seo/Tests/Functional/Fixtures/HrefLangScenario.yml
@@ -32,6 +32,8 @@ entitySettings:
 entities:
   page:
     - self: {id: *idAcmeRootPage, title: 'EN: Root', root: true, slug: '/'}
+      languageVariants:
+        - self: {id: 1001, title: 'DE-CH: Root', language: 2, slug: '/'}
       children:
         - self: {id: 1100, title: 'EN: Welcome', slug: '/hello'}
           languageVariants:
@@ -53,3 +55,6 @@ entities:
           languageVariants:
             - self: {id: 1501, title: 'DE: Kein hreflang', language: 1, slug: '/kein-hreflang'}
             - self: {id: 1502, title: 'DE-CH: Kein hreflang', language: 2, slug: '/kein-hreflang'}
+        - self: {id: 1600, title: 'EN: No translation', slug: '/no-translation'}
+          languageVariants:
+            - self: {id: 1601, title: 'DK: Hidden page', language: 5, hidden: 1, slug: '/no-translation'}
diff --git a/typo3/sysext/seo/Tests/Functional/HrefLang/HrefLangGeneratorTest.php b/typo3/sysext/seo/Tests/Functional/HrefLang/HrefLangGeneratorTest.php
index 3aa07eaab657..8b939b3c582c 100644
--- a/typo3/sysext/seo/Tests/Functional/HrefLang/HrefLangGeneratorTest.php
+++ b/typo3/sysext/seo/Tests/Functional/HrefLang/HrefLangGeneratorTest.php
@@ -42,6 +42,7 @@ class HrefLangGeneratorTest extends FunctionalTestCase
         'DE-CH' => ['id' => 2, 'title' => 'Swiss German', 'locale' => 'de_CH.UTF8', 'iso' => 'de', 'hrefLang' => 'de-CH', 'direction' => ''],
         'NL' => ['id' => 3, 'title' => 'Dutch', 'locale' => 'nl_NL.UTF8', 'iso' => 'nl', 'hrefLang' => '', 'direction' => ''],
         'FR' => ['id' => 4, 'title' => 'French', 'locale' => 'fr_FR.UTF8', 'iso' => 'fr', 'hrefLang' => 'fr-FR', 'direction' => ''],
+        'DK' => ['id' => 5, 'title' => 'Danish', 'locale' => 'da_DK.UTF8', 'iso' => 'da', 'hrefLang' => 'da-DK', 'direction' => ''],
     ];
 
     protected function setUp(): void
@@ -57,6 +58,7 @@ class HrefLangGeneratorTest extends FunctionalTestCase
                 $this->buildLanguageConfiguration('DE-CH', '/de-ch', ['DE'], 'fallback'),
                 $this->buildLanguageConfiguration('NL', '/nl'),
                 $this->buildLanguageConfiguration('FR', '/fr'),
+                $this->buildLanguageConfiguration('DK', '/dk', ['EN'], 'free'),
             ]
         );
 
@@ -64,13 +66,10 @@ class HrefLangGeneratorTest extends FunctionalTestCase
     }
 
     /**
-     * @param string $url
-     * @param array $expected
-     *
      * @test
      * @dataProvider checkHrefLangOutputDataProvider
      */
-    public function checkHrefLangOutput($url, $expectedTags, $notExpectedTags): void
+    public function checkHrefLangOutput(string $url, array $expectedTags, array $notExpectedTags): void
     {
         $this->setUpFrontendRootPage(
             1000,
@@ -206,6 +205,13 @@ class HrefLangGeneratorTest extends FunctionalTestCase
                     '<link rel="alternate" hreflang="',
                 ],
             ],
+            'Languages with fallback type free should not have hreflang when page record is not translated' => [
+                'https://acme.com/no-translation',
+                [],
+                [
+                    '<link rel="alternate" hreflang="',
+                ],
+            ],
         ];
     }
 
-- 
GitLab