Problem/Motivation

Follow-up to #2975666: Migrate Drupal 7 node entity translations data to Drupal 8. In another follow-up (#2669984: Migrate Drupal 7 user entity translations data to Drupal 8), we found that we missed 3 things:

  1. The d7_node_entity_translation migration should not be in the Node module but in the Content Translation module. This is true since the new module Migrate Drupal Multilingual was introduced in #2953360: Experimental migrate_drupal_multilingual module.
  2. We missed one entity translation metadata that we should have mapped, content_translation_outdated.
  3. We missed tow migration dependencies: language & d7_entity_translation_settings.

Proposed resolution

  1. We should move the d7_node_entity_translation migration to Content Translation, with all the other multilingual migrations.
  2. We should map the missing entity translation metadata.
  3. We should add the missing migration dependencies.

Remaining tasks

  • Write the patch
  • Review
  • Commit

User interface changes

None.

API changes

None.

Data model changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

maxocub created an issue. See original summary.

maxocub’s picture

Status: Active » Needs review
FileSize
9.31 KB

Here's a first patch.

Status: Needs review » Needs work

The last submitted patch, 2: 2989627-2.patch, failed testing. View results

maxocub’s picture

Issue summary: View changes
FileSize
4.62 KB
9.87 KB
maxocub’s picture

Status: Needs work » Needs review

Back to NR.

maxocub’s picture

Issue summary: View changes

IS update.

maxocub’s picture

Issue summary: View changes

Fix IS typos.

masipila’s picture

Status: Needs review » Needs work

Test tested this manually and analyzed node_field_data database table.

1. Translation outdated ('content_translation_outdated') property is now correctly migrated. OK.

2. Migration dependencies for 'language' and 'd7_entity_translation_settings' have now been added as described in the IS.

3. I also did manual regression testing, all OK.

  • The relations between translations ('default_langcode' for the original + 'content_translation_source' for translations) are correctly migrated, checked from both database and UI.
  • titles and langcodes are correctly migrated.
  • 'created' and 'changed' are correctly migrated.
  • author of the translation is correctly migrated.
  • translation publishing status is correctly migrated.

4. Question:
D8 node_field_data table has a column 'revision_translation_affected'. What is this property? We are not mapping it here. Will that be part of some other issue, such as #2746541: Migrate D6 and D7 node revision translations to D8?

5. I reviewed test coverage for the source plugin, OK

  • We are now testing the author of the translation (uid)
  • We are now testing the translation publishing status (status)
  • We are now testing the translation outdated status (translate)

6. I reviewed test coverage for MigrateNodeTest, NOT OK
Here are the asserts:

  protected function assertEntity($id, $type, $langcode, $title, $uid, $status, $created, $changed, $promoted, $sticky) {
    /** @var \Drupal\node\NodeInterface $node */
    $node = Node::load($id);
    $this->assertInstanceOf(NodeInterface::class, $node);
    $this->assertEquals($type, $node->getType());
    $this->assertEquals($langcode, $node->langcode->value);
    $this->assertEquals($title, $node->getTitle());
    $this->assertEquals($uid, $node->getOwnerId());
    $this->assertEquals($status, $node->isPublished());
    $this->assertEquals($created, $node->getCreatedTime());
    if (isset($changed)) {
      $this->assertEquals($changed, $node->getChangedTime());
    }
    $this->assertEquals($promoted, $node->isPromoted());
    $this->assertEquals($sticky, $node->isSticky());
  }

We don't have tests for

  • default_langcode
  • content_translation_source
  • content_translation_outdated
  • (revision_translation_affected, see point 4. above)

Could we please have test coverage for default_langcode, content_translation_source and content_translation_outdated? Kicking back to NW for those.

Cheers,
Markus

maxocub’s picture

Status: Needs work » Needs review
FileSize
6.33 KB
12.34 KB

Thanks for the review @masipila.

4. About 'revision_translation_affected', I really don't know how that property works exactly, and even less how and if we can migrate it. Here's some info I found about it:

From system_install():

Indicates if the last edit of a translation belongs to current revision.

And from entity.api.php:

An entity that is both revisionable and translatable has all the features
described above: every revision can contain one or more translations. The
canonical variant of the entity is the default translation of the default
revision. Any revision will be initially loaded as the default translation,
the other revision translations can be instantiated from this one. If a
translation has changes in a certain revision, the translation is considered
"affected" by that revision, and will be flagged as such via the
"revision_translation_affected" field. With the built-in UI, every time a new
revision is saved, the changes for the edited translations will be stored,
while all field values for the other translations will be copied as-is.
However, if multiple translations of the default revision are being
subsequently modified without creating a new revision when saving, they will
all be affected by the default revision. Additionally, all revision
translations will be affected when saving a revision containing changes for
untranslatable fields. On the other hand, pending revisions are not supposed
to contain multiple affected translations, even when they are being
manipulated via the API.

If we think it can and should be migrated, we should open a follow-up.

6. About the test coverage in MigrateNodeTest, I added asserts for the default translation, the source langcode and the translation outdated. I converted the Entity Translation tests to use the helper method assertEntity(), even though I find it less readable than with some code repetition like it was before.

phenaproxima’s picture

About 'revision_translation_affected', I really don't know how that property works exactly, and even less how and if we can migrate it.

