diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index b524890..dcf358b 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -585,11 +585,13 @@ protected function updateFieldLangcodes($langcode) { /** * {@inheritdoc} */ - public function onChange($property_name) { - if ($property_name == 'langcode') { - // Avoid using unset as this unnecessarily triggers magic methods later - // on. + public function onChange($name) { + if ($name == 'langcode') { $this->setDefaultLangcode(); + if (isset($this->translations[$this->defaultLangcode])) { + $message = format_string('A translation already exists for the specified language (@langcode).', array('@langcode' => $this->defaultLangcode)); + throw new \InvalidArgumentException($message); + } $this->updateFieldLangcodes($this->defaultLangcode); } } diff --git a/core/modules/aggregator/aggregator.install b/core/modules/aggregator/aggregator.install index f891444..b47dc01 100644 --- a/core/modules/aggregator/aggregator.install +++ b/core/modules/aggregator/aggregator.install @@ -312,7 +312,7 @@ function aggregator_update_8001() { 'length' => 12, 'not null' => TRUE, 'default' => '', - 'initial' => Language::LANGCODE_DEFAULT, + 'initial' => Language::LANGCODE_NOT_SPECIFIED, )); db_add_field('aggregator_item', 'langcode', array( 'description' => 'The {language}.langcode of this feed item.', @@ -320,6 +320,6 @@ function aggregator_update_8001() { 'length' => 12, 'not null' => TRUE, 'default' => '', - 'initial' => Language::LANGCODE_DEFAULT, + 'initial' => Language::LANGCODE_NOT_SPECIFIED, )); } diff --git a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php index bd3472f..7fe938a 100644 --- a/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php +++ b/core/modules/hal/lib/Drupal/hal/Normalizer/FieldNormalizer.php @@ -44,9 +44,9 @@ public function normalize($field, $format = NULL, array $context = array()) { // in to the field item normalizer in the context. The langcode is appended // to the field item values. else { - foreach ($entity->getTranslationLanguages() as $lang) { - $context['langcode'] = $lang->id == 'und' ? Language::LANGCODE_DEFAULT : $lang->id; - $translation = $entity->getTranslation($lang->id); + foreach ($entity->getTranslationLanguages() as $language) { + $context['langcode'] = $language->id; + $translation = $entity->getTranslation($language->id); $translated_field = $translation->get($field_name); $normalized_field_items = array_merge($normalized_field_items, $this->normalizeFieldItems($translated_field, $format, $context)); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php new file mode 100644 index 0000000..cf38de6 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php @@ -0,0 +1,134 @@ +installSchema('system', 'variable'); + $this->installSchema('entity_test', array( + 'entity_test_mul', + 'entity_test_mul_property_data', + 'entity_test_rev', + 'entity_test_rev_revision', + 'entity_test_mulrev', + 'entity_test_mulrev_revision', + 'entity_test_mulrev_property_data', + 'entity_test_mulrev_property_revision', + )); + $this->installConfig(array('language')); + + // Create the test field. + entity_test_install(); + + // Enable translations for the test entity type. + $this->state->set('entity_test.translation', TRUE); + + // Create a translatable test field. + $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); + + // Create an untranslatable test field. + $this->untranslatable_field_name = drupal_strtolower($this->randomName() . '_field_name'); + + // Create field instances in all entity variations. + foreach (entity_test_entity_types() as $entity_type) { + entity_create('field_entity', array( + 'name' => $this->field_name, + 'entity_type' => $entity_type, + 'type' => 'text', + 'cardinality' => 4, + 'translatable' => TRUE, + ))->save(); + entity_create('field_instance', array( + 'field_name' => $this->field_name, + 'entity_type' => $entity_type, + 'bundle' => $entity_type, + ))->save(); + $this->instance[$entity_type] = field_read_instance($entity_type, $this->field_name, $entity_type); + + entity_create('field_entity', array( + 'name' => $this->untranslatable_field_name, + 'entity_type' => $entity_type, + 'type' => 'text', + 'cardinality' => 4, + 'translatable' => FALSE, + ))->save(); + entity_create('field_instance', array( + 'field_name' => $this->untranslatable_field_name, + 'entity_type' => $entity_type, + 'bundle' => $entity_type, + ))->save(); + } + + // Create the default languages. + $default_language = language_save(language_default()); + $languages = language_default_locked_languages($default_language->weight); + foreach ($languages as $language) { + language_save($language); + } + + // Create test languages. + $this->langcodes = array(); + for ($i = 0; $i < 3; ++$i) { + $language = new Language(array( + 'id' => 'l' . $i, + 'name' => $this->randomString(), + 'weight' => $i, + )); + $this->langcodes[$i] = $language->id; + language_save($language); + } + } + + /** + * Toggles field translatability. + * + * @param string $entity_type + * The type of the entity fields are attached to. + */ + protected function toggleFieldTranslatability($entity_type) { + $fields = array($this->field_name, $this->untranslatable_field_name); + foreach ($fields as $field_name) { + $field = FieldService::fieldInfo()->getField($entity_type, $field_name); + $translatable = !$field->isFieldTranslatable(); + $field->set('translatable', $translatable); + $field->save(); + FieldService::fieldInfo()->flush(); + $field = FieldService::fieldInfo()->getField($entity_type, $field_name); + $this->assertEqual($field->isFieldTranslatable(), $translatable, 'Field translatability changed.'); + } + \Drupal::cache('field')->deleteAll(); + } + +} 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 eabcdeb..cd0718e 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php @@ -8,35 +8,13 @@ namespace Drupal\system\Tests\Entity; use Drupal\Component\Utility\MapArray; -use Drupal\Core\Entity\ContentEntityInterface; -use Drupal\Core\Entity\FieldableDatabaseStorageController; use Drupal\Core\Language\Language; -use Drupal\Core\TypedData\TranslatableInterface; use Drupal\entity_test\Entity\EntityTestMulRev; -use Drupal\field\Field as FieldService; /** * Tests entity translation. */ -class EntityTranslationTest extends EntityUnitTestBase { - - protected $langcodes; - - /** - * The test field name. - * - * @var string - */ - protected $field_name; - - /** - * The untranslatable test field name. - * - * @var string - */ - protected $untranslatable_field_name; - - public static $modules = array('language', 'entity_test'); +class EntityTranslationTest extends EntityLanguageTestBase { public static function getInfo() { return array( @@ -46,84 +24,6 @@ public static function getInfo() { ); } - function setUp() { - parent::setUp(); - - $this->installSchema('system', 'variable'); - $this->installSchema('entity_test', array( - 'entity_test_mul', - 'entity_test_mul_property_data', - 'entity_test_rev', - 'entity_test_rev_revision', - 'entity_test_mulrev', - 'entity_test_mulrev_revision', - 'entity_test_mulrev_property_data', - 'entity_test_mulrev_property_revision', - )); - $this->installConfig(array('language')); - - // Create the test field. - entity_test_install(); - - // Enable translations for the test entity type. - $this->state->set('entity_test.translation', TRUE); - - // Create a translatable test field. - $this->field_name = drupal_strtolower($this->randomName() . '_field_name'); - - // Create an untranslatable test field. - $this->untranslatable_field_name = drupal_strtolower($this->randomName() . '_field_name'); - - // Create field instances in all entity variations. - foreach (entity_test_entity_types() as $entity_type) { - entity_create('field_entity', array( - 'name' => $this->field_name, - 'entity_type' => $entity_type, - 'type' => 'text', - 'cardinality' => 4, - 'translatable' => TRUE, - ))->save(); - entity_create('field_instance', array( - 'field_name' => $this->field_name, - 'entity_type' => $entity_type, - 'bundle' => $entity_type, - ))->save(); - $this->instance[$entity_type] = field_read_instance($entity_type, $this->field_name, $entity_type); - - entity_create('field_entity', array( - 'name' => $this->untranslatable_field_name, - 'entity_type' => $entity_type, - 'type' => 'text', - 'cardinality' => 4, - 'translatable' => FALSE, - ))->save(); - entity_create('field_instance', array( - 'field_name' => $this->untranslatable_field_name, - 'entity_type' => $entity_type, - 'bundle' => $entity_type, - ))->save(); - } - - // Create the default languages. - $default_language = language_save(language_default()); - $languages = language_default_locked_languages($default_language->weight); - foreach ($languages as $language) { - language_save($language); - } - - // Create test languages. - $this->langcodes = array(); - for ($i = 0; $i < 3; ++$i) { - $language = new Language(array( - 'id' => 'l' . $i, - 'name' => $this->randomString(), - 'weight' => $i, - )); - $this->langcodes[$i] = $language->id; - language_save($language); - } - } - /** * Tests language related methods of the Entity class. */ @@ -668,116 +568,17 @@ public function testLanguageChange() { foreach (array($this->field_name, $this->untranslatable_field_name) as $field_name) { $this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected after translating the entity and changing language.'); } - } - /** - * Tests field SQL storage. - */ - public function testFieldSqlStorage() { - $entity_type = 'entity_test_mul'; - - $controller = $this->entityManager->getStorageController($entity_type); - $values = array( - $this->field_name => $this->randomName(), - $this->untranslatable_field_name => $this->randomName(), - ); - $entity = $controller->create($values); - $entity->save(); - - // Tests that when changing language field language codes are still correct. - $langcode = $this->langcodes[0]; - $entity->langcode->value = $langcode; - $entity->save(); - $this->assertFieldStorageLangcode($entity, 'Field language successfully changed from language neutral.'); - $langcode = $this->langcodes[1]; - $entity->langcode->value = $langcode; - $entity->save(); - $this->assertFieldStorageLangcode($entity, 'Field language successfully changed.'); - $langcode = Language::LANGCODE_NOT_SPECIFIED; - $entity->langcode->value = $langcode; - $entity->save(); - $this->assertFieldStorageLangcode($entity, 'Field language successfully changed to language neutral.'); - - // Test that after switching field translatability things keep working as - // before. - $this->toggleFieldTranslatability($entity_type); - $entity = $this->reloadEntity($entity); - foreach (array($this->field_name, $this->untranslatable_field_name) as $field_name) { - $this->assertEqual($entity->get($field_name)->value, $values[$field_name], 'Field language works as expected after switching translatability.'); + // Check that setting the default language to an existing translation + // language causes an exception to be thrown. + $message = 'An exception is thrown when setting the default language to an existing translation language'; + try { + $entity->langcode->value = $this->langcodes[2]; + $this->fail($message); } - - // Test that after disabling field translatability translated values are not - // loaded. - $this->toggleFieldTranslatability($entity_type); - $entity = $this->reloadEntity($entity); - $entity->langcode->value = $this->langcodes[0]; - $translation = $entity->addTranslation($this->langcodes[1]); - $translated_value = $this->randomName(); - $translation->get($this->field_name)->value = $translated_value; - $translation->save(); - $this->toggleFieldTranslatability($entity_type); - $entity = $this->reloadEntity($entity); - $this->assertEqual($entity->getTranslation($this->langcodes[1])->get($this->field_name)->value, $values[$this->field_name], 'Existing field translations are not loaded for untranslatable fields.'); - } - - /** - * Checks whether field languages are correctly stored for the given entity. - * - * @param \Drupal\Core\Entity\ContentEntityInterface $entity - * The entity fields are attached to. - * @param string $message - * (optional) A message to display with the assertion. - */ - protected function assertFieldStorageLangcode(ContentEntityInterface $entity, $message = '') { - $status = TRUE; - $entity_type = $entity->entityType(); - $id = $entity->id(); - $langcode = $entity->getUntranslated()->language()->id; - $fields = array($this->field_name, $this->untranslatable_field_name); - - foreach ($fields as $field_name) { - $field = FieldService::fieldInfo()->getField($entity_type, $field_name); - $tables = array( - FieldableDatabaseStorageController::_fieldTableName($field), - FieldableDatabaseStorageController::_fieldRevisionTableName($field), - ); - - foreach ($tables as $table) { - $record = \Drupal::database() - ->select($table, 'f') - ->fields('f') - ->condition('f.entity_id', $id) - ->condition('f.revision_id', $id) - ->execute() - ->fetchObject(); - - if ($record->langcode != $langcode) { - $status = FALSE; - break; - } - } + catch (\InvalidArgumentException $e) { + $this->pass($message); } - - return $this->assertTrue($status, $message); } - /** - * Toggles field translatability. - * - * @param string $entity_type - * The type of the entity fields are attached to. - */ - protected function toggleFieldTranslatability($entity_type) { - $fields = array($this->field_name, $this->untranslatable_field_name); - foreach ($fields as $field_name) { - $field = FieldService::fieldInfo()->getField($entity_type, $field_name); - $translatable = !$field->isFieldTranslatable(); - $field->set('translatable', $translatable); - $field->save(); - FieldService::fieldInfo()->flush(); - $field = FieldService::fieldInfo()->getField($entity_type, $field_name); - $this->assertEqual($field->isFieldTranslatable(), $translatable, 'Field translatability changed.'); - } - \Drupal::cache('field')->deleteAll(); - } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldTranslationSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldTranslationSqlStorageTest.php new file mode 100644 index 0000000..b6790aa --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldTranslationSqlStorageTest.php @@ -0,0 +1,119 @@ + 'Field translation SQL storage tests', + 'description' => "Test Field translation SQL Storage.", + 'group' => 'Entity API' + ); + } + + /** + * Tests field SQL storage. + */ + public function testFieldSqlStorage() { + $entity_type = 'entity_test_mul'; + + $controller = $this->entityManager->getStorageController($entity_type); + $values = array( + $this->field_name => $this->randomName(), + $this->untranslatable_field_name => $this->randomName(), + ); + $entity = $controller->create($values); + $entity->save(); + + // Tests that when changing language field language codes are still correct. + $langcode = $this->langcodes[0]; + $entity->langcode->value = $langcode; + $entity->save(); + $this->assertFieldStorageLangcode($entity, 'Field language successfully changed from language neutral.'); + $langcode = $this->langcodes[1]; + $entity->langcode->value = $langcode; + $entity->save(); + $this->assertFieldStorageLangcode($entity, 'Field language successfully changed.'); + $langcode = Language::LANGCODE_NOT_SPECIFIED; + $entity->langcode->value = $langcode; + $entity->save(); + $this->assertFieldStorageLangcode($entity, 'Field language successfully changed to language neutral.'); + + // Test that after switching field translatability things keep working as + // before. + $this->toggleFieldTranslatability($entity_type); + $entity = $this->reloadEntity($entity); + foreach (array($this->field_name, $this->untranslatable_field_name) as $field_name) { + $this->assertEqual($entity->get($field_name)->value, $values[$field_name], 'Field language works as expected after switching translatability.'); + } + + // Test that after disabling field translatability translated values are not + // loaded. + $this->toggleFieldTranslatability($entity_type); + $entity = $this->reloadEntity($entity); + $entity->langcode->value = $this->langcodes[0]; + $translation = $entity->addTranslation($this->langcodes[1]); + $translated_value = $this->randomName(); + $translation->get($this->field_name)->value = $translated_value; + $translation->save(); + $this->toggleFieldTranslatability($entity_type); + $entity = $this->reloadEntity($entity); + $this->assertEqual($entity->getTranslation($this->langcodes[1])->get($this->field_name)->value, $values[$this->field_name], 'Existing field translations are not loaded for untranslatable fields.'); + } + + /** + * Checks whether field languages are correctly stored for the given entity. + * + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * The entity fields are attached to. + * @param string $message + * (optional) A message to display with the assertion. + */ + protected function assertFieldStorageLangcode(ContentEntityInterface $entity, $message = '') { + $status = TRUE; + $entity_type = $entity->entityType(); + $id = $entity->id(); + $langcode = $entity->getUntranslated()->language()->id; + $fields = array($this->field_name, $this->untranslatable_field_name); + + foreach ($fields as $field_name) { + $field = FieldService::fieldInfo()->getField($entity_type, $field_name); + $tables = array( + FieldableDatabaseStorageController::_fieldTableName($field), + FieldableDatabaseStorageController::_fieldRevisionTableName($field), + ); + + foreach ($tables as $table) { + $record = \Drupal::database() + ->select($table, 'f') + ->fields('f') + ->condition('f.entity_id', $id) + ->condition('f.revision_id', $id) + ->execute() + ->fetchObject(); + + if ($record->langcode != $langcode) { + $status = FALSE; + break; + } + } + } + + return $this->assertTrue($status, $message); + } + +}