diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 4c9b184..f660457 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -82,6 +82,13 @@ protected $langcodeKey; /** + * The default langcode entity key. + * + * @var string + */ + protected $defaultLangcodeKey; + + /** * Language code identifying the entity active language. * * This is the language field accessors will use to determine which field @@ -144,6 +151,7 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans $this->entityTypeId = $entity_type; $this->entityKeys['bundle'] = $bundle ? $bundle : $this->entityTypeId; $this->langcodeKey = $this->getEntityType()->getKey('langcode'); + $this->defaultLangcodeKey = $this->getEntityType()->getKey('default_langcode'); foreach ($values as $key => $value) { // If the key matches an existing property set the value to the property @@ -560,14 +568,34 @@ public function onChange($name) { } } - // Update the default internal language cache. - if ($name == $this->langcodeKey && $this->isDefaultTranslation()) { - $this->setDefaultLangcode(); - if (isset($this->translations[$this->defaultLangcode])) { - $message = String::format('A translation already exists for the specified language (@langcode).', array('@langcode' => $this->defaultLangcode)); - throw new \InvalidArgumentException($message); - } - $this->updateFieldLangcodes($this->defaultLangcode); + switch ($name) { + case $this->langcodeKey: + if ($this->isDefaultTranslation()) { + // Update the default internal language cache. + $this->setDefaultLangcode(); + if (isset($this->translations[$this->defaultLangcode])) { + $message = String::format('A translation already exists for the specified language (@langcode).', array('@langcode' => $this->defaultLangcode)); + throw new \InvalidArgumentException($message); + } + $this->updateFieldLangcodes($this->defaultLangcode); + } + else { + // @todo Allow the translation language to be changed. See TODO. + $this->get($this->langcodeKey)->setValue($this->activeLangcode, FALSE); + $message = String::format('The translation language cannot be changed (@langcode).', array('@langcode' => $this->activeLangcode)); + throw new \LogicException($message); + } + break; + + case $this->defaultLangcodeKey: + // @todo Use a standard method to make the default_langcode field + // read-only. See TODO. + if (isset($this->values[$this->defaultLangcodeKey])) { + $this->get($this->defaultLangcodeKey)->setValue($this->isDefaultTranslation(), FALSE); + $message = String::format('The default translation flag cannot be changed (@langcode).', array('@langcode' => $this->activeLangcode)); + throw new \LogicException($message); + } + break; } } @@ -702,7 +730,7 @@ public function addTranslation($langcode, array $values = array()) { } } $values[$this->langcodeKey] = $langcode; - $values[$this->getEntityType()->getKey('default_langcode')] = FALSE; + $values[$this->defaultLangcodeKey] = FALSE; $this->translations[$langcode]['status'] = static::TRANSLATION_CREATED; $translation = $this->getTranslation($langcode); @@ -710,7 +738,7 @@ public function addTranslation($langcode, array $values = array()) { foreach ($values as $name => $value) { if (isset($definitions[$name]) && $definitions[$name]->isTranslatable()) { - $translation->$name = $value; + $translation->values[$name][$langcode] = $value; } } diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php index c39b0bb..f646377 100644 --- a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\Entity; use Drupal\Component\Utility\String; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\entity_test\Entity\EntityTestMulRev; use Drupal\language\Entity\ConfigurableLanguage; @@ -147,6 +148,7 @@ public function testMultilingualProperties() { */ protected function doTestMultilingualProperties($entity_type) { $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode'); $name = $this->randomMachineName(); $uid = mt_rand(0, 127); $langcode = $this->langcodes[0]; @@ -249,16 +251,16 @@ protected function doTestMultilingualProperties($entity_type) { $this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities correctly loaded by name.', array('%entity_type' => $entity_type))); // @todo The default language condition should go away in favor of an // explicit parameter. - $entities = entity_load_multiple_by_properties($entity_type, array('name' => $properties[$langcode]['name'][0], 'default_langcode' => 0)); + $entities = entity_load_multiple_by_properties($entity_type, array('name' => $properties[$langcode]['name'][0], $default_langcode_key => 0)); $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name translation.', array('%entity_type' => $entity_type))); $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $default_langcode, 'name' => $name)); $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name and language.', array('%entity_type' => $entity_type))); $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0])); $this->assertEqual(count($entities), 0, format_string('%entity_type: No entity loaded by name translation specifying the translation language.', array('%entity_type' => $entity_type))); - $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0], 'default_langcode' => 0)); + $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0], $default_langcode_key => 0)); $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity loaded by name translation and language specifying to look for translations.', array('%entity_type' => $entity_type))); - $entities = entity_load_multiple_by_properties($entity_type, array('user_id' => $properties[$langcode]['user_id'][0], 'default_langcode' => NULL)); + $entities = entity_load_multiple_by_properties($entity_type, array('user_id' => $properties[$langcode]['user_id'][0], $default_langcode_key => NULL)); $this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities loaded by uid without caring about property translatability.', array('%entity_type' => $entity_type))); // Test property conditions and orders with multiple languages in the same @@ -313,6 +315,9 @@ protected function doTestEntityTranslationAPI($entity_type) { $default_langcode = $this->langcodes[0]; $langcode = $this->langcodes[1]; $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode'); + + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ $entity = $this->entityManager ->getStorage($entity_type) ->create(array('name' => $this->randomMachineName(), $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED)); @@ -324,12 +329,13 @@ protected function doTestEntityTranslationAPI($entity_type) { // Verify that we obtain the entity object itself when we attempt to // retrieve a translation referring to it. $translation = $entity->getTranslation($langcode); - $this->assertEqual($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.'); + $this->assertIdentical($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.'); $entity->{$langcode_key}->value = $default_langcode; $translation = $entity->getTranslation($default_langcode); - $this->assertEqual($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.'); + $this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.'); $translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT); - $this->assertEqual($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.'); + $this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.'); + $this->assertTrue($entity->{$default_langcode_key}->value, 'The translation object is the default one.'); // Create a translation and verify that the translation object and the // original object behave independently. @@ -340,18 +346,47 @@ protected function doTestEntityTranslationAPI($entity_type) { $this->assertNotIdentical($entity, $translation, 'The entity and the translation object differ from one another.'); $this->assertTrue($entity->hasTranslation($langcode), 'The new translation exists.'); $this->assertEqual($translation->language()->getId(), $langcode, 'The translation language matches the specified one.'); + $this->assertEqual($translation->{$langcode_key}->value, $langcode, 'The translation field language value matches the specified one.'); + $this->assertFalse($translation->{$default_langcode_key}->value, 'The translation object is not the default one.'); $this->assertEqual($translation->getUntranslated()->language()->getId(), $default_langcode, 'The original language can still be retrieved.'); $translation->name->value = $name_translated; $this->assertEqual($entity->name->value, $name, 'The original name is retained after setting a translated value.'); $entity->name->value = $name; $this->assertEqual($translation->name->value, $name_translated, 'The translated name is retained after setting the original value.'); - // Save the translation and check that the expecte hooks are fired. + // Save the translation and check that the expected hooks are fired. $translation->save(); $hooks = $this->getHooksInfo(); $this->assertEqual($hooks['entity_translation_insert'], $langcode, 'The generic entity translation insertion hook has fired.'); $this->assertEqual($hooks[$entity_type . '_translation_insert'], $langcode, 'The entity-type-specific entity translation insertion hook has fired.'); + // Verify that changing translation language causes an exception to be + // thrown. + $message = 'The translation language cannot be changed.'; + try { + $translation->{$langcode_key}->value = $this->langcodes[2]; + $this->fail($message); + } + catch (\LogicException $e) { + $this->pass($message); + } + + // Verify that changing the default translation flag causes an exception to + // be thrown. + $message = 'The default translation flag cannot be changed.'; + foreach ($entity->getTranslationLanguages() as $t_langcode => $language) { + $translation = $entity->getTranslation($t_langcode); + $default = $translation->isDefaultTranslation(); + try { + $translation->{$default_langcode_key}->value = !$default; + $this->fail($message); + } + catch (\LogicException $e) { + $this->pass($message); + } + $this->assertEqual($translation->{$default_langcode_key}->value, $default); + } + // Check that after loading an entity the language is the default one. $entity = $this->reloadEntity($entity); $this->assertEqual($entity->language()->getId(), $default_langcode, 'The loaded entity is the original one.'); diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php index 0b5585b..c50f2c7 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php @@ -34,6 +34,7 @@ * "bundle" = "type", * "label" = "name", * "langcode" = "custom_langcode_key", + * "default_langcode" = "custom_default_langcode_key", * }, * links = { * "canonical" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}",