From cd8293e4cf0f35f943b441419ca2bb17bb37e377 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Mon, 13 Mar 2017 09:59:53 +0000
Subject: [PATCH] Issue #2479377 by dpi, edurenye, tstoeckler, andypost,
 mgifford, RajabNatshah, mglaman, jespermb, hitfactory, meramo, lluvigne,
 amateescu: content_translation_page_attachments() should check for a
 canonical link before generating a URL to it

---
 .../content_translation.module                |   4 +-
 .../Entity/EntityTestTranslatableNoUISkip.php |  12 ++
 .../ContentTranslationLinkTagTest.php         | 137 ++++++++++++++++++
 3 files changed, 151 insertions(+), 2 deletions(-)
 create mode 100644 core/modules/content_translation/tests/src/Functional/ContentTranslationLinkTagTest.php

diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 68fafbf07d5c..7c72b84cb2fb 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -572,10 +572,10 @@ function content_translation_page_attachments(&$page) {
     }
 
     $entity = $route_match->getParameter($name);
-    if ($entity instanceof ContentEntityInterface) {
+    if ($entity instanceof ContentEntityInterface && $entity->hasLinkTemplate('canonical')) {
       // Current route represents a content entity. Build hreflang links.
       foreach ($entity->getTranslationLanguages() as $language) {
-        $url = $entity->urlInfo()
+        $url = $entity->toUrl('canonical')
           ->setOption('language', $language)
           ->setAbsolute()
           ->toString();
diff --git a/core/modules/content_translation/tests/modules/content_translation_test/src/Entity/EntityTestTranslatableNoUISkip.php b/core/modules/content_translation/tests/modules/content_translation_test/src/Entity/EntityTestTranslatableNoUISkip.php
index 6a9f878f1f72..d84257a88792 100644
--- a/core/modules/content_translation/tests/modules/content_translation_test/src/Entity/EntityTestTranslatableNoUISkip.php
+++ b/core/modules/content_translation/tests/modules/content_translation_test/src/Entity/EntityTestTranslatableNoUISkip.php
@@ -10,6 +10,14 @@
  * @ContentEntityType(
  *   id = "entity_test_translatable_no_skip",
  *   label = @Translation("Test entity - Translatable check UI"),
+ *   handlers = {
+ *     "form" = {
+ *       "default" = "Drupal\entity_test\EntityTestForm",
+ *      },
+ *     "route_provider" = {
+ *       "html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
+ *     },
+ *   },
  *   base_table = "entity_test_mul",
  *   data_table = "entity_test_mul_property_data",
  *   entity_keys = {
@@ -20,6 +28,10 @@
  *     "langcode" = "langcode",
  *   },
  *   translatable = TRUE,
+ *   admin_permission = "administer entity_test content",
+ *   links = {
+ *     "edit-form" = "/entity_test_translatable_no_skip/{entity_test_translatable_no_skip}/edit",
+ *   },
  * )
  */
 class EntityTestTranslatableNoUISkip extends EntityTest {
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationLinkTagTest.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationLinkTagTest.php
new file mode 100644
index 000000000000..8ea652e954d3
--- /dev/null
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationLinkTagTest.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace Drupal\Tests\content_translation\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\entity_test\Entity\EntityTestMul;
+use Drupal\content_translation_test\Entity\EntityTestTranslatableNoUISkip;
+
+/**
+ * Tests whether canonical link tags are present for content entities.
+ *
+ * @group content_translation
+ */
+class ContentTranslationLinkTagTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['entity_test', 'content_translation', 'content_translation_test', 'language'];
+
+  /**
+   * The added languages.
+   *
+   * @var string[]
+   */
+  protected $langcodes;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Set up user.
+    $user = $this->drupalCreateUser([
+      'view test entity',
+      'view test entity translations',
+      'administer entity_test content',
+    ]);
+    $this->drupalLogin($user);
+
+    // Add additional languages.
+    $this->langcodes = ['it', 'fr'];
+    foreach ($this->langcodes as $langcode) {
+      ConfigurableLanguage::createFromLangcode($langcode)->save();
+    }
+
+    // Rebuild the container so that the new languages are picked up by services
+    // that hold a list of languages.
+    $this->rebuildContainer();
+  }
+
+  /**
+   * Create a test entity with translations.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   An entity with translations.
+   */
+  protected function createTranslatableEntity() {
+    $entity = EntityTestMul::create(['label' => $this->randomString()]);
+
+    // Create translations for non default languages.
+    foreach ($this->langcodes as $langcode) {
+      $entity->addTranslation($langcode, ['label' => $this->randomString()]);
+    }
+    $entity->save();
+
+    return $entity;
+  }
+
+  /**
+   * Tests alternate link tag found for entity types with canonical links.
+   */
+  public function testCanonicalAlternateTags() {
+    /** @var \Drupal\Core\Language\LanguageManagerInterface $languageManager */
+    $languageManager = $this->container->get('language_manager');
+    /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager */
+    $entityTypeManager = $this->container->get('entity_type.manager');
+
+    $definition = $entityTypeManager->getDefinition('entity_test_mul');
+    $this->assertTrue($definition->hasLinkTemplate('canonical'), 'Canonical link template found for entity_test.');
+
+    $entity = $this->createTranslatableEntity();
+    $url_base = $entity->toUrl('canonical')
+      ->setAbsolute();
+
+    $langcodes_all = $this->langcodes;
+    $langcodes_all[] = $languageManager
+      ->getDefaultLanguage()
+      ->getId();
+
+    /** @var \Drupal\Core\Url[] $urls */
+    $urls = array_map(
+      function ($langcode) use ($url_base, $languageManager) {
+        $url = clone $url_base;
+        return $url
+          ->setOption('language', $languageManager->getLanguage($langcode));
+      },
+      array_combine($langcodes_all, $langcodes_all)
+    );
+
+    // Ensure link tags for all languages are found on each language variation
+    // page of an entity.
+    foreach ($urls as $langcode => $url) {
+      $this->drupalGet($url);
+      foreach ($urls as $langcode_alternate => $url_alternate) {
+        $args = [':href' => $url_alternate->toString(), ':hreflang' => $langcode_alternate];
+        $links = $this->xpath('head/link[@rel = "alternate" and @href = :href and @hreflang = :hreflang]', $args);
+        $message = sprintf('The "%s" translation has the correct alternate hreflang link for "%s": %s.', $langcode, $langcode_alternate, $url->toString());
+        $this->assertTrue(isset($links[0]), $message);
+      }
+    }
+  }
+
+  /**
+   * Tests alternate link tag missing for entity types without canonical links.
+   */
+  public function testCanonicalAlternateTagsMissing() {
+    /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager */
+    $entityTypeManager = $this->container->get('entity_type.manager');
+
+    $definition = $entityTypeManager->getDefinition('entity_test_translatable_no_skip');
+    // Ensure 'canonical' link template does not exist, in case it is added in
+    // the future.
+    $this->assertFalse($definition->hasLinkTemplate('canonical'), 'Canonical link template does not exist for entity_test_translatable_no_skip entity.');
+
+    $entity = EntityTestTranslatableNoUISkip::create();
+    $entity->save();
+    $this->drupalGet($entity->toUrl('edit-form'));
+
+    $this->assertSession()->statusCodeEquals(200);
+    $result = $this->xpath('//link[@rel="alternate" and @hreflang]');
+    $this->assertFalse($result, 'No alternate link tag found.');
+  }
+
+}
-- 
GitLab