diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php index e76ee20..db7e2e6 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -3171,6 +3171,30 @@ 'created' => '1527594929', 'changed' => '1527594929', )) +->values(array( + 'entity_type' => 'user', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'fr', + 'source' => 'en', + 'uid' => '1', + 'status' => '1', + 'translate' => '0', + 'created' => '1531663916', + 'changed' => '1531663916', +)) +->values(array( + 'entity_type' => 'user', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'is', + 'source' => 'en', + 'uid' => '1', + 'status' => '1', + 'translate' => '0', + 'created' => '1531663925', + 'changed' => '1531663925', +)) ->execute(); $connection->schema()->createTable('entity_translation_revision', array( @@ -6143,6 +6167,26 @@ 'field_integer_value' => '99', )) ->values(array( + 'entity_type' => 'user', + 'bundle' => 'user', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'fr', + 'delta' => '0', + 'field_integer_value' => '9', +)) +->values(array( + 'entity_type' => 'user', + 'bundle' => 'user', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'is', + 'delta' => '0', + 'field_integer_value' => '1', +)) +->values(array( 'entity_type' => 'taxonomy_term', 'bundle' => 'test_vocabulary', 'deleted' => '0', diff --git a/core/modules/user/migrations/d7_user.yml b/core/modules/user/migrations/d7_user.yml index 00eabf2..5adbe05 100644 --- a/core/modules/user/migrations/d7_user.yml +++ b/core/modules/user/migrations/d7_user.yml @@ -21,7 +21,7 @@ process: timezone: timezone langcode: plugin: user_langcode - source: language + source: entity_language fallback_to_site_default: false preferred_langcode: plugin: user_langcode diff --git a/core/modules/user/migrations/d7_user_entity_translation.yml b/core/modules/user/migrations/d7_user_entity_translation.yml new file mode 100644 index 0000000..ce42905 --- /dev/null +++ b/core/modules/user/migrations/d7_user_entity_translation.yml @@ -0,0 +1,21 @@ +id: d7_user_entity_translation +label: User accounts entity translations +migration_tags: + - Drupal 7 + - translation + - Content + - Multilingual +class: Drupal\user\Plugin\migrate\User +source: + plugin: d7_user_entity_translation +process: + uid: entity_id + langcode: language + content_translation_source: source +destination: + plugin: entity:user + translations: true + destination_module: content_translation +migration_dependencies: + required: + - d7_user diff --git a/core/modules/user/src/Plugin/migrate/source/d7/User.php b/core/modules/user/src/Plugin/migrate/source/d7/User.php index 41f9ab4..3400bf5 100644 --- a/core/modules/user/src/Plugin/migrate/source/d7/User.php +++ b/core/modules/user/src/Plugin/migrate/source/d7/User.php @@ -62,18 +62,32 @@ public function fields() { * {@inheritdoc} */ public function prepareRow(Row $row) { + $uid = $row->getSourceProperty('uid'); + $roles = $this->select('users_roles', 'ur') ->fields('ur', ['rid']) - ->condition('ur.uid', $row->getSourceProperty('uid')) + ->condition('ur.uid', $uid) ->execute() ->fetchCol(); $row->setSourceProperty('roles', $roles); $row->setSourceProperty('data', unserialize($row->getSourceProperty('data'))); + // If this entity was translated using Entity Translation, we need to get + // its source language to get the field values in the right language. + // The translations will be migrated by the d7_user_entity_translation + // migration. + $entity_translatable = $this->isEntityTranslatable('user'); + $source_language = $this->getEntityTranslationSourceLanguage('user', $uid); + $language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language'); + $row->setSourceProperty('entity_language', $language); + // Get Field API field values. - foreach (array_keys($this->getFields('user')) as $field) { - $row->setSourceProperty($field, $this->getFieldValues('user', $field, $row->getSourceProperty('uid'))); + foreach ($this->getFields('user') as $field_name => $field) { + // Ensure we're using the right language if the entity and the field are + // translatable. + $field_language = $entity_translatable && $field['translatable'] ? $language : NULL; + $row->setSourceProperty($field_name, $this->getFieldValues('user', $field_name, $uid, NULL, $field_language)); } // Get profile field values. This code is lifted directly from the D6 diff --git a/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php b/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php new file mode 100644 index 0000000..e4228bd --- /dev/null +++ b/core/modules/user/src/Plugin/migrate/source/d7/UserEntityTranslation.php @@ -0,0 +1,78 @@ +select('entity_translation', 'et') + ->fields('et', [ + 'entity_id', + 'language', + 'source', + ]) + ->condition('et.entity_type', 'user') + ->condition('et.source', '', '<>'); + + return $query; + } + + /** + * {@inheritdoc} + */ + public function prepareRow(Row $row) { + $uid = $row->getSourceProperty('entity_id'); + $language = $row->getSourceProperty('language'); + + // Get Field API field values. + foreach ($this->getFields('user') as $field_name => $field) { + // Ensure we're using the right language if the entity is translatable. + $field_language = $field['translatable'] ? $language : NULL; + $row->setSourceProperty($field_name, $this->getFieldValues('user', $field_name, $uid, NULL, $field_language)); + } + + return parent::prepareRow($row); + } + + /** + * {@inheritdoc} + */ + public function fields() { + return [ + 'entity_id' => $this->t('Entity ID'), + 'language' => $this->t('User translation language'), + 'source' => $this->t('User translation source language'), + ]; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + return [ + 'entity_id' => [ + 'type' => 'integer', + 'alias' => 'et', + ], + 'language' => [ + 'type' => 'string', + 'alias' => 'et', + ], + ]; + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php index bbfc41d..a713e49 100644 --- a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php +++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php @@ -24,6 +24,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase { */ public static $modules = [ 'comment', + 'content_translation', 'datetime', 'file', 'image', @@ -59,6 +60,7 @@ protected function setUp() { 'd7_field', 'd7_field_instance', 'd7_user', + 'd7_user_entity_translation', ]); } @@ -81,8 +83,10 @@ protected function setUp() { * The last login time. * @param bool $blocked * Whether or not the account is blocked. - * @param string $langcode - * The user account's language code. + * @param string $entity_langcode + * The user entity language code. + * @param string $prefered_langcode + * The user prefered language code. * @param string $timezone * The user account's timezone name. * @param string $init @@ -96,7 +100,7 @@ protected function setUp() { * @param bool $has_picture * (optional) Whether the user is expected to have a picture attached. */ - protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $langcode, $timezone, $init, $roles, $field_integer, $field_file_target_id = FALSE, $has_picture = FALSE) { + protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $entity_langcode, $prefered_langcode, $timezone, $init, $roles, $field_integer, $field_file_target_id = FALSE, $has_picture = FALSE) { /** @var \Drupal\user\UserInterface $user */ $user = User::load($id); $this->assertTrue($user instanceof UserInterface); @@ -115,20 +119,20 @@ protected function assertEntity($id, $label, $mail, $password, $created, $access // test if the value was imported correctly. $language_manager = $this->container->get('language_manager'); $default_langcode = $language_manager->getDefaultLanguage()->getId(); - if ($langcode == '') { + if ($prefered_langcode == '') { $this->assertSame('en', $user->langcode->value); $this->assertSame($default_langcode, $user->preferred_langcode->value); $this->assertSame($default_langcode, $user->preferred_admin_langcode->value); } - elseif ($language_manager->getLanguage($langcode) === NULL) { + elseif ($language_manager->getLanguage($prefered_langcode) === NULL) { $this->assertSame($default_langcode, $user->langcode->value); $this->assertSame($default_langcode, $user->preferred_langcode->value); $this->assertSame($default_langcode, $user->preferred_admin_langcode->value); } else { - $this->assertSame($langcode, $user->langcode->value); - $this->assertSame($langcode, $user->preferred_langcode->value); - $this->assertSame($langcode, $user->preferred_admin_langcode->value); + $this->assertSame($entity_langcode, $user->langcode->value); + $this->assertSame($prefered_langcode, $user->preferred_langcode->value); + $this->assertSame($prefered_langcode, $user->preferred_admin_langcode->value); } $this->assertSame($timezone, $user->getTimeZone()); @@ -170,10 +174,21 @@ public function testUser() { $roles[] = reset($role); } + $entity_translation = Database::getConnection('default', 'migrate') + ->select('entity_translation', 'et') + ->fields('et', ['language']) + ->condition('et.entity_type', 'user') + ->condition('et.entity_id', $source->uid) + ->condition('et.source', '') + ->execute() + ->fetchField(); + $entity_language = $entity_translation ?: $source->language; + $field_integer = Database::getConnection('default', 'migrate') ->select('field_data_field_integer', 'fi') ->fields('fi', ['field_integer_value']) ->condition('fi.entity_id', $source->uid) + ->condition('fi.language', $entity_language) ->execute() ->fetchCol(); $field_integer = !empty($field_integer) ? $field_integer : NULL; @@ -194,6 +209,7 @@ public function testUser() { $source->access, $source->login, $source->status, + $entity_language, $source->language, $source->timezone, $source->init, @@ -218,4 +234,25 @@ public function testUser() { } } + /** + * Tests the Drupal 7 user entity translations to Drupal 8 migration. + */ + public function testUserEntityTranslations() { + // Test that fields translated with Entity Translation are migrated. + $user = User::load(2); + $this->assertSame('99', $user->field_integer->value); + $user_fr = $user->getTranslation('fr'); + $this->assertSame('9', $user_fr->field_integer->value); + $user_is = $user->getTranslation('is'); + $this->assertSame('1', $user_is->field_integer->value); + + // Test that untranslatable properties are the same as the source language. + $this->assertSame($user->label(), $user_fr->label()); + $this->assertSame($user->label(), $user_is->label()); + $this->assertSame($user->getEmail(), $user_fr->getEmail()); + $this->assertSame($user->getEmail(), $user_is->getEmail()); + $this->assertSame($user->getPassword(), $user_fr->getPassword()); + $this->assertSame($user->getPassword(), $user_is->getPassword()); + } + } diff --git a/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserEntityTranslationTest.php b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserEntityTranslationTest.php new file mode 100644 index 0000000..bfd377d --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Plugin/migrate/source/d7/UserEntityTranslationTest.php @@ -0,0 +1,185 @@ + 'user', + 'entity_id' => 1, + 'revision_id' => 1, + 'language' => 'en', + 'source' => '', + 'uid' => 1, + 'status' => 1, + 'translate' => 0, + 'created' => 1531343498, + 'changed' => 1531343498, + ], + [ + 'entity_type' => 'user', + 'entity_id' => 1, + 'revision_id' => 1, + 'language' => 'fr', + 'source' => 'en', + 'uid' => 1, + 'status' => 1, + 'translate' => 0, + 'created' => 1531343508, + 'changed' => 1531343508, + ], + [ + 'entity_type' => 'user', + 'entity_id' => 1, + 'revision_id' => 1, + 'language' => 'es', + 'source' => 'en', + 'uid' => 1, + 'status' => 1, + 'translate' => 0, + 'created' => 1531343456, + 'changed' => 1531343456, + ], + ]; + $tests[0]['source_data']['field_config'] = [ + [ + 'id' => 1, + 'field_name' => 'field_test', + 'type' => 'text', + 'module' => 'text', + 'active' => 1, + 'storage_type' => 'field_sql_storage', + 'storage_module' => 'field_sql_storage', + 'storage_active' => 1, + 'locked' => 1, + 'data' => 'a:0:{}', + 'cardinality' => 1, + 'translatable' => 1, + 'deleted' => 0, + ], + ]; + $tests[0]['source_data']['field_config_instance'] = [ + [ + 'id' => 1, + 'field_id' => 1, + 'field_name' => 'field_test', + 'entity_type' => 'user', + 'bundle' => 'user', + 'data' => 'a:0:{}', + 'deleted' => 0, + ], + ]; + $tests[0]['source_data']['field_data_field_test'] = [ + [ + 'entity_type' => 'user', + 'bundle' => 'user', + 'deleted' => 0, + 'entity_id' => 1, + 'revision_id' => 1, + 'language' => 'en', + 'delta' => 0, + 'field_test_value' => 'English field', + 'field_test_format' => NULL, + ], + [ + 'entity_type' => 'user', + 'bundle' => 'user', + 'deleted' => 0, + 'entity_id' => 1, + 'revision_id' => 1, + 'language' => 'fr', + 'delta' => 0, + 'field_test_value' => 'French field', + 'field_test_format' => NULL, + ], + [ + 'entity_type' => 'user', + 'bundle' => 'user', + 'deleted' => 0, + 'entity_id' => 1, + 'revision_id' => 1, + 'language' => 'es', + 'delta' => 0, + 'field_test_value' => 'Spanish field', + 'field_test_format' => NULL, + ], + ]; + $tests[0]['source_data']['users'] = [ + [ + 'uid' => 1, + 'name' => 'admin', + 'pass' => 'password123', + 'mail' => 'admin@example.com', + 'theme' => '', + 'signature' => '', + 'signature_format' => 'filtered_html', + 'created' => 1531343456, + 'access' => 1531343456, + 'login' => 1531343456, + 'status' => 1, + 'timezone' => 'America/New_York', + 'language' => 'fr', + 'picture' => 0, + 'init' => 'admin@example.com', + 'data' => 'a:0:{}', + ], + ]; + $tests[0]['source_data']['users_roles'] = [ + [ + 'uid' => 1, + 'rid' => 3, + ], + ]; + + // The expected results. + $tests[0]['expected_data'] = [ + [ + 'entity_id' => 1, + 'language' => 'fr', + 'source' => 'en', + 'field_test' => [ + [ + 'value' => 'French field', + 'format' => NULL, + ], + ], + ], + [ + 'entity_id' => 1, + 'language' => 'es', + 'source' => 'en', + 'field_test' => [ + [ + 'value' => 'Spanish field', + 'format' => NULL, + ], + ], + ], + ]; + + return $tests; + } + +}