diff --git a/core/modules/migrate_drupal/src/MigrationCreationTrait.php b/core/modules/migrate_drupal/src/MigrationCreationTrait.php new file mode 100644 index 0000000..e8777b7 --- /dev/null +++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php @@ -0,0 +1,136 @@ +getLegacyDrupalVersion($connection)) { + throw new \Exception($this->t('Source database does not contain a recognizable Drupal version.')); + } + + $version_tag = 'Drupal ' . $drupal_version; + + $template_storage = \Drupal::service('migrate.template_storage'); + $migration_templates = $template_storage->findTemplatesByTag($version_tag); + foreach ($migration_templates as $id => $template) { + // Configure file migrations so they can find the files. + if ($template['destination']['plugin'] == 'entity:file') { + if ($source_base_path) { + // Make sure we have a single trailing slash. + $source_base_path = rtrim($source_base_path, '/') . '/'; + $migration_templates[$id]['destination']['source_base_path'] = $source_base_path; + } + } + // @todo: Use a group to hold the db info, so we don't have to stuff it + // into every migration. + $migration_templates[$id]['source']['key'] = 'upgrade'; + $migration_templates[$id]['source']['database'] = $database; + } + + // Let the builder service create our migration configuration entities from + // the templates, expanding them to multiple entities where necessary. + /** @var \Drupal\migrate\MigrationBuilder $builder */ + $builder = \Drupal::service('migrate.migration_builder'); + $migrations = $builder->createMigrations($migration_templates); + $migration_ids = []; + foreach ($migrations as $migration) { + try { + if ($migration->getSourcePlugin() instanceof RequirementsInterface) { + $migration->getSourcePlugin()->checkRequirements(); + } + if ($migration->getDestinationPlugin() instanceof RequirementsInterface) { + $migration->getDestinationPlugin()->checkRequirements(); + } + $migration->save(); + $migration_ids[] = $migration->id(); + } + // Migrations which are not applicable given the source and destination + // site configurations (e.g., what modules are enabled) will be silently + // ignored. + catch (RequirementsException $e) { + } + catch (PluginNotFoundException $e) { + } + } + + // loadMultiple will sort the migrations in dependency order. + return array_keys(Migration::loadMultiple($migration_ids)); + } + + /** + * Determine what version of Drupal the source database contains. + * + * @param \Drupal\Core\Database\Connection $connection + * + * @return int|FALSE + */ + protected function getLegacyDrupalVersion(Connection $connection) { + // Don't assume because a table of that name exists, that it has the columns + // we're querying. Catch exceptions and report that the source database is + // not Drupal. + + // Druppal 5/6/7 can be detected by the schema_version in the system table. + if ($connection->schema()->tableExists('system')) { + try { + $version_string = $connection->query('SELECT schema_version FROM {system} WHERE name = :module', [':module' => 'system']) + ->fetchField(); + if ($version_string && $version_string[0] == '1') { + if ((int) $version_string >= 1000) { + $version_string = '5'; + } + else { + $version_string = FALSE; + } + } + } + catch (\PDOException $e) { + $version_string = FALSE; + } + } + // For Drupal 8 (and we're predicting beyond) the schema version is in the + // key_value store. + elseif ($connection->schema()->tableExists('key_value')) { + $result = $connection->query("SELECT value FROM {key_value} WHERE collection = :system_schema and name = :module", [':system_schema' => 'system.schema', ':module' => 'system'])->fetchField(); + $version_string = unserialize($result); + } + else { + $version_string = FALSE; + } + + return $version_string ? substr($version_string, 0, 1) : FALSE; + } + +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.info.yml b/core/modules/migrate_drupal_ui/migrate_drupal_ui.info.yml new file mode 100644 index 0000000..bc0ddd4 --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.info.yml @@ -0,0 +1,10 @@ +name: Migrate Drupal UI +type: module +description: 'UI for migration from older Drupal versions.' +package: Core +version: VERSION +core: 8.x +configure: migrate_drupal_ui.upgrade +dependencies: + - migrate + - migrate_drupal diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.install b/core/modules/migrate_drupal_ui/migrate_drupal_ui.install new file mode 100644 index 0000000..015e7c1 --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.install @@ -0,0 +1,16 @@ +toString(); + drupal_set_message(t("The Migrate Drupal UI module has been enabled. Proceed to the upgrade form.", array('@url' => $url))); +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.module b/core/modules/migrate_drupal_ui/migrate_drupal_ui.module new file mode 100755 index 0000000..a07f545 --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.module @@ -0,0 +1,19 @@ +' . t('This form is used for importing 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.', array('!migrate' => 'https://www.drupal.org/upgrade')) . '
'; + return $output; + } +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml b/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml new file mode 100644 index 0000000..b8e522f --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml @@ -0,0 +1,18 @@ +migrate_drupal_ui.upgrade: + path: '/upgrade' + defaults: + _form: '\Drupal\migrate_drupal_ui\Form\MigrateUpgradeForm' + _title: 'Migration' + requirements: + _permission: 'administer site configuration' + options: + _admin_route: TRUE + +migrate_drupal_ui.log: + path: '/upgrade/log' + defaults: + _controller: '\Drupal\migrate_drupal_ui\Controller\MigrateController::showLog' + requirements: + _permission: 'administer site configuration' + options: + _admin_route: TRUE diff --git a/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php b/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php new file mode 100644 index 0000000..254d8cd --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php @@ -0,0 +1,24 @@ + 'migrate_drupal_ui'); + return $this->redirect('dblog.overview'); + } +} diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php new file mode 100644 index 0000000..e736933 --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php @@ -0,0 +1,164 @@ +t('Drupal Upgrade: Source site information'); + + $form['database'] = array( + '#type' => 'details', + '#title' => $this->t('Source database'), + '#description' => $this->t('Provide credentials for the database of the Drupal site you want to migrate.'), + '#open' => TRUE, + ); + + // Copy the values from the parent form into our structure. + $form['database']['driver'] = $form['driver']; + $form['database']['settings'] = $form['settings']; + $form['database']['settings']['mysql']['host'] = $form['database']['settings']['mysql']['advanced_options']['host']; + $form['database']['settings']['mysql']['host']['#title'] = 'Database host'; + $form['database']['settings']['mysql']['host']['#weight'] = 0; + + // Remove the values from the parent form. + unset($form['driver']); + unset($form['database']['settings']['mysql']['database']['#default_value']); + unset($form['settings']); + unset($form['database']['settings']['mysql']['advanced_options']['host']); + + $form['source'] = array( + '#type' => 'details', + '#title' => $this->t('Source site files'), + '#open' => TRUE, + ); + $form['source']['source_base_path'] = array( + '#type' => 'textfield', + '#title' => $this->t('Source file path'), + '#default_value' => 'http://', + '#description' => $this->t('To import public files from your current Drupal site, enter your site address (e.g. http://example.com), or a local file directory containing your site (e.g. /var/www/docroot).'), + ); + +/* + $form['files'] = array( + '#type' => 'details', + '#title' => $this->t('Files'), + '#open' => TRUE, + '#weight' => 2, + ); + // @todo: Not yet implemented, depends on https://www.drupal.org/node/2547125. + $form['files']['private_file_directory'] = array( + '#type' => 'textfield', + '#title' => $this->t('Private file path'), + '#description' => $this->t('To import private files from your current Drupal site, enter a local file directory containing your files (e.g. /var/private_files).'), + ); +*/ + + // Rename the submit button. + $form['actions']['save']['#value'] = $this->t('Perform upgrade'); + + // The parent form uses #limit_validation_errors to avoid validating the + // unselected database drivers. This makes it difficult for us to handle + // database errors in our validation, and does not appear to actually be + // necessary with the current implementation, so we remove it. + unset($form['actions']['save']['#limit_validation_errors']); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + // 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. + $driver = $form_state->getValue('driver'); + $drivers = $this->getDatabaseTypes(); + $reflection = new \ReflectionClass($drivers[$driver]); + $install_namespace = $reflection->getNamespaceName(); + + $database = $form_state->getValue($driver); + // Cut the trailing \Install from namespace. + $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\')); + $database['driver'] = $driver; + + // Validate the driver settings and just end here if we have any issues. + if ($errors = $drivers[$driver]->validateDatabaseSettings($database)) { + foreach ($errors as $name => $message) { + $form_state->setErrorByName($name, $message); + } + return; + } + + try { + // Create all the relevant migrations and get their IDs so we can run them. + $migration_ids = $this->createMigrations($database, $form_state->getValue('source_base_path')); + + // Store the retrieved migration ids on the form state. + $form_state->setValue('migration_ids', $migration_ids); + } + catch (\Exception $e) { + $form_state->setErrorByName(NULL, $this->t($e->getMessage())); + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $batch = array( + 'title' => $this->t('Running migrations'), + 'progress_message' => '', + 'operations' => array( + array(array('Drupal\migrate_drupal_ui\MigrateUpgradeRunBatch', 'run'), array($form_state->getValue('migration_ids'))), + ), + 'finished' => array('Drupal\migrate_drupal_ui\MigrateUpgradeRunBatch', 'finished'), + ); + batch_set($batch); + $form_state->setRedirect('