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