diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 7c0a4581de4b373b79a4f438b567a7645436f3d8..a915c0793bae8f18d8280bbb001fd00e53790700 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -9,6 +9,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\Core\TypedData\TranslationStatusInterface; use Drupal\Core\TypedData\TypedDataInterface; /** @@ -16,22 +17,7 @@ * * @ingroup entity_api */ -abstract class ContentEntityBase extends Entity implements \IteratorAggregate, ContentEntityInterface { - - /** - * Status code identifying a removed translation. - */ - const TRANSLATION_REMOVED = 0; - - /** - * Status code identifying an existing translation. - */ - const TRANSLATION_EXISTING = 1; - - /** - * Status code identifying a newly created translation. - */ - const TRANSLATION_CREATED = 2; +abstract class ContentEntityBase extends Entity implements \IteratorAggregate, ContentEntityInterface, TranslationStatusInterface { /** * The plain data values of the contained fields. @@ -220,7 +206,10 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans // Initialize translations. Ensure we have at least an entry for the default // language. - $data = array('status' => static::TRANSLATION_EXISTING); + // We determine if the entity is new by checking in the entity values for + // the presence of the id entity key, as the usage of ::isNew() is not + // possible in the constructor. + $data = isset($values[$this->getEntityType()->getKey('id')]) ? ['status' => static::TRANSLATION_EXISTING] : ['status' => static::TRANSLATION_CREATED]; $this->translations[LanguageInterface::LANGCODE_DEFAULT] = $data; $this->setDefaultLangcode(); if ($translations) { @@ -365,6 +354,25 @@ public function preSave(EntityStorageInterface $storage) { public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) { } + /** + * {@inheritdoc} + */ + public function postSave(EntityStorageInterface $storage, $update = TRUE) { + parent::postSave($storage, $update); + + // Update the status of all saved translations. + $removed = []; + foreach ($this->translations as $langcode => &$data) { + if ($data['status'] == static::TRANSLATION_REMOVED) { + $removed[$langcode] = TRUE; + } + else { + $data['status'] = static::TRANSLATION_EXISTING; + } + } + $this->translations = array_diff_key($this->translations, $removed); + } + /** * {@inheritdoc} */ @@ -829,7 +837,7 @@ public function addTranslation($langcode, array $values = array()) { // Initialize the translation object. /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */ $storage = $this->entityManager()->getStorage($this->getEntityTypeId()); - $this->translations[$langcode]['status'] = static::TRANSLATION_CREATED; + $this->translations[$langcode]['status'] = !isset($this->translations[$langcode]['status_existed']) ? static::TRANSLATION_CREATED : static::TRANSLATION_EXISTING; return $storage->createTranslation($this, $langcode, $values); } @@ -844,13 +852,34 @@ public function removeTranslation($langcode) { unset($this->fields[$name][$langcode]); } } - $this->translations[$langcode]['status'] = static::TRANSLATION_REMOVED; + // If removing a translation which has not been saved yet, then we have + // to remove it completely so that ::getTranslationStatus returns the + // proper status. + if ($this->translations[$langcode]['status'] == static::TRANSLATION_CREATED) { + unset($this->translations[$langcode]); + } + else { + if ($this->translations[$langcode]['status'] == static::TRANSLATION_EXISTING) { + $this->translations[$langcode]['status_existed'] = TRUE; + } + $this->translations[$langcode]['status'] = static::TRANSLATION_REMOVED; + } } else { throw new \InvalidArgumentException("The specified translation ($langcode) cannot be removed."); } } + /** + * {@inheritdoc} + */ + public function getTranslationStatus($langcode) { + if ($langcode == $this->defaultLangcode) { + $langcode = LanguageInterface::LANGCODE_DEFAULT; + } + return isset($this->translations[$langcode]) ? $this->translations[$langcode]['status'] : NULL; + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/TypedData/TranslationStatusInterface.php b/core/lib/Drupal/Core/TypedData/TranslationStatusInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..520b0fcf8dc701b54d3820da531f66d9c1218dc3 --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/TranslationStatusInterface.php @@ -0,0 +1,37 @@ +<?php + +namespace Drupal\Core\TypedData; + +/** + * Defines an interface for checking the status of an entity translation. + */ +interface TranslationStatusInterface { + + /** + * Status code identifying a removed translation. + */ + const TRANSLATION_REMOVED = 0; + + /** + * Status code identifying an existing translation. + */ + const TRANSLATION_EXISTING = 1; + + /** + * Status code identifying a newly created translation. + */ + const TRANSLATION_CREATED = 2; + + /** + * Returns the translation status. + * + * @param string $langcode + * The language code identifying the translation. + * + * @return int|null + * One of the TRANSLATION_* constants or NULL if the given translation does + * not exist. + */ + public function getTranslationStatus($langcode); + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php index b247662a8d408c49e5de7f586e07a7635cd3704b..ed6140da0e6ebc51e1fd7c1bb9bcae0b8cff342b 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php @@ -4,6 +4,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Language\LanguageInterface; +use Drupal\Core\TypedData\TranslationStatusInterface; use Drupal\entity_test\Entity\EntityTestMulRev; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; @@ -931,4 +932,85 @@ public function testDeleteEntityTranslation() { $this->assertEqual($actual, $expected_untranslatable); } + /** + * Tests the getTranslationStatus method. + */ + public function testTranslationStatus() { + $entity_type = 'entity_test_mul'; + $storage = $this->entityManager->getStorage($entity_type); + + // Create an entity with both translatable and untranslatable test fields. + $values = array( + 'name' => $this->randomString(), + 'translatable_test_field' => $this->randomString(), + 'untranslatable_test_field' => $this->randomString(), + ); + + /** @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\TypedData\TranslationStatusInterface $entity */ + // Test that newly created entity has the translation status + // TRANSLATION_CREATED. + $entity = $storage->create($values); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_CREATED, $entity->getTranslationStatus($entity->language()->getId())); + + // Test that after saving a newly created entity it has the translation + // status TRANSLATION_EXISTING. + $entity->save(); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_EXISTING, $entity->getTranslationStatus($entity->language()->getId())); + + // Test that after loading an existing entity it has the translation status + // TRANSLATION_EXISTING. + $storage->resetCache(); + $entity = $storage->load($entity->id()); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_EXISTING, $entity->getTranslationStatus($entity->language()->getId())); + + foreach ($this->langcodes as $key => $langcode) { + // Test that after adding a new translation it has the translation status + // TRANSLATION_CREATED. + $entity->addTranslation($langcode, $values); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_CREATED, $entity->getTranslationStatus($langcode)); + + // Test that after removing a newly added and not yet saved translation + // it does not have any translation status for the removed translation. + $entity->removeTranslation($langcode); + $this->assertEquals(NULL, $entity->getTranslationStatus($langcode)); + + // Test that after adding a new translation and saving the entity it has + // the translation status TRANSLATION_EXISTING. + $entity->addTranslation($langcode, $values) + ->save(); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_EXISTING, $entity->getTranslationStatus($langcode)); + + // Test that after removing an existing translation its translation + // status has changed to TRANSLATION_REMOVED. + $entity->removeTranslation($langcode); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_REMOVED, $entity->getTranslationStatus($langcode)); + + // Test that after removing an existing translation and adding it again + // its translation status has changed back to TRANSLATION_EXISTING. + $entity->addTranslation($langcode, $values); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_EXISTING, $entity->getTranslationStatus($langcode)); + + // Test that after removing an existing translation and saving the entity + // it does not have any translation status for the removed translation. + $entity->removeTranslation($langcode); + $entity->save(); + $this->assertEquals(NULL, $entity->getTranslationStatus($langcode)); + + // Tests that after removing an existing translation, saving the entity, + // adding the translation again, the translation status of this + // translation is TRANSLATION_CREATED. + $entity->addTranslation($langcode, $values); + $this->assertEquals(TranslationStatusInterface::TRANSLATION_CREATED, $entity->getTranslationStatus($langcode)); + $entity->save(); + } + + // Test that after loading an existing entity it has the translation status + // TRANSLATION_EXISTING for all of its translations. + $storage->resetCache(); + $entity = $storage->load($entity->id()); + foreach (array_keys($entity->getTranslationLanguages()) as $langcode) { + $this->assertEquals(TranslationStatusInterface::TRANSLATION_EXISTING, $entity->getTranslationStatus($langcode)); + } + } + }