From e5366bdd580b8f2cb6d18e3cfdd5f6743999f21d Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 16 Apr 2026 13:19:21 +0000 Subject: [PATCH] Autosave: 20260416-131924 --- ... ? 1000 : 190);\\n }\"), array('notes'" | 0 add_logo_to_hero.py | 15 ++ admin.php | 40 +-- app_settings.php | 139 ++++++++++ application_detail.php | 82 ++++++ applications.php | 243 +++++++++-------- approved_school.php | 14 +- assessments.php | 12 +- assets/css/custom.css | 4 +- attendance.php | 12 +- center_application.php | 29 ++ center_profile.php | 173 ++++++++++++ dashboard.php | 9 + ...416_alter_center_applications_subjects.sql | 1 + db/migrations/20260416_app_settings.sql | 10 + db/migrations/20260416_subjects.sql | 8 + fix_sidebar.py | 7 + includes/app.php | 38 ++- includes/sidebar.php | 67 +++++ modules.php | 9 + patch_app.php | 93 +++++++ patch_app2.php | 34 +++ students.php | 11 +- subjects.php | 251 ++++++++++++++++++ teachers.php | 11 +- update.py | 64 +++++ 26 files changed, 1214 insertions(+), 162 deletions(-) create mode 100644 "'',, \" foreach (\\$data as \\$key => \\$_value) {\\n \\$data[\\$key] = clean_text((string) (\\$input[\\$key] ?? ), \\$key === notes ? 1000 : 190);\\n }\"), array('notes'" create mode 100644 add_logo_to_hero.py create mode 100644 app_settings.php create mode 100644 center_profile.php create mode 100644 db/migrations/20260416_alter_center_applications_subjects.sql create mode 100644 db/migrations/20260416_app_settings.sql create mode 100644 db/migrations/20260416_subjects.sql create mode 100644 fix_sidebar.py create mode 100644 includes/sidebar.php create mode 100644 patch_app.php create mode 100644 patch_app2.php create mode 100644 subjects.php create mode 100644 update.py diff --git "a/'',, \" foreach (\\$data as \\$key => \\$_value) {\\n \\$data[\\$key] = clean_text((string) (\\$input[\\$key] ?? ), \\$key === notes ? 1000 : 190);\\n }\"), array('notes'" "b/'',, \" foreach (\\$data as \\$key => \\$_value) {\\n \\$data[\\$key] = clean_text((string) (\\$input[\\$key] ?? ), \\$key === notes ? 1000 : 190);\\n }\"), array('notes'" new file mode 100644 index 0000000..e69de29 diff --git a/add_logo_to_hero.py b/add_logo_to_hero.py new file mode 100644 index 0000000..bc4546d --- /dev/null +++ b/add_logo_to_hero.py @@ -0,0 +1,15 @@ +with open('approved_school.php', 'r', encoding='utf-8') as f: + content = f.read() + +replacement = """ +
+ + Logo + +

+
""" + +content = content.replace('

', replacement) + +with open('approved_school.php', 'w', encoding='utf-8') as f: + f.write(content) diff --git a/admin.php b/admin.php index a565cfc..54302a3 100644 --- a/admin.php +++ b/admin.php @@ -18,45 +18,7 @@ render_flash($flash);
diff --git a/app_settings.php b/app_settings.php new file mode 100644 index 0000000..4a66fd3 --- /dev/null +++ b/app_settings.php @@ -0,0 +1,139 @@ + $settings['app_name'] ?? '', + 'app_email' => $settings['app_email'] ?? '', + 'app_telephone' => $settings['app_telephone'] ?? '', +]; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $values['app_name'] = clean_text($_POST['app_name'] ?? '', 190); + $values['app_email'] = clean_text($_POST['app_email'] ?? '', 190); + $values['app_telephone'] = clean_text($_POST['app_telephone'] ?? '', 60); + + if ($values['app_name'] === '') $errors['app_name'] = 'مطلوب'; + + $logoPath = $settings['app_logo'] ?? ''; + $faviconPath = $settings['app_favicon'] ?? ''; + + // Handle Uploads + $uploadDir = __DIR__ . '/assets/images/uploads/'; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) { + $logoExt = strtolower(pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION)); + if (in_array($logoExt, ['png', 'jpg', 'jpeg', 'svg', 'gif'])) { + $logoName = 'app_logo_' . time() . '.' . $logoExt; + if (move_uploaded_file($_FILES['logo']['tmp_name'], $uploadDir . $logoName)) { + $logoPath = 'assets/images/uploads/' . $logoName; + } + } else { + $errors['logo'] = 'صيغة غير مدعومة'; + } + } + + if (isset($_FILES['favicon']) && $_FILES['favicon']['error'] === UPLOAD_ERR_OK) { + $faviconExt = strtolower(pathinfo($_FILES['favicon']['name'], PATHINFO_EXTENSION)); + if (in_array($faviconExt, ['png', 'ico', 'svg'])) { + $faviconName = 'app_favicon_' . time() . '.' . $faviconExt; + if (move_uploaded_file($_FILES['favicon']['tmp_name'], $uploadDir . $faviconName)) { + $faviconPath = 'assets/images/uploads/' . $faviconName; + } + } else { + $errors['favicon'] = 'صيغة غير مدعومة'; + } + } + + if (empty($errors)) { + try { + $stmt = db()->prepare('UPDATE app_settings SET app_name = ?, app_email = ?, app_telephone = ?, app_logo = ?, app_favicon = ?, updated_at = NOW() WHERE id = 1'); + $stmt->execute([ + $values['app_name'], + $values['app_email'], + $values['app_telephone'], + $logoPath, + $faviconPath + ]); + set_flash('success', 'تم تحديث الإعدادات العامة بنجاح.'); + header('Location: app_settings.php'); + exit; + } catch (Throwable $e) { + $errors['form'] = 'تعذر الحفظ.'; + } + } +} + +render_page_start('إعدادات النظام', 'app_settings', 'إعدادات النظام العامة'); +render_flash($flash); +?> +
+
+
+
+ +
+
+
+

