diff --git a/migrate_upgrade.module b/migrate_upgrade.module index 199e057..59be009 100755 --- a/migrate_upgrade.module +++ b/migrate_upgrade.module @@ -16,7 +16,7 @@ function migrate_upgrade_help($route_name, RouteMatchInterface $route_match) { $output = '

' . t('The Drupal Upgrade UI module provides a one-click upgrade from an earlier version of Drupal. For more details, see upgrading from previous versions.', ['!migrate' => 'https://www.drupal.org/upgrade']) . '

'; return $output; case 'migrate_upgrade.upgrade': - $output = '

' . t('This form is used for upgrading configuration and content from a previous version of your Drupal site. Make sure you have backups of your destination site before submitting this form. Also, ensure that the source database and files are accessible to the destination site. For more details, see upgrading from previous versions.', ['!migrate' => 'https://www.drupal.org/upgrade']) . '

'; + $output = '

' . t('Performing this upgrade operation will alter site configuration to match that of your original site, and import site data including but not limited to user accounts, vocabularies, content, and comments. For more details, see upgrading from previous versions.', ['!migrate' => 'https://www.drupal.org/upgrade']) . '

'; return $output; } } diff --git a/src/Form/MigrateUpgradeForm.php b/src/Form/MigrateUpgradeForm.php index bb0a84d..4430b19 100644 --- a/src/Form/MigrateUpgradeForm.php +++ b/src/Form/MigrateUpgradeForm.php @@ -7,8 +7,13 @@ namespace Drupal\migrate_upgrade\Form; +use Drupal\Core\Form\ConfirmFormHelper; +use Drupal\Core\Form\ConfirmFormInterface; use Drupal\Core\Installer\Form\SiteSettingsForm; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; +use Drupal\migrate\Entity\Migration; +use Drupal\migrate\Entity\MigrationInterface; use Drupal\migrate_upgrade\MigrationCreationTrait; /** @@ -16,11 +21,515 @@ use Drupal\migrate_upgrade\MigrationCreationTrait; * obtaining (source) database credentials on the install process, we build off * its form. */ -class MigrateUpgradeForm extends SiteSettingsForm { +class MigrateUpgradeForm extends SiteSettingsForm implements ConfirmFormInterface { use MigrationCreationTrait; /** + * The submitted data needing to be confirmed. + * + * @var array + */ + protected $data = []; + + /** + * @todo: Find a mechanism to derive this information from the migrations + * themselves. + * + * @var array + */ + protected $moduleUpgradePaths = [ + 'd6_action_settings' => [ + 'source_module' => 'system', + 'destination_module' => 'action' + ], + 'd6_aggregator_feed' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd6_aggregator_item' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd6_aggregator_settings' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd7_aggregator_settings' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd7_blocked_ips' => [ + 'source_module' => 'system', + 'destination_module' => 'ban', + ], + 'd6_block' => [ + 'source_module' => 'block', + 'destination_module' => 'block', + ], + 'd6_block_content_body_field' => [ + 'source_module' => 'block', + 'destination_module' => 'block_content', + ], + 'd6_block_content_type' => [ + 'source_module' => 'block', + 'destination_module' => 'block_content', + ], + 'd6_custom_block' => [ + 'source_module' => 'block', + 'destination_module' => 'block_content', + ], + 'd6_book' => [ + 'source_module' => 'book', + 'destination_module' => 'book', + ], + 'd6_book_settings' => [ + 'source_module' => 'book', + 'destination_module' => 'book', + ], + 'd6_comment' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_entity_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_entity_form_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_entity_form_display_subject' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_field' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_field_instance' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_type' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_entity_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_entity_form_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_entity_form_display_subject' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_field' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_field_instance' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_type' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_contact_category' => [ + 'source_module' => 'contact', + 'destination_module' => 'contact', + ], + 'd6_contact_settings' => [ + 'source_module' => 'contact', + 'destination_module' => 'contact', + ], + 'd6_dblog_settings' => [ + 'source_module' => 'dblog', + 'destination_module' => 'dblog', + ], + 'd7_dblog_settings' => [ + 'source_module' => 'dblog', + 'destination_module' => 'dblog', + ], + 'd6_field' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd6_field_formatter_settings' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd6_field_instance' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd6_field_instance_widget_settings' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd7_field' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_field_formatter_settings' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_field_instance' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_field_instance_widget_settings' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_view_modes' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd6_file' => [ + 'source_module' => 'system', + 'destination_module' => 'file', + ], + 'd6_file_settings' => [ + 'source_module' => 'system', + 'destination_module' => 'file', + ], + 'd6_upload' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_entity_display' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_entity_form_display' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_field' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_field_instance' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd7_file' => [ + 'source_module' => 'file', + 'destination_module' => 'file', + ], + 'd6_filter_format' => [ + 'source_module' => 'filter', + 'destination_module' => 'filter', + ], + 'd7_filter_format' => [ + 'source_module' => 'filter', + 'destination_module' => 'filter', + ], + 'd6_forum_settings' => [ + 'source_module' => 'forum', + 'destination_module' => 'forum', + ], + 'd6_imagecache_presets' => [ + 'source_module' => 'imagecache', + 'destination_module' => 'image', + ], + 'd7_image_settings' => [ + 'source_module' => 'image', + 'destination_module' => 'image', + ], + 'd7_language_negotiation_settings' => [ + 'source_module' => 'locale', + 'destination_module' => 'language', + ], + 'locale_settings' => [ + 'source_module' => 'locale', + 'destination_module' => 'locale', + ], + 'd6_menu_links' => [ + 'source_module' => 'menu', + 'destination_module' => 'menu_link_content', + ], + 'd7_menu_links' => [ + 'source_module' => 'menu', + 'destination_module' => 'menu_link_content', + ], + 'menu_settings' => [ + 'source_module' => 'menu', + 'destination_module' => 'menu_ui', + ], + 'd6_node' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_revision' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_setting_promote' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_setting_status' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_setting_sticky' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_settings' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_type' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_view_modes' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_revision' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_settings' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_title_label' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_type' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_url_alias' => [ + 'source_module' => 'path', + 'destination_module' => 'path', + ], + 'd6_search_page' => [ + 'source_module' => 'search', + 'destination_module' => 'search', + ], + 'd6_search_settings' => [ + 'source_module' => 'search', + 'destination_module' => 'search', + ], + 'd7_search_settings' => [ + 'source_module' => 'search', + 'destination_module' => 'search', + ], + 'd6_simpletest_settings' => [ + 'source_module' => 'simpletest', + 'destination_module' => 'simpletest', + ], + 'd7_simpletest_settings' => [ + 'source_module' => 'simpletest', + 'destination_module' => 'simpletest', + ], + 'd6_statistics_settings' => [ + 'source_module' => 'statistics', + 'destination_module' => 'statistics', + ], + 'd6_syslog_settings' => [ + 'source_module' => 'syslog', + 'destination_module' => 'syslog', + ], + 'd7_syslog_settings' => [ + 'source_module' => 'syslog', + 'destination_module' => 'syslog', + ], + 'd6_date_formats' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_cron' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_date' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_file' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_image' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_image_gd' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_logging' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_maintenance' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_performance' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_rss' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_site' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'menu' => [ + 'source_module' => 'menu', + 'destination_module' => 'system', + ], + 'd6_taxonomy_settings' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_taxonomy_term' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_taxonomy_vocabulary' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_term_node' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_term_node_revision' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_entity_display' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_entity_form_display' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_field' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_field_instance' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'text_settings' => [ + 'source_module' => 'text', + 'destination_module' => 'text', + ], + 'd7_tracker_settings' => [ + 'source_module' => 'tracker', + 'destination_module' => 'tracker', + ], + 'd6_update_settings' => [ + 'source_module' => 'update', + 'destination_module' => 'update', + ], + 'd6_profile_values' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'd6_user' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_contact_settings' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_mail' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_picture_file' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_role' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_settings' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user_flood' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user_mail' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user_role' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_entity_display' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_entity_form_display' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_field' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_field_instance' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_profile_entity_display' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'user_profile_entity_form_display' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'user_profile_field' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'user_profile_field_instance' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + ]; + + /** * {@inheritdoc} */ public function getFormId() { @@ -31,6 +540,11 @@ class MigrateUpgradeForm extends SiteSettingsForm { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { + // When this is the confirmation step, present the confirmation form. + if ($this->data) { + return $this->buildConfirmForm($form, $form_state); + } + // Make sure the install API is available. include_once DRUPAL_ROOT . '/core/includes/install.inc'; @@ -78,7 +592,7 @@ class MigrateUpgradeForm extends SiteSettingsForm { */ // Rename the submit button. - $form['actions']['save']['#value'] = $this->t('Perform upgrade'); + $form['actions']['save']['#value'] = $this->t('Review upgrade'); // The parent form uses #limit_validation_errors to avoid validating the // unselected database drivers. This makes it difficult for us to handle @@ -92,7 +606,83 @@ class MigrateUpgradeForm extends SiteSettingsForm { /** * {@inheritdoc} */ + public function buildConfirmForm(array $form, FormStateInterface $form_state) { + $form['#title'] = $this->getQuestion(); + + $form['#attributes']['class'][] = 'confirmation'; + $form['description'] = ['#markup' => $this->getDescription()]; + $form[$this->getFormName()] = ['#type' => 'hidden', '#value' => 1]; + + $form['module_list'] = [ + '#type' => 'table', + '#header' => [$this->t('Source module'), $this->t('Destination module'), $this->t('Data to be upgraded')], + ]; + + $table_data = []; + foreach ($this->data['migration_ids'] as $migration_id) { + /** @var MigrationInterface $migration */ + $migration = Migration::load($migration_id); + $template_id = $migration->get('template'); + $source_module = $this->moduleUpgradePaths[$template_id]['source_module']; + $destination_module = $this->moduleUpgradePaths[$template_id]['destination_module']; + $table_data[$source_module][$destination_module][$migration_id] = $migration->label(); + } + ksort($table_data); + foreach ($table_data as $source_module => $destination_module_info) { + ksort($table_data[$source_module]); + } + $last_source_module = $last_destination_module = ''; + foreach ($table_data as $source_module => $destination_module_info) { + foreach ($destination_module_info as $destination_module => $migration_ids) { + foreach ($migration_ids as $migration_id => $migration_label) { + if ($source_module == $last_source_module) { + $display_source_module = ''; + } + else { + $display_source_module = $source_module; + $last_source_module = $source_module; + } + if ($destination_module == $last_destination_module) { + $display_destination_module = ''; + } + else { + $display_destination_module = $destination_module; + $last_destination_module = $destination_module; + } + $form['module_list'][$migration_id] = [ + 'source_module' => ['#plain_text' => $display_source_module], + 'destination_module' => ['#plain_text' => $display_destination_module], + 'migration' => ['#plain_text' => $migration_label], + ]; + } + } + } + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->getConfirmText(), + '#button_type' => 'primary', + ]; + + $form['actions']['cancel'] = ConfirmFormHelper::buildCancelLink($this, $this->getRequest()); + + // By default, render the form using theme_confirm_form(). + if (!isset($form['#theme'])) { + $form['#theme'] = 'confirm_form'; + } + return $form; + } + + /** + * {@inheritdoc} + */ public function validateForm(array &$form, FormStateInterface $form_state) { + // The confirmation step needs no additional validation. + if ($this->data) { + return; + } + // Retrieve the database driver from the form, use reflection to get the // namespace and then construct a valid database array the same as in // settings.php. @@ -120,6 +710,10 @@ class MigrateUpgradeForm extends SiteSettingsForm { // Store the retrieved migration ids on the form state. $form_state->setValue('migration_ids', $migration_ids); + + // Also save the computed upgrade path info. + $form_state->setValue('valid_upgrade_paths', $this->validUpgradePaths); + $form_state->setValue('missing_destinations', $this->missingDestinations); } catch (\Exception $e) { $form_state->setErrorByName(NULL, $this->t($e->getMessage())); @@ -130,11 +724,18 @@ class MigrateUpgradeForm extends SiteSettingsForm { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + // If this form has not yet been confirmed, store the values and rebuild. + if (!$this->data) { + $form_state->setRebuild(); + $this->data = $form_state->getValues(); + return; + } + $batch = [ 'title' => $this->t('Running upgrade'), 'progress_message' => '', 'operations' => [ - [['Drupal\migrate_upgrade\MigrateUpgradeRunBatch', 'run'], [$form_state->getValue('migration_ids')]], + [['Drupal\migrate_upgrade\MigrateUpgradeRunBatch', 'run'], [$this->data['migration_ids']]], ], 'finished' => ['Drupal\migrate_upgrade\MigrateUpgradeRunBatch', 'finished'], ]; @@ -154,4 +755,46 @@ class MigrateUpgradeForm extends SiteSettingsForm { return drupal_get_database_types(); } + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->t('Are you sure?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('migrate_upgrade.upgrade'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->t('

This operation cannot be undone - be sure you have backed up your site database before proceeding

'); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Perform upgrade'); + } + + /** + * {@inheritdoc} + */ + public function getCancelText() { + return $this->t('Cancel'); + } + + /** + * {@inheritdoc} + */ + public function getFormName() { + return 'confirm'; + } + }