adding installation file
This commit is contained in:
parent
e135bdd9e0
commit
9a5d45c4c5
@ -222,7 +222,7 @@ $translations = [
|
||||
'customer_has_orders' => 'Cannot delete customer because they have existing orders',
|
||||
'loyalty_settings' => 'Loyalty System Settings',
|
||||
'loyalty_enabled' => 'Loyalty System Enabled',
|
||||
'loyalty_points_per_currency' => 'Points earned per 1 unit spent',
|
||||
'loyalty_points_per_currency' => 'Points earned per 1 Omani Rial spent',
|
||||
'loyalty_currency_per_point' => 'Value of 1 point (discount amount)',
|
||||
'loyalty_points' => 'Loyalty Points',
|
||||
'points_adjusted' => 'Points adjusted successfully',
|
||||
@ -348,7 +348,7 @@ $translations = [
|
||||
'order' => 'الطلب',
|
||||
'customer' => 'العميل',
|
||||
'qty' => 'الكمية',
|
||||
'close' => 'إغلاق',
|
||||
'close' => 'إإغلاق',
|
||||
'payments' => 'المدفوعات',
|
||||
'no_payments' => 'لا يوجد مدفوعات',
|
||||
'update_status' => 'تحديث الحالة',
|
||||
@ -444,7 +444,7 @@ $translations = [
|
||||
'customer_has_orders' => 'لا يمكن حذف العميل لوجود طلبات مرتبطة به',
|
||||
'loyalty_settings' => 'إعدادات نظام الولاء',
|
||||
'loyalty_enabled' => 'تفعيل نظام الولاء',
|
||||
'loyalty_points_per_currency' => 'النقاط المكتسبة لكل وحدة عملة يتم إنفاقها',
|
||||
'loyalty_points_per_currency' => 'النقاط المكتسبة لكل ريال عماني يتم إنفاقه',
|
||||
'loyalty_currency_per_point' => 'قيمة النقطة الواحدة (مبلغ الخصم)',
|
||||
'loyalty_points' => 'نقاط الولاء',
|
||||
'points_adjusted' => 'تم تعديل النقاط بنجاح',
|
||||
@ -491,4 +491,4 @@ function branch_url($new_branch_id) {
|
||||
$params = $_GET;
|
||||
$params['switch_branch'] = $new_branch_id;
|
||||
return '?' . http_build_query($params);
|
||||
}
|
||||
}
|
||||
284
install.php
Normal file
284
install.php
Normal file
@ -0,0 +1,284 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
$lockFile = __DIR__ . '/install.lock';
|
||||
if (file_exists($lockFile)) {
|
||||
die("Installation is locked. Remove 'install.lock' to re-run the installer.");
|
||||
}
|
||||
|
||||
$step = isset($_GET['step']) ? (int)$_GET['step'] : 1;
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
// Helper to run SQL file
|
||||
function runSqlFile($pdo, $filePath) {
|
||||
if (!file_exists($filePath)) return false;
|
||||
$sql = file_get_contents($filePath);
|
||||
// Split by semicolon, but be careful with triggers/procedures if any
|
||||
$queries = preg_split("/;+(?=[^']*'([^']*'[^']*')*[^']*$)/", $sql);
|
||||
foreach ($queries as $query) {
|
||||
$query = trim($query);
|
||||
if ($query) {
|
||||
try {
|
||||
$pdo->exec($query);
|
||||
} catch (PDOException $e) {
|
||||
// Ignore errors if table already exists during re-run
|
||||
if (strpos($e->getMessage(), 'already exists') === false) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($step === 2) {
|
||||
$host = $_POST['db_host'] ?? '127.0.0.1';
|
||||
$user = $_POST['db_user'] ?? '';
|
||||
$pass = $_POST['db_pass'] ?? '';
|
||||
$name = $_POST['db_name'] ?? '';
|
||||
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;charset=utf8mb4", $user, $pass);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$pdo->exec("CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
||||
$pdo->exec("USE `$name` text");
|
||||
|
||||
// Read existing helpers from current config if it exists
|
||||
$helpers = '';
|
||||
if (file_exists(__DIR__ . '/db/config.php')) {
|
||||
$currentConfig = file_get_contents(__DIR__ . '/db/config.php');
|
||||
// Extract functions after the db() function or constants
|
||||
if (preg_match('/function check_permission.*$/s', $currentConfig, $matches)) {
|
||||
$helpers = $matches[0];
|
||||
}
|
||||
}
|
||||
|
||||
// If helpers weren't found, use a set of default ones
|
||||
if (empty($helpers)) {
|
||||
$helpers = "
|
||||
function check_permission(\$page = null, \$user_id = null) {
|
||||
if (!\$user_id) \$user_id = \$_SESSION['user_id'] ?? null;
|
||||
if (!\$page) \$page = basename(\$_SERVER['PHP_SELF']);
|
||||
if (!\$user_id) return ['view' => 0, 'add' => 0, 'edit' => 0, 'delete' => 0];
|
||||
static \$permissions_cache = [];
|
||||
\$cache_key = \$user_id . '_' . \$page;
|
||||
if (isset(\$permissions_cache[\$cache_key])) return \$permissions_cache[\$cache_key];
|
||||
\$stmt = db()->prepare(\"SELECT can_view as view, can_add as `add`, can_edit as edit, can_delete as `delete` FROM user_permissions WHERE user_id = ? AND page = ?\");
|
||||
\$stmt->execute([\$user_id, \$page]);
|
||||
\$perms = \$stmt->fetch();
|
||||
if (!\$perms) return ['view' => 0, 'add' => 0, 'edit' => 0, 'delete' => 0];
|
||||
\$result = ['view' => (int)\$perms['view'], 'add' => (int)\$perms['add'], 'edit' => (int)\$perms['edit'], 'delete' => (int)\$perms['delete']];
|
||||
\$permissions_cache[\$cache_key] = \$result;
|
||||
return \$result;
|
||||
}
|
||||
function has_permission(\$action, \$page = null, \$user_id = null) {
|
||||
\$perms = check_permission(\$page, \$user_id);
|
||||
return !empty(\$perms[\$action]);
|
||||
}";
|
||||
}
|
||||
|
||||
$configContent = "<?php\n"
|
||||
. "define('DB_HOST', '$host');\n"
|
||||
. "define('DB_NAME', '$name');\n"
|
||||
. "define('DB_USER', '$user');\n"
|
||||
. "define('DB_PASS', '$pass');\n\n"
|
||||
. "function db() {\n"
|
||||
. " static \$pdo;\n"
|
||||
. " if (!\$pdo) {\n"
|
||||
. " \$pdo = new PDO(\"mysql:host=\" . DB_HOST . \";dbname=\" . DB_NAME . \";charset=utf8mb4\", DB_USER, DB_PASS, [\n"
|
||||
. " PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,\n"
|
||||
. " PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,\n"
|
||||
. " PDO::ATTR_EMULATE_PREPARES => false,\n"
|
||||
. " ]);\n"
|
||||
. " }\n"
|
||||
. " return \$pdo;\n"
|
||||
. "}\n\n"
|
||||
. trim($helpers) . "\n";
|
||||
|
||||
if (!is_dir(__DIR__ . '/db')) mkdir(__DIR__ . '/db', 0755, true);
|
||||
file_put_contents(__DIR__ . '/db/config.php', $configContent);
|
||||
|
||||
// Run initial schema
|
||||
runSqlFile($pdo, __DIR__ . '/db/migrations/01_initial_schema.sql');
|
||||
|
||||
$_SESSION['db_configured'] = true;
|
||||
header("Location: install.php?step=3");
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
$error = "Connection failed: " . $e->getMessage();
|
||||
}
|
||||
} elseif ($step === 3) {
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
$user = $_POST['admin_user'] ?? '';
|
||||
$pass = $_POST['admin_pass'] ?? '';
|
||||
$email = $_POST['admin_email'] ?? '';
|
||||
|
||||
if (empty($user) || empty($pass)) {
|
||||
$error = "Username and password are required.";
|
||||
} else {
|
||||
try {
|
||||
$hash = password_hash($pass, PASSWORD_DEFAULT);
|
||||
$db = db();
|
||||
|
||||
// Clear existing users
|
||||
$db->exec("SET FOREIGN_KEY_CHECKS = 0;");
|
||||
$db->exec("TRUNCATE users;");
|
||||
$db->exec("SET FOREIGN_KEY_CHECKS = 1;");
|
||||
|
||||
// Ensure company and branch exist (schema usually seeds them, but we ensure)
|
||||
$db->exec("INSERT IGNORE INTO companies (id, name_en, name_ar) VALUES (1, 'Laundry Brand', 'علامة غسيل')");
|
||||
$db->exec("INSERT IGNORE INTO branches (id, company_id, name_en, name_ar) VALUES (1, 1, 'Main Branch', 'الفرع الرئيسي')");
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO users (branch_id, company_id, username, password_hash, full_name_en, role, email) VALUES (1, 1, ?, ?, 'System Administrator', 'super_admin', ?)");
|
||||
$stmt->execute([$user, $hash, $email]);
|
||||
|
||||
file_put_contents($lockFile, date('Y-m-d H:i:s'));
|
||||
header("Location: install.php?step=4");
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
$error = "Admin setup failed: " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Environment Checks
|
||||
$checks = [
|
||||
'PHP Version >= 8.0' => version_compare(PHP_VERSION, '8.0.0', '>='),
|
||||
'PDO Extension' => extension_loaded('pdo_mysql'),
|
||||
'Config Writable' => is_writable(__DIR__ . '/db/config.php') || is_writable(__DIR__),
|
||||
'Assets Writable' => (is_writable(__DIR__ . '/assets/images') || (is_dir(__DIR__ . '/assets/images') && is_writable(__DIR__ . '/assets/images')))
|
||||
];
|
||||
$envReady = !in_array(false, $checks, true);
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Fresh Installation</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background: #f0f2f5; font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; }
|
||||
.install-card { width: 100%; max-width: 550px; padding: 2.5rem; border-radius: 20px; box-shadow: 0 15px 35px rgba(0,0,0,0.05); background: #fff; }
|
||||
.step-indicator { display: flex; justify-content: space-between; margin-bottom: 2.5rem; position: relative; }
|
||||
.step-indicator::before { content: ''; position: absolute; top: 15px; left: 0; right: 0; height: 2px; background: #e9ecef; z-index: 1; }
|
||||
.step-dot { width: 32px; height: 32px; border-radius: 50%; background: #e9ecef; display: flex; align-items: center; justify-content: center; font-weight: 600; color: #6c757d; z-index: 2; position: relative; transition: all 0.3s ease; }
|
||||
.step-dot.active { background: #0d6efd; color: #fff; transform: scale(1.1); box-shadow: 0 0 15px rgba(13, 110, 253, 0.3); }
|
||||
.step-dot.completed { background: #198754; color: #fff; }
|
||||
h3 { font-weight: 700; color: #1a1d20; letter-spacing: -0.5px; }
|
||||
h5 { font-weight: 600; color: #495057; margin-bottom: 1.5rem; }
|
||||
.form-label { font-weight: 500; color: #495057; }
|
||||
.btn-primary { padding: 0.75rem; font-weight: 600; border-radius: 10px; transition: all 0.2s; }
|
||||
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(13, 110, 253, 0.2); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="install-card">
|
||||
<div class="text-center mb-4">
|
||||
<h3 class="mb-1">Setup Wizard</h3>
|
||||
<p class="text-muted small">Step-by-step application installation</p>
|
||||
</div>
|
||||
|
||||
<div class="step-indicator">
|
||||
<div class="step-dot <?php echo $step >= 1 ? ($step > 1 ? 'completed' : 'active') : ''; ?>">1</div>
|
||||
<div class="step-dot <?php echo $step >= 2 ? ($step > 2 ? 'completed' : 'active') : ''; ?>">2</div>
|
||||
<div class="step-dot <?php echo $step >= 3 ? ($step > 3 ? 'completed' : 'active') : ''; ?>">3</div>
|
||||
<div class="step-dot <?php echo $step >= 4 ? ($step > 4 ? 'completed' : 'active') : ''; ?>">4</div>
|
||||
</div>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger border-0 shadow-sm mb-4"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($step === 1): ?>
|
||||
<h5>Checking Environment</h5>
|
||||
<div class="list-group list-group-flush mb-4 border rounded-3 overflow-hidden">
|
||||
<?php foreach ($checks as $label => $pass): ?>
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center py-3">
|
||||
<span class="text-secondary"><?php echo $label; ?></span>
|
||||
<?php if ($pass): ?>
|
||||
<span class="badge bg-success-subtle text-success px-3 py-2 rounded-pill">Passed</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-danger-subtle text-danger px-3 py-2 rounded-pill">Failed</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<a href="?step=2" class="btn btn-primary <?php echo !$envReady ? 'disabled' : ''; ?>">Continue to Database</a>
|
||||
</div>
|
||||
|
||||
<?php elseif ($step === 2): ?>
|
||||
<h5>Database Configuration</h5>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Database Host</label>
|
||||
<input type="text" name="db_host" class="form-control form-control-lg bg-light border-0" value="127.0.0.1" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Database Name</label>
|
||||
<input type="text" name="db_name" class="form-control form-control-lg bg-light border-0" placeholder="e.g. laundry_app" required>
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Username</label>
|
||||
<input type="text" name="db_user" class="form-control form-control-lg bg-light border-0" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="password" name="db_pass" class="form-control form-control-lg bg-light border-0">
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Initialize Database</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php elseif ($step === 3): ?>
|
||||
<h5>Administrator Account</h5>
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Admin Username</label>
|
||||
<input type="text" name="admin_user" class="form-control form-control-lg bg-light border-0" value="admin" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Email Address</label>
|
||||
<input type="email" name="admin_email" class="form-control form-control-lg bg-light border-0" placeholder="admin@domain.com">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="password" name="admin_pass" class="form-control form-control-lg bg-light border-0" required>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Complete Setup</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php elseif ($step === 4): ?>
|
||||
<div class="text-center">
|
||||
<div class="mb-4">
|
||||
<div class="bg-success-subtle text-success d-inline-flex align-items-center justify-content-center rounded-circle" style="width: 80px; height: 80px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="currentColor" class="bi bi-check2-all" viewBox="0 0 16 16">
|
||||
<path d="M12.354 4.354a.5.5 0 0 0-.708-.708L5 10.293 1.854 7.146a.5.5 0 1 0-.708.708l3.5 3.5a.5.5 0 0 0 .708 0l7-7zm-4.208 7-.896-.897.707-.707.543.543 6.646-6.647a.5.5 0 0 1 .708.708l-7 7a.5.5 0 0 1-.708 0z"/>
|
||||
<path d="m5.354 7.146.896.897-.707.707-.897-.896a.5.5 0 1 1 .708-.708z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h5>Installation Complete!</h5>
|
||||
<p class="text-muted mb-4">The application has been configured and the administrator account is ready.</p>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="login.php" class="btn btn-primary btn-lg">Login to System</a>
|
||||
<p class="small text-danger mt-3">Warning: Delete install.php for production security.</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user