Commit a5db9246 authored by catch's avatar catch

Issue #2639352 by tduong, swentel, Berdir: File records, files themselves lost in translation

parent 3a27d38f
......@@ -440,7 +440,8 @@ protected function invokeHook($hook, EntityInterface $entity) {
protected function invokeFieldMethod($method, ContentEntityInterface $entity) {
$result = [];
$args = array_slice(func_get_args(), 2);
foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
$langcodes = array_keys($entity->getTranslationLanguages());
foreach ($langcodes as $langcode) {
$translation = $entity->getTranslation($langcode);
// For non translatable fields, there is only one field object instance
// across all translations and it has as parent entity the entity in the
......@@ -453,6 +454,20 @@ protected function invokeFieldMethod($method, ContentEntityInterface $entity) {
$result[$langcode][$name] = $args ? call_user_func_array([$items, $method], $args) : $items->{$method}();
}
}
// We need to call the delete method for field items of removed
// translations.
if ($method == 'postSave' && !empty($entity->original)) {
$original_langcodes = array_keys($entity->original->getTranslationLanguages());
foreach (array_diff($original_langcodes, $langcodes) as $removed_langcode) {
$translation = $entity->original->getTranslation($removed_langcode);
$fields = $translation->getTranslatableFields();
foreach ($fields as $name => $items) {
$items->delete();
}
}
}
return $result;
}
......
......@@ -56,9 +56,11 @@ public function postSave($update) {
$original_ids = array();
$langcode = $this->getLangcode();
$original = $entity->original;
$original_items = $original->hasTranslation($langcode) ? $original->getTranslation($langcode)->{$field_name} : $original->{$field_name};
foreach ($original_items as $item) {
$original_ids[] = $item->target_id;
if ($original->hasTranslation($langcode)) {
$original_items = $original->getTranslation($langcode)->{$field_name};
foreach ($original_items as $item) {
$original_ids[] = $item->target_id;
}
}
// Decrement file usage by 1 for files that were removed from the field.
......
<?php
/**
* @file
* Contains \Drupal\file\Tests\FileOnTranslatedEntityTest.
*/
namespace Drupal\file\Tests;
use Drupal\file\Entity\File;
use Drupal\node\Entity\Node;
/**
* Uploads files to translated nodes.
*
* @group file
*/
class FileOnTranslatedEntityTest extends FileFieldTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('language', 'content_translation');
/**
* The name of the file field used in the test.
*
* @var string
*/
protected $fieldName;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create the "Basic page" node type.
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
// Create a file field on the "Basic page" node type.
$this->fieldName = strtolower($this->randomMachineName());
$this->createFileField($this->fieldName, 'node', 'page');
// Create and login user.
$permissions = array(
'access administration pages',
'administer content translation',
'administer content types',
'administer languages',
'create content translations',
'create page content',
'edit any page content',
'translate any entity',
'delete any page content',
);
$admin_user = $this->drupalCreateUser($permissions);
$this->drupalLogin($admin_user);
// Add a second and third language.
$edit = array();
$edit['predefined_langcode'] = 'fr';
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
$edit = array();
$edit['predefined_langcode'] = 'nl';
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
// Enable translation for "Basic page" nodes.
$edit = array(
'entity_types[node]' => 1,
'settings[node][page][translatable]' => 1,
"settings[node][page][fields][$this->fieldName]" => 1,
);
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
\Drupal::entityManager()->clearCachedDefinitions();
}
/**
* Tests synced file fields on translated nodes.
*/
public function testSyncedFiles() {
// Verify that the file field on the "Basic page" node type is translatable.
$definitions = \Drupal::entityManager()->getFieldDefinitions('node', 'page');
$this->assertTrue($definitions[$this->fieldName]->isTranslatable(), 'Node file field is translatable.');
// Create a default language node.
$default_language_node = $this->drupalCreateNode(array('type' => 'page', 'title' => 'Lost in translation'));
// Edit the node to upload a file.
$edit = array();
$name = 'files[' . $this->fieldName . '_0]';
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[0]->uri);
$this->drupalPostForm('node/' . $default_language_node->id() . '/edit', $edit, t('Save'));
$first_fid = $this->getLastFileId();
// Translate the node into French: remove the existing file.
$this->drupalPostForm('node/' . $default_language_node->id() . '/translations/add/en/fr', array(), t('Remove'));
// Upload a different file.
$edit = array();
$edit['title[0][value]'] = 'Bill Murray';
$name = 'files[' . $this->fieldName . '_0]';
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[1]->uri);
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
// This inspects the HTML after the post of the translation, the file
// should be displayed on the original node.
$this->assertRaw('file--mime-text-plain');
$second_fid = $this->getLastFileId();
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
/* @var $file \Drupal\file\FileInterface */
// Ensure the file status of the first file permanent.
$file = File::load($first_fid);
$this->assertTrue($file->isPermanent());
// Ensure the file status of the second file is permanent.
$file = File::load($second_fid);
$this->assertTrue($file->isPermanent());
// Translate the node into dutch: remove the existing file.
$this->drupalPostForm('node/' . $default_language_node->id() . '/translations/add/en/nl', array(), t('Remove'));
// Upload a different file.
$edit = array();
$edit['title[0][value]'] = 'Scarlett Johansson';
$name = 'files[' . $this->fieldName . '_0]';
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[2]->uri);
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
$third_fid = $this->getLastFileId();
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
// Ensure the first file is untouched.
$file = File::load($first_fid);
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
// This inspects the HTML after the post of the translation, the file
// should be displayed on the original node.
$this->assertRaw('file--mime-text-plain');
// Ensure the file status of the second file is permanent.
$file = File::load($second_fid);
$this->assertTrue($file->isPermanent());
// Ensure the file status of the third file is permanent.
$file = File::load($third_fid);
$this->assertTrue($file->isPermanent());
// Edit the second translation: remove the existing file.
$this->drupalPostForm('fr/node/' . $default_language_node->id() . '/edit', array(), t('Remove'));
// Upload a different file.
$edit = array();
$edit['title[0][value]'] = 'David Bowie';
$name = 'files[' . $this->fieldName . '_0]';
$edit[$name] = drupal_realpath($this->drupalGetTestFiles('text')[3]->uri);
$this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
$replaced_second_fid = $this->getLastFileId();
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
// Ensure the first and third files are untouched.
$file = File::load($first_fid);
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
$file = File::load($third_fid);
$this->assertTrue($file->isPermanent());
// Ensure the file status of the replaced second file is permanent.
$file = File::load($replaced_second_fid);
$this->assertTrue($file->isPermanent());
// Ensure the file status of the old second file is now temporary.
$file = File::load($second_fid);
$this->assertTrue($file->isTemporary());
// Delete the third translation.
$this->drupalPostForm('nl/node/' . $default_language_node->id() . '/delete', array(), t('Delete Dutch translation'));
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
// Ensure the first and replaced second files are untouched.
$file = File::load($first_fid);
$this->assertTrue($file->isPermanent(), 'First file still exists and is permanent.');
$file = File::load($replaced_second_fid);
$this->assertTrue($file->isPermanent());
// Ensure the file status of the third file is now temporary.
$file = File::load($third_fid);
$this->assertTrue($file->isTemporary());
// Delete the all translations.
$this->drupalPostForm('node/' . $default_language_node->id() . '/delete', array(), t('Delete all translations'));
\Drupal::entityTypeManager()->getStorage('file')->resetCache();
// Ensure the file status of the all files are now temporary.
$file = File::load($first_fid);
$this->assertTrue($file->isTemporary(), 'First file still exists and is temporary.');
$file = File::load($replaced_second_fid);
$this->assertTrue($file->isTemporary());
}
}
......@@ -10,6 +10,8 @@
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Language\LanguageInterface;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
/**
......@@ -810,4 +812,97 @@ public function testFieldEntityReference() {
}
}
/**
* Tests if entity translation statuses are correct after removing two
* translation.
*/
public function testDeleteEntityTranslation() {
$entity_type = 'entity_test_mul';
$controller = $this->entityManager->getStorage($entity_type);
// Create a translatable test field.
$field_storage = FieldStorageConfig::create([
'entity_type' => $entity_type,
'field_name' => 'translatable_test_field',
'type' => 'field_test',
]);
$field_storage->save();
$field = FieldConfig::create([
'field_storage' => $field_storage,
'label' => $this->randomMachineName(),
'bundle' => $entity_type,
]);
$field->save();
// Create an untranslatable test field.
$field_storage = FieldStorageConfig::create([
'entity_type' => $entity_type,
'field_name' => 'untranslatable_test_field',
'type' => 'field_test',
'translatable' => FALSE,
]);
$field_storage->save();
$field = FieldConfig::create([
'field_storage' => $field_storage,
'label' => $this->randomMachineName(),
'bundle' => $entity_type,
]);
$field->save();
// 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 $entity */
$entity = $controller->create($values);
foreach ($this->langcodes as $langcode) {
$entity->addTranslation($langcode, $values);
}
$entity->save();
// Assert there are no deleted languages in the lists yet.
$this->assertNull(\Drupal::state()->get('entity_test.delete.translatable_test_field'));
$this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field'));
// Remove the second and third langcodes from the entity.
$entity->removeTranslation('l1');
$entity->removeTranslation('l2');
$entity->save();
// Ensure that for the translatable test field the second and third
// langcodes are in the deleted languages list.
$actual = \Drupal::state()->get('entity_test.delete.translatable_test_field');
$expected_translatable = ['l1', 'l2'];
sort($actual);
sort($expected_translatable);
$this->assertEqual($actual, $expected_translatable);
// Ensure that the untranslatable test field is untouched.
$this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field'));
// Delete the entity, which removes all remaining translations.
$entity->delete();
// All languages have been deleted now.
$actual = \Drupal::state()->get('entity_test.delete.translatable_test_field');
$expected_translatable[] = 'en';
$expected_translatable[] = 'l0';
sort($actual);
sort($expected_translatable);
$this->assertEqual($actual, $expected_translatable);
// The untranslatable field is shared and only deleted once, for the
// default langcode.
$actual = \Drupal::state()->get('entity_test.delete.untranslatable_test_field');
$expected_untranslatable = ['en'];
sort($actual);
sort($expected_untranslatable);
$this->assertEqual($actual, $expected_untranslatable);
}
}
......@@ -115,4 +115,14 @@ protected function mustResave() {
return $this->getValue()['value'] == 'resave';
}
/**
* {@inheritdoc}
*/
public function delete() {
parent::delete();
$deleted_languages = \Drupal::state()->get('entity_test.delete.' . $this->getFieldDefinition()->getName()) ?: [];
$deleted_languages[] = $this->getLangcode();
\Drupal::state()->set('entity_test.delete.' . $this->getFieldDefinition()->getName(), $deleted_languages);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment