diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php index e8c006b2da536f6b3f02208d5cba1f3822c07e23..aacbc2b98a5ed554febac933d67715e64800eb09 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -41044,11 +41044,29 @@ 'login' => '0', 'status' => '1', 'timezone' => 'America/Chicago', - 'language' => 'en', + 'language' => 'is', 'picture' => '0', 'init' => 'odo@local.host', 'data' => 'a:1:{s:7:"contact";i:1;}', )) +->values(array( + 'uid' => '3', + 'name' => 'Bob', + 'pass' => '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ', + 'mail' => 'bob@local.host', + 'theme' => '', + 'signature' => '', + 'signature_format' => 'filtered_html', + 'created' => '1440532218', + 'access' => '0', + 'login' => '0', + 'status' => '1', + 'timezone' => 'America/New_York', + 'language' => 'fr', + 'picture' => '0', + 'init' => 'bob@local.host', + 'data' => 'a:1:{s:7:"contact";i:1;}', +)) ->execute(); $connection->schema()->createTable('users_roles', array( @@ -41084,6 +41102,14 @@ 'uid' => '1', 'rid' => '3', )) +->values(array( + 'uid' => '2', + 'rid' => '3', +)) +->values(array( + 'uid' => '3', + 'rid' => '3', +)) ->execute(); $connection->schema()->createTable('variable', array( diff --git a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php index bac70e538eeb60c6dc8057cbec3c3d48aeee0029..eef7b908267abbb835f767e2224adcf57d61ade5 100644 --- a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php +++ b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php @@ -61,7 +61,7 @@ protected function getEntityCounts() { 'taxonomy_term' => 18, 'taxonomy_vocabulary' => 3, 'tour' => 4, - 'user' => 3, + 'user' => 4, 'user_role' => 4, 'menu_link_content' => 9, 'view' => 12, diff --git a/core/modules/user/migration_templates/d6_user.yml b/core/modules/user/migration_templates/d6_user.yml index bf6ec2aded2e6a97f71fa09ccdcdfeaad5933b7a..fa6326e930b2723a916baa2c5805af08c74a7563 100644 --- a/core/modules/user/migration_templates/d6_user.yml +++ b/core/modules/user/migration_templates/d6_user.yml @@ -16,7 +16,18 @@ process: timezone: plugin: user_update_7002 source: timezone - preferred_langcode: language + langcode: + plugin: user_langcode + source: language + fallback_to_site_default: false + preferred_langcode: + plugin: user_langcode + source: language + fallback_to_site_default: true + preferred_admin_langcode: + plugin: user_langcode + source: language + fallback_to_site_default: true init: init roles: plugin: migration @@ -34,6 +45,8 @@ migration_dependencies: required: - d6_user_role optional: + - language + - default_language - d6_user_picture_file - user_picture_entity_display - user_picture_entity_form_display diff --git a/core/modules/user/migration_templates/d7_user.yml b/core/modules/user/migration_templates/d7_user.yml index d68fddff4b2079e6fbdb28bc1f40d331913f56c7..b72e8c72892b74bb2bc6fff3657e15d51a886e9c 100644 --- a/core/modules/user/migration_templates/d7_user.yml +++ b/core/modules/user/migration_templates/d7_user.yml @@ -15,9 +15,18 @@ process: login: login status: status timezone: timezone - langcode: language - preferred_langcode: language - preferred_admin_langcode: language + langcode: + plugin: user_langcode + source: language + fallback_to_site_default: false + preferred_langcode: + plugin: user_langcode + source: language + fallback_to_site_default: true + preferred_admin_langcode: + plugin: user_langcode + source: language + fallback_to_site_default: true init: init roles: plugin: migration @@ -38,6 +47,8 @@ migration_dependencies: - d7_user_role optional: - d7_file + - language + - default_language - user_picture_field_instance - user_picture_entity_display - user_picture_entity_form_display diff --git a/core/modules/user/src/Plugin/migrate/process/UserLangcode.php b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php new file mode 100644 index 0000000000000000000000000000000000000000..80f20783b62274f61bcc48ad83628dbdd6c2708b --- /dev/null +++ b/core/modules/user/src/Plugin/migrate/process/UserLangcode.php @@ -0,0 +1,86 @@ +languageManager = $language_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('language_manager') + ); + } + + /** + * {@inheritdoc} + */ + public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { + if (!isset($this->configuration['fallback_to_site_default'])) { + $this->configuration['fallback_to_site_default'] = TRUE; + } + + // If the user's language is empty, it means the locale module was not + // installed, so the user's langcode should be English and the user's + // preferred_langcode and preferred_admin_langcode should fallback to the + // default language. + if (empty($value)) { + if ($this->configuration['fallback_to_site_default']) { + return $this->languageManager->getDefaultLanguage()->getId(); + } + else { + return 'en'; + } + } + // If the user's language does not exists, use the default language. + elseif ($this->languageManager->getLanguage($value) === NULL) { + return $this->languageManager->getDefaultLanguage()->getId(); + } + + // If the langcode is a valid one, just return it. + return $value; + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php index 978c0bf45b2ffd8d0175bf60312458f040ea09d0..e734517a5ee2fa8397fd5689fd3dd62ac8106cf4 100644 --- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php @@ -12,6 +12,11 @@ */ class MigrateUserProfileValuesTest extends MigrateDrupal6TestBase { + /** + * {@inheritdoc} + */ + public static $modules = ['language']; + /** * {@inheritdoc} */ @@ -19,6 +24,7 @@ protected function setUp() { parent::setUp(); $this->executeMigrations([ + 'language', 'user_profile_field', 'user_profile_field_instance', 'user_profile_entity_display', diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php index 93178340681aea41383c0bcacc663ba76d9cf33c..33952d177a9a5db5e35f3059186e431e70294045 100644 --- a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php @@ -19,6 +19,11 @@ class MigrateUserTest extends MigrateDrupal6TestBase { use FileMigrationTestTrait; + /** + * {@inheritdoc} + */ + public static $modules = ['language']; + /** * {@inheritdoc} */ @@ -60,6 +65,7 @@ protected function setUp() { file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-2.jpg')); $file->save(); + $this->executeMigration('language'); $this->migrateUsers(); } @@ -91,28 +97,47 @@ public function testUser() { /** @var \Drupal\user\UserInterface $user */ $user = User::load($source->uid); - $this->assertIdentical($source->uid, $user->id()); - $this->assertIdentical($source->name, $user->label()); - $this->assertIdentical($source->mail, $user->getEmail()); - $this->assertIdentical($source->created, $user->getCreatedTime()); - $this->assertIdentical($source->access, $user->getLastAccessedTime()); - $this->assertIdentical($source->login, $user->getLastLoginTime()); + $this->assertSame($source->uid, $user->id()); + $this->assertSame($source->name, $user->label()); + $this->assertSame($source->mail, $user->getEmail()); + $this->assertSame($source->created, $user->getCreatedTime()); + $this->assertSame($source->access, $user->getLastAccessedTime()); + $this->assertSame($source->login, $user->getLastLoginTime()); $is_blocked = $source->status == 0; - $this->assertIdentical($is_blocked, $user->isBlocked()); + $this->assertSame($is_blocked, $user->isBlocked()); + $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default'); + $this->assertSame($expected_timezone_name, $user->getTimeZone()); + $this->assertSame($source->init, $user->getInitialEmail()); + $this->assertSame($roles, $user->getRoles()); + + // Ensure the user's langcode, preferred_langcode and + // preferred_admin_langcode are valid. // $user->getPreferredLangcode() might fallback to default language if the // user preferred language is not configured on the site. We just want to // test if the value was imported correctly. - $this->assertIdentical($source->language, $user->preferred_langcode->value); - $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default'); - $this->assertIdentical($expected_timezone_name, $user->getTimeZone()); - $this->assertIdentical($source->init, $user->getInitialEmail()); - $this->assertIdentical($roles, $user->getRoles()); + $language_manager = $this->container->get('language_manager'); + $default_langcode = $language_manager->getDefaultLanguage()->getId(); + if (empty($source->language)) { + $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($source->language) === 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($source->language, $user->langcode->value); + $this->assertSame($source->language, $user->preferred_langcode->value); + $this->assertSame($source->language, $user->preferred_admin_langcode->value); + } // We have one empty picture in the data so don't try load that. if (!empty($source->picture)) { // Test the user picture. $file = File::load($user->user_picture->target_id); - $this->assertIdentical(basename($source->picture), $file->getFilename()); + $this->assertSame(basename($source->picture), $file->getFilename()); } else { // Ensure the user does not have a picture. 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 33bc5fe8b85381a6c0ab92fb46ac17b742c19a61..c21856e5b87043ce4a25b659da38696ec02288d7 100644 --- a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php +++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\user\Kernel\Migrate\d7; use Drupal\comment\Entity\CommentType; +use Drupal\Core\Database\Database; use Drupal\node\Entity\NodeType; use Drupal\taxonomy\Entity\Vocabulary; use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase; @@ -25,6 +26,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase { 'datetime', 'file', 'image', + 'language', 'link', 'node', 'system', @@ -49,6 +51,7 @@ protected function setUp() { $this->createType('test_content_type'); Vocabulary::create(['vid' => 'test_vocabulary'])->save(); $this->executeMigrations([ + 'language', 'user_picture_field', 'user_picture_field_instance', 'd7_user_role', @@ -88,6 +91,8 @@ protected function createType($id) { * The user's email address. * @param string $password * The password for this user. + * @param int $created + * The user's creation time. * @param int $access * The last access time. * @param int $login @@ -96,37 +101,59 @@ protected function createType($id) { * Whether or not the account is blocked. * @param string $langcode * The user account's language code. + * @param string $timezone + * The user account's timezone name. * @param string $init * The user's initial email address. * @param string[] $roles * Role IDs the user account is expected to have. - * @param bool $has_picture - * Whether the user is expected to have a picture attached. * @param int $field_integer * The value of the integer field. + * @param bool $has_picture + * Whether the user is expected to have a picture attached. */ - protected function assertEntity($id, $label, $mail, $password, $access, $login, $blocked, $langcode, $init, array $roles = [RoleInterface::AUTHENTICATED_ID], $has_picture = FALSE, $field_integer = NULL) { + protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $langcode, $timezone, $init, $roles, $field_integer, $has_picture = FALSE) { /** @var \Drupal\user\UserInterface $user */ $user = User::load($id); $this->assertTrue($user instanceof UserInterface); - $this->assertIdentical($label, $user->label()); - $this->assertIdentical($mail, $user->getEmail()); - $this->assertIdentical($access, $user->getLastAccessedTime()); - $this->assertIdentical($login, $user->getLastLoginTime()); - $this->assertIdentical($blocked, $user->isBlocked()); + $this->assertSame($label, $user->label()); + $this->assertSame($mail, $user->getEmail()); + $this->assertSame($password, $user->getPassword()); + $this->assertSame($created, $user->getCreatedTime()); + $this->assertSame($access, $user->getLastAccessedTime()); + $this->assertSame($login, $user->getLastLoginTime()); + $this->assertNotSame($blocked, $user->isBlocked()); + + // Ensure the user's langcode, preferred_langcode and + // preferred_admin_langcode are valid. // $user->getPreferredLangcode() might fallback to default language if the // user preferred language is not configured on the site. We just want to // test if the value was imported correctly. - $this->assertIdentical($langcode, $user->langcode->value); - $this->assertIdentical($langcode, $user->preferred_langcode->value); - $this->assertIdentical($langcode, $user->preferred_admin_langcode->value); - $this->assertIdentical($init, $user->getInitialEmail()); - $this->assertIdentical($roles, $user->getRoles()); - $this->assertIdentical($has_picture, !$user->user_picture->isEmpty()); - $this->assertIdentical($password, $user->getPassword()); + $language_manager = $this->container->get('language_manager'); + $default_langcode = $language_manager->getDefaultLanguage()->getId(); + if ($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) { + $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($timezone, $user->getTimeZone()); + $this->assertSame($init, $user->getInitialEmail()); + $this->assertSame($roles, $user->getRoles()); + $this->assertSame($has_picture, !$user->user_picture->isEmpty()); if (!is_null($field_integer)) { $this->assertTrue($user->hasField('field_integer')); - $this->assertEquals($field_integer, $user->field_integer->value); + $this->assertEquals($field_integer[0], $user->field_integer->value); } } @@ -134,22 +161,65 @@ protected function assertEntity($id, $label, $mail, $password, $access, $login, * Tests the Drupal 7 user to Drupal 8 migration. */ public function testUser() { - $password = '$S$DGFZUE.FhrXbe4y52eC7p0ZVRGD/gOPtVctDlmC89qkujnBokAlJ'; - $this->assertEntity(2, 'Odo', 'odo@local.host', $password, '0', '0', FALSE, 'en', 'odo@local.host', [RoleInterface::AUTHENTICATED_ID], FALSE, 99); - - // Ensure that the user can authenticate. - $this->assertEquals(2, \Drupal::service('user.auth')->authenticate('Odo', 'a password')); - // After authenticating the password will be rehashed because the password - // stretching iteration count has changed from 15 in Drupal 7 to 16 in - // Drupal 8. - $user = User::load(2); - $rehash = $user->getPassword(); - $this->assertNotEquals($password, $rehash); - - // Authenticate again and there should be no re-hash. - $this->assertEquals(2, \Drupal::service('user.auth')->authenticate('Odo', 'a password')); - $user = User::load(2); - $this->assertEquals($rehash, $user->getPassword()); + $users = Database::getConnection('default', 'migrate') + ->select('users', 'u') + ->fields('u') + ->condition('uid', 1, '>') + ->execute() + ->fetchAll(); + + foreach ($users as $source) { + $rids = Database::getConnection('default', 'migrate') + ->select('users_roles', 'ur') + ->fields('ur', array('rid')) + ->condition('ur.uid', $source->uid) + ->execute() + ->fetchCol(); + $roles = array(RoleInterface::AUTHENTICATED_ID); + $id_map = $this->getMigration('d7_user_role')->getIdMap(); + foreach ($rids as $rid) { + $role = $id_map->lookupDestinationId(array($rid)); + $roles[] = reset($role); + } + + $field_integer = Database::getConnection('default', 'migrate') + ->select('field_data_field_integer', 'fi') + ->fields('fi', array('field_integer_value')) + ->condition('fi.entity_id', $source->uid) + ->execute() + ->fetchCol(); + $field_integer = !empty($field_integer) ? $field_integer : NULL; + + $this->assertEntity( + $source->uid, + $source->name, + $source->mail, + $source->pass, + $source->created, + $source->access, + $source->login, + $source->status, + $source->language, + $source->timezone, + $source->init, + $roles, + $field_integer + ); + + // Ensure that the user can authenticate. + $this->assertEquals($source->uid, $this->container->get('user.auth')->authenticate($source->name, 'a password')); + // After authenticating the password will be rehashed because the password + // stretching iteration count has changed from 15 in Drupal 7 to 16 in + // Drupal 8. + $user = User::load($source->uid); + $rehash = $user->getPassword(); + $this->assertNotEquals($source->pass, $rehash); + + // Authenticate again and there should be no re-hash. + $this->assertEquals($source->uid, $this->container->get('user.auth')->authenticate($source->name, 'a password')); + $user = User::load($source->uid); + $this->assertEquals($rehash, $user->getPassword()); + } } }