summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2018-07-03 15:53:47 (GMT)
committerNathaniel Catchpole2018-07-03 15:54:34 (GMT)
commit769b731a1dd3b42c2edd006356e9d0f20c73fc1e (patch)
tree53aedbfda0c26eeccd273c4bfcc7c9c329c00afa
parent3b1b5e6494b927e5644a7a5b491cf9959c76d3df (diff)
Issue #2921661 by heddn, maxocub, alexpott, phenaproxima, Jo Fitzgerald, badmetevils, quietone: Add support to migrate multilingual revisions
(cherry picked from commit 9eb798813219857a3ec6dee5b0da785990460e1f)
-rw-r--r--core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php13
-rw-r--r--core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php23
-rw-r--r--core/modules/migrate/tests/src/Kernel/Plugin/EntityRevisionTest.php131
-rw-r--r--core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php3
-rw-r--r--core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityRevisionTest.php162
-rw-r--r--core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php4
-rw-r--r--core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php2
7 files changed, 326 insertions, 12 deletions
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
index 8d15cf4..3e18d28 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
@@ -148,7 +148,7 @@ class EntityContentBase extends Entity implements HighestIdInterface {
}
$ids = $this->save($entity, $old_destination_id_values);
- if (!empty($this->configuration['translations'])) {
+ if ($this->isTranslationDestination()) {
$ids[] = $entity->language()->getId();
}
return $ids;
@@ -181,12 +181,15 @@ class EntityContentBase extends Entity implements HighestIdInterface {
* {@inheritdoc}
*/
public function getIds() {
+ $ids = [];
+
$id_key = $this->getKey('id');
$ids[$id_key] = $this->getDefinitionFromEntity($id_key);
if ($this->isTranslationDestination()) {
- if (!$langcode_key = $this->getKey('langcode')) {
- throw new MigrateException('This entity type does not support translation.');
+ $langcode_key = $this->getKey('langcode');
+ if (!$langcode_key) {
+ throw new MigrateException(sprintf('The "%s" entity type does not support translations.', $this->storage->getEntityTypeId()));
}
$ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
}
@@ -202,8 +205,8 @@ class EntityContentBase extends Entity implements HighestIdInterface {
* @param \Drupal\migrate\Row $row
* The row object to update from.
*
- * @return \Drupal\Core\Entity\EntityInterface|null
- * An updated entity, or NULL if it's the same as the one passed in.
+ * @return \Drupal\Core\Entity\EntityInterface
+ * An updated entity from row values.
*/
protected function updateEntity(EntityInterface $entity, Row $row) {
$empty_destinations = $row->getEmptyDestinationProperties();
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
index 52d0f4f..ff3f007 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
@@ -160,7 +160,9 @@ class EntityRevision extends EntityContentBase {
$entity->enforceIsNew(FALSE);
$entity->setNewRevision(TRUE);
}
- $this->updateEntity($entity, $row);
+ // We need to update the entity, so that the destination row IDs are
+ // correct.
+ $entity = $this->updateEntity($entity, $row);
$entity->isDefaultRevision(FALSE);
return $entity;
}
@@ -177,10 +179,23 @@ class EntityRevision extends EntityContentBase {
* {@inheritdoc}
*/
public function getIds() {
- if ($key = $this->getKey('revision')) {
- return [$key => $this->getDefinitionFromEntity($key)];
+ $ids = [];
+
+ $revision_key = $this->getKey('revision');
+ if (!$revision_key) {
+ throw new MigrateException(sprintf('The "%s" entity type does not support revisions.', $this->storage->getEntityTypeId()));
}
- throw new MigrateException('This entity type does not support revisions.');
+ $ids[$revision_key] = $this->getDefinitionFromEntity($revision_key);
+
+ if ($this->isTranslationDestination()) {
+ $langcode_key = $this->getKey('langcode');
+ if (!$langcode_key) {
+ throw new MigrateException(sprintf('The "%s" entity type does not support translations.', $this->storage->getEntityTypeId()));
+ }
+ $ids[$langcode_key] = $this->getDefinitionFromEntity($langcode_key);
+ }
+
+ return $ids;
}
/**
diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/EntityRevisionTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/EntityRevisionTest.php
new file mode 100644
index 0000000..d15446f
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/Plugin/EntityRevisionTest.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace Drupal\Tests\migrate\Kernel\Plugin;
+
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\node\Entity\Node;
+use Drupal\Tests\migrate\Kernel\MigrateTestBase;
+use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
+
+/**
+ * Tests the EntityRevision destination plugin.
+ *
+ * @group migrate
+ */
+class EntityRevisionTest extends MigrateTestBase {
+
+ use ContentTypeCreationTrait;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = [
+ 'content_translation',
+ 'field',
+ 'filter',
+ 'language',
+ 'node',
+ 'system',
+ 'text',
+ 'user',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->installConfig('node');
+ $this->installSchema('node', ['node_access']);
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('user');
+ }
+
+ /**
+ * Tests that EntityRevision correctly handles revision translations.
+ */
+ public function testRevisionTranslation() {
+ ConfigurableLanguage::createFromLangcode('fr')->save();
+
+ /** @var \Drupal\node\NodeInterface $node */
+ $node = Node::create([
+ 'type' => $this->createContentType()->id(),
+ 'title' => 'Default 1',
+ ]);
+ $node->addTranslation('fr', [
+ 'title' => 'French 1',
+ ]);
+ $node->save();
+ $node->setNewRevision();
+ $node->setTitle('Default 2');
+ $node->getTranslation('fr')->setTitle('French 2');
+ $node->save();
+
+ $migration = [
+ 'source' => [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => [
+ [
+ 'nid' => $node->id(),
+ 'vid' => $node->getRevisionId(),
+ 'langcode' => 'fr',
+ 'title' => 'Titre nouveau, tabarnak!',
+ ],
+ ],
+ 'ids' => [
+ 'nid' => [
+ 'type' => 'integer',
+ ],
+ 'vid' => [
+ 'type' => 'integer',
+ ],
+ 'langcode' => [
+ 'type' => 'string',
+ ],
+ ],
+ ],
+ 'process' => [
+ 'nid' => 'nid',
+ 'vid' => 'vid',
+ 'langcode' => 'langcode',
+ 'title' => 'title',
+ ],
+ 'destination' => [
+ 'plugin' => 'entity_revision:node',
+ 'translations' => TRUE,
+ ],
+ ];
+
+ /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
+ $migration = $this->container
+ ->get('plugin.manager.migration')
+ ->createStubMigration($migration);
+
+ $this->executeMigration($migration);
+
+ // The entity_revision destination uses the revision ID and langcode as its
+ // keys (the langcode is only used if the destination is configured for
+ // translation), so we should be able to look up the source IDs by revision
+ // ID and langcode.
+ $source_ids = $migration->getIdMap()->lookupSourceID([
+ 'vid' => $node->getRevisionId(),
+ 'langcode' => 'fr',
+ ]);
+ $this->assertNotEmpty($source_ids);
+ $this->assertSame($node->id(), $source_ids['nid']);
+ $this->assertSame($node->getRevisionId(), $source_ids['vid']);
+ $this->assertSame('fr', $source_ids['langcode']);
+
+ // Confirm the french revision was used in the migration, instead of the
+ // default revision.
+ /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
+ $entity_type_manager = \Drupal::entityTypeManager();
+ $revision = $entity_type_manager->getStorage('node')->loadRevision(1);
+ $this->assertSame('Default 1', $revision->label());
+ $this->assertSame('French 1', $revision->getTranslation('fr')->label());
+ $revision = $entity_type_manager->getStorage('node')->loadRevision(2);
+ $this->assertSame('Default 2', $revision->label());
+ $this->assertSame('Titre nouveau, tabarnak!', $revision->getTranslation('fr')->label());
+ }
+
+}
diff --git a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
index 9f43523..d0fd987 100644
--- a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
+++ b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
@@ -60,6 +60,7 @@ class EntityContentBaseTest extends UnitTestCase {
$this->entityType = $this->prophesize(EntityTypeInterface::class);
$this->entityType->getPluralLabel()->willReturn('wonkiness');
$this->storage->getEntityType()->willReturn($this->entityType->reveal());
+ $this->storage->getEntityTypeId()->willReturn('foo');
$this->entityManager = $this->prophesize(EntityManagerInterface::class);
}
@@ -129,7 +130,7 @@ class EntityContentBaseTest extends UnitTestCase {
$this->entityManager->reveal(),
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal()
);
- $this->setExpectedException(MigrateException::class, 'This entity type does not support translation');
+ $this->setExpectedException(MigrateException::class, 'The "foo" entity type does not support translations.');
$destination->getIds();
}
diff --git a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityRevisionTest.php
new file mode 100644
index 0000000..3fb4b7d
--- /dev/null
+++ b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityRevisionTest.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace Drupal\Tests\migrate\Unit\Plugin\migrate\destination;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\migrate\MigrateException;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Plugin\migrate\destination\EntityRevision;
+use Drupal\migrate\Row;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests entity revision destination functionality.
+ *
+ * @coversDefaultClass \Drupal\migrate\Plugin\migrate\destination\EntityRevision
+ * @group migrate
+ */
+class EntityRevisionTest extends UnitTestCase {
+
+ /**
+ * The migration.
+ *
+ * @var \Drupal\migrate\Plugin\MigrationInterface
+ */
+ protected $migration;
+
+ /**
+ * The entity storage.
+ *
+ * @var \Drupal\Core\Entity\EntityStorageInterface
+ */
+ protected $storage;
+
+ /**
+ * The entity type.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeInterface
+ */
+ protected $entityType;
+
+ /**
+ * The entity manager.
+ *
+ * @var \Drupal\Core\Entity\EntityManagerInterface
+ */
+ protected $entityManager;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ $this->migration = $this->prophesize(MigrationInterface::class);
+ $this->storage = $this->prophesize(EntityStorageInterface::class);
+
+ $this->entityType = $this->prophesize(EntityTypeInterface::class);
+ $this->entityType->getSingularLabel()->willReturn('foo');
+ $this->entityType->getPluralLabel()->willReturn('bar');
+ $this->storage->getEntityType()->willReturn($this->entityType->reveal());
+ $this->storage->getEntityTypeId()->willReturn('foo');
+
+ $this->entityManager = $this->prophesize(EntityManagerInterface::class);
+ }
+
+ /**
+ * Tests that revision destination fails for unrevisionable entities.
+ */
+ public function testUnrevisionable() {
+ $this->entityType->getKey('id')->willReturn('id');
+ $this->entityType->getKey('revision')->willReturn('');
+ $this->entityManager->getBaseFieldDefinitions('foo')
+ ->willReturn([
+ 'id' => BaseFieldDefinitionTest::create('integer'),
+ ]);
+
+ $destination = new EntityRevisionTestDestination(
+ [],
+ '',
+ [],
+ $this->migration->reveal(),
+ $this->storage->reveal(),
+ [],
+ $this->entityManager->reveal(),
+ $this->prophesize(FieldTypePluginManagerInterface::class)->reveal()
+ );
+ $this->setExpectedException(MigrateException::class, 'The "foo" entity type does not support revisions.');
+ $destination->getIds();
+ }
+
+ /**
+ * Tests that translation destination fails for untranslatable entities.
+ */
+ public function testUntranslatable() {
+ $this->entityType->getKey('id')->willReturn('id');
+ $this->entityType->getKey('revision')->willReturn('vid');
+ $this->entityType->getKey('langcode')->willReturn('');
+ $this->entityManager->getBaseFieldDefinitions('foo')
+ ->willReturn([
+ 'id' => BaseFieldDefinitionTest::create('integer'),
+ 'vid' => BaseFieldDefinitionTest::create('integer'),
+ ]);
+
+ $destination = new EntityRevisionTestDestination(
+ ['translations' => TRUE],
+ '',
+ [],
+ $this->migration->reveal(),
+ $this->storage->reveal(),
+ [],
+ $this->entityManager->reveal(),
+ $this->prophesize(FieldTypePluginManagerInterface::class)->reveal()
+ );
+ $this->setExpectedException(MigrateException::class, 'The "foo" entity type does not support translations.');
+ $destination->getIds();
+ }
+
+}
+
+/**
+ * Stub class for testing EntityRevision methods.
+ */
+class EntityRevisionTestDestination extends EntityRevision {
+
+ private $entity = NULL;
+
+ public function setEntity($entity) {
+ $this->entity = $entity;
+ }
+
+ protected function getEntity(Row $row, array $old_destination_id_values) {
+ return $this->entity;
+ }
+
+ public static function getEntityTypeId($plugin_id) {
+ return 'foo';
+ }
+
+}
+
+/**
+ * Stub class for BaseFieldDefinition.
+ */
+class BaseFieldDefinitionTest extends BaseFieldDefinition {
+
+ public static function create($type) {
+ return new static([]);
+ }
+
+ public function getSettings() {
+ return [];
+ }
+
+ public function getType() {
+ return 'integer';
+ }
+
+}
diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
index 8a6fe9a..5a63f06 100644
--- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
@@ -230,6 +230,8 @@ class EntityRevision extends RealEntityRevision {
* workings of its implementation which would trickle into mock assertions. An
* empty implementation avoids this.
*/
- protected function updateEntity(EntityInterface $entity, Row $row) {}
+ protected function updateEntity(EntityInterface $entity, Row $row) {
+ return $entity;
+ }
}
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php
index 6be7801..db44c63 100644
--- a/core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php
+++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php
@@ -115,7 +115,7 @@ class EntityContentBaseTest extends MigrateDrupal6TestBase {
// Match the expected message. Can't use default argument types, because
// we need to convert to string from TranslatableMarkup.
$argument = Argument::that(function ($msg) {
- return strpos((string) $msg, "This entity type does not support translation") !== FALSE;
+ return strpos((string) $msg, htmlentities('The "no_language_entity_test" entity type does not support translations.')) !== FALSE;
});
$message->display($argument, Argument::any())
->shouldBeCalled();