fix install file

This commit is contained in:
Flatlogic Bot 2026-02-28 05:55:01 +00:00
parent cfc04e4af0
commit d2bc35de8c
2 changed files with 227 additions and 230 deletions

View File

@ -55,10 +55,41 @@ if (!function_exists('get_settings')) {
}
}
/**
* Compatibility wrapper for getCharitySettings
* @return array
*/
if (!function_exists('getCharitySettings')) {
function getCharitySettings() {
$settings = get_settings();
// Return only the keys that might be expected by older code
return [
'charity_name' => $settings['site_name'],
'charity_email' => $settings['site_email'],
'charity_phone' => $settings['site_phone'],
'charity_address' => $settings['site_address'],
'charity_logo' => $settings['site_logo'],
'charity_favicon' => $settings['site_favicon'],
'site_maintenance' => $settings['site_maintenance'],
'site_footer' => $settings['site_footer'],
'allow_registration' => $settings['allow_registration']
];
}
}
// Global settings variable
$sys_settings = get_settings();
// Maintenance Mode Check
if ($sys_settings['site_maintenance'] && !isAdmin() && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename($_SERVER['PHP_SELF']) !== 'logout.php') {
die("<h1>النظام تحت الصيانة حالياً</h1><p>يرجى المحاولة مرة أخرى في وقت لاحق.</p>");
// Maintenance Mode Check - only if not admin and not on login/logout
// Assuming isAdmin() exists, if not we should be careful
if ($sys_settings['site_maintenance'] && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename($_SERVER['PHP_SELF']) !== 'logout.php') {
// If isAdmin is not defined, we might need a fallback or just check session
$is_admin = false;
if (isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin') {
$is_admin = true;
}
if (!$is_admin) {
die("<h1>النظام تحت الصيانة حالياً</h1><p>يرجى المحاولة مرة أخرى في وقت لاحق.</p>");
}
}

View File

@ -1,279 +1,253 @@
<?php
/**
* Installation File for Admin Panel
* This file handles requirement checks, database configuration, migrations, and super admin setup.
*/
session_start();
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Configuration file path
$config_file = __DIR__ . '/db/config.php';
$is_configured = file_exists($config_file);
// Step 0: Check requirements
$requirements = [
'PHP Version >= 7.4' => version_compare(PHP_VERSION, '7.4.0', '>='),
'PDO Extension' => extension_loaded('pdo'),
'PDO MySQL Extension' => extension_loaded('pdo_mysql'),
'Config Directory Writable' => is_writable(__DIR__ . '/db/'),
'Uploads Directory Writable' => is_writable(__DIR__ . '/uploads/') || (mkdir(__DIR__ . '/uploads/', 0777, true) && is_writable(__DIR__ . '/uploads/')),
];
$all_requirements_met = !in_array(false, $requirements, true);
// Current step
$step = isset($_GET['step']) ? (int)$_GET['step'] : 1;
// Handle form submissions
$error = '';
$success = '';
// Path to config file
$config_file = __DIR__ . '/db/config.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($step === 2) {
// Save database configuration
$host = $_POST['db_host'] ?? '127.0.0.1';
$name = $_POST['db_name'] ?? 'app_database';
$user = $_POST['db_user'] ?? 'root';
$pass = $_POST['db_pass'] ?? '';
// Helper to update config file
function update_config($host, $name, $user, $pass) {
$content = "<?php\n";
$content .= "// Database configuration generated by installer\n";
$content .= "define('DB_HOST', " . var_export($host, true) . ");\n";
$content .= "define('DB_NAME', " . var_export($name, true) . ");\n";
$content .= "define('DB_USER', " . var_export($user, true) . ");\n";
$content .= "define('DB_PASS', " . var_export($pass, true) . ");\n\n";
$content .= "function db() {\n";
$content .= " static $pdo;
";
$content .= " if (!$pdo) {\n";
$content .= " try {\n";
$content .= " $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
";
$content .= " PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
";
$content .= " PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
";
$content .= " ]);\n";
$content .= " } catch (PDOException $e) {\n";
$content .= " die('Database connection failed: ' . $e->getMessage());\n";
$content .= " }\n";
$content .= " }\n";
$content .= " return $pdo;
";
$content .= "}\n\n";
// Test connection
try {
$pdo = new PDO("mysql:host=$host;dbname=$name;charset=utf8mb4", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Include existing helpers if needed, or keep it clean
$content .= "/**
* Generates the next reference number
*/\n";
$content .= "function generateRefNo($type) {\n";
$content .= " $prefix = 'IN';
";
$content .= " if ($type === 'outbound') $prefix = 'OUT';
";
$content .= " if ($type === 'internal') $prefix = 'INT';
// Generate config file content
$content = "<?php\n";
$content .= "// Database configuration generated by installer\n";
$content .= "define('DB_HOST', " . var_export($host, true) . ");\n";
$content .= "define('DB_NAME', " . var_export($name, true) . ");\n";
$content .= "define('DB_USER', " . var_export($user, true) . ");\n";
$content .= "define('DB_PASS', " . var_export($pass, true) . ");\n";
$content .= "\n";
$content .= "if (!function_exists('db')) {\n";
$content .= " function db() {\n";
$content .= " static $pdo;\n";
$content .= " if (!$pdo) {\n";
$content .= " try {\n";
$content .= " $pdo = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4', DB_USER, DB_PASS);\n";
$content .= " $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);\n";
$content .= " $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);\n";
$content .= " } catch (PDOException $e) {\n";
$content .= " die('Connection failed: ' . $e->getMessage());\n";
$content .= " }\n";
$content .= " }\n";
$content .= " return $pdo;\n";
$content .= " }\n";
$content .= "}\n";
";
$content .= " $year = date('Y');
";
$content .= " $pattern = $prefix . '-' . $year . '-%';
";
$content .= " $stmt = db()->prepare(\"SELECT ref_no FROM mailbox WHERE type = ? AND ref_no LIKE ? ORDER BY id DESC LIMIT 1\");
";
$content .= " $stmt->execute([$type, $pattern]);
";
$content .= " $last_ref = $stmt->fetchColumn();
";
$content .= " $serial = 1;
";
$content .= " if ($last_ref) {\n";
$content .= " $parts = explode('-', $last_ref);
";
$content .= " if (count($parts) === 3) {\n";
$content .= " $serial = (int)$parts[2] + 1;
";
$content .= " }
";
$content .= " }
";
$content .= " return $prefix . '-' . $year . '-' . str_pad($serial, 3, '0', STR_PAD_LEFT);
";
$content .= "}
";
return file_put_contents(__DIR__ . '/db/config.php', $content);
}
// Step 2: Database Connection Test & Save
if ($step == 2 && $_SERVER['REQUEST_METHOD'] == 'POST') {
$host = $_POST['db_host'] ?? '';
$name = $_POST['db_name'] ?? '';
$user = $_POST['db_user'] ?? '';
$pass = $_POST['db_pass'] ?? '';
try {
$pdo = new PDO("mysql:host=$host;dbname=$name;charset=utf8mb4", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if (update_config($host, $name, $user, $pass)) {
header('Location: install.php?step=3');
exit;
} else {
$error = "Failed to write to db/config.php. Check permissions.";
if (file_put_contents($config_file, $content)) {
header('Location: install.php?step=3');
exit;
} else {
$error = "Failed to write configuration file to $config_file. Please check permissions.";
}
} catch (PDOException $e) {
$error = "Connection failed: " . $e->getMessage();
}
} catch (PDOException $e) {
$error = "Connection failed: " . $e->getMessage();
}
}
} elseif ($step === 3) {
// Run migrations
if (!file_exists($config_file)) {
$error = "Configuration file not found. Please go back to Step 2.";
} else {
try {
require_once $config_file;
if (!function_exists('db')) {
throw new Exception("The 'db()' function is not defined in your config file.");
}
$pdo = db();
$migrations_dir = __DIR__ . '/db/migrations/';
$files = glob($migrations_dir . '*.sql');
sort($files);
// Step 3: Run Migrations
if ($step == 3 && isset($_POST['run_migrations'])) {
try {
$applied = 0;
$errors = [];
foreach ($files as $file) {
$sql = file_get_contents($file);
if (empty($sql)) continue;
try {
// PDO::exec might fail if there's an error in the SQL,
// but we want to continue if it's "column already exists" or "table already exists"
$pdo->exec($sql);
$applied++;
} catch (Throwable $e) {
$msg = $e->getMessage();
// Check if it's a common "already exists" error which we can safely ignore
if (strpos($msg, 'Duplicate column name') !== false ||
strpos($msg, 'Duplicate key name') !== false ||
strpos($msg, 'already exists') !== false) {
$applied++;
} else {
$errors[] = basename($file) . ": " . $msg;
}
}
}
if (empty($errors)) {
$success = "Successfully applied $applied migrations.";
header('Location: install.php?step=4');
exit;
} else {
$error = "Applied $applied migrations, but some errors occurred:<br><ul><li>" . implode('</li><li>', $errors) . "</li></ul>";
}
} catch (Throwable $e) {
$error = "Migration failed: " . $e->getMessage();
}
}
} elseif ($step === 4) {
// Final setup (Admin account)
require_once $config_file;
$pdo = db();
$migrations_dir = __DIR__ . '/db/migrations';
$files = glob($migrations_dir . '/*.sql');
sort($files);
foreach ($files as $file) {
$sql = file_get_contents($file);
// Split by semicolon to execute multiple statements if needed,
// but PDO::exec usually handles multiple statements in one call for SQL scripts.
$pdo->exec($sql);
}
$success = "Migrations completed successfully!";
header('Location: install.php?step=4');
exit;
} catch (Exception $e) {
$error = "Migration error: " . $e->getMessage();
}
}
$admin_user = $_POST['admin_user'] ?? 'admin';
$admin_pass = $_POST['admin_pass'] ?? '';
$admin_email = $_POST['admin_email'] ?? 'admin@example.com';
// Step 4: Super Admin Setup
if ($step == 4 && $_SERVER['REQUEST_METHOD'] == 'POST') {
$admin_user = $_POST['admin_user'] ?? '';
$admin_pass = $_POST['admin_pass'] ?? '';
$admin_email = $_POST['admin_email'] ?? '';
if (strlen($admin_pass) < 6) {
$error = "Password must be at least 6 characters long.";
} else {
try {
$hashed_pass = password_hash($admin_pass, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, 'admin')
ON DUPLICATE KEY UPDATE password = ?, email = ?");
$stmt->execute([$admin_user, $hashed_pass, $admin_email, $hashed_pass, $admin_email]);
if (empty($admin_user) || empty($admin_pass)) {
$error = "Username and password are required.";
} else {
try {
require_once $config_file;
$pdo = db();
$hash = password_hash($admin_pass, PASSWORD_DEFAULT);
// Set initial settings
$pdo->exec("INSERT IGNORE INTO charity_settings (id, charity_name) VALUES (1, 'Admin Panel')");
$pdo->exec("INSERT IGNORE INTO smtp_settings (id, is_enabled) VALUES (1, 0)");
// Check if user exists
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
$stmt->execute([$admin_user]);
$existing = $stmt->fetch();
if ($existing) {
$stmt = $pdo->prepare("UPDATE users SET password = ?, email = ?, is_super_admin = 1 WHERE id = ?");
$stmt->execute([$hash, $admin_email, $existing['id']]);
} else {
$stmt = $pdo->prepare("INSERT INTO users (username, password, email, is_super_admin) VALUES (?, ?, ?, 1)");
$stmt->execute([$admin_user, $hash, $admin_email]);
header('Location: install.php?step=5');
exit;
} catch (Throwable $e) {
$error = "Failed to create admin account: " . $e->getMessage();
}
// Set some default settings if table exists
$pdo->exec("INSERT IGNORE INTO charity_settings (id, site_name) VALUES (1, 'Admin Panel')");
$_SESSION['installed'] = true;
header('Location: install.php?step=5');
exit;
} catch (Exception $e) {
$error = "Admin setup error: " . $e->getMessage();
}
}
}
// UI Template
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Installer - Admin Panel</title>
<title>Installer - Step <?= $step ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background-color: #f8f9fa; }
.install-card { max-width: 600px; margin: 50px auto; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); }
.step-indicator { display: flex; justify-content: space-between; margin-bottom: 30px; }
.step-dot { width: 30px; height: 30px; border-radius: 50%; background: #dee2e6; display: flex; align-items: center; justify-content: center; font-weight: bold; color: white; }
.step-dot.active { background: #0d6efd; }
.step-dot.completed { background: #198754; }
body { background-color: #f8f9fa; padding-top: 50px; }
.installer-card { max-width: 600px; margin: 0 auto; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); border-radius: 1rem; }
.step-indicator { margin-bottom: 2rem; }
.step-dot { width: 30px; height: 30px; border-radius: 50%; background: #dee2e6; display: inline-block; text-align: center; line-height: 30px; font-weight: bold; margin: 0 5px; }
.step-dot.active { background: #0d6efd; color: white; }
.step-dot.completed { background: #198754; color: white; }
</style>
</head>
<body>
<div class="container">
<div class="card install-card">
<div class="card installer-card">
<div class="card-body p-5">
<h2 class="text-center mb-4">Installation</h2>
<h2 class="text-center mb-4">Installer</h2>
<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 class="step-dot <?php echo $step >= 5 ? 'active' : ''; ?>">5</div>
<div class="text-center step-indicator">
<?php for ($i = 1; $i <= 5; $i++): ?>
<span class="step-dot <?= $i == $step ? 'active' : ($i < $step ? 'completed' : '') ?>"><?= $i ?></span>
<?php endfor; ?>
</div>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo $error; ?></div>
<div class="alert alert-danger"><?= $error ?></div>
<?php endif; ?>
<?php if ($step == 1): ?>
<?php if ($success): ?>
<div class="alert alert-success"><?= $success ?></div>
<?php endif; ?>
<?php if ($step === 1): ?>
<h4>Step 1: System Requirements</h4>
<ul class="list-group mb-4">
<?php
$reqs = [
'PHP Version >= 7.4' => version_compare(PHP_VERSION, '7.4.0', '>='),
'PDO MySQL Extension' => extension_loaded('pdo_mysql'),
'Config File Writable' => is_writable(__DIR__ . '/db') || is_writable($config_file),
'Uploads Directory Writable' => is_writable(__DIR__ . '/uploads')
];
$all_ok = true;
foreach ($reqs as $label => $ok):
if (!$ok) $all_ok = false;
?>
<?php foreach ($requirements as $name => $met): ?>
<li class="list-group-item d-flex justify-content-between align-items-center">
<?php echo $label; ?>
<span class="badge <?php echo $ok ? 'bg-success' : 'bg-danger'; ?> rounded-pill">
<?php echo $ok ? 'OK' : 'Fail'; ?>
</span>
<?= $name ?>
<?php if ($met): ?>
<span class="badge bg-success rounded-pill">OK</span>
<?php else: ?>
<span class="badge bg-danger rounded-pill">Failed</span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php if ($all_ok): ?>
<a href="install.php?step=2" class="btn btn-primary w-100">Continue to Database Setup</a>
<?php else:
// This is a deliberate newline for readability in the code, not an escaping issue.
?>
<div class="alert alert-warning">Please fix the issues above to continue.</div>
<button onclick="window.location.reload()" class="btn btn-secondary w-100">Check Again</button>
<?php endif; ?>
<div class="d-grid">
<?php if ($all_requirements_met): ?>
<a href="install.php?step=2" class="btn btn-primary">Next: Database Config</a>
<?php else: ?>
<button class="btn btn-secondary" disabled>Fix requirements to continue</button>
<?php endif; ?>
</div>
<?php elseif ($step == 2):
// Deliberate newline for readability.
?>
<h4>Step 2: Database Configuration</h4>
<?php elseif ($step === 2): ?>
<h4>Step 2: Database Connection</h4>
<form method="POST">
<div class="mb-3">
<label class="form-label">DB Host</label>
<label class="form-label">Database Host</label>
<input type="text" name="db_host" class="form-control" value="127.0.0.1" required>
</div>
<div class="mb-3">
<label class="form-label">DB Name</label>
<label class="form-label">Database Name</label>
<input type="text" name="db_name" class="form-control" value="app_database" required>
</div>
<div class="mb-3">
<label class="form-label">DB User</label>
<label class="form-label">Database User</label>
<input type="text" name="db_user" class="form-control" value="root" required>
</div>
<div class="mb-3">
<label class="form-label">DB Password</label>
<label class="form-label">Database Password</label>
<input type="password" name="db_pass" class="form-control">
</div>
<button type="submit" class="btn btn-primary w-100">Test & Save Config</button>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Test & Save Config</button>
</div>
</form>
<?php elseif ($step == 3):
// Deliberate newline for readability.
?>
<?php elseif ($step === 3): ?>
<h4>Step 3: Database Migrations</h4>
<p>Ready to set up the database tables and initial data.</p>
<p>We will now run the SQL scripts to set up your database tables.</p>
<form method="POST">
<button type="submit" name="run_migrations" class="btn btn-primary w-100">Run Migrations Now</button>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Run Migrations</button>
</div>
</form>
<?php elseif ($step == 4):
// Deliberate newline for readability.
?>
<h4>Step 4: Super Admin Setup</h4>
<?php elseif ($step === 4): ?>
<h4>Step 4: Admin Account</h4>
<form method="POST">
<div class="mb-3">
<label class="form-label">Admin Username</label>
@ -281,36 +255,28 @@ if ($step == 4 && $_SERVER['REQUEST_METHOD'] == 'POST') {
</div>
<div class="mb-3">
<label class="form-label">Admin Email</label>
<input type="email" name="admin_email" class="form-control" placeholder="admin@example.com">
<input type="email" name="admin_email" class="form-control" value="admin@example.com" required>
</div>
<div class="mb-3">
<label class="form-label">Admin Password</label>
<input type="password" name="admin_pass" class="form-control" required>
<input type="password" name="admin_pass" class="form-control" required minlength="6">
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Complete Setup</button>
</div>
<button type="submit" class="btn btn-primary w-100">Complete Installation</button>
</form>
<?php elseif ($step == 5):
// Deliberate newline for readability.
?>
<?php elseif ($step === 5): ?>
<div class="text-center">
<div class="mb-4">
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" class="bi bi-check-circle-fill text-success" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
</svg>
<h4 class="text-success">Installation Complete!</h4>
<p>The system is ready to use. For security, please delete <b>install.php</b> or rename it.</p>
<div class="d-grid">
<a href="login.php" class="btn btn-primary">Go to Login</a>
</div>
<h4>Success!</h4>
<p>The application has been installed successfully.</p>
<div class="alert alert-warning small">
<strong>Security Warning:</strong> Please delete the <code>install.php</code> file from your server.
</div>
<a href="login.php" class="btn btn-primary w-100">Go to Login</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</body>
</html>