الإعدادات العامة للنظام

+

تعديل اسم النظام، الشعار (Logo)، الأيقونة (Favicon)، وبيانات التواصل.

+
+ +
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
Logo
+ + +
+
+
+ + +
Favicon
+ + +
+
+
+ +
+ +
+
+
+ +
+
+
+
+ \ No newline at end of file diff --git a/application_detail.php b/application_detail.php index 65024b2..68ac527 100644 --- a/application_detail.php +++ b/application_detail.php @@ -27,6 +27,19 @@ if (!$application) { $reviewErrors = []; if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + if (isset($_POST['action']) && $_POST['action'] === 'update_subjects') { + $selected_subjects = isset($_POST['subjects']) && is_array($_POST['subjects']) ? $_POST['subjects'] : []; + $valid_subjects = []; + foreach ($selected_subjects as $sid) { + $sid = filter_var($sid, FILTER_VALIDATE_INT); + if ($sid) $valid_subjects[] = $sid; + } + update_application_subjects($applicationId, $valid_subjects); + set_flash('success', 'تم تحديث المواد الدراسية بنجاح.'); + header('Location: application_detail.php?id=' . urlencode((string) $applicationId)); + exit; + } $status = clean_text((string) ($_POST['status'] ?? 'submitted'), 30); $adminNotes = clean_text((string) ($_POST['admin_notes'] ?? ''), 1000); $evaluationScore = filter_var($_POST['evaluation_score'] ?? null, FILTER_VALIDATE_INT, [ @@ -63,6 +76,12 @@ render_flash($flash); ?>
+
+
+ +
+
+
@@ -111,6 +130,31 @@ render_flash($flash);
فترة البرنامج
+ +
+
+
المواد الدراسية المطلوبة
+ +
+
+ + لم يتم اختيار أي مواد. + + + + + +
+
+
ملخص البرنامج والاحتياجات

@@ -187,6 +231,44 @@ render_flash($flash);
+
+
+
+ + + + diff --git a/applications.php b/applications.php index 9d45b80..25ed7d9 100644 --- a/applications.php +++ b/applications.php @@ -2,131 +2,146 @@ declare(strict_types=1); require_once __DIR__ . '/includes/app.php'; -$flash = consume_flash(); -$statusFilter = clean_text((string) ($_GET['status'] ?? 'all'), 50); -$applications = list_applications($statusFilter); -$stats = dashboard_metrics(); -$statusTabs = ['all' => 'الكل'] + array_map(fn ($meta) => $meta['label'], status_map()); -$currentCount = count($applications); -$reviewBacklog = $stats['submitted'] + $stats['under_review']; -$approvalRate = $stats['all'] > 0 ? (int) round(($stats['approved'] / $stats['all']) * 100) : 0; +// Handle POST actions (e.g. Delete) +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? ''; + + if ($action === 'delete') { + $id = (int)($_POST['id'] ?? 0); + if ($id > 0) { + $stmt = db()->prepare('DELETE FROM center_applications WHERE id = ?'); + $stmt->execute([$id]); + set_flash('success', 'تم حذف الطلب بنجاح.'); + } + header('Location: applications.php'); + exit; + } +} -render_page_start('لوحة الطلبات', 'applications', 'قائمة موحدة لطلبات فتح المراكز الصيفية مع فلترة حسب حالة المراجعة.'); +// Read list +$search = clean_text($_GET['search'] ?? '', 255); +$query = 'SELECT * FROM center_applications'; +$params = []; +if ($search !== '') { + $query .= ' WHERE center_name LIKE ? OR city LIKE ? OR director_name LIKE ?'; + $params[] = "%$search%"; + $params[] = "%$search%"; + $params[] = "%$search%"; +} +$query .= ' ORDER BY id DESC'; +$stmt = db()->prepare($query); +$stmt->execute($params); +$applications = $stmt->fetchAll(); + +$flash = consume_flash(); +render_page_start('إدارة الطلبات', 'applications', 'قائمة بطلبات فتح المراكز'); render_flash($flash); ?>
-
-
-
- صفحة مستقلة للمراجعة -

لوحة طلبات فتح المراكز

-

تم تخصيص هذه الصفحة بالكامل لمتابعة الطلبات: مؤشرات سريعة، فلاتر واضحة، وجدول مراجعة عملي يقود مباشرة إلى صفحة التفاصيل واتخاذ القرار.

-
-
-
-
الحمل التشغيلي الحالي
-
-
طلبات تحتاج عملاً من المشرف العام الآن، مع نسبة اعتماد إجمالية تبلغ ٪.
+
+
+ +
+
+
+
+
إدارة طلبات فتح المراكز
+ + + + + إضافة طلب +
-
-
-
-
-
إجمالي الطلبات
جميع الطلبات في قاعدة البيانات.
-
بانتظار الاستلام
طلبات جديدة لم يبدأ تقييمها بعد.
-
تحت المراجعة
ملفات قيد التحقق والملاحظات.
-
معتمد
مراكز جاهزة للانتقال لمرحلة التشغيل.
-
+ +
+
+ + + + إلغاء + +
+
-
-
-
-
أدوات الفرز والمتابعة
-
اختر حالة محددة للتركيز على شريحة واحدة من الطلبات، أو ارجع إلى الكل لمراجعة الصورة العامة.
-
- -
-
- $label): ?> - - - - - - - -
-
-
الفلتر الحالي:
-
عدد السجلات المعروضة:
-
-
- -
- -
-
لا توجد طلبات ضمن هذا التصنيف.
-

يمكنك إنشاء طلب جديد أو العودة إلى جميع الطلبات لاستكمال المراجعة.

- -
- -
- - - - - - - - - - - - - - - +
+
المرجعالمركزالمدينةالمسؤولالفترةالحالةالتقييمإجراء
+ - - - - - - - - + + + + + + + - - -
# -
-
سعة متوقعة: طالب
-
-
-
-
-
-
حتى
-
- - صفحة المركز - - فتح الملف - - المرجعالمركزالمدينةالمسؤولالفترةالحالةالإجراءات
+ + + + + لا توجد طلبات مسجلة أو لم يتم العثور على نتائج. + + + + + # + +
+
سعة متوقعة: طالب
+ + + +
+
+ + +
+
حتى
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + + +
+
+ + + + + + +
- +
- + \ No newline at end of file diff --git a/approved_school.php b/approved_school.php index d16e90d..60f592a 100644 --- a/approved_school.php +++ b/approved_school.php @@ -17,6 +17,12 @@ if (!$application) { ?>
+
+
+ +
+
+
المركز غير موجود

تحقق من رقم الطلب أو ارجع إلى قائمة المراكز المعتمدة.

@@ -138,7 +144,7 @@ $pageDescription = $isApproved ? 'صفحة تشغيلية للمركز المعتمد تعرض الجاهزية، الدورات الموسمية، والخطوات التالية بعد الموافقة.' : 'هذه الصفحة تصبح متاحة بعد اعتماد طلب المركز من المشرف العام.'; -render_page_start($pageTitle, 'approved', $pageDescription); +render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? '')); render_flash($flash); ?>
@@ -177,6 +183,7 @@ render_flash($flash);
+ +
+
+
+
+
+ +
+
+
المدرسة غير موجودة
@@ -366,6 +372,10 @@ render_flash($flash);
+ +
+
+
+
+
+ +
+
+
المدرسة غير موجودة
@@ -368,6 +374,10 @@ render_flash($flash);
+ +
+
+
+ +
+
+
+

المواد الدراسية

+

اختر المواد الدراسية المطلوب تدريسها في هذا المركز.

+
+
+
+
+ + +

لا توجد مواد دراسية مفعلة حالياً.

+ +
+ +
+ > + +
+ +
+
+ +
+
+
العودة إلى لوحة الطلبات diff --git a/center_profile.php b/center_profile.php new file mode 100644 index 0000000..3d224e9 --- /dev/null +++ b/center_profile.php @@ -0,0 +1,173 @@ + 0 ? get_application($applicationId) : null; + +if (!$application) { + http_response_code(404); + render_page_start('ملف المركز غير موجود', 'approved', 'تعذر العثور على المركز.'); + render_flash($flash); + ?> +
+
+
+
+ +
+
+
+
المركز غير موجود
+ العودة +
+
+
+
+
+ $application['center_name'], + 'email' => $application['email'], + 'phone' => $application['phone'], +]; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $values['center_name'] = clean_text($_POST['center_name'] ?? '', 190); + $values['email'] = clean_text($_POST['email'] ?? '', 190); + $values['phone'] = clean_text($_POST['phone'] ?? '', 60); + + if ($values['center_name'] === '') $errors['center_name'] = 'مطلوب'; + if ($values['email'] === '') $errors['email'] = 'مطلوب'; + if ($values['phone'] === '') $errors['phone'] = 'مطلوب'; + + $logoPath = $application['logo']; + $faviconPath = $application['favicon']; + + // Handle Uploads + $uploadDir = __DIR__ . '/assets/images/uploads/'; + + if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) { + $logoExt = strtolower(pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION)); + if (in_array($logoExt, ['png', 'jpg', 'jpeg', 'svg', 'gif'])) { + $logoName = 'logo_' . $applicationId . '_' . time() . '.' . $logoExt; + if (move_uploaded_file($_FILES['logo']['tmp_name'], $uploadDir . $logoName)) { + $logoPath = 'assets/images/uploads/' . $logoName; + } + } else { + $errors['logo'] = 'صيغة غير مدعومة'; + } + } + + if (isset($_FILES['favicon']) && $_FILES['favicon']['error'] === UPLOAD_ERR_OK) { + $faviconExt = strtolower(pathinfo($_FILES['favicon']['name'], PATHINFO_EXTENSION)); + if (in_array($faviconExt, ['png', 'ico', 'svg'])) { + $faviconName = 'favicon_' . $applicationId . '_' . time() . '.' . $faviconExt; + if (move_uploaded_file($_FILES['favicon']['tmp_name'], $uploadDir . $faviconName)) { + $faviconPath = 'assets/images/uploads/' . $faviconName; + } + } else { + $errors['favicon'] = 'صيغة غير مدعومة'; + } + } + + if (empty($errors)) { + try { + $stmt = db()->prepare('UPDATE center_applications SET center_name = ?, email = ?, phone = ?, logo = ?, favicon = ?, updated_at = NOW() WHERE id = ?'); + $stmt->execute([ + $values['center_name'], + $values['email'], + $values['phone'], + $logoPath, + $faviconPath, + $applicationId + ]); + set_flash('success', 'تم تحديث بيانات المركز بنجاح.'); + header('Location: center_profile.php?id=' . $applicationId); + exit; + } catch (Throwable $e) { + $errors['form'] = 'تعذر الحفظ.'; + } + } +} + +render_page_start('إعدادات المركز', 'approved', 'تعديل بيانات وهوية المركز.', (string) ($application['favicon'] ?? '')); +render_flash($flash); +?> +
+
+
+
+ +
+
+
+

إعدادات وهوية المركز

+

تعديل اسم المركز، الشعار (Logo)، الأيقونة (Favicon)، وبيانات التواصل.

+
+ +
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
Logo
+ + +
+
+
+ + +
Favicon
+ + +
+
+
+ +
+ + إلغاء +
+
+
+ +
+
+
+
+ diff --git a/dashboard.php b/dashboard.php index 0ce7d33..fe6d009 100644 --- a/dashboard.php +++ b/dashboard.php @@ -11,6 +11,12 @@ render_flash($flash); ?>
+
+
+ +
+
+
@@ -123,6 +129,9 @@ render_flash($flash);
+
+
+
diff --git a/db/migrations/20260416_alter_center_applications_subjects.sql b/db/migrations/20260416_alter_center_applications_subjects.sql new file mode 100644 index 0000000..95cb024 --- /dev/null +++ b/db/migrations/20260416_alter_center_applications_subjects.sql @@ -0,0 +1 @@ +ALTER TABLE center_applications ADD COLUMN subjects JSON NULL AFTER notes; diff --git a/db/migrations/20260416_app_settings.sql b/db/migrations/20260416_app_settings.sql new file mode 100644 index 0000000..ca7f11c --- /dev/null +++ b/db/migrations/20260416_app_settings.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS app_settings ( + id INT PRIMARY KEY DEFAULT 1, + app_name VARCHAR(255), + app_email VARCHAR(255), + app_telephone VARCHAR(255), + app_logo VARCHAR(255), + app_favicon VARCHAR(255), + updated_at DATETIME +); +INSERT IGNORE INTO app_settings (id, app_name) VALUES (1, 'Central Admin'); diff --git a/db/migrations/20260416_subjects.sql b/db/migrations/20260416_subjects.sql new file mode 100644 index 0000000..57a9586 --- /dev/null +++ b/db/migrations/20260416_subjects.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS subjects ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + status ENUM('enabled', 'disabled') DEFAULT 'enabled', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/fix_sidebar.py b/fix_sidebar.py new file mode 100644 index 0000000..18fe051 --- /dev/null +++ b/fix_sidebar.py @@ -0,0 +1,7 @@ +with open('includes/sidebar.php', 'r', encoding='utf-8') as f: + content = f.read() + +content = content.replace("href=\"approved_school.php', 'center_profile.php?id=", "href=\"approved_school.php?id=") + +with open('includes/sidebar.php', 'w', encoding='utf-8') as f: + f.write(content) diff --git a/includes/app.php b/includes/app.php index 54992f7..a57e7f0 100644 --- a/includes/app.php +++ b/includes/app.php @@ -409,6 +409,7 @@ function application_defaults(): array 'start_date' => '', 'end_date' => '', 'notes' => '', + 'subjects' => [], ]; } @@ -416,6 +417,10 @@ function validate_application_input(array $input): array { $data = application_defaults(); foreach ($data as $key => $_value) { + if ($key === 'subjects') { + $data[$key] = is_array($input[$key] ?? null) ? $input[$key] : []; + continue; + } $data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190); } @@ -467,6 +472,12 @@ function validate_application_input(array $input): array $errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.'; } + if (empty($data['subjects'])) { + $errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.'; + } else { + $data['subjects'] = array_map('intval', $data['subjects']); + } + return [$data, $errors]; } @@ -476,10 +487,10 @@ function create_application(array $data): int $stmt = $pdo->prepare( 'INSERT INTO center_applications ( center_name, city, center_type, gender_scope, director_name, phone, email, - expected_students, start_date, end_date, notes, status, submitted_at, updated_at + expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at ) VALUES ( :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email, - :expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW() + :expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW() )' ); @@ -495,6 +506,7 @@ function create_application(array $data): int ':start_date' => $data['start_date'], ':end_date' => $data['end_date'], ':notes' => $data['notes'], + ':subjects' => json_encode($data['subjects']), ':status' => 'submitted', ]); @@ -1334,3 +1346,25 @@ function render_page_end(): void prepare('UPDATE center_applications SET subjects = :subjects, updated_at = NOW() WHERE id = :id'); + $stmt->bindValue(':subjects', json_encode(array_values($subjects)), PDO::PARAM_STR); + $stmt->bindValue(':id', $id, PDO::PARAM_INT); + $stmt->execute(); +} + +function get_enabled_subjects(): array +{ + try { + $pdo = db_connection(); + $stmt = $pdo->query("SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC"); + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + return $result !== false ? $result : []; + } catch (Throwable $e) { + return []; + } +} diff --git a/includes/sidebar.php b/includes/sidebar.php new file mode 100644 index 0000000..63f80b3 --- /dev/null +++ b/includes/sidebar.php @@ -0,0 +1,67 @@ + + \ No newline at end of file diff --git a/modules.php b/modules.php index 44d5e86..2440a74 100644 --- a/modules.php +++ b/modules.php @@ -9,6 +9,12 @@ render_flash($flash); ?>
+
+
+ +
+
+
هيكل الصفحات والوحدات

