Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -0,0 +1,23 @@
/**
* @file
* Styles for the upgrade analysis report tables.
*/
.upgrade-analysis-report__status-icon:before {
content: "";
background-size: 16px;
background-position: left center;
background-repeat: no-repeat;
width: 32px;
height: 14px;
display: inline-block;
}
.upgrade-analysis-report__status-icon--warning:before {
background-image: url(../../../../misc/icons/e29700/warning.svg);
}
.upgrade-analysis-report__status-icon--checked:before {
background-image: url(../../../../misc/icons/73b355/check.svg);
}
.upgrade-analysis-report__status-icon--error:before {
background-image: url(../../../../misc/icons/e32700/error.svg);
}

View file

@ -1,11 +1,11 @@
name: 'Migrate Drupal UI'
type: module
description: 'Provides a user interface for migrating from older Drupal versions.'
package: 'Core (Experimental)'
package: Migration
version: VERSION
core: 8.x
configure: migrate_drupal_ui.upgrade
dependencies:
- migrate
- migrate_drupal
- dblog
- drupal:migrate
- drupal:migrate_drupal
- drupal:dblog

View file

@ -11,6 +11,6 @@ use Drupal\Core\Url;
* Implements hook_install().
*/
function migrate_drupal_ui_install() {
$url = Url::fromUri('base:upgrade')->toString();
drupal_set_message(t('The Migrate Drupal UI module has been enabled. Proceed to the <a href=":url">upgrade form</a>.', [':url' => $url]));
$url = Url::fromRoute('migrate_drupal_ui.upgrade')->toString();
\Drupal::messenger()->addStatus(t('The Migrate Drupal UI module has been enabled. Proceed to the <a href=":url">upgrade form</a>.', [':url' => $url]));
}

View file

@ -0,0 +1,5 @@
base:
version: VERSION
css:
component:
css/components/upgrade-analysis-report-tables.css: {}

View file