I think @plach would be better qualified to weigh in on this authoritatively, but I believe revision_translation_affected is a completely internal field, used by the translation system, and should not be touched by us if we can at all avoid it.

  1. +++ b/core/modules/content_translation/migrations/d7_node_entity_translation.yml
    @@ -23,10 +23,13 @@ process:
    +  content_translation_outdated: translate
    

    This could benefit from a comment explaining what this mapping is for. It's not at all obvious, and this is the sort of obscure sorcery that could come back later to bite us in the ass.

  2. +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7NoMultilingualTest.php
    @@ -207,7 +207,7 @@ public function testMigrateUpgradeExecute() {
    +    $session->pageTextContains("Install migrate_drupal_multilingual to run migration 'd7_node_translation:article'.");
    

    Why did this change?

  3. +++ b/core/modules/node/src/Plugin/migrate/source/d7/NodeEntityTranslation.php
    @@ -88,14 +79,16 @@ public function prepareRow(Row $row) {
    +      'entity_id' => $this->t('The entity id this translation relates to'),
    +      'revision_id' => $this->t('The entity revision id this translation relates to'),
    

    'id' should be 'ID'.

  4. +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php
    @@ -109,22 +120,33 @@ protected function getFileMigrationInfo() {
    +   * @param string $field_integer
    +   *   The expected value of the field_integer field.
    +   * @param bool $default_translation
    +   *   Whether the node is expected to be the default translation.
    +   * @param string $source_langcode
    +   *   The expected translation source langcode.
    +   * @param bool $translation_outdated
    +   *   Whether the translation is expected to be outdated.
    

    I agree with @maxocub that this stuff shouldn't be in assertEntity(). We are dangerously close to over-abstracting the assertions; in tests, it's okay to repeat some code like this.

maxocub’s picture

Status: Needs review » Needs work

Thanks for the review @phenaproxima! Back to NW.

maxocub’s picture

Re #10:

  1. Done.
  2. This Migrate Drupal UI warning only show us the first to be run multilingual migration that needs Migrate Drupal Multilingual. Since this patch adds 2 dependencies to the d7_node_entity_translation migration (language & d7_entity_translation_settings) it will now be run after d7_node_translation.
  3. Done.
  4. Reverted the test to what was in the patch from #4.
quietone’s picture

Reading the patch I see that all the changes in #10 have been addressed.

I haven't read the whole issue but see that phenaproxima suggested speaking to plach about #9.4. Is that still needed ?

maxocub’s picture

@quietone: I don't think it's needed anymore. After playing with that field I'm pretty convinced that we should not migrate.

phenaproxima’s picture

Status: Needs review » Needs work

Okay, so...let's just remove revision_translation_affected from the migration and see if any tests break. If they don't, great! If they do, we should understand exactly why they break and be able to justify to @plach why we should migrate that field.

maxocub’s picture

Status: Needs work » Needs review

But, revision_translation_affected is not currently migrated. I don't even think there's an equivalent in D7 to migrate it from.

masipila’s picture

Peekaboo, getting back to this issue after a while.

I tested this manually in #8. The changes in #9 (after my review) and #12 (after @phenaproxima's review) are so minor that my manual test results are still valid.

The only remaining topic that has been discussed is the D8 'revision_translation_affected' property. I did some research on this as well and came to the same conclusion with @maxocub in #16. There is no equivalent property in D7 as far as I can tell. The current patch does not map this property, which is correct behavior.

Triggering the testbot for PostgreSQL and SQLite. Once those are green, this is RTBC.

Cheers,
Markus

heddn’s picture

Status: Needs review » Reviewed & tested by the community

Per #17, moving to RTBC.

Gábor Hojtsy’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

Does not apply to Drupal 8.7 currently:

$ git apply --index 2989627-12.patch 
error: patch failed: core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7NoMultilingualTest.php:207
error: core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7NoMultilingualTest.php: patch does not apply
maxocub’s picture

Re-roll done.
I took the liberty to make one change (see the interdiff) to the Node source plugin so it handle the source language the same way as the Comment, Term & User source plugins.

Gábor Hojtsy’s picture

Hm, you made this slight logic change but no tests failed and no tests needed to be updated. Looks like if there was no entity translation source language on the node the behaviour would be now different?

masipila’s picture

Re-parented so that all remaining multilingual migration issues can be found from the same meta. Moved the previous parent issue to related issues.

masipila’s picture

Re #21. I cross checked what we have in the other Entity Translation source plugins.

Proposed code (from #21) for the Node source plugin that is being discussed here:

+++ b/core/modules/node/src/Plugin/migrate/source/d7/Node.php
@@ -113,7 +113,8 @@ public function prepareRow(Row $row) {
     $entity_translatable = $this->isEntityTranslatable('node') && (int) $this->variableGet('language_content_type_' . $type, 0) === 4;
-    $language = $entity_translatable ? $this->getEntityTranslationSourceLanguage('node', $nid) : $row->getSourceProperty('language');
+    $source_language = $this->getEntityTranslationSourceLanguage('node', $nid);
+    $language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language');
 
     // Get Field API field values.

User source plugin:

+    $entity_translatable = $this->isEntityTranslatable('user');
+    $source_language = $this->getEntityTranslationSourceLanguage('user', $uid);
+    $language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language');
+    $row->setSourceProperty('entity_language', $language);
+
     // Get Field API field values.

Taxonomy term source plugin:

+    $translatable_vocabularies = array_keys(array_filter($this->variableGet('entity_translation_taxonomy', [])));
+    $entity_translatable = $this->isEntityTranslatable('taxonomy_term') && in_array($vocabulary, $translatable_vocabularies, TRUE);
+    $source_language = $this->getEntityTranslationSourceLanguage('taxonomy_term', $tid);
+    $language = $entity_translatable && $source_language ? $source_language : $default_language['language'];
+    $row->setSourceProperty('language', $language);
+
     // Get Field API field values.

Comment source plugin:

+    $entity_translatable = $this->isEntityTranslatable('comment') && (int) $this->variableGet('language_content_type_' . $node_type, 0) === 4;
+    $source_language = $this->getEntityTranslationSourceLanguage('comment', $cid);
+    $language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language');
+
+    // Get Field API field values.

The determination of $language is consistent.

@maxocub: I did not have time on the gate to check this but why do we have $row->setSourceProperty('language', $language); in user and taxonomy term source plugins but not in node and comment source plugins?

Cheers,
Markus

maxocub’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Re #21: You're right, this needs a test. I think that when you use Entity Translation you will always have a source language in the entity_translation table, unless you messed with it. I added this extra logic because when I was writing the current tests I messed up my DB. While it should not really happen, I agree we need a test.

Re #23: For Node and Comment we don't need the $row->setSourceProperty('language', $language); because we already have a language property from the D7 table to migrate from.
The D7 taxonomy_term table does not have a language column so we need to set it.
The D7 User table does have a language column, but we already use it to determine the preferred_langcode and preferred_admin_langcode, so we need to do set the entity_language source property to set the D8 user langcode without affecting the user's preferred langcodes.

maxocub’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
3.9 KB
12.95 KB
13.94 KB

I added a failing test for when the source language is missing from the entity_translation table.

The last submitted patch, 25: 2989627-25-test-only.patch, failed testing. View results

quietone’s picture

@maxocub, Just took a wee look and I have a question.

+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php
@@ -422,6 +422,139 @@ public function providerSource() {
+    // The source data with the source language missing from the
+    // entity_translation table.
...
+        'source' => 'en',

This looks like the source language. What field is supposed to be missing?

maxocub’s picture

Status: Needs review » Needs work

You're right, the comment is misleading. What is missing from the entity_translation table is the source language node. The source language node is the entry with the 'source' => ''. All entries with 'source' => 'en' are translations of the English (source language) node.

maxocub’s picture

Status: Needs work » Needs review
FileSize
1.11 KB
14.06 KB

I updated the misleading comment.

quietone’s picture

Thanks. I still have trouble understanding what is missing.

So, for the first time, I used entity translation with the test fixture. It took a while but I was finally getting new entries in the entity_translation table. So, when I make a new node a row is added to that table with the source language property empty. When that node is translated a new row is added with language = fr and source = en. Which is correct the source was English and the translation was French.

So, from that, I think what it meant by missing in the comment is that the row where language = the first language used, and the source is empty is what is missing. That is a row in the table is missing not that the property source is missing. Is that right?

maxocub’s picture

So, from that, I think what it meant by missing in the comment is that the row where language = the first language used, and the source is empty is what is missing. That is a row in the table is missing not that the property source is missing. Is that right?

Yes, your understanding is right. I have a hard time finding a comment that would be more clear. Do you have any suggestions?

quietone’s picture

Phew, glad I got that right. As for a suggestion, let's see.

+++ b/core/modules/node/tests/src/Kernel/Plugin/migrate/source/d7/NodeTest.php
@@ -422,6 +422,140 @@ public function providerSource() {
+    // The source data with the source language node missing from the
+    // entity_translation table. The source language node should have an empty
+    // source column. Here we only have the French translation.

The source data with a row missing from the entity_translation table. There should be a row where the language property is set and the source property is empty.

How about that?

Hmm, may be better to have two tests? Would it make sense to modify tests[1] to have that extra row and then make tests[2] the same as the current tests[1]? The first could have a comment 'Test with a correct entity_translation table' and then test[2] could be 'Repeat the previous test with an incorrect entity_translation table where the row with source property is empty is missing'.

Currently leaning to the second suggestion.

maxocub’s picture

FileSize
2.23 KB
14.83 KB

I agree with your 2nd suggestion, here it is.

quietone’s picture

Status: Needs review » Reviewed & tested by the community

@maxocub, thanks for working through this. It is really clear and obvious what is going on in the test now.

Since the last RTBC there have been questions which have all been answered and the need for a new test was discovered. That test was added and then another test was added with comments to make it clear what a 'good set' of source data looked like and a 'bad set' of source data.

Back to RTBC!

  • Gábor Hojtsy committed b4ece22 on 8.7.x
    Issue #2989627 by maxocub, quietone, masipila, phenaproxima, Gábor...
Gábor Hojtsy’s picture

Status: Reviewed & tested by the community » Fixed

Thanks for adding a test for the logic improvement, looks much better now.

  • Gábor Hojtsy committed f7978e8 on 8.6.x
    Issue #2989627 by maxocub, quietone, masipila, phenaproxima, Gábor...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.