تم فصل المحتوى التعريفي والتنظيمي في هذه الصفحة حتى تبقى الصفحات التشغيلية مركزة: التقديم في شاشة، المتابعة في شاشة، والتفاصيل في شاشة مستقلة.

@@ -65,6 +71,9 @@ render_flash($flash);
+
+
+ diff --git a/patch_app.php b/patch_app.php new file mode 100644 index 0000000..4e47915 --- /dev/null +++ b/patch_app.php @@ -0,0 +1,93 @@ + \'', + '\'notes\' => \'', + '\'subjects\' => [], + $content +); + +$old_loop = <<<'EOD' + foreach ($data as $key => $_value) { + $data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190); + } +EOD; + +$new_loop = <<<'EOD' + foreach ($data as $key => $_value) { + if ($key === 'subjects') { + $data[$key] = is_array($input[$key] ?? []) ? $input[$key] : []; + continue; + } + $data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190); + } +EOD; +$content = str_replace($old_loop, $new_loop, $content); + +$old_val = <<<'EOD' + if ($startDate !== '' && $endDate !== '' && strtotime($endDate) < strtotime($startDate)) { + $errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.'; + } +EOD; + +$new_val = $old_val . <<<'EOD' + + if (empty($data['subjects'])) { + $errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.'; + } else { + $data['subjects'] = array_map('intval', $data['subjects']); + } +EOD; +$content = str_replace($old_val, $new_val, $content); + +$old_insert_sql = <<<'EOD' + 'INSERT INTO center_applications ( + center_name, city, center_type, gender_scope, director_name, phone, email, + expected_students, start_date, end_date, notes, status, submitted_at, updated_at + ) VALUES ( + :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email, + :expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW() + )' +EOD; + +$new_insert_sql = <<<'EOD' + 'INSERT INTO center_applications ( + center_name, city, center_type, gender_scope, director_name, phone, email, + expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at + ) VALUES ( + :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email, + :expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW() + )' +EOD; +$content = str_replace($old_insert_sql, $new_insert_sql, $content); + +$old_execute = <<<'EOD' + ':notes' => $data['notes'], + ':status' => 'submitted', + ]); +EOD; + +$new_execute = <<<'EOD' + ':notes' => $data['notes'], + ':subjects' => json_encode($data['subjects']), + ':status' => 'submitted', + ]); +EOD; +$content = str_replace($old_execute, $new_execute, $content); + +if (strpos($content, "function get_enabled_subjects") === false) { + $content .= <<<'EOD' + +function get_enabled_subjects(): array +{ + $pdo = db_connection(); + $stmt = $pdo->query("SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC"); + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + return $result !== false ? $result : []; +} +EOD; +} + +file_put_contents('includes/app.php', $content); +echo "Patch applied."; diff --git a/patch_app2.php b/patch_app2.php new file mode 100644 index 0000000..1a800ae --- /dev/null +++ b/patch_app2.php @@ -0,0 +1,34 @@ + \'', + '\'notes\' => \'', + '\'subjects\' => [], + $content +); + +$old_loop = " foreach (\$data as \$key => \$_value) {\n \$data[\$key] = clean_text((string) (\$input[\$key] ?? \'\'), \$key === 'notes' ? 1000 : 190);\n }"; +$new_loop = " foreach (\$data as \$key => \$_value) {\n if (\$key === 'subjects') {\n \$data[\$key] = is_array(\$input[\$key] ?? []) ? \$input[\$key] : [];\n continue;\n }\n \$data[\$key] = clean_text((string) (\$input[\$key] ?? \'\'), \$key === 'notes' ? 1000 : 190);\n }"; +$content = str_replace($old_loop, $new_loop, $content); + +$old_val = " if (\$startDate !== '' && \$endDate !== '' && strtotime(\$endDate) < strtotime(\$startDate)) {\n \$errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.';\n }"; +$new_val = $old_val . "\n\n if (empty(\$data['subjects'])) {\n \$errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.';\n } else {\n \$data['subjects'] = array_map('intval', \$data['subjects']);\n }"; +$content = str_replace($old_val, $new_val, $content); + +$old_insert_sql = " 'INSERT INTO center_applications (\n center_name, city, center_type, gender_scope, director_name, phone, email,\n expected_students, start_date, end_date, notes, status, submitted_at, updated_at\n ) VALUES (\n :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,\n :expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW()\n )'"; +$new_insert_sql = " 'INSERT INTO center_applications (\n center_name, city, center_type, gender_scope, director_name, phone, email,\n expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at\n ) VALUES (\n :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email,\n :expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW()\n )'"; +$content = str_replace($old_insert_sql, $new_insert_sql, $content); + +$old_execute = " ':notes' => \$data['notes'],\n ':status' => 'submitted',\n ]);"; +$new_execute = " ':notes' => \$data['notes'],\n ':subjects' => json_encode(\$data['subjects']), + ':status' => 'submitted',\n ]);"; +$content = str_replace($old_execute, $new_execute, $content); + +if (strpos($content, "function get_enabled_subjects") === false) { + $content .= "\nfunction get_enabled_subjects(): array\n{\n \$pdo = db_connection();\n \$stmt = \$pdo->query(\"SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC\");\n \$result = \$stmt->fetchAll(PDO::FETCH_ASSOC);\n return \$result !== false ? \$result : [];\n}\n"; +} + +file_put_contents('includes/app.php', $content); +echo "Patch applied.\n"; + diff --git a/students.php b/students.php index 083710e..0af762d 100644 --- a/students.php +++ b/students.php @@ -77,11 +77,17 @@ if (!$application) { http_response_code(404); } -render_page_start($pageTitle, 'approved', $pageDescription); +render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? '')); render_flash($flash); ?>
+
+
+ +
+
+
المدرسة غير موجودة
@@ -378,6 +384,9 @@ render_flash($flash);
+
+
+
diff --git a/subjects.php b/subjects.php new file mode 100644 index 0000000..36bd467 --- /dev/null +++ b/subjects.php @@ -0,0 +1,251 @@ +prepare('INSERT INTO subjects (name, description, status) VALUES (?, ?, ?)'); + $stmt->execute([$name, $description, $status]); + set_flash('success', 'تمت إضافة المادة بنجاح.'); + } else { + set_flash('error', 'اسم المادة مطلوب.'); + } + header('Location: subjects.php'); + exit; + } + + if ($action === 'edit') { + $id = (int)($_POST['id'] ?? 0); + $name = clean_text($_POST['name'] ?? '', 255); + $description = clean_text($_POST['description'] ?? '', 1000); + $status = in_array($_POST['status'] ?? '', ['enabled', 'disabled']) ? $_POST['status'] : 'enabled'; + + if ($id > 0 && $name !== '') { + $stmt = db()->prepare('UPDATE subjects SET name = ?, description = ?, status = ? WHERE id = ?'); + $stmt->execute([$name, $description, $status, $id]); + set_flash('success', 'تم تحديث المادة بنجاح.'); + } else { + set_flash('error', 'تأكد من إدخال اسم المادة.'); + } + header('Location: subjects.php'); + exit; + } + + if ($action === 'delete') { + $id = (int)($_POST['id'] ?? 0); + if ($id > 0) { + $stmt = db()->prepare('DELETE FROM subjects WHERE id = ?'); + $stmt->execute([$id]); + set_flash('success', 'تم حذف المادة بنجاح.'); + } + header('Location: subjects.php'); + exit; + } +} + +// Read list +$search = clean_text($_GET['search'] ?? '', 255); +$query = 'SELECT * FROM subjects'; +$params = []; +if ($search !== '') { + $query .= ' WHERE name LIKE ? OR description LIKE ?'; + $params[] = "%$search%"; + $params[] = "%$search%"; +} +$query .= ' ORDER BY id DESC'; +$stmt = db()->prepare($query); +$stmt->execute($params); +$subjects = $stmt->fetchAll(); + +$flash = consume_flash(); +render_page_start('المواد الدراسية', 'subjects', 'إدارة المواد الدراسية الخاصة بالمراكز'); +render_flash($flash); +?> +
+
+
+
+ +
+
+
+
+
إدارة المواد الدراسية
+ +
+ + +
+
+ + + + إلغاء + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
#الاسمالوصفالحالةالإجراءات
لا توجد مواد مسجلة أو لم يتم العثور على نتائج.
+ + مفعل + + معطل + + +
+ + + +
+ + + +
+
+
+
+
+
+
+
+
+ + + + + + + + + + diff --git a/teachers.php b/teachers.php index 51839a9..0d12a4c 100644 --- a/teachers.php +++ b/teachers.php @@ -76,11 +76,17 @@ if (!$application) { http_response_code(404); } -render_page_start($pageTitle, 'approved', $pageDescription); +render_page_start($pageTitle, 'approved', $pageDescription, (string) ($application['favicon'] ?? '')); render_flash($flash); ?>
+
+
+ +
+
+
المدرسة غير موجودة
@@ -356,6 +362,9 @@ render_flash($flash);
+
+
+
diff --git a/update.py b/update.py new file mode 100644 index 0000000..ec05c8a --- /dev/null +++ b/update.py @@ -0,0 +1,64 @@ +import json + +with open("includes/app.php", "r") as f: + c = f.read() + +c = c.replace( + "'notes' => '',", + "'notes' => '',\n 'subjects' => []," +) + +old_loop = """ foreach ($data as $key => $_value) { + $data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190); + }""" +new_loop = """ foreach ($data as $key => $_value) { + if ($key === 'subjects') { + $data[$key] = is_array($input[$key] ?? []) ? $input[$key] : []; + continue; + } + $data[$key] = clean_text((string) ($input[$key] ?? ''), $key === 'notes' ? 1000 : 190); + }""" +c = c.replace(old_loop, new_loop) + +old_val = """ if ($startDate !== '' && $endDate !== '' && strtotime($endDate) < strtotime($startDate)) { + $errors['end_date'] = 'يجب أن يكون تاريخ النهاية بعد البداية.'; + }""" +new_val = old_val + "\n\n if (empty($data['subjects'])) { + $errors['subjects'] = 'يرجى اختيار مادة واحدة على الأقل.'; + } else { + $data['subjects'] = array_map('intval', $data['subjects']); + }" +c = c.replace(old_val, new_val) + +old_insert_sql = " 'INSERT INTO center_applications ( + center_name, city, center_type, gender_scope, director_name, phone, email, + expected_students, start_date, end_date, notes, status, submitted_at, updated_at + ) VALUES ( + :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email, + :expected_students, :start_date, :end_date, :notes, :status, NOW(), NOW() + )"" +new_insert_sql = " 'INSERT INTO center_applications ( + center_name, city, center_type, gender_scope, director_name, phone, email, + expected_students, start_date, end_date, notes, subjects, status, submitted_at, updated_at + ) VALUES ( + :center_name, :city, :center_type, :gender_scope, :director_name, :phone, :email, + :expected_students, :start_date, :end_date, :notes, :subjects, :status, NOW(), NOW() + )"" +c = c.replace(old_insert_sql, new_insert_sql) + +old_execute = """ ':notes' => $data['notes'], + ':status' => 'submitted', + ]);""" +new_execute = """ ':notes' => $data['notes'], + ':subjects' => json_encode($data['subjects']), + ':status' => 'submitted', + ]);""" +c = c.replace(old_execute, new_execute) + +if "function get_enabled_subjects" not in c: + c += "\nfunction get_enabled_subjects(): array\n{\n $pdo = db_connection();\n $stmt = $pdo->query(\"SELECT id, name, description FROM subjects WHERE status = 'enabled' ORDER BY name ASC\");\n $result = $stmt->fetchAll(PDO::FETCH_ASSOC);\n return $result !== false ? $result : [];\n}\n" + +with open("includes/app.php", "w") as f: + f.write(c) + +print("Patch applied.") \ No newline at end of file