adding permissions

This commit is contained in:
Flatlogic Bot 2026-02-28 05:28:11 +00:00
parent 607b9d8838
commit 37abbe5d1e
12 changed files with 717 additions and 415 deletions

View File

@ -2,8 +2,8 @@
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/mail/MailService.php'; require_once __DIR__ . '/mail/MailService.php';
// Only admins can access this page // Only users with settings view permission can access this page
if (!isAdmin()) { if (!canView('settings')) {
redirect("index.php"); redirect("index.php");
} }
@ -12,8 +12,12 @@ $error_msg = '';
// Handle Re-enable SMTP // Handle Re-enable SMTP
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['enable_smtp'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['enable_smtp'])) {
db()->query("UPDATE smtp_settings SET is_enabled = 1, consecutive_failures = 0 WHERE id = 1"); if (canEdit('settings')) {
$_SESSION['success'] = 'تم إعادة تفعيل SMTP وتصفير عداد الأخطاء'; db()->query("UPDATE smtp_settings SET is_enabled = 1, consecutive_failures = 0 WHERE id = 1");
$_SESSION['success'] = 'تم إعادة تفعيل SMTP وتصفير عداد الأخطاء';
} else {
$_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لتعديل الإعدادات';
}
redirect('charity-settings.php'); redirect('charity-settings.php');
} }
@ -27,101 +31,125 @@ $smtp = $stmt->fetch();
// Handle Charity Settings Update // Handle Charity Settings Update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_charity'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_charity'])) {
$charity_name = $_POST['charity_name']; if (!canEdit('settings')) {
$charity_email = $_POST['charity_email']; $_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لتعديل الإعدادات';
$charity_phone = $_POST['charity_phone']; } else {
$charity_address = $_POST['charity_address']; $charity_name = $_POST['charity_name'];
$charity_logo = $charity['charity_logo']; $charity_email = $_POST['charity_email'];
$charity_favicon = $charity['charity_favicon']; $charity_phone = $_POST['charity_phone'];
$charity_address = $_POST['charity_address'];
$charity_logo = $charity['charity_logo'];
$charity_favicon = $charity['charity_favicon'];
$upload_dir = 'uploads/charity/'; $upload_dir = 'uploads/charity/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0775, true); if (!is_dir($upload_dir)) mkdir($upload_dir, 0775, true);
if (isset($_FILES['charity_logo']) && $_FILES['charity_logo']['error'] === UPLOAD_ERR_OK) { if (isset($_FILES['charity_logo']) && $_FILES['charity_logo']['error'] === UPLOAD_ERR_OK) {
$file_ext = pathinfo($_FILES['charity_logo']['name'], PATHINFO_EXTENSION); $file_ext = pathinfo($_FILES['charity_logo']['name'], PATHINFO_EXTENSION);
$new_logo = 'logo_' . time() . '.' . $file_ext; $new_logo = 'logo_' . time() . '.' . $file_ext;
if (move_uploaded_file($_FILES['charity_logo']['tmp_name'], $upload_dir . $new_logo)) { if (move_uploaded_file($_FILES['charity_logo']['tmp_name'], $upload_dir . $new_logo)) {
$charity_logo = $upload_dir . $new_logo; $charity_logo = $upload_dir . $new_logo;
}
} }
}
if (isset($_FILES['charity_favicon']) && $_FILES['charity_favicon']['error'] === UPLOAD_ERR_OK) { if (isset($_FILES['charity_favicon']) && $_FILES['charity_favicon']['error'] === UPLOAD_ERR_OK) {
$file_ext = pathinfo($_FILES['charity_favicon']['name'], PATHINFO_EXTENSION); $file_ext = pathinfo($_FILES['charity_favicon']['name'], PATHINFO_EXTENSION);
$new_favicon = 'favicon_' . time() . '.' . $file_ext; $new_favicon = 'favicon_' . time() . '.' . $file_ext;
if (move_uploaded_file($_FILES['charity_favicon']['tmp_name'], $upload_dir . $new_favicon)) { if (move_uploaded_file($_FILES['charity_favicon']['tmp_name'], $upload_dir . $new_favicon)) {
$charity_favicon = $upload_dir . $new_favicon; $charity_favicon = $upload_dir . $new_favicon;
}
} }
}
$stmt = db()->prepare("UPDATE charity_settings SET charity_name = ?, charity_email = ?, charity_phone = ?, charity_address = ?, charity_logo = ?, charity_favicon = ? WHERE id = 1"); $stmt = db()->prepare("UPDATE charity_settings SET charity_name = ?, charity_email = ?, charity_phone = ?, charity_address = ?, charity_logo = ?, charity_favicon = ? WHERE id = 1");
$stmt->execute([$charity_name, $charity_email, $charity_phone, $charity_address, $charity_logo, $charity_favicon]); $stmt->execute([$charity_name, $charity_email, $charity_phone, $charity_address, $charity_logo, $charity_favicon]);
$_SESSION['success'] = 'تم تحديث إعدادات النظام بنجاح'; $_SESSION['success'] = 'تم تحديث إعدادات النظام بنجاح';
}
redirect('charity-settings.php'); redirect('charity-settings.php');
} }
// Handle SMTP Settings Update // Handle SMTP Settings Update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_smtp'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_smtp'])) {
$stmt = db()->prepare("UPDATE smtp_settings SET smtp_host = ?, smtp_port = ?, smtp_secure = ?, smtp_user = ?, smtp_pass = ?, from_email = ?, from_name = ?, reply_to = ?, max_failures = ? WHERE id = 1"); if (!canEdit('settings')) {
$stmt->execute([ $_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لتعديل الإعدادات';
$_POST['smtp_host'], } else {
(int)$_POST['smtp_port'], $stmt = db()->prepare("UPDATE smtp_settings SET smtp_host = ?, smtp_port = ?, smtp_secure = ?, smtp_user = ?, smtp_pass = ?, from_email = ?, from_name = ?, reply_to = ?, max_failures = ? WHERE id = 1");
$_POST['smtp_secure'], $stmt->execute([
$_POST['smtp_user'], $_POST['smtp_host'],
$_POST['smtp_pass'], (int)$_POST['smtp_port'],
$_POST['from_email'], $_POST['smtp_secure'],
$_POST['from_name'], $_POST['smtp_user'],
$_POST['reply_to'], $_POST['smtp_pass'],
(int)$_POST['max_failures'] $_POST['from_email'],
]); $_POST['from_name'],
$_SESSION['success'] = 'تم تحديث إعدادات البريد (SMTP) بنجاح'; $_POST['reply_to'],
(int)$_POST['max_failures']
]);
$_SESSION['success'] = 'تم تحديث إعدادات البريد (SMTP) بنجاح';
}
redirect('charity-settings.php'); redirect('charity-settings.php');
} }
// Handle Test Email // Handle Test Email
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['test_email_addr'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['test_email_addr'])) {
$to = $_POST['test_email_addr']; if (!canEdit('settings')) {
$res = MailService::sendMail($to, "رسالة تجريبية - Test Email", "<p>إذا كنت ترى هذه الرسالة، فإن إعدادات SMTP تعمل بشكل صحيح.</p>"); $_SESSION['error'] = 'عذراً، ليس لديك الصلاحية للقيام بهذا الإجراء';
if ($res['success']) {
$_SESSION['success'] = "تم إرسال الرسالة التجريبية بنجاح إلى $to";
} else { } else {
$_SESSION['error'] = "فشل إرسال الرسالة التجريبية: " . $res['error']; $to = $_POST['test_email_addr'];
$res = MailService::sendMail($to, "رسالة تجريبية - Test Email", "<p>إذا كنت ترى هذه الرسالة، فإن إعدادات SMTP تعمل بشكل صحيح.</p>");
if ($res['success']) {
$_SESSION['success'] = "تم إرسال الرسالة التجريبية بنجاح إلى $to";
} else {
$_SESSION['error'] = "فشل إرسال الرسالة التجريبية: " . $res['error'];
}
} }
redirect('charity-settings.php'); redirect('charity-settings.php');
} }
// Handle Status Operations // Handle Status Operations
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_status'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_status'])) {
$name = $_POST['status_name']; if (!canEdit('settings')) {
$color = $_POST['status_color']; $_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لتعديل الإعدادات';
$is_default = isset($_POST['is_default']) ? 1 : 0; } else {
if ($is_default) db()->query("UPDATE mailbox_statuses SET is_default = 0"); $name = $_POST['status_name'];
$stmt = db()->prepare("INSERT INTO mailbox_statuses (name, color, is_default) VALUES (?, ?, ?)"); $color = $_POST['status_color'];
$stmt->execute([$name, $color, $is_default]); $is_default = isset($_POST['is_default']) ? 1 : 0;
$_SESSION['success'] = 'تم إضافة نوع الحالة بنجاح'; if ($is_default) db()->query("UPDATE mailbox_statuses SET is_default = 0");
$stmt = db()->prepare("INSERT INTO mailbox_statuses (name, color, is_default) VALUES (?, ?, ?)");
$stmt->execute([$name, $color, $is_default]);
$_SESSION['success'] = 'تم إضافة نوع الحالة بنجاح';
}
redirect('charity-settings.php'); redirect('charity-settings.php');
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_status'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_status'])) {
$id = $_POST['status_id']; if (!canEdit('settings')) {
$name = $_POST['status_name']; $_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لتعديل الإعدادات';
$color = $_POST['status_color']; } else {
$is_default = isset($_POST['is_default']) ? 1 : 0; $id = $_POST['status_id'];
if ($is_default) db()->query("UPDATE mailbox_statuses SET is_default = 0"); $name = $_POST['status_name'];
$stmt = db()->prepare("UPDATE mailbox_statuses SET name = ?, color = ?, is_default = ? WHERE id = ?"); $color = $_POST['status_color'];
$stmt->execute([$name, $color, $is_default, $id]); $is_default = isset($_POST['is_default']) ? 1 : 0;
$_SESSION['success'] = 'تم تحديث نوع الحالة بنجاح'; if ($is_default) db()->query("UPDATE mailbox_statuses SET is_default = 0");
$stmt = db()->prepare("UPDATE mailbox_statuses SET name = ?, color = ?, is_default = ? WHERE id = ?");
$stmt->execute([$name, $color, $is_default, $id]);
$_SESSION['success'] = 'تم تحديث نوع الحالة بنجاح';
}
redirect('charity-settings.php'); redirect('charity-settings.php');
} }
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_status'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_status'])) {
$id = $_POST['status_id']; if (!canDelete('settings')) {
$count = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE status_id = ?"); $_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لحذف الإعدادات';
$count->execute([$id]);
if ($count->fetchColumn() > 0) {
$_SESSION['error'] = 'لا يمكن حذف هذه الحالة لأنها مستخدمة في بعض السجلات';
} else { } else {
db()->prepare("DELETE FROM mailbox_statuses WHERE id = ?")->execute([$id]); $id = $_POST['status_id'];
$_SESSION['success'] = 'تم حذف نوع الحالة بنجاح'; $count = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE status_id = ?");
$count->execute([$id]);
if ($count->fetchColumn() > 0) {
$_SESSION['error'] = 'لا يمكن حذف هذه الحالة لأنها مستخدمة في بعض السجلات';
} else {
db()->prepare("DELETE FROM mailbox_statuses WHERE id = ?")->execute([$id]);
$_SESSION['success'] = 'تم حذف نوع الحالة بنجاح';
}
} }
redirect('charity-settings.php'); redirect('charity-settings.php');
} }
@ -228,7 +256,9 @@ $post_max = ini_get('post_max_size');
</div> </div>
</div> </div>
<div class="text-end mt-4"> <div class="text-end mt-4">
<?php if (canEdit('settings')): ?>
<button type="submit" class="btn btn-dark px-4">حفظ جميع التغييرات</button> <button type="submit" class="btn btn-dark px-4">حفظ جميع التغييرات</button>
<?php endif; ?>
</div> </div>
</form> </form>
</div> </div>
@ -240,9 +270,11 @@ $post_max = ini_get('post_max_size');
<?php if (!$smtp['is_enabled']): ?> <?php if (!$smtp['is_enabled']): ?>
<div class="alert alert-danger py-2 px-3 mb-0 d-flex align-items-center"> <div class="alert alert-danger py-2 px-3 mb-0 d-flex align-items-center">
<small><i class="fas fa-exclamation-triangle me-2"></i> SMTP معطل حالياً</small> <small><i class="fas fa-exclamation-triangle me-2"></i> SMTP معطل حالياً</small>
<?php if (canEdit('settings')): ?>
<form method="POST" class="ms-3"> <form method="POST" class="ms-3">
<button type="submit" name="enable_smtp" class="btn btn-sm btn-outline-danger">تفعيل الآن</button> <button type="submit" name="enable_smtp" class="btn btn-sm btn-outline-danger">تفعيل الآن</button>
</form> </form>
<?php endif; ?>
</div> </div>
<?php else: ?> <?php else: ?>
<div class="badge bg-success p-2"> <div class="badge bg-success p-2">
@ -301,7 +333,9 @@ $post_max = ini_get('post_max_size');
<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>
<?php if (canEdit('settings')): ?>
<button type="submit" class="btn btn-primary">حفظ إعدادات البريد</button> <button type="submit" class="btn btn-primary">حفظ إعدادات البريد</button>
<?php endif; ?>
</form> </form>
<div class="mt-5 p-4 bg-light rounded border"> <div class="mt-5 p-4 bg-light rounded border">
@ -310,7 +344,7 @@ $post_max = ini_get('post_max_size');
<form method="POST"> <form method="POST">
<div class="input-group" style="max-width: 450px;"> <div class="input-group" style="max-width: 450px;">
<input type="email" name="test_email_addr" class="form-control" placeholder="بريد الوجهة (example@mail.com)" required> <input type="email" name="test_email_addr" class="form-control" placeholder="بريد الوجهة (example@mail.com)" required>
<button class="btn btn-secondary" type="submit"><i class="fas fa-paper-plane me-2"></i> إرسال اختبار</button> <button class="btn btn-secondary" type="submit" <?= !canEdit('settings') ? 'disabled' : '' ?>><i class="fas fa-paper-plane me-2"></i> إرسال اختبار</button>
</div> </div>
</form> </form>
</div> </div>
@ -320,7 +354,9 @@ $post_max = ini_get('post_max_size');
<div class="tab-pane fade" id="statuses" role="tabpanel"> <div class="tab-pane fade" id="statuses" 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">
<h5 class="fw-bold text-primary mb-0">أنواع حالات البريد</h5> <h5 class="fw-bold text-primary mb-0">أنواع حالات البريد</h5>
<?php if (canAdd('settings')): ?>
<button class="btn btn-sm btn-primary" onclick="new bootstrap.Modal(document.getElementById('addStatusModal')).show()"><i class="fas fa-plus me-2"></i> إضافة حالة جديدة</button> <button class="btn btn-sm btn-primary" onclick="new bootstrap.Modal(document.getElementById('addStatusModal')).show()"><i class="fas fa-plus me-2"></i> إضافة حالة جديدة</button>
<?php endif; ?>
</div> </div>
<div class="table-responsive"> <div class="table-responsive">
@ -345,8 +381,10 @@ $post_max = ini_get('post_max_size');
<?php endif; ?> <?php endif; ?>
</td> </td>
<td class="text-end"> <td class="text-end">
<?php if (canEdit('settings')): ?>
<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>
<?php if (!$status['is_default']): ?> <?php endif; ?>
<?php if (canDelete('settings') && !$status['is_default']): ?>
<form method="POST" onsubmit="return confirm('هل أنت متأكد من حذف هذه الحالة؟');" style="display:inline;"> <form method="POST" onsubmit="return confirm('هل أنت متأكد من حذف هذه الحالة؟');" style="display:inline;">
<input type="hidden" name="status_id" value="<?= $status['id'] ?>"> <input type="hidden" name="status_id" value="<?= $status['id'] ?>">
<input type="hidden" name="delete_status" value="1"> <input type="hidden" name="delete_status" value="1">
@ -429,7 +467,9 @@ $post_max = ini_get('post_max_size');
<h6 class="fw-bold mb-2 text-warning-emphasis"><i class="fas fa-tools me-2"></i> أدوات الصيانة</h6> <h6 class="fw-bold mb-2 text-warning-emphasis"><i class="fas fa-tools me-2"></i> أدوات الصيانة</h6>
<p class="small mb-3">هذه الأدوات مخصصة لمدير النظام فقط. يرجى توخي الحذر عند الاستخدام.</p> <p class="small mb-3">هذه الأدوات مخصصة لمدير النظام فقط. يرجى توخي الحذر عند الاستخدام.</p>
<div class="d-flex gap-2"> <div class="d-flex gap-2">
<?php if (canEdit('settings')): ?>
<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-warning" onclick="alert('قريباً: نسخة احتياطية لقاعدة البيانات')"><i class="fas fa-database me-1"></i> نسخة احتياطية</button>
<?php endif; ?>
<button class="btn btn-sm btn-outline-secondary" onclick="location.reload()"><i class="fas fa-sync-alt 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>
@ -547,4 +587,4 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
</script> </script>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -0,0 +1,50 @@
-- Migration: Add per-page granular permissions
CREATE TABLE IF NOT EXISTS user_permissions (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
page VARCHAR(50) NOT NULL,
can_view TINYINT(1) DEFAULT 0,
can_add TINYINT(1) DEFAULT 0,
can_edit TINYINT(1) DEFAULT 0,
can_delete TINYINT(1) DEFAULT 0,
UNIQUE KEY user_page (user_id, page),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Seed permissions for existing users based on their roles
-- Inbound Mail
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, 'inbound', can_view, can_add, can_edit, can_delete FROM users;
-- Outbound Mail
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, 'outbound', can_view, can_add, can_edit, can_delete FROM users;
-- Internal Mail
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, 'internal', can_view, can_add, can_edit, can_delete FROM users;
-- Users (Only Admins)
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, 'users',
IF(role = 'admin', 1, 0),
IF(role = 'admin', 1, 0),
IF(role = 'admin', 1, 0),
IF(role = 'admin', 1, 0)
FROM users;
-- Settings (Only Admins)
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, 'settings',
IF(role = 'admin', 1, 0),
IF(role = 'admin', 1, 0),
IF(role = 'admin', 1, 0),
IF(role = 'admin', 1, 0)
FROM users;
-- Reports
INSERT IGNORE INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete)
SELECT id, 'reports',
IF(role IN ('admin', 'clerk'), 1, 0),
0, 0, 0
FROM users;