@ -26,13 +26,11 @@ function migrate_drupal_ui_help($route_name, RouteMatchInterface $route_match) {
$output .= '<dd>' . t('On the <a href=":upgrade">Upgrade</a> page, you are guided through performing the upgrade in several steps.',
[':upgrade' => \Drupal::url('migrate_drupal_ui.upgrade')]) . '</dd>';
$output .= '<dd><ol><li>' . t('You need to enter the database credentials of the Drupal site that you want to upgrade. You can also include its files directory in the upgrade.') . '</li>';
$output .= '<li>' . t('The next page then provides an overview of which upgrade paths are available or missing, before you proceed to perform the upgrade.') . '</li>';
$output .= '<li>' . t('The next page provides an overview of the modules that will be upgraded and those that will not be upgraded, before you proceed to perform the upgrade.') . '</li>';
$output .= '<li>' . t('Lastly, a message is displayed about the number of upgrade tasks that were successful or failed.') . '</li></ol></dd>';
$output .= '<dt>' . t('Reviewing the upgrade log') . '</dt>';
$output .= '<dd>' . t('You can review a <a href=":log">log of upgrade messages</a> by clicking the link in the message provided after the upgrade or by filtering the messages for the type <em>migrate_drupal_ui</em> on the <a href=":messages">Recent log messages</a> page.',
[':log' => \Drupal::url('migrate_drupal_ui.log'), ':messages' => \Drupal::url('dblog.overview')]) . '</dd>';
$output .= '<dt>' . t('Incremental upgrades') . '</dt>';
$output .= '<dd>' . t('Incremental upgrades are not yet supported through the user interface.') . '</dd>';
$output .= '<dt>' . t('Rolling back an upgrade') . '</dt>';
$output .= '<dd>' . t('Rolling back an upgrade is not yet supported through the user interface.') . '</dd>';
$output .= '</dl>';

View file

@ -1,7 +1,43 @@
migrate_drupal_ui.upgrade:
path: '/upgrade'
defaults:
_form: '\Drupal\migrate_drupal_ui\Form\MigrateUpgradeForm'
_form: '\Drupal\migrate_drupal_ui\Form\OverviewForm'
_title: 'Upgrade'
requirements:
_custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess'
options:
_admin_route: TRUE
migrate_drupal_ui.upgrade_incremental:
path: '/upgrade/incremental'
defaults:
_form: '\Drupal\migrate_drupal_ui\Form\IncrementalForm'
_title: 'Upgrade'
requirements:
_custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess'
options:
_admin_route: TRUE
migrate_drupal_ui.upgrade_credential:
path: '/upgrade/credentials'
defaults:
_form: '\Drupal\migrate_drupal_ui\Form\CredentialForm'
_title: 'Upgrade'
requirements:
_custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess'
options:
_admin_route: TRUE
migrate_drupal_ui.upgrade_idconflict:
path: '/upgrade/idconflict'
defaults:
_form: '\Drupal\migrate_drupal_ui\Form\IdConflictForm'
_title: 'Upgrade'
requirements:
_custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess'
options:
_admin_route: TRUE
migrate_drupal_ui.upgrade_review:
path: '/upgrade/review'
defaults:
_form: '\Drupal\migrate_drupal_ui\Form\ReviewForm'
_title: 'Upgrade'
requirements:
_custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess'

View file

@ -9,11 +9,13 @@ use Drupal\Core\Url;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Event\MigrateIdMapMessageEvent;
use Drupal\migrate\Event\MigrateImportEvent;
use Drupal\migrate\Event\MigrateMapDeleteEvent;
use Drupal\migrate\Event\MigrateMapSaveEvent;
use Drupal\migrate\Event\MigratePostRowSaveEvent;
use Drupal\migrate\Event\MigrateRowDeleteEvent;
use Drupal\migrate\MigrateExecutable;
use Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface;
/**
* Runs a single migration batch.
@ -55,6 +57,13 @@ class MigrateUpgradeImportBatch {
*/
protected static $messages;
/**
* The follow-up migrations.
*
* @var \Drupal\migrate\Plugin\MigrationInterface[]
*/
protected static $followUpMigrations;
/**
* Runs a single migrate batch import.
*
@ -69,6 +78,7 @@ class MigrateUpgradeImportBatch {
if (!static::$listenersAdded) {
$event_dispatcher = \Drupal::service('event_dispatcher');
$event_dispatcher->addListener(MigrateEvents::POST_ROW_SAVE, [static::class, 'onPostRowSave']);
$event_dispatcher->addListener(MigrateEvents::POST_IMPORT, [static::class, 'onPostImport']);
$event_dispatcher->addListener(MigrateEvents::MAP_SAVE, [static::class, 'onMapSave']);
$event_dispatcher->addListener(MigrateEvents::IDMAP_MESSAGE, [static::class, 'onIdMapMessage']);
@ -104,6 +114,9 @@ class MigrateUpgradeImportBatch {
// @todo Find a way to avoid this in https://www.drupal.org/node/2804611.
if ($definition['destination']['plugin'] === 'entity:file') {
// Make sure we have a single trailing slash.
if ($definition['source']['plugin'] === 'd7_file_private') {
$configuration['source']['constants']['source_base_path'] = rtrim($config['source_private_file_path'], '/') . '/';
}
$configuration['source']['constants']['source_base_path'] = rtrim($config['source_base_path'], '/') . '/';
}
@ -135,6 +148,25 @@ class MigrateUpgradeImportBatch {
\Drupal::logger('migrate_drupal_ui')->notice($message);
$context['sandbox']['num_processed'] = 0;
$context['results']['successes']++;
// If the completed migration has any follow-up migrations, add them
// to the batch migrations.
// @see onPostImport()
if (!empty(static::$followUpMigrations)) {
foreach (static::$followUpMigrations as $migration_id => $migration) {
if (!in_array($migration_id, $context['sandbox']['migration_ids'], TRUE)) {
// Add the follow-up migration ID to the batch migration IDs for
// later execution.
$context['sandbox']['migration_ids'][] = $migration_id;
// Increase the number of migrations in the batch to update the
// progress bar and keep it accurate.
$context['sandbox']['max']++;
// Unset the follow-up migration to make sure it won't get added
// to the batch twice.
unset(static::$followUpMigrations[$migration_id]);
}
}
}
break;
case MigrationInterface::RESULT_INCOMPLETE:
@ -225,24 +257,24 @@ class MigrateUpgradeImportBatch {
// If we had any successes log that for the user.
if ($successes > 0) {
drupal_set_message(\Drupal::translation()
\Drupal::messenger()->addStatus(\Drupal::translation()
->formatPlural($successes, 'Completed 1 upgrade task successfully', 'Completed @count upgrade tasks successfully'));
}
// If we had failures, log them and show the migration failed.
if ($failures > 0) {
drupal_set_message(\Drupal::translation()
\Drupal::messenger()->addError(\Drupal::translation()
->formatPlural($failures, '1 upgrade failed', '@count upgrades failed'));
drupal_set_message(t('Upgrade process not completed'), 'error');
\Drupal::messenger()->addError(t('Upgrade process not completed'));
}
else {
// Everything went off without a hitch. We may not have had successes
// but we didn't have failures so this is fine.
drupal_set_message(t('Congratulations, you upgraded Drupal!'));
\Drupal::messenger()->addStatus(t('Congratulations, you upgraded Drupal!'));
}
if (\Drupal::moduleHandler()->moduleExists('dblog')) {
$url = Url::fromRoute('migrate_drupal_ui.log');
drupal_set_message(Link::fromTextAndUrl(new TranslatableMarkup('Review the detailed upgrade log'), $url), $failures ? 'error' : 'status');
\Drupal::messenger()->addMessage(Link::fromTextAndUrl(new TranslatableMarkup('Review the detailed upgrade log'), $url), $failures ? 'error' : 'status');
}
}
@ -259,6 +291,24 @@ class MigrateUpgradeImportBatch {
}
}
/**
* Adds follow-up migrations.
*
* @param \Drupal\migrate\Event\MigrateImportEvent $event
* The import event.
*/
public static function onPostImport(MigrateImportEvent $event) {
$migration = $event->getMigration();
if ($migration instanceof MigrationWithFollowUpInterface) {
// After the migration on which they depend has been successfully
// executed, the follow-up migrations are immediately added to the batch
// and removed from the $followUpMigrations property. This means that the
// $followUpMigrations property is always empty at this point and it's OK
// to override it with the next follow-up migrations.
static::$followUpMigrations = $migration->generateFollowUpMigrations();
}
}
/**
* Reacts to item deletion.
*

View file

@ -0,0 +1,310 @@
<?php
namespace Drupal\migrate_drupal_ui\Form;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\TransferException;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Migrate Upgrade database credential form.
*
* @internal
*/
class CredentialForm extends MigrateUpgradeFormBase {
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* The HTTP client to fetch the files with.
*
* @var \GuzzleHttp\ClientInterface
*/
protected $httpClient;
/**
* An array of error information.
*
* @var array
*/
protected $errors = [];
/**
* CredentialForm constructor.
*
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_private
* The private tempstore factory.
* @param \GuzzleHttp\ClientInterface $http_client
* A Guzzle client object.
*/
public function __construct(RendererInterface $renderer, PrivateTempStoreFactory $tempstore_private, ClientInterface $http_client) {
parent::__construct($tempstore_private);
$this->renderer = $renderer;
$this->httpClient = $http_client;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('renderer'),
$container->get('tempstore.private'),
$container->get('http_client')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'migrate_drupal_ui_credential_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
if ($this->store->get('step') != 'credential') {
return $this->restartUpgradeForm();
}
$form = parent::buildForm($form, $form_state);
$form['actions']['submit']['#value'] = $this->t('Review upgrade');
$form['#title'] = $this->t('Drupal Upgrade');
$drivers = $this->getDatabaseTypes();
$drivers_keys = array_keys($drivers);
// @todo https://www.drupal.org/node/2678510 Because this is a multi-step
// form, the form is not rebuilt during submission. Ideally we would get
// the chosen driver from form input, if available, in order to use
// #limit_validation_errors in the same way
// \Drupal\Core\Installer\Form\SiteSettingsForm does.
$default_driver = current($drivers_keys);
$default_options = [];
$form['help'] = [
'#type' => 'item',
'#description' => $this->t('Provide the information to access the Drupal site you want to upgrade. Files can be imported into the upgraded site as well. See the <a href=":url">Upgrade documentation for more detailed instructions</a>.', [':url' => 'https://www.drupal.org/upgrade/migrate']),
];
$form['version'] = [
'#type' => 'radios',
'#default_value' => 7,
'#title' => $this->t('Drupal version of the source site'),
'#options' => ['6' => $this->t('Drupal 6'), '7' => $this->t('Drupal 7')],
'#required' => TRUE,
];
$form['database'] = [
'#type' => 'details',
'#title' => $this->t('Source database'),
'#description' => $this->t('Provide credentials for the database of the Drupal site you want to upgrade.'),
'#open' => TRUE,
];
$form['database']['driver'] = [
'#type' => 'radios',
'#title' => $this->t('Database type'),
'#required' => TRUE,
'#default_value' => $default_driver,
];
if (count($drivers) == 1) {
$form['database']['driver']['#disabled'] = TRUE;
}
// Add driver-specific configuration options.
foreach ($drivers as $key => $driver) {
$form['database']['driver']['#options'][$key] = $driver->name();
$form['database']['settings'][$key] = $driver->getFormOptions($default_options);
// @todo https://www.drupal.org/node/2678510 Using
// #limit_validation_errors in the submit does not work so it is not
// possible to require the database and username for mysql and pgsql.
// This is because this is a multi-step form.
$form['database']['settings'][$key]['database']['#required'] = FALSE;
$form['database']['settings'][$key]['username']['#required'] = FALSE;
$form['database']['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . $this->t('@driver_name settings', ['@driver_name' => $driver->name()]) . '</h2>';
$form['database']['settings'][$key]['#type'] = 'container';
$form['database']['settings'][$key]['#tree'] = TRUE;
$form['database']['settings'][$key]['advanced_options']['#parents'] = [$key];
$form['database']['settings'][$key]['#states'] = [
'visible' => [
':input[name=driver]' => ['value' => $key],
],
];
// Move the host fields out of advanced settings.
if (isset($form['database']['settings'][$key]['advanced_options']['host'])) {
$form['database']['settings'][$key]['host'] = $form['database']['settings'][$key]['advanced_options']['host'];
$form['database']['settings'][$key]['host']['#title'] = 'Database host';
$form['database']['settings'][$key]['host']['#weight'] = -1;
unset($form['database']['settings'][$key]['database']['#default_value']);
unset($form['database']['settings'][$key]['advanced_options']['host']);
}
}
$form['source'] = [
'#type' => 'details',
'#title' => $this->t('Source files'),
'#open' => TRUE,
];
$form['source']['d6_source_base_path'] = [
'#type' => 'textfield',
'#title' => $this->t('Files directory'),
'#description' => $this->t('To import files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot), or your site address (for example http://example.com).'),
'#states' => [
'visible' => [
':input[name="version"]' => ['value' => '6'],
],
],
'#element_validate' => ['::validatePaths'],
];
$form['source']['source_base_path'] = [
'#type' => 'textfield',
'#title' => $this->t('Public files directory'),
'#description' => $this->t('To import public files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot), or your site address (for example http://example.com).'),
'#states' => [
'visible' => [
':input[name="version"]' => ['value' => '7'],
],
],
'#element_validate' => ['::validatePaths'],
];
$form['source']['source_private_file_path'] = [
'#type' => 'textfield',
'#title' => $this->t('Private files directory'),
'#default_value' => '',
'#description' => $this->t('To import private files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot).'),
'#states' => [
'visible' => [
':input[name="version"]' => ['value' => '7'],
],
],
'#element_validate' => ['::validatePaths'],
];
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) {
$this->errors[$name] = $message;
}
}
else {
try {
$connection = $this->getConnection($database);
$version = (string) $this->getLegacyDrupalVersion($connection);
if (!$version) {
$this->errors[$database['driver'] . '][database'] = $this->t('Source database does not contain a recognizable Drupal version.');
}
elseif ($version !== (string) $form_state->getValue('version')) {
$this->errors['version'] = $this->t('Source database is Drupal version @version but version @selected was selected.',
[
'@version' => $version,
'@selected' => $form_state->getValue('version'),
]);
}
else {
// Setup migrations and save form data to private store.
$this->setupMigrations($database, $form_state);
}
}
catch (\Exception $e) {
$this->errors[$database['driver'] . '][database'] = $e->getMessage();
}
}
// Display all errors as a list of items.
if ($this->errors) {
$form_state->setError($form, $this->t('<h3>Resolve all issues below to continue the upgrade.</h3>'));
foreach ($this->errors as $name => $message) {
$form_state->setErrorByName($name, $message);
}
}
}
/**
* The #element_validate handler for the source path elements.
*
* Ensures that entered path can be read.
*/
public function validatePaths($element, FormStateInterface $form_state) {
if ($source = $element['#value']) {
$msg = $this->t('Unable to read from @title.', ['@title' => $element['#title']]);
if (UrlHelper::isExternal($source)) {
try {
$this->httpClient->head($source);
}
catch (TransferException $e) {
$this->errors[$element['#name']] = $msg . ' ' . $e->getMessage();
}
}
elseif (!file_exists($source) || (!is_dir($source)) || (!is_readable($source))) {
$this->errors[$element['#name']] = $msg;
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('step', 'idconflict');
$form_state->setRedirect('migrate_drupal_ui.upgrade_idconflict');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Review upgrade');
}
/**
* Returns all supported database driver installer objects.
*
* @return \Drupal\Core\Database\Install\Tasks[]
* An array of available database driver installer objects.
*/
protected function getDatabaseTypes() {
// Make sure the install API is available.
include_once DRUPAL_ROOT . '/core/includes/install.inc';
return drupal_get_database_types();
}
}

View file

@ -0,0 +1,203 @@
<?php
namespace Drupal\migrate_drupal_ui\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\migrate\Audit\IdAuditor;
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Migrate Upgrade Id Conflict form.
*
* @internal
*/
class IdConflictForm extends MigrateUpgradeFormBase {
/**
* The migration plugin manager service.
*
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
*/
protected $pluginManager;
/**
* IdConflictForm constructor.
*
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
* The migration plugin manager service.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_private
* The private tempstore factory.
*/
public function __construct(MigrationPluginManagerInterface $migration_plugin_manager, PrivateTempStoreFactory $tempstore_private) {
parent::__construct($tempstore_private);
$this->pluginManager = $migration_plugin_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.migration'),
$container->get('tempstore.private')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'migrate_drupal_ui_idconflict_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Get all the data needed for this form.
$migrations = $this->store->get('migrations');
// If data is missing or this is the wrong step, start over.
if (!$migrations || ($this->store->get('step') != 'idconflict')) {
return $this->restartUpgradeForm();
}
$migration_ids = array_keys($migrations);
// Check if there are conflicts. If none, just skip this form!
$migrations = $this->pluginManager->createInstances($migration_ids);
$translated_content_conflicts = $content_conflicts = [];
$results = (new IdAuditor())->auditMultiple($migrations);
/** @var \Drupal\migrate\Audit\AuditResult $result */
foreach ($results as $result) {
$destination = $result->getMigration()->getDestinationPlugin();
if ($destination instanceof EntityContentBase && $destination->isTranslationDestination()) {
// Translations are not yet supported by the audit system. For now, we
// only warn the user to be cautious when migrating translated content.
// I18n support should be added in https://www.drupal.org/node/2905759.
$translated_content_conflicts[] = $result;
}
elseif (!$result->passed()) {
$content_conflicts[] = $result;
}
}
if ($content_conflicts || $translated_content_conflicts) {
$this->messenger()->addWarning($this->t('WARNING: Content may be overwritten on your new site.'));
$form = parent::buildForm($form, $form_state);
$form['#title'] = $this->t('Upgrade analysis report');
if ($content_conflicts) {
$form = $this->conflictsForm($form, $content_conflicts);
}
if ($translated_content_conflicts) {
$form = $this->i18nWarningForm($form, $translated_content_conflicts);
}
return $form;
}
else {
$this->store->set('step', 'review');
return $this->redirect('migrate_drupal_ui.upgrade_review');
}
}
/**
* Build the markup for conflict warnings.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\migrate\Audit\AuditResult[] $conflicts
* The failing audit results.
*
* @return array
* The form structure.
*/
protected function conflictsForm(array &$form, array $conflicts) {
$form['conflicts'] = [
'#title' => $this->t('There is conflicting content of these types:'),
'#theme' => 'item_list',
'#items' => $this->formatConflicts($conflicts),
];
$form['warning'] = [
'#type' => 'markup',
'#markup' => '<p>' . $this->t('It looks like you have content on your new site which <strong>may be overwritten</strong> if you continue to run this upgrade. The upgrade should be performed on a clean Drupal 8 installation. For more information see the <a target="_blank" href=":id-conflicts-handbook">upgrade handbook</a>.', [':id-conflicts-handbook' => 'https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#id_conflicts']) . '</p>',
];
return $form;
}
/**
* Formats a set of failing audit results as strings.
*
* Each string is the label of the destination plugin of the migration that
* failed the audit, keyed by the destination plugin ID in order to prevent
* duplication.
*
* @param \Drupal\migrate\Audit\AuditResult[] $conflicts
* The failing audit results.
*
* @return string[]
* The formatted audit results.
*/
protected function formatConflicts(array $conflicts) {
$items = [];
foreach ($conflicts as $conflict) {
$definition = $conflict->getMigration()->getDestinationPlugin()->getPluginDefinition();
$id = $definition['id'];
$items[$id] = $definition['label'];
}
sort($items, SORT_STRING);
return $items;
}
/**
* Build the markup for i18n warnings.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\migrate\Audit\AuditResult[] $conflicts
* The failing audit results.
*
* @return array
* The form structure.
*/
protected function i18nWarningForm(array &$form, array $conflicts) {
$form['i18n'] = [
'#title' => $this->t('There is translated content of these types:'),
'#theme' => 'item_list',
'#items' => $this->formatConflicts($conflicts),
];
$form['i18n_warning'] = [
'#type' => 'markup',
'#markup' => '<p>' . $this->t('It looks like you are migrating translated content from your old site. Possible ID conflicts for translations are not automatically detected in the current version of Drupal. Refer to the <a target="_blank" href=":id-conflicts-handbook">upgrade handbook</a> for instructions on how to avoid ID conflicts with translated content.', [':id-conflicts-handbook' => 'https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#id_conflicts']) . '</p>',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('step', 'review');
$form_state->setRedirect('migrate_drupal_ui.upgrade_review');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('I acknowledge I may lose data. Continue anyway.');
}
}

View file

@ -0,0 +1,130 @@
<?php
namespace Drupal\migrate_drupal_ui\Form;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Migrate Upgrade Incremental form.
*
* @internal
*/
class IncrementalForm extends MigrateUpgradeFormBase {
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* IncrementalForm constructor.
*
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_private
* The private temp store factory.
*/
public function __construct(StateInterface $state, DateFormatterInterface $date_formatter, PrivateTempStoreFactory $tempstore_private) {
parent::__construct($tempstore_private);
$this->state = $state;
$this->dateFormatter = $date_formatter;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('state'),
$container->get('date.formatter'),
$container->get('tempstore.private')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'migrate_drupal_ui_incremental_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Get all the data needed for this form.
$date_performed = $this->state->get('migrate_drupal_ui.performed');
// If data is missing or this is the wrong step, start over.
if (!$date_performed || $this->store->get('step') != 'incremental') {
return $this->restartUpgradeForm();
}
$form = parent::buildForm($form, $form_state);
$form['#title'] = $this->t('Upgrade');
// @todo Add back support for rollbacks.
// https://www.drupal.org/node/2687849
$form['upgrade_option_item'] = [
'#type' => 'item',
'#prefix' => $this->t('An upgrade has already been performed on this site. To perform a new migration, create a clean and empty new install of Drupal 8. Rollbacks are not yet supported through the user interface. For more information, see the <a href=":url">upgrading handbook</a>.', [':url' => 'https://www.drupal.org/upgrade/migrate']),
'#description' => $this->t('Last upgrade: @date', ['@date' => $this->dateFormatter->format($date_performed)]),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Retrieve the database driver from state.
$database_state_key = $this->state->get('migrate.fallback_state_key', '');
if ($database_state_key) {
try {
$database = $this->state->get($database_state_key, [])['database'];
if ($connection = $this->getConnection($database)) {
if ($version = $this->getLegacyDrupalVersion($connection)) {
$this->setupMigrations($database, $form_state);
$valid_legacy_database = TRUE;
}
}
}
catch (DatabaseExceptionWrapper $exception) {
// Hide DB exceptions and forward to the DB credentials form. In that
// form we can more properly display errors and accept new credentials.
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('step', 'credential');
$form_state->setRedirect('migrate_drupal_ui.upgrade_credential');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Import new configuration and content from old site');
}
}

View file

@ -0,0 +1,120 @@
<?php
namespace Drupal\migrate_drupal_ui\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form base for the Migrate Upgrade UI.
*/
abstract class MigrateUpgradeFormBase extends FormBase {
use MigrationConfigurationTrait;
/**
* Private temporary storage.
*
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $store;
/**
* Constructs the Migrate Upgrade Form Base.
*
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_private
* Private store.
*/
public function __construct(PrivateTempStoreFactory $tempstore_private) {
$this->store = $tempstore_private->get('migrate_drupal_ui');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('tempstore.private')
);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = [];
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->getConfirmText(),
'#button_type' => 'primary',
'#weight' => 10,
];
return $form;
}
/**
* Gets and stores information for this migration in temporary store.
*
* Gets all the migrations, converts each to an array and stores it in the
* form state. The source base path for public and private files is also
* put into form state.
*
* @param array $database
* Database array representing the source Drupal database.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
protected function setupMigrations(array $database, FormStateInterface $form_state) {
$connection = $this->getConnection($database);
$version = $this->getLegacyDrupalVersion($connection);
$this->createDatabaseStateSettings($database, $version);
$migrations = $this->getMigrations('migrate_drupal_' . $version, $version);
// Get the system data from source database.
$system_data = $this->getSystemData($connection);
// Convert the migration object into array
// so that it can be stored in form storage.
$migration_array = [];
foreach ($migrations as $migration) {
$migration_array[$migration->id()] = $migration->label();
}
// Store information in the private store.
$this->store->set('version', $version);
$this->store->set('migrations', $migration_array);
if ($version == 6) {
$this->store->set('source_base_path', $form_state->getValue('d6_source_base_path'));
}
else {
$this->store->set('source_base_path', $form_state->getValue('source_base_path'));
}
$this->store->set('source_private_file_path', $form_state->getValue('source_private_file_path'));
// Store the retrieved system data in the private store.
$this->store->set('system_data', $system_data);
}
/**
* Helper to redirect to the Overview form.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirect response object that may be returned by the controller.
*/
protected function restartUpgradeForm() {
$this->store->set('step', 'overview');
return $this->redirect('migrate_drupal_ui.upgrade');
}
/**
* Returns a caption for the button that confirms the action.
*
* @return string
* The form confirmation text.
*/
abstract protected function getConfirmText();
}

View file

@ -0,0 +1,124 @@
<?php
namespace Drupal\migrate_drupal_ui\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Migrate Upgrade Overview form.
*
* @internal
*/
class OverviewForm extends MigrateUpgradeFormBase {
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Overview form constructor.
*
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_private
* The private tempstore factory.
*/
public function __construct(StateInterface $state, PrivateTempStoreFactory $tempstore_private) {
parent::__construct($tempstore_private);
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('state'),
$container->get('tempstore.private')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'migrate_drupal_ui_overview_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// If an upgrade has already been performed, redirect to the incremental
// form.
if ($this->state->get('migrate_drupal_ui.performed')) {
$this->store->set('step', 'incremental');
return $this->redirect('migrate_drupal_ui.upgrade_incremental');
}
$form = parent::buildForm($form, $form_state);
$form['#title'] = $this->t('Upgrade');
$form['info_header'] = [
'#markup' => '<p>' . $this->t('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8. See the <a href=":url">Drupal site upgrades handbook</a> for more information.', [
':url' => 'https://www.drupal.org/upgrade/migrate',
]),
];
$form['legend']['#markup'] = '';
$form['legend']['#markup'] .= '<h3>' . $this->t('Definitions') . '</h3>';
$form['legend']['#markup'] .= '<dl>';
$form['legend']['#markup'] .= '<dt>' . $this->t('Old site') . '</dt>';
$form['legend']['#markup'] .= '<dd>' . $this->t('The site you want to upgrade.') . '</dd>';
$form['legend']['#markup'] .= '<dt>' . $this->t('New site') . '</dt>';
$form['legend']['#markup'] .= '<dd>' . $this->t('This empty Drupal 8 installation you will import the old site to.') . '</dd>';
$form['legend']['#markup'] .= '</dl>';
$info[] = $this->t('Make sure that <strong>access to the database</strong> for the old site is available from this new site.');
$info[] = $this->t('<strong>If the old site has private files</strong>, a copy of its files directory must also be accessible on the host of this new site.');
$info[] = $this->t('<strong>Enable all modules on this new site</strong> that are enabled on the old site. For example, if the old site uses the book module, then enable the book module on this new site so that the existing data can be imported to it.');
$info[] = $this->t('<strong>Do not add any content to the new site</strong> before upgrading. Any existing content is likely to be overwritten by the upgrade process. See <a href=":url">the upgrade preparation guide</a>.', [
':url' => 'https://www.drupal.org/docs/8/upgrade/preparing-an-upgrade#dont_create_content',
]);
$info[] = $this->t('Put this site into <a href=":url">maintenance mode</a>.', [
':url' => Url::fromRoute('system.site_maintenance_mode')
->toString(TRUE)
->getGeneratedUrl(),
]);
$form['info'] = [
'#theme' => 'item_list',
'#title' => $this->t('Preparation steps'),
'#list_type' => 'ol',
'#items' => $info,
];
$form['info_footer'] = [
'#markup' => '<p>' . $this->t('The upgrade can take a long time. It is better to upgrade from a local copy of your site instead of directly from your live site.'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->store->set('step', 'credential');
$form_state->setRedirect('migrate_drupal_ui.upgrade_credential');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Continue');
}
}

View file

@ -0,0 +1,405 @@
<?php
namespace Drupal\migrate_drupal_ui\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Drupal\migrate_drupal_ui\Batch\MigrateUpgradeImportBatch;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Migrate Upgrade review form.
*
* This confirmation form uses the source_module and destination_module
* properties on the source, destination and field plugins as well as the
* system data from the source to determine if there is a migration path for
* each module in the source.
*
* @internal
*/
class ReviewForm extends MigrateUpgradeFormBase {
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The migration plugin manager service.
*
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
*/
protected $pluginManager;
/**
* The field plugin manager service.
*
* @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface
*/
protected $fieldPluginManager;
/**
* The migrations.
*
* @var \Drupal\migrate\Plugin\MigrationInterface[]
*/
protected $migrations;
/**
* List of extensions that do not need an upgrade path.
*
* This property is an array where the keys are the major Drupal core version
* from which we are upgrading, and the values are arrays of extension names
* that do not need an upgrade path.
*
* @var array[]
*/
protected $noUpgradePaths = [
'6' => [
'blog',
'blogapi',
'calendarsignup',
'color',
'content_copy',
'content_multigroup',
'content_permissions',
'date_api',
'date_locale',
'date_php4',
'date_popup',
'date_repeat',
'date_timezone',
'date_tools',
'datepicker',
'ddblock',
'event',
'fieldgroup',
'filefield_meta',
'help',
'i18nstrings',
'imageapi',
'imageapi_gd',
'imageapi_imagemagick',
'imagecache_ui',
'jquery_ui',
'nodeaccess',
'number',
'openid',
'php',
'ping',
'poll',
'throttle',
'tracker',
'translation',
'trigger',
'variable',
'variable_admin',
'views_export',
'views_ui',
],
'7' => [
'blog',
'bulk_export',
'contextual',
'ctools',
'ctools_access_ruleset',
'ctools_ajax_sample',
'ctools_custom_content',
'dashboard',
'date_all_day',
'date_api',
'date_context',
'date_migrate',
'date_popup',
'date_repeat',
'date_repeat_field',
'date_tools',
'date_views',
'entity',
'entity_feature',
'entity_token',
'entityreference',
'field_ui',
'help',
'openid',
'overlay',
'page_manager',
'php',
'poll',
'search_embedded_form',
'search_extra_type',
'search_node_tags',
'simpletest',
'stylizer',
'term_depth',
'title',
'toolbar',
'translation',
'trigger',
'views_content',
'views_ui',
],
];
/**
* ReviewForm constructor.
*
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
* The migration plugin manager service.
* @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_plugin_manager
* The field plugin manager service.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_private
* The private tempstore factory.
*/
public function __construct(StateInterface $state, MigrationPluginManagerInterface $migration_plugin_manager, MigrateFieldPluginManagerInterface $field_plugin_manager, PrivateTempStoreFactory $tempstore_private) {
parent::__construct($tempstore_private);
$this->state = $state;
$this->pluginManager = $migration_plugin_manager;
$this->fieldPluginManager = $field_plugin_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('state'),
$container->get('plugin.manager.migration'),
$container->get('plugin.manager.migrate.field'),
$container->get('tempstore.private')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'migrate_drupal_ui_review_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Get all the data needed for this form.
$version = $this->store->get('version');
$this->migrations = $this->store->get('migrations');
// Fetch the system data at the first opportunity.
$system_data = $this->store->get('system_data');
// If data is missing or this is the wrong step, start over.
if (!$version || !$this->migrations || !$system_data ||
($this->store->get('step') != 'review')) {
return $this->restartUpgradeForm();
}
$form = parent::buildForm($form, $form_state);
$form['#title'] = $this->t('What will be upgraded?');
// Get the source_module and destination_module for each migration.
$migrations = $this->pluginManager->createInstances(array_keys($this->store->get('migrations')));
$table_data = [];
foreach ($migrations as $migration) {
$migration_id = $migration->getPluginId();
$source_module = $migration->getSourcePlugin()->getSourceModule();
if (!$source_module) {
$this->messenger()->addError($this->t('Source module not found for @migration_id.', ['@migration_id' => $migration_id]));
}
$destination_module = $migration->getDestinationPlugin()->getDestinationModule();
if (!$destination_module) {
$this->messenger()->addError($this->t('Destination module not found for @migration_id.', ['@migration_id' => $migration_id]));
}
if ($source_module && $destination_module) {
$table_data[$source_module][$destination_module][$migration_id] = $migration->label();
}
}
// Get the source_module and destination_module from the field plugins.
$definitions = $this->fieldPluginManager->getDefinitions();
foreach ($definitions as $definition) {
// This is not strict so that we find field plugins with an annotation
// where the Drupal core version is an integer and when it is a string.
if (in_array($version, $definition['core'])) {
$source_module = $definition['source_module'];
$destination_module = $definition['destination_module'];
$table_data[$source_module][$destination_module][$definition['id']] = $definition['id'];
}
}
// Add source_module and destination_module for modules that do not need an
// upgrade path and are enabled on the source site.
foreach ($this->noUpgradePaths[$version] as $extension) {
if (isset($system_data['module'][$extension]) && $system_data['module'][$extension]['status']) {
$table_data[$extension]['core'][$extension] = $extension;
}
}
// Sort the table by source module names and within that destination
// module names.
ksort($table_data);
foreach ($table_data as $source_module => $destination_module_info) {
ksort($table_data[$source_module]);
}
// Remove core profiles from the system data.
foreach (['standard', 'minimal'] as $profile) {
unset($system_data['module'][$profile]);
}
$unmigrated_source_modules = array_diff_key($system_data['module'], $table_data);
// Missing migrations.
$missing_module_list = [
'#type' => 'details',
'#open' => TRUE,
'#title' => $this->t('Modules that will not be upgraded'),
'#summary_attributes' => ['id' => ['error']],
'#description' => $this->t('There are no modules installed on your new site to replace these modules. If you proceed with the upgrade now, configuration and/or content needed by these modules will not be available on your new site. For more information, see <a href=":review">Review the pre-upgrade analysis</a> in the <a href=":migrate">Upgrading to Drupal 8</a> handbook.', [':review' => 'https://www.drupal.org/docs/8/upgrade/upgrade-using-web-browser#pre-upgrade-analysis', ':migrate' => 'https://www.drupal.org/docs/8/upgrade']),
'#weight' => 2,
];
$missing_module_list['module_list'] = [
'#type' => 'table',
'#header' => [
$this->t('Drupal @version', ['@version' => $version]),
$this->t('Drupal 8'),
],
];
$missing_count = 0;
ksort($unmigrated_source_modules);
foreach ($unmigrated_source_modules as $source_module => $module_data) {
if ($module_data['status']) {
$missing_count++;
$missing_module_list['module_list'][$source_module] = [
'source_module' => [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => $source_module,
'#attributes' => [
'class' => [
'upgrade-analysis-report__status-icon',
'upgrade-analysis-report__status-icon--error',
],
],
],
'destination_module' => ['#plain_text' => 'Not upgraded'],
];
}
}
// Available migrations.
$available_module_list = [
'#type' => 'details',
'#title' => $this->t('Modules that will be upgraded'),
'#summary_attributes' => ['id' => ['checked']],
'#weight' => 3,
];
$available_module_list['module_list'] = [
'#type' => 'table',
'#header' => [
$this->t('Drupal @version', ['@version' => $version]),
$this->t('Drupal 8'),
],
];
$available_count = 0;
foreach ($table_data as $source_module => $destination_module_info) {
$available_count++;
$destination_details = [];
foreach ($destination_module_info as $destination_module => $migration_ids) {
$destination_details[$destination_module] = [
'#type' => 'item',
'#plain_text' => $destination_module,
];
}
$available_module_list['module_list'][$source_module] = [
'source_module' => [
'#type' => 'html_tag',
'#tag' => 'span',
'#value' => $source_module,
'#attributes' => [
'class' => [
'upgrade-analysis-report__status-icon',
'upgrade-analysis-report__status-icon--checked',
],
],
],
'destination_module' => $destination_details,
];
}
$counters = [];
$general_info = [];
if ($missing_count) {
$counters[] = [
'#theme' => 'status_report_counter',
'#amount' => $missing_count,
'#text' => $this->formatPlural($missing_count, 'Module will not be upgraded', 'Modules will not be upgraded'),
'#severity' => 'error',
'#weight' => 0,
];
$general_info[] = $missing_module_list;
}
if ($available_count) {
$counters[] = [
'#theme' => 'status_report_counter',
'#amount' => $available_count,
'#text' => $this->formatPlural($available_count, 'Module will be upgraded', 'Modules will be upgraded'),
'#severity' => 'checked',
'#weight' => 1,
];
$general_info[] = $available_module_list;
}
$form['status_report_page'] = [
'#theme' => 'status_report_page',
'#counters' => $counters,
'#general_info' => $general_info,
];
$form['#attached']['library'][] = 'migrate_drupal_ui/base';
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$config['source_base_path'] = $this->store->get('source_base_path');
$batch = [
'title' => $this->t('Running upgrade'),
'progress_message' => '',
'operations' => [
[
[MigrateUpgradeImportBatch::class, 'run'],
[array_keys($this->migrations), $config],
],
],
'finished' => [
MigrateUpgradeImportBatch::class, 'finished',
],
];
batch_set($batch);
$form_state->setRedirect('<front>');
$this->store->set('step', 'overview');
$this->state->set('migrate_drupal_ui.performed', REQUEST_TIME);
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Perform upgrade');
}
}

View file

@ -2,6 +2,8 @@
namespace Drupal\migrate_drupal_ui\Tests;
@trigger_error('\Drupal\migrate_drupal_ui\Tests\MigrateUpgradeTestBase is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Use \Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeTestBase instead.', E_USER_DEPRECATED);
use Drupal\Core\Database\Database;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
@ -9,12 +11,17 @@ use Drupal\simpletest\WebTestBase;
/**
* Provides a base class for testing migration upgrades in the UI.
*
* @deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Use
* \Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeTestBase instead.
*/
abstract class MigrateUpgradeTestBase extends WebTestBase {
use MigrationConfigurationTrait;
/**
* Use the Standard profile to test help implementations of many core modules.
*
* @var string
*/
protected $profile = 'standard';
@ -39,6 +46,7 @@ abstract class MigrateUpgradeTestBase extends WebTestBase {
'book',
'forum',
'statistics',
'modules_available_test',
];
/**
@ -113,7 +121,7 @@ abstract class MigrateUpgradeTestBase extends WebTestBase {
public function testMigrateUpgrade() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$this->drupalGet('/upgrade');
$this->assertText('Upgrade a site by importing it into a clean and empty new install of Drupal 8. You will lose any existing configuration once you import your site into it. See the online documentation for Drupal site upgrades for more detailed information.');
$this->assertText('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$this->assertText('Provide credentials for the database of the Drupal site you want to upgrade.');
@ -143,12 +151,36 @@ abstract class MigrateUpgradeTestBase extends WebTestBase {
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$this->assertResponse(200);
$this->assertText('Are you sure?');
$this->assertText('Upgrade analysis report');
// Ensure we get errors about missing modules.
$this->assertText(t('Source module not found for module_no_annotation.'));
$this->assertText(t('Source module not found for modules_available_test.'));
$this->assertText(t('Destination module not found for modules_available_test'));
// Uninstall the module causing the missing module error messages.
$this->container->get('module_installer')->uninstall(['modules_available_test'], TRUE);
// Restart the upgrade process.
$this->drupalGet('/upgrade');
$this->assertText('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$this->assertText('Provide credentials for the database of the Drupal site you want to upgrade.');
$this->assertFieldByName('mysql[host]');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$this->assertResponse(200);
$this->assertText('Upgrade analysis report');
// Ensure there are no errors about the missing modules.
$this->assertNoText(t('Source module not found for module_no_annotation.'));
$this->assertNoText(t('Source module not found for modules_available_test.'));
$this->assertNoText(t('Destination module not found for modules_available_test'));
// Check for any missing module errors.
$this->drupalPostForm(NULL, [], t('Perform upgrade'));
$this->assertText(t('Congratulations, you upgraded Drupal!'));
// Have to reset all the statics after migration to ensure entities are
// loadable.
// Have to reset all the static caches after migration to ensure entities
// are loadable.
$this->resetAll();
$expected_counts = $this->getEntityCounts();

View file

@ -1,90 +0,0 @@
<?php
namespace Drupal\migrate_drupal_ui\Tests\d6;
use Drupal\migrate_drupal_ui\Tests\MigrateUpgradeTestBase;
use Drupal\user\Entity\User;
/**
* Tests Drupal 6 upgrade using the migrate UI.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group migrate_drupal_ui
*/
class MigrateUpgrade6Test extends MigrateUpgradeTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal6.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'aggregator_item' => 1,
'aggregator_feed' => 1,
'block' => 35,
'block_content' => 2,
'block_content_type' => 1,
'comment' => 3,
'comment_type' => 3,
'contact_form' => 5,
'configurable_language' => 5,
'editor' => 2,
'field_config' => 73,
'field_storage_config' => 48,
'file' => 7,
'filter_format' => 7,
'image_style' => 5,
'language_content_settings' => 2,
'migration' => 105,
'node' => 16,
'node_type' => 13,
'rdf_mapping' => 7,
'search_page' => 2,
'shortcut' => 2,
'shortcut_set' => 1,
'action' => 22,
'menu' => 8,
'taxonomy_term' => 6,
'taxonomy_vocabulary' => 6,
'tour' => 4,
'user' => 7,
'user_role' => 6,
'menu_link_content' => 4,
'view' => 14,
'date_format' => 11,
'entity_form_display' => 19,
'entity_form_mode' => 1,
'entity_view_display' => 43,
'entity_view_mode' => 14,
'base_field_override' => 38,
];
}
/**
* Executes all steps of migrations upgrade.
*/
public function testMigrateUpgrade() {
parent::testMigrateUpgrade();
// Ensure migrated users can log in.
$user = User::load(2);
$user->pass_raw = 'john.doe_pass';
$this->drupalLogin($user);
}
}

View file

@ -1,91 +0,0 @@
<?php
namespace Drupal\migrate_drupal_ui\Tests\d7;
use Drupal\migrate_drupal_ui\Tests\MigrateUpgradeTestBase;
use Drupal\user\Entity\User;
/**
* Tests Drupal 7 upgrade using the migrate UI.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group migrate_drupal_ui
*/
class MigrateUpgrade7Test extends MigrateUpgradeTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal7.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'aggregator_item' => 10,
'aggregator_feed' => 1,
'block' => 25,
'block_content' => 1,
'block_content_type' => 1,
'comment' => 1,
'comment_type' => 8,
// Module 'language' comes with 'en', 'und', 'zxx'. Migration adds 'is'.
'configurable_language' => 4,
'contact_form' => 3,
'editor' => 2,
'field_config' => 52,
'field_storage_config' => 39,
'file' => 2,
'filter_format' => 7,
'image_style' => 6,
'language_content_settings' => 2,
'migration' => 73,
'node' => 3,
'node_type' => 6,
'rdf_mapping' => 7,
'search_page' => 2,
'shortcut' => 6,
'shortcut_set' => 2,
'action' => 16,
'menu' => 6,
'taxonomy_term' => 18,
'taxonomy_vocabulary' => 4,
'tour' => 4,
'user' => 4,
'user_role' => 3,
'menu_link_content' => 7,
'view' => 14,
'date_format' => 11,
'entity_form_display' => 18,
'entity_form_mode' => 1,
'entity_view_display' => 29,
'entity_view_mode' => 14,
'base_field_override' => 9,
];
}
/**
* Executes all steps of migrations upgrade.
*/
public function testMigrateUpgrade() {
parent::testMigrateUpgrade();
// Ensure migrated users can log in.
$user = User::load(2);
$user->pass_raw = 'a password';
$this->drupalLogin($user);
}
}

View file

@ -0,0 +1,6 @@
name: 'Migration provider missing'
type: module
description: 'Add a migration missing a source and destination migration provider and a source plugin missing a migration provider.'
package: Testing
version: VERSION
core: 8.x

View file

@ -0,0 +1,13 @@
id: migration_provider_no_annotation
label: Missing source annotation
migration_tags:
- Drupal 6
- Drupal 7
source:
# Test plugin without a source_module annotation
plugin: no_source_module
process:
message: site_offline_message
destination:
plugin: config
config_name: test.settings

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\migration_provider_test\Plugin\migrate\source;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
/**
* A test source plugin without a source_module.
*
* @MigrateSource(
* id = "no_source_module",
* )
*/
class NoSourceModule extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function query() {}
/**
* {@inheritdoc}
*/
public function fields() {}
/**
* {@inheritdoc}
*/
public function getIds() {}
}

