Commit 4b65a2be authored by alexpott's avatar alexpott

Issue #2675010 by hchonov, Berdir, yongt9412, tstoeckler: Cloned entity will...

Issue #2675010 by hchonov, Berdir, yongt9412, tstoeckler: Cloned entity will point to the same field objects if the clone was created after an entity translation has been initialized
parent 9b7b2ee4
......@@ -1070,6 +1070,12 @@ public function __clone() {
// adapter object.
$this->typedData = NULL;
$definitions = $this->getFieldDefinitions();
// Ensure the fields array is actually cloned by overwriting the original
// reference with one pointing to a copy of the array.
$fields = $this->fields;
$this->fields = &$fields;
foreach ($this->fields as $name => $values) {
$this->fields[$name] = array();
// Untranslatable fields may have multiple references for the same field
......@@ -1091,13 +1097,15 @@ public function __clone() {
$translations = $this->translations;
$this->translations = &$translations;
// Ensure the enforceIsNew property is actually cloned by overwriting the
// original reference with one pointing to a copy of it.
// Ensure that the following properties are actually cloned by
// overwriting the original references with ones pointing to copies of
// them: enforceIsNew, newRevision and loadedRevisionId.
$enforce_is_new = $this->enforceIsNew;
$this->enforceIsNew = &$enforce_is_new;
// Ensure the loadedRevisionId property is actually cloned by
// overwriting the original reference with one pointing to a copy of it.
$new_revision = $this->newRevision;
$this->newRevision = &$new_revision;
$original_revision_id = $this->loadedRevisionId;
$this->loadedRevisionId = &$original_revision_id;
}
......
......@@ -350,10 +350,13 @@ public function setFormDisplay(EntityFormDisplayInterface $form_display, FormSta
* @see \Drupal\Core\Entity\ContentEntityForm::form()
*/
public function updateFormLangcode($entity_type_id, EntityInterface $entity, array $form, FormStateInterface $form_state) {
// Update the form language as it might have changed.
if ($this->isDefaultFormLangcode($form_state)) {
$langcode = $entity->language()->getId();
$form_state->set('langcode', $langcode);
$langcode = $entity->language()->getId();
$form_state->set('langcode', $langcode);
// If this is the original entity language, also update the default
// langcode.
if ($langcode == $entity->getUntranslated()->language()->getId()) {
$form_state->set('entity_default_langcode', $langcode);
}
}
......
......@@ -286,7 +286,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En
if (isset($languages[$form_langcode]) && ($has_translations || $new_translation)) {
$title = $this->entityFormTitle($entity);
// When editing the original values display just the entity label.
if ($form_langcode != $entity_langcode) {
if ($is_translation) {
$t_args = array('%language' => $languages[$form_langcode]->getName(), '%title' => $entity->label(), '@title' => $title);
$title = empty($source_langcode) ? t('@title [%language translation]', $t_args) : t('Create %language translation of %title', $t_args);
}
......
<?php
namespace Drupal\content_translation\Tests;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Tests\NodeTestBase;
/**
* Tests the content translation language that is set.
*
* @group content_translation
*/
class ContentTranslationLanguageChangeTest extends NodeTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'content_translation_test', 'node', 'block', 'field_ui', 'image'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$langcodes = ['de', 'fr'];
foreach ($langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
$this->drupalPlaceBlock('local_tasks_block');
$user = $this->drupalCreateUser(array(
'administer site configuration',
'administer nodes',
'create article content',
'edit any article content',
'delete any article content',
'administer content translation',
'translate any entity',
'create content translations',
'administer languages',
'administer content types',
'administer node fields',
));
$this->drupalLogin($user);
// Enable translation for article.
$edit = [
'entity_types[node]' => TRUE,
'settings[node][article][translatable]' => TRUE,
'settings[node][article][settings][language][language_alterable]' => TRUE,
];
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
// Add an image field.
$this->drupalGet('admin/structure/types/manage/article/fields/add-field');
$edit = [
'new_storage_type' => 'image',
'field_name' => 'image_field',
'label' => 'image_field',
];
$this->drupalPostForm(NULL, $edit, t('Save and continue'));
$this->drupalPostForm(NULL, [], t('Save field settings'));
$this->drupalPostForm(NULL, [], t('Save settings'));
}
/**
* Test that the source language is properly set when changing.
*/
public function testLanguageChange() {
// Create a node in English.
$this->drupalGet('node/add/article');
$edit = [
'title[0][value]' => 'english_title',
];
$this->drupalPostForm(NULL, $edit, t('Save and publish'));
// Create a translation in French.
$this->clickLink('Translate');
$this->clickLink('Add');
$this->drupalPostForm(NULL, [], t('Save and keep published (this translation)'));
$this->clickLink('Translate');
// Edit English translation.
$this->clickLink('Edit');
// Upload and image after changing the node language.
$images = $this->drupalGetTestFiles('image')[1];
$edit = [
'langcode[0][value]' => 'de',
'files[field_image_field_0]' => $images->uri,
];
$this->drupalPostForm(NULL, $edit, t('Upload'));
$this->drupalPostForm(NULL, ['field_image_field[0][alt]' => 'alternative_text'], t('Save and keep published (this translation)'));
// Check that the translation languages are correct.
$node = $this->getNodeByTitle('english_title');
$translation_languages = array_keys($node->getTranslationLanguages());
$this->assertTrue(in_array('fr', $translation_languages));
$this->assertTrue(in_array('de', $translation_languages));
}
/**
* Test that title does not change on ajax call with new language value.
*/
public function testTitleDoesNotChangesOnChangingLanguageWidgetAndTriggeringAjaxCall() {
// Create a node in English.
$this->drupalGet('node/add/article', ['query' => ['test_field_only_en_fr' => 1]]);
$edit = [
'title[0][value]' => 'english_title',
'test_field_only_en_fr' => 'node created',
];
$this->drupalPostForm(NULL, $edit, t('Save and publish'));
$this->assertEqual('node created', \Drupal::state()->get('test_field_only_en_fr'));
// Create a translation in French.
$this->clickLink('Translate');
$this->clickLink('Add');
$this->drupalPostForm(NULL, [], t('Save and keep published (this translation)'));
$this->clickLink('Translate');
// Edit English translation.
$node = $this->getNodeByTitle('english_title');
$this->drupalGet('node/' . $node->id() . '/edit');
// Test the expected title when loading the form.
$this->assertRaw('<title>Edit Article english_title | Drupal</title>');
// Upload and image after changing the node language.
$images = $this->drupalGetTestFiles('image')[1];
$edit = [
'langcode[0][value]' => 'de',
'files[field_image_field_0]' => $images->uri,
];
$this->drupalPostForm(NULL, $edit, t('Upload'));
// Test the expected title after triggering an ajax call with a new
// language selected.
$this->assertRaw('<title>Edit Article english_title | Drupal</title>');
$edit = [
'langcode[0][value]' => 'en',
'field_image_field[0][alt]' => 'alternative_text'
];
$this->drupalPostForm(NULL, $edit, t('Save and keep published (this translation)'));
// Check that the translation languages are correct.
$node = $this->getNodeByTitle('english_title');
$translation_languages = array_keys($node->getTranslationLanguages());
$this->assertTrue(in_array('fr', $translation_languages));
$this->assertTrue(!in_array('de', $translation_languages));
}
}
<?php
/**
* @file
* Helper module for the Content Translation tests.
*/
use \Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_BASE_FORM_ID_alter().
*
* Adds a textfield to node forms based on a request parameter.
*/
function content_translation_test_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$langcode = $form_state->getFormObject()->getFormLangcode($form_state);
if (in_array($langcode, ['en', 'fr']) && \Drupal::request()->get('test_field_only_en_fr')) {
$form['test_field_only_en_fr'] = [
'#type' => 'textfield',
'#title' => 'Field only available on the english and french form',
];
foreach (array_keys($form['actions']) as $action) {
if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
$form['actions'][$action]['#submit'][] = 'content_translation_test_form_node_form_submit';
}
}
}
}
/**
* Form submission handler for custom field added based on a request parameter.
*
* @see content_translation_test_form_node_article_form_alter()
*/
function content_translation_test_form_node_form_submit($form, FormStateInterface $form_state) {
\Drupal::state()->set('test_field_only_en_fr', $form_state->getValue('test_field_only_en_fr'));
}
......@@ -3,6 +3,7 @@
namespace Drupal\KernelTests\Core\Entity;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\language\Entity\ConfigurableLanguage;
/**
......@@ -27,6 +28,7 @@ protected function setUp() {
ConfigurableLanguage::createFromLangcode('de')->save();
$this->installEntitySchema('entity_test_mul');
$this->installEntitySchema('entity_test_mulrev');
}
/**
......@@ -78,15 +80,93 @@ public function testEnforceIsNewOnClonedEntityTranslation() {
// The entity is not new anymore.
$this->assertFalse($entity_translation->isNew());
// The clone should not be new as well.
// The clone should not be new either.
$clone = clone $entity_translation;
$this->assertFalse($clone->isNew());
// After enforcing the clone to be new only it should be flagged as new,
// but the original entity should not be flagged as new.
// After forcing the clone to be new only it should be flagged as new, but
// the original entity should not.
$clone->enforceIsNew();
$this->assertTrue($clone->isNew());
$this->assertFalse($entity_translation->isNew());
}
/**
* Tests if the entity fields are properly cloned.
*/
public function testClonedEntityFields() {
$user = $this->createUser();
// Create a test entity.
$entity = EntityTestMul::create([
'name' => $this->randomString(),
'user_id' => $user->id(),
'language' => 'en',
]);
$entity->addTranslation('de');
$entity->save();
$fields = array_keys($entity->getFieldDefinitions());
// Reload the entity, clone it and check that both entity objects reference
// different field instances.
$entity = $this->reloadEntity($entity);
$clone = clone $entity;
$different_references = TRUE;
foreach ($fields as $field_name) {
if ($entity->get($field_name) === $clone->get($field_name)) {
$different_references = FALSE;
}
}
$this->assertTrue($different_references, 'The entity object and the cloned entity object reference different field item list objects.');
// Reload the entity, initialize one translation, clone it and check that
// both entity objects reference different field instances.
$entity = $this->reloadEntity($entity);
$entity->getTranslation('de');
$clone = clone $entity;
$different_references = TRUE;
foreach ($fields as $field_name) {
if ($entity->get($field_name) === $clone->get($field_name)) {
$different_references = FALSE;
}
}
$this->assertTrue($different_references, 'The entity object and the cloned entity object reference different field item list objects if the entity is cloned after an entity translation has been initialized.');
}
/**
* Tests that the flag for enforcing a new revision is not shared.
*/
public function testNewRevisionOnCloneEntityTranslation() {
// Create a test entity.
$entity = EntityTestMulRev::create([
'name' => $this->randomString(),
'language' => 'en',
]);
$entity->save();
$entity->addTranslation('de');
$entity->save();
// Reload the entity as ContentEntityBase::postCreate() forces the entity to
// be a new revision.
$entity = EntityTestMulRev::load($entity->id());
$entity_translation = $entity->getTranslation('de');
// The entity is not set to be a new revision.
$this->assertFalse($entity_translation->isNewRevision());
// The clone should not be set to be a new revision either.
$clone = clone $entity_translation;
$this->assertFalse($clone->isNewRevision());
// After forcing the clone to be a new revision only it should be flagged
// as a new revision, but the original entity should not.
$clone->setNewRevision();
$this->assertTrue($clone->isNewRevision());
$this->assertFalse($entity_translation->isNewRevision());
}
}
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