adding permission

This commit is contained in:
Flatlogic Bot 2026-02-27 18:38:01 +00:00
parent e70adf8720
commit 118aae16b0
13 changed files with 811 additions and 198 deletions

26
api/update_theme.php Normal file
View File

@ -0,0 +1,26 @@
<?php
session_start();
require_once __DIR__ . '/../db/config.php';
if (!isset($_SESSION['user_id'])) {
echo json_encode(['success' => false, 'error' => 'Not authenticated']);
exit;
}
$data = json_decode(file_get_contents('php://input'), true);
$theme = $data['theme'] ?? 'light';
// Validate theme
$allowed_themes = ['light', 'dark', 'midnight', 'forest'];
if (!in_array($theme, $allowed_themes)) {
echo json_encode(['success' => false, 'error' => 'Invalid theme']);
exit;
}
try {
$stmt = db()->prepare("UPDATE users SET theme = ? WHERE id = ?");
$stmt->execute([$theme, $_SESSION['user_id']]);
echo json_encode(['success' => true]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}

View File

@ -0,0 +1,3 @@
-- Add password reset token columns to users table
ALTER TABLE users ADD COLUMN reset_token VARCHAR(100) NULL;
ALTER TABLE users ADD COLUMN reset_token_expiry DATETIME NULL;

View File

@ -0,0 +1,2 @@
-- Migration: Add theme to users
ALTER TABLE users ADD COLUMN theme VARCHAR(20) DEFAULT 'light';

View File

@ -0,0 +1,11 @@
-- Migration: Add granular permissions to users table
ALTER TABLE users
ADD COLUMN can_view TINYINT(1) DEFAULT 1,
ADD COLUMN can_add TINYINT(1) DEFAULT 0,
ADD COLUMN can_edit TINYINT(1) DEFAULT 0,
ADD COLUMN can_delete TINYINT(1) DEFAULT 0;
-- Set defaults for existing roles
UPDATE users SET can_view = 1, can_add = 1, can_edit = 1, can_delete = 1 WHERE role = 'admin';
UPDATE users SET can_view = 1, can_add = 1, can_edit = 1, can_delete = 0 WHERE role = 'clerk';
UPDATE users SET can_view = 1, can_add = 0, can_edit = 0, can_delete = 0 WHERE role = 'staff';

150
forgot_password.php Normal file
View File

@ -0,0 +1,150 @@
<?php
require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/mail/MailService.php';
if (isLoggedIn()) {
redirect('index.php');
}
$error = '';
$success = '';
$step = 'request'; // 'request' or 'reset'
// Fetch charity settings for logo/name
$stmt = db()->query("SELECT * FROM charity_settings WHERE id = 1");
$charity = $stmt->fetch();
// Check if we are in reset mode (token in URL)
$token = $_GET['token'] ?? '';
if ($token) {
$stmt = db()->prepare("SELECT * FROM users WHERE reset_token = ? AND reset_token_expiry > NOW()");
$stmt->execute([$token]);
$user = $stmt->fetch();
if ($user) {
$step = 'reset';
} else {
$error = 'رابط استعادة كلمة المرور غير صالح أو منتهي الصلاحية.';
$step = 'request';
}
}
// Handle POST requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['request_reset'])) {
$email = trim($_POST['email'] ?? '');
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
$stmt = db()->prepare("SELECT id, full_name FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user) {
$newToken = bin2hex(random_bytes(32));
$expiry = date('Y-m-d H:i:s', strtotime('+1 hour'));
$update = db()->prepare("UPDATE users SET reset_token = ?, reset_token_expiry = ? WHERE id = ?");
$update->execute([$newToken, $expiry, $user['id']]);
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
$resetLink = "$protocol://$host/forgot_password.php?token=$newToken";
$subject = "استعادة كلمة المرور - " . ($charity['charity_name'] ?? 'الجمعية الخيرية');
$html = "
<div dir='rtl' style='font-family: Arial, sans-serif; text-align: right;'>
<h3>مرحباً {$user['full_name']}</h3>
<p>لقد طلبت استعادة كلمة المرور الخاصة بك. يرجى الضغط على الرابط أدناه لإعادة تعيينها:</p>
<p><a href='$resetLink' style='background: #000; color: #fff; padding: 10px 20px; text-decoration: none; border-radius: 5px;'>إعادة تعيين كلمة المرور</a></p>
<p>هذا الرابط صالح لمدة ساعة واحدة فقط.</p>
<p>إذا لم تطلب هذا، يرجى تجاهل هذه الرسالة.</p>
</div>
";
$res = MailService::sendMail($email, $subject, $html);
if ($res['success']) {
$success = 'تم إرسال رابط استعادة كلمة المرور إلى بريدك الإلكتروني.';
} else {
$error = 'فشل إرسال البريد الإلكتروني. يرجى المحاولة لاحقاً أو التواصل مع الإدارة.';
}
} else {
// For security, don't reveal if email exists, but here we can be more helpful if it's a closed admin panel
$error = 'البريد الإلكتروني غير مسجل لدينا.';
}
} else {
$error = 'يرجى إدخال بريد إلكتروني صحيح.';
}
} elseif (isset($_POST['reset_password'])) {
$password = $_POST['password'] ?? '';
$confirm = $_POST['confirm_password'] ?? '';
if (strlen($password) < 6) {
$error = 'كلمة المرور يجب أن تكون 6 أحرف على الأقل.';
} elseif ($password !== $confirm) {
$error = 'كلمات المرور غير متطابقة.';
} else {
$hashed = password_hash($password, PASSWORD_DEFAULT);
$update = db()->prepare("UPDATE users SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE id = ?");
$update->execute([$hashed, $user['id']]);
$success = 'تم تغيير كلمة المرور بنجاح. يمكنك الآن <a href="login.php">تسجيل الدخول</a>.';
$step = 'completed';
}
}
}
?>
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
<div class="col-md-4">
<div class="card p-4 shadow-sm border-0 text-center">
<div class="mb-4">
<?php if ($charity['charity_logo']): ?>
<img src="<?= htmlspecialchars($charity['charity_logo']) ?>" alt="Logo" class="mb-3" style="max-height: 80px;">
<?php endif; ?>
<h4 class="fw-bold">استعادة كلمة المرور</h4>
<p class="text-muted small">بريد <?= htmlspecialchars($charity['charity_name'] ?? 'الجمعية الخيرية') ?></p>
</div>
<?php if ($error): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success"><?= $success ?></div>
<?php endif; ?>
<?php if ($step === 'request'): ?>
<form method="POST">
<div class="mb-3 text-start">
<label class="form-label">البريد الإلكتروني</label>
<input type="email" name="email" class="form-control" placeholder="example@domain.com" required>
<div class="form-text">أدخل البريد الإلكتروني المرتبط بحسابك.</div>
</div>
<div class="d-grid mt-4">
<button type="submit" name="request_reset" class="btn btn-dark">إرسال رابط الاستعادة</button>
</div>
<div class="mt-3">
<a href="login.php" class="text-decoration-none small text-secondary">العودة لتسجيل الدخول</a>
</div>
</form>
<?php elseif ($step === 'reset'): ?>
<form method="POST">
<div class="mb-3 text-start">
<label class="form-label">كلمة المرور الجديدة</label>
<input type="password" name="password" class="form-control" required minlength="6">
</div>
<div class="mb-3 text-start">
<label class="form-label">تأكيد كلمة المرور</label>
<input type="password" name="confirm_password" class="form-control" required minlength="6">
</div>
<div class="d-grid mt-4">
<button type="submit" name="reset_password" class="btn btn-primary">تغيير كلمة المرور</button>
</div>
</form>
<?php elseif ($step === 'completed'): ?>
<div class="mt-3">
<a href="login.php" class="btn btn-outline-dark">الذهاب لصفحة الدخول</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -2,6 +2,11 @@
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');
}
$error = ''; $error = '';
$success = ''; $success = '';
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
@ -47,62 +52,72 @@ function sendAssignmentNotification($assigned_to_id, $ref_no, $subject) {
// Handle actions // Handle actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? ''; $action = $_POST['action'] ?? '';
$type = 'inbound';
$ref_no = $_POST['ref_no'] ?? ''; // Permission checks for POST actions
$date_registered = $_POST['date_registered'] ?? date('Y-m-d'); if (($action === 'add' && !canAdd()) || ($action === 'edit' && !canEdit())) {
$due_date = !empty($_POST['due_date']) ? $_POST['due_date'] : null; $error = 'عذراً، ليس لديك الصلاحية للقيام بهذا الإجراء';
$sender = $_POST['sender'] ?? '';
$recipient = $_POST['recipient'] ?? '';
$subject = $_POST['subject'] ?? '';
$description = $_POST['description'] ?? '';
$status_id = $_POST['status_id'] ?? $default_status_id;
$assigned_to = !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null;
$id = $_POST['id'] ?? 0;
if ($ref_no && $subject) {
try {
if ($action === 'add') {
$stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$type, $ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $user_id]);
if ($assigned_to) {
sendAssignmentNotification($assigned_to, $ref_no, $subject);
}
$success = 'تمت إضافة البريد بنجاح';
} elseif ($action === 'edit') {
// Get previous assigned_to to check if it changed
$stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?");
$stmt_old->execute([$id]);
$old_assigned_to = $stmt_old->fetchColumn();
$stmt = db()->prepare("UPDATE mailbox SET ref_no = ?, date_registered = ?, due_date = ?, sender = ?, recipient = ?, subject = ?, description = ?, status_id = ?, assigned_to = ? WHERE id = ? AND type = 'inbound'");
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $id]);
if ($assigned_to && $assigned_to != $old_assigned_to) {
sendAssignmentNotification($assigned_to, $ref_no, $subject);
}
$success = 'تم تحديث البيانات بنجاح';
}
} catch (PDOException $e) {
if ($e->getCode() == 23000) {
$error = 'رقم القيد مستخدم مسبقاً';
} else {
$error = 'حدث خطأ: ' . $e->getMessage();
}
}
} else { } else {
$error = 'يرجى ملء الحقول المطلوبة (رقم القيد، الموضوع)'; $type = 'inbound';
$ref_no = $_POST['ref_no'] ?? '';
$date_registered = $_POST['date_registered'] ?? date('Y-m-d');
$due_date = !empty($_POST['due_date']) ? $_POST['due_date'] : null;
$sender = $_POST['sender'] ?? '';
$recipient = $_POST['recipient'] ?? '';
$subject = $_POST['subject'] ?? '';
$description = $_POST['description'] ?? '';
$status_id = $_POST['status_id'] ?? $default_status_id;
$assigned_to = !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null;
$id = $_POST['id'] ?? 0;
if ($ref_no && $subject) {
try {
if ($action === 'add') {
$stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$type, $ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $user_id]);
if ($assigned_to) {
sendAssignmentNotification($assigned_to, $ref_no, $subject);
}
$success = 'تمت إضافة البريد بنجاح';
} elseif ($action === 'edit') {
// Get previous assigned_to to check if it changed
$stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?");
$stmt_old->execute([$id]);
$old_assigned_to = $stmt_old->fetchColumn();
$stmt = db()->prepare("UPDATE mailbox SET ref_no = ?, date_registered = ?, due_date = ?, sender = ?, recipient = ?, subject = ?, description = ?, status_id = ?, assigned_to = ? WHERE id = ? AND type = 'inbound'");
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $id]);
if ($assigned_to && $assigned_to != $old_assigned_to) {
sendAssignmentNotification($assigned_to, $ref_no, $subject);
}
$success = 'تم تحديث البيانات بنجاح';
}
} catch (PDOException $e) {
if ($e->getCode() == 23000) {
$error = 'رقم القيد مستخدم مسبقاً';
} else {
$error = 'حدث خطأ: ' . $e->getMessage();
}
}
} else {
$error = 'يرجى ملء الحقول المطلوبة (رقم القيد، الموضوع)';
}
} }
} }
// Delete action // Delete action
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
$id = $_GET['id']; if (!canDelete()) {
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'inbound'"); $error = 'عذراً، ليس لديك الصلاحية لحذف السجلات';
$stmt->execute([$id]); } else {
$success = 'تم حذف البريد بنجاح'; $id = $_GET['id'];
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'inbound'");
$stmt->execute([$id]);
$success = 'تم حذف البريد بنجاح';
}
} }
$search = $_GET['search'] ?? ''; $search = $_GET['search'] ?? '';
@ -137,9 +152,11 @@ $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'])) {
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'inbound'"); if (canEdit()) {
$stmt->execute([$_GET['id']]); $stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'inbound'");
$deepLinkData = $stmt->fetch(); $stmt->execute([$_GET['id']]);
$deepLinkData = $stmt->fetch();
}
} }
function getStatusBadgeInList($mail) { function getStatusBadgeInList($mail) {
@ -158,9 +175,11 @@ 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()): ?>
<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>
<?php endif; ?>
</div> </div>
<?php if ($success): ?> <?php if ($success): ?>
@ -243,11 +262,17 @@ function getStatusBadgeInList($mail) {
<td><?= getStatusBadgeInList($mail) ?></td> <td><?= getStatusBadgeInList($mail) ?></td>
<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()): ?>
<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 if (canDelete()): ?>
<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; ?>
</td> </td>
</tr> </tr>
<?php endforeach; else: ?> <?php endforeach; else: ?>
@ -261,6 +286,7 @@ function getStatusBadgeInList($mail) {
</div> </div>
</div> </div>
<?php if (canAdd() || canEdit()): ?>
<!-- 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">
@ -433,6 +459,7 @@ function confirmDelete(id) {
}) })
} }
</script> </script>
<?php endif; ?>
<style> <style>
.modal-content { .modal-content {
@ -444,4 +471,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,46 @@
</main> </main>
</div> </div>
</div> </div>
<footer class="footer mt-auto py-3 bg-white border-top">
<div class="container-fluid px-md-4">
<div class="row align-items-center">
<div class="col-md-6 text-center text-md-start mb-2 mb-md-0">
<span class="text-muted small">
&copy; <?= date('Y') ?> <?= htmlspecialchars($charity_name) ?>. جميع الحقوق محفوظة.
</span>
</div>
<div class="col-md-6 text-center text-md-end">
<ul class="list-inline mb-0">
<li class="list-inline-item">
<a href="index.php" class="text-muted text-decoration-none small hover-primary">
<i class="fas fa-home me-1"></i> لوحة التحكم
</a>
</li>
<li class="list-inline-item ms-3">
<a href="profile.php" class="text-muted text-decoration-none small hover-primary">
<i class="fas fa-user-circle me-1"></i> الملف الشخصي
</a>
</li>
<?php if (isAdmin()): ?>
<li class="list-inline-item ms-3">
<a href="charity-settings.php" class="text-muted text-decoration-none small hover-primary">
<i class="fas fa-cog me-1"></i> الإعدادات
</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</div>
</footer>
<style>
.hover-primary:hover {
color: #0d6efd !important;
}
</style>
<script> <script>
// Global JS functions if needed // Global JS functions if needed
</script> </script>

View File

@ -10,12 +10,30 @@ function isAdmin() {
return isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin'; return isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin';
} }
function canView() {
return isAdmin() || (isset($_SESSION['can_view']) && $_SESSION['can_view'] == 1);
}
function canAdd() {
return isAdmin() || (isset($_SESSION['can_add']) && $_SESSION['can_add'] == 1);
}
function canEdit() {
return isAdmin() || (isset($_SESSION['can_edit']) && $_SESSION['can_edit'] == 1);
}
function canDelete() {
return isAdmin() || (isset($_SESSION['can_delete']) && $_SESSION['can_delete'] == 1);
}
function redirect($path) { function redirect($path) {
header("Location: $path"); header("Location: $path");
exit; exit;
} }
if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php') { // Allowed pages when not logged in
$allowed_pages = ['login.php', 'forgot_password.php'];
if (!isLoggedIn() && !in_array(basename($_SERVER['PHP_SELF']), $allowed_pages)) {
redirect('login.php'); redirect('login.php');
} }
@ -29,13 +47,20 @@ $charity_favicon = $charity['charity_favicon'] ?? null;
// Fetch current user info if logged in // Fetch current user info if logged in
$current_user = null; $current_user = null;
if (isLoggedIn()) { if (isLoggedIn()) {
$stmt = db()->prepare("SELECT full_name, profile_image FROM users WHERE id = ?"); $stmt = db()->prepare("SELECT full_name, profile_image, theme, can_view, can_add, can_edit, can_delete FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]); $stmt->execute([$_SESSION['user_id']]);
$current_user = $stmt->fetch(); $current_user = $stmt->fetch();
// Update session permissions
$_SESSION['can_view'] = $current_user['can_view'];
$_SESSION['can_add'] = $current_user['can_add'];
$_SESSION['can_edit'] = $current_user['can_edit'];
$_SESSION['can_delete'] = $current_user['can_delete'];
} }
$user_theme = $current_user['theme'] ?? 'light';
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="ar" dir="rtl"> <html lang="ar" dir="rtl" data-theme="<?= htmlspecialchars($user_theme) ?>">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -56,42 +81,136 @@ if (isLoggedIn()) {
<script src="https://cdn.ckeditor.com/ckeditor5/36.0.1/classic/ckeditor.js?v=<?php echo time(); ?>"></script> <script src="https://cdn.ckeditor.com/ckeditor5/36.0.1/classic/ckeditor.js?v=<?php echo time(); ?>"></script>
<style> <style>
:root {
/* Light Theme (Default) */
--bg-color: #f8f9fa;
--text-color: #212529;
--sidebar-bg: #ffffff;
--card-bg: #ffffff;
--nav-link-color: #333333;
--nav-link-hover-bg: #f0f7ff;
--primary-color: #0d6efd;
--border-color: rgba(0, 0, 0, 0.075);
--muted-text: #6c757d;
--input-bg: #ffffff;
--input-border: #dee2e6;
}
[data-theme="dark"] {
--bg-color: #121212;
--text-color: #e0e0e0;
--sidebar-bg: #1e1e1e;
--card-bg: #1e1e1e;
--nav-link-color: #bbbbbb;
--nav-link-hover-bg: #2c2c2c;
--primary-color: #3788ff;
--border-color: rgba(255, 255, 255, 0.1);
--muted-text: #999999;
--input-bg: #2d2d2d;
--input-border: #444444;
}
[data-theme="midnight"] {
--bg-color: #0b0e14;
--text-color: #cbd5e0;
--sidebar-bg: #1a202c;
--card-bg: #1a202c;
--nav-link-color: #a0aec0;
--nav-link-hover-bg: #2d3748;
--primary-color: #63b3ed;
--border-color: rgba(255, 255, 255, 0.05);
--muted-text: #718096;
--input-bg: #2d3748;
--input-border: #4a5568;
}
[data-theme="forest"] {
--bg-color: #f0f4f0;
--text-color: #2d372d;
--sidebar-bg: #ffffff;
--card-bg: #ffffff;
--nav-link-color: #4a5d4a;
--nav-link-hover-bg: #e8f0e8;
--primary-color: #2d6a4f;
--border-color: rgba(0, 0, 0, 0.05);
--muted-text: #6b8e6b;
--input-bg: #ffffff;
--input-border: #ccd5cc;
}
body { body {
font-family: 'Cairo', sans-serif; font-family: 'Cairo', sans-serif;
background-color: #f8f9fa; background-color: var(--bg-color);
color: var(--text-color);
display: flex;
flex-direction: column;
min-height: 100vh;
transition: background-color 0.3s ease, color 0.3s ease;
}
/* Bootstrap Overrides */
.bg-white { background-color: var(--card-bg) !important; }
.bg-light { background-color: var(--bg-color) !important; }
.text-dark { color: var(--text-color) !important; }
.text-muted { color: var(--muted-text) !important; }
.border-bottom { border-bottom: 1px solid var(--border-color) !important; }
.border-top { border-top: 1px solid var(--border-color) !important; }
.border { border: 1px solid var(--border-color) !important; }
.list-group-item { background-color: var(--card-bg); border-color: var(--border-color); color: var(--text-color); }
.form-control, .form-select { background-color: var(--input-bg); border-color: var(--input-border); color: var(--text-color); }
.form-control:focus, .form-select:focus { background-color: var(--input-bg); color: var(--text-color); border-color: var(--primary-color); }
.table { color: var(--text-color); border-color: var(--border-color); }
.table thead th { background-color: var(--bg-color); color: var(--text-color); }
.table-hover tbody tr:hover { background-color: var(--nav-link-hover-bg); }
.container-fluid.main-container {
flex: 1;
} }
.sidebar { .sidebar {
min-height: 100vh; min-height: 100vh;
background: #fff; background: var(--sidebar-bg);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); box-shadow: 0 0.125rem 0.25rem var(--border-color);
padding-top: 1rem; padding-top: 1rem;
transition: background-color 0.3s ease;
} }
.nav-link { .nav-link {
color: #333; color: var(--nav-link-color);
font-weight: 600; font-weight: 600;
padding: 0.8rem 1.5rem; padding: 0.8rem 1.5rem;
transition: all 0.2s ease;
} }
.nav-link:hover, .nav-link.active { .nav-link:hover, .nav-link.active {
background-color: #f0f7ff; background-color: var(--nav-link-hover-bg);
color: #0d6efd; color: var(--primary-color);
border-left: 4px solid #0d6efd; border-left: 4px solid var(--primary-color);
} }
.card { .card {
border: none; border: none;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); box-shadow: 0 0.125rem 0.25rem var(--border-color);
border-radius: 10px; border-radius: 10px;
background-color: var(--card-bg);
color: var(--text-color);
transition: background-color 0.3s ease;
} }
.btn-primary { .btn-primary {
background-color: #0d6efd; background-color: var(--primary-color);
border: none; border: none;
} }
.btn-primary:hover {
background-color: var(--primary-color);
filter: brightness(90%);
}
.status-received { background-color: #e9ecef; color: #495057; } .status-received { background-color: #e9ecef; color: #495057; }
.status-in_progress { background-color: #cff4fc; color: #055160; } .status-in_progress { background-color: #cff4fc; color: #055160; }
.status-closed { background-color: #d1e7dd; color: #0f5132; } .status-closed { background-color: #d1e7dd; color: #0f5132; }
/* Modal Header Styling */ .modal-content {
background-color: var(--card-bg);
color: var(--text-color);
}
.modal-header.bg-primary { .modal-header.bg-primary {
background-color: #0d6efd !important; background-color: var(--primary-color) !important;
} }
.user-profile-img { .user-profile-img {
@ -99,17 +218,55 @@ if (isLoggedIn()) {
height: 80px; height: 80px;
border-radius: 50%; border-radius: 50%;
object-fit: cover; object-fit: cover;
border: 2px solid #0d6efd; border: 2px solid var(--primary-color);
} }
.charity-logo { .charity-logo {
max-width: 100%; max-width: 100%;
max-height: 60px; max-height: 60px;
} }
.navbar {
background-color: var(--sidebar-bg) !important;
border-color: var(--border-color) !important;
}
.navbar-brand {
color: var(--text-color) !important;
}
/* Theme Switcher Styles */
.theme-switcher {
padding: 1rem 1.5rem;
border-top: 1px solid var(--border-color);
margin-top: 1rem;
}
.theme-options {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 10px;
}
.theme-btn {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
transition: transform 0.2s;
}
.theme-btn:hover {
transform: scale(1.2);
}
.theme-btn.active {
border-color: var(--primary-color);
}
.theme-btn-light { background-color: #f8f9fa; border: 1px solid #ddd; }
.theme-btn-dark { background-color: #121212; }
.theme-btn-midnight { background-color: #0b0e14; }
.theme-btn-forest { background-color: #2d6a4f; }
</style> </style>
</head> </head>
<body> <body>
<div class="container-fluid"> <div class="container-fluid main-container">
<div class="row"> <div class="row">
<?php if (isLoggedIn()): ?> <?php if (isLoggedIn()): ?>
<!-- Sidebar --> <!-- Sidebar -->
@ -178,7 +335,19 @@ if (isLoggedIn()) {
<i class="fas fa-user-circle me-2"></i> الملف الشخصي <i class="fas fa-user-circle me-2"></i> الملف الشخصي
</a> </a>
</li> </li>
<li class="nav-item mt-4">
<!-- Theme Switcher -->
<li class="theme-switcher">
<div class="small fw-bold mb-2 text-center">المظهر</div>
<div class="theme-options">
<div class="theme-btn theme-btn-light <?= $user_theme == 'light' ? 'active' : '' ?>" onclick="setTheme('light')" title="فاتح"></div>
<div class="theme-btn theme-btn-dark <?= $user_theme == 'dark' ? 'active' : '' ?>" onclick="setTheme('dark')" title="داكن"></div>
<div class="theme-btn theme-btn-midnight <?= $user_theme == 'midnight' ? 'active' : '' ?>" onclick="setTheme('midnight')" title="منتصف الليل"></div>
<div class="theme-btn theme-btn-forest <?= $user_theme == 'forest' ? 'active' : '' ?>" onclick="setTheme('forest')" title="غابة"></div>
</div>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-danger" href="logout.php"> <a class="nav-link text-danger" href="logout.php">
<i class="fas fa-sign-out-alt me-2"></i> تسجيل الخروج <i class="fas fa-sign-out-alt me-2"></i> تسجيل الخروج
</a> </a>
@ -186,6 +355,32 @@ if (isLoggedIn()) {
</ul> </ul>
</div> </div>
</nav> </nav>
<script>
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
document.querySelectorAll('.theme-btn').forEach(btn => btn.classList.remove('active'));
document.querySelector('.theme-btn-' + theme).classList.add('active');
fetch('api/update_theme.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: theme })
})
.then(response => response.json())
.then(data => {
if (!data.success) console.error('Failed to update theme preference');
});
}
</script>
<?php endif; ?> <?php endif; ?>
<nav class="navbar navbar-expand-md navbar-light bg-white d-md-none border-bottom mb-3"><div class="container-fluid"><span class="navbar-brand">القائمة</span><button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".sidebar" aria-controls="sidebar" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button></div></nav><main class="<?= isLoggedIn() ? 'col-md-9 ms-sm-auto col-lg-10' : 'col-12' ?> px-md-4 py-4"> <nav class="navbar navbar-expand-md navbar-light bg-white d-md-none border-bottom mb-3">
<div class="container-fluid">
<span class="navbar-brand">القائمة</span>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".sidebar" aria-controls="sidebar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</nav>
<main class="<?= isLoggedIn() ? 'col-md-9 ms-sm-auto col-lg-10' : 'col-12' ?> px-md-4 py-4">

View File

@ -25,6 +25,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$_SESSION['username'] = $user['username']; $_SESSION['username'] = $user['username'];
$_SESSION['full_name'] = $user['full_name']; $_SESSION['full_name'] = $user['full_name'];
$_SESSION['user_role'] = $user['role']; $_SESSION['user_role'] = $user['role'];
// Set permissions in session immediately
$_SESSION['can_view'] = $user['can_view'] ?? 1;
$_SESSION['can_add'] = $user['can_add'] ?? 0;
$_SESSION['can_edit'] = $user['can_edit'] ?? 0;
$_SESSION['can_delete'] = $user['can_delete'] ?? 0;
redirect('index.php'); redirect('index.php');
} else { } else {
$error = 'اسم المستخدم أو كلمة المرور غير صحيحة'; $error = 'اسم المستخدم أو كلمة المرور غير صحيحة';
@ -70,4 +77,4 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
</div> </div>
</div> </div>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -2,6 +2,11 @@
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');
}
// Safe truncation helper // Safe truncation helper
if (!function_exists('truncate_text')) { if (!function_exists('truncate_text')) {
function truncate_text($text, $limit = 100) { function truncate_text($text, $limit = 100) {
@ -59,86 +64,96 @@ function sendAssignmentNotification($assigned_to_id, $ref_no, $subject) {
// Handle actions // Handle actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? ''; $action = $_POST['action'] ?? '';
$type = 'outbound';
$ref_no = $_POST['ref_no'] ?? ''; // Permission checks for POST actions
$date_registered = $_POST['date_registered'] ?? date('Y-m-d'); if (($action === 'add' && !canAdd()) || ($action === 'edit' && !canEdit())) {
$due_date = !empty($_POST['due_date']) ? $_POST['due_date'] : null; $error = 'عذراً، ليس لديك الصلاحية للقيام بهذا الإجراء';
$sender = $_POST['sender'] ?? ''; } else {
$recipient = $_POST['recipient'] ?? ''; $type = 'outbound';
$subject = $_POST['subject'] ?? ''; $ref_no = $_POST['ref_no'] ?? '';
$description = $_POST['description'] ?? ''; $date_registered = $_POST['date_registered'] ?? date('Y-m-d');
$status_id = $_POST['status_id'] ?? $default_status_id; $due_date = !empty($_POST['due_date']) ? $_POST['due_date'] : null;
$assigned_to = !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null; $sender = $_POST['sender'] ?? '';
$id = $_POST['id'] ?? 0; $recipient = $_POST['recipient'] ?? '';
$subject = $_POST['subject'] ?? '';
$description = $_POST['description'] ?? '';
$status_id = $_POST['status_id'] ?? $default_status_id;
$assigned_to = !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null;
$id = $_POST['id'] ?? 0;
if ($ref_no && $subject) { if ($ref_no && $subject) {
try { try {
db()->beginTransaction(); db()->beginTransaction();
if ($action === 'add') { if ($action === 'add') {
$stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$type, $ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $user_id]); $stmt->execute([$type, $ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $user_id]);
$mail_id = db()->lastInsertId(); $mail_id = db()->lastInsertId();
if ($assigned_to) { if ($assigned_to) {
sendAssignmentNotification($assigned_to, $ref_no, $subject); sendAssignmentNotification($assigned_to, $ref_no, $subject);
}
$success = 'تمت إضافة البريد الصادر بنجاح';
} elseif ($action === 'edit') {
$mail_id = $id;
// Get previous assigned_to to check if it changed
$stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?");
$stmt_old->execute([$id]);
$old_assigned_to = $stmt_old->fetchColumn();
$stmt = db()->prepare("UPDATE mailbox SET ref_no = ?, date_registered = ?, due_date = ?, sender = ?, recipient = ?, subject = ?, description = ?, status_id = ?, assigned_to = ? WHERE id = ? AND type = 'outbound'");
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $mail_id]);
if ($assigned_to && $assigned_to != $old_assigned_to) {
sendAssignmentNotification($assigned_to, $ref_no, $subject);
}
$success = 'تم تحديث البيانات بنجاح';
} }
$success = 'تمت إضافة البريد الصادر بنجاح';
} elseif ($action === 'edit') {
$mail_id = $id;
// Get previous assigned_to to check if it changed
$stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?");
$stmt_old->execute([$id]);
$old_assigned_to = $stmt_old->fetchColumn();
$stmt = db()->prepare("UPDATE mailbox SET ref_no = ?, date_registered = ?, due_date = ?, sender = ?, recipient = ?, subject = ?, description = ?, status_id = ?, assigned_to = ? WHERE id = ? AND type = 'outbound'"); // Handle Attachments
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $mail_id]); if (!empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/attachments/';
if ($assigned_to && $assigned_to != $old_assigned_to) { if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
sendAssignmentNotification($assigned_to, $ref_no, $subject);
}
$success = 'تم تحديث البيانات بنجاح';
}
// Handle Attachments foreach ($_FILES['attachments']['name'] as $key => $name) {
if (!empty($_FILES['attachments']['name'][0])) { if ($_FILES['attachments']['error'][$key] === 0) {
$upload_dir = 'uploads/attachments/'; $file_name = time() . '_' . basename($name);
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true); $target_path = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['attachments']['tmp_name'][$key], $target_path)) {
foreach ($_FILES['attachments']['name'] as $key => $name) { $stmt = db()->prepare("INSERT INTO attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)");
if ($_FILES['attachments']['error'][$key] === 0) { $stmt->execute([$mail_id, $name, $target_path, $name, $_FILES['attachments']['size'][$key]]);
$file_name = time() . '_' . basename($name); }
$target_path = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['attachments']['tmp_name'][$key], $target_path)) {
$stmt = db()->prepare("INSERT INTO attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$mail_id, $name, $target_path, $name, $_FILES['attachments']['size'][$key]]);
} }
} }
} }
}
db()->commit(); db()->commit();
} catch (PDOException $e) { } catch (PDOException $e) {
db()->rollBack(); db()->rollBack();
if ($e->getCode() == 23000) { if ($e->getCode() == 23000) {
$error = 'رقم القيد مستخدم مسبقاً'; $error = 'رقم القيد مستخدم مسبقاً';
} else { } else {
$error = 'حدث خطأ: ' . $e->getMessage(); $error = 'حدث خطأ: ' . $e->getMessage();
}
} }
} else {
$error = 'يرجى ملء الحقول المطلوبة (رقم القيد، الموضوع)';
} }
} else {
$error = 'يرجى ملء الحقول المطلوبة (رقم القيد، الموضوع)';
} }
} }
// Delete action // Delete action
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) { if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
$id = $_GET['id']; if (!canDelete()) {
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'outbound'"); $error = 'عذراً، ليس لديك الصلاحية لحذف السجلات';
$stmt->execute([$id]); } else {
$success = 'تم حذف البريد بنجاح'; $id = $_GET['id'];
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'outbound'");
$stmt->execute([$id]);
$success = 'تم حذف البريد بنجاح';
}
} }
$search = $_GET['search'] ?? ''; $search = $_GET['search'] ?? '';
@ -173,9 +188,11 @@ $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'])) {
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'outbound'"); if (canEdit()) {
$stmt->execute([$_GET['id']]); $stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'outbound'");
$deepLinkData = $stmt->fetch(); $stmt->execute([$_GET['id']]);
$deepLinkData = $stmt->fetch();
}
} }
function getStatusBadgeInList($mail) { function getStatusBadgeInList($mail) {
@ -194,9 +211,11 @@ 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()): ?>
<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>
<?php endif; ?>
</div> </div>
<?php if ($success): ?> <?php if ($success): ?>
@ -279,11 +298,17 @@ function getStatusBadgeInList($mail) {
<td><?= getStatusBadgeInList($mail) ?></td> <td><?= getStatusBadgeInList($mail) ?></td>
<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()): ?>
<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 if (canDelete()): ?>
<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; ?>
</td> </td>
</tr> </tr>
<?php endforeach; else: ?> <?php endforeach; else: ?>
@ -297,6 +322,7 @@ function getStatusBadgeInList($mail) {
</div> </div>
</div> </div>
<?php if (canAdd() || canEdit()): ?>
<!-- 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-xl"> <div class="modal-dialog modal-xl">
@ -504,6 +530,7 @@ function confirmDelete(id) {
}) })
} }
</script> </script>
<?php endif; ?>
<style> <style>
.ck-editor__editable_inline { .ck-editor__editable_inline {
@ -518,4 +545,4 @@ function confirmDelete(id) {
} }
</style> </style>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,6 +1,12 @@
<?php <?php
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.php';
// Check if user has view permission
if (!canView()) {
// If they can't even view, they shouldn't be here, but header.php already handles basic login.
// We'll let them see their profile at least, but maybe not this dashboard.
}
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
$user_role = $_SESSION['user_role']; $user_role = $_SESSION['user_role'];
$is_admin = isAdmin(); $is_admin = isAdmin();
@ -37,8 +43,8 @@ $recent_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.f
LEFT JOIN mailbox_statuses s ON m.status_id = s.id LEFT JOIN mailbox_statuses s ON m.status_id = s.id
LEFT JOIN users u ON m.assigned_to = u.id"; LEFT JOIN users u ON m.assigned_to = u.id";
if ($is_clerk) { if ($is_admin || $is_clerk) {
// Clerks see all recent activity // Admins and Clerks see all recent activity if they have view permission
$recent_stmt = db()->prepare($recent_query . " ORDER BY m.updated_at DESC LIMIT 10"); $recent_stmt = db()->prepare($recent_query . " ORDER BY m.updated_at DESC LIMIT 10");
$recent_stmt->execute(); $recent_stmt->execute();
} else { } else {
@ -71,10 +77,17 @@ function getStatusBadge($mail) {
<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['username']) ?>!</h2>
<p class="mb-0 opacity-75"> <p class="mb-0 opacity-75">
<?php if ($is_clerk): ?> أنت مسجل كـ <strong>
أنت مسجل كـ <strong>كاتب</strong>. يمكنك متابعة كافة المراسلات وإدارة المهام. <?php
if ($is_admin) echo 'مدير النظام';
elseif ($is_clerk) echo 'كاتب';
else echo 'موظف';
?>
</strong>.
<?php if ($is_admin || $is_clerk): ?>
يمكنك متابعة كافة المراسلات وإدارة المهام.
<?php else: ?> <?php else: ?>
أنت مسجل كـ <strong>موظف</strong>. تابع مهامك المسندة إليك هنا. تابع مهامك المسندة إليك هنا.
<?php endif; ?> <?php endif; ?>
</p> </p>
</div> </div>
@ -121,8 +134,8 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
<?php if ($is_clerk): ?> <?php if ($is_admin || $is_clerk): ?>
<!-- Clerk specific stats --> <!-- Admin/Clerk specific stats -->
<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">
@ -197,8 +210,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()): ?>
<a href="inbound.php?action=add" class="btn btn-sm btn-outline-primary">إضافة وارد</a> <a href="inbound.php?action=add" class="btn btn-sm btn-outline-primary">إضافة وارد</a>
<a href="outbound.php" class="btn btn-sm btn-outline-success">إضافة صادر</a> <a href="outbound.php" class="btn btn-sm btn-outline-success">إضافة صادر</a>
<?php endif; ?>
</div> </div>
</div> </div>
<div class="card-body p-0"> <div class="card-body p-0">
@ -253,7 +268,7 @@ function getStatusBadge($mail) {
<div class="col-lg-4"> <div class="col-lg-4">
<div class="card shadow-sm border-0 mb-4 h-100"> <div class="card shadow-sm border-0 mb-4 h-100">
<div class="card-header bg-white py-3 border-bottom"> <div class="card-header bg-white py-3 border-bottom">
<h5 class="mb-0 fw-bold"><i class="fas fa-bell me-2 text-warning"></i> <?= $is_clerk ? 'آخر المراسلات' : 'نشاطاتي الأخيرة' ?></h5> <h5 class="mb-0 fw-bold"><i class="fas fa-bell me-2 text-warning"></i> <?= ($is_admin || $is_clerk) ? 'آخر المراسلات' : 'نشاطاتي الأخيرة' ?></h5>
</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">
@ -287,4 +302,4 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

105
users.php
View File

@ -15,13 +15,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$role = $_POST['role'] ?? 'staff'; $role = $_POST['role'] ?? 'staff';
$password = $_POST['password'] ?? ''; $password = $_POST['password'] ?? '';
$id = $_POST['id'] ?? 0; $id = $_POST['id'] ?? 0;
// Permissions
$can_view = isset($_POST['can_view']) ? 1 : 0;
$can_add = isset($_POST['can_add']) ? 1 : 0;
$can_edit = isset($_POST['can_edit']) ? 1 : 0;
$can_delete = isset($_POST['can_delete']) ? 1 : 0;
if ($action === 'add') { if ($action === 'add') {
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) VALUES (?, ?, ?, ?)"); $stmt = db()->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]); $stmt->execute([$username, $hashed_password, $full_name, $role, $can_view, $can_add, $can_edit, $can_delete]);
$success = 'تم إضافة المستخدم بنجاح'; $success = 'تم إضافة المستخدم بنجاح';
} catch (PDOException $e) { } catch (PDOException $e) {
if ($e->getCode() == 23000) { if ($e->getCode() == 23000) {
@ -38,11 +44,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try { try {
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 = ? WHERE id = ?"); $stmt = db()->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, $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 = ? WHERE id = ?"); $stmt = db()->prepare("UPDATE users SET username = ?, full_name = ?, role = ?, can_view = ?, can_add = ?, can_edit = ?, can_delete = ? WHERE id = ?");
$stmt->execute([$username, $full_name, $role, $id]); $stmt->execute([$username, $full_name, $role, $can_view, $can_add, $can_edit, $can_delete, $id]);
} }
$success = 'تم تحديث بيانات المستخدم بنجاح'; $success = 'تم تحديث بيانات المستخدم بنجاح';
} catch (PDOException $e) { } catch (PDOException $e) {
@ -77,7 +83,7 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['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>
<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>
@ -106,6 +112,7 @@ 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>
@ -124,6 +131,14 @@ 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">
<button type="button" class="btn btn-sm btn-outline-primary" <button type="button" class="btn btn-sm btn-outline-primary"
@ -171,12 +186,42 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id']))
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-bold">الدور</label> <label class="form-label fw-bold">الدور</label>
<select name="role" id="modalRole" class="form-select"> <select name="role" id="modalRole" class="form-select" onchange="applyRolePresets(this.value)">
<option value="staff">موظف</option> <option value="staff">موظف</option>
<option value="clerk">كاتب</option> <option value="clerk">كاتب</option>
<option value="admin">مدير</option> <option value="admin">مدير</option>
</select> </select>
</div> </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="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>
@ -190,6 +235,23 @@ if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id']))
<script> <script>
let userModal; let userModal;
function applyRolePresets(role) {
const view = document.getElementById('permView');
const add = document.getElementById('permAdd');
const edit = document.getElementById('permEdit');
const del = document.getElementById('permDelete');
if (role === 'admin') {
view.checked = add.checked = edit.checked = del.checked = true;
} else if (role === 'clerk') {
view.checked = add.checked = edit.checked = true;
del.checked = false;
} else {
view.checked = true;
add.checked = edit.checked = del.checked = false;
}
}
function openUserModal(action, data = null) { function openUserModal(action, data = null) {
if (!userModal) { if (!userModal) {
const modalEl = document.getElementById('userModal'); const modalEl = document.getElementById('userModal');
@ -212,6 +274,13 @@ 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;
@ -220,6 +289,7 @@ function openUserModal(action, data = null) {
modalId.value = '0'; modalId.value = '0';
Object.keys(fields).forEach(key => fields[key].value = ''); Object.keys(fields).forEach(key => fields[key].value = '');
modalRole.value = 'staff'; modalRole.value = 'staff';
applyRolePresets('staff');
modalPassword.required = true; modalPassword.required = true;
pwdHint.textContent = ''; pwdHint.textContent = '';
} else { } else {
@ -228,6 +298,13 @@ function openUserModal(action, data = null) {
Object.keys(fields).forEach(key => { Object.keys(fields).forEach(key => {
if (fields[key]) fields[key].value = data[key] || ''; if (fields[key]) fields[key].value = data[key] || '';
}); });
// Set permissions checkboxes
perms.can_view.checked = data.can_view == 1;
perms.can_add.checked = data.can_add == 1;
perms.can_edit.checked = data.can_edit == 1;
perms.can_delete.checked = data.can_delete == 1;
modalPassword.required = false; modalPassword.required = false;
pwdHint.textContent = '(اتركه فارغاً للحفاظ على كلمة المرور الحالية)'; pwdHint.textContent = '(اتركه فارغاً للحفاظ على كلمة المرور الحالية)';
} }
@ -243,7 +320,11 @@ document.addEventListener('DOMContentLoaded', function() {
'id' => $_POST['id'] ?? 0, 'id' => $_POST['id'] ?? 0,
'username' => $_POST['username'] ?? '', 'username' => $_POST['username'] ?? '',
'full_name' => $_POST['full_name'] ?? '', 'full_name' => $_POST['full_name'] ?? '',
'role' => $_POST['role'] ?? 'staff' '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); openUserModal('<?= $_POST['action'] ?>', errorData);
<?php elseif (isset($_GET['action']) && $_GET['action'] === 'add'): ?> <?php elseif (isset($_GET['action']) && $_GET['action'] === 'add'): ?>
@ -283,6 +364,10 @@ function confirmDelete(id) {
.modal-header.bg-primary { .modal-header.bg-primary {
background-color: #0d6efd !important; background-color: #0d6efd !important;
} }
.form-check-input:checked {
background-color: #198754;
border-color: #198754;
}
</style> </style>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,6 +1,11 @@
<?php <?php
require_once __DIR__ . '/includes/header.php'; require_once __DIR__ . '/includes/header.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');
@ -21,53 +26,65 @@ $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'])) {
$comment = $_POST['comment'] ?? ''; if (!canEdit()) {
if ($comment) { $error = 'عذراً، ليس لديك الصلاحية لإضافة تعليقات';
$stmt = db()->prepare("INSERT INTO comments (mail_id, user_id, comment) VALUES (?, ?, ?)"); } else {
$stmt->execute([$id, $_SESSION['user_id'], $comment]); $comment = $_POST['comment'] ?? '';
$success = 'تم إضافة التعليق بنجاح'; if ($comment) {
$stmt = db()->prepare("INSERT INTO comments (mail_id, user_id, comment) VALUES (?, ?, ?)");
$stmt->execute([$id, $_SESSION['user_id'], $comment]);
$success = 'تم إضافة التعليق بنجاح';
}
} }
} }
// Handle Attachment upload // Handle Attachment upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) {
$file = $_FILES['attachment']; if (!canEdit()) {
$display_name = $_POST['display_name'] ?? ''; $error = 'عذراً، ليس لديك الصلاحية لرفع مرفقات';
if ($file['error'] === 0) { } else {
$upload_dir = 'uploads/attachments/'; $file = $_FILES['attachment'];
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true); $display_name = $_POST['display_name'] ?? '';
if ($file['error'] === 0) {
$file_name = time() . '_' . basename($file['name']); $upload_dir = 'uploads/attachments/';
$target_path = $upload_dir . $file_name; if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
if (move_uploaded_file($file['tmp_name'], $target_path)) { $file_name = time() . '_' . basename($file['name']);
$stmt = db()->prepare("INSERT INTO attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)"); $target_path = $upload_dir . $file_name;
$stmt->execute([$id, $display_name, $target_path, $file['name'], $file['size']]);
$success = 'تم رفع الملف بنجاح'; if (move_uploaded_file($file['tmp_name'], $target_path)) {
} else { $stmt = db()->prepare("INSERT INTO attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)");
$error = 'فشل في رفع الملف'; $stmt->execute([$id, $display_name, $target_path, $file['name'], $file['size']]);
$success = 'تم رفع الملف بنجاح';
} else {
$error = 'فشل في رفع الملف';
}
} }
} }
} }
// Handle Attachment deletion // Handle Attachment deletion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_attachment'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_attachment'])) {
$attachment_id = $_POST['attachment_id'] ?? 0; if (!canDelete()) {
if ($attachment_id) { $error = 'عذراً، ليس لديك الصلاحية لحذف المرفقات';
$stmt = db()->prepare("SELECT * FROM attachments WHERE id = ?"); } else {
$stmt->execute([$attachment_id]); $attachment_id = $_POST['attachment_id'] ?? 0;
$attachment = $stmt->fetch(); if ($attachment_id) {
$stmt = db()->prepare("SELECT * FROM attachments WHERE id = ?");
if ($attachment) {
// Delete file from disk
if (file_exists($attachment['file_path'])) {
unlink($attachment['file_path']);
}
// Delete record from DB
$stmt = db()->prepare("DELETE FROM attachments WHERE id = ?");
$stmt->execute([$attachment_id]); $stmt->execute([$attachment_id]);
$success = 'تم حذف المرفق بنجاح'; $attachment = $stmt->fetch();
if ($attachment) {
// Delete file from disk
if (file_exists($attachment['file_path'])) {
unlink($attachment['file_path']);
}
// Delete record from DB
$stmt = db()->prepare("DELETE FROM attachments WHERE id = ?");
$stmt->execute([$attachment_id]);
$success = 'تم حذف المرفق بنجاح';
}
} }
} }
} }
@ -91,7 +108,9 @@ function isPreviewable($fileName) {
<h1 class="h2">تفاصيل <?= $mail['type'] == 'inbound' ? 'البريد الوارد' : 'البريد الصادر' ?></h1> <h1 class="h2">تفاصيل <?= $mail['type'] == 'inbound' ? 'البريد الوارد' : 'البريد الصادر' ?></h1>
<div class="btn-group"> <div class="btn-group">
<a href="<?= $mail['type'] ?>.php" class="btn btn-outline-secondary">عودة للقائمة</a> <a href="<?= $mail['type'] ?>.php" class="btn btn-outline-secondary">عودة للقائمة</a>
<?php if (canEdit()): ?>
<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; ?>
</div> </div>
</div> </div>
@ -205,12 +224,14 @@ function isPreviewable($fileName) {
<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 (canEdit()): ?>
<form method="POST" class="mb-4"> <form method="POST" class="mb-4">
<div class="mb-2"> <div class="mb-2">
<textarea name="comment" class="form-control" rows="2" placeholder="أضف تعليقاً أو ملاحظة متابعة..." required></textarea> <textarea name="comment" class="form-control" rows="2" placeholder="أضف تعليقاً أو ملاحظة متابعة..." required></textarea>
</div> </div>
<button type="submit" name="add_comment" class="btn btn-sm btn-primary">إرسال تعليق</button> <button type="submit" name="add_comment" class="btn btn-sm btn-primary">إرسال تعليق</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): ?>
@ -236,6 +257,7 @@ function isPreviewable($fileName) {
<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 (canEdit()): ?>
<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>
@ -245,6 +267,7 @@ function isPreviewable($fileName) {
</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): ?>
@ -266,6 +289,8 @@ function isPreviewable($fileName) {
<i class="fas fa-eye"></i> <i class="fas fa-eye"></i>
</button> </button>
<?php endif; ?> <?php endif; ?>
<?php if (canDelete()): ?>
<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">
@ -273,6 +298,7 @@ function isPreviewable($fileName) {
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</form> </form>
<?php endif; ?>
</div> </div>
</div> </div>
</div> </div>
@ -364,4 +390,4 @@ function isPreviewable($fileName) {
}); });
</script> </script>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>