updating internal mail

This commit is contained in:
Flatlogic Bot 2026-02-28 03:56:18 +00:00
parent 3aa10f537e
commit 607b9d8838
9 changed files with 494 additions and 268 deletions

View File

@ -138,6 +138,13 @@ if (isset($_SESSION['error'])) {
$statuses = db()->query("SELECT * FROM mailbox_statuses ORDER BY id ASC")->fetchAll(); $statuses = db()->query("SELECT * FROM mailbox_statuses ORDER BY id ASC")->fetchAll();
$email_logs = db()->query("SELECT * FROM email_logs ORDER BY id DESC LIMIT 50")->fetchAll(); $email_logs = db()->query("SELECT * FROM email_logs ORDER BY id DESC LIMIT 50")->fetchAll();
// System Info
$php_version = phpversion();
$mysql_version = db()->query("SELECT VERSION()")->fetchColumn();
$server_addr = $_SERVER['SERVER_ADDR'] ?? '127.0.0.1';
$upload_max = ini_get('upload_max_filesize');
$post_max = ini_get('post_max_size');
?> ?>
<div class="row"> <div class="row">
@ -146,86 +153,100 @@ $email_logs = db()->query("SELECT * FROM email_logs ORDER BY id DESC LIMIT 50")-
</div> </div>
<?php if ($success_msg): ?> <?php if ($success_msg): ?>
<div class="alert alert-success"><?= $success_msg ?></div> <div class="alert alert-success border-0 shadow-sm"><i class="fas fa-check-circle me-2"></i> <?= $success_msg ?></div>
<?php endif; ?> <?php endif; ?>
<?php if ($error_msg): ?> <?php if ($error_msg): ?>
<div class="alert alert-danger"><?= $error_msg ?></div> <div class="alert alert-danger border-0 shadow-sm"><i class="fas fa-exclamation-circle me-2"></i> <?= $error_msg ?></div>
<?php endif; ?> <?php endif; ?>
<div class="col-md-12"> <div class="col-md-12">
<ul class="nav nav-tabs mb-4" id="settingsTabs" role="tablist"> <div class="card shadow-sm border-0">
<div class="card-header bg-white p-0">
<ul class="nav nav-tabs border-bottom-0" id="settingsTabs" role="tablist">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active" id="general-tab" data-bs-toggle="tab" data-bs-target="#general" type="button" role="tab">بيانات النظام</button> <button class="nav-link active px-4 py-3" id="general-tab" data-bs-toggle="tab" data-bs-target="#general" type="button" role="tab">بيانات النظام</button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="smtp-tab" data-bs-toggle="tab" data-bs-target="#smtp" type="button" role="tab">إعدادات SMTP</button> <button class="nav-link px-4 py-3" id="smtp-tab" data-bs-toggle="tab" data-bs-target="#smtp" type="button" role="tab">إعدادات SMTP</button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="statuses-tab" data-bs-toggle="tab" data-bs-target="#statuses" type="button" role="tab">حالات البريد</button> <button class="nav-link px-4 py-3" id="statuses-tab" data-bs-toggle="tab" data-bs-target="#statuses" type="button" role="tab">حالات البريد</button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="logs-tab" data-bs-toggle="tab" data-bs-target="#logs" type="button" role="tab">سجلات البريد</button> <button class="nav-link px-4 py-3" id="logs-tab" data-bs-toggle="tab" data-bs-target="#logs" type="button" role="tab">سجلات البريد</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link px-4 py-3" id="sysinfo-tab" data-bs-toggle="tab" data-bs-target="#sysinfo" type="button" role="tab">معلومات الخادم</button>
</li> </li>
</ul> </ul>
</div>
<div class="tab-content bg-white p-4 shadow-sm rounded border" id="settingsTabsContent"> <div class="card-body p-4">
<div class="tab-content" id="settingsTabsContent">
<!-- General Settings --> <!-- General Settings -->
<div class="tab-pane show active" id="general" role="tabpanel"> <div class="tab-pane fade show active" id="general" role="tabpanel">
<h4 class="mb-4">بيانات النظام</h4> <h5 class="fw-bold mb-4 text-primary">بيانات النظام والجهة</h5>
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
<input type="hidden" name="update_charity" value="1"> <input type="hidden" name="update_charity" value="1">
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">اسم النظام</label> <label class="form-label fw-bold">اسم النظام / الجهة</label>
<input type="text" name="charity_name" class="form-control" value="<?= htmlspecialchars($charity['charity_name'] ?? '') ?>" required> <input type="text" name="charity_name" class="form-control" value="<?= htmlspecialchars($charity['charity_name'] ?? '') ?>" required>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">البريد الإلكتروني للنظام</label> <label class="form-label fw-bold">البريد الإلكتروني الرسمي</label>
<input type="email" name="charity_email" class="form-control" value="<?= htmlspecialchars($charity['charity_email'] ?? '') ?>"> <input type="email" name="charity_email" class="form-control" value="<?= htmlspecialchars($charity['charity_email'] ?? '') ?>">
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">رقم الهاتف</label> <label class="form-label fw-bold">رقم الهاتف</label>
<input type="text" name="charity_phone" class="form-control" value="<?= htmlspecialchars($charity['charity_phone'] ?? '') ?>"> <input type="text" name="charity_phone" class="form-control" value="<?= htmlspecialchars($charity['charity_phone'] ?? '') ?>">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">العنوان</label> <label class="form-label fw-bold">العنوان</label>
<textarea name="charity_address" class="form-control" rows="3"><?= htmlspecialchars($charity['charity_address'] ?? '') ?></textarea> <textarea name="charity_address" class="form-control" rows="3"><?= htmlspecialchars($charity['charity_address'] ?? '') ?></textarea>
</div> </div>
<div class="row"> <div class="row mt-4">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">شعار النظام</label> <label class="form-label fw-bold">شعار النظام</label>
<input type="file" name="charity_logo" class="form-control" accept="image/*"> <input type="file" name="charity_logo" class="form-control" accept="image/*">
<?php if ($charity['charity_logo']): ?> <?php if ($charity['charity_logo']): ?>
<div class="mt-2"><img src="<?= $charity['charity_logo'] ?>" alt="Logo" style="max-height: 80px;"></div> <div class="mt-3 p-3 bg-light rounded text-center border">
<img src="<?= $charity['charity_logo'] ?>" alt="Logo" style="max-height: 100px;">
<p class="small text-muted mt-2 mb-0">الشعار الحالي</p>
</div>
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">أيقونة الموقع (Favicon)</label> <label class="form-label fw-bold">أيقونة الموقع (Favicon)</label>
<input type="file" name="charity_favicon" class="form-control" accept="image/x-icon,image/png"> <input type="file" name="charity_favicon" class="form-control" accept="image/x-icon,image/png">
<?php if ($charity['charity_favicon']): ?> <?php if ($charity['charity_favicon']): ?>
<div class="mt-2"><img src="<?= $charity['charity_favicon'] ?>" alt="Favicon" style="max-height: 32px;"></div> <div class="mt-3 p-3 bg-light rounded text-center border">
<img src="<?= $charity['charity_favicon'] ?>" alt="Favicon" style="max-height: 48px;">
<p class="small text-muted mt-2 mb-0">الأيقونة الحالية</p>
</div>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
<button type="submit" class="btn btn-dark">تحديث إعدادات النظام</button> <div class="text-end mt-4">
<button type="submit" class="btn btn-dark px-4">حفظ جميع التغييرات</button>
</div>
</form> </form>
</div> </div>
<!-- SMTP Settings --> <!-- SMTP Settings -->
<div class="tab-pane" id="smtp" role="tabpanel"> <div class="tab-pane fade" id="smtp" role="tabpanel">
<div class="d-flex justify-content-between align-items-center mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<h4>إعدادات البريد (SMTP)</h4> <h5 class="fw-bold text-primary mb-0">إعدادات البريد (SMTP)</h5>
<?php if (!$smtp['is_enabled']): ?> <?php if (!$smtp['is_enabled']): ?>
<div class="badge bg-danger p-2"> <div class="alert alert-danger py-2 px-3 mb-0 d-flex align-items-center">
<i class="fas fa-exclamation-triangle me-1"></i> SMTP معطل بسبب كثرة الأخطاء <small><i class="fas fa-exclamation-triangle me-2"></i> SMTP معطل حالياً</small>
<form method="POST" style="display:inline;" class="ms-2"> <form method="POST" class="ms-3">
<button type="submit" name="enable_smtp" class="btn btn-sm btn-light">إعادة تفعيل</button> <button type="submit" name="enable_smtp" class="btn btn-sm btn-outline-danger">تفعيل الآن</button>
</form> </form>
</div> </div>
<?php else: ?> <?php else: ?>
<div class="badge bg-success p-2"> <div class="badge bg-success p-2">
<i class="fas fa-check-circle me-1"></i> SMTP مفعل (أخطاء: <?= $smtp['consecutive_failures'] ?>/<?= $smtp['max_failures'] ?>) <i class="fas fa-check-circle me-1"></i> SMTP يعمل (الأخطاء: <?= $smtp['consecutive_failures'] ?>/<?= $smtp['max_failures'] ?>)
</div> </div>
<?php endif; ?> <?php endif; ?>
</div> </div>
@ -234,102 +255,104 @@ $email_logs = db()->query("SELECT * FROM email_logs ORDER BY id DESC LIMIT 50")-
<input type="hidden" name="update_smtp" value="1"> <input type="hidden" name="update_smtp" value="1">
<div class="row"> <div class="row">
<div class="col-md-8 mb-3"> <div class="col-md-8 mb-3">
<label class="form-label">SMTP Host</label> <label class="form-label fw-bold">SMTP Host</label>
<input type="text" name="smtp_host" class="form-control" value="<?= htmlspecialchars($smtp['smtp_host'] ?? '') ?>"> <input type="text" name="smtp_host" class="form-control" value="<?= htmlspecialchars($smtp['smtp_host'] ?? '') ?>" placeholder="smtp.example.com">
</div> </div>
<div class="col-md-4 mb-3"> <div class="col-md-4 mb-3">
<label class="form-label">SMTP Port</label> <label class="form-label fw-bold">SMTP Port</label>
<input type="number" name="smtp_port" class="form-control" value="<?= htmlspecialchars($smtp['smtp_port'] ?? 587) ?>"> <input type="number" name="smtp_port" class="form-control" value="<?= htmlspecialchars($smtp['smtp_port'] ?? 587) ?>">
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">SMTP Security</label> <label class="form-label fw-bold">SMTP Security</label>
<select name="smtp_secure" class="form-select"> <select name="smtp_secure" class="form-select">
<option value="tls" <?= ($smtp['smtp_secure'] ?? '') === 'tls' ? 'selected' : '' ?>>TLS (Recommended)</option> <option value="tls" <?= ($smtp['smtp_secure'] ?? '') === 'tls' ? 'selected' : '' ?>>TLS (الأكثر أماناً)</option>
<option value="ssl" <?= ($smtp['smtp_secure'] ?? '') === 'ssl' ? 'selected' : '' ?>>SSL</option> <option value="ssl" <?= ($smtp['smtp_secure'] ?? '') === 'ssl' ? 'selected' : '' ?>>SSL</option>
<option value="none" <?= ($smtp['smtp_secure'] ?? '') === 'none' ? 'selected' : '' ?>>None</option> <option value="none" <?= ($smtp['smtp_secure'] ?? '') === 'none' ? 'selected' : '' ?>>بدون تشفير</option>
</select> </select>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">Sender Name</label> <label class="form-label fw-bold">اسم المرسل (Display Name)</label>
<input type="text" name="from_name" class="form-control" value="<?= htmlspecialchars($smtp['from_name'] ?? '') ?>"> <input type="text" name="from_name" class="form-control" value="<?= htmlspecialchars($smtp['from_name'] ?? '') ?>" placeholder="نظام المراسلات">
</div> </div>
</div> </div>
<div class="mb-3"> <div class="row">
<label class="form-label">SMTP Username</label> <div class="col-md-6 mb-3">
<label class="form-label fw-bold">SMTP Username</label>
<input type="text" name="smtp_user" class="form-control" value="<?= htmlspecialchars($smtp['smtp_user'] ?? '') ?>"> <input type="text" name="smtp_user" class="form-control" value="<?= htmlspecialchars($smtp['smtp_user'] ?? '') ?>">
</div> </div>
<div class="mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">SMTP Password</label> <label class="form-label fw-bold">SMTP Password</label>
<input type="password" name="smtp_pass" class="form-control" value="<?= htmlspecialchars($smtp['smtp_pass'] ?? '') ?>"> <input type="password" name="smtp_pass" class="form-control" value="<?= htmlspecialchars($smtp['smtp_pass'] ?? '') ?>">
</div> </div>
</div>
<div class="row"> <div class="row">
<div class="col-md-4 mb-3"> <div class="col-md-4 mb-3">
<label class="form-label">From Email</label> <label class="form-label fw-bold">From Email</label>
<input type="email" name="from_email" class="form-control" value="<?= htmlspecialchars($smtp['from_email'] ?? '') ?>"> <input type="email" name="from_email" class="form-control" value="<?= htmlspecialchars($smtp['from_email'] ?? '') ?>">
</div> </div>
<div class="col-md-4 mb-3"> <div class="col-md-4 mb-3">
<label class="form-label">Reply-To Email</label> <label class="form-label fw-bold">Reply-To Email</label>
<input type="email" name="reply_to" class="form-control" value="<?= htmlspecialchars($smtp['reply_to'] ?? '') ?>"> <input type="email" name="reply_to" class="form-control" value="<?= htmlspecialchars($smtp['reply_to'] ?? '') ?>">
</div> </div>
<div class="col-md-4 mb-3"> <div class="col-md-4 mb-3">
<label class="form-label">حد الأخطاء قبل التعطيل</label> <label class="form-label fw-bold">حد الأخطاء قبل التعطيل</label>
<input type="number" name="max_failures" class="form-control" value="<?= htmlspecialchars($smtp['max_failures'] ?? 5) ?>"> <input type="number" name="max_failures" class="form-control" value="<?= htmlspecialchars($smtp['max_failures'] ?? 5) ?>">
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary">حفظ إعدادات SMTP</button> <button type="submit" class="btn btn-primary">حفظ إعدادات البريد</button>
</form> </form>
<hr class="my-4"> <div class="mt-5 p-4 bg-light rounded border">
<h5>اختبار الإرسال</h5> <h6 class="fw-bold mb-3">اختبار الإرسال</h6>
<p class="small text-muted mb-3">أدخل بريداً إلكترونياً صالحاً لإرسال رسالة تجريبية للتأكد من صحة الإعدادات.</p>
<form method="POST"> <form method="POST">
<div class="input-group" style="max-width: 400px;"> <div class="input-group" style="max-width: 450px;">
<input type="email" name="test_email_addr" class="form-control" placeholder="بريد الوجهة" required> <input type="email" name="test_email_addr" class="form-control" placeholder="بريد الوجهة (example@mail.com)" required>
<button class="btn btn-outline-secondary" type="submit">إرسال تجريبي</button> <button class="btn btn-secondary" type="submit"><i class="fas fa-paper-plane me-2"></i> إرسال اختبار</button>
</div> </div>
</form> </form>
</div> </div>
</div>
<!-- Statuses Settings --> <!-- Statuses Settings -->
<div class="tab-pane" id="statuses" role="tabpanel"> <div class="tab-pane fade" id="statuses" role="tabpanel">
<h4 class="mb-4">أنواع حالات البريد</h4> <div class="d-flex justify-content-between align-items-center mb-4">
<form method="POST" class="mb-4 bg-light p-3 rounded"> <h5 class="fw-bold text-primary mb-0">أنواع حالات البريد</h5>
<input type="hidden" name="add_status" value="1"> <button class="btn btn-sm btn-primary" onclick="new bootstrap.Modal(document.getElementById('addStatusModal')).show()"><i class="fas fa-plus me-2"></i> إضافة حالة جديدة</button>
<div class="row g-2 align-items-end">
<div class="col-md-5">
<label class="form-label">اسم الحالة</label>
<input type="text" name="status_name" class="form-control" required>
</div> </div>
<div class="col-md-2">
<label class="form-label">اللون</label>
<input type="color" name="status_color" class="form-control form-control-color w-100" value="#0d6efd">
</div>
<div class="col-md-3 text-center">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="is_default" id="is_default">
<label class="form-check-label" for="is_default">افتراضية</label>
</div>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">إضافة</button>
</div>
</div>
</form>
<div class="table-responsive"> <div class="table-responsive">
<table class="table align-middle"> <table class="table table-hover align-middle border">
<thead> <thead class="bg-light">
<tr><th>الاسم</th><th>اللون</th><th>افتراضية</th><th class="text-end">الإجراء</th></tr> <tr><th>الحالة</th><th>كود اللون</th><th>افتراضية</th><th class="text-end">الإجراء</th></tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($statuses as $status): ?> <?php foreach ($statuses as $status): ?>
<tr> <tr>
<td><span class="badge" style="background-color: <?= $status['color'] ?>;"><?= htmlspecialchars($status['name']) ?></span></td> <td>
<span class="badge px-3 py-2" style="background-color: <?= $status['color'] ?>;">
<?= htmlspecialchars($status['name']) ?>
</span>
</td>
<td><code><?= $status['color'] ?></code></td> <td><code><?= $status['color'] ?></code></td>
<td><?= $status['is_default'] ? '<i class="fas fa-check text-success"></i>' : '' ?></td> <td>
<?php if ($status['is_default']): ?>
<span class="badge bg-success-subtle text-success border border-success px-2 py-1">نعم</span>
<?php else: ?>
<span class="text-muted">-</span>
<?php endif; ?>
</td>
<td class="text-end"> <td class="text-end">
<button type="button" class="btn btn-sm btn-outline-primary" onclick="editStatus(<?= $status['id'] ?>, '<?= htmlspecialchars($status['name'], ENT_QUOTES) ?>', '<?= $status['color'] ?>', <?= $status['is_default'] ?>)"><i class="fas fa-edit"></i></button> <button type="button" class="btn btn-sm btn-outline-primary" onclick="editStatus(<?= $status['id'] ?>, '<?= htmlspecialchars($status['name'], ENT_QUOTES) ?>', '<?= $status['color'] ?>', <?= $status['is_default'] ?>)"><i class="fas fa-edit"></i></button>
<form method="POST" onsubmit="return confirm('حذف؟');" style="display:inline;"><input type="hidden" name="status_id" value="<?= $status['id'] ?>"><input type="hidden" name="delete_status" value="1"><button type="submit" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></button></form> <?php if (!$status['is_default']): ?>
<form method="POST" onsubmit="return confirm('هل أنت متأكد من حذف هذه الحالة؟');" style="display:inline;">
<input type="hidden" name="status_id" value="<?= $status['id'] ?>">
<input type="hidden" name="delete_status" value="1">
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></button>
</form>
<?php endif; ?>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
@ -339,58 +362,147 @@ $email_logs = db()->query("SELECT * FROM email_logs ORDER BY id DESC LIMIT 50")-
</div> </div>
<!-- Email Logs --> <!-- Email Logs -->
<div class="tab-pane" id="logs" role="tabpanel"> <div class="tab-pane fade" id="logs" role="tabpanel">
<h4 class="mb-4">سجلات البريد المرسل (آخر 50 عملية)</h4> <h5 class="fw-bold text-primary mb-4">سجلات المراسلات البريدية (آخر 50 عملية)</h5>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-sm table-hover"> <table class="table table-sm table-hover border">
<thead> <thead class="bg-light">
<tr> <tr>
<th>الوقت</th> <th class="py-3 ps-3">الوقت والتاريخ</th>
<th>المستلم</th> <th class="py-3">المستلم</th>
<th>الموضوع</th> <th class="py-3">الموضوع</th>
<th>الحالة</th> <th class="py-3">الحالة</th>
<th>الخطأ</th> <th class="py-3 pe-3">تفاصيل الخطأ</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php if ($email_logs): ?>
<?php foreach ($email_logs as $log): ?> <?php foreach ($email_logs as $log): ?>
<tr> <tr>
<td class="small"><?= date('Y-m-d H:i', strtotime($log['created_at'])) ?></td> <td class="ps-3 small"><?= date('Y-m-d H:i:s', strtotime($log['created_at'])) ?></td>
<td><?= htmlspecialchars($log['recipient']) ?></td> <td><span class="fw-bold"><?= htmlspecialchars($log['recipient']) ?></span></td>
<td class="small"><?= htmlspecialchars($log['subject']) ?></td> <td class="small"><?= htmlspecialchars($log['subject']) ?></td>
<td> <td>
<span class="badge bg-<?= $log['status'] === 'success' ? 'success' : 'danger' ?>"> <span class="badge bg-<?= $log['status'] === 'success' ? 'success' : 'danger' ?>">
<?= $log['status'] === 'success' ? 'نجاح' : 'فشل' ?> <?= $log['status'] === 'success' ? 'تم الإرسال' : 'فشل' ?>
</span> </span>
</td> </td>
<td class="small text-danger"><?= htmlspecialchars($log['error_message'] ?? '') ?></td> <td class="pe-3 small text-danger"><?= htmlspecialchars($log['error_message'] ?? '-') ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php else: ?>
<tr><td colspan="5" class="text-center py-4 text-muted">لا يوجد سجلات حالياً</td></tr>
<?php endif; ?>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<!-- System Info Tab -->
<div class="tab-pane fade" id="sysinfo" role="tabpanel">
<h5 class="fw-bold text-primary mb-4">معلومات النظام والخادم</h5>
<div class="row g-4">
<div class="col-md-6">
<div class="p-3 border rounded bg-light">
<h6 class="fw-bold border-bottom pb-2 mb-3">بيئة البرمجيات</h6>
<table class="table table-sm table-borderless mb-0">
<tr><td width="150" class="text-muted">نسخة PHP:</td><td class="fw-bold"><?= $php_version ?></td></tr>
<tr><td class="text-muted">نسخة MySQL:</td><td class="fw-bold"><?= $mysql_version ?></td></tr>
<tr><td class="text-muted">نظام التشغيل:</td><td class="fw-bold"><?= PHP_OS ?></td></tr>
<tr><td class="text-muted">عنوان الخادم:</td><td class="fw-bold"><?= $server_addr ?></td></tr>
</table>
</div>
</div>
<div class="col-md-6">
<div class="p-3 border rounded bg-light">
<h6 class="fw-bold border-bottom pb-2 mb-3">إعدادات الملفات</h6>
<table class="table table-sm table-borderless mb-0">
<tr><td width="150" class="text-muted">أقصى حجم رفع:</td><td class="fw-bold"><?= $upload_max ?></td></tr>
<tr><td class="text-muted">أقصى حجم POST:</td><td class="fw-bold"><?= $post_max ?></td></tr>
<tr><td class="text-muted">ترميز قاعدة البيانات:</td><td class="fw-bold">utf8mb4_unicode_ci</td></tr>
<tr><td class="text-muted">نطاق النظام:</td><td class="fw-bold"><?= $_SERVER['HTTP_HOST'] ?></td></tr>
</table>
</div>
</div>
<div class="col-md-12">
<div class="p-3 border rounded bg-warning bg-opacity-10 border-warning">
<h6 class="fw-bold mb-2 text-warning-emphasis"><i class="fas fa-tools me-2"></i> أدوات الصيانة</h6>
<p class="small mb-3">هذه الأدوات مخصصة لمدير النظام فقط. يرجى توخي الحذر عند الاستخدام.</p>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-warning" onclick="alert('قريباً: نسخة احتياطية لقاعدة البيانات')"><i class="fas fa-database me-1"></i> نسخة احتياطية</button>
<button class="btn btn-sm btn-outline-secondary" onclick="location.reload()"><i class="fas fa-sync-alt me-1"></i> تحديث الحالة</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- Add Status Modal -->
<div class="modal fade" id="addStatusModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="POST">
<div class="modal-header">
<h5 class="modal-title fw-bold">إضافة حالة جديدة</h5>
<button type="button" class="btn-close ms-0 me-auto" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="add_status" value="1">
<div class="mb-3">
<label class="form-label fw-bold">اسم الحالة</label>
<input type="text" name="status_name" class="form-control" placeholder="مثال: تحت الدراسة" required>
</div>
<div class="mb-3">
<label class="form-label fw-bold">اللون التعريفي</label>
<input type="color" name="status_color" class="form-control form-control-color w-100" value="#0d6efd">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_default" id="add_is_default">
<label class="form-check-label" for="add_is_default">تعيين كحالة افتراضية للبريد الجديد</label>
</div>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-primary px-4">إضافة الحالة</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Status Modal -->
<div class="modal fade" id="editStatusModal" tabindex="-1"> <div class="modal fade" id="editStatusModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<form method="POST"> <form method="POST">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">تعديل نوع الحالة</h5> <h5 class="modal-title fw-bold">تعديل نوع الحالة</h5>
<button type="button" class="btn-close ms-0 me-auto" data-bs-dismiss="modal"></button> <button type="button" class="btn-close ms-0 me-auto" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<input type="hidden" name="update_status" value="1"><input type="hidden" name="status_id" id="edit_status_id"> <input type="hidden" name="update_status" value="1">
<div class="mb-3"><label class="form-label">اسم الحالة</label><input type="text" name="status_name" id="edit_status_name" class="form-control" required></div> <input type="hidden" name="status_id" id="edit_status_id">
<div class="mb-3"><label class="form-label">اللون</label><input type="color" name="status_color" id="edit_status_color" class="form-control form-control-color w-100"></div> <div class="mb-3">
<div class="form-check"><input class="form-check-input" type="checkbox" name="is_default" id="edit_is_default"><label class="form-check-label" for="edit_is_default">افتراضية</label></div> <label class="form-label fw-bold">اسم الحالة</label>
<input type="text" name="status_name" id="edit_status_name" class="form-control" required>
</div> </div>
<div class="modal-footer"> <div class="mb-3">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button> <label class="form-label fw-bold">اللون التعريفي</label>
<button type="submit" class="btn btn-primary">حفظ</button> <input type="color" name="status_color" id="edit_status_color" class="form-control form-control-color w-100">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_default" id="edit_is_default">
<label class="form-check-label" for="edit_is_default">تعيين كحالة افتراضية</label>
</div>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-primary px-4">حفظ التغييرات</button>
</div> </div>
</form> </form>
</div> </div>
@ -412,15 +524,26 @@ document.addEventListener('DOMContentLoaded', function() {
if (activeTab) { if (activeTab) {
var tabEl = document.querySelector('button[data-bs-target="' + activeTab + '"]'); var tabEl = document.querySelector('button[data-bs-target="' + activeTab + '"]');
if (tabEl) { if (tabEl) {
// Check if it's already shown to avoid flicker
if (!tabEl.classList.contains('active')) {
bootstrap.Tab.getInstance(tabEl)?.show() || new bootstrap.Tab(tabEl).show(); bootstrap.Tab.getInstance(tabEl)?.show() || new bootstrap.Tab(tabEl).show();
} }
} }
}
document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(function(tab) { document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(function(tab) {
tab.addEventListener('shown.bs.tab', function(e) { tab.addEventListener('shown.bs.tab', function(e) {
localStorage.setItem('activeSettingsTab', e.target.getAttribute('data-bs-target')); localStorage.setItem('activeSettingsTab', e.target.getAttribute('data-bs-target'));
}); });
}); });
// Handle hash in URL for direct tab access
if (window.location.hash) {
var hashTab = document.querySelector('button[data-bs-target="' + window.location.hash + '"]');
if (hashTab) {
bootstrap.Tab.getInstance(hashTab)?.show() || new bootstrap.Tab(hashTab).show();
}
}
}); });
</script> </script>

