File: /home/xedaptot/ai.naniguide.com/app/Http/Controllers/InstallController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class InstallController extends Controller
{
/**
* Apply locale from cookie if present (DB-free locale switching during install).
*/
public function __construct()
{
if (isset($_COOKIE['last_language_code'])) {
$code = preg_replace('/[^a-zA-Z_-]/', '', $_COOKIE['last_language_code']);
if ($code && is_dir(resource_path('lang/' . $code))) {
\App::setLocale($code);
\Carbon\Carbon::setLocale($code);
}
}
}
/**
* Check for current step progress via session.
*/
public function step($request)
{
$step = 0;
if ($request->session()->has('compatibilities')) {
$step = 1;
} else {
return $step;
}
if ($request->session()->has('site_info')) {
$step = 3;
} else {
return $step;
}
if ($request->session()->has('database')) {
$step = 4;
} else {
return $step;
}
if ($request->session()->has('database_imported')) {
$step = 5;
} else {
return $step;
}
if ($request->session()->has('cron_jobs')) {
$step = 6;
} else {
return $step;
}
return $step;
}
/**
* Starting — redirect to system compatibility.
*/
public function starting(Request $request)
{
return redirect()->route('install.compatibility');
}
/**
* Step 1: System compatibility check.
*/
public function systemCompatibility(Request $request)
{
$request->session()->forget('compatibilities');
$compatibilities = $this->checkSystemCompatibility();
$result = true;
foreach ($compatibilities as $compatibility) {
if (!$compatibility['check']) {
$result = false;
}
}
try {
if ($result) {
$request->session()->put('compatibilities', $compatibilities);
}
return view('install.compatibilities', [
'compatibilities' => $compatibilities,
'result' => $result,
'step' => $this->step($request),
'current' => 1,
]);
} catch (\Exception $e) {
return redirect()->route('install.compatibility');
}
}
/**
* Step 2: Site info / configuration.
*/
public function siteInfo(Request $request)
{
if ($this->step($request) < 1) {
return redirect()->route('install.compatibility');
}
$rules = [
'site_name' => 'required',
'site_description' => 'required',
'name' => 'required',
'first_name' => 'required',
'last_name' => 'required',
'email' => 'required|email',
'password' => 'required',
'timezone' => 'required',
];
$mail_rules = [
'smtp' => [
'smtp_hostname' => 'required',
'smtp_port' => 'required',
'smtp_username' => 'required',
'smtp_password' => 'required',
'mail_from_email' => 'required|email',
'mail_from_name' => 'required',
],
'sendmail' => [
'mail_from_email' => 'required|email',
'mail_from_name' => 'required',
'sendmail_path' => 'required',
],
];
$smtp_rules = $mail_rules['smtp'];
if ($request->isMethod('post')) {
$request->session()->forget('site_info');
$rules = array_merge($rules, $mail_rules[$request->mail_mailer]);
$this->validate($request, $rules);
$site_info = $request->all();
// Check license
if (!empty($request->license)) {
try {
$license = \App\Helpers\LicenseHelper::getLicense($site_info['license']);
$site_info['license'] = $license['purchase_code'];
$site_info['license_type'] = $license['licence'];
$site_info['license_status'] = $license['status'];
$site_info['license_supported_until'] = $license['supported_until'];
} catch (\Exception $ex) {
throw \Illuminate\Validation\ValidationException::withMessages([
'license' => $ex->getMessage(),
]);
}
} else {
$site_info['license'] = '';
$site_info['license_type'] = '';
$site_info['license_status'] = '';
$site_info['license_supported_until'] = '';
}
// Check SMTP connection
if ($request->mail_mailer == 'smtp') {
$rules = [];
try {
$useTls = match (strtolower((string) $request->smtp_encryption)) {
"ssl" => true,
"tls" => null,
default => false,
};
$transport = new \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
$request->smtp_hostname,
(int) $request->smtp_port,
$useTls
);
$transport->setUsername($request->smtp_username);
$transport->setPassword($request->smtp_password);
$transport->start();
} catch (\Symfony\Component\Mailer\Exception\TransportExceptionInterface $e) {
$rules['smtp_valid'] = 'required';
} catch (\Exception $e) {
$rules['smtp_valid'] = 'required';
}
$this->validate($request, $rules);
}
$request->session()->put('site_info', $site_info);
return redirect()->route('install.database');
}
$site_info = $request->session()->get('site_info');
if (!empty($request->old())) {
$site_info = $request->old();
}
return view('install.site_info', [
'site_info' => $site_info,
'rules' => $rules,
'smtp_rules' => $smtp_rules,
'step' => $this->step($request),
'current' => 2,
]);
}
/**
* Step 3: Database configuration.
*/
public function database(Request $request)
{
if ($this->step($request) < 2) {
return redirect()->route('install.site_info');
}
$rules = [
'hostname' => 'required',
'port' => 'required',
'username' => 'required',
'password' => 'required',
'database_name' => 'required',
];
if ($request->isMethod('post')) {
$request->session()->forget('database');
$this->validate($request, $rules);
try {
$port = (int) $request->port;
$conn = new \mysqli($request->hostname, $request->username, $request->password, $request->database_name, $port);
} catch (\Exception $e) {
$rules['mysql_connection'] = 'required';
$request->session()->flash('alert-error', $e->getMessage());
}
$this->validate($request, $rules);
$database = $request->all();
$request->session()->put('database', $database);
// Write env
$this->writeEnv($request);
return redirect()->route('install.database_import');
}
$database = $request->session()->get('database');
if (!empty($request->old())) {
$database = $request->old();
}
return view('install.database', [
'database' => $database,
'rules' => $rules,
'step' => $this->step($request),
'current' => 3,
]);
}
/**
* Step 3b: Database import warning page.
*/
public function databaseImport(Request $request)
{
if ($this->step($request) < 3) {
return redirect()->route('install.database');
}
$database = $request->session()->get('database');
return view('install.database_import', [
'database' => $database,
'step' => $this->step($request),
'current' => 3,
]);
}
/**
* Step 3c: Execute database import.
*/
public function import(Request $request)
{
if ($this->step($request) < 3) {
return redirect()->route('install.database');
}
$database = $request->session()->get('database');
$site_info = $request->session()->get('site_info');
$mysqli = new \mysqli($database['hostname'], $database['username'], $database['password'], $database['database_name'], $database['port']);
$request->session()->forget('database_imported');
// Drop all existing tables
$sql = 'SHOW TABLE STATUS';
$statement = $mysqli->query($sql);
while ($result = $statement->fetch_object()) {
$tableName = $result->Name;
$mysqli->query('SET FOREIGN_KEY_CHECKS = 0;');
$mysqli->query("DROP TABLE IF EXISTS {$tableName};");
$mysqli->query('SET FOREIGN_KEY_CHECKS = 1;');
}
$request->session()->put('database_imported', true);
// Run migrate
artisan_migrate();
// Import database via seeder
\Artisan::call('db:seed', ['--class' => 'DatabaseInit']);
\Artisan::call('db:seed', ['--class' => 'TemplateSeeder']);
// Reconnect
$mysqli = new \mysqli($database['hostname'], $database['username'], $database['password'], $database['database_name'], $database['port']);
// Locale / Language
$locale = \App::getLocale();
$language = \App\Model\Language::getFirstLanguageByCode($locale);
[$user, $admin, $customer] = app(\App\Services\AccountManagement\AccountProvisioningService::class)
->createInstallAccount($site_info, $language->id);
// Insert system URLs
// Wave F: `url_delivery_handler` removed — see InstallController note.
// Display-only template — signed routes embed a per-URL HMAC so we
// can't use route() with placeholder values. Show the path shape with
// a SIGNATURE token for admin reference.
\App\Model\Setting::set('url_unsubscribe', rtrim(url('/'), '/') . '/c/SUBSCRIBER/unsubscribe-form/MESSAGE_ID?signature=SIGNATURE');
\App\Model\Setting::set('url_open_track', route('openTrackingUrl', ['message_id' => 'MESSAGE_ID']));
\App\Model\Setting::set('url_web_view', route('webViewerUrl', ['message_id' => 'MESSAGE_ID']));
\App\Model\Setting::set('url_click_track', route('clickTrackingUrl', ['message_id' => 'MESSAGE_ID', 'url' => 'URL']));
\App\Model\Setting::set('url_update_profile', action('Pub\MailListController@profileUpdateForm', ['list_uid' => 'LIST_UID', 'id' => 'SUBSCRIBER_ID', 'customer_uid' => 'CUSTOMER_UID']));
// Site info settings
\App\Model\Setting::set('site_name', $site_info['site_name']);
\App\Model\Setting::set('site_description', $site_info['site_description']);
\App\Model\Setting::set('license', $site_info['license']);
\App\Model\Setting::set('license_type', $site_info['license_type']);
// Mail settings
\App\Model\Setting::set('mailer.mailer', $site_info['mail_mailer']);
\App\Model\Setting::set('mailer.host', $site_info['smtp_hostname']);
\App\Model\Setting::set('mailer.port', $site_info['smtp_port']);
\App\Model\Setting::set('mailer.encryption', $site_info['smtp_encryption']);
\App\Model\Setting::set('mailer.username', $site_info['smtp_username']);
\App\Model\Setting::set('mailer.password', $site_info['smtp_password']);
\App\Model\Setting::set('mailer.from.name', $site_info['mail_from_name']);
\App\Model\Setting::set('mailer.from.address', $site_info['mail_from_email']);
\App\Model\Setting::set('mailer.sendmail_path', $site_info['sendmail_path']);
// Generate translation files
\App\Model\Language::dump();
$request->session()->flash('alert-success', trans('install.database_import.import_success'));
return redirect()->route('install.cron_jobs');
}
/**
* Step 4: Cron jobs configuration.
*/
public function cronJobs(Request $request)
{
if ($this->step($request) < 5) {
return redirect()->route('install.database');
}
$respone = \App\Library\Tool::cronjobUpdateController($request, $this);
if ($respone == 'done' || $respone == 'remote') {
return redirect()->route('install.finishing');
}
return view('install.cron_jobs', $respone);
}
/**
* Finishing — redirect to finish. Notifications table is recreated empty
* by the redesign migration, so no cleanup pass is needed at install.
*/
public function finishing(Request $request)
{
return redirect()->route('install.finish');
}
/**
* Step 5: Installation complete.
*/
public function finish(Request $request)
{
if ($this->step($request) < 6) {
return redirect()->route('install.database');
}
$request->session()->put('install_finish', true);
$file_path = storage_path('app/installed');
$file = fopen($file_path, 'w') or die('Unable to open file!');
fwrite($file, '');
fclose($file);
// Generate translation files again (after installed file is available)
\App\Model\Language::dump();
return view('install.finish', [
'step' => $this->step($request),
'current' => 6,
]);
}
/**
* Check system compatibility requirements.
*/
public function checkSystemCompatibility()
{
return [
['type' => 'requirement', 'name' => 'PHP', 'check' => version_compare(PHP_VERSION, config('custom.php'), '>='), 'note' => trans('messages.install.error.php', ['version' => config('custom.php')])],
['type' => 'requirement', 'name' => 'MySQLi', 'check' => function_exists('mysqli_connect'), 'note' => trans('messages.install.error.extension', ['name' => 'MySQLi'])],
['type' => 'requirement', 'name' => 'OpenSSL', 'check' => extension_loaded('openssl'), 'note' => trans('messages.install.error.extension', ['name' => 'OpenSSL'])],
['type' => 'requirement', 'name' => 'Multibyte String', 'check' => extension_loaded('mbstring'), 'note' => trans('messages.install.error.extension', ['name' => 'Multibyte String'])],
['type' => 'requirement', 'name' => 'PHP PDO', 'check' => extension_loaded('pdo'), 'note' => trans('messages.install.error.extension', ['name' => 'PHP PDO'])],
['type' => 'requirement', 'name' => 'Tokenizer', 'check' => extension_loaded('tokenizer'), 'note' => trans('messages.install.error.extension', ['name' => 'Tokenizer'])],
['type' => 'requirement', 'name' => 'PHP ZIP', 'check' => class_exists('ZipArchive', false), 'note' => trans('messages.install.error.extension', ['name' => 'PHP ZIP'])],
['type' => 'requirement', 'name' => 'IMAP', 'check' => extension_loaded('imap'), 'note' => trans('messages.install.error.extension', ['name' => 'IMAP'])],
['type' => 'requirement', 'name' => 'SQLite3', 'check' => class_exists('SQLite3'), 'note' => trans('messages.install.error.extension', ['name' => 'SQLite3'])],
['type' => 'requirement', 'name' => 'PHP GD', 'check' => (extension_loaded('gd') && function_exists('gd_info')), 'note' => trans('messages.install.error.extension', ['name' => 'PHP GD'])],
['type' => 'requirement', 'name' => 'PHP FileInfo', 'check' => extension_loaded('fileinfo'), 'note' => trans('messages.install.error.extension', ['name' => 'PHP FileInfo'])],
['type' => 'requirement', 'name' => 'PHP Curl', 'check' => extension_loaded('curl'), 'note' => trans('messages.install.error.extension', ['name' => 'PHP Curl'])],
['type' => 'requirement', 'name' => 'PHP XML', 'check' => extension_loaded('xml'), 'note' => trans('messages.install.error.extension', ['name' => 'PHP XML'])],
['type' => 'requirement', 'name' => 'proc_close()', 'check' => func_enabled('proc_close'), 'note' => trans('messages.install.error.function', ['name' => 'proc_close()'])],
['type' => 'requirement', 'name' => 'escapeshellarg()', 'check' => func_enabled('escapeshellarg'), 'note' => trans('messages.install.error.function', ['name' => 'escapeshellarg()'])],
['type' => 'permission', 'name' => base_path('storage/app'), 'check' => file_exists(base_path('/storage/app')) && is_dir(base_path('/storage/app')) && is_writable(base_path('/storage/app')), 'note' => trans('messages.install.notice.writable')],
['type' => 'permission', 'name' => base_path('storage/framework'), 'check' => file_exists(base_path('/storage/framework')) && is_dir(base_path('/storage/framework')) && is_writable(base_path('/storage/framework')), 'note' => trans('messages.install.notice.writable')],
['type' => 'permission', 'name' => base_path('storage/logs'), 'check' => file_exists(base_path('/storage/logs')) && is_dir(base_path('/storage/logs')) && is_writable(base_path('/storage/logs')), 'note' => trans('messages.install.notice.writable')],
['type' => 'permission', 'name' => base_path('storage/job'), 'check' => file_exists(base_path('/storage/job')) && is_dir(base_path('/storage/job')) && is_writable(base_path('/storage/job')), 'note' => trans('messages.install.notice.writable')],
['type' => 'permission', 'name' => base_path('bootstrap/cache'), 'check' => file_exists(base_path('/bootstrap/cache')) && is_dir(base_path('/bootstrap/cache')) && is_writable(base_path('/bootstrap/cache')), 'note' => trans('messages.install.notice.writable')],
];
}
/**
* Write configuration values to .env file.
*/
public function writeEnv($request)
{
$database = $request->session()->get('database');
$database = !empty($database) ? $database : [];
$smtp = $request->session()->get('site_info');
$smtp = !empty($smtp) ? $smtp : [];
\App\Helpers\write_envs([
'APP_URL' => url('/'),
'DB_HOST' => $database['hostname'] ?? '',
'DB_DATABASE' => $database['database_name'] ?? '',
'DB_USERNAME' => $database['username'] ?? '',
'DB_PASSWORD' => isset($database['password']) ? quoteDotEnvValue($database['password']) : '',
'DB_PORT' => $database['port'] ?? '',
'DB_TABLES_PREFIX' => $database['tables_prefix'] ?? '',
'MAIL_MAILER' => $smtp['mail_mailer'] ?? 'mail',
'MAIL_HOST' => $smtp['smtp_hostname'] ?? '',
'MAIL_PORT' => $smtp['smtp_port'] ?? '',
'MAIL_USERNAME' => $smtp['smtp_username'] ?? '',
'MAIL_PASSWORD' => isset($smtp['smtp_password']) ? quoteDotEnvValue($smtp['smtp_password']) : '',
'MAIL_ENCRYPTION' => $smtp['smtp_encryption'] ?? '',
'MAIL_FROM_ADDRESS' => $smtp['mail_from_email'] ?? '',
'MAIL_FROM_NAME' => $smtp['mail_from_name'] ?? '',
]);
}
}