View file

@ -1,15 +1,15 @@
<?php
namespace Drupal\migrate_drupal_ui\Tests;
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\simpletest\WebTestBase;
use Drupal\Tests\BrowserTestBase;
/**
* Tests that only user 1 can access the migrate UI.
*
* @group migrate_drupal_ui
*/
class MigrateAccessTest extends WebTestBase {
class MigrateAccessTest extends BrowserTestBase {
/**
* Modules to enable.

View file

@ -0,0 +1,160 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
/**
* Provides a base class for testing a complete upgrade via the UI.
*/
abstract class MigrateUpgradeExecuteTestBase extends MigrateUpgradeTestBase {
use CreateTestContentEntitiesTrait;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create content.
$this->createContent();
}
/**
* Executes all steps of migrations upgrade.
*
* The upgrade is started three times. The first time is to test that
* providing incorrect database credentials fails as expected. The second
* time is to run the migration and assert the results. The third time is
* to test an incremental migration, by installing the aggregator module,
* and assert the results.
*/
public function testMigrateUpgradeExecute() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$this->drupalGet('/upgrade');
$session = $this->assertSession();
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$session->fieldExists('mysql[host]');
$driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default'];
// Use the driver connection form to get the correct options out of the
// database settings. This supports all of the databases we test against.
$drivers = drupal_get_database_types();
$form = $drivers[$driver]->getFormOptions($connection_options);
$connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
$version = $this->getLegacyDrupalVersion($this->sourceDatabase);
$edit = [
$driver => $connection_options,
'source_private_file_path' => $this->getSourceBasePath(),
'version' => $version,
];
if ($version == 6) {
$edit['d6_source_base_path'] = $this->getSourceBasePath();
}
else {
$edit['source_base_path'] = $this->getSourceBasePath();
}
if (count($drivers) !== 1) {
$edit['driver'] = $driver;
}
$edits = $this->translatePostValues($edit);
// Ensure submitting the form with invalid database credentials gives us a
// nice warning.
$this->drupalPostForm(NULL, [$driver . '[database]' => 'wrong'] + $edits, t('Review upgrade'));
$session->pageTextContains('Resolve all issues below to continue the upgrade.');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
// Ensure we get errors about missing modules.
$session->pageTextContains(t('Resolve all issues below to continue the upgrade.'));
$session->pageTextContains(t('The no_source_module plugin must define the source_module property.'));
// Uninstall the module causing the missing module error messages.
$this->container->get('module_installer')->uninstall(['migration_provider_test'], TRUE);
// Test the file sources.
$this->drupalGet('/upgrade');
$this->drupalPostForm(NULL, [], t('Continue'));
if ($version == 6) {
$paths['d6_source_base_path'] = DRUPAL_ROOT . '/wrong-path';
}
else {
$paths['source_base_path'] = 'https://example.com/wrong-path';
$paths['source_private_file_path'] = DRUPAL_ROOT . '/wrong-path';
}
$this->drupalPostForm(NULL, $paths + $edits, t('Review upgrade'));
if ($version == 6) {
$session->responseContains('Unable to read from Files directory.');
}
else {
$session->responseContains('Unable to read from Public files directory.');
$session->responseContains('Unable to read from Private files directory.');
}
// Restart the upgrade process.
$this->drupalGet('/upgrade');
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$session->fieldExists('mysql[host]');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$this->assertIdConflict($session);
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$session->statusCodeEquals(200);
// Ensure there are no errors about missing modules from the test module.
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
// Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found'));
// Test the upgrade paths.
$available_paths = $this->getAvailablePaths();
$missing_paths = $this->getMissingPaths();
$this->assertReviewPage($session, $available_paths, $missing_paths);
$this->drupalPostForm(NULL, [], t('Perform upgrade'));
$this->assertText(t('Congratulations, you upgraded Drupal!'));
$this->assertMigrationResults($this->getEntityCounts(), $version);
\Drupal::service('module_installer')->install(['forum']);
\Drupal::service('module_installer')->install(['book']);
// Test incremental migration.
$this->createContentPostUpgrade();
$this->drupalGet('/upgrade');
$session->pageTextContains('An upgrade has already been performed on this site. To perform a new migration, create a clean and empty new install of Drupal 8. Rollbacks are not yet supported through the user interface.');
$this->drupalPostForm(NULL, [], t('Import new configuration and content from old site'));
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$session->pageTextContains('WARNING: Content may be overwritten on your new site.');
$session->pageTextContains('There is conflicting content of these types:');
$session->pageTextContains('files');
$session->pageTextContains('content item revisions');
$session->pageTextContains('There is translated content of these types:');
$session->pageTextContains('content items');
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$session->statusCodeEquals(200);
// Need to update available and missing path lists.
$all_available = $this->getAvailablePaths();
$all_available[] = 'aggregator';
$all_missing = $this->getMissingPaths();
$all_missing = array_diff($all_missing, ['aggregator']);
$this->assertReviewPage($session, $all_available, $all_missing);
$this->drupalPostForm(NULL, [], t('Perform upgrade'));
$session->pageTextContains(t('Congratulations, you upgraded Drupal!'));
$this->assertMigrationResults($this->getEntityCountsIncremental(), $version);
}
}

