diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php index 62f3d19ba3..3a3487f69a 100644 --- a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php +++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php @@ -41,6 +41,7 @@ class MigrationPluginListTest extends KernelTestBase { 'menu_link_content', 'menu_ui', 'node', + 'options', 'path', 'search', 'shortcut', diff --git a/core/modules/migrate_drupal/migrate_drupal.services.yml b/core/modules/migrate_drupal/migrate_drupal.services.yml index 23b3492baf..b023f4e3d9 100644 --- a/core/modules/migrate_drupal/migrate_drupal.services.yml +++ b/core/modules/migrate_drupal/migrate_drupal.services.yml @@ -16,3 +16,7 @@ services: - '@module_handler' - '\Drupal\migrate_drupal\Annotation\MigrateCckField' deprecated: The "%service_id%" service is deprecated. You should use the 'plugin.manager.migrate.field' service instead. See https://www.drupal.org/node/2751897 + post_process_migration_manager: + class: Drupal\migrate_drupal\PostProcessMigrationManager + arguments: + - '@plugin.manager.migration' diff --git a/core/modules/migrate_drupal/migrations/entity_reference_translation.yml b/core/modules/migrate_drupal/migrations/entity_reference_translation.yml new file mode 100644 index 0000000000..9a6b117d38 --- /dev/null +++ b/core/modules/migrate_drupal/migrations/entity_reference_translation.yml @@ -0,0 +1,15 @@ +id: entity_reference_translation +label: Entity reference translations +migration_tags: + - migrate_drupal_post_process +deriver: Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver +target_types: + - node +source: + plugin: empty + key: default + target: default +migration_dependencies: + optional: + - d6_node_translation + - d7_node_translation diff --git a/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php index b566c82167..bc3bf39b5b 100644 --- a/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php +++ b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php @@ -97,6 +97,10 @@ protected function getMigrations($database_state_key, $drupal_version) { $migrations = []; foreach ($all_migrations as $migration) { try { + $plugin_definition = $migration->getPluginDefinition(); + if (in_array('migrate_drupal_post_process', $plugin_definition['migration_tags'])) { + continue; + } // @todo https://drupal.org/node/2681867 We should be able to validate // the entire migration at this point. $source_plugin = $migration->getSourcePlugin(); diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php b/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php new file mode 100644 index 0000000000..71542372b2 --- /dev/null +++ b/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php @@ -0,0 +1,139 @@ +basePluginId = $base_plugin_id; + $this->entityFieldManager = $entity_field_manager; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $base_plugin_id, + $container->get('entity_field.manager'), + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + $field_map = $this->entityFieldManager->getFieldMapByFieldType('entity_reference'); + foreach ($field_map as $entity_type => $fields) { + foreach ($fields as $field_name => $field) { + foreach ($field['bundles'] as $bundle) { + $storage = $this->entityTypeManager->getStorage($entity_type); + if (!($storage instanceof SqlEntityStorageInterface)) { + continue; + } + $field_definitions = $this->entityFieldManager->getFieldDefinitions($entity_type, $bundle); + $target_type = $field_definitions[$field_name]->getSettings()['target_type']; + if (!in_array($target_type, $base_plugin_definition['target_types'])) { + continue; + } + + $derivative = $entity_type . '__' . $bundle; + if (!isset($this->derivatives[$derivative])) { + $this->derivatives[$derivative] = $base_plugin_definition; + + $this->derivatives[$derivative]['label'] = $this->t('@label (@derivative)', [ + '@label' => $base_plugin_definition['label'], + '@derivative' => $derivative, + ]); + + $this->derivatives[$derivative]['source']['plugin'] = 'content_entity:' . $entity_type; + $this->derivatives[$derivative]['source']['bundle'] = $bundle; + + $base_field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type); + foreach (array_keys($base_field_definitions) as $base_field) { + $this->derivatives[$derivative]['process'][$base_field] = $base_field; + } + + $this->derivatives[$derivative]['destination']['plugin'] = 'entity:' . $entity_type; + $this->derivatives[$derivative]['destination']['default_bundle'] = $bundle; + if ($this->entityTypeManager->getDefinition($entity_type)->isTranslatable()) { + $this->derivatives[$derivative]['destination']['translations'] = TRUE; + } + } + $this->derivatives[$derivative]['destination']['overwrite_properties'][] = $field_name; + + $this->derivatives[$derivative]['process'][$field_name] = [ + 'plugin' => 'sub_process', + 'source' => $field_name, + 'process' => [ + 'target_id' => [ + [ + 'plugin' => 'migration_lookup', + 'source' => 'target_id', + 'migration' => $this->derivatives[$derivative]['migration_dependencies']['optional'], + 'no_stub' => TRUE, + ], + [ + 'plugin' => 'skip_on_empty', + 'method' => 'row', + ], + [ + 'plugin' => 'extract', + 'index' => [0], + ], + ], + ], + ]; + } + } + } + return $this->derivatives; + } + +} diff --git a/core/modules/migrate_drupal/src/PostProcessMigrationManager.php b/core/modules/migrate_drupal/src/PostProcessMigrationManager.php new file mode 100644 index 0000000000..6a1f4bb55d --- /dev/null +++ b/core/modules/migrate_drupal/src/PostProcessMigrationManager.php @@ -0,0 +1,34 @@ +pluginManager = $plugin_manager; + } + + /** + * TODO + */ + public function getPostProcessMigrations() { + $this->pluginManager->clearCachedDefinitions(); + return $this->pluginManager->createInstancesByTag('migrate_drupal_post_process'); + } + +} diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php index a7117303e7..b92db9f425 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -3606,6 +3606,21 @@ 'translatable' => '0', 'deleted' => '0', )) +->values(array( + 'id' => '37', + 'field_name' => 'field_reference', + 'type' => 'entityreference', + 'module' => 'entityreference', + 'active' => '1', + 'storage_type' => 'field_sql_storage', + 'storage_module' => 'field_sql_storage', + 'storage_active' => '1', + 'locked' => '0', + 'data' => 'a:7:{s:12:"translatable";s:1:"0";s:12:"entity_types";a:0:{}s:8:"settings";a:3:{s:11:"target_type";s:4:"node";s:7:"handler";s:4:"base";s:16:"handler_settings";a:2:{s:14:"target_bundles";a:1:{s:7:"article";s:7:"article";}s:4:"sort";a:1:{s:4:"type";s:4:"none";}}}s:7:"storage";a:5:{s:4:"type";s:17:"field_sql_storage";s:8:"settings";a:0:{}s:6:"module";s:17:"field_sql_storage";s:6:"active";s:1:"1";s:7:"details";a:1:{s:3:"sql";a:2:{s:18:"FIELD_LOAD_CURRENT";a:1:{s:26:"field_data_field_reference";a:1:{s:9:"target_id";s:25:"field_reference_target_id";}}s:19:"FIELD_LOAD_REVISION";a:1:{s:30:"field_revision_field_reference";a:1:{s:9:"target_id";s:25:"field_reference_target_id";}}}}}s:12:"foreign keys";a:1:{s:4:"node";a:2:{s:5:"table";s:4:"node";s:7:"columns";a:1:{s:9:"target_id";s:3:"nid";}}}s:7:"indexes";a:1:{s:9:"target_id";a:1:{i:0;s:9:"target_id";}}s:2:"id";s:2:"37";}', + 'cardinality' => '1', + 'translatable' => '0', + 'deleted' => '0', +)) ->execute(); $connection->schema()->createTable('field_config_instance', array( @@ -4206,6 +4221,15 @@ 'data' => 'a:6:{s:5:"label";s:17:"Date without time";s:6:"widget";a:5:{s:6:"weight";s:1:"2";s:4:"type";s:11:"date_select";s:6:"module";s:4:"date";s:6:"active";i:1;s:8:"settings";a:6:{s:12:"input_format";s:13:"m/d/Y - H:i:s";s:19:"input_format_custom";s:0:"";s:10:"year_range";s:5:"-3:+3";s:9:"increment";s:2:"15";s:14:"label_position";s:5:"above";s:10:"text_parts";a:0:{}}}s:8:"settings";a:5:{s:13:"default_value";s:3:"now";s:18:"default_value_code";s:0:"";s:14:"default_value2";s:4:"same";s:19:"default_value_code2";s:0:"";s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:12:"date_default";s:6:"weight";s:1:"3";s:8:"settings";a:5:{s:11:"format_type";s:4:"long";s:15:"multiple_number";s:0:"";s:13:"multiple_from";s:0:"";s:11:"multiple_to";s:0:"";s:6:"fromto";s:4:"both";}s:6:"module";s:4:"date";}}s:8:"required";i:0;s:11:"description";s:0:"";}', 'deleted' => '0', )) +->values(array( + 'id' => '63', + 'field_id' => '37', + 'field_name' => 'field_reference', + 'entity_type' => 'node', + 'bundle' => 'article', + 'data' => 'a:7:{s:5:"label";s:9:"Reference";s:6:"widget";a:5:{s:6:"weight";s:2:"20";s:4:"type";s:14:"options_select";s:6:"module";s:7:"options";s:6:"active";i:1;s:8:"settings";a:0:{}}s:8:"settings";a:1:{s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:21:"entityreference_label";s:6:"weight";s:2:"20";s:8:"settings";a:2:{s:13:"bypass_access";i:0;s:4:"link";i:1;}s:6:"module";s:15:"entityreference";}}s:8:"required";i:0;s:11:"description";s:0:"";s:13:"default_value";N;}', + 'deleted' => '0', +)) ->execute(); $connection->schema()->createTable('field_data_body', array( @@ -6007,6 +6031,143 @@ )) ->execute(); +$connection->schema()->createTable('field_data_field_reference', array( + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'bundle' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'deleted' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'tiny', + 'default' => '0', + ), + 'entity_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'revision_id' => array( + 'type' => 'int', + 'not null' => FALSE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ), + 'delta' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'field_reference_target_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + ), + 'primary key' => array( + 'entity_type', + 'entity_id', + 'deleted', + 'delta', + 'language', + ), + 'indexes' => array( + 'entity_type' => array( + 'entity_type', + ), + 'bundle' => array( + 'bundle', + ), + 'deleted' => array( + 'deleted', + ), + 'entity_id' => array( + 'entity_id', + ), + 'revision_id' => array( + 'revision_id', + ), + 'language' => array( + 'language', + ), + 'field_reference_target_id' => array( + 'field_reference_target_id', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('field_data_field_reference') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'field_reference_target_id', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '5', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '3', + 'revision_id' => '3', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '4', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '3', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '5', + 'revision_id' => '5', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '2', +)) +->execute(); + $connection->schema()->createTable('field_data_field_tags', array( 'fields' => array( 'entity_type' => array( @@ -9385,6 +9546,144 @@ )) ->execute(); +$connection->schema()->createTable('field_revision_field_reference', array( + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'bundle' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'deleted' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'tiny', + 'default' => '0', + ), + 'entity_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'revision_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ), + 'delta' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'field_reference_target_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + ), + 'primary key' => array( + 'entity_type', + 'entity_id', + 'revision_id', + 'deleted', + 'delta', + 'language', + ), + 'indexes' => array( + 'entity_type' => array( + 'entity_type', + ), + 'bundle' => array( + 'bundle', + ), + 'deleted' => array( + 'deleted', + ), + 'entity_id' => array( + 'entity_id', + ), + 'revision_id' => array( + 'revision_id', + ), + 'language' => array( + 'language', + ), + 'field_reference_target_id' => array( + 'field_reference_target_id', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('field_revision_field_reference') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'field_reference_target_id', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '5', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '3', + 'revision_id' => '3', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '4', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '3', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '5', + 'revision_id' => '5', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '2', +)) +->execute(); + $connection->schema()->createTable('field_revision_field_tags', array( 'fields' => array( 'entity_type' => array( diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/PostProcessMigrationTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/PostProcessMigrationTest.php new file mode 100644 index 0000000000..4552c10c4b --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/PostProcessMigrationTest.php @@ -0,0 +1,99 @@ +fileMigrationSetup(); + + $this->installEntitySchema('node'); + $this->installEntitySchema('comment'); + $this->installEntitySchema('taxonomy_term'); + $this->installConfig(static::$modules); + $this->installSchema('node', ['node_access']); + + $this->executeMigrations([ + 'language', + 'd7_user_role', + 'd7_user', + 'd7_node_type', + 'd7_language_content_settings', + 'd7_comment_type', + 'd7_taxonomy_vocabulary', + 'd7_field', + 'd7_field_instance', + 'd7_node', + 'd7_node_translation', + ]); + } + + /** + * Test entity reference translations. + */ + public function testEntityReferenceTranslations() { + $this->assertTrue(TRUE); + + $node = Node::load(2); + $this->assertSame([0 => ['target_id' => '5']], $node->get('field_reference')->getValue()); + $translation = $node->getTranslation('is'); + $this->assertSame([0 => ['target_id' => '4']], $translation->get('field_reference')->getValue()); + + $node = Node::load(4); + $this->assertSame([0 => ['target_id' => '3']], $node->get('field_reference')->getValue()); + $translation = $node->getTranslation('en'); + $this->assertSame([0 => ['target_id' => '2']], $translation->get('field_reference')->getValue()); + + $post_process_migration_manager = $this->container->get('post_process_migration_manager'); + $post_process_migrations = $post_process_migration_manager->getPostProcessMigrations(); + $migrations = []; + foreach ($post_process_migrations as $migration) { + $migrations[] = $migration->id(); + } + $this->executeMigrations($migrations); + + $node = Node::load(2); + $this->assertSame([0 => ['target_id' => '4']], $node->get('field_reference')->getValue()); + $translation = $node->getTranslation('is'); + $this->assertSame([0 => ['target_id' => '4']], $translation->get('field_reference')->getValue()); + + $node = Node::load(4); + $this->assertSame([0 => ['target_id' => '2']], $node->get('field_reference')->getValue()); + $translation = $node->getTranslation('en'); + $this->assertSame([0 => ['target_id' => '2']], $translation->get('field_reference')->getValue()); + } + +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml b/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml index d02ea98dbd..9fa26672d7 100644 --- a/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml @@ -7,6 +7,15 @@ migrate_drupal_ui.upgrade: _custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess' options: _admin_route: TRUE +migrate_drupal_ui.upgrade_post_process: + path: '/upgrade/postprocess' + defaults: + _form: '\Drupal\migrate_drupal_ui\Form\PostProcessForm' + _title: 'Post Process' + requirements: + _custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess' + options: + _admin_route: TRUE migrate_drupal_ui.log: path: '/admin/reports/upgrade' diff --git a/core/modules/migrate_drupal_ui/src/Form/PostProcessForm.php b/core/modules/migrate_drupal_ui/src/Form/PostProcessForm.php new file mode 100644 index 0000000000..56f565edad --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Form/PostProcessForm.php @@ -0,0 +1,106 @@ +postProcessMigrationManager = $post_process_migration_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('post_process_migration_manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'migrate_drupal_ui_post_process_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $migrations = $this->postProcessMigrationManager->getPostProcessMigrations(); + $migration_array = []; + foreach ($migrations as $migration) { + $migration_array[$migration->id()] = $migration->label(); + } + $form_state->set('post_process_migrations', $migration_array); + $form = parent::buildForm($form, $form_state); + $form['actions']['submit']['#value'] = $this->t('Perform post process migrations'); + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $storage = $form_state->getStorage(); + + $migrations = $storage['post_process_migrations']; + $config['source_base_path'] = $storage['source_base_path']; + $batch = [ + 'title' => $this->t('Running post process migrations'), + 'progress_message' => '', + 'operations' => [ + [ + [MigrateUpgradeImportBatch::class, 'run'], + [array_keys($migrations), $config], + ], + ], + 'finished' => [ + MigrateUpgradeImportBatch::class, 'finished', + ], + ]; + batch_set($batch); + $form_state->setRedirect(''); + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->t('Post process migrations'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url(''); + } + +} diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php index 385a4e5804..72ce58fe55 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php @@ -65,8 +65,8 @@ protected function getEntityCounts() { 'configurable_language' => 4, 'contact_form' => 3, 'editor' => 2, - 'field_config' => 63, - 'field_storage_config' => 46, + 'field_config' => 64, + 'field_storage_config' => 47, 'file' => 3, 'filter_format' => 7, 'image_style' => 6,