diff --git a/core/drupalci.yml b/core/drupalci.yml index 2085b9737b..d3ca79bb03 100644 --- a/core/drupalci.yml +++ b/core/drupalci.yml @@ -18,33 +18,9 @@ build: # run_tests task is executed several times in order of performance speeds. # halt-on-fail can be set on the run_tests tasks in order to fail fast. # suppress-deprecations is false in order to be alerted to usages of - # deprecated code. - run_tests.phpunit: - types: 'PHPUnit-Unit' - testgroups: '--all' - suppress-deprecations: false - halt-on-fail: false + # deprecated code run_tests.kernel: types: 'PHPUnit-Kernel' - testgroups: '--all' - suppress-deprecations: false - halt-on-fail: false - run_tests.simpletest: - types: 'Simpletest' - testgroups: '--all' - suppress-deprecations: false - halt-on-fail: false - run_tests.functional: - types: 'PHPUnit-Functional' - testgroups: '--all' - suppress-deprecations: false - halt-on-fail: false - run_tests.javascript: - concurrency: 15 - types: 'PHPUnit-FunctionalJavascript' - testgroups: '--all' + testgroups: 'migrationtests' suppress-deprecations: false halt-on-fail: false - # Run nightwatch testing. - # @see https://www.drupal.org/project/drupal/issues/2869825 - nightwatchjs: diff --git a/core/modules/content_translation/migrations/d7_node_master.yml b/core/modules/content_translation/migrations/d7_node_master.yml new file mode 100644 index 0000000000..0763b5a46a --- /dev/null +++ b/core/modules/content_translation/migrations/d7_node_master.yml @@ -0,0 +1,45 @@ +id: d7_node_master +label: Nodes +audit: true +migration_tags: + - Drupal 7 + - Content + - Multilingual +deriver: Drupal\node\Plugin\migrate\D7NodeDeriver +source: + plugin: d7_node_master +process: + # If you are using this file to build a custom migration consider removing + # the nid and vid fields to allow incremental migrations. + # In D7, nodes always have a tnid, but it's zero for untranslated nodes. + # We normalize it to equal the nid in that case. + # @see \Drupal\node\Plugin\migrate\source\d7\Node::prepareRow(). + nid: tnid + vid: vid + langcode: + plugin: default_value + source: language + default_value: "und" + title: title + uid: node_uid + status: status + created: created + changed: timestamp + promote: promote + sticky: sticky + revision_uid: revision_uid + revision_log: log + revision_timestamp: timestamp + content_translation_source: source_langcode +destination: + plugin: entity_master:node + translations: true + destination_module: content_translation +migration_dependencies: + required: + - d7_user + - d7_node_type + - language + optional: + - d7_field_instance + - d7_comment_field_instance diff --git a/core/modules/migrate/src/Plugin/Derivative/MigrateEntityMaster.php b/core/modules/migrate/src/Plugin/Derivative/MigrateEntityMaster.php new file mode 100644 index 0000000000..d54db942f7 --- /dev/null +++ b/core/modules/migrate/src/Plugin/Derivative/MigrateEntityMaster.php @@ -0,0 +1,25 @@ +entityDefinitions as $entity_type => $entity_info) { + $this->derivatives[$entity_type] = [ + 'id' => "entity_master:$entity_type", + 'class' => 'Drupal\migrate\Plugin\migrate\destination\EntityContentMaster', + 'requirements_met' => 1, + 'provider' => $entity_info->getProvider(), + ]; + } + return $this->derivatives; + } + +} diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php index 3f8dae982f..3d39e4a1a4 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php @@ -264,6 +264,9 @@ protected function updateEntity(EntityInterface $entity, Row $row) { $language = $row->getDestinationProperty($property); if (!$entity->hasTranslation($language)) { $entity->addTranslation($language); + if ($this->storage->getEntityType()->isRevisionable() && $entity->isNewRevision()) { + $entity->setNewRevision(TRUE); + } // We're adding a translation, so delete it on rollback. $rollback_action = MigrateIdMapInterface::ROLLBACK_DELETE; diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentMaster.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentMaster.php new file mode 100644 index 0000000000..1f5690428c --- /dev/null +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentMaster.php @@ -0,0 +1,114 @@ +getKey('id'); + $ids[$id_key] = $this->getDefinitionFromEntity($id_key); + + $revision_key = $this->getKey('revision'); + if ($revision_key) { + $ids[$revision_key] = $this->getDefinitionFromEntity($revision_key); + } + + $langcode_key = $this->getKey('langcode'); + if ($langcode_key) { + $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key); + } + + return $ids; + } + + /** + * {@inheritdoc} + */ + protected static function getEntityTypeId($plugin_id) { + // Remove entity_revision: + return substr($plugin_id, 14); + } + + /** + * Gets the entity. + * + * @param \Drupal\migrate\Row $row + * The row object. + * @param array $old_destination_id_values + * The old destination IDs. + * + * @return \Drupal\Core\Entity\EntityInterface|false + * The entity or false if it can not be created. + */ + protected function getEntity(Row $row, array $old_destination_id_values) { + $revision_id = $old_destination_id_values ? + $old_destination_id_values[1] : + $row->getDestinationProperty($this->getKey('revision')); + if (!empty($revision_id) && ($entity = $this->storage->loadRevision($revision_id))) { + $entity->setNewRevision(FALSE); + } + else { + if (($entity_id = $row->getDestinationProperty($this->getKey('id'))) && + ($entity = $this->storage->load($entity_id))) { + $entity->enforceIsNew(FALSE); + $entity->setNewRevision(TRUE); + } + else { + // Attempt to ensure we always have a bundle. + if ($bundle = $this->getBundle($row)) { + $row->setDestinationProperty($this->getKey('bundle'), $bundle); + } + + // Stubs might need some required fields filled in. + if ($row->isStub()) { + $this->processStubRow($row); + } + $entity = $this->storage->create($row->getDestination()); + $entity->enforceIsNew(); + } + } + // We need to update the entity, so that the destination row IDs are + // correct. + $entity = $this->updateEntity($entity, $row); + $entity->isDefaultRevision(TRUE); + if ($entity instanceof EntityChangedInterface && $entity instanceof ContentEntityInterface) { + // If we updated any untranslatable fields, update the timestamp for the + // other translations. + /** @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\Entity\EntityChangedInterface $entity */ + foreach ($entity->getTranslationLanguages() as $langcode => $language) { + if ($entity->getTranslation($langcode)->hasTranslationChanges()) { + $entity->getTranslation($langcode)->setChangedTime($entity->getChangedTime()); + } + } + } + return $entity; + } + + /** + * {@inheritdoc} + */ + protected function save(ContentEntityInterface $entity, array $old_destination_id_values = []) { + parent::save($entity, $old_destination_id_values); + return [ + $entity->id(), + $entity->getRevisionId(), + ]; + } + +} diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php index 03f631fb26..727f5eb6e3 100644 --- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php +++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php @@ -971,14 +971,9 @@ protected function getMigrationPluginManager() { * {@inheritdoc} */ public function getHighestId() { - array_filter( - $this->migration->getDestinationPlugin()->getIds(), - function (array $id) { - if ($id['type'] !== 'integer') { - throw new \LogicException('Cannot determine the highest migrated ID without an integer ID column'); - } - } - ); + if (array_search('integer', array_column($this->migration->getDestinationPlugin()->getIds(), 'type')) === FALSE) { + throw new \LogicException('Cannot determine the highest migrated ID without an integer ID column'); + }; // List of mapping tables to look in for the highest ID. $map_tables = [ diff --git a/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php index f17768ef3b..82c29b045d 100644 --- a/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php +++ b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php @@ -102,6 +102,16 @@ protected function getMigrations($database_state_key, $drupal_version) { $plugin_manager = \Drupal::service('plugin.manager.migration'); /** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */ $all_migrations = $plugin_manager->createInstancesByTag($version_tag); + + if ($this->isMultilingualSource($database_state_key, $drupal_version)) { + $patterns = '/(d' . $drupal_version . '_node:)|(d' . $drupal_version . '_node_translation:)|(d' . $drupal_version . '_node_revision:)/'; + foreach ($all_migrations as $key => $migrations) { + if (preg_match($patterns, $key)) { + unset($all_migrations[$key]); + } + } + } + $migrations = []; foreach ($all_migrations as $migration) { // Skip migrations tagged with any of the follow-up migration tags. They @@ -210,4 +220,24 @@ protected function getLegacyDrupalVersion(Connection $connection) { return $version_string ? substr($version_string, 0, 1) : FALSE; } + /** + * Determines if the source database uses i18n module. + * + * @return bool + * The version if this is a source database with i18n, FALSE otherwise. + */ + protected function isMultilingualSource($database_state_key, $version) { + $status = 0; + $database_state = \Drupal::state()->get($database_state_key); + // Connect to source database. + $connection = $this->getConnection($database_state['database']); + if ($connection) { + $status = $connection->query("SELECT status FROM {system} WHERE name = :name and type = :type", [ + ':name' => 'system', + ':type' => 'module', + ])->fetchField(); + } + return ($status === '1') ? TRUE : FALSE; + } + } diff --git a/core/modules/migrate_drupal_ui/src/Form/IdConflictForm.php b/core/modules/migrate_drupal_ui/src/Form/IdConflictForm.php index 2492143abc..c5f96c5a26 100644 --- a/core/modules/migrate_drupal_ui/src/Form/IdConflictForm.php +++ b/core/modules/migrate_drupal_ui/src/Form/IdConflictForm.php @@ -4,6 +4,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\TempStore\PrivateTempStoreFactory; +use Drupal\migrate\Audit\AuditResult; use Drupal\migrate\Audit\IdAuditor; use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; use Drupal\migrate\Plugin\MigrationPluginManagerInterface; @@ -73,6 +74,47 @@ public function buildForm(array $form, FormStateInterface $form_state) { $results = (new IdAuditor())->auditMultiple($migrations); + // Get the audit results for node revisions for the node_master migrations. + $migration_plugin_manager = \Drupal::service('plugin.manager.migration'); + foreach ($migrations as $migration) { + $migration_id = $migration->getPluginId(); + if (preg_match('/node_master/', $migration_id) === 1) { + $map_table = $migration->getIdMap()->mapTableName(); + + $database = \Drupal::database(); + if (!$database->schema()->tableExists($map_table)) { + continue; + } + // Get highest migrated node revision id. + $query = $database->select($map_table, 'map') + ->fields('map', ['destid2']) + ->range(0, 1) + ->orderBy('destid2', 'DESC'); + $ids[] = $query->execute()->fetchField(); + $max = (int) (max($ids)); + + // Make a migration based on node_master but with and entity_revision + // destination. + $revision_migration = $migration->getPluginDefinition(); + $revision_migration['id'] = $migration->getPluginId() . '-revision'; + $revision_migration['destination']['plugin'] = 'entity_revision:node'; + $revision_migration = $migration_plugin_manager->createStubMigration($revision_migration); + + // Get the highest node revision id. + $destination = $revision_migration->getDestinationPlugin(); + $highest = $destination->getHighestId(); + + if ($highest > $max) { + $results[$migration_id . '-revision'] = AuditResult::fail($revision_migration, [ + $this->t('The destination system contains data which was not created by a migration.'), + ]); + } + else { + $results[$migration_id . '-revision'] = AuditResult::pass($revision_migration); + } + } + } + /** @var \Drupal\migrate\Audit\AuditResult $result */ foreach ($results as $result) { $destination = $result->getMigration()->getDestinationPlugin(); diff --git a/core/modules/node/src/Plugin/migrate/process/d7/RevisionTranslationAffected.php b/core/modules/node/src/Plugin/migrate/process/d7/RevisionTranslationAffected.php new file mode 100644 index 0000000000..6b8b880f7f --- /dev/null +++ b/core/modules/node/src/Plugin/migrate/process/d7/RevisionTranslationAffected.php @@ -0,0 +1,35 @@ +getSourceProperty('etr_changed')) { + $nr_timestamp = $row->getSourceProperty('timestamp'); + // When the entity translation revision changed time is the same as the + // corresponding node revision changed time then this is a revision that + // has changed. + if ($nr_timestamp === $etr_changed) { + $ret = 1; + } + } + return $ret; + } + +} diff --git a/core/modules/node/src/Plugin/migrate/source/d7/NodeMaster.php b/core/modules/node/src/Plugin/migrate/source/d7/NodeMaster.php new file mode 100644 index 0000000000..ef7db1db26 --- /dev/null +++ b/core/modules/node/src/Plugin/migrate/source/d7/NodeMaster.php @@ -0,0 +1,105 @@ +leftJoin('entity_translation_revision', 'etr', 'nr.nid = etr.entity_id and nr.vid=etr.revision_id'); + $conditions = $query->orConditionGroup(); + $conditions->condition('etr.entity_type', 'node'); + $conditions->isNull('etr.entity_type'); + $query->condition($conditions); + $query->addField('etr', 'language', 'etr_language'); + $query->addField('etr', 'uid', 'etr_uid'); + $query->addField('etr', 'created', 'etr_created'); + $query->addField('etr', 'changed', 'etr_changed'); + + $query->orderBy('n.nid'); + $query->orderBy('nr.vid'); + $query->orderBy('etr_language'); + $query->orderBy('n.language'); + return $query; + } + + /** + * {@inheritdoc} + */ + public function initializeIterator() { + // Change the language if this is an entity translation revision. + $rows = $this->prepareQuery()->execute()->fetchAll(); + foreach ($rows as &$row) { + if (isset($row['etr_language'])) { + $row['language'] = $row['etr_language']; + } + yield $row; + } + } + + /** + * {@inheritdoc} + */ + public function prepareRow(Row $row) { + parent::prepareRow($row); + // Override properties when this is an entity translation revision. + // @todo: do here or in pipeline? + if ($row->getSourceProperty('etr_language')) { + $row->setSourceProperty('language', $row->getSourceProperty('etr_language')); + $row->setSourceProperty('source_langcode', $row->getSourceProperty('source')); + // @todo: Decide if 'tnid' should be used in the migration yml. It is not + // used in entity translation and adds confusion. + $row->setSourceProperty('tnid', $row->getSourceProperty('nid')); + $row->setSourceProperty('revision_uid', $row->getSourceProperty('etr_uid')); + $row->setSourceProperty('created', $row->getSourceProperty('etr_created')); + $row->setSourceProperty('timestamp', $row->getSourceProperty('etr_changed')); + } + } + + /** + * {@inheritdoc} + */ + public function getIds() { + return [ + 'nid' => [ + 'type' => 'integer', + 'alias' => 'n', + ], + 'vid' => [ + 'type' => 'integer', + 'alias' => 'nr', + ], + 'language' => [ + 'type' => 'string', + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function handleTranslations(SelectInterface $query) { + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeMasterTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeMasterTest.php new file mode 100644 index 0000000000..dbf59fb28a --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeMasterTest.php @@ -0,0 +1,709 @@ +fileMigrationSetup(); + + $this->installEntitySchema('comment'); + $this->installEntitySchema('taxonomy_term'); + $this->installSchema('comment', ['comment_entity_statistics']); + $this->installSchema('node', ['node_access']); + + $this->migrateUsers(); + $this->migrateFields(); + $this->executeMigrations([ + 'language', + 'd7_language_content_settings', + 'd7_comment_field', + 'd7_comment_field_instance', + 'd7_node_master', + ]); + $this->nodeStorage = $this->container->get('entity_type.manager') + ->getStorage('node'); + } + + /** + * Gets the path to the fixture file. + */ + protected function getFixtureFilePath() { + return __DIR__ . '/../../../../../../migrate_drupal/tests/fixtures/drupal7_et.php'; + } + + /** + * {@inheritdoc} + */ + protected function getFileMigrationInfo() { + return [ + 'path' => 'public://sites/default/files/cube.jpeg', + 'size' => '3620', + 'base_path' => 'public://', + 'plugin_id' => 'd7_file', + ]; + } + + /** + * Tests the master node migration. + */ + public function testNodeMasterMigration() { + $db = \Drupal::database(); + $this->assertEquals($this->expectedNodeFieldRevisionTable(), $db->select('node_field_revision', 'nr') + ->fields('nr') + ->execute() + ->fetchAll(\PDO::FETCH_ASSOC)); + $this->assertEquals($this->expectedNodeFieldDataTable(), $db->select('node_field_data', 'nr') + ->fields('nr') + ->execute() + ->fetchAll(\PDO::FETCH_ASSOC)); + + // Now load and test each revision. + $data = $this->expectedRevisionEntityData()[0]; + foreach ($this->expectedNodeFieldRevisionTable() as $key => $revision) { + $this->assertRevision($revision, $data[$key]); + } + } + + /** + * Asserts various aspects of a node revision. + * + * @param array $revision + * An array of revision data matching database table node_field_revision. + * @param array $data + * An array of revision data. + */ + protected function assertRevision(array $revision, array $data) { + /* @var \Drupal\node\NodeInterface $actual */ + $actual = $this->nodeStorage->loadRevision($revision['vid']) + ->getTranslation($revision['langcode']); + $this->assertInstanceOf(NodeInterface::class, $actual); + $this->assertSame($revision['title'], $actual->getTitle(), sprintf("Title '%s' does not match actual '%s' for revision '%d' langcode '%s'", $revision['title'], $actual->getTitle(), $revision['vid'], $revision['langcode'])); + $this->assertSame($revision['revision_translation_affected'], $actual->get('revision_translation_affected')->value, sprintf("revision_translation_affected '%s' does not match actual '%s' for revision '%d' langcode '%s'", $revision['revision_translation_affected'], $actual->get('revision_translation_affected')->value, $revision['vid'], $revision['langcode'])); + + if ($data['created']) { + $this->assertSame($data['created'], $actual->getRevisionCreationTime(), sprintf("Created time '%s' does not match actual '%s' for revision '%d' langcode '%s'", $data['created'], $actual->getRevisionCreationTime(), $revision['vid'], $revision['langcode'])); + } + if ($data['changed']) { + $this->assertSame($data['changed'], $actual->getChangedTime(), sprintf("Changed time '%s' does not match actual '%s' for revision '%d' langcode '%s'", $data['changed'], $actual->getChangedTime(), $revision['vid'], $revision['langcode'])); + } + $this->assertSame($data['log'], $actual->getRevisionLogMessage(), sprintf("Revision log '%s' does not match actual '%s' for revision '%d' langcode '%s'", var_export($data['log'], TRUE), $actual->getRevisionLogMessage(), $revision['vid'], $revision['langcode'])); + if ($data['field_tree']) { + $this->assertSame($data['field_tree'], $actual->field_tree->value, sprintf("field_tree value '%s' does not match actual '%s' for revision '%d' langcode '%s'", var_export($data['field_tree'], TRUE), $actual->field_tree->value, $revision['vid'], $revision['langcode'])); + } + } + + /** + * Provides the expected node_field_data table. + * + * @return array + * The table. + */ + protected function expectedNodeFieldDataTable() { + return [ + 0 => + [ + 'nid' => '1', + 'vid' => '4', + 'type' => 'page', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261523', + 'changed' => '1568261687', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => NULL, + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 1 => + [ + 'nid' => '1', + 'vid' => '4', + 'type' => 'page', + 'langcode' => 'it', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261721', + 'changed' => '1568261721', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 2 => + [ + 'nid' => '1', + 'vid' => '4', + 'type' => 'page', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261548', + 'changed' => '1568261548', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => NULL, + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 3 => + [ + 'nid' => '2', + 'vid' => '8', + 'type' => 'article', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702317', + 'changed' => '1568702345', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => NULL, + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 4 => + [ + 'nid' => '2', + 'vid' => '8', + 'type' => 'article', + 'langcode' => 'it', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568703243', + 'changed' => '1568703243', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 5 => + [ + 'nid' => '2', + 'vid' => '8', + 'type' => 'article', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702543', + 'changed' => '1568703365', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => NULL, + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + ]; + } + + /** + * Provides the expected node_field_revision table. + * + * @return array + * The table. + */ + protected function expectedNodeFieldRevisionTable() { + return [ + 0 => + [ + 'nid' => '1', + 'vid' => '1', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261523', + 'changed' => '1568261523', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => '1', + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 1 => + [ + 'nid' => '1', + 'vid' => '2', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page tahi', + 'created' => '1568261523', + // @todo: why is this the current date. + 'changed' => '1568794727', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => '1', + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 2 => + [ + 'nid' => '1', + 'vid' => '2', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page tahi', + 'created' => '1568261548', + 'changed' => '1568261548', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 3 => + [ + 'nid' => '1', + 'vid' => '3', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261523', + 'changed' => '1568261687', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => '1', + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 4 => + [ + 'nid' => '1', + 'vid' => '3', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261548', + // @todo. Find out why this becomes the current time. + 'changed' => '1568261548', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 5 => + [ + 'nid' => '1', + 'vid' => '4', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261523', + 'changed' => '1568261687', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => NULL, + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 6 => + [ + 'nid' => '1', + 'vid' => '4', + 'langcode' => 'it', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261721', + 'changed' => '1568261721', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 7 => + [ + 'nid' => '1', + 'vid' => '4', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Page one', + 'created' => '1568261548', + 'changed' => '1568261548', + 'promote' => '0', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => NULL, + 'content_translation_source' => NULL, + 'content_translation_outdated' => '0', + ], + 8 => + [ + 'nid' => '2', + 'vid' => '5', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702317', + 'changed' => '1568702317', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => '1', + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 9 => + [ + 'nid' => '2', + 'vid' => '6', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702317', + 'changed' => '1568702345', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => '1', + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 10 => + [ + 'nid' => '2', + 'vid' => '7', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702317', + 'changed' => '1568702345', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => NULL, + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 11 => + [ + 'nid' => '2', + 'vid' => '7', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702543', + 'changed' => '1568702543', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 12 => + [ + 'nid' => '2', + 'vid' => '8', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702317', + 'changed' => '1568702345', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => NULL, + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 13 => + [ + 'nid' => '2', + 'vid' => '8', + 'langcode' => 'it', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568703243', + 'changed' => '1568703243', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 14 => + [ + 'nid' => '2', + 'vid' => '8', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702543', + 'changed' => '1568703365', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => NULL, + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 15 => + [ + 'nid' => '2', + 'vid' => '9', + 'langcode' => 'en', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702317', + 'changed' => '1568702345', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '1', + 'revision_translation_affected' => NULL, + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + 16 => + [ + 'nid' => '2', + 'vid' => '9', + 'langcode' => 'mi', + 'status' => '1', + 'uid' => '1', + 'title' => 'Colors', + 'created' => '1568702543', + 'changed' => '1568703365', + 'promote' => '1', + 'sticky' => '0', + 'default_langcode' => '0', + 'revision_translation_affected' => '1', + 'content_translation_source' => 'en', + 'content_translation_outdated' => '0', + ], + ]; + } + + /** + * Provides the expected node_field_revision table. + * + * @return array + * Selected properties and fields on the revision. + */ + protected function expectedRevisionEntityData() { + return [ + $revision_data = [ + // Node 1, revision 1, en. + 0 => + [ + 'log' => NULL, + 'field_tree' => 'lancewood', + 'created' => '1568261523', + 'changed' => '1568261523', + ], + // Node 1, revision 2, en. + 1 => + [ + 'log' => NULL, + 'field_tree' => 'lancewood', + 'created' => '1568261548', + // @todo: why is this current time. + 'changed' => NULL, + ], + // Node 1, revision 2, mi. + 2 => + [ + 'log' => NULL, + 'field_tree' => 'horoeka', + 'created' => '1568261548', + 'changed' => '1568261548', + ], + // Node 1, revision 3, en. + 3 => + [ + 'log' => '2nd', + 'field_tree' => 'lancewood', + 'created' => '1568261548', + 'changed' => '1568261687', + ], + // Node 1, revision 3, mi. + 4 => + [ + 'log' => '2nd', + 'field_tree' => 'horoeka', + 'created' => '1568261548', + 'changed' => '1568261548', + ], + // Node 1, revision 4, en. + 5 => + [ + 'log' => NULL, + 'field_tree' => 'lancewood', + 'created' => '1568261548', + 'changed' => '1568261687', + ], + // Node 1, revision 4, it. + 6 => + [ + 'log' => NULL, + 'field_tree' => 'it - lancewood', + 'created' => '1568261548', + 'changed' => '1568261721', + ], + // Node 1, revision 4, mi. + 7 => + [ + 'log' => NULL, + 'field_tree' => 'horoeka', + 'created' => '1568261548', + 'changed' => '1568261548', + ], + // Node 2, revision 5, en. + 8 => + [ + 'log' => '1st', + 'field_tree' => NULL, + 'created' => '1568702317', + 'changed' => '1568702317', + ], + // Node 2, revision 6, en. + 9 => + [ + 'log' => '2nd', + 'field_tree' => NULL, + 'created' => '1568702345', + 'changed' => '1568702345', + ], + // Node 2, revision 7, en. + 10 => + [ + 'log' => '3rd', + 'field_tree' => NULL, + 'created' => '1568702543', + 'changed' => '1568702345', + ], + // Node 2, revision 7, mi. + 11 => + [ + 'log' => '3rd', + 'field_tree' => NULL, + 'created' => '1568702543', + 'changed' => '1568702543', + ], + // Node 2, revision 8, en. + 12 => + [ + 'log' => '4th', + 'field_tree' => NULL, + 'created' => '1568703243', + 'changed' => '1568702345', + ], + // Node 2, revision 8, it. + 13 => + [ + 'log' => '4th', + 'field_tree' => NULL, + 'created' => '1568703243', + 'changed' => '1568703243', + ], + // Node 2, revision 8, mi. + 14 => + [ + 'log' => '4th', + 'field_tree' => NULL, + 'created' => '1568703243', + 'changed' => '1568703365', + ], + // Node 2, revision 9, en. + 15 => + [ + 'log' => '5th', + 'field_tree' => NULL, + 'created' => '1568703365', + 'changed' => '1568702345', + ], + // Node 2, revision 9, mi. + 16 => + [ + 'log' => '5th', + 'field_tree' => NULL, + 'created' => '1568703365', + 'changed' => '1568703365', + ], + ], + ]; + } + +}