View file

@ -0,0 +1,138 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\WebAssert;
/**
* Tests the flow of the Migrate Drupal UI form.
*
* @group migrate_drupal_ui
*/
class MigrateUpgradeFormStepsTest extends BrowserTestBase {
use MigrationConfigurationTrait;
use CreateTestContentEntitiesTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['migrate_drupal_ui'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Log in as user 1. Migrations in the UI can only be performed as user 1.
$this->drupalLogin($this->rootUser);
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* Tests the flow of the Migrate Drupal UI form.
*
* The Migrate Drupal UI uses several forms to guide you through the upgrade
* process. The forms displayed depend on if this is an incremental migration
* or if there are potential ID conflicts. The forms are to be displayed in
* this order; Overview or Incremental, if a migration has already been run
* then Credential, Id conflict, if conflicts are detected, and lastly Review.
*/
public function testMigrateUpgradeReviewPage() {
/** @var \Drupal\Core\TempStore\PrivateTempStore $store */
$store = \Drupal::service('tempstore.private')->get('migrate_drupal_ui');
$state = \Drupal::service('state');
// Test that when data required by a form is missing that the correct first
// form is displayed. The first form for an initial migration is the
// Overview form and for an incremental migration it is the Incremental
// form.
$session = $this->assertSession();
$expected['initial'] = 'Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.';
$expected['incremental'] = "An upgrade has already been performed on this site.";
foreach (['/upgrade', '/upgrade/incremental'] as $expected) {
if ($expected === '/upgrade/incremental') {
// Set a performed time to signify an incremental migration. The time
// value is a UNIX timestamp.
$state->set('migrate_drupal_ui.performed', 1);
}
// Test that an invalid step to any form goes to the correct first form.
$store->set('step', 'foo');
$this->assertFirstForm($session, $expected);
// Test that an undefined step to any form goes to the correct first form.
$store->delete('step');
$this->assertFirstForm($session, $expected);
// For forms that require data from the private store, test that when that
// data is missing the correct first page is displayed.
// The Id conflict form requires the migrations array.
$store->delete('migrations');
$store->set('step', 'idconflict');
$this->drupalGet('/upgrade/idconflict');
$session->addressEquals($expected);
// The Review form requires version, migrations and system_data. Test
// three times with only one of the variables missing.
$store->delete('version');
$store->set('migrations', ['foo', 'bar']);
$store->set('system_data', ['bar', 'foo']);
$store->set('step', 'review');
$this->drupalGet('/upgrade/review');
$session->addressEquals($expected);
$store->set('version', '6');
$store->delete('migrations');
$store->set('system_data', ['bar', 'foo']);
$store->set('step', 'review');
$this->drupalGet('/upgrade/review');
$session->addressEquals($expected);
$store->set('version', '6');
$store->set('migrations', ['foo', 'bar']);
$store->delete('system_data');
$store->set('step', 'review');
$this->drupalGet('/upgrade/review');
$session->addressEquals($expected);
}
// Test that the credential form is displayed for incremental migrations.
$store->set('step', 'overview');
$this->drupalGet('/upgrade');
$session->pageTextContains('An upgrade has already been performed on this site. To perform a new migration, create a clean and empty new install of Drupal 8. Rollbacks are not yet supported through the user interface.');
$this->drupalPostForm(NULL, [], t('Import new configuration and content from old site'));
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
}
/**
* Helper to test that a path goes to the Overview form.
*
* @param \Drupal\Tests\WebAssert $session
* The WebAssert object.
* @param string $expected
* The expected response text.
*/
protected function assertFirstForm(WebAssert $session, $expected) {
$paths = [
'',
'/incremental',
'/credentials',
'/idconflict',
'/review',
];
foreach ($paths as $path) {
$this->drupalGet('/upgrade' . $path);
$session->addressEquals($expected);
}
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional;
/**
* Tests the upgrade review form without translations.
*
* When using this test class do not enable content_translation.
*/
abstract class MigrateUpgradeI18nReviewPageTestBase extends MigrateUpgradeReviewPageTestBase {
/**
* Tests the review page when content_translation is enabled.
*/
public function testMigrateUpgradeReviewPage() {
$this->prepare();
// Start the upgrade process.
$this->drupalGet('/upgrade');
$this->drupalPostForm(NULL, [], t('Continue'));
$this->drupalPostForm(NULL, $this->edits, t('Review upgrade'));
$session = $this->assertSession();
$session->pageTextContains('WARNING: Content may be overwritten on your new site.');
$session->pageTextContains('There is conflicting content of these types:');
$session->pageTextContains('taxonomy terms');
$session->pageTextNotContains('There is translated content of these types:');
$session->pageTextNotContains('custom menu links');
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$session->statusCodeEquals(200);
// Ensure there are no errors about missing modules from the test module.
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
$session->pageTextNotContains(t('Destination module not found for migration_provider_test'));
// Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found'));
// Test the upgrade paths.
$available_paths = $this->getAvailablePaths();
$missing_paths = $this->getMissingPaths();
$this->assertUpgradePaths($session, $available_paths, $missing_paths);
}
}

View file

@ -0,0 +1,155 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
/**
* Provides a base class for testing the review step of the Upgrade form.
*/
abstract class MigrateUpgradeReviewPageTestBase extends MigrateUpgradeTestBase {
use CreateTestContentEntitiesTrait;
/**
* An array suitable for drupalPostForm().
*
* @var array
*/
protected $edits = [];
/**
* {@inheritdoc}
*/
public static $modules = ['migrate_drupal_ui'];
/**
* Tests the migrate upgrade review form.
*
* The upgrade review form displays a list of modules that will be upgraded
* and a list of modules that will not be upgraded. This test is to ensure
* that the review page works correctly for all contributed Drupal 6 and
* Drupal 7 modules that have moved to core, e.g. Views, and for modules that
* were in Drupal 6 or Drupal 7 core but are not in Drupal 8 core, e.g.
* Overlay.
*
* To do this all modules in the source fixtures are enabled, except test and
* example modules. This means that we can test that the modules listed in the
* the $noUpgradePath property of the update form class are correct, since
* there will be no available migrations which declare those modules as their
* source_module. It is assumed that the test fixtures include all modules
* that have moved to or dropped from core.
*
* The upgrade review form will also display errors for each migration that
* does not have a source_module definition. That function is not tested here.
*
* @see \Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase
*/
public function testMigrateUpgradeReviewPage() {
$this->prepare();
// Start the upgrade process.
$this->drupalGet('/upgrade');
$this->drupalPostForm(NULL, [], t('Continue'));
$this->drupalPostForm(NULL, $this->edits, t('Review upgrade'));
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
// Ensure there are no errors about missing modules from the test module.
$session = $this->assertSession();
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
$session->pageTextNotContains(t('Destination module not found for migration_provider_test'));
// Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found'));
// Test the upgrade paths.
$available_paths = $this->getAvailablePaths();
$missing_paths = $this->getMissingPaths();
$this->assertUpgradePaths($session, $available_paths, $missing_paths);
// Check there are no errors when a module in noUpgradePaths is not in the
// source system tables. Test with a module that is listed in noUpgradePaths
// for both Drupal 6 and Drupal 7.
// @see \Drupal\migrate_drupal_ui\Form\ReviewForm::$noUpgradePaths
$module = 'help';
$query = $this->sourceDatabase->delete('system');
$query->condition('type', 'module');
$query->condition('name', $module);
$query->execute();
// Start the upgrade process.
$this->drupalGet('/upgrade');
$this->drupalPostForm(NULL, [], t('Continue'));
$this->drupalPostForm(NULL, $this->edits, t('Review upgrade'));
$this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
// Test the upgrade paths.
$available_paths = $this->getAvailablePaths();
$available_paths = array_diff($available_paths, [$module]);
$missing_paths = $this->getMissingPaths();
$this->assertUpgradePaths($session, $available_paths, $missing_paths);
}
/**
* Performs preparation for the form tests.
*
* This is not done in setup because setup executes before the source database
* is loaded.
*/
public function prepare() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default'];
// Use the driver connection form to get the correct options out of the
// database settings. This supports all of the databases we test against.
$drivers = drupal_get_database_types();
$form = $drivers[$driver]->getFormOptions($connection_options);
$connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
$version = $this->getLegacyDrupalVersion($this->sourceDatabase);
$edit = [
$driver => $connection_options,
'source_private_file_path' => $this->getSourceBasePath(),
'version' => $version,
];
if ($version == 6) {
$edit['d6_source_base_path'] = $this->getSourceBasePath();
}
else {
$edit['source_base_path'] = $this->getSourceBasePath();
}
if (count($drivers) !== 1) {
$edit['driver'] = $driver;
}
$this->edits = $this->translatePostValues($edit);
// Enable all modules in the source except test and example modules, but
// include simpletest.
/** @var \Drupal\Core\Database\Query\SelectInterface $update */
$update = $this->sourceDatabase->update('system')
->fields(['status' => 1])
->condition('type', 'module');
$and = $update->andConditionGroup()
->condition('name', '%test%', 'NOT LIKE')
->condition('name', '%example%', 'NOT LIKE');
$conditions = $update->orConditionGroup();
$conditions->condition($and);
$conditions->condition('name', 'simpletest');
$update->condition($conditions);
$update->execute();
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [];
}
/**
* {@inheritdoc}
*/
protected function getEntityCountsIncremental() {
return [];
}
}

View file

@ -0,0 +1,263 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional;
use Drupal\Core\Database\Database;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
use Drupal\Tests\WebAssert;
/**
* Provides a base class for testing migration upgrades in the UI.
*/
abstract class MigrateUpgradeTestBase extends BrowserTestBase {
use MigrationConfigurationTrait;
use CreateTestContentEntitiesTrait;
/**
* Use the Standard profile to test help implementations of many core modules.
*
* @var string
*/
protected $profile = 'standard';
/**
* The source database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $sourceDatabase;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->createMigrationConnection();
$this->sourceDatabase = Database::getConnection('default', 'migrate_drupal_ui');
// Log in as user 1. Migrations in the UI can only be performed as user 1.
$this->drupalLogin($this->rootUser);
}
/**
* Loads a database fixture into the source database connection.
*
* @param string $path
* Path to the dump file.
*/
protected function loadFixture($path) {
$default_db = Database::getConnection()->getKey();
Database::setActiveConnection($this->sourceDatabase->getKey());
if (substr($path, -3) == '.gz') {
$path = 'compress.zlib://' . $path;
}
require $path;
Database::setActiveConnection($default_db);
}
/**
* Changes the database connection to the prefixed one.
*
* @todo Remove when we don't use global. https://www.drupal.org/node/2552791
*/
protected function createMigrationConnection() {
$connection_info = Database::getConnectionInfo('default')['default'];
if ($connection_info['driver'] === 'sqlite') {
// Create database file in the test site's public file directory so that
// \Drupal\simpletest\TestBase::restoreEnvironment() will delete this once
// the test is complete.
$file = $this->publicFilesDirectory . '/' . $this->testId . '-migrate.db.sqlite';
touch($file);
$connection_info['database'] = $file;
$connection_info['prefix'] = '';
}
else {
$prefix = is_array($connection_info['prefix']) ? $connection_info['prefix']['default'] : $connection_info['prefix'];
// Simpletest uses fixed length prefixes. Create a new prefix for the
// source database. Adding to the end of the prefix ensures that
// \Drupal\simpletest\TestBase::restoreEnvironment() will remove the
// additional tables.
$connection_info['prefix'] = $prefix . '0';
}
Database::addConnectionInfo('migrate_drupal_ui', 'default', $connection_info);
}
/**
* {@inheritdoc}
*/
protected function tearDown() {
Database::removeConnection('migrate_drupal_ui');
parent::tearDown();
}
/**
* Tests the displayed upgrade paths.
*
* @param \Drupal\Tests\WebAssert $session
* The web-assert session.
* @param array $available_paths
* An array of modules that will be upgraded.
* @param array $missing_paths
* An array of modules that will not be upgraded.
*/
protected function assertUpgradePaths(WebAssert $session, array $available_paths, array $missing_paths) {
// Test the available migration paths.
foreach ($available_paths as $available) {
$session->elementExists('xpath', "//span[contains(@class, 'checked') and text() = '$available']");
$session->elementNotExists('xpath', "//span[contains(@class, 'error') and text() = '$available']");
}
// Test the missing migration paths.
foreach ($missing_paths as $missing) {
$session->elementExists('xpath', "//span[contains(@class, 'error') and text() = '$missing']");
$session->elementNotExists('xpath', "//span[contains(@class, 'checked') and text() = '$missing']");
}
// Test the total count of missing and available paths.
$session->elementsCount('xpath', "//span[contains(@class, 'upgrade-analysis-report__status-icon--error')]", count($missing_paths));
$session->elementsCount('xpath', "//span[contains(@class, 'upgrade-analysis-report__status-icon--checked')]", count($available_paths));
}
/**
* Gets the source base path for the concrete test.
*
* @return string
* The source base path.
*/
abstract protected function getSourceBasePath();
/**
* Gets the expected number of entities per entity type after migration.
*
* @return int[]
* An array of expected counts keyed by entity type ID.
*/
abstract protected function getEntityCounts();
/**
* Gets the available upgrade paths.
*
* @return string[]
* An array of available upgrade paths.
*/
abstract protected function getAvailablePaths();
/**
* Gets the missing upgrade paths.
*
* @return string[]
* An array of missing upgrade paths.
*/
abstract protected function getMissingPaths();
/**
* Gets expected number of entities per entity after incremental migration.
*
* @return int[]
* An array of expected counts keyed by entity type ID.
*/
abstract protected function getEntityCountsIncremental();
/**
* Helper method to assert the text on the 'Upgrade analysis report' page.
*
* @param \Drupal\Tests\WebAssert $session
* The current session.
* @param array $all_available
* Array of modules that will be upgraded.
* @param array $all_missing
* Array of modules that will not be upgraded.
*/
protected function assertReviewPage(WebAssert $session, array $all_available, array $all_missing) {
$this->assertText('What will be upgraded?');
// Ensure there are no errors about the missing modules from the test module.
$session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
$session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
$session->pageTextNotContains(t('Destination module not found for migration_provider_test'));
// Ensure there are no errors about any other missing migration providers.
$session->pageTextNotContains(t('module not found'));
// Test the available migration paths.
foreach ($all_available as $available) {
$session->elementExists('xpath', "//span[contains(@class, 'checked') and text() = '$available']");
$session->elementNotExists('xpath', "//span[contains(@class, 'error') and text() = '$available']");
}
// Test the missing migration paths.
foreach ($all_missing as $missing) {
$session->elementExists('xpath', "//span[contains(@class, 'error') and text() = '$missing']");
$session->elementNotExists('xpath', "//span[contains(@class, 'checked') and text() = '$missing']");
}
}
/**
* Helper method that asserts text on the ID conflict form.
*
* @param \Drupal\Tests\WebAssert $session
* The current session.
* @param $session
* The current session.
*/
protected function assertIdConflict(WebAssert $session) {
$session->pageTextContains('WARNING: Content may be overwritten on your new site.');
$session->pageTextContains('There is conflicting content of these types:');
$session->pageTextContains('custom blocks');
$session->pageTextContains('custom menu links');
$session->pageTextContains('files');
$session->pageTextContains('taxonomy terms');
$session->pageTextContains('users');
$session->pageTextContains('comments');
$session->pageTextContains('content item revisions');
$session->pageTextContains('content items');
$session->pageTextContains('There is translated content of these types:');
}
/**
* Checks that migrations have been performed successfully.
*
* @param array $expected_counts
* The expected counts of each entity type.
* @param int $version
* The Drupal version.
*/
protected function assertMigrationResults(array $expected_counts, $version) {
// Have to reset all the statics after migration to ensure entities are
// loadable.
$this->resetAll();
foreach (array_keys(\Drupal::entityTypeManager()->getDefinitions()) as $entity_type) {
$real_count = (int) \Drupal::entityQuery($entity_type)->count()->execute();
$expected_count = isset($expected_counts[$entity_type]) ? $expected_counts[$entity_type] : 0;
$this->assertSame($expected_count, $real_count, "Found $real_count $entity_type entities, expected $expected_count.");
}
$plugin_manager = \Drupal::service('plugin.manager.migration');
/** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */
$all_migrations = $plugin_manager->createInstancesByTag('Drupal ' . $version);
foreach ($all_migrations as $migration) {
$id_map = $migration->getIdMap();
foreach ($id_map as $source_id => $map) {
// Convert $source_id into a keyless array so that
// \Drupal\migrate\Plugin\migrate\id_map\Sql::getSourceHash() works as
// expected.
$source_id_values = array_values(unserialize($source_id));
$row = $id_map->getRowBySource($source_id_values);
$destination = serialize($id_map->currentDestination());
$message = "Migration of $source_id to $destination as part of the {$migration->id()} migration. The source row status is " . $row['source_row_status'];
// A completed migration should have maps with
// MigrateIdMapInterface::STATUS_IGNORED or
// MigrateIdMapInterface::STATUS_IMPORTED.
$this->assertNotSame(MigrateIdMapInterface::STATUS_FAILED, $row['source_row_status'], $message);
$this->assertNotSame(MigrateIdMapInterface::STATUS_NEEDS_UPDATE, $row['source_row_status'], $message);
}
}
}
}

View file

@ -0,0 +1,151 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional\d6;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeI18nReviewPageTestBase;
/**
* Tests migrate upgrade review page for Drupal 6 without translations.
*
* @group migrate_drupal_6
* @group migrate_drupal_ui
*/
class MigrateUpgrade6I18nReviewPageTest extends MigrateUpgradeI18nReviewPageTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'language',
'telephone',
'aggregator',
'book',
'forum',
'statistics',
'syslog',
'tracker',
'update',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal6.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'aggregator',
'block',
'book',
'comment',
'contact',
'content',
'date',
'dblog',
'email',
'filefield',
'filter',
'forum',
'imagecache',
'imagefield',
'language',
'link',
'locale',
'menu',
'node',
'nodereference',
'optionwidgets',
'path',
'profile',
'search',
'statistics',
'syslog',
'system',
'taxonomy',
'text',
'update',
'upload',
'user',
'userreference',
// Include modules that do not have an upgrade path, defined in the
// $noUpgradePath property in MigrateUpgradeForm.
'blog',
'blogapi',
'calendarsignup',
'color',
'content_copy',
'content_multigroup',
'content_permissions',
'date_api',
'date_locale',
'date_php4',
'date_popup',
'date_repeat',
'date_timezone',
'date_tools',
'datepicker',
'ddblock',
'event',
'fieldgroup',
'filefield_meta',
'help',
'i18nstrings',
'imageapi',
'imageapi_gd',
'imageapi_imagemagick',
'imagecache_ui',
'jquery_ui',
'nodeaccess',
'number',
'openid',
'php',
'ping',
'poll',
'throttle',
'tracker',
'translation',
'trigger',
'variable',
'variable_admin',
'views_export',
'views_ui',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
'devel',
'devel_generate',
'devel_node_access',
'i18n',
'i18nblocks',
'i18ncck',
'i18ncontent',
'i18nmenu',
'i18npoll',
'i18nprofile',
'i18nsync',
'i18ntaxonomy',
'i18nviews',
'phone',
'views',
];
}
}

