SiteSettingsForm.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. namespace Drupal\Core\Installer\Form;
  3. use Drupal\Component\Utility\Crypt;
  4. use Drupal\Core\Database\Database;
  5. use Drupal\Core\Form\FormBase;
  6. use Drupal\Core\Form\FormStateInterface;
  7. use Drupal\Core\Render\RendererInterface;
  8. use Symfony\Component\DependencyInjection\ContainerInterface;
  9. /**
  10. * Provides a form to configure and rewrite settings.php.
  11. *
  12. * @internal
  13. */
  14. class SiteSettingsForm extends FormBase {
  15. /**
  16. * The site path.
  17. *
  18. * @var string
  19. */
  20. protected $sitePath;
  21. /**
  22. * The renderer.
  23. *
  24. * @var \Drupal\Core\Render\RendererInterface
  25. */
  26. protected $renderer;
  27. /**
  28. * Constructs a new SiteSettingsForm.
  29. *
  30. * @param string $site_path
  31. * The site path.
  32. */
  33. public function __construct($site_path, RendererInterface $renderer) {
  34. $this->sitePath = $site_path;
  35. $this->renderer = $renderer;
  36. }
  37. /**
  38. * {@inheritdoc}
  39. */
  40. public static function create(ContainerInterface $container) {
  41. return new static(
  42. $container->get('site.path'),
  43. $container->get('renderer')
  44. );
  45. }
  46. /**
  47. * {@inheritdoc}
  48. */
  49. public function getFormId() {
  50. return 'install_settings_form';
  51. }
  52. /**
  53. * {@inheritdoc}
  54. */
  55. public function buildForm(array $form, FormStateInterface $form_state) {
  56. $settings_file = './' . $this->sitePath . '/settings.php';
  57. $form['#title'] = $this->t('Database configuration');
  58. $drivers = drupal_get_database_types();
  59. $drivers_keys = array_keys($drivers);
  60. // Unless there is input for this form (for a non-interactive installation,
  61. // input originates from the $settings array passed into install_drupal()),
  62. // check whether database connection settings have been prepared in
  63. // settings.php already.
  64. // Note: The installer even executes this form if there is a valid database
  65. // connection already, since the submit handler of this form is responsible
  66. // for writing all $settings to settings.php (not limited to $databases).
  67. $input = &$form_state->getUserInput();
  68. if (!isset($input['driver']) && $database = Database::getConnectionInfo()) {
  69. $input['driver'] = $database['default']['driver'];
  70. $input[$database['default']['driver']] = $database['default'];
  71. }
  72. if (isset($input['driver'])) {
  73. $default_driver = $input['driver'];
  74. // In case of database connection info from settings.php, as well as for a
  75. // programmed form submission (non-interactive installer), the table prefix
  76. // information is usually normalized into an array already, but the form
  77. // element only allows to configure one default prefix for all tables.
  78. $prefix = &$input[$default_driver]['prefix'];
  79. if (isset($prefix) && is_array($prefix)) {
  80. $prefix = $prefix['default'];
  81. }
  82. $default_options = $input[$default_driver];
  83. }
  84. // If there is no database information yet, suggest the first available driver
  85. // as default value, so that its settings form is made visible via #states
  86. // when JavaScript is enabled (see below).
  87. else {
  88. $default_driver = current($drivers_keys);
  89. $default_options = [];
  90. }
  91. $form['driver'] = [
  92. '#type' => 'radios',
  93. '#title' => $this->t('Database type'),
  94. '#required' => TRUE,
  95. '#default_value' => $default_driver,
  96. ];
  97. if (count($drivers) == 1) {
  98. $form['driver']['#disabled'] = TRUE;
  99. }
  100. // Add driver specific configuration options.
  101. foreach ($drivers as $key => $driver) {
  102. $form['driver']['#options'][$key] = $driver->name();
  103. $form['settings'][$key] = $driver->getFormOptions($default_options);
  104. $form['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . $this->t('@driver_name settings', ['@driver_name' => $driver->name()]) . '</h2>';
  105. $form['settings'][$key]['#type'] = 'container';
  106. $form['settings'][$key]['#tree'] = TRUE;
  107. $form['settings'][$key]['advanced_options']['#parents'] = [$key];
  108. $form['settings'][$key]['#states'] = [
  109. 'visible' => [
  110. ':input[name=driver]' => ['value' => $key],
  111. ]
  112. ];
  113. }
  114. $form['actions'] = ['#type' => 'actions'];
  115. $form['actions']['save'] = [
  116. '#type' => 'submit',
  117. '#value' => $this->t('Save and continue'),
  118. '#button_type' => 'primary',
  119. '#limit_validation_errors' => [
  120. ['driver'],
  121. [$default_driver],
  122. ],
  123. '#submit' => ['::submitForm'],
  124. ];
  125. $form['errors'] = [];
  126. $form['settings_file'] = ['#type' => 'value', '#value' => $settings_file];
  127. return $form;
  128. }
  129. /**
  130. * {@inheritdoc}
  131. */
  132. public function validateForm(array &$form, FormStateInterface $form_state) {
  133. $driver = $form_state->getValue('driver');
  134. $database = $form_state->getValue($driver);
  135. $drivers = drupal_get_database_types();
  136. $reflection = new \ReflectionClass($drivers[$driver]);
  137. $install_namespace = $reflection->getNamespaceName();
  138. // Cut the trailing \Install from namespace.
  139. $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\'));
  140. $database['driver'] = $driver;
  141. $form_state->set('database', $database);
  142. foreach ($this->getDatabaseErrors($database, $form_state->getValue('settings_file')) as $name => $message) {
  143. $form_state->setErrorByName($name, $message);
  144. }
  145. }
  146. /**
  147. * Get any database errors and links them to a form element.
  148. *
  149. * @param array $database
  150. * An array of database settings.
  151. * @param string $settings_file
  152. * The settings file that contains the database settings.
  153. *
  154. * @return array
  155. * An array of form errors keyed by the element name and parents.
  156. */
  157. protected function getDatabaseErrors(array $database, $settings_file) {
  158. $errors = install_database_errors($database, $settings_file);
  159. $form_errors = array_filter($errors, function ($value) {
  160. // Errors keyed by something other than an integer already are linked to
  161. // form elements.
  162. return is_int($value);
  163. });
  164. // Find the generic errors.
  165. $errors = array_diff_key($errors, $form_errors);
  166. if (count($errors)) {
  167. $error_message = [
  168. '#type' => 'inline_template',
  169. '#template' => '{% trans %}Resolve all issues below to continue the installation. For help configuring your database server, see the <a href="https://www.drupal.org/getting-started/install">installation handbook</a>, or contact your hosting provider.{% endtrans%}{{ errors }}',
  170. '#context' => [
  171. 'errors' => [
  172. '#theme' => 'item_list',
  173. '#items' => $errors,
  174. ],
  175. ],
  176. ];
  177. // These are generic errors, so we do not have any specific key of the
  178. // database connection array to attach them to; therefore, we just put
  179. // them in the error array with standard numeric keys.
  180. $form_errors[$database['driver'] . '][0'] = $this->renderer->renderPlain($error_message);
  181. }
  182. return $form_errors;
  183. }
  184. /**
  185. * {@inheritdoc}
  186. */
  187. public function submitForm(array &$form, FormStateInterface $form_state) {
  188. global $install_state;
  189. // Update global settings array and save.
  190. $settings = [];
  191. $database = $form_state->get('database');
  192. $settings['databases']['default']['default'] = (object) [
  193. 'value' => $database,
  194. 'required' => TRUE,
  195. ];
  196. $settings['settings']['hash_salt'] = (object) [
  197. 'value' => Crypt::randomBytesBase64(55),
  198. 'required' => TRUE,
  199. ];
  200. // Remember the profile which was used.
  201. $settings['settings']['install_profile'] = (object) [
  202. 'value' => $install_state['parameters']['profile'],
  203. 'required' => TRUE,
  204. ];
  205. drupal_rewrite_settings($settings);
  206. // Add the config directories to settings.php.
  207. drupal_install_config_directories();
  208. // Indicate that the settings file has been verified, and check the database
  209. // for the last completed task, now that we have a valid connection. This
  210. // last step is important since we want to trigger an error if the new
  211. // database already has Drupal installed.
  212. $install_state['settings_verified'] = TRUE;
  213. $install_state['config_verified'] = TRUE;
  214. $install_state['database_verified'] = TRUE;
  215. $install_state['completed_task'] = install_verify_completed_task();
  216. }
  217. }