diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 9802c3df8da989a3ee925ef9fda7d998c160bee4..fb77dee8c2e6c8253f2286ffad90b99b54c15378 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -86,6 +86,18 @@ abstract class ContentEntityBase extends EntityBase implements \IteratorAggregat */ protected $activeLangcode = LanguageInterface::LANGCODE_DEFAULT; + /** + * Override the result of isDefaultTranslation(). + * + * Under certain circumstances, such as when changing default translation, the + * default value needs to be overridden. + * + * @var bool|null + * + * @internal + */ + protected ?bool $enforceDefaultTranslation = NULL; + /** * Local cache for the default language code. * @@ -409,10 +421,28 @@ public function setRevisionTranslationAffectedEnforced($enforced) { return $this; } + /** + * Set or clear an override of the isDefaultTranslation() result. + * + * @param bool|null $enforce_default_translation + * If boolean value is passed, the value will override the result of + * isDefaultTranslation() method. If NULL is passed, the default logic will + * be used. + * + * @return $this + */ + public function setDefaultTranslationEnforced(?bool $enforce_default_translation): static { + $this->enforceDefaultTranslation = $enforce_default_translation; + return $this; + } + /** * {@inheritdoc} */ public function isDefaultTranslation() { + if ($this->enforceDefaultTranslation !== NULL) { + return $this->enforceDefaultTranslation; + } return $this->activeLangcode === LanguageInterface::LANGCODE_DEFAULT; } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index 9a5d079439900fb9a42c31b976c852e44f0a10b4..930694c28dce4f5822628a9abd8ac0052593f1bf 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -944,7 +944,21 @@ protected function invokeFieldMethod($method, ContentEntityInterface $entity) { if ($method == 'postSave' && !empty($entity->original)) { $original_langcodes = array_keys($entity->original->getTranslationLanguages()); foreach (array_diff($original_langcodes, $langcodes) as $removed_langcode) { + /** @var \Drupal\Core\Entity\ContentEntityInterface $translation */ $translation = $entity->original->getTranslation($removed_langcode); + + // Fields may rely on the isDefaultTranslation() method to determine + // what is going to be deleted - the whole entity or a particular + // translation. + if ($translation->isDefaultTranslation()) { + if (method_exists($translation, 'setDefaultTranslationEnforced')) { + $translation->setDefaultTranslationEnforced(FALSE); + } + else { + @trigger_error('Not providing a setDefaultTranslationEnforced() method when implementing \Drupal\Core\TypedData\TranslatableInterface is deprecated in drupal:10.2.0 and is required from drupal:11.0.0. See https://www.drupal.org/node/3376146', E_USER_DEPRECATED); + } + } + $fields = $translation->getTranslatableFields(); foreach ($fields as $name => $items) { $items->delete(); diff --git a/core/modules/file/tests/src/Functional/FileOnTranslatedEntityTest.php b/core/modules/file/tests/src/Functional/FileOnTranslatedEntityTest.php index 41df460cb71483f60a5d8c11843d7f752c8540c8..969b20643e31d9af159c84cf418282aed7812290 100644 --- a/core/modules/file/tests/src/Functional/FileOnTranslatedEntityTest.php +++ b/core/modules/file/tests/src/Functional/FileOnTranslatedEntityTest.php @@ -215,4 +215,40 @@ public function testSyncedFiles() { $this->assertTrue($file->isTemporary()); } + /** + * Tests if file field tracks file usages correctly on translated nodes. + */ + public function testFileUsage() { + /** @var \Drupal\file\FileUsage\FileUsageInterface $file_usage */ + $file_usage = \Drupal::service('file.usage'); + + // Enable language selector on the page edit form. + $edit = [ + 'language_configuration[language_alterable]' => 1, + ]; + $this->drupalGet('admin/structure/types/manage/page'); + $this->submitForm($edit, 'Save'); + + // Create a node and upload a file. + $node = $this->drupalCreateNode(['type' => 'page']); + $edit = [ + 'files[' . $this->fieldName . '_0]' => \Drupal::service('file_system')->realpath($this->drupalGetTestFiles('text')[0]->uri), + ]; + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->submitForm($edit, 'Save'); + + // Check if the file usage is correct. + $file = File::load($this->getLastFileId()); + $this->assertEquals($file_usage->listUsage($file), ['file' => ['node' => [$node->id() => '1']]]); + + // Check if the file usage is tracked correctly when changing the original + // language of an entity. + $edit = [ + 'langcode[0][value]' => 'fr', + ]; + $this->drupalGet('node/' . $node->id() . '/edit'); + $this->submitForm($edit, 'Save'); + $this->assertEquals($file_usage->listUsage($file), ['file' => ['node' => [$node->id() => '1']]]); + } + } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php index d1a0520b27125b3e16cdb31d74fb76a2368e03fb..74cbc4eeff4a6e7ad07c44fe9749b19920b079c3 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php @@ -297,7 +297,7 @@ public function testEntityPropertiesModifications() { // Retrieve the entity properties. $reflection = new \ReflectionClass($entity); $properties = $reflection->getProperties(~\ReflectionProperty::IS_STATIC); - $translation_unique_properties = ['activeLangcode', 'translationInitialize', 'fieldDefinitions', 'languages', 'langcodeKey', 'defaultLangcode', 'defaultLangcodeKey', 'revisionTranslationAffectedKey', 'validated', 'validationRequired', 'entityTypeId', 'typedData', 'cacheContexts', 'cacheTags', 'cacheMaxAge', '_serviceIds', '_entityStorages']; + $translation_unique_properties = ['activeLangcode', 'translationInitialize', 'fieldDefinitions', 'languages', 'langcodeKey', 'defaultLangcode', 'defaultLangcodeKey', 'revisionTranslationAffectedKey', 'validated', 'validationRequired', 'entityTypeId', 'typedData', 'cacheContexts', 'cacheTags', 'cacheMaxAge', '_serviceIds', '_entityStorages', 'enforceDefaultTranslation']; foreach ($properties as $property) { // Modify each entity property on the clone and assert that the change is