View file

@ -0,0 +1,211 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional\d6;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase;
/**
* Tests Drupal 6 upgrade without translations.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group migrate_drupal_ui
*/
class MigrateUpgrade6NoMultilingualTest extends MigrateUpgradeExecuteTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'language',
'content_translation',
'config_translation',
'migrate_drupal_ui',
'telephone',
'aggregator',
'book',
'forum',
'statistics',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal6.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'aggregator_item' => 1,
'aggregator_feed' => 2,
'block' => 35,
'block_content' => 2,
'block_content_type' => 1,
'comment' => 6,
// The 'standard' profile provides the 'comment' comment type, and the
// migration creates 12 comment types, one per node type.
'comment_type' => 13,
'contact_form' => 5,
'configurable_language' => 5,
'editor' => 2,
'field_config' => 89,
'field_storage_config' => 63,
'file' => 8,
'filter_format' => 7,
'image_style' => 5,
'language_content_settings' => 3,
'migration' => 105,
'node' => 17,
// The 'book' module provides the 'book' node type, and the migration
// creates 12 node types.
'node_type' => 13,
'rdf_mapping' => 7,
'search_page' => 2,
'shortcut' => 2,
'shortcut_set' => 1,
'action' => 23,
'menu' => 8,
'taxonomy_term' => 8,
'taxonomy_vocabulary' => 7,
'tour' => 4,
'user' => 7,
'user_role' => 6,
'menu_link_content' => 8,
'view' => 16,
'date_format' => 11,
'entity_form_display' => 29,
'entity_form_mode' => 1,
'entity_view_display' => 55,
'entity_view_mode' => 14,
'base_field_override' => 38,
];
}
/**
* {@inheritdoc}
*/
protected function getEntityCountsIncremental() {
$counts = $this->getEntityCounts();
$counts['block_content'] = 3;
$counts['comment'] = 7;
$counts['file'] = 9;
$counts['menu_link_content'] = 9;
$counts['node'] = 18;
$counts['taxonomy_term'] = 9;
$counts['user'] = 8;
$counts['view'] = 16;
return $counts;
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'aggregator',
'block',
'book',
'comment',
'contact',
'content',
'date',
'dblog',
'email',
'filefield',
'filter',
'forum',
'imagecache',
'imagefield',
'language',
'link',
'locale',
'menu',
'node',
'nodereference',
'optionwidgets',
'path',
'profile',
'search',
'statistics',
'system',
'taxonomy',
'text',
'upload',
'user',
'userreference',
// Include modules that do not have an upgrade path and are enabled in the
// source database, defined in the $noUpgradePath property
// in MigrateUpgradeForm.
'date_api',
'date_timezone',
'event',
'i18n',
'i18nstrings',
'imageapi',
'number',
'php',
'profile',
'variable_admin',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
'i18nblocks',
'i18ncck',
'i18ncontent',
'i18nmenu',
'i18nprofile',
];
}
/**
* {@inheritdoc}
*/
public function testMigrateUpgradeExecute() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$this->drupalGet('/upgrade');
$session = $this->assertSession();
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$button = $session->buttonExists('Continue');
$button->click();
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default'];
// Use the driver connection form to get the correct options out of the
// database settings. This supports all of the databases we test against.
$drivers = drupal_get_database_types();
$form = $drivers[$driver]->getFormOptions($connection_options);
$connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
$version = $this->getLegacyDrupalVersion($this->sourceDatabase);
$edit = [
$driver => $connection_options,
'version' => $version,
];
if (count($drivers) !== 1) {
$edit['driver'] = $driver;
}
$edits = $this->translatePostValues($edit);
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$session->pageTextContains("Install migrate_drupal_multilingual to run migration 'd6_system_maintenance_translation'.");
}
}

