'20260318_10_add_outlet_id_to_purchases.sql', '20260318_create_outlets_table.sql' => '20260318_20_create_outlets_table.sql', '20260318_multi_outlet_schema.sql' => '20260318_30_multi_outlet_schema.sql', '20260318_local_definitions.sql' => '20260318_40_local_definitions.sql', '20260318_user_outlets_table.sql' => '20260318_50_user_outlets_table.sql', default => $basename, }; } function installationLegacyNumberedMigrationFile(string $filePath): bool { // Skip obsolete pre-date-based migrations from older packaged builds. return preg_match('/^\d{1,7}_/', basename($filePath)) === 1; } function installationMigrationFiles(): array { $files = array_merge( glob(__DIR__ . '/../db/migrations/*.sql') ?: [], glob(__DIR__ . '/../db/migrations/*.php') ?: [] ); $files = array_values(array_filter($files, static function (string $filePath): bool { return !installationLegacyNumberedMigrationFile($filePath); })); usort($files, static function (string $left, string $right): int { return strnatcasecmp(installationMigrationSortKey($left), installationMigrationSortKey($right)); }); return $files; } function installationBuildDsn(string $host, ?string $dbName = null): string { $normalizedHost = trim($host); $port = null; if (substr_count($normalizedHost, ':') === 1) { [$hostPart, $portPart] = explode(':', $normalizedHost, 2); if ($hostPart !== '' && ctype_digit($portPart)) { $normalizedHost = $hostPart; $port = $portPart; } } $dsn = 'mysql:host=' . $normalizedHost; if ($port !== null) { $dsn .= ';port=' . $port; } if ($dbName !== null && $dbName !== '') { $dsn .= ';dbname=' . $dbName; } return $dsn . ';charset=utf8mb4'; } function installationQuoteIdentifier(string $identifier): string { return '`' . str_replace('`', '``', $identifier) . '`'; } function installationBuildConfigContent(string $host, string $name, string $user, string $pass): string { return sprintf( <<<'PHP' PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); if (session_status() === PHP_SESSION_NONE && !headers_sent()) { @session_start(); } if (isset($_SESSION['outlet_id'])) { $pdo->exec("SET @session_outlet_id = " . (int)$_SESSION['outlet_id']); } else { $pdo->exec("SET @session_outlet_id = 0"); } } return $pdo; } PHP, var_export($host, true), var_export($name, true), var_export($user, true), var_export($pass, true) ); } // Step 1: Requirements if ($step === 1) { $requirements = [ 'php_version' => [ 'name' => 'PHP Version (>= 8.0)', 'status' => version_compare(PHP_VERSION, '8.0.0', '>='), 'current' => PHP_VERSION ], 'pdo_mysql' => [ 'name' => 'PDO MySQL Extension', 'status' => extension_loaded('pdo_mysql'), 'current' => extension_loaded('pdo_mysql') ? 'Loaded' : 'Missing' ], 'curl' => [ 'name' => 'cURL Extension (Required for Licensing)', 'status' => extension_loaded('curl'), 'current' => extension_loaded('curl') ? 'Loaded' : 'Missing' ], 'config_writable' => [ 'name' => 'db/config.php Writable', 'status' => is_writable(__DIR__ . '/../db/config.php'), 'current' => is_writable(__DIR__ . '/../db/config.php') ? 'Yes' : 'No' ], 'root_writable' => [ 'name' => 'Root Directory Writable', 'status' => is_writable(__DIR__ . '/..'), 'current' => is_writable(__DIR__ . '/..') ? 'Yes' : 'No' ] ]; $allOk = true; foreach ($requirements as $req) { if (!$req['status']) $allOk = false; } } // Step 2: Database if ($step === 2 && $_SERVER['REQUEST_METHOD'] === 'POST') { $host = trim((string) ($_POST['db_host'] ?? '')); $name = trim((string) ($_POST['db_name'] ?? '')); $user = trim((string) ($_POST['db_user'] ?? '')); $pass = (string) ($_POST['db_pass'] ?? ''); if ($host === '' || $name === '' || $user === '') { $error = 'Database host, name, and user are required.'; } else { try { $pdoOptions = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]; try { $pdo = new PDO(installationBuildDsn($host, $name), $user, $pass, $pdoOptions); } catch (PDOException $connectionException) { $pdo = new PDO(installationBuildDsn($host), $user, $pass, $pdoOptions); $pdo->exec('CREATE DATABASE IF NOT EXISTS ' . installationQuoteIdentifier($name) . ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'); } $configPath = __DIR__ . '/../db/config.php'; $bytesWritten = file_put_contents($configPath, installationBuildConfigContent($host, $name, $user, $pass)); if ($bytesWritten === false) { throw new RuntimeException('Unable to write db/config.php'); } require_once __DIR__ . '/../includes/DatabaseInstaller.php'; DatabaseInstaller::install(); header("Location: index.php?step=3"); exit; } catch (Throwable $e) { $error = "Database Setup Failed: " . $e->getMessage(); } } } // Step 3: Super Admin if ($step === 3 && $_SERVER['REQUEST_METHOD'] === 'POST') { require_once __DIR__ . '/../db/config.php'; $adminUser = $_POST['admin_user'] ?? ''; $adminPass = $_POST['admin_pass'] ?? ''; $adminEmail = $_POST['admin_email'] ?? ''; try { require_once __DIR__ . '/../includes/DatabaseInstaller.php'; if (!DatabaseInstaller::isInstalled()) { DatabaseInstaller::install(); } else { DatabaseInstaller::ensureCurrentSchema(); } $pdo = db(); // Ensure Admin Role exists (might be in schema but just in case) $stmt = $pdo->prepare("SELECT id FROM role_groups WHERE name = 'Administrator'"); $stmt->execute(); $role = $stmt->fetch(); if (!$role) { $pdo->exec("INSERT INTO role_groups (name) VALUES ('Administrator')"); $roleId = $pdo->lastInsertId(); $pdo->exec("INSERT INTO role_permissions (role_id, permission) VALUES ($roleId, 'all')"); } else { $roleId = $role['id']; // Also ensure 'all' permission exists for it $stmt = $pdo->prepare("SELECT id FROM role_permissions WHERE role_id = ? AND permission = 'all'"); $stmt->execute([$roleId]); if (!$stmt->fetch()) { $pdo->exec("INSERT INTO role_permissions (role_id, permission) VALUES ($roleId, 'all')"); } } // Insert Admin User (Use ON DUPLICATE KEY UPDATE to handle existing user from schema) $hashedPass = password_hash($adminPass, PASSWORD_DEFAULT); $stmt = $pdo->prepare("INSERT INTO users (username, password, email, group_id, status) VALUES (?, ?, ?, ?, 'active') ON DUPLICATE KEY UPDATE password = VALUES(password), email = VALUES(email), group_id = VALUES(group_id), status = 'active'"); $stmt->execute([$adminUser, $hashedPass, $adminEmail, $roleId]); header("Location: index.php?step=4"); exit; } catch (Exception $e) { $error = "Setup Failed: " . $e->getMessage(); } } // Step 4: Finish if ($step === 4) { file_put_contents($lockFile, date('Y-m-d H:i:s')); } ?> Installation - Step <?= $step ?>

System Installation

Configure your application in minutes

1 ? '' : '1' ?>
2 ? '' : '2' ?>
3 ? '' : '3' ?>
4
Step 1: Check Requirements
Current:
Passed Failed
Please fix the issues above to continue.
Step 2: Database Configuration
This step creates the database tables and imports the base schema before you add the admin account.
Step 3: Super Admin Setup

Ready to Launch!

Installation completed successfully. Your system is now secure and ready to use.

For security, the installed.lock file has been created.