add login page

This commit is contained in:
Flatlogic Bot 2026-04-07 07:11:44 +00:00
parent 6f28e66799
commit be93531517
13 changed files with 855 additions and 77 deletions

View File

@ -1,6 +1,8 @@
<?php ob_start(); ?>
<?php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_login();
$page = $_GET['page'] ?? 'dashboard';
$action = $_GET['action'] ?? '';

View File

@ -7,7 +7,8 @@ if (!isset($pdo)) {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'save_landing') {
$settings = $_POST['settings'] ?? [];
$stmt = $pdo->prepare("UPDATE landing_settings SET value_en = :en, value_ar = :ar WHERE setting_key = :key");
// Save text settings
$stmt = $pdo->prepare("INSERT INTO landing_settings (setting_key, value_en, value_ar) VALUES (:key, :en, :ar) ON DUPLICATE KEY UPDATE value_en = :en, value_ar = :ar");
foreach ($settings as $key => $values) {
$stmt->execute([
'key' => $key,
@ -16,6 +17,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
]);
}
// Save image settings
$upload_dir = __DIR__ . '/assets/images/uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0775, true);
}
$sections = ['hero', 'courses', 'subjects', 'flow', 'plans'];
foreach ($sections as $sec) {
if (isset($_FILES['images']['error'][$sec]) && $_FILES['images']['error'][$sec] === UPLOAD_ERR_OK) {
$filename = time() . '_' . basename($_FILES['images']['name'][$sec]);
$target_file = $upload_dir . $filename;
if (move_uploaded_file($_FILES['images']['tmp_name'][$sec], $target_file)) {
$picture = 'assets/images/uploads/' . $filename;
$stmt->execute([
'key' => $sec . '_image',
'en' => $picture,
'ar' => $picture
]);
}
}
}
header('Location: ' . app_url('admin.php', ['page' => 'landing', 'saved' => 1]));
exit;
}
@ -28,18 +51,43 @@ foreach ($all_settings as $row) {
$settings[$row['setting_key']] = $row;
}
$fields = [
'hero_eyebrow' => ['label' => 'Hero Eyebrow / النص الصغير في البداية', 'type' => 'text'],
'hero_title' => ['label' => 'Hero Title / العنوان الرئيسي', 'type' => 'textarea'],
'hero_desc' => ['label' => 'Hero Description / الوصف', 'type' => 'textarea'],
'courses_eyebrow' => ['label' => 'Courses Eyebrow / نص قسم الدورات', 'type' => 'text'],
'courses_title' => ['label' => 'Courses Title / عنوان قسم الدورات', 'type' => 'textarea'],
'subjects_eyebrow' => ['label' => 'Subjects Eyebrow / نص قسم المواد', 'type' => 'text'],
'subjects_title' => ['label' => 'Subjects Title / عنوان قسم المواد', 'type' => 'textarea'],
'flow_eyebrow' => ['label' => 'Delivery Flow Eyebrow / نص قسم مسار التسليم', 'type' => 'text'],
'flow_title' => ['label' => 'Delivery Flow Title / عنوان قسم مسار التسليم', 'type' => 'textarea'],
'plans_eyebrow' => ['label' => 'Plans Eyebrow / نص قسم الخطط', 'type' => 'text'],
'plans_title' => ['label' => 'Plans Title / عنوان قسم الخطط', 'type' => 'textarea']
$sections = [
'hero' => [
'label' => 'Hero Section / القسم الأول',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
'desc' => ['label' => 'Description / الوصف', 'type' => 'textarea'],
]
],
'courses' => [
'label' => 'Courses Section / قسم الدورات',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
],
'subjects' => [
'label' => 'Subjects Section / قسم المواد',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
],
'flow' => [
'label' => 'Delivery Flow Section / قسم مسار التسليم',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
],
'plans' => [
'label' => 'Plans Section / قسم الخطط',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
]
];
?>
@ -48,7 +96,7 @@ $fields = [
<div>
<span class="eyebrow"><?= h(t('Settings', 'الإعدادات')) ?></span>
<h1 class="section-title mb-2"><?= h(t('Landing Page Settings', 'إعدادات الصفحة الرئيسية')) ?></h1>
<p class="text-secondary mb-0"><?= h(t('Customize the content of the landing page in English and Arabic.', 'تخصيص محتوى الصفحة الرئيسية باللغتين الإنجليزية والعربية.')) ?></p>
<p class="text-secondary mb-0"><?= h(t('Customize the content of the landing page in English and Arabic. Grouped by sections.', 'تخصيص محتوى الصفحة الرئيسية باللغتين الإنجليزية والعربية. مجمعة حسب الأقسام.')) ?></p>
</div>
</div>
@ -56,51 +104,80 @@ $fields = [
<div class="alert alert-success"><?= h(t('Landing page settings updated successfully.', 'تم تحديث إعدادات الصفحة الرئيسية بنجاح.')) ?></div>
<?php endif; ?>
<div class="panel-card" style="max-width: 800px;">
<form method="POST" action="">
<div class="panel-card" style="max-width: 900px;">
<form method="POST" action="" enctype="multipart/form-data">
<input type="hidden" name="action" value="save_landing">
<ul class="nav nav-tabs mb-4" id="langTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="en-tab" data-bs-toggle="tab" data-bs-target="#en" type="button" role="tab">English</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="ar-tab" data-bs-toggle="tab" data-bs-target="#ar" type="button" role="tab">العربية</button>
</li>
</ul>
<div class="tab-content" id="langTabsContent">
<!-- English Tab -->
<div class="tab-pane fade show active" id="en" role="tabpanel">
<?php foreach ($fields as $key => $field): ?>
<div class="mb-3">
<label class="form-label fw-bold"><?= h($field['label']) ?></label>
<?php if ($field['type'] === 'textarea'): ?>
<textarea class="form-control" name="settings[<?= $key ?>][en]" rows="2"><?= h($settings[$key]['value_en'] ?? '') ?></textarea>
<?php else: ?>
<input type="text" class="form-control" name="settings[<?= $key ?>][en]" value="<?= h($settings[$key]['value_en'] ?? '') ?>">
<?php endif; ?>
<div class="accordion" id="sectionsAccordion">
<?php foreach ($sections as $sec_key => $section): ?>
<div class="accordion-item mb-3 border rounded">
<h2 class="accordion-header" id="heading-<?= $sec_key ?>">
<button class="accordion-button <?= $sec_key === 'hero' ? '' : 'collapsed' ?> bg-light fw-bold" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-<?= $sec_key ?>" aria-expanded="<?= $sec_key === 'hero' ? 'true' : 'false' ?>" aria-controls="collapse-<?= $sec_key ?>">
<?= h($section['label']) ?>
</button>
</h2>
<div id="collapse-<?= $sec_key ?>" class="accordion-collapse collapse <?= $sec_key === 'hero' ? 'show' : '' ?>" aria-labelledby="heading-<?= $sec_key ?>" data-bs-parent="#sectionsAccordion">
<div class="accordion-body">
<!-- Image Upload for Section -->
<div class="mb-4 p-3 border rounded bg-white">
<label class="form-label fw-bold"><?= h(t('Section Image', 'صورة القسم')) ?></label>
<?php if (!empty($settings[$sec_key . '_image']['value_en'])): ?>
<div class="mb-2">
<img src="<?= h($settings[$sec_key . '_image']['value_en']) ?>" alt="Current Image" style="max-height: 100px; border-radius: 8px;">
</div>
<?php endif; ?>
<input type="file" class="form-control" name="images[<?= $sec_key ?>]" accept="image/*">
<small class="text-muted"><?= h(t('Upload a new image to replace the current one.', 'قم برفع صورة جديدة لاستبدال الصورة الحالية.')) ?></small>
</div>
<ul class="nav nav-tabs mb-3" id="tab-<?= $sec_key ?>" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#<?= $sec_key ?>-en" type="button" role="tab">English</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#<?= $sec_key ?>-ar" type="button" role="tab">العربية</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="<?= $sec_key ?>-en" role="tabpanel">
<?php foreach ($section['fields'] as $field_key => $field): ?>
<?php $full_key = $sec_key . '_' . $field_key; ?>
<div class="mb-3">
<label class="form-label fw-bold text-secondary"><?= h($field['label']) ?></label>
<?php if ($field['type'] === 'textarea'): ?>
<textarea class="form-control" name="settings[<?= $full_key ?>][en]" rows="2"><?= h($settings[$full_key]['value_en'] ?? '') ?></textarea>
<?php else: ?>
<input type="text" class="form-control" name="settings[<?= $full_key ?>][en]" value="<?= h($settings[$full_key]['value_en'] ?? '') ?>">
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<div class="tab-pane fade" id="<?= $sec_key ?>-ar" role="tabpanel" dir="rtl">
<?php foreach ($section['fields'] as $field_key => $field): ?>
<?php $full_key = $sec_key . '_' . $field_key; ?>
<div class="mb-3">
<label class="form-label fw-bold text-secondary"><?= h($field['label']) ?></label>
<?php if ($field['type'] === 'textarea'): ?>
<textarea class="form-control" name="settings[<?= $full_key ?>][ar]" rows="2"><?= h($settings[$full_key]['value_ar'] ?? '') ?></textarea>
<?php else: ?>
<input type="text" class="form-control" name="settings[<?= $full_key ?>][ar]" value="<?= h($settings[$full_key]['value_ar'] ?? '') ?>">
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- Arabic Tab -->
<div class="tab-pane fade" id="ar" role="tabpanel" dir="rtl">
<?php foreach ($fields as $key => $field): ?>
<div class="mb-3">
<label class="form-label fw-bold"><?= h($field['label']) ?></label>
<?php if ($field['type'] === 'textarea'): ?>
<textarea class="form-control" name="settings[<?= $key ?>][ar]" rows="2"><?= h($settings[$key]['value_ar'] ?? '') ?></textarea>
<?php else: ?>
<input type="text" class="form-control" name="settings[<?= $key ?>][ar]" value="<?= h($settings[$key]['value_ar'] ?? '') ?>">
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-dark"><?= h(t('Save Settings', 'حفظ الإعدادات')) ?></button>
<button type="submit" class="btn btn-dark btn-lg px-4"><?= h(t('Save All Sections', 'حفظ كل الأقسام')) ?></button>
</div>
</form>
</div>
</div>

View File

@ -29,6 +29,7 @@ $errors = [];
$form = [
'full_name' => trim((string) ($_POST['full_name'] ?? '')),
'email' => trim((string) ($_POST['email'] ?? '')),
'password' => (string) ($_POST['password'] ?? ''),
'whatsapp' => trim((string) ($_POST['whatsapp'] ?? '')),
'preferred_language' => (string) ($_POST['preferred_language'] ?? current_lang()),
'billing_cycle' => $cycle,
@ -40,6 +41,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($form['full_name'] === '' || strlen($form['full_name']) < 2) {
$errors[] = t('Please enter a valid full name.', 'يرجى إدخال اسم كامل صحيح.');
}
if (empty($form['password']) || strlen($form['password']) < 6) {
$errors[] = t('Password must be at least 6 characters.', 'يجب أن تتكون كلمة المرور من 6 أحرف على الأقل.');
}
if (!filter_var($form['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = t('Please enter a valid email address.', 'يرجى إدخال بريد إلكتروني صحيح.');
}
@ -55,6 +59,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = create_subscription([
'full_name' => $form['full_name'],
'email' => $form['email'],
'password' => password_hash($form['password'], PASSWORD_DEFAULT),
'whatsapp' => $form['whatsapp'],
'preferred_language' => $form['preferred_language'],
'plan_key' => $plan['key'],
@ -195,6 +200,10 @@ render_nav('pricing.php');
<label class="form-label" for="email"><?= h(t('Email', 'البريد الإلكتروني')) ?></label>
<input class="form-control" id="email" type="email" name="email" required value="<?= h($form['email']) ?>">
</div>
<div class="col-md-6">
<label class="form-label" for="password"><?= h(t('Password', 'كلمة المرور')) ?></label>
<input class="form-control" id="password" type="password" name="password" required>
</div>
<div class="col-md-6">
<label class="form-label" for="whatsapp"><?= h(t('WhatsApp', 'واتساب')) ?></label>
<input class="form-control" id="whatsapp" name="whatsapp" placeholder="+968 9000 0000" required value="<?= h($form['whatsapp']) ?>">

View File

@ -53,7 +53,7 @@ function get_landing_settings(): array {
return $settings;
}
function landing_setting(string $key, string $default_en, string $default_ar): string {
function landing_setting(string $key, string $default_en, string $default_ar = ""): string {
$settings = get_landing_settings();
$lang = current_lang();
if (isset($settings[$key])) {
@ -647,6 +647,14 @@ function render_nav(string $active = ''): void
<a class="btn btn-outline-dark <?= current_lang() === 'en' ? 'active' : '' ?>" href="<?= h(page_lang_link('en')) ?>">EN</a>
<a class="btn btn-outline-dark <?= current_lang() === 'ar' ? 'active' : '' ?>" href="<?= h(page_lang_link('ar')) ?>">AR</a>
</div>
<div class="d-flex gap-2 ms-2">
<?php if (!empty($_SESSION['user_id'])): ?>
<a href="<?= h(app_url('profile.php')) ?>" class="btn btn-outline-primary btn-sm px-3"><?= h(t('Profile', 'حسابي')) ?></a>
<a href="<?= h(app_url('logout.php')) ?>" class="btn btn-outline-danger btn-sm px-3"><?= h(t('Logout', 'خروج')) ?></a>
<?php else: ?>
<a href="<?= h(app_url('login.php')) ?>" class="btn btn-outline-dark btn-sm px-3"><?= h(t('Login', 'دخول')) ?></a>
<?php endif; ?>
</div>
</div>
</div>
</div>

16
includes/auth.php Normal file
View File

@ -0,0 +1,16 @@
<?php
require_once __DIR__ . '/app.php';
function require_login() {
if (empty($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
}
function get_logged_in_user() {
if (empty($_SESSION['user_id'])) return null;
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}

View File

@ -36,6 +36,9 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
</div>
</div>
<div class="col-lg-5">
<?php if ($hero_img = landing_setting('hero_image', '')): ?>
<img src="<?= h($hero_img) ?>" class="img-fluid rounded shadow-lg w-100" style="object-fit: cover; max-height: 500px;" alt="Hero Image">
<?php else: ?>
<div class="panel-card">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="h5 mb-0"><?= h(t('Today inside the platform', 'اليوم داخل المنصة')) ?></h2>
@ -66,6 +69,7 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
</div>
<a class="btn btn-outline-dark w-100 mt-3" href="<?= h(app_url('dashboard.php')) ?>"><?= h(t('Open student dashboard', 'افتح لوحة الطالب')) ?></a>
</div>
<?php endif; ?>
</div>
</div>
</div>
@ -74,12 +78,24 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
<section class="py-5 border-bottom bg-light">
<div class="container">
<?php if ($courses_img = landing_setting('courses_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('courses_eyebrow', 'Featured Courses', 'الدورات المميزة')) ?></span>
<h2 class="section-title mb-0"><?= h(landing_setting('courses_title', 'Enroll in specialized short courses designed to accelerate your skills.', 'سجل في دورات قصيرة متخصصة لتسريع تطوير مهاراتك.')) ?></h2>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($courses_img) ?>" class="img-fluid rounded shadow-sm" alt="Courses" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header">
<div>
<span class="eyebrow"><?= h(landing_setting('courses_eyebrow', 'Featured Courses', 'الدورات المميزة')) ?></span>
<h2 class="section-title"><?= h(landing_setting('courses_title', 'Enroll in specialized short courses designed to accelerate your skills.', 'سجل في دورات قصيرة متخصصة لتسريع تطوير مهاراتك.')) ?></h2>
</div>
</div>
<?php endif; ?>
<div class="row g-4 mt-3">
<?php foreach ($courses as $course): ?>
<div class="col-lg-4">
@ -121,6 +137,18 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
</section>
<section class="py-5 border-bottom bg-white">
<div class="container">
<?php if ($subjects_img = landing_setting('subjects_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('subjects_eyebrow', 'Featured subjects', 'المواد المميزة')) ?></span>
<h2 class="section-title mb-2"><?= h(landing_setting('subjects_title', 'Separate pages for catalog, detail, checkout, and dashboards.', 'صفحات منفصلة للكتالوج والتفاصيل والدفع ولوحات التحكم.')) ?></h2>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('catalog.php')) ?>"><?= h(t('See all subjects', 'عرض جميع المواد')) ?></a>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($subjects_img) ?>" class="img-fluid rounded shadow-sm" alt="Subjects" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header">
<div>
<span class="eyebrow"><?= h(landing_setting('subjects_eyebrow', 'Featured subjects', 'المواد المميزة')) ?></span>
@ -128,6 +156,7 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
</div>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('catalog.php')) ?>"><?= h(t('See all subjects', 'عرض جميع المواد')) ?></a>
</div>
<?php endif; ?>
<div class="row g-3 mt-1">
<?php foreach (array_slice($subjects, 0, 3) as $subject): ?>
<div class="col-lg-4">
@ -151,12 +180,24 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
<section class="py-5 border-bottom">
<div class="container">
<?php if ($flow_img = landing_setting('flow_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('flow_eyebrow', 'Delivery flow', 'مسار التسليم')) ?></span>
<h2 class="section-title mb-0"><?= h(landing_setting('flow_title', 'One thin slice from discovery to live access.', 'شريحة رشيقة من الاكتشاف حتى الوصول المباشر.')) ?></h2>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($flow_img) ?>" class="img-fluid rounded shadow-sm" alt="Delivery Flow" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header mb-4">
<div>
<span class="eyebrow"><?= h(landing_setting('flow_eyebrow', 'Delivery flow', 'مسار التسليم')) ?></span>
<h2 class="section-title"><?= h(landing_setting('flow_title', 'One thin slice from discovery to live access.', 'شريحة رشيقة من الاكتشاف حتى الوصول المباشر.')) ?></h2>
</div>
</div>
<?php endif; ?>
<div class="row g-3">
<div class="col-md-4"><div class="workflow-card"><span class="step-index">01</span><h3 class="h6"><?= h(t('Browse plans', 'تصفح الخطط')) ?></h3><p><?= h(t('Students compare monthly or yearly plans and pick the right access model.', 'يقارن الطلاب بين الخطط الشهرية والسنوية ويختارون نموذج الوصول المناسب.')) ?></p></div></div>
<div class="col-md-4"><div class="workflow-card"><span class="step-index">02</span><h3 class="h6"><?= h(t('Capture checkout details', 'جمع بيانات الدفع')) ?></h3><p><?= h(t('The checkout form records student identity, language preference, and WhatsApp opt-in.', 'يسجل نموذج الدفع هوية الطالب وتفضيل اللغة وخيار الاشتراك في واتساب.')) ?></p></div></div>
@ -167,6 +208,18 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
<section class="py-5 bg-white">
<div class="container">
<?php if ($plans_img = landing_setting('plans_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('plans_eyebrow', 'Plans', 'الخطط')) ?></span>
<h2 class="section-title mb-2"><?= h(landing_setting('plans_title', 'Plan-based access for a shared marketplace.', 'وصول قائم على الخطط لمنصة مشتركة.')) ?></h2>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Open pricing', 'افتح التسعير')) ?></a>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($plans_img) ?>" class="img-fluid rounded shadow-sm" alt="Plans" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header mb-4">
<div>
<span class="eyebrow"><?= h(landing_setting('plans_eyebrow', 'Plans', 'الخطط')) ?></span>
@ -174,6 +227,7 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
</div>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Open pricing', 'افتح التسعير')) ?></a>
</div>
<?php endif; ?>
<div class="row g-3">
<?php foreach ($plans as $plan): ?>
<div class="col-lg-4">

65
login.php Normal file
View File

@ -0,0 +1,65 @@
<?php
require_once __DIR__ . '/includes/auth.php';
if (!empty($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if ($email && $password) {
$stmt = db()->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_role'] = $user['role'];
header('Location: admin.php');
exit;
} else {
$error = t('Invalid email or password.', 'البريد الإلكتروني أو كلمة المرور غير صحيحة.');
}
} else {
$error = t('Please fill in all fields.', 'يرجى تعبئة جميع الحقول.');
}
}
render_head(t('Login', 'تسجيل الدخول'));
render_nav('login.php');
?>
<main class="py-5 bg-light min-vh-100 d-flex align-items-center">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-5 col-lg-4">
<div class="card border-0 shadow-sm" style="border-radius: 1rem;">
<div class="card-body p-4 p-md-5">
<h1 class="h4 mb-4 text-center fw-bold"><?= h(t('Welcome back', 'مرحباً بعودتك')) ?></h1>
<?php if ($error): ?>
<div class="alert alert-danger py-2 small"><?= h($error) ?></div>
<?php endif; ?>
<form method="post" action="login.php">
<div class="mb-3">
<label class="form-label small fw-semibold"><?= h(t('Email address', 'البريد الإلكتروني')) ?></label>
<input type="email" name="email" class="form-control form-control-lg" required autofocus>
</div>
<div class="mb-4">
<div class="d-flex justify-content-between align-items-center mb-1">
<label class="form-label small fw-semibold mb-0"><?= h(t('Password', 'كلمة المرور')) ?></label>
<a href="reset_password.php" class="small text-decoration-none"><?= h(t('Forgot?', 'نسيت؟')) ?></a>
</div>
<input type="password" name="password" class="form-control form-control-lg" required>
</div>
<button type="submit" class="btn btn-dark btn-lg w-100"><?= h(t('Log In', 'تسجيل الدخول')) ?></button>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
<?php render_footer(); ?>

8
logout.php Normal file
View File

@ -0,0 +1,8 @@
<?php
require_once __DIR__ . '/includes/auth.php';
session_unset();
session_destroy();
header('Location: login.php');
exit;

187
patch_admin.py Normal file
View File

@ -0,0 +1,187 @@
import re
import os
admin_landing_path = 'admin_landing.php'
with open(admin_landing_path, 'r', encoding='utf-8') as f:
admin_landing_content = f.read()
# Replace the save logic and the form rendering logic in admin_landing.php
new_php_logic = """
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'save_landing') {
$settings = $_POST['settings'] ?? [];
# Save text settings
$stmt = $pdo->prepare("INSERT INTO landing_settings (setting_key, value_en, value_ar) VALUES (:key, :en, :ar) ON DUPLICATE KEY UPDATE value_en = :en, value_ar = :ar");
foreach ($settings as $key => $values) {
$stmt->execute([
'key' => $key,
'en' => $values['en'] ?? '',
'ar' => $values['ar'] ?? ''
]);
}
# Save image settings
$upload_dir = __DIR__ . '/assets/images/uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0775, true);
}
$sections = ['hero', 'courses', 'subjects', 'flow', 'plans'];
foreach ($sections as $sec) {
if (isset($_FILES['images']['error'][$sec]) && $_FILES['images']['error'][$sec] === UPLOAD_ERR_OK) {
$filename = time() . '_' . basename($_FILES['images']['name'][$sec]);
$target_file = $upload_dir . $filename;
if (move_uploaded_file($_FILES['images']['tmp_name'][$sec], $target_file)) {
$picture = 'assets/images/uploads/' . $filename;
$stmt->execute([
'key' => $sec . '_image',
'en' => $picture,
'ar' => $picture
]);
}
}
}
header('Location: ' . app_url('admin.php', ['page' => 'landing', 'saved' => 1]));
exit;
}
$stmt = $pdo->query("SELECT setting_key, value_en, value_ar FROM landing_settings");
$all_settings = $stmt->fetchAll(PDO::FETCH_ASSOC);
$settings = [];
foreach ($all_settings as $row) {
$settings[$row['setting_key']] = $row;
}
$sections = [
'hero' => [
'label' => 'Hero Section / القسم الأول',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
'desc' => ['label' => 'Description / الوصف', 'type' => 'textarea'],
]
],
'courses' => [
'label' => 'Courses Section / قسم الدورات',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
],
'subjects' => [
'label' => 'Subjects Section / قسم المواد',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
],
'flow' => [
'label' => 'Delivery Flow Section / قسم مسار التسليم',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
],
'plans' => [
'label' => 'Plans Section / قسم الخطط',
'fields' => [
'eyebrow' => ['label' => 'Eyebrow / النص الصغير', 'type' => 'text'],
'title' => ['label' => 'Title / العنوان الرئيسي', 'type' => 'textarea'],
]
]
];
"""
new_html_form = """
<div class="panel-card" style="max-width: 900px;">
<form method="POST" action="" enctype="multipart/form-data">
<input type="hidden" name="action" value="save_landing">
<div class="accordion" id="sectionsAccordion">
<?php foreach ($sections as $sec_key => $section): ?>
<div class="accordion-item mb-3 border rounded">
<h2 class="accordion-header" id="heading-<?= $sec_key ?>">
<button class="accordion-button <?= $sec_key === 'hero' ? '' : 'collapsed' ?> bg-light fw-bold" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-<?= $sec_key ?>" aria-expanded="<?= $sec_key === 'hero' ? 'true' : 'false' ?>" aria-controls="collapse-<?= $sec_key ?>">
<?= h($section['label']) ?>
</button>
</h2>
<div id="collapse-<?= $sec_key ?>" class="accordion-collapse collapse <?= $sec_key === 'hero' ? 'show' : '' ?>" aria-labelledby="heading-<?= $sec_key ?>" data-bs-parent="#sectionsAccordion">
<div class="accordion-body">
<!-- Image Upload for Section -->
<div class="mb-4 p-3 border rounded bg-white">
<label class="form-label fw-bold"><?= h(t('Section Image', 'صورة القسم')) ?></label>
<?php if (!empty($settings[$sec_key . '_image']['value_en'])): ?>
<div class="mb-2">
<img src="<?= h($settings[$sec_key . '_image']['value_en']) ?>" alt="Current Image" style="max-height: 100px; border-radius: 8px;">
</div>
<?php endif; ?>
<input type="file" class="form-control" name="images[<?= $sec_key ?>]" accept="image/*">
<small class="text-muted"><?= h(t('Upload a new image to replace the current one.', 'قم برفع صورة جديدة لاستبدال الصورة الحالية.')) ?></small>
</div>
<ul class="nav nav-tabs mb-3" id="tab-<?= $sec_key ?>" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#<?= $sec_key ?>-en" type="button" role="tab">English</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#<?= $sec_key ?>-ar" type="button" role="tab">العربية</button>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="<?= $sec_key ?>-en" role="tabpanel">
<?php foreach ($section['fields'] as $field_key => $field): ?>
<?php $full_key = $sec_key . '_' . $field_key; ?>
<div class="mb-3">
<label class="form-label fw-bold text-secondary"><?= h($field['label']) ?></label>
<?php if ($field['type'] === 'textarea'): ?>
<textarea class="form-control" name="settings[<?= $full_key ?>][en]" rows="2"><?= h($settings[$full_key]['value_en'] ?? '') ?></textarea>
<?php else: ?>
<input type="text" class="form-control" name="settings[<?= $full_key ?>][en]" value="<?= h($settings[$full_key]['value_en'] ?? '') ?>">
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<div class="tab-pane fade" id="<?= $sec_key ?>-ar" role="tabpanel" dir="rtl">
<?php foreach ($section['fields'] as $field_key => $field): ?>
<?php $full_key = $sec_key . '_' . $field_key; ?>
<div class="mb-3">
<label class="form-label fw-bold text-secondary"><?= h($field['label']) ?></label>
<?php if ($field['type'] === 'textarea'): ?>
<textarea class="form-control" name="settings[<?= $full_key ?>][ar]" rows="2"><?= h($settings[$full_key]['value_ar'] ?? '') ?></textarea>
<?php else: ?>
<input type="text" class="form-control" name="settings[<?= $full_key ?>][ar]" value="<?= h($settings[$full_key]['value_ar'] ?? '') ?>">
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-dark btn-lg px-4"><?= h(t('Save All Sections', 'حفظ كل الأقسام')) ?></button>
</div>
</form>
</div>
"""
# Now replace the parts in admin_landing.php
# 1. from `if ($_SERVER['REQUEST_METHOD']` to `$fields = [...];`
php_pattern = re.compile(r"if \(\$_SERVER\[\'REQUEST_METHOD\'\].*?\];\s*", re.DOTALL)
admin_landing_content = php_pattern.sub(new_php_logic, admin_landing_content)
# 2. from `<div class="panel-card"` to `</div>` at the end
html_pattern = re.compile(r"<div class=\"panel-card\".*?</div>\s*$", re.DOTALL)
admin_landing_content = html_pattern.sub(new_html_form, admin_landing_content)
with open(admin_landing_path, 'w', encoding='utf-8') as f:
f.write(admin_landing_content)

View File

@ -2,28 +2,21 @@
$file = 'checkout.php';
$content = file_get_contents($file);
// check course limits
$limit_check = <<<PHP
if ($courseIdForView > 0) {
$course = db()->query("SELECT * FROM courses WHERE id = $courseIdForView")->fetch();
if ($course) {
if (! $course['registration_open']) {
die(t('Registration for this course is currently closed.', 'التسجيل في هذه الدورة مغلق حالياً.'));
}
if ($course['max_students'] > 0) {
$enrolled = db()->query("SELECT COUNT(*) FROM course_students WHERE course_id = $courseIdForView")->fetchColumn();
if ($enrolled >= $course['max_students']) {
die(t('This course has reached its maximum number of students.', 'لقد وصلت هذه الدورة إلى الحد الأقصى لعدد الطلاب.'));
}
}
} else {
die('Course not found');
}
}
PHP;
$search1 = "'email' => trim((string) (\$_POST['email'] ?? '')),";
$replace1 = $search1 . "\n 'password' => (string) (\$_POST['password'] ?? ''),";
$content = str_replace($search1, $replace1, $content);
$content = preg_replace("/if \(\\$courseIdForView > 0\) \{\\s*\\\$course = db\(\)->query\(\"SELECT \* FROM courses WHERE id = \\\$courseIdForView\"\)->fetch\(\);\\s*\}", $limit_check, $content);
$search2 = "if (!filter_var(\$form['email'], FILTER_VALIDATE_EMAIL)) {";
$replace2 = "if (empty(\$form['password']) || strlen(\$form['password']) < 6) {\n \$errors[] = t('Password must be at least 6 characters.', 'يجب أن تتكون كلمة المرور من 6 أحرف على الأقل.');\n }\n " . $search2;
$content = str_replace($search2, $replace2, $content);
$search3 = "'email' => \$form['email'],";
$replace3 = "'email' => \$form['email'],\n 'password' => password_hash(\$form['password'], PASSWORD_DEFAULT),";
$content = str_replace($search3, $replace3, $content);
$search4 = '/<div class="col-md-6">\s*<label class="form-label" for="email">.*?<\/div>/s';
$replace4 = '$0'."\n".' <div class="col-md-6">'."\n".' <label class="form-label" for="password"><?= h(t(\'Password\', \'كلمة المرور\')) ?></label>'."\n".' <input class="form-control" id="password" type="password" name="password" required>'."\n".' </div>';
$content = preg_replace($search4, $replace4, $content);
file_put_contents($file, $content);
echo "Done\n";
echo "checkout.php patched\n";

161
patch_index.py Normal file
View File

@ -0,0 +1,161 @@
with open('index.php', 'r', encoding='utf-8') as f:
content = f.read()
# 1. Hero Section
# find the panel-card div and everything inside it up to </section>
# It starts with:
# <div class="col-lg-5">
# <div class="panel-card">
old_hero_col = """ <div class="col-lg-5">
<div class="panel-card">"""
new_hero_col = """ <div class="col-lg-5">
<?php if ($hero_img = landing_setting('hero_image', '')): ?>
<img src="<?= h($hero_img) ?>" class="img-fluid rounded shadow-lg w-100" style="object-fit: cover; max-height: 500px;" alt="Hero Image">
<?php else: ?>
<div class="panel-card">"""
content = content.replace(old_hero_col, new_hero_col)
old_hero_end = """ <a class="btn btn-outline-dark w-100 mt-3" href="<?= h(app_url('dashboard.php')) ?>"><?= h(t('Open student dashboard', 'افتح لوحة الطالب')) ?></a>
</div>
</div>
</div>
</div>
</section>"""
new_hero_end = """ <a class="btn btn-outline-dark w-100 mt-3" href="<?= h(app_url('dashboard.php')) ?>"><?= h(t('Open student dashboard', 'افتح لوحة الطالب')) ?></a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</section>"""
content = content.replace(old_hero_end, new_hero_end)
# 2. Courses Section
old_courses = """ <div class="section-header">
<div>
<span class="eyebrow"><?= h(landing_setting('courses_eyebrow', 'Featured Courses', 'الدورات المميزة')) ?></span>
<h2 class="section-title"><?= h(landing_setting('courses_title', 'Enroll in specialized short courses designed to accelerate your skills.', 'سجل في دورات قصيرة متخصصة لتسريع تطوير مهاراتك.')) ?></h2>
</div>
</div>"""
new_courses = """ <?php if ($courses_img = landing_setting('courses_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('courses_eyebrow', 'Featured Courses', 'الدورات المميزة')) ?></span>
<h2 class="section-title mb-0"><?= h(landing_setting('courses_title', 'Enroll in specialized short courses designed to accelerate your skills.', 'سجل في دورات قصيرة متخصصة لتسريع تطوير مهاراتك.')) ?></h2>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($courses_img) ?>" class="img-fluid rounded shadow-sm" alt="Courses" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header">
<div>
<span class="eyebrow"><?= h(landing_setting('courses_eyebrow', 'Featured Courses', 'الدورات المميزة')) ?></span>
<h2 class="section-title"><?= h(landing_setting('courses_title', 'Enroll in specialized short courses designed to accelerate your skills.', 'سجل في دورات قصيرة متخصصة لتسريع تطوير مهاراتك.')) ?></h2>
</div>
</div>
<?php endif; ?>"""
content = content.replace(old_courses, new_courses)
# 3. Subjects Section
old_subjects = """ <div class="section-header">
<div>
<span class="eyebrow"><?= h(landing_setting('subjects_eyebrow', 'Featured subjects', 'المواد المميزة')) ?></span>
<h2 class="section-title"><?= h(landing_setting('subjects_title', 'Separate pages for catalog, detail, checkout, and dashboards.', 'صفحات منفصلة للكتالوج والتفاصيل والدفع ولوحات التحكم.')) ?></h2>
</div>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('catalog.php')) ?>"><?= h(t('See all subjects', 'عرض جميع المواد')) ?></a>
</div>"""
new_subjects = """ <?php if ($subjects_img = landing_setting('subjects_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('subjects_eyebrow', 'Featured subjects', 'المواد المميزة')) ?></span>
<h2 class="section-title mb-2"><?= h(landing_setting('subjects_title', 'Separate pages for catalog, detail, checkout, and dashboards.', 'صفحات منفصلة للكتالوج والتفاصيل والدفع ولوحات التحكم.')) ?></h2>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('catalog.php')) ?>"><?= h(t('See all subjects', 'عرض جميع المواد')) ?></a>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($subjects_img) ?>" class="img-fluid rounded shadow-sm" alt="Subjects" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header">
<div>
<span class="eyebrow"><?= h(landing_setting('subjects_eyebrow', 'Featured subjects', 'المواد المميزة')) ?></span>
<h2 class="section-title"><?= h(landing_setting('subjects_title', 'Separate pages for catalog, detail, checkout, and dashboards.', 'صفحات منفصلة للكتالوج والتفاصيل والدفع ولوحات التحكم.')) ?></h2>
</div>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('catalog.php')) ?>"><?= h(t('See all subjects', 'عرض جميع المواد')) ?></a>
</div>
<?php endif; ?>"""
content = content.replace(old_subjects, new_subjects)
# 4. Flow Section
old_flow = """ <div class="section-header mb-4">
<div>
<span class="eyebrow"><?= h(landing_setting('flow_eyebrow', 'Delivery flow', 'مسار التسليم')) ?></span>
<h2 class="section-title"><?= h(landing_setting('flow_title', 'One thin slice from discovery to live access.', 'شريحة رشيقة من الاكتشاف حتى الوصول المباشر.')) ?></h2>
</div>
</div>"""
new_flow = """ <?php if ($flow_img = landing_setting('flow_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('flow_eyebrow', 'Delivery flow', 'مسار التسليم')) ?></span>
<h2 class="section-title mb-0"><?= h(landing_setting('flow_title', 'One thin slice from discovery to live access.', 'شريحة رشيقة من الاكتشاف حتى الوصول المباشر.')) ?></h2>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($flow_img) ?>" class="img-fluid rounded shadow-sm" alt="Delivery Flow" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header mb-4">
<div>
<span class="eyebrow"><?= h(landing_setting('flow_eyebrow', 'Delivery flow', 'مسار التسليم')) ?></span>
<h2 class="section-title"><?= h(landing_setting('flow_title', 'One thin slice from discovery to live access.', 'شريحة رشيقة من الاكتشاف حتى الوصول المباشر.')) ?></h2>
</div>
</div>
<?php endif; ?>"""
content = content.replace(old_flow, new_flow)
# 5. Plans Section
old_plans = """ <div class="section-header mb-4">
<div>
<span class="eyebrow"><?= h(landing_setting('plans_eyebrow', 'Plans', 'الخطط')) ?></span>
<h2 class="section-title"><?= h(landing_setting('plans_title', 'Plan-based access for a shared marketplace.', 'وصول قائم على الخطط لمنصة مشتركة.')) ?></h2>
</div>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Open pricing', 'افتح التسعير')) ?></a>
</div>"""
new_plans = """ <?php if ($plans_img = landing_setting('plans_image', '')): ?>
<div class="row align-items-center mb-4">
<div class="col-lg-7">
<span class="eyebrow"><?= h(landing_setting('plans_eyebrow', 'Plans', 'الخطط')) ?></span>
<h2 class="section-title mb-2"><?= h(landing_setting('plans_title', 'Plan-based access for a shared marketplace.', 'وصول قائم على الخطط لمنصة مشتركة.')) ?></h2>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Open pricing', 'افتح التسعير')) ?></a>
</div>
<div class="col-lg-5 text-lg-end mt-3 mt-lg-0">
<img src="<?= h($plans_img) ?>" class="img-fluid rounded shadow-sm" alt="Plans" style="max-height: 150px; object-fit: cover;">
</div>
</div>
<?php else: ?>
<div class="section-header mb-4">
<div>
<span class="eyebrow"><?= h(landing_setting('plans_eyebrow', 'Plans', 'الخطط')) ?></span>
<h2 class="section-title"><?= h(landing_setting('plans_title', 'Plan-based access for a shared marketplace.', 'وصول قائم على الخطط لمنصة مشتركة.')) ?></h2>
</div>
<a class="link-dark fw-semibold text-decoration-none" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Open pricing', 'افتح التسعير')) ?></a>
</div>
<?php endif; ?>"""
content = content.replace(old_plans, new_plans)
with open('index.php', 'w', encoding='utf-8') as f:
f.write(content)

86
profile.php Normal file
View File

@ -0,0 +1,86 @@
<?php
require_once __DIR__ . '/includes/auth.php';
require_login();
$user = get_logged_in_user();
if (!$user) {
header('Location: logout.php');
exit;
}
$success = '';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
$password_confirm = $_POST['password_confirm'] ?? '';
if (empty($name) || empty($email)) {
$error = t('Name and email are required.', 'الاسم والبريد الإلكتروني مطلوبان.');
} elseif ($password !== $password_confirm) {
$error = t('Passwords do not match.', 'كلمتا المرور غير متطابقتين.');
} else {
$stmt = db()->prepare("SELECT id FROM users WHERE email = ? AND id != ?");
$stmt->execute([$email, $user['id']]);
if ($stmt->fetchColumn()) {
$error = t('Email already taken.', 'البريد الإلكتروني مستخدم بالفعل.');
} else {
if ($password) {
$hash = password_hash($password, PASSWORD_DEFAULT);
$update = db()->prepare("UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?");
$update->execute([$name, $email, $hash, $user['id']]);
} else {
$update = db()->prepare("UPDATE users SET name = ?, email = ? WHERE id = ?");
$update->execute([$name, $email, $user['id']]);
}
$success = t('Profile updated successfully.', 'تم تحديث الملف الشخصي بنجاح.');
$user = get_logged_in_user(); // Refresh
}
}
}
render_head(t('My Profile', 'الملف الشخصي'));
render_nav('profile.php');
?>
<main class="py-5 bg-light min-vh-100">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card border-0 shadow-sm" style="border-radius: 1rem;">
<div class="card-body p-4 p-md-5">
<h1 class="h4 mb-4 fw-bold"><?= h(t('My Profile', 'الملف الشخصي')) ?></h1>
<?php if ($success): ?>
<div class="alert alert-success py-2 small"><?= h($success) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger py-2 small"><?= h($error) ?></div>
<?php endif; ?>
<form method="post" action="profile.php">
<div class="mb-3">
<label class="form-label small fw-semibold"><?= h(t('Full Name', 'الاسم الكامل')) ?></label>
<input type="text" name="name" class="form-control" value="<?= h($user['name']) ?>" required>
</div>
<div class="mb-4">
<label class="form-label small fw-semibold"><?= h(t('Email address', 'البريد الإلكتروني')) ?></label>
<input type="email" name="email" class="form-control" value="<?= h($user['email']) ?>" required>
</div>
<h5 class="h6 mb-3 fw-bold border-top pt-4"><?= h(t('Change Password', 'تغيير كلمة المرور')) ?> <small class="text-secondary fw-normal"><?= h(t('(Optional)', '(اختياري)')) ?></small></h5>
<div class="mb-3">
<label class="form-label small fw-semibold"><?= h(t('New Password', 'كلمة المرور الجديدة')) ?></label>
<input type="password" name="password" class="form-control" placeholder="••••••••">
</div>
<div class="mb-4">
<label class="form-label small fw-semibold"><?= h(t('Confirm New Password', 'تأكيد كلمة المرور')) ?></label>
<input type="password" name="password_confirm" class="form-control" placeholder="••••••••">
</div>
<button type="submit" class="btn btn-dark w-100"><?= h(t('Save Changes', 'حفظ التغييرات')) ?></button>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
<?php render_footer(); ?>

112
reset_password.php Normal file
View File

@ -0,0 +1,112 @@
<?php
require_once __DIR__ . '/includes/auth.php';
require_once __DIR__ . '/mail/MailService.php';
if (!empty($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
$success = '';
$error = '';
$action = $_GET['action'] ?? 'forgot'; // forgot or reset
$token = $_GET['token'] ?? '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($action === 'forgot') {
$email = trim($_POST['email'] ?? '');
if ($email) {
$stmt = db()->prepare("SELECT id FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user) {
$newToken = bin2hex(random_bytes(32));
$expires = date('Y-m-d H:i:s', strtotime('+1 hour'));
$update = db()->prepare("UPDATE users SET reset_token = ?, reset_expires = ? WHERE id = ?");
$update->execute([$newToken, $expires, $user['id']]);
$resetUrl = app_url('reset_password.php', ['action' => 'reset', 'token' => $newToken]);
$fullResetUrl = 'http://' . $_SERVER['HTTP_HOST'] . '/' . ltrim($resetUrl, '/');
$htmlBody = "<p>You requested a password reset. Click the link below to reset it:</p><p><a href='{$fullResetUrl}'>{$fullResetUrl}</a></p><p>Link expires in 1 hour.</p>";
MailService::sendMail($email, "Password Reset", $htmlBody);
}
$success = t('If that email is in our system, you will receive a password reset link shortly.', 'إذا كان البريد الإلكتروني مسجلاً لدينا، ستتلقى رابطاً لإعادة تعيين كلمة المرور قريباً.');
}
} elseif ($action === 'reset' && $token) {
$password = $_POST['password'] ?? '';
$password_confirm = $_POST['password_confirm'] ?? '';
$stmt = db()->prepare("SELECT id FROM users WHERE reset_token = ? AND reset_expires > NOW()");
$stmt->execute([$token]);
$user = $stmt->fetch();
if (!$user) {
$error = t('Invalid or expired token.', 'رمز غير صالح أو منتهي الصلاحية.');
} elseif ($password !== $password_confirm) {
$error = t('Passwords do not match.', 'كلمتا المرور غير متطابقتين.');
} else {
$hash = password_hash($password, PASSWORD_DEFAULT);
$update = db()->prepare("UPDATE users SET password = ?, reset_token = NULL, reset_expires = NULL WHERE id = ?");
$update->execute([$hash, $user['id']]);
$success = t('Password updated successfully. You can now log in.', 'تم تحديث كلمة المرور بنجاح. يمكنك الآن تسجيل الدخول.');
$action = 'done';
}
}
}
render_head(t('Reset Password', 'إعادة تعيين كلمة المرور'));
render_nav('login.php');
?>
<main class="py-5 bg-light min-vh-100">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-5 col-lg-4">
<div class="card border-0 shadow-sm" style="border-radius: 1rem;">
<div class="card-body p-4 p-md-5">
<?php if ($action === 'forgot'): ?>
<h1 class="h4 mb-3 text-center fw-bold"><?= h(t('Forgot Password?', 'نسيت كلمة المرور؟')) ?></h1>
<p class="text-center text-secondary small mb-4"><?= h(t('Enter your email to receive a reset link.', 'أدخل بريدك الإلكتروني لتلقي رابط إعادة التعيين.')) ?></p>
<?php if ($success): ?>
<div class="alert alert-success py-2 small"><?= h($success) ?></div>
<?php endif; ?>
<form method="post" action="reset_password.php?action=forgot">
<div class="mb-4">
<label class="form-label small fw-semibold"><?= h(t('Email address', 'البريد الإلكتروني')) ?></label>
<input type="email" name="email" class="form-control form-control-lg" required>
</div>
<button type="submit" class="btn btn-dark btn-lg w-100"><?= h(t('Send Reset Link', 'إرسال الرابط')) ?></button>
<div class="text-center mt-3"><a href="login.php" class="small text-secondary text-decoration-none"><?= h(t('Back to Login', 'العودة لتسجيل الدخول')) ?></a></div>
</form>
<?php elseif ($action === 'reset'): ?>
<h1 class="h4 mb-3 text-center fw-bold"><?= h(t('Set New Password', 'تعيين كلمة مرور جديدة')) ?></h1>
<?php if ($error): ?>
<div class="alert alert-danger py-2 small"><?= h($error) ?></div>
<?php endif; ?>
<form method="post" action="reset_password.php?action=reset&token=<?= h($token) ?>">
<div class="mb-3">
<label class="form-label small fw-semibold"><?= h(t('New Password', 'كلمة المرور الجديدة')) ?></label>
<input type="password" name="password" class="form-control form-control-lg" required>
</div>
<div class="mb-4">
<label class="form-label small fw-semibold"><?= h(t('Confirm Password', 'تأكيد كلمة المرور')) ?></label>
<input type="password" name="password_confirm" class="form-control form-control-lg" required>
</div>
<button type="submit" class="btn btn-dark btn-lg w-100"><?= h(t('Reset Password', 'إعادة تعيين')) ?></button>
</form>
<?php elseif ($action === 'done'): ?>
<div class="text-center">
<h1 class="h4 mb-3 fw-bold text-success"><?= h(t('Success!', 'نجاح!')) ?></h1>
<p class="text-secondary small mb-4"><?= h($success) ?></p>
<a href="login.php" class="btn btn-dark btn-lg w-100"><?= h(t('Log In', 'تسجيل الدخول')) ?></a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</main>
<?php render_footer(); ?>