xref: /webtrees/app/Http/RequestHandlers/SetupWizard.php (revision 89f7189b61a494347591c99bdb92afb7d8b66e1b)
1430ed1cbSGreg Roach<?php
2430ed1cbSGreg Roach
3430ed1cbSGreg Roach/**
4430ed1cbSGreg Roach * webtrees: online genealogy
51fe542e9SGreg Roach * Copyright (C) 2021 webtrees development team
6430ed1cbSGreg Roach * This program is free software: you can redistribute it and/or modify
7430ed1cbSGreg Roach * it under the terms of the GNU General Public License as published by
8430ed1cbSGreg Roach * the Free Software Foundation, either version 3 of the License, or
9430ed1cbSGreg Roach * (at your option) any later version.
10430ed1cbSGreg Roach * This program is distributed in the hope that it will be useful,
11430ed1cbSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12430ed1cbSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13430ed1cbSGreg Roach * GNU General Public License for more details.
14430ed1cbSGreg Roach * You should have received a copy of the GNU General Public License
15*89f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
16430ed1cbSGreg Roach */
17430ed1cbSGreg Roach
18430ed1cbSGreg Roachdeclare(strict_types=1);
19430ed1cbSGreg Roach
20430ed1cbSGreg Roachnamespace Fisharebest\Webtrees\Http\RequestHandlers;
21430ed1cbSGreg Roach
22430ed1cbSGreg Roachuse Exception;
23430ed1cbSGreg Roachuse Fisharebest\Localization\Locale;
24430ed1cbSGreg Roachuse Fisharebest\Localization\Locale\LocaleEnUs;
25430ed1cbSGreg Roachuse Fisharebest\Localization\Locale\LocaleInterface;
26430ed1cbSGreg Roachuse Fisharebest\Webtrees\Auth;
27430ed1cbSGreg Roachuse Fisharebest\Webtrees\Cache;
281fe542e9SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface;
2955037a57SGreg Roachuse Fisharebest\Webtrees\Factories\CacheFactory;
30430ed1cbSGreg Roachuse Fisharebest\Webtrees\Http\ViewResponseTrait;
31430ed1cbSGreg Roachuse Fisharebest\Webtrees\I18N;
32430ed1cbSGreg Roachuse Fisharebest\Webtrees\Module\ModuleLanguageInterface;
336b9cb339SGreg Roachuse Fisharebest\Webtrees\Registry;
34430ed1cbSGreg Roachuse Fisharebest\Webtrees\Services\MigrationService;
35430ed1cbSGreg Roachuse Fisharebest\Webtrees\Services\ModuleService;
36430ed1cbSGreg Roachuse Fisharebest\Webtrees\Services\ServerCheckService;
37430ed1cbSGreg Roachuse Fisharebest\Webtrees\Services\UserService;
38430ed1cbSGreg Roachuse Fisharebest\Webtrees\Session;
39430ed1cbSGreg Roachuse Fisharebest\Webtrees\Webtrees;
40430ed1cbSGreg Roachuse Illuminate\Database\Capsule\Manager as DB;
41430ed1cbSGreg Roachuse Psr\Http\Message\ResponseInterface;
42430ed1cbSGreg Roachuse Psr\Http\Message\ServerRequestInterface;
43430ed1cbSGreg Roachuse Psr\Http\Server\RequestHandlerInterface;
44430ed1cbSGreg Roachuse Symfony\Component\Cache\Adapter\NullAdapter;
45430ed1cbSGreg Roachuse Throwable;
46430ed1cbSGreg Roach
47430ed1cbSGreg Roachuse function app;
48430ed1cbSGreg Roachuse function e;
49430ed1cbSGreg Roachuse function file_get_contents;
50430ed1cbSGreg Roachuse function file_put_contents;
51430ed1cbSGreg Roachuse function ini_get;
52430ed1cbSGreg Roachuse function random_bytes;
53430ed1cbSGreg Roachuse function realpath;
54430ed1cbSGreg Roachuse function redirect;
55430ed1cbSGreg Roachuse function substr;
56430ed1cbSGreg Roachuse function touch;
57430ed1cbSGreg Roachuse function unlink;
58430ed1cbSGreg Roachuse function view;
59430ed1cbSGreg Roach
60430ed1cbSGreg Roach/**
61430ed1cbSGreg Roach * Controller for the installation wizard
62430ed1cbSGreg Roach */
63430ed1cbSGreg Roachclass SetupWizard implements RequestHandlerInterface
64430ed1cbSGreg Roach{
65430ed1cbSGreg Roach    use ViewResponseTrait;
66430ed1cbSGreg Roach
67430ed1cbSGreg Roach    private const DEFAULT_DBTYPE = 'mysql';
68430ed1cbSGreg Roach    private const DEFAULT_PREFIX = 'wt_';
69430ed1cbSGreg Roach    private const DEFAULT_DATA   = [
70430ed1cbSGreg Roach        'baseurl' => '',
71430ed1cbSGreg Roach        'lang'    => '',
72430ed1cbSGreg Roach        'dbtype'  => self::DEFAULT_DBTYPE,
73430ed1cbSGreg Roach        'dbhost'  => '',
74430ed1cbSGreg Roach        'dbport'  => '',
75430ed1cbSGreg Roach        'dbuser'  => '',
76430ed1cbSGreg Roach        'dbpass'  => '',
77430ed1cbSGreg Roach        'dbname'  => '',
78430ed1cbSGreg Roach        'tblpfx'  => self::DEFAULT_PREFIX,
79430ed1cbSGreg Roach        'wtname'  => '',
80430ed1cbSGreg Roach        'wtuser'  => '',
81430ed1cbSGreg Roach        'wtpass'  => '',
82430ed1cbSGreg Roach        'wtemail' => '',
83430ed1cbSGreg Roach    ];
84430ed1cbSGreg Roach
85354679afSGreg Roach    private const DEFAULT_PORTS = [
86354679afSGreg Roach        'mysql'  => '3306',
87354679afSGreg Roach        'pgsql'  => '5432',
88354679afSGreg Roach        'sqlite' => '',
89354679afSGreg Roach        'sqlsvr' => '1433',
90354679afSGreg Roach    ];
91354679afSGreg Roach
92430ed1cbSGreg Roach    /** @var MigrationService */
93430ed1cbSGreg Roach    private $migration_service;
94430ed1cbSGreg Roach
95430ed1cbSGreg Roach    /** @var ModuleService */
96430ed1cbSGreg Roach    private $module_service;
97430ed1cbSGreg Roach
98430ed1cbSGreg Roach    /** @var ServerCheckService */
99430ed1cbSGreg Roach    private $server_check_service;
100430ed1cbSGreg Roach
101430ed1cbSGreg Roach    /** @var UserService */
102430ed1cbSGreg Roach    private $user_service;
103430ed1cbSGreg Roach
104430ed1cbSGreg Roach    /**
105430ed1cbSGreg Roach     * SetupWizard constructor.
106430ed1cbSGreg Roach     *
107430ed1cbSGreg Roach     * @param MigrationService   $migration_service
108430ed1cbSGreg Roach     * @param ModuleService      $module_service
109430ed1cbSGreg Roach     * @param ServerCheckService $server_check_service
110430ed1cbSGreg Roach     * @param UserService        $user_service
111430ed1cbSGreg Roach     */
112430ed1cbSGreg Roach    public function __construct(
113430ed1cbSGreg Roach        MigrationService $migration_service,
114430ed1cbSGreg Roach        ModuleService $module_service,
115430ed1cbSGreg Roach        ServerCheckService $server_check_service,
116430ed1cbSGreg Roach        UserService $user_service
117430ed1cbSGreg Roach    ) {
118430ed1cbSGreg Roach        $this->user_service         = $user_service;
119430ed1cbSGreg Roach        $this->migration_service    = $migration_service;
120430ed1cbSGreg Roach        $this->module_service       = $module_service;
121430ed1cbSGreg Roach        $this->server_check_service = $server_check_service;
122430ed1cbSGreg Roach    }
123430ed1cbSGreg Roach
124430ed1cbSGreg Roach    /**
125430ed1cbSGreg Roach     * Installation wizard - check user input and proceed to the next step.
126430ed1cbSGreg Roach     *
127430ed1cbSGreg Roach     * @param ServerRequestInterface $request
128430ed1cbSGreg Roach     *
129430ed1cbSGreg Roach     * @return ResponseInterface
130430ed1cbSGreg Roach     */
131430ed1cbSGreg Roach    public function handle(ServerRequestInterface $request): ResponseInterface
132430ed1cbSGreg Roach    {
133430ed1cbSGreg Roach        $this->layout = 'layouts/setup';
134430ed1cbSGreg Roach
13555037a57SGreg Roach        // Some functions need a cache, but we don't have one yet.
1366b9cb339SGreg Roach        Registry::cache(new CacheFactory());
13755037a57SGreg Roach
138430ed1cbSGreg Roach        // We will need an IP address for the logs.
139430ed1cbSGreg Roach        $ip_address  = $request->getServerParams()['REMOTE_ADDR'] ?? '127.0.0.1';
140430ed1cbSGreg Roach        $request     = $request->withAttribute('client-ip', $ip_address);
141430ed1cbSGreg Roach
142430ed1cbSGreg Roach        app()->instance(ServerRequestInterface::class, $request);
143430ed1cbSGreg Roach        app()->instance('cache.array', new Cache(new NullAdapter()));
144430ed1cbSGreg Roach
145430ed1cbSGreg Roach        $data = $this->userData($request);
146430ed1cbSGreg Roach
147430ed1cbSGreg Roach        $params = (array) $request->getParsedBody();
148430ed1cbSGreg Roach        $step   = (int) ($params['step'] ?? '1');
149430ed1cbSGreg Roach
150430ed1cbSGreg Roach        $locales = $this->module_service
151430ed1cbSGreg Roach            ->setupLanguages()
152430ed1cbSGreg Roach            ->map(static function (ModuleLanguageInterface $module): LocaleInterface {
153430ed1cbSGreg Roach                return $module->locale();
154430ed1cbSGreg Roach            });
155430ed1cbSGreg Roach
156430ed1cbSGreg Roach        if ($data['lang'] === '') {
157430ed1cbSGreg Roach            $default = new LocaleEnUs();
158430ed1cbSGreg Roach
159430ed1cbSGreg Roach            $locale  = Locale::httpAcceptLanguage($request->getServerParams(), $locales->all(), $default);
160430ed1cbSGreg Roach
161430ed1cbSGreg Roach            $data['lang'] = $locale->languageTag();
162430ed1cbSGreg Roach        }
163430ed1cbSGreg Roach
164430ed1cbSGreg Roach        I18N::init($data['lang'], true);
165430ed1cbSGreg Roach
166430ed1cbSGreg Roach        $data['cpu_limit']    = $this->maxExecutionTime();
167430ed1cbSGreg Roach        $data['locales']      = $locales->all();
168430ed1cbSGreg Roach        $data['memory_limit'] = $this->memoryLimit();
169430ed1cbSGreg Roach
170430ed1cbSGreg Roach        // Only show database errors after the user has chosen a driver.
171430ed1cbSGreg Roach        if ($step >= 4) {
172430ed1cbSGreg Roach            $data['errors']   = $this->server_check_service->serverErrors($data['dbtype']);
173430ed1cbSGreg Roach            $data['warnings'] = $this->server_check_service->serverWarnings($data['dbtype']);
174430ed1cbSGreg Roach        } else {
175430ed1cbSGreg Roach            $data['errors']   = $this->server_check_service->serverErrors();
176430ed1cbSGreg Roach            $data['warnings'] = $this->server_check_service->serverWarnings();
177430ed1cbSGreg Roach        }
178430ed1cbSGreg Roach
179430ed1cbSGreg Roach        if (!$this->checkFolderIsWritable(Webtrees::DATA_DIR)) {
180430ed1cbSGreg Roach            $data['errors']->push(
181430ed1cbSGreg Roach                '<code>' . e(realpath(Webtrees::DATA_DIR)) . '</code><br>' .
182430ed1cbSGreg Roach                I18N::translate('Oops! webtrees was unable to create files in this folder.') . ' ' .
183430ed1cbSGreg Roach                I18N::translate('This usually means that you need to change the folder permissions to 777.')
184430ed1cbSGreg Roach            );
185430ed1cbSGreg Roach        }
186430ed1cbSGreg Roach
187430ed1cbSGreg Roach        switch ($step) {
188430ed1cbSGreg Roach            default:
189430ed1cbSGreg Roach            case 1:
190430ed1cbSGreg Roach                return $this->step1Language($data);
191430ed1cbSGreg Roach            case 2:
192430ed1cbSGreg Roach                return $this->step2CheckServer($data);
193430ed1cbSGreg Roach            case 3:
194430ed1cbSGreg Roach                return $this->step3DatabaseType($data);
195430ed1cbSGreg Roach            case 4:
196430ed1cbSGreg Roach                return $this->step4DatabaseConnection($data);
197430ed1cbSGreg Roach            case 5:
198430ed1cbSGreg Roach                return $this->step5Administrator($data);
199430ed1cbSGreg Roach            case 6:
200430ed1cbSGreg Roach                return $this->step6Install($data);
201430ed1cbSGreg Roach        }
202430ed1cbSGreg Roach    }
203430ed1cbSGreg Roach
204430ed1cbSGreg Roach    /**
205430ed1cbSGreg Roach     * @param ServerRequestInterface $request
206430ed1cbSGreg Roach     *
207430ed1cbSGreg Roach     * @return array<string,mixed>
208430ed1cbSGreg Roach     */
209430ed1cbSGreg Roach    private function userData(ServerRequestInterface $request): array
210430ed1cbSGreg Roach    {
211430ed1cbSGreg Roach        $params = (array) $request->getParsedBody();
212430ed1cbSGreg Roach
213430ed1cbSGreg Roach        $data = [];
214430ed1cbSGreg Roach
215430ed1cbSGreg Roach        foreach (self::DEFAULT_DATA as $key => $default) {
216430ed1cbSGreg Roach            $data[$key] = $params[$key] ?? $default;
217430ed1cbSGreg Roach        }
218430ed1cbSGreg Roach
219430ed1cbSGreg Roach        return $data;
220430ed1cbSGreg Roach    }
221430ed1cbSGreg Roach
222430ed1cbSGreg Roach    /**
223430ed1cbSGreg Roach     * The server's memory limit
224430ed1cbSGreg Roach     *
225430ed1cbSGreg Roach     * @return int
226430ed1cbSGreg Roach     */
227430ed1cbSGreg Roach    private function maxExecutionTime(): int
228430ed1cbSGreg Roach    {
229430ed1cbSGreg Roach        return (int) ini_get('max_execution_time');
230430ed1cbSGreg Roach    }
231430ed1cbSGreg Roach
232430ed1cbSGreg Roach    /**
233430ed1cbSGreg Roach     * The server's memory limit (in MB).
234430ed1cbSGreg Roach     *
235430ed1cbSGreg Roach     * @return int
236430ed1cbSGreg Roach     */
237430ed1cbSGreg Roach    private function memoryLimit(): int
238430ed1cbSGreg Roach    {
239430ed1cbSGreg Roach        $memory_limit = ini_get('memory_limit');
240430ed1cbSGreg Roach
241430ed1cbSGreg Roach        $number = (int) $memory_limit;
242430ed1cbSGreg Roach
243430ed1cbSGreg Roach        switch (substr($memory_limit, -1)) {
244430ed1cbSGreg Roach            case 'g':
245430ed1cbSGreg Roach            case 'G':
246430ed1cbSGreg Roach                return $number * 1024;
247430ed1cbSGreg Roach            case 'm':
248430ed1cbSGreg Roach            case 'M':
249430ed1cbSGreg Roach                return $number;
250430ed1cbSGreg Roach            case 'k':
251430ed1cbSGreg Roach            case 'K':
252430ed1cbSGreg Roach                return (int) ($number / 1024);
253430ed1cbSGreg Roach            default:
254430ed1cbSGreg Roach                return (int) ($number / 1048576);
255430ed1cbSGreg Roach        }
256430ed1cbSGreg Roach    }
257430ed1cbSGreg Roach
258430ed1cbSGreg Roach    /**
259430ed1cbSGreg Roach     * Check we can write to the data folder.
260430ed1cbSGreg Roach     *
261430ed1cbSGreg Roach     * @param string $data_dir
262430ed1cbSGreg Roach     *
263430ed1cbSGreg Roach     * @return bool
264430ed1cbSGreg Roach     */
265430ed1cbSGreg Roach    private function checkFolderIsWritable(string $data_dir): bool
266430ed1cbSGreg Roach    {
267430ed1cbSGreg Roach        $text1 = random_bytes(32);
268430ed1cbSGreg Roach
269430ed1cbSGreg Roach        try {
270430ed1cbSGreg Roach            file_put_contents($data_dir . 'test.txt', $text1);
271430ed1cbSGreg Roach            $text2 = file_get_contents(Webtrees::DATA_DIR . 'test.txt');
272430ed1cbSGreg Roach            unlink(Webtrees::DATA_DIR . 'test.txt');
273430ed1cbSGreg Roach        } catch (Exception $ex) {
274430ed1cbSGreg Roach            return false;
275430ed1cbSGreg Roach        }
276430ed1cbSGreg Roach
277430ed1cbSGreg Roach        return $text1 === $text2;
278430ed1cbSGreg Roach    }
279430ed1cbSGreg Roach
280430ed1cbSGreg Roach    /**
281430ed1cbSGreg Roach     * @param array<string,mixed> $data
282430ed1cbSGreg Roach     *
283430ed1cbSGreg Roach     * @return ResponseInterface
284430ed1cbSGreg Roach     */
285430ed1cbSGreg Roach    private function step1Language(array $data): ResponseInterface
286430ed1cbSGreg Roach    {
287430ed1cbSGreg Roach        return $this->viewResponse('setup/step-1-language', $data);
288430ed1cbSGreg Roach    }
289430ed1cbSGreg Roach
290430ed1cbSGreg Roach    /**
291430ed1cbSGreg Roach     * @param array<string,mixed> $data
292430ed1cbSGreg Roach     *
293430ed1cbSGreg Roach     * @return ResponseInterface
294430ed1cbSGreg Roach     */
295430ed1cbSGreg Roach    private function step2CheckServer(array $data): ResponseInterface
296430ed1cbSGreg Roach    {
297430ed1cbSGreg Roach        return $this->viewResponse('setup/step-2-server-checks', $data);
298430ed1cbSGreg Roach    }
299430ed1cbSGreg Roach
300430ed1cbSGreg Roach    /**
301430ed1cbSGreg Roach     * @param array<string,mixed> $data
302430ed1cbSGreg Roach     *
303430ed1cbSGreg Roach     * @return ResponseInterface
304430ed1cbSGreg Roach     */
305430ed1cbSGreg Roach    private function step3DatabaseType(array $data): ResponseInterface
306430ed1cbSGreg Roach    {
307430ed1cbSGreg Roach        if ($data['errors']->isNotEmpty()) {
308430ed1cbSGreg Roach            return $this->viewResponse('setup/step-2-server-checks', $data);
309430ed1cbSGreg Roach        }
310430ed1cbSGreg Roach
311430ed1cbSGreg Roach        return $this->viewResponse('setup/step-3-database-type', $data);
312430ed1cbSGreg Roach    }
313430ed1cbSGreg Roach
314430ed1cbSGreg Roach    /**
315430ed1cbSGreg Roach     * @param array<string,mixed> $data
316430ed1cbSGreg Roach     *
317430ed1cbSGreg Roach     * @return ResponseInterface
318430ed1cbSGreg Roach     */
319430ed1cbSGreg Roach    private function step4DatabaseConnection(array $data): ResponseInterface
320430ed1cbSGreg Roach    {
321430ed1cbSGreg Roach        if ($data['errors']->isNotEmpty()) {
322430ed1cbSGreg Roach            return $this->step3DatabaseType($data);
323430ed1cbSGreg Roach        }
324430ed1cbSGreg Roach
325430ed1cbSGreg Roach        return $this->viewResponse('setup/step-4-database-' . $data['dbtype'], $data);
326430ed1cbSGreg Roach    }
327430ed1cbSGreg Roach
328430ed1cbSGreg Roach    /**
329430ed1cbSGreg Roach     * @param array<string,mixed> $data
330430ed1cbSGreg Roach     *
331430ed1cbSGreg Roach     * @return ResponseInterface
332430ed1cbSGreg Roach     */
333430ed1cbSGreg Roach    private function step5Administrator(array $data): ResponseInterface
334430ed1cbSGreg Roach    {
335354679afSGreg Roach        // Use default port, if none specified.
336354679afSGreg Roach        $data['dbport'] = $data['dbport'] ?: self::DEFAULT_PORTS[$data['dbtype']];
337354679afSGreg Roach
338430ed1cbSGreg Roach        try {
339430ed1cbSGreg Roach            $this->connectToDatabase($data);
340430ed1cbSGreg Roach        } catch (Throwable $ex) {
341430ed1cbSGreg Roach            $data['errors']->push($ex->getMessage());
342430ed1cbSGreg Roach
343430ed1cbSGreg Roach            // Don't jump to step 4, as the error will make it jump to step 3.
344430ed1cbSGreg Roach            return $this->viewResponse('setup/step-4-database-' . $data['dbtype'], $data);
345430ed1cbSGreg Roach        }
346430ed1cbSGreg Roach
347430ed1cbSGreg Roach        return $this->viewResponse('setup/step-5-administrator', $data);
348430ed1cbSGreg Roach    }
349430ed1cbSGreg Roach
350430ed1cbSGreg Roach    /**
351430ed1cbSGreg Roach     * @param array<string,mixed> $data
352430ed1cbSGreg Roach     *
353430ed1cbSGreg Roach     * @return ResponseInterface
354430ed1cbSGreg Roach     */
355430ed1cbSGreg Roach    private function step6Install(array $data): ResponseInterface
356430ed1cbSGreg Roach    {
357430ed1cbSGreg Roach        $error = $this->checkAdminUser($data['wtname'], $data['wtuser'], $data['wtpass'], $data['wtemail']);
358430ed1cbSGreg Roach
359430ed1cbSGreg Roach        if ($error !== '') {
360430ed1cbSGreg Roach            $data['errors']->push($error);
361430ed1cbSGreg Roach
362430ed1cbSGreg Roach            return $this->step5Administrator($data);
363430ed1cbSGreg Roach        }
364430ed1cbSGreg Roach
365430ed1cbSGreg Roach        try {
366430ed1cbSGreg Roach            $this->createConfigFile($data);
367430ed1cbSGreg Roach        } catch (Throwable $exception) {
368430ed1cbSGreg Roach            return $this->viewResponse('setup/step-6-failed', ['exception' => $exception]);
369430ed1cbSGreg Roach        }
370430ed1cbSGreg Roach
371430ed1cbSGreg Roach        // Done - start using webtrees!
372430ed1cbSGreg Roach        return redirect($data['baseurl']);
373430ed1cbSGreg Roach    }
374430ed1cbSGreg Roach
375430ed1cbSGreg Roach    /**
376430ed1cbSGreg Roach     * @param string $wtname
377430ed1cbSGreg Roach     * @param string $wtuser
378430ed1cbSGreg Roach     * @param string $wtpass
379430ed1cbSGreg Roach     * @param string $wtemail
380430ed1cbSGreg Roach     *
381430ed1cbSGreg Roach     * @return string
382430ed1cbSGreg Roach     */
383430ed1cbSGreg Roach    private function checkAdminUser($wtname, $wtuser, $wtpass, $wtemail): string
384430ed1cbSGreg Roach    {
385430ed1cbSGreg Roach        if ($wtname === '' || $wtuser === '' || $wtpass === '' || $wtemail === '') {
386430ed1cbSGreg Roach            return I18N::translate('You must enter all the administrator account fields.');
387430ed1cbSGreg Roach        }
388430ed1cbSGreg Roach
389430ed1cbSGreg Roach        if (mb_strlen($wtpass) < 6) {
390430ed1cbSGreg Roach            return I18N::translate('The password needs to be at least six characters long.');
391430ed1cbSGreg Roach        }
392430ed1cbSGreg Roach
393430ed1cbSGreg Roach        return '';
394430ed1cbSGreg Roach    }
395430ed1cbSGreg Roach
396430ed1cbSGreg Roach    /**
397430ed1cbSGreg Roach     * @param array<string,mixed> $data
398430ed1cbSGreg Roach     *
399430ed1cbSGreg Roach     * @return void
400430ed1cbSGreg Roach     */
401430ed1cbSGreg Roach    private function createConfigFile(array $data): void
402430ed1cbSGreg Roach    {
403430ed1cbSGreg Roach        // Create/update the database tables.
404430ed1cbSGreg Roach        $this->connectToDatabase($data);
405430ed1cbSGreg Roach        $this->migration_service->updateSchema('\Fisharebest\Webtrees\Schema', 'WT_SCHEMA_VERSION', Webtrees::SCHEMA_VERSION);
406430ed1cbSGreg Roach
407430ed1cbSGreg Roach        // Add some default/necessary configuration data.
408430ed1cbSGreg Roach        $this->migration_service->seedDatabase();
409430ed1cbSGreg Roach
410430ed1cbSGreg Roach        // If we are re-installing, then this user may already exist.
411430ed1cbSGreg Roach        $admin = $this->user_service->findByIdentifier($data['wtemail']);
412430ed1cbSGreg Roach        if ($admin === null) {
413430ed1cbSGreg Roach            $admin = $this->user_service->findByIdentifier($data['wtuser']);
414430ed1cbSGreg Roach        }
415430ed1cbSGreg Roach        // Create the user
416430ed1cbSGreg Roach        if ($admin === null) {
417430ed1cbSGreg Roach            $admin = $this->user_service->create($data['wtuser'], $data['wtname'], $data['wtemail'], $data['wtpass']);
4181fe542e9SGreg Roach            $admin->setPreference(UserInterface::PREF_LANGUAGE, $data['lang']);
4191fe542e9SGreg Roach            $admin->setPreference(UserInterface::PREF_IS_VISIBLE_ONLINE, '1');
420430ed1cbSGreg Roach        } else {
421430ed1cbSGreg Roach            $admin->setPassword($_POST['wtpass']);
422430ed1cbSGreg Roach        }
423430ed1cbSGreg Roach        // Make the user an administrator
4241fe542e9SGreg Roach        $admin->setPreference(UserInterface::PREF_IS_ADMINISTRATOR, '1');
4251fe542e9SGreg Roach        $admin->setPreference(UserInterface::PREF_IS_EMAIL_VERIFIED, '1');
4261fe542e9SGreg Roach        $admin->setPreference(UserInterface::PREF_IS_ACCOUNT_APPROVED, '1');
427430ed1cbSGreg Roach
428430ed1cbSGreg Roach        // Write the config file. We already checked that this would work.
429430ed1cbSGreg Roach        $config_ini_php = view('setup/config.ini', $data);
430430ed1cbSGreg Roach
431430ed1cbSGreg Roach        file_put_contents(Webtrees::CONFIG_FILE, $config_ini_php);
432430ed1cbSGreg Roach
433430ed1cbSGreg Roach        // Login as the new user
434430ed1cbSGreg Roach        $request = app(ServerRequestInterface::class)
435430ed1cbSGreg Roach            ->withAttribute('base_url', $data['baseurl']);
436430ed1cbSGreg Roach
437430ed1cbSGreg Roach        Session::start($request);
438430ed1cbSGreg Roach        Auth::login($admin);
439430ed1cbSGreg Roach        Session::put('language', $data['lang']);
440430ed1cbSGreg Roach    }
441430ed1cbSGreg Roach
442430ed1cbSGreg Roach    /**
443430ed1cbSGreg Roach     * @param array<string,mixed> $data
444430ed1cbSGreg Roach     *
445430ed1cbSGreg Roach     * @return void
446430ed1cbSGreg Roach     */
447430ed1cbSGreg Roach    private function connectToDatabase(array $data): void
448430ed1cbSGreg Roach    {
449430ed1cbSGreg Roach        $capsule = new DB();
450430ed1cbSGreg Roach
451430ed1cbSGreg Roach        // Try to create the database, if it does not already exist.
452430ed1cbSGreg Roach        switch ($data['dbtype']) {
453430ed1cbSGreg Roach            case 'sqlite':
454430ed1cbSGreg Roach                $data['dbname'] = Webtrees::ROOT_DIR . 'data/' . $data['dbname'] . '.sqlite';
455430ed1cbSGreg Roach                touch($data['dbname']);
456430ed1cbSGreg Roach                break;
457430ed1cbSGreg Roach
458430ed1cbSGreg Roach            case 'mysql':
459430ed1cbSGreg Roach                $capsule->addConnection([
460430ed1cbSGreg Roach                    'driver'                  => $data['dbtype'],
461430ed1cbSGreg Roach                    'host'                    => $data['dbhost'],
462430ed1cbSGreg Roach                    'port'                    => $data['dbport'],
463430ed1cbSGreg Roach                    'database'                => '',
464430ed1cbSGreg Roach                    'username'                => $data['dbuser'],
465430ed1cbSGreg Roach                    'password'                => $data['dbpass'],
466430ed1cbSGreg Roach                ], 'temp');
467430ed1cbSGreg Roach                $capsule->getConnection('temp')->statement('CREATE DATABASE IF NOT EXISTS `' . $data['dbname'] . '` COLLATE utf8_unicode_ci');
468430ed1cbSGreg Roach                break;
469430ed1cbSGreg Roach        }
470430ed1cbSGreg Roach
471430ed1cbSGreg Roach        // Connect to the database.
472430ed1cbSGreg Roach        $capsule->addConnection([
473430ed1cbSGreg Roach            'driver'                  => $data['dbtype'],
474430ed1cbSGreg Roach            'host'                    => $data['dbhost'],
475430ed1cbSGreg Roach            'port'                    => $data['dbport'],
476430ed1cbSGreg Roach            'database'                => $data['dbname'],
477430ed1cbSGreg Roach            'username'                => $data['dbuser'],
478430ed1cbSGreg Roach            'password'                => $data['dbpass'],
479430ed1cbSGreg Roach            'prefix'                  => $data['tblpfx'],
480430ed1cbSGreg Roach            'prefix_indexes'          => true,
481430ed1cbSGreg Roach            // For MySQL
482430ed1cbSGreg Roach            'charset'                 => 'utf8',
483430ed1cbSGreg Roach            'collation'               => 'utf8_unicode_ci',
484430ed1cbSGreg Roach            'timezone'                => '+00:00',
485430ed1cbSGreg Roach            'engine'                  => 'InnoDB',
486430ed1cbSGreg Roach            'modes'                   => [
487430ed1cbSGreg Roach                'ANSI',
488430ed1cbSGreg Roach                'STRICT_TRANS_TABLES',
489430ed1cbSGreg Roach                'NO_ZERO_IN_DATE',
490430ed1cbSGreg Roach                'NO_ZERO_DATE',
491430ed1cbSGreg Roach                'ERROR_FOR_DIVISION_BY_ZERO',
492430ed1cbSGreg Roach            ],
493430ed1cbSGreg Roach            // For SQLite
494430ed1cbSGreg Roach            'foreign_key_constraints' => true,
495430ed1cbSGreg Roach        ]);
496430ed1cbSGreg Roach
497430ed1cbSGreg Roach        $capsule->setAsGlobal();
498430ed1cbSGreg Roach    }
499430ed1cbSGreg Roach}
500