View file

@ -0,0 +1,155 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional\d6;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeReviewPageTestBase;
/**
* Tests migrate upgrade review page for Drupal 6.
*
* @group migrate_drupal_6
* @group migrate_drupal_ui
*/
class MigrateUpgrade6ReviewPageTest extends MigrateUpgradeReviewPageTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'language',
'content_translation',
'config_translation',
'telephone',
'aggregator',
'book',
'forum',
'statistics',
'syslog',
'tracker',
'update',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal6.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'aggregator',
'block',
'book',
'comment',
'contact',
'content',
'date',
'dblog',
'email',
'filefield',
'filter',
'forum',
'i18n',
'i18nblocks',
'i18ncck',
'i18nmenu',
'i18nprofile',
'i18nstrings',
'i18ntaxonomy',
'imagecache',
'imagefield',
'language',
'link',
'locale',
'menu',
'node',
'nodereference',
'optionwidgets',
'path',
'profile',
'search',
'statistics',
'syslog',
'system',
'taxonomy',
'text',
'update',
'upload',
'user',
'userreference',
// Include modules that do not have an upgrade path, defined in the
// $noUpgradePath property in MigrateUpgradeForm.
'blog',
'blogapi',
'calendarsignup',
'color',
'content_copy',
'content_multigroup',
'content_permissions',
'date_api',
'date_locale',
'date_php4',
'date_popup',
'date_repeat',
'date_timezone',
'date_tools',
'datepicker',
'ddblock',
'event',
'fieldgroup',
'filefield_meta',
'help',
'imageapi',
'imageapi_gd',
'imageapi_imagemagick',
'imagecache_ui',
'jquery_ui',
'nodeaccess',
'number',
'openid',
'php',
'ping',
'poll',
'throttle',
'tracker',
'translation',
'trigger',
'variable',
'variable_admin',
'views_export',
'views_ui',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
'devel',
'devel_generate',
'devel_node_access',
'i18ncontent',
'i18npoll',
'i18nsync',
'i18nviews',
'phone',
'views',
];
}
}

