diff --git a/core/modules/content_translation/migrations/d6_taxonomy_term_localized_translation.yml b/core/modules/content_translation/migrations/d6_taxonomy_term_localized_translation.yml new file mode 100644 index 0000000000000000000000000000000000000000..7001982763640a868d15f9152fa1f27d8d36eecf --- /dev/null +++ b/core/modules/content_translation/migrations/d6_taxonomy_term_localized_translation.yml @@ -0,0 +1,43 @@ +id: d6_taxonomy_term_localized_translation +label: Taxonomy localized term translations +migration_tags: + - Drupal 6 + - Content +source: + plugin: d6_term_localized_translation + translations: true +process: + # If you are using this file to build a custom migration consider removing + # the tid field to allow incremental migrations. + tid: tid + langcode: language + vid: + plugin: migration + migration: d6_taxonomy_vocabulary + source: vid + name: + - + plugin: callback + source: + - name_translated + - name + callable: array_filter + - + plugin: callback + callable: current + description: + - + plugin: callback + source: + - description_translated + - description + callable: array_filter + - + plugin: callback + callable: current +destination: + plugin: entity:taxonomy_term + translations: true +migration_dependencies: + required: + - d6_taxonomy_term diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal6.php b/core/modules/migrate_drupal/tests/fixtures/drupal6.php index 502e2631d5b80ce99259613395ac14703f0ee58a..bd4151c2e8ae7c47bc211604e360a0351dab1981 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal6.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal6.php @@ -9856,6 +9856,30 @@ 'objectindex' => '0', 'format' => '0', )) +->values(array( + 'lid' => '1679', + 'objectid' => '14', + 'type' => 'term', + 'property' => 'name', + 'objectindex' => '14', + 'format' => '0', +)) +->values(array( + 'lid' => '1680', + 'objectid' => '15', + 'type' => 'term', + 'property' => 'name', + 'objectindex' => '15', + 'format' => '0', +)) +->values(array( + 'lid' => '1681', + 'objectid' => '14', + 'type' => 'term', + 'property' => 'description', + 'objectindex' => '14', + 'format' => '0', +)) ->execute(); $connection->schema()->createTable('i18n_variable', array( @@ -22357,6 +22381,27 @@ 'source' => 'I really, really, really love migrating', 'version' => '1', )) +->values(array( + 'lid' => '1679', + 'location' => 'term:14:name', + 'textgroup' => 'taxonomy', + 'source' => 'Talos IV', + 'version' => '1', +)) +->values(array( + 'lid' => '1680', + 'location' => 'term:15:name', + 'textgroup' => 'taxonomy', + 'source' => 'Vulcan', + 'version' => '1', +)) +->values(array( + 'lid' => '1681', + 'location' => 'term:14:description', + 'textgroup' => 'taxonomy', + 'source' => 'The home of Captain Christopher Pike.', + 'version' => '1', +)) ->execute(); $connection->schema()->createTable('locales_target', array( @@ -27409,6 +27454,30 @@ 'plural' => '0', 'i18n_status' => '0', )) +->values(array( + 'lid' => '1672', + 'translation' => 'fr - Type', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '1679', + 'translation' => 'fr - Talos IV', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '1681', + 'translation' => 'fr - The home of Captain Christopher Pike.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) ->values(array( 'lid' => '66', 'translation' => 'zu - CCK - Aucune Intégration aux Vues', @@ -27505,6 +27574,30 @@ 'plural' => '0', 'i18n_status' => '0', )) +->values(array( + 'lid' => '1672', + 'translation' => 'zu - Type', + 'language' => 'zu', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '1680', + 'translation' => 'zu - Vulcan', + 'language' => 'zu', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '1681', + 'translation' => 'zu - The home of Captain Christopher Pike.', + 'language' => 'zu', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) ->execute(); $connection->schema()->createTable('menu_custom', array( @@ -40593,6 +40686,28 @@ 'weight' => '0', 'file' => 'sites/all/modules/i18n/i18n.admin.inc', )) +->values(array( + 'path' => 'admin/settings/language/i18n/variables', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:25:"i18n_admin_variables_form";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/language/i18n', + 'tab_root' => 'admin/settings/language', + 'title' => 'Variables', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => 'Multilingual variables.', + 'position' => '', + 'weight' => '0', + 'file' => 'sites/all/modules/i18n/i18n.admin.inc', +)) ->values(array( 'path' => 'admin/settings/language/overview', 'load_functions' => '', @@ -46668,6 +46783,69 @@ 'language' => '', 'trid' => '0', )) +->values(array( + 'tid' => '9', + 'vid' => '3', + 'name' => 'fr - term 4 of vocabulary 3', + 'description' => '', + 'weight' => '0', + 'language' => 'fr', + 'trid' => '1', +)) +->values(array( + 'tid' => '10', + 'vid' => '3', + 'name' => 'zu - term 4 of vocabulary 3', + 'description' => '', + 'weight' => '0', + 'language' => 'zu', + 'trid' => '1', +)) +->values(array( + 'tid' => '11', + 'vid' => '3', + 'name' => 'term 7 of vocabulary 3', + 'description' => '', + 'weight' => '0', + 'language' => 'en', + 'trid' => '2', +)) +->values(array( + 'tid' => '12', + 'vid' => '3', + 'name' => 'fr - term 7 of vocabulary 3', + 'description' => '', + 'weight' => '0', + 'language' => 'fr', + 'trid' => '2', +)) +->values(array( + 'tid' => '13', + 'vid' => '3', + 'name' => 'zu - term 7 of vocabulary 3', + 'description' => '', + 'weight' => '0', + 'language' => 'zu', + 'trid' => '2', +)) +->values(array( + 'tid' => '14', + 'vid' => '5', + 'name' => 'Talos IV', + 'description' => 'The home of Captain Christopher Pike.', + 'weight' => '0', + 'language' => '', + 'trid' => '0', +)) +->values(array( + 'tid' => '15', + 'vid' => '5', + 'name' => 'Vulcan', + 'description' => '', + 'weight' => '0', + 'language' => '', + 'trid' => '0', +)) ->execute(); $connection->schema()->createTable('term_hierarchy', array( @@ -46719,6 +46897,34 @@ 'tid' => '8', 'parent' => '0', )) +->values(array( + 'tid' => '9', + 'parent' => '0', +)) +->values(array( + 'tid' => '10', + 'parent' => '0', +)) +->values(array( + 'tid' => '11', + 'parent' => '0', +)) +->values(array( + 'tid' => '12', + 'parent' => '0', +)) +->values(array( + 'tid' => '13', + 'parent' => '0', +)) +->values(array( + 'tid' => '14', + 'parent' => '0', +)) +->values(array( + 'tid' => '15', + 'parent' => '0', +)) ->values(array( 'tid' => '3', 'parent' => '2', @@ -47247,8 +47453,8 @@ 'signature' => '', 'signature_format' => '0', 'created' => '0', - 'access' => '1494966478', - 'login' => '1494966280', + 'access' => '1521962400', + 'login' => '1521961881', 'status' => '1', 'timezone' => NULL, 'language' => '', @@ -48317,7 +48523,7 @@ )) ->values(array( 'name' => 'i18ntaxonomy_vocabulary', - 'value' => 'a:2:{i:1;s:1:"3";i:2;s:1:"2";}', + 'value' => 'a:4:{i:1;s:1:"3";i:2;s:1:"2";i:3;s:1:"3";i:5;s:1:"1";}', )) ->values(array( 'name' => 'i18n_lock_node_article', diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php index abd6b8993caec1da96ef46d4eec024d17536ce39..52b37fdcda23f7e68891930037c1fc321af2e27f 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php @@ -81,7 +81,7 @@ protected function getEntityCounts() { 'shortcut_set' => 1, 'action' => 23, 'menu' => 8, - 'taxonomy_term' => 8, + 'taxonomy_term' => 15, 'taxonomy_vocabulary' => 7, 'tour' => 4, 'user' => 7, @@ -109,7 +109,7 @@ protected function getEntityCountsIncremental() { $counts['file'] = 9; $counts['menu_link_content'] = 11; $counts['node'] = 18; - $counts['taxonomy_term'] = 9; + $counts['taxonomy_term'] = 16; $counts['user'] = 8; $counts['view'] = 16; return $counts; diff --git a/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php b/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php new file mode 100644 index 0000000000000000000000000000000000000000..315332621923ec460714fcbc20e5112f953d3840 --- /dev/null +++ b/core/modules/taxonomy/src/Plugin/migrate/source/d6/TermLocalizedTranslation.php @@ -0,0 +1,99 @@ +addField('td', 'language', 'td.language'); + + // Add in the property, which is either name or description. + $query->leftJoin('i18n_strings', 'i18n', 'td.tid = i18n.objectid'); + $query->isNotNull('i18n.lid'); + $query->addField('i18n', 'lid'); + $query->addField('i18n', 'property'); + + // Add in the translation for the property. + $query->innerJoin('locales_target', 'lt', 'i18n.lid = lt.lid'); + $query->addField('lt', 'language', 'lt.language'); + $query->addField('lt', 'translation'); + return $query; + } + + /** + * {@inheritdoc} + */ + public function prepareRow(Row $row) { + $language = $row->getSourceProperty('ltlanguage'); + $row->setSourceProperty('language', $language); + $tid = $row->getSourceProperty('tid'); + + // If this row has been migrated it is a duplicate then skip it. + if ($this->idMap->lookupDestinationIds(['tid' => $tid, 'language' => $language])) { + return FALSE; + } + + // Save the translation for this property. + $property = $row->getSourceProperty('property'); + $row->setSourceProperty($property . '_translated', $row->getSourceProperty('translation')); + + // Get the translation, if one exists, for the property not already in the + // row. + $other_property = ($property == 'name') ? 'description' : 'name'; + $query = $this->select('i18n_strings', 'i18n') + ->fields('i18n', ['lid']) + ->condition('i18n.property', $other_property) + ->condition('i18n.objectid', $tid); + $query->leftJoin('locales_target', 'lt', 'i18n.lid = lt.lid'); + $query->condition('lt.language', $language); + $query->addField('lt', 'translation'); + $results = $query->execute()->fetchAssoc(); + $row->setSourceProperty($other_property . '_translated', $results['translation']); + + parent::prepareRow($row); + } + + /** + * {@inheritdoc} + */ + public function fields() { + $fields = [ + 'language' => $this->t('Language for this term.'), + 'name_translated' => $this->t('Term name translation.'), + 'description_translated' => $this->t('Term description translation.'), + ]; + return parent::fields() + $fields; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + $ids['language']['type'] = 'string'; + $ids['language']['alias'] = 'lt'; + return parent::getIds() + $ids; + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermLocalizedTranslationTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermLocalizedTranslationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4c5e216163d52faf4d513321819ddc315d84fcd9 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermLocalizedTranslationTest.php @@ -0,0 +1,140 @@ +installEntitySchema('taxonomy_term'); + $this->installConfig(static::$modules); + $this->executeMigrations([ + 'language', + 'd6_node_type', + 'd6_field', + 'd6_taxonomy_vocabulary', + 'd6_field_instance', + 'd6_taxonomy_term', + 'd6_taxonomy_term_localized_translation', + ]); + } + + /** + * Validates a migrated term contains the expected values. + * + * @param int $id + * Entity ID to load and check. + * @param string $expected_language + * The language code for this term. + * @param string $expected_label + * The label the migrated entity should have. + * @param string $expected_vid + * The parent vocabulary the migrated entity should have. + * @param string $expected_description + * The description the migrated entity should have. + * @param string $expected_format + * The format the migrated entity should have. + * @param int $expected_weight + * The weight the migrated entity should have. + * @param array $expected_parents + * The parent terms the migrated entity should have. + * @param int $expected_field_integer_value + * The value the migrated entity field should have. + * @param int $expected_term_reference_tid + * The term reference ID the migrated entity field should have. + */ + protected function assertEntity($id, $expected_language, $expected_label, $expected_vid, $expected_description = '', $expected_format = NULL, $expected_weight = 0, array $expected_parents = [], $expected_field_integer_value = NULL, $expected_term_reference_tid = NULL) { + /** @var \Drupal\taxonomy\TermInterface $entity */ + $entity = Term::load($id); + $this->assertInstanceOf(TermInterface::class, $entity); + $this->assertSame($expected_language, $entity->language()->getId()); + $this->assertSame($expected_label, $entity->label()); + $this->assertSame($expected_vid, $entity->bundle()); + $this->assertSame($expected_description, $entity->getDescription()); + $this->assertSame($expected_format, $entity->getFormat()); + $this->assertSame($expected_weight, $entity->getWeight()); + $this->assertHierarchy($expected_vid, $id, $expected_parents); + } + + /** + * Asserts that a term is present in the tree storage, with the right parents. + * + * @param string $vid + * Vocabulary ID. + * @param int $tid + * ID of the term to check. + * @param array $parent_ids + * The expected parent term IDs. + */ + protected function assertHierarchy($vid, $tid, array $parent_ids) { + if (!isset($this->treeData[$vid])) { + $tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid); + $this->treeData[$vid] = []; + foreach ($tree as $item) { + $this->treeData[$vid][$item->tid] = $item; + } + } + + $this->assertArrayHasKey($tid, $this->treeData[$vid], "Term $tid exists in taxonomy tree"); + $term = $this->treeData[$vid][$tid]; + $this->assertEquals($parent_ids, array_filter($term->parents), "Term $tid has correct parents in taxonomy tree"); + } + + /** + * Tests the Drupal 6 i18n localized taxonomy term to Drupal 8 migration. + */ + public function testTranslatedLocalizedTaxonomyTerms() { + $this->assertEntity(14, 'en', 'Talos IV', 'vocabulary_name_much_longer_than', 'The home of Captain Christopher Pike.', NULL, '0', []); + $this->assertEntity(15, 'en', 'Vulcan', 'vocabulary_name_much_longer_than', NULL, NULL, '0', []); + + /** @var \Drupal\taxonomy\TermInterface $entity */ + $entity = Term::load(14); + $this->assertTrue($entity->hasTranslation('fr')); + $translation = $entity->getTranslation('fr'); + $this->assertSame('fr - Talos IV', $translation->label()); + $this->assertSame('fr - The home of Captain Christopher Pike.', $translation->getDescription()); + + $this->assertTrue($entity->hasTranslation('zu')); + $translation = $entity->getTranslation('zu'); + $this->assertSame('Talos IV', $translation->label()); + $this->assertSame('zu - The home of Captain Christopher Pike.', $translation->getDescription()); + + $entity = Term::load(15); + $this->assertFalse($entity->hasTranslation('fr')); + $this->assertTrue($entity->hasTranslation('zu')); + $translation = $entity->getTranslation('zu'); + $this->assertSame('zu - Vulcan', $translation->label()); + $this->assertSame('', $translation->getDescription()); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/TermLocalizedTranslationTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/TermLocalizedTranslationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c2fea26d1040f3315f2de4f2f391208c41622993 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d6/TermLocalizedTranslationTest.php @@ -0,0 +1,179 @@ + 1, + 'vid' => 5, + 'name' => 'name value 1', + 'description' => 'description value 1', + 'weight' => 0, + 'language' => NULL, + ], + [ + 'tid' => 2, + 'vid' => 6, + 'name' => 'name value 2', + 'description' => 'description value 2', + 'weight' => 0, + 'language' => NULL, + ], + [ + 'tid' => 3, + 'vid' => 6, + 'name' => 'name value 3', + 'description' => 'description value 3', + 'weight' => 0, + 'language' => NULL, + ], + [ + 'tid' => 4, + 'vid' => 5, + 'name' => 'name value 4', + 'description' => 'description value 4', + 'weight' => 1, + 'language' => NULL, + ], + ]; + $tests[0]['source_data']['term_hierarchy'] = [ + [ + 'tid' => 1, + 'parent' => 0, + ], + [ + 'tid' => 2, + 'parent' => 0, + ], + [ + 'tid' => 3, + 'parent' => 0, + ], + [ + 'tid' => 4, + 'parent' => 1, + ], + ]; + $tests[0]['source_data']['i18n_strings'] = [ + [ + 'lid' => 6, + 'objectid' => 1, + 'type' => 'term', + 'property' => 'name', + 'objectindex' => '1', + 'format' => 0, + ], + [ + 'lid' => 7, + 'objectid' => 1, + 'type' => 'term', + 'property' => 'description', + 'objectindex' => '1', + 'format' => 0, + ], + [ + 'lid' => 8, + 'objectid' => 3, + 'type' => 'term', + 'property' => 'name', + 'objectindex' => '3', + 'format' => 0, + ], + ]; + $tests[0]['source_data']['locales_target'] = [ + [ + 'lid' => 6, + 'language' => 'fr', + 'translation' => 'fr - name value 1 translation', + 'plid' => 0, + 'plural' => 0, + 'i18n_status' => 0, + ], + [ + 'lid' => 7, + 'language' => 'fr', + 'translation' => 'fr - description value 1 translation', + 'plid' => 0, + 'plural' => 0, + 'i18n_status' => 0, + ], + [ + 'lid' => 8, + 'language' => 'zu', + 'translation' => 'zu - description value 2 translation', + 'plid' => 0, + 'plural' => 0, + 'i18n_status' => 0, + ], + ]; + + // The expected results. + $tests[0]['expected_data'] = [ + [ + 'tid' => 1, + 'vid' => 5, + 'name' => 'name value 1', + 'description' => 'description value 1', + 'weight' => 0, + 'parent' => [0], + 'property' => 'name', + 'language' => 'fr', + 'name_translated' => 'fr - name value 1 translation', + 'description_translated' => 'fr - description value 1 translation', + ], + [ + 'tid' => 1, + 'vid' => 5, + 'name' => 'name value 1', + 'description' => 'description value 1', + 'weight' => 0, + 'parent' => [0], + 'property' => 'description', + 'language' => 'fr', + 'name_translated' => 'fr - name value 1 translation', + 'description_translated' => 'fr - description value 1 translation', + ], + [ + 'tid' => 3, + 'vid' => 6, + 'name' => 'name value 3', + 'description' => 'description value 3', + 'weight' => 0, + 'parent' => [0], + 'property' => 'name', + 'language' => 'zu', + 'name_translated' => 'zu - description value 2 translation', + 'description_translated' => NULL, + ], + ]; + + $tests[0]['expected_count'] = NULL; + // Empty configuration will return terms for all vocabularies. + $tests[0]['configuration'] = []; + + return $tests; + } + +}