Loading core/modules/content_translation/src/Controller/ContentTranslationController.php +7 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ use Drupal\Core\Link; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\RedirectResponse; /** * Base class for entity translation controllers. Loading Loading @@ -362,7 +363,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL * @param string $entity_type_id * (optional) The entity type ID. * * @return array * @return array|\Symfony\Component\HttpFoundation\RedirectResponse * A processed form array ready to be rendered. */ public function add(LanguageInterface $source, LanguageInterface $target, RouteMatchInterface $route_match, $entity_type_id = NULL) { Loading @@ -381,6 +382,11 @@ public function add(LanguageInterface $source, LanguageInterface $target, RouteM } } if ($entity->hasTranslation($target->getId())) { $this->messenger()->addError($this->t('A translation already exists for @language.', ['@language' => $target->getName()])); return new RedirectResponse($entity->getTranslation($target->getId())->toUrl('edit-form')->toString()); } // @todo Exploit the upcoming hook_entity_prepare() when available. // See https://www.drupal.org/node/1810394. $entity = clone $entity; Loading core/modules/content_translation/tests/src/Kernel/ContentTranslationConcurrencyTest.php 0 → 100644 +89 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Tests\content_translation\Kernel; use Drupal\content_translation\Controller\ContentTranslationController; use Drupal\Core\Routing\RouteMatch; use Drupal\entity_test\Entity\EntityTestMul; use Drupal\entity_test\Entity\EntityTestMulBundle; use Drupal\KernelTests\KernelTestBase; use Drupal\language\Entity\ConfigurableLanguage; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\Route; /** * Tests concurrency in the content translation controller. */ #[CoversClass(ContentTranslationController::class)] #[Group('content_translation')] #[RunTestsInSeparateProcesses] class ContentTranslationConcurrencyTest extends KernelTestBase { /** * {@inheritdoc} */ protected static $modules = ['system', 'language', 'content_translation', 'user', 'entity_test']; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); $this->installEntitySchema('entity_test_mul'); $this->installEntitySchema('entity_test_mul_with_bundle'); EntityTestMulBundle::create([ 'id' => 'test', 'label' => 'Test label', 'description' => 'My test description', ])->save(); ConfigurableLanguage::createFromLangcode('fr')->save(); } /** * Tests that adding a translation that already exists redirects gracefully. */ public function testConcurrentTranslationAddition(): void { $language_manager = $this->container->get('language_manager'); $source_lang = $language_manager->getLanguage('en'); $target_lang = $language_manager->getLanguage('fr'); $entity = EntityTestMul::create([ 'name' => 'English Entity', 'type' => 'test', 'langcode' => 'en', ]); $entity->save(); $entity->addTranslation('fr', ['name' => 'French Entity'])->save(); $controller = ContentTranslationController::create($this->container); $route_match = new RouteMatch( 'entity.entity_test_mul.content_translation_add', new Route('/entity-test-mul/{entity_test_mul}/translations/add/{source}/{target}'), ['entity_test_mul' => $entity], ['entity_test_mul' => $entity->id()] ); $response = $controller->add($source_lang, $target_lang, $route_match, 'entity_test_mul'); $this->assertInstanceOf(RedirectResponse::class, $response); $expected = $entity->toUrl('edit-form')->toString(); $this->assertStringContainsString($expected, $response->getTargetUrl()); /** @var \Drupal\Core\Messenger\MessengerInterface $messenger */ $messenger = $this->container->get('messenger'); $errors = $messenger->messagesByType('error'); $this->assertCount(1, $errors); $actual_message = (string) reset($errors); $this->assertEquals('A translation already exists for French.', $actual_message); } } Loading
core/modules/content_translation/src/Controller/ContentTranslationController.php +7 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ use Drupal\Core\Link; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\RedirectResponse; /** * Base class for entity translation controllers. Loading Loading @@ -362,7 +363,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL * @param string $entity_type_id * (optional) The entity type ID. * * @return array * @return array|\Symfony\Component\HttpFoundation\RedirectResponse * A processed form array ready to be rendered. */ public function add(LanguageInterface $source, LanguageInterface $target, RouteMatchInterface $route_match, $entity_type_id = NULL) { Loading @@ -381,6 +382,11 @@ public function add(LanguageInterface $source, LanguageInterface $target, RouteM } } if ($entity->hasTranslation($target->getId())) { $this->messenger()->addError($this->t('A translation already exists for @language.', ['@language' => $target->getName()])); return new RedirectResponse($entity->getTranslation($target->getId())->toUrl('edit-form')->toString()); } // @todo Exploit the upcoming hook_entity_prepare() when available. // See https://www.drupal.org/node/1810394. $entity = clone $entity; Loading
core/modules/content_translation/tests/src/Kernel/ContentTranslationConcurrencyTest.php 0 → 100644 +89 −0 Original line number Diff line number Diff line <?php declare(strict_types=1); namespace Drupal\Tests\content_translation\Kernel; use Drupal\content_translation\Controller\ContentTranslationController; use Drupal\Core\Routing\RouteMatch; use Drupal\entity_test\Entity\EntityTestMul; use Drupal\entity_test\Entity\EntityTestMulBundle; use Drupal\KernelTests\KernelTestBase; use Drupal\language\Entity\ConfigurableLanguage; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\Route; /** * Tests concurrency in the content translation controller. */ #[CoversClass(ContentTranslationController::class)] #[Group('content_translation')] #[RunTestsInSeparateProcesses] class ContentTranslationConcurrencyTest extends KernelTestBase { /** * {@inheritdoc} */ protected static $modules = ['system', 'language', 'content_translation', 'user', 'entity_test']; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); $this->installEntitySchema('entity_test_mul'); $this->installEntitySchema('entity_test_mul_with_bundle'); EntityTestMulBundle::create([ 'id' => 'test', 'label' => 'Test label', 'description' => 'My test description', ])->save(); ConfigurableLanguage::createFromLangcode('fr')->save(); } /** * Tests that adding a translation that already exists redirects gracefully. */ public function testConcurrentTranslationAddition(): void { $language_manager = $this->container->get('language_manager'); $source_lang = $language_manager->getLanguage('en'); $target_lang = $language_manager->getLanguage('fr'); $entity = EntityTestMul::create([ 'name' => 'English Entity', 'type' => 'test', 'langcode' => 'en', ]); $entity->save(); $entity->addTranslation('fr', ['name' => 'French Entity'])->save(); $controller = ContentTranslationController::create($this->container); $route_match = new RouteMatch( 'entity.entity_test_mul.content_translation_add', new Route('/entity-test-mul/{entity_test_mul}/translations/add/{source}/{target}'), ['entity_test_mul' => $entity], ['entity_test_mul' => $entity->id()] ); $response = $controller->add($source_lang, $target_lang, $route_match, 'entity_test_mul'); $this->assertInstanceOf(RedirectResponse::class, $response); $expected = $entity->toUrl('edit-form')->toString(); $this->assertStringContainsString($expected, $response->getTargetUrl()); /** @var \Drupal\Core\Messenger\MessengerInterface $messenger */ $messenger = $this->container->get('messenger'); $errors = $messenger->messagesByType('error'); $this->assertCount(1, $errors); $actual_message = (string) reset($errors); $this->assertEquals('A translation already exists for French.', $actual_message); } }