add login page
This commit is contained in:
parent
6f28e66799
commit
be93531517
@ -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'] ?? '';
|
||||
|
||||
@ -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>
|
||||
@ -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']) ?>">
|
||||
|
||||
@ -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
16
includes/auth.php
Normal 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;
|
||||
}
|
||||
54
index.php
54
index.php
@ -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
65
login.php
Normal 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
8
logout.php
Normal 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
187
patch_admin.py
Normal 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)
|
||||
@ -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
161
patch_index.py
Normal 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
86
profile.php
Normal 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
112
reset_password.php
Normal 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(); ?>
|
||||
Loading…
x
Reference in New Issue
Block a user