View file

@ -0,0 +1,219 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional\d6;
use Drupal\node\Entity\Node;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase;
use Drupal\user\Entity\User;
/**
* Tests Drupal 6 upgrade using the migrate UI.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group migrate_drupal_ui
*/
class MigrateUpgrade6Test extends MigrateUpgradeExecuteTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'language',
'content_translation',
'config_translation',
'migrate_drupal_ui',
'telephone',
'aggregator',
'book',
'forum',
'statistics',
'migration_provider_test',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal6.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'aggregator_item' => 1,
'aggregator_feed' => 2,
'block' => 35,
'block_content' => 2,
'block_content_type' => 1,
'comment' => 8,
// The 'standard' profile provides the 'comment' comment type, and the
// migration creates 12 comment types, one per node type.
'comment_type' => 13,
'contact_form' => 5,
'configurable_language' => 5,
'editor' => 2,
'field_config' => 90,
'field_storage_config' => 64,
'file' => 7,
'filter_format' => 7,
'image_style' => 5,
'language_content_settings' => 10,
'migration' => 105,
'node' => 18,
// The 'book' module provides the 'book' node type, and the migration
// creates 12 node types.
'node_type' => 13,
'rdf_mapping' => 7,
'search_page' => 2,
'shortcut' => 2,
'shortcut_set' => 1,
'action' => 23,
'menu' => 8,
'taxonomy_term' => 8,
'taxonomy_vocabulary' => 7,
'tour' => 5,
'user' => 7,
'user_role' => 6,
'menu_link_content' => 10,
'view' => 16,
'date_format' => 11,
'entity_form_display' => 29,
'entity_form_mode' => 1,
'entity_view_display' => 55,
'entity_view_mode' => 14,
'base_field_override' => 38,
];
}
/**
* {@inheritdoc}
*/
protected function getEntityCountsIncremental() {
$counts = $this->getEntityCounts();
$counts['block_content'] = 3;
$counts['comment'] = 9;
$counts['entity_view_display'] = 55;
$counts['entity_view_mode'] = 14;
$counts['file'] = 8;
$counts['menu_link_content'] = 11;
$counts['node'] = 19;
$counts['taxonomy_term'] = 9;
$counts['user'] = 8;
$counts['view'] = 16;
return $counts;
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'aggregator',
'block',
'book',
'comment',
'contact',
'content',
'date',
'dblog',
'email',
'filefield',
'filter',
'forum',
'i18n',
'i18nblocks',
'i18ncck',
'i18nmenu',
'i18nprofile',
'i18nstrings',
'i18ntaxonomy',
'imagecache',
'imagefield',
'language',
'link',
'locale',
'menu',
'node',
'nodereference',
'optionwidgets',
'path',
'profile',
'search',
'statistics',
'system',
'taxonomy',
'text',
'upload',
'user',
'userreference',
// Include modules that do not have an upgrade path and are enabled in the
// source database, defined in the $noUpgradePath property
// in MigrateUpgradeForm.
'date_api',
'date_timezone',
'event',
'imageapi',
'number',
'php',
'profile',
'variable_admin',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
'i18ncontent',
];
}
/**
* Executes all steps of migrations upgrade.
*/
public function testMigrateUpgradeExecute() {
parent::testMigrateUpgradeExecute();
// Ensure migrated users can log in.
$user = User::load(2);
$user->passRaw = 'john.doe_pass';
$this->drupalLogin($user);
$this->assertFollowUpMigrationResults();
}
/**
* Tests that follow-up migrations have been run successfully.
*/
protected function assertFollowUpMigrationResults() {
$node = Node::load(10);
$this->assertSame('12', $node->get('field_reference')->target_id);
$this->assertSame('12', $node->get('field_reference_2')->target_id);
$translation = $node->getTranslation('fr');
$this->assertSame('12', $translation->get('field_reference')->target_id);
$this->assertSame('12', $translation->get('field_reference_2')->target_id);
$node = Node::load(12)->getTranslation('en');
$this->assertSame('10', $node->get('field_reference')->target_id);
$this->assertSame('10', $node->get('field_reference_2')->target_id);
$translation = $node->getTranslation('fr');
$this->assertSame('10', $translation->get('field_reference')->target_id);
$this->assertSame('10', $translation->get('field_reference_2')->target_id);
}
}

View file

