diff --git a/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php index cd767cc3d1d73c56fca61f07b650f5d97a09a983..deeebe5987a55a6acc24ba70b62a8f5a43ae2356 100644 --- a/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php +++ b/core/modules/migrate_drupal_ui/src/Form/CredentialForm.php @@ -2,9 +2,12 @@ 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; /** @@ -21,6 +24,20 @@ class CredentialForm extends MigrateUpgradeFormBase { */ 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. * @@ -28,10 +45,13 @@ class CredentialForm extends MigrateUpgradeFormBase { * 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) { + public function __construct(RendererInterface $renderer, PrivateTempStoreFactory $tempstore_private, ClientInterface $http_client) { parent::__construct($tempstore_private); $this->renderer = $renderer; + $this->httpClient = $http_client; } /** @@ -40,7 +60,8 @@ public function __construct(RendererInterface $renderer, PrivateTempStoreFactory public static function create(ContainerInterface $container) { return new static( $container->get('renderer'), - $container->get('tempstore.private') + $container->get('tempstore.private'), + $container->get('http_client') ); } @@ -75,6 +96,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { $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 Upgrade documentation for more detailed instructions.', [':url' => 'https://www.drupal.org/upgrade/migrate']), + ]; + $form['version'] = [ '#type' => 'radios', '#default_value' => 7, @@ -145,6 +171,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ':input[name="version"]' => ['value' => '6'], ], ], + '#element_validate' => ['::validatePaths'], ]; $form['source']['source_base_path'] = [ @@ -156,6 +183,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ':input[name="version"]' => ['value' => '7'], ], ], + '#element_validate' => ['::validatePaths'], ]; $form['source']['source_private_file_path'] = [ @@ -168,6 +196,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ':input[name="version"]' => ['value' => '7'], ], ], + '#element_validate' => ['::validatePaths'], ]; return $form; @@ -177,7 +206,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@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. @@ -194,35 +222,61 @@ public function validateForm(array &$form, FormStateInterface $form_state) { // Validate the driver settings and just end here if we have any issues. if ($errors = $drivers[$driver]->validateDatabaseSettings($database)) { foreach ($errors as $name => $message) { - $form_state->setErrorByName($name, $message); + $this->errors[$name] = $message; } - return; } - - try { - $connection = $this->getConnection($database); - $version = (string) $this->getLegacyDrupalVersion($connection); - if (!$version) { - $form_state->setErrorByName($database['driver'] . '][0', $this->t('Source database does not contain a recognizable Drupal version.')); + 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); + } } - elseif ($version !== (string) $form_state->getValue('version')) { - $form_state->setErrorByName($database['driver'] . '][0', $this->t('Source database is Drupal version @version but version @selected was selected.', [ - '@version' => $version, - '@selected' => $form_state->getValue('version'), - ])); + catch (\Exception $e) { + $this->errors[$database['driver'] . '][database'] = $e->getMessage(); } - else { - // Setup migrations and save form data to private store. - $this->setupMigrations($database, $form_state); + } + + // Display all errors as a list of items. + if ($this->errors) { + $form_state->setError($form, $this->t('

Resolve all issues below to continue the upgrade.

')); + foreach ($this->errors as $name => $message) { + $form_state->setErrorByName($name, $message); } } - catch (\Exception $e) { - $error_message = [ - '#title' => $this->t('Resolve the issue below to continue the upgrade.'), - '#theme' => 'item_list', - '#items' => [$e->getMessage()], - ]; - $form_state->setErrorByName($database['driver'] . '][0', $this->renderer->renderPlain($error_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; + } } } diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php index 22ec01c0ebd8b352a9c2bab9d83d5812987bfdef..bb822b9370ced47c21c935c93725af4136eb0fc9 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php @@ -68,16 +68,35 @@ public function testMigrateUpgradeExecute() { // 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 the issue below to continue the 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 the issue below to continue the upgrade')); + $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 file 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.');