Commit f1e54b02 authored by catch's avatar catch
Browse files

fix: #3209204 Adding existing translation throws "Invalid translation language specified"

By: trevorbradley
By: randalv
By: johnwebdev
By: dieterholvoet
By: smustgrave
By: andras_szilagyi
By: claudiu.cristea
(cherry picked from commit e179038b)
parent 3dbf1d02
Loading
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -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.
@@ -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) {
@@ -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;
+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);
  }

}