169ac8c92297a0a8ade29993e26794514fc86058
[yaffs-website] / web / core / modules / migrate_drupal_ui / src / Form / CredentialForm.php
1 <?php
2
3 namespace Drupal\migrate_drupal_ui\Form;
4
5 use Drupal\Component\Utility\UrlHelper;
6 use Drupal\Core\Form\FormStateInterface;
7 use Drupal\Core\Render\RendererInterface;
8 use Drupal\Core\TempStore\PrivateTempStoreFactory;
9 use GuzzleHttp\ClientInterface;
10 use GuzzleHttp\Exception\TransferException;
11 use Symfony\Component\DependencyInjection\ContainerInterface;
12
13 /**
14  * Migrate Upgrade database credential form.
15  *
16  * @internal
17  */
18 class CredentialForm extends MigrateUpgradeFormBase {
19
20   /**
21    * The renderer service.
22    *
23    * @var \Drupal\Core\Render\RendererInterface
24    */
25   protected $renderer;
26
27   /**
28    * The HTTP client to fetch the files with.
29    *
30    * @var \GuzzleHttp\ClientInterface
31    */
32   protected $httpClient;
33
34   /**
35    * An array of error information.
36    *
37    * @var array
38    */
39   protected $errors = [];
40
41   /**
42    * CredentialForm constructor.
43    *
44    * @param \Drupal\Core\Render\RendererInterface $renderer
45    *   The renderer service.
46    * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_private
47    *   The private tempstore factory.
48    * @param \GuzzleHttp\ClientInterface $http_client
49    *   A Guzzle client object.
50    */
51   public function __construct(RendererInterface $renderer, PrivateTempStoreFactory $tempstore_private, ClientInterface $http_client) {
52     parent::__construct($tempstore_private);
53     $this->renderer = $renderer;
54     $this->httpClient = $http_client;
55   }
56
57   /**
58    * {@inheritdoc}
59    */
60   public static function create(ContainerInterface $container) {
61     return new static(
62       $container->get('renderer'),
63       $container->get('tempstore.private'),
64       $container->get('http_client')
65     );
66   }
67
68   /**
69    * {@inheritdoc}
70    */
71   public function getFormId() {
72     return 'migrate_drupal_ui_credential_form';
73   }
74
75   /**
76    * {@inheritdoc}
77    */
78   public function buildForm(array $form, FormStateInterface $form_state) {
79     if ($this->store->get('step') != 'credential') {
80       return $this->restartUpgradeForm();
81     }
82
83     $form = parent::buildForm($form, $form_state);
84     $form['actions']['submit']['#value'] = $this->t('Review upgrade');
85
86     $form['#title'] = $this->t('Drupal Upgrade');
87
88     $drivers = $this->getDatabaseTypes();
89     $drivers_keys = array_keys($drivers);
90     // @todo https://www.drupal.org/node/2678510 Because this is a multi-step
91     //   form, the form is not rebuilt during submission. Ideally we would get
92     //   the chosen driver from form input, if available, in order to use
93     //   #limit_validation_errors in the same way
94     //   \Drupal\Core\Installer\Form\SiteSettingsForm does.
95     $default_driver = current($drivers_keys);
96
97     $default_options = [];
98
99     $form['help'] = [
100       '#type' => 'item',
101       '#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']),
102     ];
103
104     $form['version'] = [
105       '#type' => 'radios',
106       '#default_value' => 7,
107       '#title' => $this->t('Drupal version of the source site'),
108       '#options' => ['6' => $this->t('Drupal 6'), '7' => $this->t('Drupal 7')],
109       '#required' => TRUE,
110     ];
111
112     $form['database'] = [
113       '#type' => 'details',
114       '#title' => $this->t('Source database'),
115       '#description' => $this->t('Provide credentials for the database of the Drupal site you want to upgrade.'),
116       '#open' => TRUE,
117     ];
118
119     $form['database']['driver'] = [
120       '#type' => 'radios',
121       '#title' => $this->t('Database type'),
122       '#required' => TRUE,
123       '#default_value' => $default_driver,
124     ];
125     if (count($drivers) == 1) {
126       $form['database']['driver']['#disabled'] = TRUE;
127     }
128
129     // Add driver-specific configuration options.
130     foreach ($drivers as $key => $driver) {
131       $form['database']['driver']['#options'][$key] = $driver->name();
132
133       $form['database']['settings'][$key] = $driver->getFormOptions($default_options);
134       // @todo https://www.drupal.org/node/2678510 Using
135       //   #limit_validation_errors in the submit does not work so it is not
136       //   possible to require the database and username for mysql and pgsql.
137       //   This is because this is a multi-step form.
138       $form['database']['settings'][$key]['database']['#required'] = FALSE;
139       $form['database']['settings'][$key]['username']['#required'] = FALSE;
140       $form['database']['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . $this->t('@driver_name settings', ['@driver_name' => $driver->name()]) . '</h2>';
141       $form['database']['settings'][$key]['#type'] = 'container';
142       $form['database']['settings'][$key]['#tree'] = TRUE;
143       $form['database']['settings'][$key]['advanced_options']['#parents'] = [$key];
144       $form['database']['settings'][$key]['#states'] = [
145         'visible' => [
146           ':input[name=driver]' => ['value' => $key],
147         ],
148       ];
149
150       // Move the host fields out of advanced settings.
151       if (isset($form['database']['settings'][$key]['advanced_options']['host'])) {
152         $form['database']['settings'][$key]['host'] = $form['database']['settings'][$key]['advanced_options']['host'];
153         $form['database']['settings'][$key]['host']['#title'] = 'Database host';
154         $form['database']['settings'][$key]['host']['#weight'] = -1;
155         unset($form['database']['settings'][$key]['database']['#default_value']);
156         unset($form['database']['settings'][$key]['advanced_options']['host']);
157       }
158     }
159
160     $form['source'] = [
161       '#type' => 'details',
162       '#title' => $this->t('Source files'),
163       '#open' => TRUE,
164     ];
165     $form['source']['d6_source_base_path'] = [
166       '#type' => 'textfield',
167       '#title' => $this->t('Files directory'),
168       '#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).'),
169       '#states' => [
170         'visible' => [
171           ':input[name="version"]' => ['value' => '6'],
172         ],
173       ],
174       '#element_validate' => ['::validatePaths'],
175     ];
176
177     $form['source']['source_base_path'] = [
178       '#type' => 'textfield',
179       '#title' => $this->t('Public files directory'),
180       '#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).'),
181       '#states' => [
182         'visible' => [
183           ':input[name="version"]' => ['value' => '7'],
184         ],
185       ],
186       '#element_validate' => ['::validatePaths'],
187     ];
188
189     $form['source']['source_private_file_path'] = [
190       '#type' => 'textfield',
191       '#title' => $this->t('Private files directory'),
192       '#default_value' => '',
193       '#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).'),
194       '#states' => [
195         'visible' => [
196           ':input[name="version"]' => ['value' => '7'],
197         ],
198       ],
199       '#element_validate' => ['::validatePaths'],
200     ];
201
202     return $form;
203   }
204
205   /**
206    * {@inheritdoc}
207    */
208   public function validateForm(array &$form, FormStateInterface $form_state) {
209     // Retrieve the database driver from the form, use reflection to get the
210     // namespace, and then construct a valid database array the same as in
211     // settings.php.
212     $driver = $form_state->getValue('driver');
213     $drivers = $this->getDatabaseTypes();
214     $reflection = new \ReflectionClass($drivers[$driver]);
215     $install_namespace = $reflection->getNamespaceName();
216
217     $database = $form_state->getValue($driver);
218     // Cut the trailing \Install from namespace.
219     $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\'));
220     $database['driver'] = $driver;
221
222     // Validate the driver settings and just end here if we have any issues.
223     if ($errors = $drivers[$driver]->validateDatabaseSettings($database)) {
224       foreach ($errors as $name => $message) {
225         $this->errors[$name] = $message;
226       }
227     }
228     else {
229       try {
230         $connection = $this->getConnection($database);
231         $version = (string) $this->getLegacyDrupalVersion($connection);
232         if (!$version) {
233           $this->errors[$database['driver'] . '][database'] = $this->t('Source database does not contain a recognizable Drupal version.');
234         }
235         elseif ($version !== (string) $form_state->getValue('version')) {
236           $this->errors['version'] = $this->t('Source database is Drupal version @version but version @selected was selected.',
237             [
238               '@version' => $version,
239               '@selected' => $form_state->getValue('version'),
240             ]);
241         }
242         else {
243           // Setup migrations and save form data to private store.
244           $this->setupMigrations($database, $form_state);
245         }
246       }
247       catch (\Exception $e) {
248         $this->errors[$database['driver'] . '][database'] = $e->getMessage();
249       }
250     }
251
252     // Display all errors as a list of items.
253     if ($this->errors) {
254       $form_state->setError($form, $this->t('<h3>Resolve all issues below to continue the upgrade.</h3>'));
255       foreach ($this->errors as $name => $message) {
256         $form_state->setErrorByName($name, $message);
257       }
258     }
259   }
260
261   /**
262    * The #element_validate handler for the source path elements.
263    *
264    * Ensures that entered path can be read.
265    */
266   public function validatePaths($element, FormStateInterface $form_state) {
267     if ($source = $element['#value']) {
268       $msg = $this->t('Unable to read from @title.', ['@title' => $element['#title']]);
269       if (UrlHelper::isExternal($source)) {
270         try {
271           $this->httpClient->head($source);
272         }
273         catch (TransferException $e) {
274           $this->errors[$element['#name']] = $msg . ' ' . $e->getMessage();
275         }
276       }
277       elseif (!file_exists($source) || (!is_dir($source)) || (!is_readable($source))) {
278         $this->errors[$element['#name']] = $msg;
279       }
280     }
281   }
282
283   /**
284    * {@inheritdoc}
285    */
286   public function submitForm(array &$form, FormStateInterface $form_state) {
287     $this->store->set('step', 'idconflict');
288     $form_state->setRedirect('migrate_drupal_ui.upgrade_idconflict');
289   }
290
291   /**
292    * {@inheritdoc}
293    */
294   public function getConfirmText() {
295     return $this->t('Review upgrade');
296   }
297
298   /**
299    * Returns all supported database driver installer objects.
300    *
301    * @return \Drupal\Core\Database\Install\Tasks[]
302    *   An array of available database driver installer objects.
303    */
304   protected function getDatabaseTypes() {
305     // Make sure the install API is available.
306     include_once DRUPAL_ROOT . '/core/includes/install.inc';
307     return drupal_get_database_types();
308   }
309
310 }