diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 68fafbf07d5c7ec16ce5e0bd810b9b1667acabcd..7c72b84cb2fb420878ab1b0797e3bacc7b6b1a75 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 6a9f878f1f7294ca7cd86295644b19866679195b..d84257a88792eed23e415073df3306d4a3343e6a 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 0000000000000000000000000000000000000000..8ea652e954d30309be25d888c1a3f2569f716377 --- /dev/null +++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationLinkTagTest.php @@ -0,0 +1,137 @@ +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.'); + } + +}