View File

@ -71,17 +71,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($ref_no && $subject) { if ($ref_no && $subject) {
try { try {
db()->beginTransaction();
if ($action === 'add') { if ($action === 'add') {
$stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$type, $ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $user_id]); $stmt->execute([$type, $ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $user_id]);
$mail_id = db()->lastInsertId();
if ($assigned_to) { if ($assigned_to) {
sendAssignmentNotification($assigned_to, $ref_no, $subject); sendAssignmentNotification($assigned_to, $ref_no, $subject);
} }
$_SESSION['success'] = 'تمت إضافة البريد بنجاح'; $_SESSION['success'] = 'تمت إضافة البريد بنجاح';
redirect('inbound.php');
} elseif ($action === 'edit') { } elseif ($action === 'edit') {
$mail_id = $id;
// Get previous assigned_to to check if it changed // Get previous assigned_to to check if it changed
$stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?"); $stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?");
$stmt_old->execute([$id]); $stmt_old->execute([$id]);
@ -95,9 +97,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
$_SESSION['success'] = 'تم تحديث البيانات بنجاح'; $_SESSION['success'] = 'تم تحديث البيانات بنجاح';
redirect('inbound.php');
} }
// Handle Attachments
if (!empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/attachments/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
foreach ($_FILES['attachments']['name'] as $key => $name) {
if ($_FILES['attachments']['error'][$key] === 0) {
$file_name = time() . '_' . basename($name);
$target_path = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['attachments']['tmp_name'][$key], $target_path)) {
$stmt = db()->prepare("INSERT INTO attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$mail_id, $name, $target_path, $name, $_FILES['attachments']['size'][$key]]);
}
}
}
}
db()->commit();
redirect('inbound.php');
} catch (PDOException $e) { } catch (PDOException $e) {
db()->rollBack();
if ($e->getCode() == 23000) { if ($e->getCode() == 23000) {
$error = 'رقم القيد مستخدم مسبقاً'; $error = 'رقم القيد مستخدم مسبقاً';
} else { } else {
@ -350,7 +372,7 @@ function getStatusBadgeInList($mail) {
<h5 class="modal-title fw-bold" id="mailModalLabel">إضافة بريد وارد جديد</h5> <h5 class="modal-title fw-bold" id="mailModalLabel">إضافة بريد وارد جديد</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<form id="mailForm" method="POST"> <form id="mailForm" method="POST" enctype="multipart/form-data">
<div class="modal-body p-4"> <div class="modal-body p-4">
<input type="hidden" name="action" id="modalAction" value="add"> <input type="hidden" name="action" id="modalAction" value="add">
<input type="hidden" name="id" id="modalId" value="0"> <input type="hidden" name="id" id="modalId" value="0">
@ -384,6 +406,10 @@ function getStatusBadgeInList($mail) {
<label class="form-label fw-bold">الوصف / ملاحظات</label> <label class="form-label fw-bold">الوصف / ملاحظات</label>
<textarea name="description" id="modalDescription" class="form-control" rows="3"></textarea> <textarea name="description" id="modalDescription" class="form-control" rows="3"></textarea>
</div> </div>
<div class="col-12">
<label class="form-label fw-bold">المرفقات</label>
<input type="file" name="attachments[]" class="form-control" multiple>
</div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-bold">الحالة</label> <label class="form-label fw-bold">الحالة</label>
<select name="status_id" id="modalStatusId" class="form-select"> <select name="status_id" id="modalStatusId" class="form-select">
@ -454,10 +480,12 @@ function openMailModal(action, data = null) {
label.textContent = 'إضافة بريد وارد جديد'; label.textContent = 'إضافة بريد وارد جديد';
modalId.value = '0'; modalId.value = '0';
Object.keys(fields).forEach(key => { Object.keys(fields).forEach(key => {
if (fields[key]) {
if (key === 'date_registered') fields[key].value = '<?= date('Y-m-d') ?>'; if (key === 'date_registered') fields[key].value = '<?= date('Y-m-d') ?>';
else if (key === 'status_id') fields[key].value = '<?= $default_status_id ?>'; else if (key === 'status_id') fields[key].value = '<?= $default_status_id ?>';
else if (key === 'ref_no') fields[key].value = '<?= generateRefNo('inbound') ?>'; else if (key === 'ref_no') fields[key].value = '<?= generateRefNo('inbound') ?>';
else fields[key].value = ''; else fields[key].value = '';
}
}); });
} else { } else {
label.textContent = 'تعديل البريد الوارد'; label.textContent = 'تعديل البريد الوارد';

View File

@ -2,29 +2,34 @@
</div><!-- Close main-content --> </div><!-- Close main-content -->
<?php endif; ?> <?php endif; ?>
<footer class="footer mt-auto py-3 bg-white border-top"> <footer class="footer mt-auto py-4 bg-white border-top">
<div class="container-fluid px-md-4 text-center"> <div class="container-fluid px-md-4 text-center">
<span class="text-muted small"> <div class="d-flex flex-column align-items-center">
<span class="text-muted small mb-1">
&copy; <?= date('Y') ?> <?= htmlspecialchars($charity_info['charity_name'] ?? 'نظام إدارة البريد') ?>. جميع الحقوق محفوظة. &copy; <?= date('Y') ?> <?= htmlspecialchars($charity_info['charity_name'] ?? 'نظام إدارة البريد') ?>. جميع الحقوق محفوظة.
</span> </span>
<div class="d-flex align-items-center gap-3">
<span class="badge bg-secondary opacity-50 fw-normal" style="font-size: 0.65rem;">نسخة النظام 1.2.0</span>
<?php if (isAdmin()): ?> <?php if (isAdmin()): ?>
<div class="mt-2"> <a href="charity-settings.php" onclick="localStorage.setItem('activeSettingsTab', '#general');" class="text-muted text-decoration-none small hover-primary border-start ps-3">
<ul class="list-inline mb-0">
<li class="list-inline-item">
<a href="charity-settings.php" class="text-muted text-decoration-none small hover-primary">
<i class="fas fa-cog me-1"></i> الإعدادات <i class="fas fa-cog me-1"></i> الإعدادات
</a> </a>
</li> <a href="charity-settings.php#logs" onclick="localStorage.setItem('activeSettingsTab', '#logs');" class="text-muted text-decoration-none small hover-primary border-start ps-3">
</ul> <i class="fas fa-history me-1"></i> سجل المراسلات
</div> </a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div>
</div>
</footer> </footer>
<style> <style>
.hover-primary:hover { .hover-primary:hover {
color: #0d6efd !important; color: #0d6efd !important;
} }
.footer .border-start {
border-color: rgba(0,0,0,0.1) !important;
}
</style> </style>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
@ -53,3 +58,8 @@
</script> </script>
</body> </body>
</html> </html>
<?php
if (ob_get_level() > 0) {
ob_end_flush();
}
?>

View File

@ -1,4 +1,5 @@
<?php <?php
ob_start();
session_start(); session_start();
require_once __DIR__ . '/../db/config.php'; require_once __DIR__ . '/../db/config.php';
@ -8,11 +9,18 @@ function isLoggedIn() {
} }
function isAdmin() { function isAdmin() {
return isset($_SESSION['role']) && $_SESSION['role'] === 'admin'; // Check session first
if (isset($_SESSION['user_role']) && strtolower($_SESSION['user_role']) === 'admin') return true;
if (isset($_SESSION['role']) && strtolower($_SESSION['role']) === 'admin') return true;
return false;
} }
function redirect($path) { function redirect($path) {
if (!headers_sent()) {
header("Location: $path"); header("Location: $path");
} else {
echo "<script>window.location.href='$path';</script>";
}
exit; exit;
} }
@ -32,15 +40,16 @@ function canEdit() {
return $_SESSION['can_edit'] ?? false; return $_SESSION['can_edit'] ?? false;
} }
function canViewInternal() {
if (isAdmin()) return true;
return canView();
}
function canDelete() { function canDelete() {
if (isAdmin()) return true; if (isAdmin()) return true;
return $_SESSION['can_delete'] ?? false; return $_SESSION['can_delete'] ?? false;
} }
if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename($_SERVER['PHP_SELF']) !== 'forgot_password.php') {
redirect('login.php');
}
// Fetch user info (theme and permissions) // Fetch user info (theme and permissions)
$user_theme = 'light'; $user_theme = 'light';
$current_user = null; $current_user = null;
@ -56,9 +65,16 @@ if (isLoggedIn()) {
$_SESSION['can_edit'] = (bool)$current_user['can_edit']; $_SESSION['can_edit'] = (bool)$current_user['can_edit'];
$_SESSION['can_delete'] = (bool)$current_user['can_delete']; $_SESSION['can_delete'] = (bool)$current_user['can_delete'];
$_SESSION['name'] = $current_user['full_name'] ?: $current_user['username']; $_SESSION['name'] = $current_user['full_name'] ?: $current_user['username'];
$_SESSION['user_role'] = strtolower($current_user['role']);
$_SESSION['role'] = strtolower($current_user['role']);
} }
} }
// Auth Check (after fetch to ensure session is updated)
if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php' && basename($_SERVER['PHP_SELF']) !== 'forgot_password.php') {
redirect('login.php');
}
// Fetch charity info for header/favicon // Fetch charity info for header/favicon
$charity_stmt = db()->query("SELECT * FROM charity_settings WHERE id = 1"); $charity_stmt = db()->query("SELECT * FROM charity_settings WHERE id = 1");
$charity_info = $charity_stmt->fetch(); $charity_info = $charity_stmt->fetch();
@ -90,7 +106,7 @@ $charity_info = $charity_stmt->fetch();
background-color: var(--bs-body-bg); background-color: var(--bs-body-bg);
} }
.sidebar { .sidebar {
min-height: 100vh; height: 100vh;
background-color: #212529; background-color: #212529;
color: #fff; color: #fff;
width: 250px; width: 250px;
@ -99,6 +115,16 @@ $charity_info = $charity_stmt->fetch();
right: 0; right: 0;
z-index: 1000; z-index: 1000;
transition: all 0.3s; transition: all 0.3s;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.2) transparent;
}
.sidebar::-webkit-scrollbar {
width: 6px;
}
.sidebar::-webkit-scrollbar-thumb {
background-color: rgba(255,255,255,0.2);
border-radius: 10px;
} }
.sidebar .nav-link { .sidebar .nav-link {
color: rgba(255,255,255,0.7); color: rgba(255,255,255,0.7);
@ -202,9 +228,9 @@ $charity_info = $charity_stmt->fetch();
<h5 class="mb-0 fw-bold"><?= htmlspecialchars($charity_info['charity_name'] ?? 'إدارة البريد') ?></h5> <h5 class="mb-0 fw-bold"><?= htmlspecialchars($charity_info['charity_name'] ?? 'إدارة البريد') ?></h5>
</div> </div>
<ul class="nav flex-column mt-3"> <ul class="nav flex-column mt-3 mb-4">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'user_dashboard.php' ? 'active' : '' ?>" href="user_dashboard.php"> <a class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'index.php' || basename($_SERVER['PHP_SELF']) == 'user_dashboard.php') ? 'active' : '' ?>" href="index.php">
<i class="fas fa-tachometer-alt me-2"></i> لوحة التحكم <i class="fas fa-tachometer-alt me-2"></i> لوحة التحكم
</a> </a>
</li> </li>
@ -233,14 +259,21 @@ $charity_info = $charity_stmt->fetch();
</a> </a>
</li> </li>
<?php if (isAdmin()): ?>
<div class="sidebar-heading">التقارير</div> <div class="sidebar-heading">التقارير</div>
<?php if (isAdmin()): ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'overdue_report.php' ? 'active' : '' ?>" href="overdue_report.php"> <a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'overdue_report.php' ? 'active' : '' ?>" href="overdue_report.php">
<i class="fas fa-clock me-2"></i> بريد متأخر <i class="fas fa-clock me-2"></i> بريد متأخر
</a> </a>
</li> </li>
<?php endif; ?>
<li class="nav-item">
<a class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'inbound.php' && isset($_GET['my_tasks'])) ? 'active' : '' ?>" href="inbound.php?my_tasks=1">
<i class="fas fa-tasks me-2"></i> مهامي الحالية
</a>
</li>
<?php if (isAdmin()): ?>
<div class="sidebar-heading">الإدارة</div> <div class="sidebar-heading">الإدارة</div>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'users.php' ? 'active' : '' ?>" href="users.php"> <a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'users.php' ? 'active' : '' ?>" href="users.php">
@ -248,8 +281,13 @@ $charity_info = $charity_stmt->fetch();
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'charity-settings.php' ? 'active' : '' ?>" href="charity-settings.php"> <a class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'charity-settings.php' && !isset($_GET['tab'])) ? 'active' : '' ?>" href="charity-settings.php" onclick="localStorage.setItem('activeSettingsTab', '#general');">
<i class="fas fa-cog me-2"></i> إعدادات النظام <i class="fas fa-cog me-2"></i> الإعدادات
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="charity-settings.php#logs" onclick="localStorage.setItem('activeSettingsTab', '#logs'); if(location.pathname.includes('charity-settings.php')) location.hash='#logs';">
<i class="fas fa-history me-2"></i> سجل المراسلات
</a> </a>
</li> </li>
<?php endif; ?> <?php endif; ?>

View File

@ -1,6 +1,10 @@
<?php <?php
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
if (!isAdmin()) { redirect('user_dashboard.php'); }
// Role-based routing: Admins stay here, others go to their dashboard
if (!isAdmin()) {
redirect('user_dashboard.php');
}
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
$is_admin = isAdmin(); $is_admin = isAdmin();
@ -41,7 +45,8 @@ $recent_mail_query = "SELECT m.*, s.name as status_name, s.color as status_color
FROM mailbox m FROM mailbox m
LEFT JOIN mailbox_statuses s ON m.status_id = s.id LEFT JOIN mailbox_statuses s ON m.status_id = s.id
LEFT JOIN users u ON m.assigned_to = u.id"; LEFT JOIN users u ON m.assigned_to = u.id";
if (!$is_admin && $_SESSION['user_role'] !== 'clerk') {
if (!$is_admin && ($_SESSION['user_role'] ?? '') !== 'clerk') {
$recent_mail_query .= " WHERE m.assigned_to = ? OR m.created_by = ?"; $recent_mail_query .= " WHERE m.assigned_to = ? OR m.created_by = ?";
$recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10"); $recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10");
$recent_stmt->execute([$user_id, $user_id]); $recent_stmt->execute([$user_id, $user_id]);
@ -66,10 +71,10 @@ function getStatusBadge($mail) {
?> ?>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">لوحة التحكم</h1> <h1 class="h2">لوحة التحكم الإدارية</h1>
<div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2"> <div class="btn-group me-2">
<?php if (isAdmin()): ?><a href="charity-settings.php" class="btn btn-sm btn-outline-dark"><i class="fas fa-cog me-1"></i> الإعدادات</a><?php endif; ?> <a href="charity-settings.php" class="btn btn-sm btn-outline-dark"><i class="fas fa-cog me-1"></i> الإعدادات</a>
<a href="inbound.php?action=add" class="btn btn-sm btn-outline-primary">إضافة بريد وارد</a> <a href="inbound.php?action=add" class="btn btn-sm btn-outline-primary">إضافة بريد وارد</a>
<a href="outbound.php" class="btn btn-sm btn-outline-secondary">إضافة بريد صادر</a> <a href="outbound.php" class="btn btn-sm btn-outline-secondary">إضافة بريد صادر</a>
</div> </div>
@ -78,7 +83,7 @@ function getStatusBadge($mail) {
<!-- Overdue Alert --> <!-- Overdue Alert -->
<?php <?php
$overdue_count = db()->query("SELECT COUNT(*) FROM mailbox WHERE due_date < CURDATE() AND status != 'closed'")->fetchColumn(); $overdue_count = db()->query("SELECT COUNT(*) FROM mailbox WHERE due_date < CURDATE() AND status_id IN (SELECT id FROM mailbox_statuses WHERE name != 'closed')")->fetchColumn();
if ($overdue_count > 0): if ($overdue_count > 0):
?> ?>
<div class="row mb-4"> <div class="row mb-4">
@ -88,9 +93,7 @@ if ($overdue_count > 0):
<i class="fas fa-exclamation-triangle fs-4 me-3"></i> <i class="fas fa-exclamation-triangle fs-4 me-3"></i>
<span class="fw-bold">هناك <?= $overdue_count ?> مهام متأخرة تتطلب انتباهك!</span> <span class="fw-bold">هناك <?= $overdue_count ?> مهام متأخرة تتطلب انتباهك!</span>
</div> </div>
<?php if (isAdmin()): ?>
<a href="overdue_report.php" class="btn btn-danger btn-sm">عرض التقرير</a> <a href="overdue_report.php" class="btn btn-danger btn-sm">عرض التقرير</a>
<?php endif; ?>
</div> </div>
</div> </div>
</div> </div>

View File

@ -22,8 +22,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
if ($recipient_id && $subject) { if ($recipient_id && $subject) {
try { try {
db()->beginTransaction();
$stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$type, $ref_no, $date_registered, $subject, $description, $default_status_id, $recipient_id, $user_id]); $stmt->execute([$type, $ref_no, $date_registered, $subject, $description, $default_status_id, $recipient_id, $user_id]);
$mail_id = db()->lastInsertId();
// Handle Attachments
if (!empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/attachments/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
foreach ($_FILES['attachments']['name'] as $key => $name) {
if ($_FILES['attachments']['error'][$key] === 0) {
$file_name = time() . '_' . basename($name);
$target_path = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['attachments']['tmp_name'][$key], $target_path)) {
$stmt = db()->prepare("INSERT INTO attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$mail_id, $name, $target_path, $name, $_FILES['attachments']['size'][$key]]);
}
}
}
}
// Notify recipient // Notify recipient
$stmt_recp = db()->prepare("SELECT full_name, email FROM users WHERE id = ?"); $stmt_recp = db()->prepare("SELECT full_name, email FROM users WHERE id = ?");
@ -46,9 +65,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
MailService::sendMail($recipient['email'], $email_subject, $htmlBody); MailService::sendMail($recipient['email'], $email_subject, $htmlBody);
} }
db()->commit();
$_SESSION['success'] = 'تم إرسال الرسالة بنجاح'; $_SESSION['success'] = 'تم إرسال الرسالة بنجاح';
redirect('internal_outbox.php'); redirect('internal_outbox.php');
} catch (PDOException $e) { } catch (PDOException $e) {
db()->rollBack();
$error = 'حدث خطأ: ' . $e->getMessage(); $error = 'حدث خطأ: ' . $e->getMessage();
} }
} else { } else {
@ -237,14 +258,14 @@ function getStatusBadgeInternal($mail) {
<h5 class="modal-title fw-bold" id="composeModalLabel">إنشاء رسالة جديدة للموظفين</h5> <h5 class="modal-title fw-bold" id="composeModalLabel">إنشاء رسالة جديدة للموظفين</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<form id="composeForm" method="POST"> <form id="composeForm" method="POST" enctype="multipart/form-data">
<div class="modal-body p-4"> <div class="modal-body p-4">
<input type="hidden" name="action" value="compose"> <input type="hidden" name="action" value="compose">
<div class="row g-3"> <div class="row g-3">
<div class="col-md-12"> <div class="col-md-12">
<label class="form-label fw-bold">إلى <span class="text-danger">*</span></label> <label class="form-label fw-bold">إلى <span class="text-danger">*</span></label>
<select name="recipient_id" class="form-select border-2" required> <select name="recipient_id" class="form-select border-2" required>
<option value="">-- اختر الموظف --</option> <option value="">-- اختر המوظف --</option>
<?php foreach ($users_list as $u): ?> <?php foreach ($users_list as $u): ?>
<option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['full_name'] . ' (@' . $u['username'] . ')') ?></option> <option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['full_name'] . ' (@' . $u['username'] . ')') ?></option>
<?php endforeach; ?> <?php endforeach; ?>
@ -258,6 +279,10 @@ function getStatusBadgeInternal($mail) {
<label class="form-label fw-bold">الرسالة</label> <label class="form-label fw-bold">الرسالة</label>
<textarea name="description" id="composeEditor" class="form-control border-2" rows="10"></textarea> <textarea name="description" id="composeEditor" class="form-control border-2" rows="10"></textarea>
</div> </div>
<div class="col-md-12">
<label class="form-label fw-bold">المرفقات</label>
<input type="file" name="attachments[]" class="form-control border-2" multiple>
</div>
</div> </div>
</div> </div>
<div class="modal-footer border-top-0 bg-light p-3"> <div class="modal-footer border-top-0 bg-light p-3">

View File

@ -9,7 +9,7 @@ $type_filter = $_GET['type'] ?? '';
$user_filter = $_GET['user_id'] ?? ''; $user_filter = $_GET['user_id'] ?? '';
$params = []; $params = [];
$where = ["due_date < CURDATE()", "status != 'closed'"]; $where = ["due_date < CURDATE()", "status != 'closed'", "type != 'internal'"];
if ($type_filter) { if ($type_filter) {
$where[] = "type = ?"; $where[] = "type = ?";

View File

@ -4,7 +4,6 @@ require_once __DIR__ . '/includes/header.php';
// Check if user has view permission // Check if user has view permission
if (!canView()) { if (!canView()) {
// If they can't even view, they shouldn't be here, but header.php already handles basic login. // If they can't even view, they shouldn't be here, but header.php already handles basic login.
// We'll let them see their profile at least, but maybe not this dashboard.
} }
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@ -44,9 +43,9 @@ $recent_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.f
LEFT JOIN users u ON m.assigned_to = u.id"; LEFT JOIN users u ON m.assigned_to = u.id";
if ($is_admin || $is_clerk) { if ($is_admin || $is_clerk) {
// Admins and Clerks see all recent activity if they have view permission // Admins and Clerks see all recent activity EXCEPT internal mail they are not part of
$recent_stmt = db()->prepare($recent_query . " ORDER BY m.updated_at DESC LIMIT 10"); $recent_stmt = db()->prepare($recent_query . " WHERE m.type != 'internal' OR m.assigned_to = ? OR m.created_by = ? ORDER BY m.updated_at DESC LIMIT 10");
$recent_stmt->execute(); $recent_stmt->execute([$user_id, $user_id]);
} else { } else {
// Staff see only theirs // Staff see only theirs
$recent_stmt = db()->prepare($recent_query . " WHERE m.assigned_to = ? OR m.created_by = ? ORDER BY m.updated_at DESC LIMIT 10"); $recent_stmt = db()->prepare($recent_query . " WHERE m.assigned_to = ? OR m.created_by = ? ORDER BY m.updated_at DESC LIMIT 10");
@ -199,7 +198,6 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
</div> </div>
</div>
<?php endif; ?> <?php endif; ?>
</div> </div>
@ -281,7 +279,7 @@ function getStatusBadge($mail) {
</div> </div>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<small class="text-muted"> <small class="text-muted">
<i class="fas <?= $act['type'] == 'inbound' ? 'fa-arrow-down text-primary' : 'fa-arrow-up text-success' ?> me-1"></i> <i class="fas <?= $act['type'] == 'inbound' ? 'fa-arrow-down text-primary' : ($act['type'] == 'outbound' ? 'fa-arrow-up text-success' : 'fa-exchange-alt text-info') ?> me-1"></i>
<?= $act['ref_no'] ?> <?= $act['ref_no'] ?>
</small> </small>
<?= getStatusBadge($act) ?> <?= getStatusBadge($act) ?>

View File

@ -23,8 +23,9 @@ $mail = $stmt->fetch();
if (!$mail) redirect('index.php'); if (!$mail) redirect('index.php');
// Security check for internal mail: only sender or recipient can view // Security check for internal mail: only sender or recipient can view
// Even admins should only see their own internal mail for privacy
if ($mail['type'] === 'internal') { if ($mail['type'] === 'internal') {
if ($mail['created_by'] != $_SESSION['user_id'] && $mail['assigned_to'] != $_SESSION['user_id'] && !isAdmin()) { if ($mail['created_by'] != $_SESSION['user_id'] && $mail['assigned_to'] != $_SESSION['user_id']) {
redirect('internal_inbox.php'); redirect('internal_inbox.php');
} }
} }