diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php index 6a2fb98..f9ab09f 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php @@ -7,11 +7,13 @@ 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; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\Row; + use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -122,6 +124,19 @@ public function getIds() { * The row object to update from. */ protected function updateEntity(EntityInterface $entity, Row $row) { + 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. diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php index 62469e7..fbc39eb 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php @@ -41,6 +41,10 @@ protected function getEntity(Row $row, array $old_destination_id_values) { $row->getDestinationProperty($this->getKey('revision')); if (!empty($revision_id) && ($entity = $this->storage->loadRevision($revision_id))) { $entity->setNewRevision(FALSE); + + // Check if we're the default. + $default = $this->storage->load($entity->id()); + $entity->isDefaultRevision($default->getRevisionId() === $revision_id); } else { $entity_id = $row->getDestinationProperty($this->getKey('id')); @@ -54,9 +58,10 @@ protected function getEntity(Row $row, array $old_destination_id_values) { $entity->enforceIsNew(FALSE); $entity->setNewRevision(TRUE); + $entity->isDefaultRevision(FALSE); } + $this->updateEntity($entity, $row); - $entity->isDefaultRevision(FALSE); return $entity; } @@ -65,7 +70,13 @@ protected function getEntity(Row $row, array $old_destination_id_values) { */ protected function save(ContentEntityInterface $entity, array $old_destination_id_values = array()) { $entity->save(); - return array($entity->getRevisionId()); + $ids = array($entity->getRevisionId()); + + if (isset($this->configuration['translated']) && $this->configuration['translated']) { + $ids[] = $entity->language()->getId(); + } + + return $ids; } /** @@ -74,9 +85,21 @@ protected function save(ContentEntityInterface $entity, array $old_destination_i public function getIds() { if ($key = $this->getKey('revision')) { $ids[$key]['type'] = 'integer'; - return $ids; } - throw new MigrateException('This entity type does not support revisions.'); + else { + throw new MigrateException('This entity type does not support revisions.'); + } + + if (isset($this->configuration['translated']) && $this->configuration['translated']) { + if ($key = $this->getKey('langcode')) { + $ids[$key]['type'] = 'string'; + } + else { + throw new MigrateException('This entity type does not support translation.'); + } + } + + return $ids; } } diff --git a/core/modules/migrate/src/Tests/MigrateFilterExecutable.php b/core/modules/migrate/src/Tests/MigrateFilterExecutable.php new file mode 100644 index 0000000..422904e --- /dev/null +++ b/core/modules/migrate/src/Tests/MigrateFilterExecutable.php @@ -0,0 +1,46 @@ +filter = $filter; + } + + public function processRow(Row $row, array $process = NULL, $value = NULL) { + if (!call_user_func($this->filter, $this->migration, $row)) { + throw new MigrateSkipRowException("Row filtered", FALSE); + } + parent::processRow($row, $process, $value); + } + +} diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php index f1e288e..2759ad7 100644 --- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php +++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php @@ -50,6 +50,22 @@ protected function setUp() { $this->storage = $this->prophesize('\Drupal\Core\Entity\EntityStorageInterface'); $this->entityManager = $this->prophesize('\Drupal\Core\Entity\EntityManagerInterface'); $this->fieldTypeManager = $this->prophesize('\Drupal\Core\Field\FieldTypePluginManagerInterface'); + + // Add a mock for getting keys. + $entity_type = $this->prophesize('\Drupal\Core\Entity\EntityTypeInterface'); + $entity_type->getKey('id')->willReturn('nid'); + $entity_type->getKey('revision')->willReturn('vid'); + $this->storage->getEntityType()->willReturn($entity_type->reveal()); + + // Add the default revision of the entity. + // Claim to be revision 12 of node 5. + $default = $this->prophesize('\Drupal\Core\Entity\EntityInterface') + ->willImplement('\Drupal\Core\Entity\RevisionableInterface'); + $default->getRevisionId() + ->willReturn(12); + $default->id()->willReturn(5); + $this->storage->load(5) + ->willReturn($default->reveal()); } /** @@ -62,6 +78,14 @@ public function testGetEntityDestinationValues() { // Return a dummy because we don't care what gets called. $entity = $this->prophesize('\Drupal\Core\Entity\EntityInterface') ->willImplement('\Drupal\Core\Entity\RevisionableInterface'); + + // Make sure we check whether or not we're the default. + $entity->id()->willReturn(5); + $this->storage->load(5)->shouldBeCalled(); + $entity->isDefaultRevision(TRUE)->shouldBeCalled(); + + $entity->setNewRevision(FALSE)->shouldBeCalled(); + // Assert that the first ID from the destination values is used to load the // entity. $this->storage->loadRevision(12) @@ -81,19 +105,19 @@ public function testGetEntityUpdateRevision() { $entity = $this->prophesize('\Drupal\Core\Entity\EntityInterface') ->willImplement('\Drupal\Core\Entity\RevisionableInterface'); - $entity_type = $this->prophesize('\Drupal\Core\Entity\EntityTypeInterface'); - $entity_type->getKey('id')->willReturn('nid'); - $entity_type->getKey('revision')->willReturn('vid'); - $this->storage->getEntityType()->willReturn($entity_type->reveal()); - // Assert we load the correct revision. $this->storage->loadRevision(2) ->shouldBeCalled() ->willReturn($entity->reveal()); - // Make sure its set as an update and not the default revision. - $entity->setNewRevision(FALSE)->shouldBeCalled(); + + // Make sure we check whether or not we're the default. + $entity->id()->willReturn(5); + $this->storage->load(5)->shouldBeCalled(); $entity->isDefaultRevision(FALSE)->shouldBeCalled(); + // Make sure its set as an update. + $entity->setNewRevision(FALSE)->shouldBeCalled(); + $row = new Row(['nid' => 1, 'vid' => 2], ['nid' => 1, 'vid' => 2]); $row->setDestinationProperty('vid', 2); $this->assertEquals($entity->reveal(), $destination->getEntity($row, [])); @@ -109,11 +133,6 @@ public function testGetEntityNewRevision() { $entity = $this->prophesize('\Drupal\Core\Entity\EntityInterface') ->willImplement('\Drupal\Core\Entity\RevisionableInterface'); - $entity_type = $this->prophesize('\Drupal\Core\Entity\EntityTypeInterface'); - $entity_type->getKey('id')->willReturn('nid'); - $entity_type->getKey('revision')->willReturn('vid'); - $this->storage->getEntityType()->willReturn($entity_type->reveal()); - // Enforce is new should be disabled. $entity->enforceIsNew(FALSE)->shouldBeCalled(); // And toggle this as new revision but not the default revision. @@ -138,11 +157,6 @@ public function testGetEntityNewRevision() { public function testGetEntityLoadFailure() { $destination = $this->getEntityRevisionDestination([]); - $entity_type = $this->prophesize('\Drupal\Core\Entity\EntityTypeInterface'); - $entity_type->getKey('id')->willReturn('nid'); - $entity_type->getKey('revision')->willReturn('vid'); - $this->storage->getEntityType()->willReturn($entity_type->reveal()); - // Return a failed load and make sure we don't fail and we return FALSE. $this->storage->load(1) ->shouldBeCalled() 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..359b75c 100644 --- a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php +++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\migrate_drupal\Kernel\d6; +use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase; /** @@ -80,12 +81,26 @@ protected function migrateFields() { } /** + * Configure the languages necessary for translations. + */ + protected function configureLanguages() { + ConfigurableLanguage::createFromLangcode('en')->save(); + ConfigurableLanguage::createFromLangcode('fr')->save(); + } + + /** * Executes all content migrations. * * @param bool $include_revisions * If TRUE, migrates node revisions. */ protected function migrateContent($include_revisions = FALSE) { + // The revision migrations include translations, so we need to install + // the necessary languages. + if ($include_revisions) { + $this->configureLanguages(); + } + $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 fd22c6e..1497ba1 100644 --- a/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php +++ b/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php @@ -40,7 +40,7 @@ protected function getEntityCounts() { 'comment_type' => 2, 'contact_form' => 5, 'editor' => 2, - 'field_config' => 62, + 'field_config' => 63, 'field_storage_config' => 43, 'file' => 7, 'filter_format' => 8, diff --git a/core/modules/node/migration_templates/d6_node_revision.yml b/core/modules/node/migration_templates/d6_node_revision.yml index cd32ea0..4a51fbf 100644 --- a/core/modules/node/migration_templates/d6_node_revision.yml +++ b/core/modules/node/migration_templates/d6_node_revision.yml @@ -38,3 +38,4 @@ process: destination: plugin: entity_revision:node + translated: true 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..ea3ebc6 100644 --- a/core/modules/node/src/Plugin/migrate/source/d6/Node.php +++ b/core/modules/node/src/Plugin/migrate/source/d6/Node.php @@ -13,11 +13,10 @@ * ) */ class Node extends DrupalSqlBase { - /** - * The join options between the node and the node_revisions table. + * An expression for the translation set of a node. */ - const JOIN = 'n.vid = nr.vid'; + const NODE_TRANSLATION_SET = 'CASE n.tnid WHEN 0 THEN n.nid ELSE n.tnid END'; /** * The default filter format. @@ -37,12 +36,11 @@ class Node extends DrupalSqlBase { * {@inheritdoc} */ public function query() { + $query = $this->translationQuery(); + // Select node in its last revision. - $query = $this->select('node_revisions', 'nr') - ->fields('n', array( - 'nid', + $query->fields('n', array( 'type', - 'language', 'status', 'created', 'changed', @@ -50,11 +48,9 @@ public function query() { 'promote', 'moderate', 'sticky', - 'tnid', 'translate', )) ->fields('nr', array( - 'vid', 'title', 'body', 'teaser', @@ -64,10 +60,9 @@ public function query() { )); $query->addField('n', 'uid', 'node_uid'); $query->addField('nr', 'uid', 'revision_uid'); - $query->innerJoin('node', 'n', static::JOIN); if (isset($this->configuration['node_type'])) { - $query->condition('type', $this->configuration['node_type']); + $query->condition('n.type', $this->configuration['node_type']); } return $query; @@ -87,6 +82,7 @@ protected function initializeIterator() { public function fields() { $fields = array( 'nid' => $this->t('Node ID'), + 'vid' => $this->t('Revision ID'), 'type' => $this->t('Type'), 'title' => $this->t('Title'), 'body' => $this->t('Body'), @@ -101,7 +97,6 @@ public function fields() { 'sticky' => $this->t('Sticky at top of lists'), 'revision' => $this->t('Create new revision'), 'language' => $this->t('Language (fr, en, ...)'), - 'tnid' => $this->t('The translation set id for this node'), 'timestamp' => $this->t('The timestamp the latest revision of this node was created.'), ); return $fields; @@ -233,7 +228,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('field_vid')) ->execute() ->fetchAllAssoc('delta'); } @@ -251,4 +246,44 @@ public function getIds() { 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', 'n'); + $query->fields('n', ['nid', 'language']); + + // Only yield the default node for each translation set. + $query->where('n.tnid = 0 OR n.nid = n.tnid'); + + // Claim our vid is the maximum vid of our translation set. + $query->join($this->maxVidQuery(), 'max_vid', 'n.nid = max_vid.translation_set'); + $query->fields('max_vid', ['vid']); + + // Use the real vid for finding fields. + $query->join('node_revisions', 'nr', 'n.vid = nr.vid'); + $query->addField('nr', 'vid', 'field_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..7185682 100644 --- a/core/modules/node/src/Plugin/migrate/source/d6/NodeRevision.php +++ b/core/modules/node/src/Plugin/migrate/source/d6/NodeRevision.php @@ -12,9 +12,9 @@ class NodeRevision extends Node { /** - * The join options between the node and the node_revisions_table. + * The join options between a node table and its translations. */ - const JOIN = 'n.nid = nr.nid AND n.vid <> nr.vid'; + const NODE_TRANSLATION_JOIN = '(n.tnid <> 0 AND n.tnid = nt.tnid) OR n.nid = nt.nid'; /** * {@inheritdoc} @@ -22,7 +22,6 @@ class NodeRevision extends Node { public function fields() { // Use all the node fields plus the vid that identifies the version. return parent::fields() + array( - 'vid' => t('The primary identifier for this version.'), 'log' => $this->t('Revision Log message'), 'timestamp' => $this->t('Revision timestamp'), ); @@ -33,8 +32,87 @@ public function fields() { */ public function getIds() { $ids['vid']['type'] = 'integer'; - $ids['vid']['alias'] = 'nr'; + $ids['vid']['alias'] = 'tvids'; + $ids['language']['type'] = 'string'; + $ids['language']['alias'] = 'tvids'; return $ids; } + /** + * Build a query to turn each revision into multiple translation rows. + * + * For each D6 revision, generate a row for each translation that existed + * at that time. + * + * Eg: If we have: + * | tnid | nid | vid | language | + * | 1 | 1 | 1 | en | + * | 1 | 2 | 2 | fr | + * | 1 | 3 | 3 | de | + * | 1 | 1 | 4 | en | + * + * + * Then we generate the following rows: + * | vid | language | tvid | + * | 1 | en | 1 | + * | 2 | en | 1 | + * | 2 | fr | 2 | + * | 3 | en | 1 | + * | 3 | fr | 2 | + * | 3 | de | 3 | + * | 4 | en | 4 | + * | 4 | fr | 2 | + * | 4 | de | 3 | + * + * @return \Drupal\Core\Database\Query\SelectInterface + * The generated query. + */ + protected function translationRevisionsQuery() { + // Find the nodes that are translations of this node. + $query = $this->select('node_revisions', 'nr'); + $query->join('node', 'n', 'n.nid = nr.nid'); + $query->join('node', 'nt', self::NODE_TRANSLATION_JOIN); + + // Find all translation revisions with lower vids than the current one. + $query->join('node_revisions', 'nrt', + 'nrt.nid = nt.nid AND nr.vid >= nrt.vid'); + + // For each vid/language pair, we only want the top translation vid. + $query->groupBy('nr.vid'); + $query->groupBy('nt.language'); + $query->addField('nr', 'vid'); + $query->addField('nt', 'language'); + $query->addExpression('MAX(nrt.vid)', 'tvid'); + + return $query; + } + + /** + * {@inheritdoc} + */ + protected function translationQuery() { + // Multiply each revision according to translationRevisionsQuery. + $query = $this->select('node_revisions', 'nr'); + $query->join($this->translationRevisionsQuery(), 'tvids', + 'nr.vid = tvids.tvid'); + + // Get fields according to this row of node_revisions. + $query->addField('nr', 'vid', 'field_vid'); + // Claim to be from a translation, see 'vid' in the table from + // translationRevisionsQuery. + $query->fields('tvids', ['vid', 'language']); + + // Get the default node for this translation set. + $query->join('node', 'nt', 'nt.nid = nr.nid'); + $query->join('node', 'n', self::NODE_TRANSLATION_JOIN); + $query->where('n.tnid = 0 OR n.tnid = n.nid'); + $query->addField('n', 'nid'); + + // Add orders, for reproducible results. + $query->orderBy('vid'); + $query->orderBy('language'); + + return $query; + } + } diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeRevisionTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeRevisionTest.php deleted file mode 100644 index 05be99f..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeRevisionTest.php +++ /dev/null @@ -1,45 +0,0 @@ -executeMigrations(['d6_node', 'd6_node_revision']); - } - - /** - * Test node revisions migration from Drupal 6 to 8. - */ - public function testNodeRevision() { - $node = \Drupal::entityManager()->getStorage('node')->loadRevision(2); - /** @var \Drupal\node\NodeInterface $node */ - $this->assertIdentical('1', $node->id()); - $this->assertIdentical('2', $node->getRevisionId()); - $this->assertIdentical('und', $node->langcode->value); - $this->assertIdentical('Test title rev 2', $node->getTitle()); - $this->assertIdentical('body test rev 2', $node->body->value); - $this->assertIdentical('teaser test rev 2', $node->body->summary); - $this->assertIdentical('2', $node->getRevisionUser()->id()); - $this->assertIdentical('modified rev 2', $node->revision_log->value); - $this->assertIdentical('1390095702', $node->getRevisionCreationTime()); - - $node = \Drupal::entityManager()->getStorage('node')->loadRevision(5); - $this->assertIdentical('1', $node->id()); - $this->assertIdentical('body test rev 3', $node->body->value); - $this->assertIdentical('1', $node->getRevisionUser()->id()); - $this->assertIdentical('modified rev 3', $node->revision_log->value); - $this->assertIdentical('1390095703', $node->getRevisionCreationTime()); - } - -} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeRevisionTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeRevisionTest.php new file mode 100644 index 0000000..2a08cba --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeRevisionTest.php @@ -0,0 +1,130 @@ +configureLanguages(); + } + + /** + * Test node revisions migration from Drupal 6 to 8. + */ + public function testNodeRevision() { + $this->executeMigrations(['d6_node', 'd6_node_revision']); + $storage = \Drupal::entityManager()->getStorage('node'); + $node = $storage->loadRevision(2); + + /** @var \Drupal\node\NodeInterface $node */ + $this->assertIdentical('1', $node->id()); + $this->assertIdentical('2', $node->getRevisionId()); + $this->assertIdentical('und', $node->langcode->value); + $this->assertIdentical('Test title rev 2', $node->getTitle()); + $this->assertIdentical('body test rev 2', $node->body->value); + $this->assertIdentical('teaser test rev 2', $node->body->summary); + $this->assertIdentical('2', $node->getRevisionUser()->id()); + $this->assertIdentical('modified rev 2', $node->revision_log->value); + $this->assertIdentical('1390095702', $node->getRevisionCreationTime()); + + $node = $storage->loadRevision(5); + $this->assertIdentical('1', $node->id()); + $this->assertIdentical('body test rev 3', $node->body->value); + $this->assertIdentical('1', $node->getRevisionUser()->id()); + $this->assertIdentical('modified rev 3', $node->revision_log->value); + $this->assertIdentical('1390095703', $node->getRevisionCreationTime()); + + // Revision 12 is the default revision of node 9. + $node = $storage->loadRevision(12); + $this->assertTrue($node instanceof NodeInterface); + $this->assertIdentical('9', $node->id()); + // The French translation is only introduced in rev. 13. + $this->assertTrue($node->hasTranslation('en')); + $this->assertFalse($node->hasTranslation('fr')); + $this->assertIdentical('The Real McCoy', $node->getTitle()); + $this->assertIdentical("In the original, Queen's English.", $node->body->value); + + // Revision 13 was part of node 10, which is a translation of node 9. + $node = $storage->loadRevision(13); + $this->assertTrue($node instanceof NodeInterface); + $this->assertIdentical('9', $node->id()); + $this->assertTrue($node->isDefaultRevision()); + // English is the node's default language, in any revision. + $this->assertIdentical('en', $node->language()->getId()); + // The English title and body did not change in this revision... + $this->assertIdentical('The Real McCoy', $node->getTitle()); + $this->assertIdentical("In the original, Queen's English.", $node->body->value); + // ...but a French translation was introduced. + $this->assertTrue($node->hasTranslation('fr')); + $node = $node->getTranslation('fr'); + $this->assertIdentical('Le Vrai McCoy', $node->getTitle()); + $this->assertIdentical("Ooh là là!", $node->body->value); + + // The node as a whole should have both languages. + $node = $storage->load(9); + $languages = array_keys($node->getTranslationLanguages()); + sort($languages); + $this->assertIdentical(['en', 'fr'], $languages); + + // Check that the translations are actually present. + $this->assertIdentical('The Real McCoy', $node->title->value); + $this->assertIdentical('Le Vrai McCoy', $node->getTranslation('fr')->title->value); + } + + /** + * Test partial node revision migrations from Drupal 6 to 8. + */ + public function testSomeNodeRevision() { + $storage = \Drupal::entityManager()->getStorage('node'); + + // Make sure we don't already have a revision. + $this->assertNull($storage->loadRevision(13), 'No revision before migrations'); + + $this->executeMigrations([ + 'd6_node', + 'd6_node_revision' + ], function (MigrationInterface $migration, Row $row) { + // Only include nodes with nid 9. + if ($row->getSourceProperty('nid') != 9) { + return FALSE; + } + // Only include revision 13. + if (strstr($migration->id(), 'node_revision') !== FALSE) { + if ($row->getSourceProperty('vid') != 13) { + return FALSE; + } + } + return TRUE; + }); + + /** @var \Drupal\node\NodeInterface $node */ + $node = $storage->loadRevision(13); + // Both languages should be present in this revision. + $this->assertTrue($node->hasTranslation('en'), 'English translation exists'); + $this->assertIdentical('The Real McCoy', $node->getTranslation('en') + ->getTitle()); + $this->assertTrue($node->hasTranslation('fr'), 'French translation exists'); + $this->assertIdentical('Le Vrai McCoy', $node->getTranslation('fr') + ->getTitle()); + } + +} 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..106976d 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; /** @@ -79,6 +80,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') diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeByNodeTypeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeByNodeTypeTest.php index 0c31810..38e42a1 100644 --- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeByNodeTypeTest.php +++ b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeByNodeTypeTest.php @@ -40,7 +40,6 @@ class NodeByNodeTypeTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'body' => 'body for node 1', @@ -64,7 +63,6 @@ class NodeByNodeTypeTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'body' => 'body for node 2', @@ -101,7 +99,6 @@ protected function setUp() { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'body' => 'body for node 5', @@ -113,6 +110,8 @@ protected function setUp() { // Add another row with an article node and make sure it is not migrated. foreach ($database_contents as $k => $row) { + $row['tnid'] = 0; + foreach (array('nid', 'vid', 'title', 'uid', 'body', 'teaser', 'format', 'timestamp', 'log') as $field) { $this->databaseContents['node_revisions'][$k][$field] = $row[$field]; switch ($field) { diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php index 8c78be7..86e467b 100644 --- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php +++ b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionByNodeTypeTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\node\Unit\Plugin\migrate\source\d6; +use Drupal\node\Plugin\migrate\source\d6\NodeRevision; use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase; /** @@ -11,26 +12,14 @@ */ class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestCase { - const PLUGIN_CLASS = 'Drupal\node\Plugin\migrate\source\d6\NodeRevision'; + const PLUGIN_CLASS = NodeRevision::class; - // The fake Migration configuration entity. protected $migrationConfiguration = [ 'id' => 'test', - // The fake configuration for the source. 'source' => [ 'plugin' => 'd6_node_revision', 'node_type' => 'page', ], - 'sourceIds' => [ - 'vid' => [ - 'alias' => 'v', - ], - ], - 'destinationIds' => [ - 'vid' => [ - // This is where the field schema would go. - ], - ], ]; protected $databaseContents = [ @@ -118,10 +107,6 @@ class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestCase { ], ]; - // There are three revisions of nid 1; vid 4 is the current one. The - // NodeRevision plugin should capture every revision EXCEPT that one. - // nid 2 will be ignored because $this->migrationConfiguration specifies - // a particular node type. protected $expectedResults = [ [ 'nid' => 1, @@ -134,7 +119,6 @@ class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, 'vid' => 1, 'node_uid' => 1, @@ -156,7 +140,6 @@ class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, 'vid' => 3, 'node_uid' => 1, @@ -167,6 +150,28 @@ class NodeRevisionByNodeTypeTest extends MigrateSqlSourceTestCase { 'log' => 'log for revision 3 (node 1)', 'format' => 1, ], + [ + 'nid' => 1, + 'type' => 'page', + 'language' => 'en', + 'status' => 1, + 'created' => 1279051598, + 'changed' => 1279051598, + 'comment' => 2, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 4, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 4 (node 1)', + 'body' => 'body for revision 4 (node 1)', + 'teaser' => 'teaser for revision 4 (node 1)', + 'log' => 'log for revision 4 (node 1)', + 'format' => 1, + 'timestamp' => 1279051598, + ], ]; } diff --git a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionTest.php b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionTest.php index b1c23f8..3cd0c7e 100644 --- a/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionTest.php +++ b/core/modules/node/tests/src/Unit/Plugin/migrate/source/d6/NodeRevisionTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\node\Unit\Plugin\migrate\source\d6; +use Drupal\node\Plugin\migrate\source\d6\NodeRevision; use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase; /** @@ -11,25 +12,13 @@ */ class NodeRevisionTest extends MigrateSqlSourceTestCase { - const PLUGIN_CLASS = 'Drupal\node\Plugin\migrate\source\d6\NodeRevision'; + const PLUGIN_CLASS = NodeRevision::class; - // The fake Migration configuration entity. protected $migrationConfiguration = [ 'id' => 'test', - // The fake configuration for the source. 'source' => [ 'plugin' => 'd6_node_revision', ], - 'sourceIds' => [ - 'vid' => [ - 'alias' => 'v', - ], - ], - 'destinationIds' => [ - 'vid' => [ - // This is where the field schema would go. - ], - ], ]; protected $databaseContents = [ @@ -68,6 +57,42 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase { 'uid' => 1, 'title' => 'title for revision 2 (node 2)', ], + // A translation set. + [ + 'nid' => 3, + 'type' => 'article', + 'language' => 'en', + 'status' => 1, + 'created' => 1279309100, + 'changed' => 1279309300, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'tnid' => 3, + 'translate' => 0, + 'vid' => 2, + 'uid' => 1, + 'title' => 'title for revision 7 (node 3)', + ], + [ + 'nid' => 4, + 'type' => 'article', + 'language' => 'fr', + 'status' => 1, + 'created' => 1279309200, + 'changed' => 1279309200, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'tnid' => 3, + 'translate' => 0, + 'vid' => 2, + 'uid' => 1, + 'title' => 'title for revision 6 (node 4)', + ], + ], 'node_revisions' => [ [ @@ -114,13 +139,46 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase { 'format' => 1, 'timestamp' => 1279308993, ], + // Translations. + [ + 'nid' => 3, + 'vid' => 5, + 'uid' => 1, + 'title' => 'title for revision 5 (node 3)', + 'body' => 'body for revision 5 (node 3)', + 'teaser' => 'teaser for revision 5 (node 3)', + 'log' => 'log for revision 5 (node 3)', + 'format' => 1, + 'timestamp' => 1279309100, + ], + [ + 'nid' => 3, + 'vid' => 7, + 'uid' => 1, + 'title' => 'title for revision 7 (node 3)', + 'body' => 'body for revision 7 (node 3)', + 'teaser' => 'teaser for revision 7 (node 3)', + 'log' => 'log for revision 7 (node 3)', + 'format' => 1, + 'timestamp' => 1279309300, + ], + [ + 'nid' => 4, + 'vid' => 6, + 'uid' => 1, + 'title' => 'title for revision 6 (node 4)', + 'body' => 'body for revision 6 (node 4)', + 'teaser' => 'teaser for revision 6 (node 4)', + 'log' => 'log for revision 6 (node 4)', + 'format' => 1, + 'timestamp' => 1279309200, + ], ], ]; - // There are three revisions of nid 1, but the NodeRevision source ignores - // the current revision. So only two revisions will be returned here. nid 2 - // is ignored because it only has one revision (the current one). - protected $expectedResults = [ + // There are three revisions of nid 1, and one for node 2. Results are sorted + // by vid. + protected $expectedResults = [ [ // Node fields. 'nid' => 1, @@ -133,7 +191,6 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'vid' => 1, @@ -144,6 +201,30 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase { 'teaser' => 'teaser for revision 1 (node 1)', 'log' => 'log for revision 1 (node 1)', 'format' => 1, + 'field_vid' => 1, + ], + [ + 'nid' => 2, + 'type' => 'article', + 'language' => 'en', + 'status' => 1, + 'created' => 1279290908, + 'changed' => 1279308993, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 2, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 2 (node 2)', + 'body' => 'body for revision 2 (node 2)', + 'teaser' => 'teaser for revision 2 (node 2)', + 'log' => 'log for revision 2 (node 2)', + 'format' => 1, + 'timestamp' => 1279308993, + 'field_vid' => 2, ], [ // Node fields. @@ -157,7 +238,6 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'vid' => 3, @@ -168,6 +248,146 @@ class NodeRevisionTest extends MigrateSqlSourceTestCase { 'teaser' => 'teaser for revision 3 (node 1)', 'log' => 'log for revision 3 (node 1)', 'format' => 1, + 'field_vid' => 3, + ], + [ + 'nid' => 1, + 'type' => 'page', + 'language' => 'en', + 'status' => 1, + 'created' => 1279051598, + 'changed' => 1279051598, + 'comment' => 2, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 4, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 4 (node 1)', + 'body' => 'body for revision 4 (node 1)', + 'teaser' => 'teaser for revision 4 (node 1)', + 'log' => 'log for revision 4 (node 1)', + 'format' => 1, + 'timestamp' => 1279051598, + 'field_vid' => 4, + ], + // Translations add extra revisions. + [ + 'nid' => 3, + 'type' => 'article', + 'language' => 'en', + 'status' => 1, + 'created' => 1279309100, + 'changed' => 1279309300, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 5, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 5 (node 3)', + 'body' => 'body for revision 5 (node 3)', + 'teaser' => 'teaser for revision 5 (node 3)', + 'log' => 'log for revision 5 (node 3)', + 'format' => 1, + 'timestamp' => 1279309100, + 'field_vid' => 5, + ], + [ + 'nid' => 3, + 'type' => 'article', + 'language' => 'en', + 'status' => 1, + 'created' => 1279309100, + 'changed' => 1279309300, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 6, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 5 (node 3)', + 'body' => 'body for revision 5 (node 3)', + 'teaser' => 'teaser for revision 5 (node 3)', + 'log' => 'log for revision 5 (node 3)', + 'format' => 1, + 'timestamp' => 1279309100, + 'field_vid' => 5, + ], + [ + 'nid' => 3, + 'type' => 'article', + 'language' => 'fr', + 'status' => 1, + 'created' => 1279309100, + 'changed' => 1279309300, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 6, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 6 (node 4)', + 'body' => 'body for revision 6 (node 4)', + 'teaser' => 'teaser for revision 6 (node 4)', + 'log' => 'log for revision 6 (node 4)', + 'format' => 1, + 'timestamp' => 1279309200, + 'field_vid' => 6, + ], + [ + 'nid' => 3, + 'type' => 'article', + 'language' => 'en', + 'status' => 1, + 'created' => 1279309100, + 'changed' => 1279309300, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 7, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 7 (node 3)', + 'body' => 'body for revision 7 (node 3)', + 'teaser' => 'teaser for revision 7 (node 3)', + 'log' => 'log for revision 7 (node 3)', + 'format' => 1, + 'timestamp' => 1279309300, + 'field_vid' => 7, + ], + [ + 'nid' => 3, + 'type' => 'article', + 'language' => 'fr', + 'status' => 1, + 'created' => 1279309100, + 'changed' => 1279309300, + 'comment' => 0, + 'promote' => 1, + 'moderate' => 0, + 'sticky' => 0, + 'translate' => 0, + 'vid' => 7, + 'node_uid' => 1, + 'revision_uid' => 1, + 'title' => 'title for revision 6 (node 4)', + 'body' => 'body for revision 6 (node 4)', + 'teaser' => 'teaser for revision 6 (node 4)', + 'log' => 'log for revision 6 (node 4)', + 'format' => 1, + 'timestamp' => 1279309200, + 'field_vid' => 6, ], ]; 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..45b20f8 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 @@ -36,7 +36,6 @@ class NodeTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'body' => 'body for node 1', @@ -60,7 +59,6 @@ class NodeTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'body' => 'body for node 2', @@ -83,7 +81,6 @@ class NodeTest extends MigrateSqlSourceTestCase { 'promote' => 1, 'moderate' => 0, 'sticky' => 0, - 'tnid' => 0, 'translate' => 0, // Node revision fields. 'body' => 'body for node 5', @@ -95,6 +92,28 @@ 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, + 'translate' => 0, + // Node revision fields. + 'body' => 'body for node 6', + 'teaser' => 'body for node 6', + 'log' => '', + 'timestamp' => 1279308994, + 'format' => 1, + ), ); /** @@ -145,22 +164,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_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; diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php index ff2b514..2747677 100644 --- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php @@ -14,7 +14,7 @@ class MigrateTermNodeRevisionTest extends MigrateDrupal6TestBase { /** * {@inheritdoc} */ - public static $modules = ['taxonomy']; + public static $modules = ['taxonomy', 'language']; /** * {@inheritdoc}