From ccbe4efa870b3f73b68c3c486e4bce038b447e32 Mon Sep 17 00:00:00 2001 From: Alex Tkachev Date: Tue, 14 Jan 2025 21:45:55 +0400 Subject: [PATCH 1/3] test: preview draft-only translations --- .../e2e/specs/drupal/content-editing.spec.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/e2e/specs/drupal/content-editing.spec.ts b/tests/e2e/specs/drupal/content-editing.spec.ts index b90810480..528fb34a3 100644 --- a/tests/e2e/specs/drupal/content-editing.spec.ts +++ b/tests/e2e/specs/drupal/content-editing.spec.ts @@ -23,4 +23,33 @@ test.describe('content-editing', () => { // access await expect(page.locator(':text-is("More settings")')).toHaveCount(0); }); + + test('preview a draft translation', async ({ page }) => { + await page.goto(cmsUrl('/en/entity/create/node/page')); + await page + .getByLabel('Title', { exact: true }) + .fill('Will have a draft translation'); + await page.getByLabel('Save as').selectOption('published'); + await page.getByRole('button', { name: 'Save' }).click(); + await page.getByLabel('Headline').fill('Will have a draft translation'); + await page.getByText('Save', { exact: true }).click(); + await page.getByRole('link', { name: 'Translate' }).click(); + const translateUrl = page.url(); + await page.getByRole('link', { name: 'Add', exact: true }).click(); + await page.getByLabel('Titel', { exact: true }).fill('A draft translation'); + await page.getByLabel('Ändern in').selectOption('draft'); + await page.getByLabel('Headline').fill('A draft translation'); + await page.getByText('Speichern (diese Übersetzung)').click(); + + await page.goto(translateUrl); + await page + .getByRole('link', { name: 'A draft translation', exact: true }) + .click(); + await expect( + page + .frameLocator('iframe') + .first() + .getByRole('heading', { name: 'A draft translation', exact: true }), + ).toBeVisible(); + }); }); From f2e7476142d15980384e6c2a4c93e99f5e31c270 Mon Sep 17 00:00:00 2001 From: Alex Tkachev Date: Tue, 14 Jan 2025 21:46:23 +0400 Subject: [PATCH 2/3] fix: make it possible to preview draft-only translations --- .../EntityLanguageRedirectSubscriber.php | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/packages/drupal/custom/src/EventSubscriber/EntityLanguageRedirectSubscriber.php b/packages/drupal/custom/src/EventSubscriber/EntityLanguageRedirectSubscriber.php index 63016e2af..92d194316 100644 --- a/packages/drupal/custom/src/EventSubscriber/EntityLanguageRedirectSubscriber.php +++ b/packages/drupal/custom/src/EventSubscriber/EntityLanguageRedirectSubscriber.php @@ -36,6 +36,28 @@ public static function getSubscribedEvents() { } public function onKernelRequest(RequestEvent $event): void { + $redirect = $this->getMissingDefaultRevisionRedirect($event) + ?? $this->getMissingTranslationRedirect($event); + if ($redirect) { + // Add the necessary cache contexts to the response, as redirect + // responses are cached as well. + $metadata = new CacheableMetadata(); + $metadata->addCacheContexts([ + 'languages:language_interface', + 'url.query_args', + 'user', + ]); + $redirect->addCacheableDependency($metadata); + if ($this->routeMatch->getRouteName() === 'entity.node.canonical') { + $node = $this->routeMatch->getCurrentRouteMatch()->getParameter('node'); + $redirect->addCacheableDependency($node); + } + + $event->setResponse($redirect); + } + } + + private function getMissingTranslationRedirect(RequestEvent $event): ?TrustedRedirectResponse { // In case the user tries to access a node in a language entity is not // translated to, we redirect to the entity in the original language and // display a warning message. @@ -57,16 +79,43 @@ public function onKernelRequest(RequestEvent $event): void { } $urlString = $url->toString() . '?' . $queryString . 'content_language_not_available=true&requested_language=' . $requestedLanguageId; - // Add the necessary cache contexts to the response, as redirect - // responses are cached as well. - $metadata = new CacheableMetadata(); - $metadata->addCacheContexts(['languages:language_interface', 'url.query_args']); - $response = new TrustedRedirectResponse($urlString); - $response->addCacheableDependency($entity); - $response->addCacheableDependency($metadata); + return new TrustedRedirectResponse($urlString); + } + } - $event->setResponse($response); + return NULL; + } + + private function getMissingDefaultRevisionRedirect(RequestEvent $event): ?TrustedRedirectResponse { + // This spaghetti code detects if user is trying to view a translation that + // is a draft only and does not have a published revision on the canonical + // route. + // Why do we do it: The content translation overview page links to + // `/{lang}/node/{nid}`, but in the case mentioned above, the canonical + // route displays the content in the original language. Which is quite + // confusing. + if ($this->routeMatch->getRouteName() === 'entity.node.canonical') { + $entity = $this->routeMatch->getCurrentRouteMatch()->getParameter('node'); + $requestedLanguageId = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); + if ($entity->language()->getId() != $requestedLanguageId) { + $storage = \Drupal::entityTypeManager()->getStorage('node'); + $latestRevisionId = $storage->getLatestTranslationAffectedRevisionId($entity->id(), $requestedLanguageId); + if ($latestRevisionId) { + /** @var \Drupal\Core\Entity\ContentEntityInterface $latestRevision */ + $latestRevision = $storage->loadRevision($latestRevisionId); + $translations = $latestRevision->getTranslationLanguages(); + if (array_key_exists($requestedLanguageId, $translations)) { + $latestRevision = $latestRevision->getTranslation($requestedLanguageId); + // Bingo! We found the target case. Redirect to the latest revision. + if ($latestRevision->access('view')) { + $url = $latestRevision->toUrl('latest-version')->toString(); + return new TrustedRedirectResponse($url); + } + } + } } } + return NULL; } + } From d15ad0529d8a910493ead7f920a27380c3f4c311 Mon Sep 17 00:00:00 2001 From: Alex Tkachev Date: Wed, 15 Jan 2025 12:34:29 +0400 Subject: [PATCH 3/3] test: make the test compatible with future changes existing on dev --- tests/e2e/specs/drupal/content-editing.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/specs/drupal/content-editing.spec.ts b/tests/e2e/specs/drupal/content-editing.spec.ts index 528fb34a3..ea0d9ce8b 100644 --- a/tests/e2e/specs/drupal/content-editing.spec.ts +++ b/tests/e2e/specs/drupal/content-editing.spec.ts @@ -29,8 +29,8 @@ test.describe('content-editing', () => { await page .getByLabel('Title', { exact: true }) .fill('Will have a draft translation'); - await page.getByLabel('Save as').selectOption('published'); - await page.getByRole('button', { name: 'Save' }).click(); + await page.getByRole('button', { name: /Save|Create/ }).click(); + await page.getByLabel('Change to').selectOption('published'); await page.getByLabel('Headline').fill('Will have a draft translation'); await page.getByText('Save', { exact: true }).click(); await page.getByRole('link', { name: 'Translate' }).click();