diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php index 13bd281..b727a4c 100644 --- a/core/lib/Drupal/Core/Entity/EntityNG.php +++ b/core/lib/Drupal/Core/Entity/EntityNG.php @@ -558,6 +558,9 @@ protected function initializeTranslation($langcode) { * {@inheritdoc} */ public function hasTranslation($langcode) { + if ($langcode == $this->language->id) { + $langcode = Language::LANGCODE_DEFAULT; + } return !empty($this->translations[$langcode]['status']); } @@ -603,10 +606,10 @@ public function addTranslation($langcode, array $values = array()) { */ public function removeTranslation($langcode) { if (isset($this->translations[$langcode]) && $langcode != Language::LANGCODE_DEFAULT && $langcode != $this->getDefaultLanguage()->id) { - foreach ($this->getPropertyDefinitions() as $definition) { + foreach ($this->getPropertyDefinitions() as $name => $definition) { if (!empty($definition['translatable'])) { - unset($this->values[$langcode]); - unset($this->fields[$langcode]); + unset($this->values[$name][$langcode]); + unset($this->fields[$name][$langcode]); } } $this->translations[$langcode]['status'] = static::TRANSLATION_REMOVED; @@ -714,6 +717,11 @@ public function __set($name, $value) { elseif ($this->getPropertyDefinition($name)) { $this->getTranslatedField($name, $this->activeLangcode)->setValue($value); } + // The translations array is unset when cloning the entity object, we just + // need to restore it. + elseif ($name == 'translations') { + $this->translations = $value; + } // Else directly read/write plain values. That way, fields not yet converted // to the entity field API can always be directly accessed. else { @@ -784,7 +792,15 @@ public function __clone() { $this->fields[$name][$langcode]->setContext($name, $this); } } + + // Ensure the translations array is actually cloned by removing the + // original reference and re-creating its values. $this->clearTranslationCache(); + $translations = $this->translations; + unset($this->translations); + // This will trigger the magic setter as the translations array is + // undefined now. + $this->translations = $translations; } } diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php index ffef8e0..42c4357 100644 --- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php +++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php @@ -60,9 +60,12 @@ protected function loadFieldItems(array $entities, $age) { $cid = "field:{$this->entityType}:$id"; if (isset($cache[$cid])) { unset($queried_entities[$id]); - foreach ($cache[$cid]->data as $field_name => $values) { - foreach ($values as $langcode => $items) { - $entity->getTranslation($langcode)->$field_name = $items; + foreach ($cache[$cid]->data as $langcode => $values) { + $translation = $entity->getTranslation($langcode); + // We do not need to worry about field translatability here, the + // translation object will manage that automatically. + foreach ($values as $field_name => $items) { + $translation->$field_name = $items; } } } @@ -85,8 +88,9 @@ protected function loadFieldItems(array $entities, $age) { $data = array(); $instances = field_info_instances($this->entityType, $entity->bundle()); foreach ($entity->getTranslationLanguages() as $langcode => $language) { + $translation = $entity->getTranslation($langcode); foreach ($instances as $instance) { - $data[$instance['field_name']][$langcode] = $entity->getTranslation($langcode)->{$instance['field_name']}->getValue(TRUE); + $data[$langcode][$instance['field_name']] = $translation->{$instance['field_name']}->getValue(TRUE); } } $cid = "field:{$this->entityType}:$id"; diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index d93ece4..f12872b 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -637,18 +637,7 @@ function content_translation_field_language_alter(&$display_language, $context) foreach ($entity->translation as $langcode => $translation) { if ($langcode == $context['langcode'] || !content_translation_view_access($entity, $langcode)) { - // Unset unaccessible field translations: if the field is untranslatable - // unsetting a language different from Language::LANGCODE_NOT_SPECIFIED has no - // effect. - foreach ($instances as $instance) { - // @todo BC entities have the same value accessibile both with the - // entity language and with Language::LANGCODE_DEFAULT. We need need to unset - // both until we remove the BC layer. - if ($langcode == $entity_langcode) { - unset($entity->{$instance['field_name']}[Language::LANGCODE_DEFAULT]); - } - unset($entity->{$instance['field_name']}[$langcode]); - } + $entity->removeTranslation($langcode); } } diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc index 31bb0ce..432677e 100644 --- a/core/modules/field/field.multilingual.inc +++ b/core/modules/field/field.multilingual.inc @@ -89,16 +89,18 @@ function field_language_fallback(&$field_langcodes, EntityInterface $entity, $la foreach ($field_langcodes as $field_name => $field_langcode) { // If the requested language is defined for the current field use it, // otherwise search for a fallback value among the fallback candidates. - if (isset($entity->{$field_name}[$langcode])) { + $value = $entity->hasTranslation($langcode) ? $entity->getTranslation($langcode)->$field_name->getValue() : NULL; + if (!empty($value) && is_array($value) && array_filter($value, function($value) { return array_filter($value); })) { $field_langcodes[$field_name] = $langcode; } - elseif (!empty($entity->{$field_name})) { + else { if (!isset($fallback_candidates)) { require_once DRUPAL_ROOT . '/core/includes/language.inc'; $fallback_candidates = language_fallback_get_candidates(); } foreach ($fallback_candidates as $fallback_langcode) { - if (isset($entity->{$field_name}[$fallback_langcode])) { + $value = $entity->hasTranslation($fallback_langcode) ? $entity->getTranslation($fallback_langcode)->$field_name->getValue() : NULL; + if (!empty($value) && is_array($value) && array_filter($value, function($value) { return array_filter($value); })) { $field_langcodes[$field_name] = $fallback_langcode; break; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php index dc2fb1f..ec480a7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -380,7 +380,7 @@ function testEntityTranslationAPI() { $entity->name->value = $name; $name_translated = $langcode . '_' . $this->randomName(); $translation = $entity->addTranslation($langcode); - $this->assertNotEqual($entity, $translation, 'The entity and the translation object differ from one another.'); + $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()->id, $langcode, 'The translation language matches the specified one.'); $this->assertEqual($translation->getUntranslated()->language()->id, $default_langcode, 'The original language can still be retrieved.'); @@ -404,7 +404,7 @@ function testEntityTranslationAPI() { // language. $langcode2 = $this->langcodes[2]; $translation = $entity->getTranslation($langcode2); - $value = $entity != $translation && $translation->language()->id == $langcode2 && $entity->hasTranslation($langcode2); + $value = $entity !== $translation && $translation->language()->id == $langcode2 && $entity->hasTranslation($langcode2); $this->assertTrue($value, 'A new translation object can be obtained also by specifying a valid language.'); $this->assertEqual($entity->language()->id, $default_langcode, 'The original language has been preserved.'); $translation->save(); @@ -478,7 +478,7 @@ function testEntityTranslationAPI() { $entity->getTranslation($langcode); $cloned = clone $entity; $translation = $cloned->getTranslation($langcode); - $this->assertNotEqual($entity, $translation->getUntranslated(), 'A cloned entity object has no reference to the original one.'); + $this->assertNotIdentical($entity, $translation->getUntranslated(), 'A cloned entity object has no reference to the original one.'); // Check that per-language defaults are properly populated. $entity = $this->reloadEntity($entity);