Newer
Older

Yassine Doghri
committed
<?php
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
namespace App\Controllers;

Yassine Doghri
committed
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use Throwable;
use Dotenv\Exception\ValidationException;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\Database\Exceptions\DatabaseException;
use Config\Database;
use App\Entities\User;

Yassine Doghri
committed
use App\Models\UserModel;
use CodeIgniter\Controller;

Yassine Doghri
committed
use Config\Services;
use Dotenv\Dotenv;
class InstallController extends Controller

Yassine Doghri
committed
{

Yassine Doghri
committed
/**
* @var string[]
*/
protected $helpers = ['form', 'components', 'svg'];
/**
* Constructor.
*/
public function initController(

Yassine Doghri
committed
RequestInterface $request,
ResponseInterface $response,
LoggerInterface $logger
): void {
// Do Not Edit This Line
parent::initController($request, $response, $logger);
}

Yassine Doghri
committed
/**
* Every operation goes through this method to handle
* the install logic.
*
* If all required actions have already been performed,
* the install route will show a 404 page.
*/

Yassine Doghri
committed
public function index(): string

Yassine Doghri
committed
{
if (!file_exists(ROOTPATH . '.env')) {

Benjamin Bellamy
committed
$this->createEnv();

Yassine Doghri
committed
}
// Check if .env has all required fields
$dotenv = Dotenv::createUnsafeImmutable(ROOTPATH);
$dotenv->load();
// Check if the created .env file is writable to continue install process

Yassine Doghri
committed
if (is_really_writable(ROOTPATH . '.env')) {
try {
$dotenv->required([
'app.baseURL',
'app.adminGateway',
'app.authGateway',
]);

Yassine Doghri
committed
} catch (ValidationException $e) {
// form to input instance configuration
return $this->instanceConfig();
}
try {
$dotenv->required([
'database.default.hostname',
'database.default.database',
'database.default.username',
'database.default.password',
'database.default.DBPrefix',
]);

Yassine Doghri
committed
} catch (ValidationException $validationException) {
return $this->databaseConfig();
}
try {
$dotenv->required('cache.handler');

Yassine Doghri
committed
} catch (ValidationException $validationException) {
return $this->cacheConfig();
}
} else {
try {
$dotenv->required([
'app.baseURL',
'app.adminGateway',
'app.authGateway',
'database.default.hostname',
'database.default.database',
'database.default.username',
'database.default.password',
'database.default.DBPrefix',
'cache.handler',
]);

Yassine Doghri
committed
} catch (ValidationException $e) {
return view('install/manual_config');
}
}

Yassine Doghri
committed
try {
$db = db_connect();
// Check if superadmin has been created, meaning migrations and seeds have passed
if (
$db->tableExists('users') &&
(new UserModel())->countAll() > 0
) {
// if so, show a 404 page

Yassine Doghri
committed
throw PageNotFoundException::forPageNotFound();

Yassine Doghri
committed
}

Yassine Doghri
committed
} catch (DatabaseException $databaseException) {
// Could not connect to the database
// show database config view to fix value
session()->setFlashdata(
'error',

Yassine Doghri
committed
lang('Install.messages.databaseConnectError'),
);
return view('install/database_config');

Yassine Doghri
committed
}
// migrate if no user has been created
$this->migrate();
// Check if all seeds have succeeded
$this->seed();
return $this->createSuperAdmin();
}
/**
* Returns the form to generate the .env config file for the instance.

Yassine Doghri
committed
* @return mixed|void

Yassine Doghri
committed
*/
public function createEnv()
{
// create empty .env file
try {
$envFile = fopen(ROOTPATH . '.env', 'w');
fclose($envFile);

Yassine Doghri
committed
} catch (Throwable $throwable) {
// Could not create the .env file, redirect to a view with manual instructions on how to add it
return view('install/manual_config');
}

Yassine Doghri
committed
}
public function instanceConfig()

Yassine Doghri
committed
{
return view('install/instance_config');
}
public function attemptInstanceConfig()
{
$rules = [
'hostname' => 'required|validate_url',
'admin_gateway' => 'required',
'auth_gateway' => 'required|differs[admin_gateway]',
];
if (!$this->validate($rules)) {

Yassine Doghri
committed
return redirect()

Yassine Doghri
committed
->to(
(host_url() === null
? config('App')->baseURL
: host_url()) . config('App')->installGateway,

Yassine Doghri
committed
)
->withInput()

Yassine Doghri
committed
->with('errors', $this->validator->getErrors());
}
$baseUrl = $this->request->getPost('hostname');
$mediaBaseUrl = $this->request->getPost('media_base_url');
self::writeEnv([
'app.baseURL' => $baseUrl,
'app.mediaBaseURL' =>
$mediaBaseUrl === null ? $baseUrl : $mediaBaseUrl,
'app.adminGateway' => $this->request->getPost('admin_gateway'),
'app.authGateway' => $this->request->getPost('auth_gateway'),
]);

Yassine Doghri
committed
helper('text');
// redirect to full install url with new baseUrl input
return redirect()->to(
reduce_double_slashes(

Yassine Doghri
committed
$baseUrl . '/' . config('App')->installGateway,
),

Yassine Doghri
committed
public function databaseConfig()
{
return view('install/database_config');
}
public function attemptDatabaseConfig()
{
$rules = [
'db_hostname' => 'required',
'db_name' => 'required',
'db_username' => 'required',
'db_password' => 'required',
];
if (!$this->validate($rules)) {

Yassine Doghri
committed
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());

Yassine Doghri
committed
}
self::writeEnv([
'database.default.hostname' => $this->request->getPost(

Yassine Doghri
committed
'db_hostname',
),
'database.default.database' => $this->request->getPost('db_name'),
'database.default.username' => $this->request->getPost(

Yassine Doghri
committed
'db_username',
),
'database.default.password' => $this->request->getPost(

Yassine Doghri
committed
'db_password',
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
),
'database.default.DBPrefix' => $this->request->getPost('db_prefix'),
]);
return redirect()->back();
}
public function cacheConfig()
{
return view('install/cache_config');
}
public function attemptCacheConfig()
{
$rules = [
'cache_handler' => 'required',
];
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
self::writeEnv([
'cache.handler' => $this->request->getPost('cache_handler'),
]);
return redirect()->back();

Yassine Doghri
committed
}
/**
* Runs all database migrations required for instance.
*/

Yassine Doghri
committed
public function migrate(): void

Yassine Doghri
committed
{

Yassine Doghri
committed
$migrations = Services::migrations();

Yassine Doghri
committed

Yassine Doghri
committed
$migrations->setNamespace('Myth\Auth')->latest();
$migrations->setNamespace('ActivityPub')->latest();
$migrations->setNamespace('Analytics')->latest();
$migrations->setNamespace(APP_NAMESPACE)->latest();

Yassine Doghri
committed
}
/**
* Runs all database seeds required for instance.
*/

Yassine Doghri
committed
public function seed(): void

Yassine Doghri
committed
{

Yassine Doghri
committed
$seeder = Database::seeder();

Yassine Doghri
committed
// Seed database
$seeder->call('AppSeeder');

Yassine Doghri
committed
}
/**
* Returns the form to create a the first superadmin user for the instance.
*/
public function createSuperAdmin()
{
return view('install/create_superadmin');

Yassine Doghri
committed
}
/**
* Creates the first superadmin user or redirects back to form if any error.
*
* After creation, user is redirected to login page to input its credentials.
*/
public function attemptCreateSuperAdmin()
{
$userModel = new UserModel();
// Validate here first, since some things,
// like the password, can only be validated properly here.
$rules = array_merge(
$userModel->getValidationRules(['only' => ['username']]),
[
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|strong_password',

Yassine Doghri
committed
],

Yassine Doghri
committed
);
if (!$this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
// Save the user

Yassine Doghri
committed
$user = new User($this->request->getPost());

Yassine Doghri
committed
// Activate user
$user->activate();

Yassine Doghri
committed
$db = Database::connect();

Yassine Doghri
committed
$db->transStart();
if (!($userId = $userModel->insert($user, true))) {
$db->transRollback();

Yassine Doghri
committed
return redirect()
->back()
->withInput()
->with('errors', $userModel->errors());
}
// add newly created user to superadmin group
$authorization = Services::authorization();
$authorization->addUserToGroup($userId, 'superadmin');
$db->transComplete();
// Success!
// set redirect_url session as admin area to go to after login
session()->set('redirect_url', route_to('admin'));

Yassine Doghri
committed
return redirect()
->route('login')
->with('message', lang('Install.messages.createSuperAdminSuccess'));
}
/**
* writes config values in .env file
* overwrites any existing key and appends new ones
*
* @param array $configData key/value config pairs
public static function writeEnv(array $configData): void
{
$envData = file(ROOTPATH . '.env'); // reads an array of lines
foreach ($configData as $key => $value) {
$replaced = false;
$keyVal = $key . '="' . $value . '"' . PHP_EOL;
$envData = array_map(function ($line) use (
$key,
$keyVal,
&$replaced
) {

Yassine Doghri
committed
if (strpos($line, (string) $key) === 0) {
$replaced = true;
return $keyVal;
}
return $line;
},
$envData);
if (!$replaced) {

Yassine Doghri
committed
$envData[] = $keyVal;
}
}
file_put_contents(ROOTPATH . '.env', implode('', $envData));
}