@ -0,0 +1,214 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional\d7;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase;
/**
* Tests Drupal 6 upgrade without translations.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group migrate_drupal_ui
*/
class MigrateUpgrade7NoMultilingualTest extends MigrateUpgradeExecuteTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'file',
'language',
'content_translation',
'config_translation',
'migrate_drupal_ui',
'telephone',
'aggregator',
'book',
'forum',
'statistics',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal7.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'aggregator_item' => 11,
'aggregator_feed' => 1,
'block' => 25,
'block_content' => 1,
'block_content_type' => 1,
'comment' => 2,
// The 'standard' profile provides the 'comment' comment type, and the
// migration creates 6 comment types, one per node type.
'comment_type' => 7,
// Module 'language' comes with 'en', 'und', 'zxx'. Migration adds 'is'.
'configurable_language' => 4,
'contact_form' => 3,
'editor' => 2,
'field_config' => 68,
'field_storage_config' => 50,
'file' => 3,
'filter_format' => 7,
'image_style' => 6,
'language_content_settings' => 2,
'migration' => 73,
'node' => 5,
'node_type' => 6,
'rdf_mapping' => 8,
'search_page' => 2,
'shortcut' => 6,
'shortcut_set' => 2,
'action' => 17,
'menu' => 6,
'taxonomy_term' => 18,
'taxonomy_vocabulary' => 4,
'tour' => 4,
'user' => 4,
'user_role' => 3,
'menu_link_content' => 10,
'view' => 16,
'date_format' => 11,
'entity_form_display' => 17,
'entity_form_mode' => 1,
'entity_view_display' => 28,
'entity_view_mode' => 14,
'base_field_override' => 9,
];
}
/**
* {@inheritdoc}
*/
protected function getEntityCountsIncremental() {
$counts = $this->getEntityCounts();
$counts['block_content'] = 2;
$counts['comment'] = 3;
$counts['file'] = 4;
$counts['menu_link_content'] = 11;
$counts['node'] = 6;
$counts['taxonomy_term'] = 19;
$counts['user'] = 5;
return $counts;
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'aggregator',
'block',
'book',
'color',
'comment',
'contact',
'date',
'dblog',
'email',
'entityreference',
'field',
'field_sql_storage',
'file',
'filter',
'forum',
'image',
'language',
'link',
'list',
'locale',
'menu',
'node',
'number',
'options',
'path',
'phone',
'rdf',
'search',
'shortcut',
'statistics',
'system',
'taxonomy',
'text',
'user',
// Include modules that do not have an upgrade path and are enabled in the
// source database, defined in the $noUpgradePath property
// in MigrateUpgradeForm.
'blog',
'contextual',
'date_api',
'entity',
'field_ui',
'help',
'php',
'simpletest',
'toolbar',
'translation',
'trigger',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
// These modules are in the missing path list because they are installed
// on the source site but they are not installed on the destination site.
'syslog',
'tracker',
'update',
];
}
/**
* {@inheritdoc}
*/
public function testMigrateUpgradeExecute() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$this->drupalGet('/upgrade');
$session = $this->assertSession();
$session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$button = $session->buttonExists('Continue');
$button->click();
$session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
$driver = $connection_options['driver'];
$connection_options['prefix'] = $connection_options['prefix']['default'];
// Use the driver connection form to get the correct options out of the
// database settings. This supports all of the databases we test against.
$drivers = drupal_get_database_types();
$form = $drivers[$driver]->getFormOptions($connection_options);
$connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']);
$version = $this->getLegacyDrupalVersion($this->sourceDatabase);
$edit = [
$driver => $connection_options,
'version' => $version,
];
if (count($drivers) !== 1) {
$edit['driver'] = $driver;
}
$edits = $this->translatePostValues($edit);
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
$session->pageTextContains("Install migrate_drupal_multilingual to run migration 'd7_system_maintenance_translation'.");
}
}

View file

@ -0,0 +1,168 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional\d7;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeReviewPageTestBase;
/**
* Tests migrate upgrade review page for Drupal 7.
*
* @group migrate_drupal_7
* @group migrate_drupal_ui
*/
class MigrateUpgrade7ReviewPageTest extends MigrateUpgradeReviewPageTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'language',
'content_translation',
'telephone',
'aggregator',
'book',
'forum',
'statistics',
'syslog',
'tracker',
'update',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal7.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'aggregator',
'block',
'book',
'color',
'comment',
'contact',
'date',
'dblog',
'email',
'field',
'field_sql_storage',
'file',
'filter',
'forum',
'image',
'language',
'link',
'list',
'locale',
'menu',
'node',
'number',
'options',
'path',
'phone',
'profile',
'rdf',
'search',
'shortcut',
'statistics',
'syslog',
'system',
'taxonomy',
'text',
'tracker',
'update',
'user',
// Include modules that do not have an upgrade path, defined in the
// $noUpgradePath property in MigrateUpgradeForm.
'blog',
'bulk_export',
'contextual',
'ctools',
'ctools_access_ruleset',
'ctools_ajax_sample',
'ctools_custom_content',
'dashboard',
'date_all_day',
'date_api',
'date_context',
'date_migrate',
'date_popup',
'date_repeat',
'date_repeat_field',
'date_tools',
'date_views',
'entity',
'entity_feature',
'entity_token',
'entityreference',
'entity_translation',
'field_ui',
'help',
'openid',
'overlay',
'page_manager',
'php',
'poll',
'search_embedded_form',
'search_extra_type',
'search_node_tags',
'simpletest',
'stylizer',
'term_depth',
'title',
'toolbar',
'translation',
'trigger',
'views_content',
'views_ui',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
'entity_translation_i18n_menu',
'entity_translation_upgrade',
'i18n',
'i18n_block',
'i18n_contact',
'i18n_field',
'i18n_forum',
'i18n_menu',
'i18n_node',
'i18n_path',
'i18n_redirect',
'i18n_select',
'i18n_string',
'i18n_sync',
'i18n_taxonomy',
'i18n_translation',
'i18n_user',
'i18n_variable',
'variable',
'variable_admin',
'variable_realm',
'variable_store',
'variable_views',
'views',
];
}
}

View file

@ -0,0 +1,227 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Functional\d7;
use Drupal\node\Entity\Node;
use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase;
use Drupal\user\Entity\User;
/**
* Tests Drupal 7 upgrade using the migrate UI.
*
* The test method is provided by the MigrateUpgradeTestBase class.
*
* @group migrate_drupal_ui
*/
class MigrateUpgrade7Test extends MigrateUpgradeExecuteTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'file',
'language',
'config_translation',
'content_translation',
'migrate_drupal_ui',
'telephone',
'aggregator',
'book',
'forum',
'rdf',
'statistics',
'migration_provider_test',
// Required for translation migrations.
'migrate_drupal_multilingual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal7.php');
}
/**
* {@inheritdoc}
*/
protected function getSourceBasePath() {
return __DIR__ . '/files';
}
/**
* {@inheritdoc}
*/
protected function getEntityCounts() {
return [
'aggregator_item' => 11,
'aggregator_feed' => 1,
'block' => 25,
'block_content' => 1,
'block_content_type' => 1,
'comment' => 3,
// The 'standard' profile provides the 'comment' comment type, and the
// migration creates 6 comment types, one per node type.
'comment_type' => 7,
// Module 'language' comes with 'en', 'und', 'zxx'. Migration adds 'is'
// and 'fr'.
'configurable_language' => 5,
'contact_form' => 3,
'editor' => 2,
'field_config' => 67,
'field_storage_config' => 50,
'file' => 3,
'filter_format' => 7,
'image_style' => 6,
'language_content_settings' => 6,
'migration' => 73,
'node' => 5,
'node_type' => 6,
'rdf_mapping' => 8,
'search_page' => 2,
'shortcut' => 6,
'shortcut_set' => 2,
'action' => 17,
'menu' => 6,
'taxonomy_term' => 18,
'taxonomy_vocabulary' => 4,
'tour' => 5,
'user' => 4,
'user_role' => 3,
'menu_link_content' => 12,
'view' => 16,
'date_format' => 11,
'entity_form_display' => 17,
'entity_form_mode' => 1,
'entity_view_display' => 28,
'entity_view_mode' => 14,
'base_field_override' => 9,
];
}
/**
* {@inheritdoc}
*/
protected function getEntityCountsIncremental() {
$counts = $this->getEntityCounts();
$counts['block_content'] = 2;
$counts['comment'] = 4;
$counts['file'] = 4;
$counts['menu_link_content'] = 13;
$counts['node'] = 6;
$counts['taxonomy_term'] = 19;
$counts['user'] = 5;
return $counts;
}
/**
* {@inheritdoc}
*/
protected function getAvailablePaths() {
return [
'aggregator',
'block',
'book',
'color',
'comment',
'contact',
'date',
'dblog',
'email',
'entityreference',
'field',
'field_sql_storage',
'file',
'filter',
'forum',
'i18n_variable',
'image',
'language',
'link',
'list',
'locale',
'menu',
'node',
'number',
'options',
'path',
'phone',
'rdf',
'search',
'shortcut',
'statistics',
'system',
'taxonomy',
'text',
'user',
// Include modules that do not have an upgrade path and are enabled in the
// source database, defined in the $noUpgradePath property
// in MigrateUpgradeForm.
'blog',
'contextual',
'date_api',
'entity',
'field_ui',
'help',
'php',
'simpletest',
'toolbar',
'translation',
'trigger',
];
}
/**
* {@inheritdoc}
*/
protected function getMissingPaths() {
return [
'i18n',
'variable',
'variable_realm',
'variable_store',
// These modules are in the missing path list because they are installed
// on the source site but they are not installed on the destination site.
'syslog',
'tracker',
'update',
];
}
/**
* Executes all steps of migrations upgrade.
*/
public function testMigrateUpgradeExecute() {
parent::testMigrateUpgradeExecute();
// Ensure migrated users can log in.
$user = User::load(2);
$user->passRaw = 'a password';
$this->drupalLogin($user);
$this->assertFollowUpMigrationResults();
}
/**
* Tests that follow-up migrations have been run successfully.
*/
protected function assertFollowUpMigrationResults() {
$node = Node::load(2);
$this->assertSame('4', $node->get('field_reference')->target_id);
$this->assertSame('4', $node->get('field_reference_2')->target_id);
$translation = $node->getTranslation('is');
$this->assertSame('4', $translation->get('field_reference')->target_id);
$this->assertSame('4', $translation->get('field_reference_2')->target_id);
$node = Node::load(4);
$this->assertSame('2', $node->get('field_reference')->target_id);
$this->assertSame('2', $node->get('field_reference_2')->target_id);
$translation = $node->getTranslation('en');
$this->assertSame('2', $translation->get('field_reference')->target_id);
$this->assertSame('2', $translation->get('field_reference_2')->target_id);
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\migrate_drupal_ui\Kernel;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\KernelTests\FileSystemModuleDiscoveryDataProviderTrait;
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
/**
* Tests that labels exist for all migrations.
*
* @group migrate_drupal_ui
*/
class MigrationLabelExistTest extends MigrateDrupalTestBase {
use FileSystemModuleDiscoveryDataProviderTrait;
/**
* Tests that labels exist for all migrations.
*/
public function testLabelExist() {
// Install all available modules.
$module_handler = $this->container->get('module_handler');
$modules = $this->coreModuleListDataProvider();
$modules_enabled = $module_handler->getModuleList();
$modules_to_enable = array_keys(array_diff_key($modules, $modules_enabled));
$this->enableModules($modules_to_enable);
/** @var \Drupal\migrate\Plugin\MigrationPluginManager $plugin_manager */
$plugin_manager = $this->container->get('plugin.manager.migration');
// Get all the migrations
$migrations = $plugin_manager->createInstances(array_keys($plugin_manager->getDefinitions()));
/** @var \Drupal\migrate\Plugin\Migration $migration */
foreach ($migrations as $migration) {
$migration_id = $migration->getPluginId();
$this->assertNotEmpty($migration->label(), new FormattableMarkup('Label found for @migration_id.', ['@migration_id' => $migration_id]));
}
}
}