diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php index 55c59fc..a534851 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php @@ -109,7 +109,8 @@ public function fields(MigrationInterface $migration = NULL) { protected function getEntity(Row $row, array $old_destination_id_values) { $entity_id = reset($old_destination_id_values) ?: $this->getEntityId($row); if (!empty($entity_id) && ($entity = $this->storage->load($entity_id))) { - $this->updateEntity($entity, $row); + // Allow updateEntity() to change the entity. + $entity = $this->updateEntity($entity, $row) ?: $entity; } else { // Stubs might need some required fields filled in. diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php index 6a2fb98..9430628 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; +use Drupal\Core\TypedData\TranslatableInterface; use Drupal\Core\TypedData\TypedDataInterface; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateException; @@ -85,7 +86,40 @@ public function import(Row $row, array $old_destination_id_values = array()) { if (!$entity) { throw new MigrateException('Unable to get entity'); } - return $this->save($entity, $old_destination_id_values); + + $ids = $this->save($entity, $old_destination_id_values); + if (!empty($this->configuration['source_language_keys'])) { + $ids[] = $entity->language()->getId(); + } + return $ids; + } + + /** + * Get the destination ID values of a translation of this entity. + * + * @param \Drupal\migrate\Row $row + * The row object. + * @param array &$destination_ids + * The old destination IDs, to be modified by this method. + */ + protected function getTranslatedDestinationIds(Row $row, array &$destination_ids) { + if (empty($destination_ids) && isset($this->configuration['source_language_keys'])) { + // Get source IDs, without language keys. + $source_language_keys = (array) $this->configuration['source_language_keys']; + $source_ids = array_diff_key($row->getSourceIdValues(), array_flip($source_language_keys)); + + // Try to lookup another translation of the same entity. + $dest_ids = $this->migration->getIdMap()->lookupDestinationIds($source_ids); + $destination_ids = reset($dest_ids) ?: []; + } + } + + /** + * {@inheritdoc} + */ + protected function getEntity(Row $row, array $old_destination_id_values) { + $this->getTranslatedDestinationIds($row, $old_destination_id_values); + return parent::getEntity($row, $old_destination_id_values); } /** @@ -105,14 +139,36 @@ protected function save(ContentEntityInterface $entity, array $old_destination_i } /** - * {@inheritdoc} + * Get the base IDs of this entity, not including dynamically determined IDs. + * + * @return array + * An array of IDs. */ - public function getIds() { + protected function baseIds() { $id_key = $this->getKey('id'); $ids[$id_key]['type'] = 'integer'; return $ids; } + + /** + * {@inheritdoc} + */ + public function getIds() { + $ids = $this->baseIds(); + + if (!empty($this->configuration['source_language_keys'])) { + if ($key = $this->getKey('langcode')) { + $ids[$key]['type'] = 'string'; + } + else { + throw new MigrateException('This entity type does not support translation.'); + } + } + + return $ids; + } + /** * Updates an entity with the new values from row. * @@ -122,6 +178,18 @@ public function getIds() { * The row object to update from. */ protected function updateEntity(EntityInterface $entity, Row $row) { + // Make sure we have the right translation. + if ($entity instanceof TranslatableInterface) { + $property = $this->storage->getEntityType()->getKey('langcode'); + if ($row->hasDestinationProperty($property)) { + $language = $row->getDestinationProperty($property); + if (!$entity->hasTranslation($language)) { + $entity->addTranslation($language); + } + $entity = $entity->getTranslation($language); + } + } + // If the migration has specified a list of properties to be overwritten, // clone the row with an empty set of destination values, and re-add only // the specified properties. @@ -141,6 +209,9 @@ protected function updateEntity(EntityInterface $entity, Row $row) { } $this->setRollbackAction($row->getIdMap()); + + // We might have a different (translated) entity, so return it. + return $entity; } /** diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal6.php b/core/modules/migrate_drupal/tests/fixtures/drupal6.php index 1ca67a5..9a55470 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal6.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal6.php @@ -34727,7 +34727,7 @@ 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:7:"company";s:4:"name";s:7:"Company";s:6:"module";s:4:"node";s:11:"description";s:17:"Company node type";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:4:"Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:11:"Description";s:14:"min_word_count";s:2:"20";s:6:"custom";s:1:"0";s:8:"modified";s:1:"0";s:6:"locked";s:1:"0";s:9:"orig_type";s:7:"company";}}', + 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:7:"company";s:4:"name";s:7:"Company";s:6:"module";s:4:"node";s:11:"description";s:17:"Company node type";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:4:"Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:11:"Description";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"0";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:7:"company";}}', 'fit' => '15', 'number_parts' => '4', 'tab_parent' => '', @@ -34749,7 +34749,7 @@ 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:2:{i:0;s:24:"node_type_delete_confirm";i:1;O:8:"stdClass":14:{s:4:"type";s:7:"company";s:4:"name";s:7:"Company";s:6:"module";s:4:"node";s:11:"description";s:17:"Company node type";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:4:"Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:11:"Description";s:14:"min_word_count";s:2:"20";s:6:"custom";s:1:"0";s:8:"modified";s:1:"0";s:6:"locked";s:1:"0";s:9:"orig_type";s:7:"company";}}', + 'page_arguments' => 'a:2:{i:0;s:24:"node_type_delete_confirm";i:1;O:8:"stdClass":14:{s:4:"type";s:7:"company";s:4:"name";s:7:"Company";s:6:"module";s:4:"node";s:11:"description";s:17:"Company node type";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:4:"Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:11:"Description";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"0";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:7:"company";}}', 'fit' => '31', 'number_parts' => '5', 'tab_parent' => '', @@ -34859,7 +34859,7 @@ 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:7:"company";s:4:"name";s:7:"Company";s:6:"module";s:4:"node";s:11:"description";s:17:"Company node type";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:4:"Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:11:"Description";s:14:"min_word_count";s:2:"20";s:6:"custom";s:1:"0";s:8:"modified";s:1:"0";s:6:"locked";s:1:"0";s:9:"orig_type";s:7:"company";}}', + 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:7:"company";s:4:"name";s:7:"Company";s:6:"module";s:4:"node";s:11:"description";s:17:"Company node type";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:4:"Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:11:"Description";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"0";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:7:"company";}}', 'fit' => '31', 'number_parts' => '5', 'tab_parent' => 'admin/content/node-type/company', @@ -41338,18 +41338,35 @@ ->values(array( 'nid' => '9', 'vid' => '12', - 'type' => 'story', - 'language' => '', - 'title' => 'Once upon a time', + 'type' => 'article', + 'language' => 'en', + 'title' => 'The Real McCoy', 'uid' => '1', 'status' => '1', - 'created' => '1444671588', - 'changed' => '1444671588', + 'created' => '1444238800', + 'changed' => '1444238808', 'comment' => '2', 'promote' => '1', 'moderate' => '0', 'sticky' => '0', - 'tnid' => '0', + 'tnid' => '9', + 'translate' => '0', +)) +->values(array( + 'nid' => '10', + 'vid' => '13', + 'type' => 'article', + 'language' => 'fr', + 'title' => 'Le Vrai McCoy', + 'uid' => '1', + 'status' => '1', + 'created' => '1444239050', + 'changed' => '1444239050', + 'comment' => '2', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '9', 'translate' => '0', )) ->execute(); @@ -41497,7 +41514,14 @@ )) ->values(array( 'nid' => '9', - 'last_comment_timestamp' => '1444671588', + 'last_comment_timestamp' => '1444238800', + 'last_comment_name' => NULL, + 'last_comment_uid' => '1', + 'comment_count' => '0', +)) +->values(array( + 'nid' => '10', + 'last_comment_timestamp' => '1444239050', 'last_comment_name' => NULL, 'last_comment_uid' => '1', 'comment_count' => '0', @@ -41738,11 +41762,22 @@ 'nid' => '9', 'vid' => '12', 'uid' => '1', - 'title' => 'Once upon a time', - 'body' => 'Come on kid, go to sleep.', - 'teaser' => 'Come on kid, go to sleep.', + 'title' => 'The Real McCoy', + 'body' => "In the original, Queen's English.", + 'teaser' => "In the original, Queen's English.", + 'log' => '', + 'timestamp' => '1444238808', + 'format' => '1', +)) +->values(array( + 'nid' => '10', + 'vid' => '13', + 'uid' => '1', + 'title' => 'Le Vrai McCoy', + 'body' => 'Ooh là là!', + 'teaser' => 'Ooh là là!', 'log' => '', - 'timestamp' => '1444671588', + 'timestamp' => '1444239050', 'format' => '1', )) ->execute(); @@ -41879,9 +41914,9 @@ 'title_label' => 'Name', 'has_body' => '1', 'body_label' => 'Description', - 'min_word_count' => '20', + 'min_word_count' => '0', 'custom' => '0', - 'modified' => '0', + 'modified' => '1', 'locked' => '0', 'orig_type' => 'company', )) @@ -44828,12 +44863,16 @@ 'value' => 's:1:"2";', )) ->values(array( + 'name' => 'comment_company', + 'value' => 's:1:"2";', +)) +->values(array( 'name' => 'comment_controls_article', 'value' => 'i:3;', )) ->values(array( 'name' => 'comment_controls_company', - 'value' => 'i:3;', + 'value' => 's:1:"3";', )) ->values(array( 'name' => 'comment_controls_employee', @@ -44873,7 +44912,7 @@ )) ->values(array( 'name' => 'comment_default_mode_company', - 'value' => 'i:4;', + 'value' => 's:1:"4";', )) ->values(array( 'name' => 'comment_default_mode_employee', @@ -44913,7 +44952,7 @@ )) ->values(array( 'name' => 'comment_default_order_company', - 'value' => 'i:1;', + 'value' => 's:1:"1";', )) ->values(array( 'name' => 'comment_default_order_employee', @@ -44953,7 +44992,7 @@ )) ->values(array( 'name' => 'comment_default_per_page_company', - 'value' => 'i:50;', + 'value' => 's:2:"50";', )) ->values(array( 'name' => 'comment_default_per_page_employee', @@ -44993,7 +45032,7 @@ )) ->values(array( 'name' => 'comment_form_location_company', - 'value' => 'i:0;', + 'value' => 's:1:"0";', )) ->values(array( 'name' => 'comment_form_location_employee', @@ -45037,7 +45076,7 @@ )) ->values(array( 'name' => 'comment_preview_company', - 'value' => 'i:1;', + 'value' => 's:1:"1";', )) ->values(array( 'name' => 'comment_preview_employee', @@ -45081,7 +45120,7 @@ )) ->values(array( 'name' => 'comment_subject_field_company', - 'value' => 'i:1;', + 'value' => 's:1:"1";', )) ->values(array( 'name' => 'comment_subject_field_employee', @@ -45444,6 +45483,10 @@ 'value' => 's:5:"never";', )) ->values(array( + 'name' => 'event_nodeapi_company', + 'value' => 's:5:"never";', +)) +->values(array( 'name' => 'event_nodeapi_event', 'value' => 's:3:"all";', )) @@ -45505,7 +45548,11 @@ )) ->values(array( 'name' => 'form_build_id_article', - 'value' => 's:48:"form-mXZfFJxcCFGB80PPYtNOuwYbho6-xKTvrRLb3TAMkic";', + 'value' => 's:48:"form-t2zKJflpBD4rpYoGQH33ckjjWAYdo5lF3Hl1O_YnWyE";', +)) +->values(array( + 'name' => 'form_build_id_company', + 'value' => 's:48:"form-jFw2agRukPxjG5dG-N6joZLyoxXmCoxTzua0HUciqK0";', )) ->values(array( 'name' => 'forum_block_num_0', @@ -45600,6 +45647,10 @@ 'value' => 'a:1:{i:0;s:6:"status";}', )) ->values(array( + 'name' => 'node_options_company', + 'value' => 'a:2:{i:0;s:6:"status";i:1;s:7:"promote";}', +)) +->values(array( 'name' => 'node_options_forum', 'value' => 'a:1:{i:0;s:6:"status";}', )) @@ -45796,6 +45847,10 @@ 'value' => 'b:0;', )) ->values(array( + 'name' => 'upload_company', + 'value' => 's:1:"1";', +)) +->values(array( 'name' => 'upload_page', 'value' => 'b:1;', )) diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php index 761e718..476ea38 100644 --- a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php +++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php @@ -86,6 +86,7 @@ protected function migrateFields() { * If TRUE, migrates node revisions. */ protected function migrateContent($include_revisions = FALSE) { + $this->executeMigrations(['language']); $this->migrateUsers(FALSE); $this->migrateFields(); diff --git a/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php b/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php index d073c18..32d7e1f 100644 --- a/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php +++ b/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php @@ -14,6 +14,13 @@ class MigrateUpgrade6Test extends MigrateUpgradeTestBase { /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['language']; + + /** * {@inheritdoc} */ protected function setUp() { @@ -39,8 +46,9 @@ protected function getEntityCounts() { 'comment' => 3, 'comment_type' => 2, 'contact_form' => 5, + 'configurable_language' => 5, 'editor' => 2, - 'field_config' => 62, + 'field_config' => 63, 'field_storage_config' => 43, 'file' => 7, 'filter_format' => 7, @@ -56,7 +64,7 @@ protected function getEntityCounts() { 'menu' => 8, 'taxonomy_term' => 6, 'taxonomy_vocabulary' => 6, - 'tour' => 1, + 'tour' => 4, 'user' => 7, 'user_role' => 6, 'menu_link_content' => 4, diff --git a/core/modules/node/migration_templates/d6_node.yml b/core/modules/node/migration_templates/d6_node.yml index 82571b8..0b61a6b 100644 --- a/core/modules/node/migration_templates/d6_node.yml +++ b/core/modules/node/migration_templates/d6_node.yml @@ -38,6 +38,7 @@ process: destination: plugin: entity:node + source_language_keys: language migration_dependencies: required: - d6_user diff --git a/core/modules/node/src/Plugin/migrate/source/d6/Node.php b/core/modules/node/src/Plugin/migrate/source/d6/Node.php index c70fd27..d39c486 100644 --- a/core/modules/node/src/Plugin/migrate/source/d6/Node.php +++ b/core/modules/node/src/Plugin/migrate/source/d6/Node.php @@ -15,6 +15,11 @@ class Node extends DrupalSqlBase { /** + * An expression for the translation set of a node. + */ + const NODE_TRANSLATION_SET = 'CASE n.tnid WHEN 0 THEN n.nid ELSE n.tnid END'; + + /** * The join options between the node and the node_revisions table. */ const JOIN = 'n.vid = nr.vid'; @@ -37,9 +42,9 @@ class Node extends DrupalSqlBase { * {@inheritdoc} */ public function query() { - // Select node in its last revision. - $query = $this->select('node_revisions', 'nr') - ->fields('n', array( + $query = $this->translationQuery(); + + $query->fields('n', array( 'nid', 'type', 'language', @@ -54,7 +59,6 @@ public function query() { 'translate', )) ->fields('nr', array( - 'vid', 'title', 'body', 'teaser', @@ -64,10 +68,13 @@ public function query() { )); $query->addField('n', 'uid', 'node_uid'); $query->addField('nr', 'uid', 'revision_uid'); - $query->innerJoin('node', 'n', static::JOIN); + + // Whatever we claim this revision is, use the actual field from + // node_revisions to get field values. + $query->addField('nr', 'vid', 'vid_for_fields'); if (isset($this->configuration['node_type'])) { - $query->condition('type', $this->configuration['node_type']); + $query->condition('n.type', $this->configuration['node_type']); } return $query; @@ -123,6 +130,11 @@ public function prepareRow(Row $row) { } } + // Use the same nid for translation sets. + if ($tnid = $row->getSourceProperty('tnid')) { + $row->setSourceProperty('nid', $tnid); + } + return parent::prepareRow($row); } @@ -233,7 +245,7 @@ protected function getCckData(array $field, Row $node) { // the time being. ->isNotNull($field['field_name'] . '_' . $columns[0]) ->condition('nid', $node->getSourceProperty('nid')) - ->condition('vid', $node->getSourceProperty('vid')) + ->condition('vid', $node->getSourceProperty('vid_for_fields')) ->execute() ->fetchAllAssoc('delta'); } @@ -248,7 +260,44 @@ protected function getCckData(array $field, Row $node) { public function getIds() { $ids['nid']['type'] = 'integer'; $ids['nid']['alias'] = 'n'; + $ids['language']['type'] = 'string'; + $ids['language']['alias'] = 'n'; return $ids; } + /** + * Build a query to get the maximum vid of each translation set. + * + * @return \Drupal\Core\Database\Query\SelectInterface + * The generated query. + */ + protected function maxVidQuery() { + $subquery = $this->select('node', 'n'); + $subquery->addExpression(self::NODE_TRANSLATION_SET, 'translation_set'); + $subquery->groupBy('translation_set'); + $subquery->addExpression('MAX(vid)', 'vid'); + return $subquery; + } + + /** + * Build a query that yields the rows we want to migrate. + * + * It should have a node table 'n', and a node_revision table 'nr'. + * + * @return \Drupal\Core\Database\Query\SelectInterface + * The generated query. + */ + protected function translationQuery() { + $query = $this->select('node_revisions', 'nr'); + $query->innerJoin('node', 'n', static::JOIN); + + // Claim our vid is the maximum vid of our translation set. + // Otherwise we generate translations of the same node with different + // revisions, which confuses Drupal. + $query->join($this->maxVidQuery(), 'max_vid', + 'max_vid.translation_set IN (n.nid, n.tnid)'); + $query->fields('max_vid', ['vid']); + + return $query; + } } diff --git a/core/modules/node/src/Plugin/migrate/source/d6/NodeRevision.php b/core/modules/node/src/Plugin/migrate/source/d6/NodeRevision.php index 41e7a18..e6894d4 100644 --- a/core/modules/node/src/Plugin/migrate/source/d6/NodeRevision.php +++ b/core/modules/node/src/Plugin/migrate/source/d6/NodeRevision.php @@ -37,4 +37,14 @@ public function getIds() { return $ids; } + /** + * {@inheritdoc} + */ + protected function translationQuery() { + $query = $this->select('node_revisions', 'nr'); + $query->innerJoin('node', 'n', static::JOIN); + $query->addField('nr', 'vid'); + return $query; + } + } diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTest.php index 7f07d11..715300b 100644 --- a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTest.php +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTest.php @@ -5,6 +5,7 @@ use Drupal\Core\Database\Database; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\node\Entity\Node; +use Drupal\node\NodeInterface; use Drupal\Tests\file\Kernel\Migrate\d6\FileMigrationTestTrait; /** @@ -19,11 +20,16 @@ class MigrateNodeTest extends MigrateNodeTestBase { /** * {@inheritdoc} */ + public static $modules = ['language']; + + /** + * {@inheritdoc} + */ protected function setUp() { parent::setUp(); $this->setUpMigratedFiles(); $this->installSchema('file', ['file_usage']); - $this->executeMigrations(['d6_node']); + $this->executeMigrations(['language', 'd6_node']); } /** @@ -79,6 +85,15 @@ public function testNode() { $this->assertIdentical('Drupal Groups', $node->field_test_link->title); $this->assertIdentical([], $node->field_test_link->options['attributes']); + // Test that translations are working. + $node = Node::load(9); + $this->assertTrue($node instanceof NodeInterface); + $this->assertIdentical('en', $node->langcode->value); + $this->assertIdentical('The Real McCoy', $node->title->value); + + // Node 10 is a translation of node 9, and should not be imported separately. + $this->assertNull(Node::load(10)); + // Rerun migration with invalid link attributes and a different URL and // title. If only the attributes are changed the error does not occur. Database::getConnection('default', 'migrate') @@ -109,7 +124,9 @@ public function testNode() { // Now insert a row indicating a failure and set to update later. $title = $this->rerunMigration(array( 'sourceid1' => 2, + 'sourceid2' => 'en', 'destid1' => NULL, + 'destid2' => NULL, 'source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE, )); $node = Node::load(2); @@ -140,7 +157,10 @@ protected function rerunMigration($new_row = []) { $default_connection = \Drupal::database(); $default_connection->truncate($table_name)->execute(); if ($new_row) { - $hash = $migration->getIdMap()->getSourceIDsHash(['nid' => $new_row['sourceid1']]); + $hash = $migration->getIdMap()->getSourceIDsHash([ + 'nid' => $new_row['sourceid1'], + 'language' => $new_row['sourceid2'] + ]); $new_row['source_ids_hash'] = $hash; $default_connection->insert($table_name) ->fields($new_row) diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTest.php index 79f2bac..80f75b0 100644 --- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTest.php +++ b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeTest.php @@ -95,6 +95,52 @@ class NodeTest extends MigrateSqlSourceTestCase { array('value' => '3.14159'), ), ), + array( + 'nid' => 6, + 'vid' => 7, + 'type' => 'story', + 'language' => 'en', + 'title' => 'node title 6', + 'uid' => 1, + 'status' => 1, + 'created' => 1279290909, + 'changed' => 1279308994, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'tnid' => 6, + 'translate' => 0, + // Node revision fields. + 'body' => 'body for node 6', + 'teaser' => 'body for node 6', + 'log' => '', + 'timestamp' => 1279308994, + 'format' => 1, + ), + array( + 'nid' => 6, + 'vid' => 7, + 'type' => 'story', + 'language' => 'fr', + 'title' => 'node title 7', + 'uid' => 1, + 'status' => 1, + 'created' => 1279290910, + 'changed' => 1279308995, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'tnid' => 6, + 'translate' => 0, + // Node revision fields. + 'body' => 'body for node 7', + 'teaser' => 'body for node 7', + 'log' => '', + 'timestamp' => 1279308995, + 'format' => 1, + ), ); /** @@ -145,22 +191,114 @@ protected function setUp() { 'status' => TRUE, ), ); - foreach ($this->expectedResults as $k => $row) { + $this->databaseContents['node'] = [ + [ + 'nid' => 1, + 'vid' => 1, + 'type' => 'page', + 'language' => 'en', + 'title' => 'node title 1', + 'uid' => 1, + 'status' => 1, + 'created' => 1279051598, + 'changed' => 1279051598, + 'comment' => 2, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'tnid' => 0, + ], + [ + 'nid' => 2, + 'vid' => 2, + 'type' => 'page', + 'language' => 'en', + 'title' => 'node title 2', + 'uid' => 1, + 'status' => 1, + 'created' => 1279290908, + 'changed' => 1279308993, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'tnid' => 0, + ], + [ + 'nid' => 5, + 'vid' => 5, + 'type' => 'story', + 'language' => 'en', + 'title' => 'node title 5', + 'uid' => 1, + 'status' => 1, + 'created' => 1279290908, + 'changed' => 1279308993, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'tnid' => 0, + ], + [ + 'nid' => 6, + 'vid' => 6, + 'type' => 'story', + 'language' => 'en', + 'title' => 'node title 6', + 'uid' => 1, + 'status' => 1, + 'created' => 1279290909, + 'changed' => 1279308994, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'tnid' => 6, + ], + [ + 'nid' => 7, + 'vid' => 7, + 'type' => 'story', + 'language' => 'fr', + 'title' => 'node title 7', + 'uid' => 1, + 'status' => 1, + 'created' => 1279290910, + 'changed' => 1279308995, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'tnid' => 6, + ], + ]; + + foreach ($this->databaseContents['node'] as $k => $row) { + // Find the equivalent row from expected results. + $result_row = NULL; + foreach ($this->expectedResults as $result) { + if (in_array($result['nid'], [$row['nid'], $row['tnid']]) && $result['language'] == $row['language']) { + $result_row = $result; + break; + } + } + + // Populate node_revisions. foreach (array('nid', 'vid', 'title', 'uid', 'body', 'teaser', 'format', 'timestamp', 'log') as $field) { - $this->databaseContents['node_revisions'][$k][$field] = $row[$field]; - switch ($field) { - case 'nid': case 'vid': - break; - case 'uid': - $this->databaseContents['node_revisions'][$k]['uid']++; - break; - default: - unset($row[$field]); - break; + $value = isset($row[$field]) ? $row[$field] : $result_row[$field]; + $this->databaseContents['node_revisions'][$k][$field] = $value; + if ($field == 'uid') { + $this->databaseContents['node_revisions'][$k]['uid']++; } } - $this->databaseContents['node'][$k] = $row; } + array_walk($this->expectedResults, function (&$row) { $row['node_uid'] = $row['uid']; $row['revision_uid'] = $row['uid'] + 1;