View File

@ -3,7 +3,7 @@ require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/mail/MailService.php'; require_once __DIR__ . '/mail/MailService.php';
// Check if user has view permission // Check if user has view permission
if (!canView()) { if (!canView('inbound')) {
redirect('index.php'); redirect('index.php');
} }
@ -54,7 +54,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? ''; $action = $_POST['action'] ?? '';
// Permission checks for POST actions // Permission checks for POST actions
if (($action === 'add' && !canAdd()) || ($action === 'edit' && !canEdit())) { if (($action === 'add' && !canAdd('inbound')) || ($action === 'edit' && !canEdit('inbound'))) {
$error = 'عذراً، ليس لديك الصلاحية للقيام بهذا الإجراء'; $error = 'عذراً، ليس لديك الصلاحية للقيام بهذا الإجراء';
} else { } else {
$type = 'inbound'; $type = 'inbound';
@ -134,7 +134,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Delete action // Delete action
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
if (!canDelete()) { if (!canDelete('inbound')) {
$error = 'عذراً، ليس لديك الصلاحية لحذف السجلات'; $error = 'عذراً، ليس لديك الصلاحية لحذف السجلات';
} else { } else {
$id = $_GET['id']; $id = $_GET['id'];
@ -206,7 +206,7 @@ $users_list = db()->query("SELECT id, full_name FROM users ORDER BY full_name")-
// Handle Deep Link for Edit // Handle Deep Link for Edit
$deepLinkData = null; $deepLinkData = null;
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
if (canEdit()) { if (canEdit('inbound')) {
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'inbound'"); $stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'inbound'");
$stmt->execute([$_GET['id']]); $stmt->execute([$_GET['id']]);
$deepLinkData = $stmt->fetch(); $deepLinkData = $stmt->fetch();
@ -229,7 +229,7 @@ function getStatusBadgeInList($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>
<?php if (canAdd()): ?> <?php if (canAdd('inbound')): ?>
<button type="button" class="btn btn-primary shadow-sm" onclick="openMailModal('add')"> <button type="button" class="btn btn-primary shadow-sm" onclick="openMailModal('add')">
<i class="fas fa-plus-circle me-1"></i> إضافة جديد <i class="fas fa-plus-circle me-1"></i> إضافة جديد
</button> </button>
@ -317,14 +317,14 @@ function getStatusBadgeInList($mail) {
<td class="pe-4 text-center"> <td class="pe-4 text-center">
<a href="view_mail.php?id=<?= $mail['id'] ?>" class="btn btn-sm btn-outline-info" title="عرض التفاصيل"><i class="fas fa-eye"></i></a> <a href="view_mail.php?id=<?= $mail['id'] ?>" class="btn btn-sm btn-outline-info" title="عرض التفاصيل"><i class="fas fa-eye"></i></a>
<?php if (canEdit()): ?> <?php if (canEdit('inbound')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" <button type="button" class="btn btn-sm btn-outline-primary"
onclick='openMailModal("edit", <?= json_encode($mail) ?>)' title="تعديل"> onclick='openMailModal("edit", <?= json_encode($mail) ?>)' title="تعديل">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</button> </button>
<?php endif; ?> <?php endif; ?>
<?php if (canDelete()): ?> <?php if (canDelete('inbound')): ?>
<a href="javascript:void(0)" onclick="confirmDelete(<?= $mail['id'] ?>)" class="btn btn-sm btn-outline-danger" title="حذف"><i class="fas fa-trash"></i></a> <a href="javascript:void(0)" onclick="confirmDelete(<?= $mail['id'] ?>)" class="btn btn-sm btn-outline-danger" title="حذف"><i class="fas fa-trash"></i></a>
<?php endif; ?> <?php endif; ?>
</td> </td>
@ -363,7 +363,7 @@ function getStatusBadgeInList($mail) {
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php if (canAdd() || canEdit()): ?> <?php if (canAdd('inbound') || canEdit('inbound')): ?>
<!-- Mail Modal --> <!-- Mail Modal -->
<div class="modal fade" id="mailModal" tabindex="-1" aria-labelledby="mailModalLabel" aria-hidden="true"> <div class="modal fade" id="mailModal" tabindex="-1" aria-labelledby="mailModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
@ -555,4 +555,4 @@ function confirmDelete(id) {
} }
</style> </style>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -9,7 +9,6 @@ function isLoggedIn() {
} }
function isAdmin() { function isAdmin() {
// Check session first
if (isset($_SESSION['user_role']) && strtolower($_SESSION['user_role']) === 'admin') return true; if (isset($_SESSION['user_role']) && strtolower($_SESSION['user_role']) === 'admin') return true;
if (isset($_SESSION['role']) && strtolower($_SESSION['role']) === 'admin') return true; if (isset($_SESSION['role']) && strtolower($_SESSION['role']) === 'admin') return true;
return false; return false;
@ -25,29 +24,40 @@ function redirect($path) {
} }
// Permission helpers // Permission helpers
function canView() { function canView($page = null) {
if (isAdmin()) return true; if (isAdmin()) return true;
if ($page) {
return $_SESSION['permissions'][$page]['view'] ?? false;
}
return $_SESSION['can_view'] ?? false; return $_SESSION['can_view'] ?? false;
} }
function canAdd() { function canAdd($page = null) {
if (isAdmin()) return true; if (isAdmin()) return true;
if ($page) {
return $_SESSION['permissions'][$page]['add'] ?? false;
}
return $_SESSION['can_add'] ?? false; return $_SESSION['can_add'] ?? false;
} }
function canEdit() { function canEdit($page = null) {
if (isAdmin()) return true; if (isAdmin()) return true;
if ($page) {
return $_SESSION['permissions'][$page]['edit'] ?? false;
}
return $_SESSION['can_edit'] ?? false; return $_SESSION['can_edit'] ?? false;
} }
function canViewInternal() { function canDelete($page = null) {
if (isAdmin()) return true; if (isAdmin()) return true;
return canView(); if ($page) {
return $_SESSION['permissions'][$page]['delete'] ?? false;
}
return $_SESSION['can_delete'] ?? false;
} }
function canDelete() { function canViewInternal() {
if (isAdmin()) return true; return canView('internal');
return $_SESSION['can_delete'] ?? false;
} }
// Fetch user info (theme and permissions) // Fetch user info (theme and permissions)
@ -67,6 +77,22 @@ if (isLoggedIn()) {
$_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['user_role'] = strtolower($current_user['role']);
$_SESSION['role'] = strtolower($current_user['role']); $_SESSION['role'] = strtolower($current_user['role']);
// Load granular permissions
if (!isset($_SESSION['permissions']) || empty($_SESSION['permissions'])) {
$perm_stmt = db()->prepare("SELECT * FROM user_permissions WHERE user_id = ?");
$perm_stmt->execute([$_SESSION['user_id']]);
$perms = $perm_stmt->fetchAll();
$_SESSION['permissions'] = [];
foreach ($perms as $p) {
$_SESSION['permissions'][$p['page']] = [
'view' => (bool)$p['can_view'],
'add' => (bool)$p['can_add'],
'edit' => (bool)$p['can_edit'],
'delete' => (bool)$p['can_delete']
];
}
}
} }
} }
@ -235,18 +261,27 @@ $charity_info = $charity_stmt->fetch();
</a> </a>
</li> </li>
<?php if (canView('inbound') || canView('outbound')): ?>
<div class="sidebar-heading">البريد الخارجي</div> <div class="sidebar-heading">البريد الخارجي</div>
<?php endif; ?>
<?php if (canView('inbound')): ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'inbound.php' ? 'active' : '' ?>" href="inbound.php"> <a class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'inbound.php' && !isset($_GET['my_tasks'])) ? 'active' : '' ?>" href="inbound.php">
<i class="fas fa-download me-2"></i> البريد الوارد <i class="fas fa-download me-2"></i> البريد الوارد
</a> </a>
</li> </li>
<?php endif; ?>
<?php if (canView('outbound')): ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'outbound.php' ? 'active' : '' ?>" href="outbound.php"> <a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'outbound.php' ? 'active' : '' ?>" href="outbound.php">
<i class="fas fa-upload me-2"></i> البريد الصادر <i class="fas fa-upload me-2"></i> البريد الصادر
</a> </a>
</li> </li>
<?php endif; ?>
<?php if (canView('internal')): ?>
<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']) == 'internal_inbox.php' ? 'active' : '' ?>" href="internal_inbox.php"> <a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'internal_inbox.php' ? 'active' : '' ?>" href="internal_inbox.php">
@ -258,28 +293,38 @@ $charity_info = $charity_stmt->fetch();
<i class="fas fa-paper-plane me-2"></i> الصادر الداخلي <i class="fas fa-paper-plane me-2"></i> الصادر الداخلي
</a> </a>
</li> </li>
<?php endif; ?>
<div class="sidebar-heading">التقارير</div> <div class="sidebar-heading">التقارير</div>
<?php if (isAdmin()): ?> <?php if (canView('reports')): ?>
<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; ?> <?php endif; ?>
<?php if (canView('inbound')): ?>
<li class="nav-item"> <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"> <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> مهامي الحالية <i class="fas fa-tasks me-2"></i> مهامي الحالية
</a> </a>
</li> </li>
<?php endif; ?>
<?php if (isAdmin()): ?> <?php if (canView('users') || canView('settings')): ?>
<div class="sidebar-heading">الإدارة</div> <div class="sidebar-heading">الإدارة</div>
<?php endif; ?>
<?php if (canView('users')): ?>
<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">
<i class="fas fa-users me-2"></i> إدارة المستخدمين <i class="fas fa-users me-2"></i> إدارة المستخدمين
</a> </a>
</li> </li>
<?php endif; ?>
<?php if (canView('settings')): ?>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link <?= (basename($_SERVER['PHP_SELF']) == 'charity-settings.php' && !isset($_GET['tab'])) ? 'active' : '' ?>" href="charity-settings.php" onclick="localStorage.setItem('activeSettingsTab', '#general');"> <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> الإعدادات
@ -344,4 +389,4 @@ $charity_info = $charity_stmt->fetch();
</nav> </nav>
<div class="main-content"> <div class="main-content">
<?php endif; ?> <?php endif; ?>

190
index.php
View File

@ -10,8 +10,8 @@ $user_id = $_SESSION['user_id'];
$is_admin = isAdmin(); $is_admin = isAdmin();
// Stats // Stats
$total_inbound = db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'inbound'")->fetchColumn(); $total_inbound = canView('inbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'inbound'")->fetchColumn() : 0;
$total_outbound = db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'outbound'")->fetchColumn(); $total_outbound = canView('outbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'outbound'")->fetchColumn() : 0;
// Fetch statuses for badge and count // Fetch statuses for badge and count
$statuses_data = db()->query("SELECT * FROM mailbox_statuses")->fetchAll(PDO::FETCH_UNIQUE); $statuses_data = db()->query("SELECT * FROM mailbox_statuses")->fetchAll(PDO::FETCH_UNIQUE);
@ -26,35 +26,60 @@ foreach ($statuses_data as $id => $s) {
} }
$in_progress_count = 0; $in_progress_count = 0;
if ($in_progress_id) { if ($in_progress_id) {
$stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE status_id = ?"); $where_types = [];
$stmt->execute([$in_progress_id]); if (canView('inbound')) $where_types[] = "'inbound'";
$in_progress_count = $stmt->fetchColumn(); if (canView('outbound')) $where_types[] = "'outbound'";
if (!empty($where_types)) {
$types_sql = implode(',', $where_types);
$stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE status_id = ? AND type IN ($types_sql)");
$stmt->execute([$in_progress_id]);
$in_progress_count = $stmt->fetchColumn();
}
} }
// My Assignments // My Assignments
$my_assignments = db()->prepare("SELECT m.*, s.name as status_name, s.color as status_color $my_assignments = [];
FROM mailbox m $assignment_types = [];
LEFT JOIN mailbox_statuses s ON m.status_id = s.id if (canView('inbound')) $assignment_types[] = "'inbound'";
WHERE m.assigned_to = ? if (canView('outbound')) $assignment_types[] = "'outbound'";
ORDER BY m.created_at DESC LIMIT 5"); if (canView('internal')) $assignment_types[] = "'internal'";
$my_assignments->execute([$user_id]);
$my_assignments = $my_assignments->fetchAll(); if (!empty($assignment_types)) {
$types_sql = implode(',', $assignment_types);
$my_assignments = db()->prepare("SELECT m.*, s.name as status_name, s.color as status_color
FROM mailbox m
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE m.assigned_to = ? AND m.type IN ($types_sql)
ORDER BY m.created_at DESC LIMIT 5");
$my_assignments->execute([$user_id]);
$my_assignments = $my_assignments->fetchAll();
}
// Recent Mail (Global for Admin/Clerk, otherwise limited) // Recent Mail (Global for Admin/Clerk, otherwise limited)
$recent_mail_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name $recent_mail = [];
FROM mailbox m $recent_types = [];
LEFT JOIN mailbox_statuses s ON m.status_id = s.id if (canView('inbound')) $recent_types[] = "'inbound'";
LEFT JOIN users u ON m.assigned_to = u.id"; if (canView('outbound')) $recent_types[] = "'outbound'";
if (!$is_admin && ($_SESSION['user_role'] ?? '') !== 'clerk') { if (!empty($recent_types)) {
$recent_mail_query .= " WHERE m.assigned_to = ? OR m.created_by = ?"; $types_sql = implode(',', $recent_types);
$recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10"); $recent_mail_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name
$recent_stmt->execute([$user_id, $user_id]); FROM mailbox m
} else { LEFT JOIN mailbox_statuses s ON m.status_id = s.id
$recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10"); LEFT JOIN users u ON m.assigned_to = u.id
$recent_stmt->execute(); WHERE m.type IN ($types_sql)";
if (!$is_admin && ($_SESSION['user_role'] ?? '') !== 'clerk') {
$recent_mail_query .= " AND (m.assigned_to = ? OR m.created_by = ?)";
$recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10");
$recent_stmt->execute([$user_id, $user_id]);
} else {
$recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10");
$recent_stmt->execute();
}
$recent_mail = $recent_stmt->fetchAll();
} }
$recent_mail = $recent_stmt->fetchAll();
function getStatusBadge($mail) { function getStatusBadge($mail) {
$status_name = $mail['status_name'] ?? 'غير معروف'; $status_name = $mail['status_name'] ?? 'غير معروف';
@ -74,33 +99,44 @@ function getStatusBadge($mail) {
<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 (canView('settings')): ?>
<a href="charity-settings.php" class="btn btn-sm btn-outline-dark"><i class="fas fa-cog me-1"></i> الإعدادات</a> <a href="charity-settings.php" class="btn btn-sm btn-outline-dark"><i class="fas fa-cog me-1"></i> الإعدادات</a>
<?php endif; ?>
<?php if (canAdd('inbound')): ?>
<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>
<?php endif; ?>
<?php if (canAdd('outbound')): ?>
<a href="outbound.php" class="btn btn-sm btn-outline-secondary">إضافة بريد صادر</a> <a href="outbound.php" class="btn btn-sm btn-outline-secondary">إضافة بريد صادر</a>
<?php endif; ?>
</div> </div>
</div> </div>
</div> </div>
<!-- Overdue Alert --> <!-- Overdue Alert -->
<?php <?php
$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 (canView('reports')):
if ($overdue_count > 0): $overdue_count = db()->query("SELECT COUNT(*) FROM mailbox WHERE due_date < CURDATE() AND status_id IN (SELECT id FROM mailbox_statuses WHERE name != 'closed') AND type != 'internal'")->fetchColumn();
?> if ($overdue_count > 0):
<div class="row mb-4"> ?>
<div class="col-12"> <div class="row mb-4">
<div class="alert alert-danger shadow-sm border-0 d-flex align-items-center justify-content-between mb-0"> <div class="col-12">
<div> <div class="alert alert-danger shadow-sm border-0 d-flex align-items-center justify-content-between mb-0">
<i class="fas fa-exclamation-triangle fs-4 me-3"></i> <div>
<span class="fw-bold">هناك <?= $overdue_count ?> مهام متأخرة تتطلب انتباهك!</span> <i class="fas fa-exclamation-triangle fs-4 me-3"></i>
<span class="fw-bold">هناك <?= $overdue_count ?> مهام متأخرة تتطلب انتباهك!</span>
</div>
<a href="overdue_report.php" class="btn btn-danger btn-sm">عرض التقرير</a>
</div> </div>
<a href="overdue_report.php" class="btn btn-danger btn-sm">عرض التقرير</a>
</div> </div>
</div> </div>
</div> <?php
<?php endif; ?> endif;
endif;
?>
<!-- Stats Cards --> <!-- Stats Cards -->
<div class="row g-4 mb-4"> <div class="row g-4 mb-4">
<?php if (canView('inbound')): ?>
<div class="col-md-3"> <div class="col-md-3">
<div class="card h-100 p-3 shadow-sm border-0 border-start border-primary border-4"> <div class="card h-100 p-3 shadow-sm border-0 border-start border-primary border-4">
<div class="d-flex align-items-center justify-content-between"> <div class="d-flex align-items-center justify-content-between">
@ -114,6 +150,9 @@ if ($overdue_count > 0):
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
<?php if (canView('outbound')): ?>
<div class="col-md-3"> <div class="col-md-3">
<div class="card h-100 p-3 shadow-sm border-0 border-start border-success border-4"> <div class="card h-100 p-3 shadow-sm border-0 border-start border-success border-4">
<div class="d-flex align-items-center justify-content-between"> <div class="d-flex align-items-center justify-content-between">
@ -127,6 +166,9 @@ if ($overdue_count > 0):
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
<?php if (canView('inbound') || canView('outbound')): ?>
<div class="col-md-3"> <div class="col-md-3">
<div class="card h-100 p-3 shadow-sm border-0 border-start border-info border-4"> <div class="card h-100 p-3 shadow-sm border-0 border-start border-info border-4">
<div class="d-flex align-items-center justify-content-between"> <div class="d-flex align-items-center justify-content-between">
@ -140,6 +182,9 @@ if ($overdue_count > 0):
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
<?php if (canView('users')): ?>
<div class="col-md-3"> <div class="col-md-3">
<div class="card h-100 p-3 shadow-sm border-0 border-start border-warning border-4"> <div class="card h-100 p-3 shadow-sm border-0 border-start border-warning border-4">
<div class="d-flex align-items-center justify-content-between"> <div class="d-flex align-items-center justify-content-between">
@ -153,6 +198,7 @@ if ($overdue_count > 0):
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
</div> </div>
<?php if (!empty($my_assignments)): ?> <?php if (!empty($my_assignments)): ?>
@ -204,6 +250,7 @@ if ($overdue_count > 0):
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php if (!empty($recent_mail)): ?>
<!-- Recent Mail --> <!-- Recent Mail -->
<div class="card shadow-sm border-0 mb-4"> <div class="card shadow-sm border-0 mb-4">
<div class="card-header bg-white py-3"> <div class="card-header bg-white py-3">
@ -228,48 +275,43 @@ if ($overdue_count > 0):
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php if ($recent_mail): ?> <?php foreach ($recent_mail as $mail): ?>
<?php foreach ($recent_mail as $mail): ?> <tr style="cursor: pointer;" onclick="window.location='view_mail.php?id=<?= $mail['id'] ?>'">
<tr style="cursor: pointer;" onclick="window.location='view_mail.php?id=<?= $mail['id'] ?>'"> <td class="ps-4 fw-bold text-primary"><?= $mail['ref_no'] ?></td>
<td class="ps-4 fw-bold text-primary"><?= $mail['ref_no'] ?></td> <td>
<td> <?php if ($mail['type'] == 'inbound'): ?>
<?php if ($mail['type'] == 'inbound'): ?> <span class="text-primary"><i class="fas fa-arrow-down me-1"></i> وارد</span>
<span class="text-primary"><i class="fas fa-arrow-down me-1"></i> وارد</span> <?php else: ?>
<?php else: ?> <span class="text-success"><i class="fas fa-arrow-up me-1"></i> صادر</span>
<span class="text-success"><i class="fas fa-arrow-up me-1"></i> صادر</span> <?php endif; ?>
<?php endif; ?> </td>
</td> <td><?= htmlspecialchars($mail['subject']) ?></td>
<td><?= htmlspecialchars($mail['subject']) ?></td> <td>
<td> <?php if ($mail['due_date']): ?>
<?php if ($mail['due_date']): ?> <small class="<?= (strtotime($mail['due_date']) < time() && $mail['status_name'] != 'closed') ? 'text-danger fw-bold' : 'text-muted' ?>">
<small class="<?= (strtotime($mail['due_date']) < time() && $mail['status_name'] != 'closed') ? 'text-danger fw-bold' : 'text-muted' ?>"> <?= $mail['due_date'] ?>
<?= $mail['due_date'] ?> </small>
</small> <?php else: ?>
<?php else: ?> <small class="text-muted">-</small>
<small class="text-muted">-</small> <?php endif; ?>
<?php endif; ?> </td>
</td> <td><?= htmlspecialchars($mail['sender'] ?: $mail['recipient']) ?></td>
<td><?= htmlspecialchars($mail['sender'] ?: $mail['recipient']) ?></td> <td>
<td> <?php if ($mail['assigned_to_name']): ?>
<?php if ($mail['assigned_to_name']): ?> <small><i class="fas fa-user-tag me-1 text-muted"></i> <?= htmlspecialchars($mail['assigned_to_name']) ?></small>
<small><i class="fas fa-user-tag me-1 text-muted"></i> <?= htmlspecialchars($mail['assigned_to_name']) ?></small> <?php else: ?>
<?php else: ?> <small class="text-muted">غير معين</small>
<small class="text-muted">غير معين</small> <?php endif; ?>
<?php endif; ?> </td>
</td> <td><?= getStatusBadge($mail) ?></td>
<td><?= getStatusBadge($mail) ?></td> <td class="pe-4 text-center"><?= date('Y-m-d', strtotime($mail['date_registered'])) ?></td>
<td class="pe-4 text-center"><?= date('Y-m-d', strtotime($mail['date_registered'])) ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="8" class="text-center py-4 text-muted">لا يوجد بريد مسجل حالياً</td>
</tr> </tr>
<?php endif; ?> <?php endforeach; ?>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,9 +1,9 @@
<?php <?php
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
// Every logged-in user can access their own internal mail // Every logged-in user can access their own internal mail if they have permission
if (!isLoggedIn()) { if (!canView('internal')) {
redirect('login.php'); redirect('index.php');
} }
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@ -61,9 +61,11 @@ function getStatusBadgeInternal($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"><i class="fas fa-inbox me-2 text-primary"></i> بريد الموظفين - الوارد</h1> <h1 class="h2"><i class="fas fa-inbox me-2 text-primary"></i> بريد الموظفين - الوارد</h1>
<div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-toolbar mb-2 mb-md-0">
<?php if (canAdd('internal')): ?>
<a href="internal_outbox.php?action=compose" class="btn btn-primary shadow-sm"> <a href="internal_outbox.php?action=compose" class="btn btn-primary shadow-sm">
<i class="fas fa-paper-plane me-1"></i> إرسال رسالة جديدة <i class="fas fa-paper-plane me-1"></i> إرسال رسالة جديدة
</a> </a>
<?php endif; ?>
</div> </div>
</div> </div>
@ -161,4 +163,4 @@ function getStatusBadgeInternal($mail) {
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -2,8 +2,8 @@
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/mail/MailService.php'; require_once __DIR__ . '/mail/MailService.php';
if (!isLoggedIn()) { if (!canView('internal')) {
redirect('login.php'); redirect('index.php');
} }
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@ -12,68 +12,72 @@ $error = '';
// Handle composing a new message // Handle composing a new message
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'compose') { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'compose') {
$recipient_id = $_POST['recipient_id'] ?? null; if (!canAdd('internal')) {
$subject = $_POST['subject'] ?? ''; $error = 'عذراً، ليس لديك الصلاحية لإرسال رسائل';
$description = $_POST['description'] ?? ''; } else {
$type = 'internal'; $recipient_id = $_POST['recipient_id'] ?? null;
$ref_no = generateRefNo('internal'); $subject = $_POST['subject'] ?? '';
$date_registered = date('Y-m-d'); $description = $_POST['description'] ?? '';
$default_status_id = db()->query("SELECT id FROM mailbox_statuses WHERE is_default = 1 LIMIT 1")->fetchColumn() ?: 1; $type = 'internal';
$ref_no = generateRefNo('internal');
$date_registered = date('Y-m-d');
$default_status_id = db()->query("SELECT id FROM mailbox_statuses WHERE is_default = 1 LIMIT 1")->fetchColumn() ?: 1;
if ($recipient_id && $subject) { if ($recipient_id && $subject) {
try { try {
db()->beginTransaction(); 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(); $mail_id = db()->lastInsertId();
// Handle Attachments // Handle Attachments
if (!empty($_FILES['attachments']['name'][0])) { if (!empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/attachments/'; $upload_dir = 'uploads/attachments/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true); if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
foreach ($_FILES['attachments']['name'] as $key => $name) { foreach ($_FILES['attachments']['name'] as $key => $name) {
if ($_FILES['attachments']['error'][$key] === 0) { if ($_FILES['attachments']['error'][$key] === 0) {
$file_name = time() . '_' . basename($name); $file_name = time() . '_' . basename($name);
$target_path = $upload_dir . $file_name; $target_path = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['attachments']['tmp_name'][$key], $target_path)) { 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 = 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]]); $stmt->execute([$mail_id, $name, $target_path, $name, $_FILES['attachments']['size'][$key]]);
}
} }
} }
} }
// Notify recipient
$stmt_recp = db()->prepare("SELECT full_name, email FROM users WHERE id = ?");
$stmt_recp->execute([$recipient_id]);
$recipient = $stmt_recp->fetch();
if ($recipient && !empty($recipient['email'])) {
$email_subject = "رسالة داخلية جديدة من " . $_SESSION['name'];
$htmlBody = "
<div dir='rtl' style='font-family: Arial, sans-serif;'>
<h3>لديك رسالة داخلية جديدة</h3>
<p><strong>الموضوع:</strong> " . htmlspecialchars($subject) . "</p>
<p><strong>المرسل:</strong> " . htmlspecialchars($_SESSION['name']) . "</p>
<hr>
<div>" . $description . "</div>
<br>
<p>يمكنك الرد من خلال النظام.</p>
</div>
";
MailService::sendMail($recipient['email'], $email_subject, $htmlBody);
}
db()->commit();
$_SESSION['success'] = 'تم إرسال الرسالة بنجاح';
redirect('internal_outbox.php');
} catch (PDOException $e) {
db()->rollBack();
$error = 'حدث خطأ: ' . $e->getMessage();
} }
} else {
// Notify recipient $error = 'يرجى اختيار المرسل إليه وكتابة الموضوع';
$stmt_recp = db()->prepare("SELECT full_name, email FROM users WHERE id = ?");
$stmt_recp->execute([$recipient_id]);
$recipient = $stmt_recp->fetch();
if ($recipient && !empty($recipient['email'])) {
$email_subject = "رسالة داخلية جديدة من " . $_SESSION['username'];
$htmlBody = "
<div dir='rtl' style='font-family: Arial, sans-serif;'>
<h3>لديك رسالة داخلية جديدة</h3>
<p><strong>الموضوع:</strong> " . htmlspecialchars($subject) . "</p>
<p><strong>المرسل:</strong> " . htmlspecialchars($_SESSION['username']) . "</p>
<hr>
<div>" . $description . "</div>
<br>
<p>يمكنك الرد من خلال النظام.</p>
</div>
";
MailService::sendMail($recipient['email'], $email_subject, $htmlBody);
}
db()->commit();
$_SESSION['success'] = 'تم إرسال الرسالة بنجاح';
redirect('internal_outbox.php');
} catch (PDOException $e) {
db()->rollBack();
$error = 'حدث خطأ: ' . $e->getMessage();
} }
} else {
$error = 'يرجى اختيار المرسل إليه وكتابة الموضوع';
} }
} }
@ -143,9 +147,11 @@ function getStatusBadgeInternal($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"><i class="fas fa-paper-plane me-2 text-success"></i> بريد الموظفين - الصادر</h1> <h1 class="h2"><i class="fas fa-paper-plane me-2 text-success"></i> بريد الموظفين - الصادر</h1>
<div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-toolbar mb-2 mb-md-0">
<?php if (canAdd('internal')): ?>
<button type="button" class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#composeModal"> <button type="button" class="btn btn-primary shadow-sm" data-bs-toggle="modal" data-bs-target="#composeModal">
<i class="fas fa-plus-circle me-1"></i> إنشاء رسالة جديدة <i class="fas fa-plus-circle me-1"></i> إنشاء رسالة جديدة
</button> </button>
<?php endif; ?>
</div> </div>
</div> </div>
@ -296,20 +302,22 @@ function getStatusBadgeInternal($mail) {
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
ClassicEditor if (typeof ClassicEditor !== 'undefined') {
.create(document.querySelector('#composeEditor'), { ClassicEditor
language: 'ar', .create(document.querySelector('#composeEditor'), {
toolbar: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'], language: 'ar',
direction: 'rtl' toolbar: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'],
}) direction: 'rtl'
.then(editor => { })
// Force RTL style on editable area .then(editor => {
editor.ui.view.editable.element.style.direction = 'rtl'; // Force RTL style on editable area
editor.ui.view.editable.element.style.textAlign = 'right'; editor.ui.view.editable.element.style.direction = 'rtl';
}) editor.ui.view.editable.element.style.textAlign = 'right';
.catch(error => { })
console.error(error); .catch(error => {
}); console.error(error);
});
}
<?php if (isset($_GET['action']) && $_GET['action'] === 'compose'): ?> <?php if (isset($_GET['action']) && $_GET['action'] === 'compose'): ?>
var myModal = new bootstrap.Modal(document.getElementById('composeModal')); var myModal = new bootstrap.Modal(document.getElementById('composeModal'));
@ -318,4 +326,4 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
</script> </script>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -3,7 +3,7 @@ require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/mail/MailService.php'; require_once __DIR__ . '/mail/MailService.php';
// Check if user has view permission // Check if user has view permission
if (!canView()) { if (!canView('outbound')) {
redirect('index.php'); redirect('index.php');
} }
@ -66,7 +66,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? ''; $action = $_POST['action'] ?? '';
// Permission checks for POST actions // Permission checks for POST actions
if (($action === 'add' && !canAdd()) || ($action === 'edit' && !canEdit())) { if (($action === 'add' && !canAdd('outbound')) || ($action === 'edit' && !canEdit('outbound'))) {
$error = 'عذراً، ليس لديك الصلاحية للقيام بهذا الإجراء'; $error = 'عذراً، ليس لديك الصلاحية للقيام بهذا الإجراء';
} else { } else {
$type = 'outbound'; $type = 'outbound';
@ -147,7 +147,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Delete action // Delete action
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
if (!canDelete()) { if (!canDelete('outbound')) {
$error = 'عذراً، ليس لديك الصلاحية لحذف السجلات'; $error = 'عذراً، ليس لديك الصلاحية لحذف السجلات';
} else { } else {
$id = $_GET['id']; $id = $_GET['id'];
@ -219,7 +219,7 @@ $users_list = db()->query("SELECT id, full_name FROM users ORDER BY full_name")-
// Handle Deep Link for Edit // Handle Deep Link for Edit
$deepLinkData = null; $deepLinkData = null;
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
if (canEdit()) { if (canEdit('outbound')) {
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'outbound'"); $stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'outbound'");
$stmt->execute([$_GET['id']]); $stmt->execute([$_GET['id']]);
$deepLinkData = $stmt->fetch(); $deepLinkData = $stmt->fetch();
@ -242,7 +242,7 @@ function getStatusBadgeInList($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>
<?php if (canAdd()): ?> <?php if (canAdd('outbound')): ?>
<button type="button" class="btn btn-primary shadow-sm" onclick="openMailModal('add')"> <button type="button" class="btn btn-primary shadow-sm" onclick="openMailModal('add')">
<i class="fas fa-plus-circle me-1"></i> إضافة جديد <i class="fas fa-plus-circle me-1"></i> إضافة جديد
</button> </button>
@ -330,14 +330,14 @@ function getStatusBadgeInList($mail) {
<td class="pe-4 text-center"> <td class="pe-4 text-center">
<a href="view_mail.php?id=<?= $mail['id'] ?>" class="btn btn-sm btn-outline-info" title="عرض التفاصيل"><i class="fas fa-eye"></i></a> <a href="view_mail.php?id=<?= $mail['id'] ?>" class="btn btn-sm btn-outline-info" title="عرض التفاصيل"><i class="fas fa-eye"></i></a>
<?php if (canEdit()): ?> <?php if (canEdit('outbound')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" <button type="button" class="btn btn-sm btn-outline-primary"
onclick='openMailModal("edit", <?= json_encode($mail) ?>)' title="تعديل"> onclick='openMailModal("edit", <?= json_encode($mail) ?>)' title="تعديل">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</button> </button>
<?php endif; ?> <?php endif; ?>
<?php if (canDelete()): ?> <?php if (canDelete('outbound')): ?>
<a href="javascript:void(0)" onclick="confirmDelete(<?= $mail['id'] ?>)" class="btn btn-sm btn-outline-danger" title="حذف"><i class="fas fa-trash"></i></a> <a href="javascript:void(0)" onclick="confirmDelete(<?= $mail['id'] ?>)" class="btn btn-sm btn-outline-danger" title="حذف"><i class="fas fa-trash"></i></a>
<?php endif; ?> <?php endif; ?>
</td> </td>
@ -376,7 +376,7 @@ function getStatusBadgeInList($mail) {
<?php endif; ?> <?php endif; ?>
</div> </div>
<?php if (canAdd() || canEdit()): ?> <?php if (canAdd('outbound') || canEdit('outbound')): ?>
<!-- Mail Modal --> <!-- Mail Modal -->
<div class="modal fade" id="mailModal" tabindex="-1" aria-labelledby="mailModalLabel" aria-hidden="true"> <div class="modal fade" id="mailModal" tabindex="-1" aria-labelledby="mailModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
@ -603,4 +603,4 @@ function confirmDelete(id) {
} }
</style> </style>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,7 +1,7 @@
<?php <?php
require_once 'includes/header.php'; require_once 'includes/header.php';
if (!isAdmin()) { if (!canView('reports')) {
redirect('index.php'); redirect('index.php');
} }
@ -123,4 +123,4 @@ $users = db()->query("SELECT id, full_name FROM users ORDER BY full_name")->fetc
</div> </div>
</div> </div>
<?php require_once 'includes/footer.php'; ?> <?php require_once 'includes/footer.php'; ?>

View File

@ -2,8 +2,8 @@
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
// Check if user has view permission // Check if user has view permission
if (!canView()) { if (!isLoggedIn()) {
// If they can't even view, they shouldn't be here, but header.php already handles basic login. redirect('login.php');
} }
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@ -16,42 +16,61 @@ $stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE assigned_to = ?");
$stmt->execute([$user_id]); $stmt->execute([$user_id]);
$my_total_assignments = $stmt->fetchColumn(); $my_total_assignments = $stmt->fetchColumn();
$stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE assigned_to = ? AND status != 'closed'"); $stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE assigned_to = ? AND status_id IN (SELECT id FROM mailbox_statuses WHERE name != 'closed')");
$stmt->execute([$user_id]); $stmt->execute([$user_id]);
$my_pending_tasks = $stmt->fetchColumn(); $my_pending_tasks = $stmt->fetchColumn();
// Global Stats (for Clerks or if we want to show them) // Global Stats (for Clerks or if we want to show them)
$total_inbound = db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'inbound'")->fetchColumn(); $total_inbound = canView('inbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'inbound'")->fetchColumn() : 0;
$total_outbound = db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'outbound'")->fetchColumn(); $total_outbound = canView('outbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'outbound'")->fetchColumn() : 0;
// Fetch statuses for badge and count // Fetch statuses for badge and count
$statuses_data = db()->query("SELECT * FROM mailbox_statuses")->fetchAll(PDO::FETCH_UNIQUE); $statuses_data = db()->query("SELECT * FROM mailbox_statuses")->fetchAll(PDO::FETCH_UNIQUE);
// My Assignments // My Assignments
$my_assignments = db()->prepare("SELECT m.*, s.name as status_name, s.color as status_color $my_assignments = [];
FROM mailbox m $assignment_types = [];
LEFT JOIN mailbox_statuses s ON m.status_id = s.id if (canView('inbound')) $assignment_types[] = "'inbound'";
WHERE m.assigned_to = ? if (canView('outbound')) $assignment_types[] = "'outbound'";
ORDER BY m.created_at DESC LIMIT 10"); if (canView('internal')) $assignment_types[] = "'internal'";
$my_assignments->execute([$user_id]);
$my_assignments = $my_assignments->fetchAll(); if (!empty($assignment_types)) {
$types_sql = implode(',', $assignment_types);
$my_assignments = db()->prepare("SELECT m.*, s.name as status_name, s.color as status_color
FROM mailbox m
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE m.assigned_to = ? AND m.type IN ($types_sql)
ORDER BY m.created_at DESC LIMIT 10");
$my_assignments->execute([$user_id]);
$my_assignments = $my_assignments->fetchAll();
}
// Recent Activity // Recent Activity
$recent_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name $recent_activity = [];
FROM mailbox m $recent_types = [];
LEFT JOIN mailbox_statuses s ON m.status_id = s.id if (canView('inbound')) $recent_types[] = "'inbound'";
LEFT JOIN users u ON m.assigned_to = u.id"; if (canView('outbound')) $recent_types[] = "'outbound'";
if (canView('internal')) $recent_types[] = "'internal'";
if ($is_admin || $is_clerk) { if (!empty($recent_types)) {
// Admins and Clerks see all recent activity EXCEPT internal mail they are not part of $types_sql = implode(',', $recent_types);
$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_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name
$recent_stmt->execute([$user_id, $user_id]); FROM mailbox m
} else { LEFT JOIN mailbox_statuses s ON m.status_id = s.id
// Staff see only theirs LEFT JOIN users u ON m.assigned_to = u.id
$recent_stmt = db()->prepare($recent_query . " WHERE m.assigned_to = ? OR m.created_by = ? ORDER BY m.updated_at DESC LIMIT 10"); WHERE m.type IN ($types_sql)";
$recent_stmt->execute([$user_id, $user_id]);
if ($is_admin || $is_clerk) {
// Admins and Clerks see all recent activity EXCEPT internal mail they are not part of
$recent_stmt = db()->prepare($recent_query . " AND (m.type != 'internal' OR m.assigned_to = ? OR m.created_by = ?) ORDER BY m.updated_at DESC LIMIT 10");
$recent_stmt->execute([$user_id, $user_id]);
} else {
// Staff see only theirs
$recent_stmt = db()->prepare($recent_query . " AND (m.assigned_to = ? OR m.created_by = ?) ORDER BY m.updated_at DESC LIMIT 10");
$recent_stmt->execute([$user_id, $user_id]);
}
$recent_activity = $recent_stmt->fetchAll();
} }
$recent_activity = $recent_stmt->fetchAll();
function getStatusBadge($mail) { function getStatusBadge($mail) {
$status_name = $mail['status_name'] ?? 'غير معروف'; $status_name = $mail['status_name'] ?? 'غير معروف';
@ -74,7 +93,7 @@ function getStatusBadge($mail) {
</div> </div>
<div class="d-flex justify-content-between align-items-center position-relative"> <div class="d-flex justify-content-between align-items-center position-relative">
<div> <div>
<h2 class="fw-bold mb-1">مرحباً، <?= htmlspecialchars($current_user['full_name'] ?? $_SESSION['username']) ?>!</h2> <h2 class="fw-bold mb-1">مرحباً، <?= htmlspecialchars($current_user['full_name'] ?? $_SESSION['name']) ?>!</h2>
<p class="mb-0 opacity-75"> <p class="mb-0 opacity-75">
أنت مسجل كـ <strong> أنت مسجل كـ <strong>
<?php <?php
@ -135,6 +154,7 @@ function getStatusBadge($mail) {
<?php if ($is_admin || $is_clerk): ?> <?php if ($is_admin || $is_clerk): ?>
<!-- Admin/Clerk specific stats --> <!-- Admin/Clerk specific stats -->
<?php if (canView('inbound')): ?>
<div class="col-md-3"> <div class="col-md-3">
<div class="card h-100 p-3 shadow-sm border-0 border-start border-info border-4"> <div class="card h-100 p-3 shadow-sm border-0 border-start border-info border-4">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@ -148,6 +168,8 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
<?php if (canView('outbound')): ?>
<div class="col-md-3"> <div class="col-md-3">
<div class="card h-100 p-3 shadow-sm border-0 border-start border-success border-4"> <div class="card h-100 p-3 shadow-sm border-0 border-start border-success border-4">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@ -161,6 +183,7 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
</div> </div>
<?php endif; ?>
<?php else: ?> <?php else: ?>
<!-- Staff specific stats --> <!-- Staff specific stats -->
<div class="col-md-3"> <div class="col-md-3">
@ -198,6 +221,7 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
</div> </div>
</div>
<?php endif; ?> <?php endif; ?>
</div> </div>
@ -208,8 +232,10 @@ function getStatusBadge($mail) {
<div class="card-header bg-white py-3 border-bottom d-flex justify-content-between align-items-center"> <div class="card-header bg-white py-3 border-bottom d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-bold"><i class="fas fa-clipboard-list me-2 text-primary"></i> مهامي المسندة</h5> <h5 class="mb-0 fw-bold"><i class="fas fa-clipboard-list me-2 text-primary"></i> مهامي المسندة</h5>
<div class="btn-group"> <div class="btn-group">
<?php if (canAdd()): ?> <?php if (canAdd('inbound')): ?>
<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>
<?php endif; ?>
<?php if (canAdd('outbound')): ?>
<a href="outbound.php" class="btn btn-sm btn-outline-success">إضافة صادر</a> <a href="outbound.php" class="btn btn-sm btn-outline-success">إضافة صادر</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
@ -227,7 +253,7 @@ function getStatusBadge($mail) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php if ($my_assignments): ?> <?php if (!empty($my_assignments)): ?>
<?php foreach ($my_assignments as $mail): ?> <?php foreach ($my_assignments as $mail): ?>
<tr style="cursor: pointer;" onclick="window.location='view_mail.php?id=<?= $mail['id'] ?>'"> <tr style="cursor: pointer;" onclick="window.location='view_mail.php?id=<?= $mail['id'] ?>'">
<td class="ps-4 fw-bold text-primary"><?= $mail['ref_no'] ?></td> <td class="ps-4 fw-bold text-primary"><?= $mail['ref_no'] ?></td>
@ -270,7 +296,7 @@ function getStatusBadge($mail) {
</div> </div>
<div class="card-body p-0" style="max-height: 500px; overflow-y: auto;"> <div class="card-body p-0" style="max-height: 500px; overflow-y: auto;">
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<?php if ($recent_activity): ?> <?php if (!empty($recent_activity)): ?>
<?php foreach ($recent_activity as $act): ?> <?php foreach ($recent_activity as $act): ?>
<a href="view_mail.php?id=<?= $act['id'] ?>" class="list-group-item list-group-item-action p-3 border-0 border-bottom"> <a href="view_mail.php?id=<?= $act['id'] ?>" class="list-group-item list-group-item-action p-3 border-0 border-bottom">
<div class="d-flex w-100 justify-content-between mb-1"> <div class="d-flex w-100 justify-content-between mb-1">
@ -294,10 +320,12 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
<div class="card-footer bg-light text-center py-2"> <div class="card-footer bg-light text-center py-2">
<?php if (canView('inbound')): ?>
<a href="inbound.php" class="small text-decoration-none">عرض كافة المراسلات <i class="fas fa-chevron-left ms-1"></i></a> <a href="inbound.php" class="small text-decoration-none">عرض كافة المراسلات <i class="fas fa-chevron-left ms-1"></i></a>
<?php endif; ?>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

290
users.php
View File

@ -1,13 +1,22 @@
<?php <?php
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
if (!isAdmin()) { if (!canView('users')) {
redirect('index.php'); redirect('index.php');
} }
$error = ''; $error = '';
$success = ''; $success = '';
$modules = [
'inbound' => 'البريد الوارد',
'outbound' => 'البريد الصادر',
'internal' => 'البريد الداخلي',
'users' => 'إدارة المستخدمين',
'settings' => 'الإعدادات',
'reports' => 'التقارير'
];
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? ''; $action = $_POST['action'] ?? '';
$username = $_POST['username'] ?? ''; $username = $_POST['username'] ?? '';
@ -16,21 +25,39 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$password = $_POST['password'] ?? ''; $password = $_POST['password'] ?? '';
$id = $_POST['id'] ?? 0; $id = $_POST['id'] ?? 0;
// Permissions // Global permissions (legacy/fallback)
$can_view = isset($_POST['can_view']) ? 1 : 0; $can_view = isset($_POST['can_view_global']) ? 1 : 0;
$can_add = isset($_POST['can_add']) ? 1 : 0; $can_add = isset($_POST['can_add_global']) ? 1 : 0;
$can_edit = isset($_POST['can_edit']) ? 1 : 0; $can_edit = isset($_POST['can_edit_global']) ? 1 : 0;
$can_delete = isset($_POST['can_delete']) ? 1 : 0; $can_delete = isset($_POST['can_delete_global']) ? 1 : 0;
if ($action === 'add') { if ($action === 'add') {
if (!canAdd('users')) redirect('users.php');
if ($username && $password && $full_name) { if ($username && $password && $full_name) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT); $hashed_password = password_hash($password, PASSWORD_DEFAULT);
try { try {
$stmt = db()->prepare("INSERT INTO users (username, password, full_name, role, can_view, can_add, can_edit, can_delete) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); $pdo = db();
$pdo->beginTransaction();
$stmt = $pdo->prepare("INSERT INTO users (username, password, full_name, role, can_view, can_add, can_edit, can_delete) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$username, $hashed_password, $full_name, $role, $can_view, $can_add, $can_edit, $can_delete]); $stmt->execute([$username, $hashed_password, $full_name, $role, $can_view, $can_add, $can_edit, $can_delete]);
$user_id = $pdo->lastInsertId();
// Save page permissions
$perm_stmt = $pdo->prepare("INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete) VALUES (?, ?, ?, ?, ?, ?)");
foreach ($modules as $module => $label) {
$m_view = isset($_POST["perm_{$module}_view"]) ? 1 : 0;
$m_add = isset($_POST["perm_{$module}_add"]) ? 1 : 0;
$m_edit = isset($_POST["perm_{$module}_edit"]) ? 1 : 0;
$m_delete = isset($_POST["perm_{$module}_delete"]) ? 1 : 0;
$perm_stmt->execute([$user_id, $module, $m_view, $m_add, $m_edit, $m_delete]);
}
$pdo->commit();
$_SESSION['success'] = 'تم إضافة المستخدم بنجاح'; $_SESSION['success'] = 'تم إضافة المستخدم بنجاح';
redirect('users.php'); redirect('users.php');
} catch (PDOException $e) { } catch (PDOException $e) {
if (isset($pdo)) $pdo->rollBack();
if ($e->getCode() == 23000) { if ($e->getCode() == 23000) {
$error = 'اسم المستخدم موجود مسبقاً'; $error = 'اسم المستخدم موجود مسبقاً';
} else { } else {
@ -41,19 +68,43 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$error = 'يرجى ملء جميع الحقول المطلوبة'; $error = 'يرجى ملء جميع الحقول المطلوبة';
} }
} elseif ($action === 'edit') { } elseif ($action === 'edit') {
if (!canEdit('users')) redirect('users.php');
if ($username && $full_name && $id) { if ($username && $full_name && $id) {
try { try {
$pdo = db();
$pdo->beginTransaction();
if ($password) { if ($password) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT); $hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = db()->prepare("UPDATE users SET username = ?, full_name = ?, role = ?, password = ?, can_view = ?, can_add = ?, can_edit = ?, can_delete = ? WHERE id = ?"); $stmt = $pdo->prepare("UPDATE users SET username = ?, full_name = ?, role = ?, password = ?, can_view = ?, can_add = ?, can_edit = ?, can_delete = ? WHERE id = ?");
$stmt->execute([$username, $full_name, $role, $hashed_password, $can_view, $can_add, $can_edit, $can_delete, $id]); $stmt->execute([$username, $full_name, $role, $hashed_password, $can_view, $can_add, $can_edit, $can_delete, $id]);
} else { } else {
$stmt = db()->prepare("UPDATE users SET username = ?, full_name = ?, role = ?, can_view = ?, can_add = ?, can_edit = ?, can_delete = ? WHERE id = ?"); $stmt = $pdo->prepare("UPDATE users SET username = ?, full_name = ?, role = ?, can_view = ?, can_add = ?, can_edit = ?, can_delete = ? WHERE id = ?");
$stmt->execute([$username, $full_name, $role, $can_view, $can_add, $can_edit, $can_delete, $id]); $stmt->execute([$username, $full_name, $role, $can_view, $can_add, $can_edit, $can_delete, $id]);
} }
// Update page permissions
$pdo->prepare("DELETE FROM user_permissions WHERE user_id = ?")->execute([$id]);
$perm_stmt = $pdo->prepare("INSERT INTO user_permissions (user_id, page, can_view, can_add, can_edit, can_delete) VALUES (?, ?, ?, ?, ?, ?)");
foreach ($modules as $module => $label) {
$m_view = isset($_POST["perm_{$module}_view"]) ? 1 : 0;
$m_add = isset($_POST["perm_{$module}_add"]) ? 1 : 0;
$m_edit = isset($_POST["perm_{$module}_edit"]) ? 1 : 0;
$m_delete = isset($_POST["perm_{$module}_delete"]) ? 1 : 0;
$perm_stmt->execute([$id, $module, $m_view, $m_add, $m_edit, $m_delete]);
}
$pdo->commit();
// Refresh own session if editing self
if ($id == $_SESSION['user_id']) {
unset($_SESSION['permissions']);
}
$_SESSION['success'] = 'تم تحديث بيانات المستخدم بنجاح'; $_SESSION['success'] = 'تم تحديث بيانات المستخدم بنجاح';
redirect('users.php'); redirect('users.php');
} catch (PDOException $e) { } catch (PDOException $e) {
if (isset($pdo)) $pdo->rollBack();
$error = 'حدث خطأ: ' . $e->getMessage(); $error = 'حدث خطأ: ' . $e->getMessage();
} }
} else { } else {
@ -63,6 +114,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
if (!canDelete('users')) redirect('users.php');
if ($_GET['id'] != $_SESSION['user_id']) { if ($_GET['id'] != $_SESSION['user_id']) {
$stmt = db()->prepare("DELETE FROM users WHERE id = ?"); $stmt = db()->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]); $stmt->execute([$_GET['id']]);
@ -86,20 +138,32 @@ if (isset($_SESSION['error'])) {
$stmt = db()->query("SELECT * FROM users ORDER BY created_at DESC"); $stmt = db()->query("SELECT * FROM users ORDER BY created_at DESC");
$users = $stmt->fetchAll(); $users = $stmt->fetchAll();
// Fetch permissions for all users
$user_perms = [];
$perm_stmt = db()->query("SELECT * FROM user_permissions");
while ($row = $perm_stmt->fetch()) {
$user_perms[$row['user_id']][$row['page']] = $row;
}
// Handle Deep Link for Edit // Handle Deep Link for Edit
$deepLinkData = null; $deepLinkData = null;
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?"); $stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_GET['id']]); $stmt->execute([$_GET['id']]);
$deepLinkData = $stmt->fetch(); $deepLinkData = $stmt->fetch();
if ($deepLinkData) {
$deepLinkData['page_permissions'] = $user_perms[$deepLinkData['id']] ?? [];
}
} }
?> ?>
<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>
<?php if (canAdd('users')): ?>
<button type="button" class="btn btn-primary shadow-sm" onclick="openUserModal('add')"> <button type="button" class="btn btn-primary shadow-sm" onclick="openUserModal('add')">
<i class="fas fa-user-plus me-1"></i> إضافة مستخدم جديد <i class="fas fa-user-plus me-1"></i> إضافة مستخدم جديد
</button> </button>
<?php endif; ?>
</div> </div>
<?php if ($success): ?> <?php if ($success): ?>
@ -125,7 +189,6 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id']))
<th class="ps-4">الاسم الكامل</th> <th class="ps-4">الاسم الكامل</th>
<th>اسم المستخدم</th> <th>اسم المستخدم</th>
<th>الدور</th> <th>الدور</th>
<th>الصلاحيات</th>
<th>تاريخ الإنشاء</th> <th>تاريخ الإنشاء</th>
<th class="pe-4 text-center">الإجراءات</th> <th class="pe-4 text-center">الإجراءات</th>
</tr> </tr>
@ -144,21 +207,15 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id']))
<span class="badge bg-secondary">موظف</span> <span class="badge bg-secondary">موظف</span>
<?php endif; ?> <?php endif; ?>
</td> </td>
<td>
<div class="d-flex gap-1">
<span class="badge <?= $user['can_view'] ? 'bg-success' : 'bg-light text-muted' ?>" title="عرض">ع</span>
<span class="badge <?= $user['can_add'] ? 'bg-success' : 'bg-light text-muted' ?>" title="إضافة">إ</span>
<span class="badge <?= $user['can_edit'] ? 'bg-success' : 'bg-light text-muted' ?>" title="تعديل">ت</span>
<span class="badge <?= $user['can_delete'] ? 'bg-success' : 'bg-light text-muted' ?>" title="حذف">ح</span>
</div>
</td>
<td><?= $user['created_at'] ?></td> <td><?= $user['created_at'] ?></td>
<td class="pe-4 text-center"> <td class="pe-4 text-center">
<?php if (canEdit('users')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" <button type="button" class="btn btn-sm btn-outline-primary"
onclick="openUserModal('edit', <?= htmlspecialchars(json_encode($user), ENT_QUOTES, 'UTF-8') ?>)"> onclick="openUserModal('edit', <?= htmlspecialchars(json_encode(array_merge($user, ['page_permissions' => $user_perms[$user['id']] ?? []])), ENT_QUOTES, 'UTF-8') ?>)">
<i class="fas fa-edit"></i> تعديل <i class="fas fa-edit"></i> تعديل الصلاحيات
</button> </button>
<?php if ($user['id'] != $_SESSION['user_id']): ?> <?php endif; ?>
<?php if (canDelete('users') && $user['id'] != $_SESSION['user_id']): ?>
<a href="javascript:void(0)" onclick="confirmDelete(<?= $user['id'] ?>)" class="btn btn-sm btn-outline-danger"> <a href="javascript:void(0)" onclick="confirmDelete(<?= $user['id'] ?>)" class="btn btn-sm btn-outline-danger">
<i class="fas fa-trash"></i> حذف <i class="fas fa-trash"></i> حذف
</a> </a>
@ -174,7 +231,7 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id']))
<!-- User Modal --> <!-- User Modal -->
<div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true"> <div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog modal-lg">
<div class="modal-content border-0 shadow"> <div class="modal-content border-0 shadow">
<div class="modal-header bg-primary text-white"> <div class="modal-header bg-primary text-white">
<h5 class="modal-title fw-bold" id="userModalLabel">إضافة مستخدم جديد</h5> <h5 class="modal-title fw-bold" id="userModalLabel">إضافة مستخدم جديد</h5>
@ -185,56 +242,74 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id']))
<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">
<div class="mb-3"> <div class="row">
<label class="form-label fw-bold">الاسم الكامل</label> <div class="col-md-6 mb-3">
<input type="text" name="full_name" id="modalFullName" class="form-control" required> <label class="form-label fw-bold">الاسم الكامل</label>
</div> <input type="text" name="full_name" id="modalFullName" class="form-control" required>
<div class="mb-3"> </div>
<label class="form-label fw-bold">اسم المستخدم</label> <div class="col-md-6 mb-3">
<input type="text" name="username" id="modalUsername" class="form-control" required> <label class="form-label fw-bold">اسم المستخدم</label>
</div> <input type="text" name="username" id="modalUsername" class="form-control" required>
<div class="mb-3">
<label class="form-label fw-bold">كلمة المرور <span id="pwdHint" class="text-muted small"></span></label>
<input type="password" name="password" id="modalPassword" class="form-control">
</div>
<div class="mb-3">
<label class="form-label fw-bold">الدور</label>
<select name="role" id="modalRole" class="form-select" onchange="applyRolePresets(this.value)">
<option value="staff">موظف</option>
<option value="clerk">كاتب</option>
<option value="admin">مدير</option>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-bold d-block">الصلاحيات</label>
<div class="row g-2 bg-light p-3 rounded">
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="can_view" id="permView" value="1">
<label class="form-check-label" for="permView">عرض البيانات</label>
</div>
</div>
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="can_add" id="permAdd" value="1">
<label class="form-check-label" for="permAdd">إضافة سجلات</label>
</div>
</div>
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="can_edit" id="permEdit" value="1">
<label class="form-check-label" for="permEdit">تعديل سجلات</label>
</div>
</div>
<div class="col-6">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="can_delete" id="permDelete" value="1">
<label class="form-check-label" for="permDelete">حذف سجلات</label>
</div>
</div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">كلمة المرور <span id="pwdHint" class="text-muted small"></span></label>
<input type="password" name="password" id="modalPassword" class="form-control">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">الدور</label>
<select name="role" id="modalRole" class="form-select" onchange="applyRolePresets(this.value)">
<option value="staff">موظف</option>
<option value="clerk">كاتب</option>
<option value="admin">مدير</option>
</select>
</div>
</div>
<hr>
<h6 class="fw-bold mb-3"><i class="fas fa-lock me-2 text-primary"></i> صلاحيات الوصول لكل صفحة</h6>
<div class="table-responsive">
<table class="table table-bordered table-sm align-middle">
<thead class="bg-light">
<tr class="text-center">
<th class="text-start ps-3">الصفحة / الموديول</th>
<th>عرض</th>
<th>إضافة</th>
<th>تعديل</th>
<th>حذف</th>
</tr>
</thead>
<tbody>
<?php foreach ($modules as $key => $label): ?>
<tr class="text-center">
<td class="text-start ps-3 fw-bold"><?= $label ?></td>
<td>
<input class="form-check-input" type="checkbox" name="perm_<?= $key ?>_view" id="perm_<?= $key ?>_view" value="1">
</td>
<td>
<input class="form-check-input" type="checkbox" name="perm_<?= $key ?>_add" id="perm_<?= $key ?>_add" value="1">
</td>
<td>
<input class="form-check-input" type="checkbox" name="perm_<?= $key ?>_edit" id="perm_<?= $key ?>_edit" value="1">
</td>
<td>
<input class="form-check-input" type="checkbox" name="perm_<?= $key ?>_delete" id="perm_<?= $key ?>_delete" value="1">
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Hidden legacy fields for backward compatibility if needed -->
<input type="hidden" name="can_view_global" id="permViewGlobal" value="1">
<input type="hidden" name="can_add_global" id="permAddGlobal" value="0">
<input type="hidden" name="can_edit_global" id="permEditGlobal" value="0">
<input type="hidden" name="can_delete_global" id="permDeleteGlobal" value="0">
</div> </div>
<div class="modal-footer bg-light"> <div class="modal-footer bg-light">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
@ -247,22 +322,34 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id']))
<script> <script>
let userModal; let userModal;
const modules = <?= json_encode(array_keys($modules)) ?>;
function applyRolePresets(role) { function applyRolePresets(role) {
const view = document.getElementById('permView'); modules.forEach(m => {
const add = document.getElementById('permAdd'); const view = document.getElementById(`perm_${m}_view`);
const edit = document.getElementById('permEdit'); const add = document.getElementById(`perm_${m}_add`);
const del = document.getElementById('permDelete'); const edit = document.getElementById(`perm_${m}_edit`);
const del = document.getElementById(`perm_${m}_delete`);
if (role === 'admin') {
view.checked = add.checked = edit.checked = del.checked = true; if (role === 'admin') {
} else if (role === 'clerk') { view.checked = add.checked = edit.checked = del.checked = true;
view.checked = add.checked = edit.checked = true; } else if (role === 'clerk') {
del.checked = false; if (m === 'users' || m === 'settings') {
} else { view.checked = add.checked = edit.checked = del.checked = false;
view.checked = true; } else {
add.checked = edit.checked = del.checked = false; view.checked = add.checked = edit.checked = true;
} del.checked = (m === 'reports' ? false : false);
}
} else {
if (m === 'inbound' || m === 'outbound' || m === 'internal') {
view.checked = true;
add.checked = (m === 'internal'); // Staff can send internal mail
edit.checked = del.checked = false;
} else {
view.checked = add.checked = edit.checked = del.checked = false;
}
}
});
} }
function openUserModal(action, data = null) { function openUserModal(action, data = null) {
@ -287,13 +374,6 @@ function openUserModal(action, data = null) {
username: document.getElementById('modalUsername'), username: document.getElementById('modalUsername'),
role: document.getElementById('modalRole') role: document.getElementById('modalRole')
}; };
const perms = {
can_view: document.getElementById('permView'),
can_add: document.getElementById('permAdd'),
can_edit: document.getElementById('permEdit'),
can_delete: document.getElementById('permDelete')
};
modalAction.value = action; modalAction.value = action;
@ -313,10 +393,13 @@ function openUserModal(action, data = null) {
}); });
// Set permissions checkboxes // Set permissions checkboxes
perms.can_view.checked = data.can_view == 1; modules.forEach(m => {
perms.can_add.checked = data.can_add == 1; const p = data.page_permissions && data.page_permissions[m] ? data.page_permissions[m] : {};
perms.can_edit.checked = data.can_edit == 1; document.getElementById(`perm_${m}_view`).checked = p.can_view == 1;
perms.can_delete.checked = data.can_delete == 1; document.getElementById(`perm_${m}_add`).checked = p.can_add == 1;
document.getElementById(`perm_${m}_edit`).checked = p.can_edit == 1;
document.getElementById(`perm_${m}_delete`).checked = p.can_delete == 1;
});
modalPassword.required = false; modalPassword.required = false;
pwdHint.textContent = '(اتركه فارغاً للحفاظ على كلمة المرور الحالية)'; pwdHint.textContent = '(اتركه فارغاً للحفاظ على كلمة المرور الحالية)';
@ -328,18 +411,6 @@ function openUserModal(action, data = null) {
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
<?php if ($deepLinkData): ?> <?php if ($deepLinkData): ?>
openUserModal('edit', <?= json_encode($deepLinkData) ?>); openUserModal('edit', <?= json_encode($deepLinkData) ?>);
<?php elseif ($error && isset($_POST['action'])): ?>
const errorData = <?= json_encode([
'id' => $_POST['id'] ?? 0,
'username' => $_POST['username'] ?? '',
'full_name' => $_POST['full_name'] ?? '',
'role' => $_POST['role'] ?? 'staff',
'can_view' => $_POST['can_view'] ?? 0,
'can_add' => $_POST['can_add'] ?? 0,
'can_edit' => $_POST['can_edit'] ?? 0,
'can_delete' => $_POST['can_delete'] ?? 0
]) ?>;
openUserModal('<?= $_POST['action'] ?>', errorData);
<?php elseif (isset($_GET['action']) && $_GET['action'] === 'add'): ?> <?php elseif (isset($_GET['action']) && $_GET['action'] === 'add'): ?>
openUserModal('add'); openUserModal('add');
<?php endif; ?> <?php endif; ?>
@ -381,6 +452,9 @@ function confirmDelete(id) {
background-color: #198754; background-color: #198754;
border-color: #198754; border-color: #198754;
} }
.table-sm th, .table-sm td {
padding: 0.5rem;
}
</style> </style>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -2,11 +2,6 @@
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/mail/MailService.php'; require_once __DIR__ . '/mail/MailService.php';
// Check if user has view permission
if (!canView()) {
redirect('index.php');
}
$id = $_GET['id'] ?? 0; $id = $_GET['id'] ?? 0;
if (!$id) redirect('index.php'); if (!$id) redirect('index.php');
@ -22,6 +17,11 @@ $mail = $stmt->fetch();
if (!$mail) redirect('index.php'); if (!$mail) redirect('index.php');
// Check if user has view permission for this mail type
if (!canView($mail['type'])) {
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 // Even admins should only see their own internal mail for privacy
if ($mail['type'] === 'internal') { if ($mail['type'] === 'internal') {
@ -35,7 +35,8 @@ $error = '';
// Handle Comment submission // Handle Comment submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) {
if (!canEdit() && $mail['type'] !== 'internal') { // For internal mail, users can always comment if involved. For others, check edit permission.
if ($mail['type'] !== 'internal' && !canEdit($mail['type'])) {
$error = 'عذراً، ليس لديك الصلاحية لإضافة تعليقات'; $error = 'عذراً، ليس لديك الصلاحية لإضافة تعليقات';
} else { } else {
$comment = $_POST['comment'] ?? ''; $comment = $_POST['comment'] ?? '';
@ -52,11 +53,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) {
$referred_user = $stmt_u->fetch(); $referred_user = $stmt_u->fetch();
if ($referred_user && !empty($referred_user['email'])) { if ($referred_user && !empty($referred_user['email'])) {
$sender_name = $_SESSION['full_name'] ?? 'زميلك'; $sender_name = $_SESSION['name'] ?? 'زميلك';
$mail_subject = "إحالة بريد: " . $mail['subject']; $mail_subject = "إحالة بريد: " . $mail['subject'];
$mail_link = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]" . dirname($_SERVER['PHP_SELF']) . "/view_mail.php?id=" . $id; $mail_link = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]" . dirname($_SERVER['PHP_SELF']) . "/view_mail.php?id=" . $id;
$html = " $html = "
<div dir='rtl'>
<h3>مرحباً " . htmlspecialchars($referred_user['full_name']) . "</h3> <h3>مرحباً " . htmlspecialchars($referred_user['full_name']) . "</h3>
<p>قام <strong>" . htmlspecialchars($sender_name) . "</strong> بإحالة بريد إليك مع التعليق التالي:</p> <p>قام <strong>" . htmlspecialchars($sender_name) . "</strong> بإحالة بريد إليك مع التعليق التالي:</p>
<blockquote style='background: #f9f9f9; padding: 10px; border-left: 5px solid #ccc;'> <blockquote style='background: #f9f9f9; padding: 10px; border-left: 5px solid #ccc;'>
@ -68,6 +70,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) {
<li><strong>الموضوع:</strong> " . htmlspecialchars($mail['subject']) . "</li> <li><strong>الموضوع:</strong> " . htmlspecialchars($mail['subject']) . "</li>
</ul> </ul>
<p><a href='{$mail_link}' style='display: inline-block; padding: 10px 20px; background-color: #007bff; color: white; text-decoration: none; border-radius: 5px;'>عرض البريد</a></p> <p><a href='{$mail_link}' style='display: inline-block; padding: 10px 20px; background-color: #007bff; color: white; text-decoration: none; border-radius: 5px;'>عرض البريد</a></p>
</div>
"; ";
$txt = "قام {$sender_name} بإحالة بريد إليك: {$mail['subject']}\n\nالتعليق: {$comment}\n\nعرض البريد: {$mail_link}"; $txt = "قام {$sender_name} بإحالة بريد إليك: {$mail['subject']}\n\nالتعليق: {$comment}\n\nعرض البريد: {$mail_link}";
@ -84,7 +87,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) {
// Handle Attachment upload // Handle Attachment upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) {
if (!canEdit() && $mail['type'] !== 'internal') { if ($mail['type'] !== 'internal' && !canEdit($mail['type'])) {
$error = 'عذراً، ليس لديك الصلاحية لرفع مرفقات'; $error = 'عذراً، ليس لديك الصلاحية لرفع مرفقات';
} else { } else {
$file = $_FILES['attachment']; $file = $_FILES['attachment'];
@ -110,7 +113,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) {
// Handle Attachment deletion // Handle Attachment deletion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_attachment'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_attachment'])) {
if (!canDelete() && $mail['type'] !== 'internal') { if ($mail['type'] !== 'internal' && !canDelete($mail['type'])) {
$error = 'عذراً، ليس لديك الصلاحية لحذف المرفقات'; $error = 'عذراً، ليس لديك الصلاحية لحذف المرفقات';
} else { } else {
$attachment_id = $_POST['attachment_id'] ?? 0; $attachment_id = $_POST['attachment_id'] ?? 0;
@ -182,7 +185,7 @@ if ($mail['type'] == 'internal') {
<h1 class="h2">تفاصيل <?= $type_label ?></h1> <h1 class="h2">تفاصيل <?= $type_label ?></h1>
<div class="btn-group"> <div class="btn-group">
<a href="<?= $back_link ?>" class="btn btn-outline-secondary">عودة للقائمة</a> <a href="<?= $back_link ?>" class="btn btn-outline-secondary">عودة للقائمة</a>
<?php if (canEdit() && $mail['type'] !== 'internal'): ?> <?php if ($mail['type'] !== 'internal' && canEdit($mail['type'])): ?>
<a href="<?= $mail['type'] ?>.php?action=edit&id=<?= $mail['id'] ?>" class="btn btn-outline-primary">تعديل البيانات</a> <a href="<?= $mail['type'] ?>.php?action=edit&id=<?= $mail['id'] ?>" class="btn btn-outline-primary">تعديل البيانات</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
@ -315,6 +318,7 @@ if ($mail['type'] == 'internal') {
<h5 class="mb-0 fw-bold">الردود والمتابعة</h5> <h5 class="mb-0 fw-bold">الردود والمتابعة</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<?php if ($mail['type'] === 'internal' || canEdit($mail['type'])): ?>
<form method="POST" class="mb-4 bg-light p-3 rounded border"> <form method="POST" class="mb-4 bg-light p-3 rounded border">
<div class="mb-2"> <div class="mb-2">
<label class="form-label small fw-bold">إضافة <?= $mail['type'] == 'internal' ? 'رد' : 'تعليق' ?></label> <label class="form-label small fw-bold">إضافة <?= $mail['type'] == 'internal' ? 'رد' : 'تعليق' ?></label>
@ -334,6 +338,7 @@ if ($mail['type'] == 'internal') {
<?php endif; ?> <?php endif; ?>
<button type="submit" name="add_comment" class="btn btn-sm btn-primary">إرسال <?= $mail['type'] == 'internal' ? 'الرد' : 'التعليق' ?></button> <button type="submit" name="add_comment" class="btn btn-sm btn-primary">إرسال <?= $mail['type'] == 'internal' ? 'الرد' : 'التعليق' ?></button>
</form> </form>
<?php endif; ?>
<div class="comment-list"> <div class="comment-list">
<?php if ($mail_comments): foreach ($mail_comments as $c): ?> <?php if ($mail_comments): foreach ($mail_comments as $c): ?>
@ -366,6 +371,7 @@ if ($mail['type'] == 'internal') {
<h5 class="mb-0 fw-bold">المرفقات</h5> <h5 class="mb-0 fw-bold">المرفقات</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<?php if ($mail['type'] === 'internal' || canEdit($mail['type'])): ?>
<form method="POST" enctype="multipart/form-data" class="mb-4"> <form method="POST" enctype="multipart/form-data" class="mb-4">
<div class="mb-2"> <div class="mb-2">
<label class="form-label small mb-1">اسم المرفق (يظهر في القائمة)</label> <label class="form-label small mb-1">اسم المرفق (يظهر في القائمة)</label>
@ -375,6 +381,7 @@ if ($mail['type'] == 'internal') {
</div> </div>
<button type="submit" class="btn btn-sm btn-secondary w-100">رفع ملف</button> <button type="submit" class="btn btn-sm btn-secondary w-100">رفع ملف</button>
</form> </form>
<?php endif; ?>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<?php if ($mail_attachments): foreach ($mail_attachments as $a): ?> <?php if ($mail_attachments): foreach ($mail_attachments as $a): ?>
@ -397,7 +404,7 @@ if ($mail['type'] == 'internal') {
</button> </button>
<?php endif; ?> <?php endif; ?>
<?php if (canDelete() || $mail['type'] == 'internal'): ?> <?php if ($mail['type'] == 'internal' || canDelete($mail['type'])): ?>
<form method="POST" class="d-inline delete-attachment-form"> <form method="POST" class="d-inline delete-attachment-form">
<input type="hidden" name="attachment_id" value="<?= $a['id'] ?>"> <input type="hidden" name="attachment_id" value="<?= $a['id'] ?>">
<input type="hidden" name="delete_attachment" value="1"> <input type="hidden" name="delete_attachment" value="1">
@ -479,22 +486,28 @@ if ($mail['type'] == 'internal') {
document.querySelectorAll('.delete-btn').forEach(btn => { document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', function() { btn.addEventListener('click', function() {
const form = this.closest('form'); const form = this.closest('form');
Swal.fire({ if (typeof Swal !== 'undefined') {
title: 'هل أنت متأكد؟', Swal.fire({
text: "سيتم حذف المرفق نهائياً!", title: 'هل أنت متأكد؟',
icon: 'warning', text: "سيتم حذف المرفق نهائياً!",
showCancelButton: true, icon: 'warning',
confirmButtonColor: '#d33', showCancelButton: true,
cancelButtonColor: '#3085d6', confirmButtonColor: '#d33',
confirmButtonText: 'نعم، احذف', cancelButtonColor: '#3085d6',
cancelButtonText: 'إلغاء' confirmButtonText: 'نعم، احذف',
}).then((result) => { cancelButtonText: 'إلغاء'
if (result.isConfirmed) { }).then((result) => {
if (result.isConfirmed) {
form.submit();
}
});
} else {
if (confirm('هل أنت متأكد من الحذف؟')) {
form.submit(); form.submit();
} }
}); }
}); });
}); });
</script> </script>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>