splitting db

This commit is contained in:
Flatlogic Bot 2026-02-28 08:22:46 +00:00
parent c6cb25129c
commit c5ac8e3c6e
13 changed files with 524 additions and 306 deletions

View File

@ -3,6 +3,10 @@ document.addEventListener('DOMContentLoaded', () => {
const chatInput = document.getElementById('chat-input'); const chatInput = document.getElementById('chat-input');
const chatMessages = document.getElementById('chat-messages'); const chatMessages = document.getElementById('chat-messages');
if (!chatForm || !chatInput || !chatMessages) {
return; // Not on the chat page
}
const appendMessage = (text, sender) => { const appendMessage = (text, sender) => {
const msgDiv = document.createElement('div'); const msgDiv = document.createElement('div');
msgDiv.classList.add('message', sender); msgDiv.classList.add('message', sender);
@ -36,4 +40,4 @@ document.addEventListener('DOMContentLoaded', () => {
appendMessage("Sorry, something went wrong. Please try again.", 'bot'); appendMessage("Sorry, something went wrong. Please try again.", 'bot');
} }
}); });
}); });

View File

@ -158,9 +158,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_status'])) {
$_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لحذف الإعدادات'; $_SESSION['error'] = 'عذراً، ليس لديك الصلاحية لحذف الإعدادات';
} else { } else {
$id = $_POST['status_id']; $id = $_POST['status_id'];
$count = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE status_id = ?"); $count = 0; foreach(['inbound_mail', 'outbound_mail', 'internal_mail'] as $t) { $stmt = db()->prepare("SELECT COUNT(*) FROM $t WHERE status_id = ?"); $stmt->execute([$id]); $count += $stmt->fetchColumn(); }
$count->execute([$id]);
if ($count->fetchColumn() > 0) { if ($count > 0) {
$_SESSION['error'] = 'لا يمكن حذف هذه الحالة لأنها مستخدمة في بعض السجلات'; $_SESSION['error'] = 'لا يمكن حذف هذه الحالة لأنها مستخدمة في بعض السجلات';
} else { } else {
db()->prepare("DELETE FROM mailbox_statuses WHERE id = ?")->execute([$id]); db()->prepare("DELETE FROM mailbox_statuses WHERE id = ?")->execute([$id]);

View File

@ -22,14 +22,21 @@ function db() {
*/ */
function generateRefNo($type) { function generateRefNo($type) {
$prefix = 'IN'; $prefix = 'IN';
if ($type === 'outbound') $prefix = 'OUT'; $table = 'inbound_mail';
if ($type === 'internal') $prefix = 'INT'; if ($type === 'outbound') {
$prefix = 'OUT';
$table = 'outbound_mail';
} elseif ($type === 'internal') {
$prefix = 'INT';
$table = 'internal_mail';
}
$year = date('Y'); $year = date('Y');
$pattern = $prefix . '-' . $year . '-%'; $pattern = $prefix . '-' . $year . '-%';
$stmt = db()->prepare("SELECT ref_no FROM mailbox WHERE type = ? AND ref_no LIKE ? ORDER BY id DESC LIMIT 1"); // Query the specific table for the type
$stmt->execute([$type, $pattern]); $stmt = db()->prepare("SELECT ref_no FROM $table WHERE ref_no LIKE ? ORDER BY id DESC LIMIT 1");
$stmt->execute([$pattern]);
$last_ref = $stmt->fetchColumn(); $last_ref = $stmt->fetchColumn();
$serial = 1; $serial = 1;

View File

@ -0,0 +1,176 @@
-- Migration: Split mailbox into separate tables for each module
-- This addresses the architectural concern of having all modules in a single table
-- 1. Create INBOUND tables
CREATE TABLE IF NOT EXISTS inbound_mail (
id INT AUTO_INCREMENT PRIMARY KEY,
ref_no VARCHAR(50) NOT NULL UNIQUE,
date_registered DATE NOT NULL,
due_date DATE NULL,
sender VARCHAR(255),
recipient VARCHAR(255),
subject VARCHAR(255) NOT NULL,
description TEXT,
status_id INT,
assigned_to INT,
created_by INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (assigned_to) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS inbound_attachments (
id INT AUTO_INCREMENT PRIMARY KEY,
mail_id INT NOT NULL,
display_name VARCHAR(255),
file_path VARCHAR(255) NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_size INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (mail_id) REFERENCES inbound_mail(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS inbound_comments (
id INT AUTO_INCREMENT PRIMARY KEY,
mail_id INT NOT NULL,
user_id INT,
comment TEXT NOT NULL,
referred_user_id INT DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (mail_id) REFERENCES inbound_mail(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (referred_user_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 2. Create OUTBOUND tables
CREATE TABLE IF NOT EXISTS outbound_mail (
id INT AUTO_INCREMENT PRIMARY KEY,
ref_no VARCHAR(50) NOT NULL UNIQUE,
date_registered DATE NOT NULL,
due_date DATE NULL,
sender VARCHAR(255),
recipient VARCHAR(255),
subject VARCHAR(255) NOT NULL,
description TEXT,
status_id INT,
assigned_to INT,
created_by INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (assigned_to) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS outbound_attachments (
id INT AUTO_INCREMENT PRIMARY KEY,
mail_id INT NOT NULL,
display_name VARCHAR(255),
file_path VARCHAR(255) NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_size INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (mail_id) REFERENCES outbound_mail(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS outbound_comments (
id INT AUTO_INCREMENT PRIMARY KEY,
mail_id INT NOT NULL,
user_id INT,
comment TEXT NOT NULL,
referred_user_id INT DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (mail_id) REFERENCES outbound_mail(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (referred_user_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 3. Create INTERNAL tables
CREATE TABLE IF NOT EXISTS internal_mail (
id INT AUTO_INCREMENT PRIMARY KEY,
ref_no VARCHAR(50) NOT NULL UNIQUE,
date_registered DATE NOT NULL,
due_date DATE NULL,
sender VARCHAR(255),
recipient VARCHAR(255),
subject VARCHAR(255) NOT NULL,
description TEXT,
status_id INT,
assigned_to INT,
created_by INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (assigned_to) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS internal_attachments (
id INT AUTO_INCREMENT PRIMARY KEY,
mail_id INT NOT NULL,
display_name VARCHAR(255),
file_path VARCHAR(255) NOT NULL,
file_name VARCHAR(255) NOT NULL,
file_size INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (mail_id) REFERENCES internal_mail(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS internal_comments (
id INT AUTO_INCREMENT PRIMARY KEY,
mail_id INT NOT NULL,
user_id INT,
comment TEXT NOT NULL,
referred_user_id INT DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (mail_id) REFERENCES internal_mail(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
FOREIGN KEY (referred_user_id) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 4. Migrate data from old mailbox table
-- We'll use a temporary mapping for IDs if we were strict, but since we are splitting, we can just insert.
-- Note: Original IDs will change, which means attachments and comments must be migrated carefully.
-- Migrate Inbound
INSERT INTO inbound_mail (id, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by, created_at, updated_at)
SELECT id, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by, created_at, updated_at
FROM mailbox WHERE type = 'inbound';
INSERT INTO inbound_attachments (id, mail_id, display_name, file_path, file_name, file_size, created_at)
SELECT a.id, a.mail_id, a.display_name, a.file_path, a.file_name, a.file_size, a.created_at
FROM attachments a JOIN mailbox m ON a.mail_id = m.id WHERE m.type = 'inbound';
INSERT INTO inbound_comments (id, mail_id, user_id, comment, referred_user_id, created_at)
SELECT c.id, c.mail_id, c.user_id, c.comment, c.referred_user_id, c.created_at
FROM comments c JOIN mailbox m ON c.mail_id = m.id WHERE m.type = 'inbound';
-- Migrate Outbound
INSERT INTO outbound_mail (id, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by, created_at, updated_at)
SELECT id, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by, created_at, updated_at
FROM mailbox WHERE type = 'outbound';
INSERT INTO outbound_attachments (id, mail_id, display_name, file_path, file_name, file_size, created_at)
SELECT a.id, a.mail_id, a.display_name, a.file_path, a.file_name, a.file_size, a.created_at
FROM attachments a JOIN mailbox m ON a.mail_id = m.id WHERE m.type = 'outbound';
INSERT INTO outbound_comments (id, mail_id, user_id, comment, referred_user_id, created_at)
SELECT c.id, c.mail_id, c.user_id, c.comment, c.referred_user_id, c.created_at
FROM comments c JOIN mailbox m ON c.mail_id = m.id WHERE m.type = 'outbound';
-- Migrate Internal
INSERT INTO internal_mail (id, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by, created_at, updated_at)
SELECT id, ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by, created_at, updated_at
FROM mailbox WHERE type = 'internal';
INSERT INTO internal_attachments (id, mail_id, display_name, file_path, file_name, file_size, created_at)
SELECT a.id, a.mail_id, a.display_name, a.file_path, a.file_name, a.file_size, a.created_at
FROM attachments a JOIN mailbox m ON a.mail_id = m.id WHERE m.type = 'internal';
INSERT INTO internal_comments (id, mail_id, user_id, comment, referred_user_id, created_at)
SELECT c.id, c.mail_id, c.user_id, c.comment, c.referred_user_id, c.created_at
FROM comments c JOIN mailbox m ON c.mail_id = m.id WHERE m.type = 'internal';
-- 5. Rename old tables instead of dropping for safety
RENAME TABLE mailbox TO mailbox_old;
RENAME TABLE attachments TO attachments_old;
RENAME TABLE comments TO comments_old;

View File

@ -74,8 +74,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
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 inbound_mail (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([$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) {
@ -86,11 +86,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} elseif ($action === 'edit') { } elseif ($action === 'edit') {
$mail_id = $id; $mail_id = $id;
// Get previous assigned_to to check if it changed // Get previous assigned_to to check if it changed
$stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?"); $stmt_old = db()->prepare("SELECT assigned_to FROM inbound_mail WHERE id = ?");
$stmt_old->execute([$id]); $stmt_old->execute([$id]);
$old_assigned_to = $stmt_old->fetchColumn(); $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 = db()->prepare("UPDATE inbound_mail SET ref_no = ?, date_registered = ?, due_date = ?, sender = ?, recipient = ?, subject = ?, description = ?, status_id = ?, assigned_to = ? WHERE id = ? ");
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $id]); $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) { if ($assigned_to && $assigned_to != $old_assigned_to) {
@ -110,7 +110,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$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 inbound_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]]);
} }
} }
@ -145,7 +145,7 @@ if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])
$error = 'عذراً، ليس لديك الصلاحية لحذف السجلات'; $error = 'عذراً، ليس لديك الصلاحية لحذف السجلات';
} else { } else {
$id = $_GET['id']; $id = $_GET['id'];
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'inbound'"); $stmt = db()->prepare("DELETE FROM inbound_mail WHERE id = ? ");
$stmt->execute([$id]); $stmt->execute([$id]);
$_SESSION['success'] = 'تم حذف البريد بنجاح'; $_SESSION['success'] = 'تم حذف البريد بنجاح';
redirect('inbound.php'); redirect('inbound.php');
@ -189,7 +189,7 @@ if ($my_tasks) {
$where_sql = implode(" AND ", $where_clauses); $where_sql = implode(" AND ", $where_clauses);
// Get total records for pagination // Get total records for pagination
$count_query = "SELECT COUNT(*) FROM mailbox m WHERE $where_sql"; $count_query = "SELECT COUNT(*) FROM inbound_mail m WHERE $where_sql";
$stmt_count = db()->prepare($count_query); $stmt_count = db()->prepare($count_query);
$stmt_count->execute($params); $stmt_count->execute($params);
$total_records = $stmt_count->fetchColumn(); $total_records = $stmt_count->fetchColumn();
@ -197,7 +197,7 @@ $total_pages = ceil($total_records / $limit);
// Fetch paginated results // Fetch paginated results
$query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name $query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name
FROM mailbox m FROM inbound_mail m
LEFT JOIN mailbox_statuses s ON m.status_id = s.id LEFT JOIN mailbox_statuses s ON m.status_id = s.id
LEFT JOIN users u ON m.assigned_to = u.id LEFT JOIN users u ON m.assigned_to = u.id
WHERE $where_sql WHERE $where_sql
@ -214,7 +214,7 @@ $users_list = db()->query("SELECT id, full_name FROM users ORDER BY full_name")-
$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('inbound')) { if (canEdit('inbound')) {
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'inbound'"); $stmt = db()->prepare("SELECT * FROM inbound_mail WHERE id = ? ");
$stmt->execute([$_GET['id']]); $stmt->execute([$_GET['id']]);
$deepLinkData = $stmt->fetch(); $deepLinkData = $stmt->fetch();
} }
@ -415,7 +415,7 @@ function getStatusBadgeInList($mail) {
</div> </div>
<div class="col-12"> <div class="col-12">
<label class="form-label fw-bold">المرفقات</label> <label class="form-label fw-bold">المرفقات</label>
<input type="file" name="attachments[]" class="form-control" multiple> <input type="file" name="inbound_attachments[]" class="form-control" multiple>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-bold">الحالة</label> <label class="form-label fw-bold">الحالة</label>

114
index.php
View File

@ -9,14 +9,13 @@ if (!isAdmin()) {
$user_id = $_SESSION['user_id']; $user_id = $_SESSION['user_id'];
$is_admin = isAdmin(); $is_admin = isAdmin();
// Stats // Stats - Total counts from separate tables
$total_inbound = canView('inbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'inbound'")->fetchColumn() : 0; $total_inbound = canView('inbound') ? db()->query("SELECT COUNT(*) FROM inbound_mail")->fetchColumn() : 0;
$total_outbound = canView('outbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'outbound'")->fetchColumn() : 0; $total_outbound = canView('outbound') ? db()->query("SELECT COUNT(*) FROM outbound_mail")->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);
// For the "In Progress" stat card, we might need a specific status or just a sum of non-closed statuses.
$in_progress_id = null; $in_progress_id = null;
foreach ($statuses_data as $id => $s) { foreach ($statuses_data as $id => $s) {
if ($s['name'] == 'in_progress') { if ($s['name'] == 'in_progress') {
@ -24,68 +23,83 @@ foreach ($statuses_data as $id => $s) {
break; break;
} }
} }
$in_progress_count = 0; $in_progress_count = 0;
if ($in_progress_id) { if ($in_progress_id) {
$where_types = []; if (canView('inbound')) {
if (canView('inbound')) $where_types[] = "'inbound'"; $in_progress_count += db()->prepare("SELECT COUNT(*) FROM inbound_mail WHERE status_id = ?")->execute([$in_progress_id]) ? db()->prepare("SELECT COUNT(*) FROM inbound_mail WHERE status_id = ?")->execute([$in_progress_id]) : 0;
if (canView('outbound')) $where_types[] = "'outbound'"; // Wait, execute returns bool.
$stmt = db()->prepare("SELECT COUNT(*) FROM inbound_mail WHERE status_id = ?");
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]); $stmt->execute([$in_progress_id]);
$in_progress_count = $stmt->fetchColumn(); $in_progress_count += $stmt->fetchColumn();
}
if (canView('outbound')) {
$stmt = db()->prepare("SELECT COUNT(*) FROM outbound_mail WHERE status_id = ?");
$stmt->execute([$in_progress_id]);
$in_progress_count += $stmt->fetchColumn();
} }
} }
// My Assignments // My Assignments - Combine from all tables
$my_assignments = []; $my_assignments = [];
$assignment_types = []; $queries = [];
if (canView('inbound')) $assignment_types[] = "'inbound'"; if (canView('inbound')) {
if (canView('outbound')) $assignment_types[] = "'outbound'"; $queries[] = "SELECT id, 'inbound' as type, ref_no, subject, due_date, status_id, created_at FROM inbound_mail WHERE assigned_to = $user_id";
if (canView('internal')) $assignment_types[] = "'internal'"; }
if (canView('outbound')) {
$queries[] = "SELECT id, 'outbound' as type, ref_no, subject, due_date, status_id, created_at FROM outbound_mail WHERE assigned_to = $user_id";
}
if (canView('internal')) {
$queries[] = "SELECT id, 'internal' as type, ref_no, subject, due_date, status_id, created_at FROM internal_mail WHERE assigned_to = $user_id";
}
if (!empty($assignment_types)) { if (!empty($queries)) {
$types_sql = implode(',', $assignment_types); $full_query = "(" . implode(") UNION ALL (", $queries) . ") ORDER BY created_at DESC LIMIT 5";
$my_assignments = db()->prepare("SELECT m.*, s.name as status_name, s.color as status_color $stmt = db()->query($full_query);
FROM mailbox m $my_assignments = $stmt->fetchAll();
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE m.assigned_to = ? AND m.type IN ($types_sql) // Add status info to assignments
ORDER BY m.created_at DESC LIMIT 5"); foreach ($my_assignments as &$m) {
$my_assignments->execute([$user_id]); $m['status_name'] = $statuses_data[$m['status_id']]['name'] ?? 'unknown';
$my_assignments = $my_assignments->fetchAll(); $m['status_color'] = $statuses_data[$m['status_id']]['color'] ?? '#6c757d';
}
} }
// Recent Mail (Global for Admin/Clerk, otherwise limited) // Recent Mail (Global for Admin/Clerk, otherwise limited)
$recent_mail = []; $recent_mail = [];
$recent_types = []; $recent_queries = [];
if (canView('inbound')) $recent_types[] = "'inbound'"; if (canView('inbound')) {
if (canView('outbound')) $recent_types[] = "'outbound'"; $recent_queries[] = "SELECT m.id, 'inbound' as type, m.ref_no, m.subject, m.due_date, m.sender, m.recipient, m.status_id, m.assigned_to, m.created_by, m.date_registered, m.created_at, u.full_name as assigned_to_name
FROM inbound_mail m LEFT JOIN users u ON m.assigned_to = u.id";
if (!empty($recent_types)) { }
$types_sql = implode(',', $recent_types); if (canView('outbound')) {
$recent_mail_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name $recent_queries[] = "SELECT m.id, 'outbound' as type, m.ref_no, m.subject, m.due_date, m.sender, m.recipient, m.status_id, m.assigned_to, m.created_by, m.date_registered, m.created_at, u.full_name as assigned_to_name
FROM mailbox m FROM outbound_mail m LEFT JOIN users u ON m.assigned_to = u.id";
LEFT JOIN mailbox_statuses s ON m.status_id = s.id }
LEFT JOIN users u ON m.assigned_to = u.id
WHERE m.type IN ($types_sql)";
if (!empty($recent_queries)) {
$full_recent_query = "(" . implode(") UNION ALL (", $recent_queries) . ")";
if (!$is_admin && ($_SESSION['user_role'] ?? '') !== 'clerk') { if (!$is_admin && ($_SESSION['user_role'] ?? '') !== 'clerk') {
$recent_mail_query .= " AND (m.assigned_to = ? OR m.created_by = ?)"; $full_recent_query = "SELECT * FROM ($full_recent_query) AS combined WHERE assigned_to = $user_id OR created_by = $user_id ORDER BY created_at DESC LIMIT 10";
$recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10");
$recent_stmt->execute([$user_id, $user_id]);
} else { } else {
$recent_stmt = db()->prepare($recent_mail_query . " ORDER BY m.created_at DESC LIMIT 10"); $full_recent_query = "SELECT * FROM ($full_recent_query) AS combined ORDER BY created_at DESC LIMIT 10";
$recent_stmt->execute(); }
$stmt = db()->query($full_recent_query);
$recent_mail = $stmt->fetchAll();
// Add status info
foreach ($recent_mail as &$m) {
$m['status_name'] = $statuses_data[$m['status_id']]['name'] ?? 'unknown';
$m['status_color'] = $statuses_data[$m['status_id']]['color'] ?? '#6c757d';
} }
$recent_mail = $recent_stmt->fetchAll();
} }
function getStatusBadge($mail) { function getStatusBadge($mail) {
$status_name = $mail['status_name'] ?? 'غير معروف'; $status_name = $mail['status_name'] ?? 'غير معروف';
$status_color = $mail['status_color'] ?? '#6c757d'; $status_color = $mail['status_color'] ?? '#6c757d';
// Translation for default statuses
$display_name = $status_name; $display_name = $status_name;
if ($status_name == 'received') $display_name = 'تم الاستلام'; if ($status_name == 'received') $display_name = 'تم الاستلام';
if ($status_name == 'in_progress') $display_name = 'قيد المعالجة'; if ($status_name == 'in_progress') $display_name = 'قيد المعالجة';
@ -106,7 +120,7 @@ function getStatusBadge($mail) {
<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 endif; ?>
<?php if (canAdd('outbound')): ?> <?php if (canAdd('outbound')): ?>
<a href="outbound.php" class="btn btn-sm btn-outline-secondary">إضافة بريد صادر</a> <a href="outbound.php?action=add" class="btn btn-sm btn-outline-secondary">إضافة بريد صادر</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
@ -115,7 +129,11 @@ function getStatusBadge($mail) {
<!-- Overdue Alert --> <!-- Overdue Alert -->
<?php <?php
if (canView('reports')): if (canView('reports')):
$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(); // Combine overdue counts from inbound and outbound
$overdue_count = 0;
$overdue_count += db()->query("SELECT COUNT(*) FROM inbound_mail WHERE due_date < CURDATE() AND status_id IN (SELECT id FROM mailbox_statuses WHERE name != 'closed')")->fetchColumn();
$overdue_count += db()->query("SELECT COUNT(*) FROM outbound_mail WHERE due_date < CURDATE() AND status_id IN (SELECT id FROM mailbox_statuses WHERE name != 'closed')")->fetchColumn();
if ($overdue_count > 0): if ($overdue_count > 0):
?> ?>
<div class="row mb-4"> <div class="row mb-4">
@ -215,7 +233,7 @@ endif;
<table class="table table-hover align-middle mb-0"> <table class="table table-hover align-middle mb-0">
<tbody> <tbody>
<?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'] ?>&type=<?= $mail['type'] ?>'">
<td class="ps-4" width="120"> <td class="ps-4" width="120">
<small class="text-muted d-block">رقم القيد</small> <small class="text-muted d-block">رقم القيد</small>
<span class="fw-bold text-primary"><?= $mail['ref_no'] ?></span> <span class="fw-bold text-primary"><?= $mail['ref_no'] ?></span>
@ -276,7 +294,7 @@ endif;
</thead> </thead>
<tbody> <tbody>
<?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'] ?>&type=<?= $mail['type'] ?>'">
<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'): ?>

View File

@ -27,14 +27,14 @@ if ($search) {
} }
// Get total for pagination // Get total for pagination
$count_stmt = db()->prepare("SELECT COUNT(*) FROM mailbox m LEFT JOIN users u_sender ON m.created_by = u_sender.id WHERE $where"); $count_stmt = db()->prepare("SELECT COUNT(*) FROM internal_mail m LEFT JOIN users u_sender ON m.created_by = u_sender.id WHERE $where");
$count_stmt->execute($params); $count_stmt->execute($params);
$total_records = $count_stmt->fetchColumn(); $total_records = $count_stmt->fetchColumn();
$total_pages = ceil($total_records / $limit); $total_pages = ceil($total_records / $limit);
// Fetch messages // Fetch messages
$query = "SELECT m.*, u_sender.full_name as sender_name, u_sender.profile_image as sender_image, s.name as status_name, s.color as status_color $query = "SELECT m.*, u_sender.full_name as sender_name, u_sender.profile_image as sender_image, s.name as status_name, s.color as status_color
FROM mailbox m FROM internal_mail m
LEFT JOIN users u_sender ON m.created_by = u_sender.id LEFT JOIN users u_sender ON m.created_by = u_sender.id
LEFT JOIN mailbox_statuses s ON m.status_id = s.id LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE $where WHERE $where

View File

@ -28,8 +28,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
$recipient_email = ''; $recipient_email = '';
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 internal_mail (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([$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
@ -42,7 +42,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
$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 internal_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]]);
} }
} }
@ -117,14 +117,14 @@ if ($search) {
} }
// Get total for pagination // Get total for pagination
$count_stmt = db()->prepare("SELECT COUNT(*) FROM mailbox m LEFT JOIN users u_recp ON m.assigned_to = u_recp.id WHERE $where"); $count_stmt = db()->prepare("SELECT COUNT(*) FROM internal_mail m LEFT JOIN users u_recp ON m.assigned_to = u_recp.id WHERE $where");
$count_stmt->execute($params); $count_stmt->execute($params);
$total_records = $count_stmt->fetchColumn(); $total_records = $count_stmt->fetchColumn();
$total_pages = ceil($total_records / $limit); $total_pages = ceil($total_records / $limit);
// Fetch messages // Fetch messages
$query = "SELECT m.*, u_recp.full_name as recipient_name, u_recp.profile_image as recipient_image, s.name as status_name, s.color as status_color $query = "SELECT m.*, u_recp.full_name as recipient_name, u_recp.profile_image as recipient_image, s.name as status_name, s.color as status_color
FROM mailbox m FROM internal_mail m
LEFT JOIN users u_recp ON m.assigned_to = u_recp.id LEFT JOIN users u_recp ON m.assigned_to = u_recp.id
LEFT JOIN mailbox_statuses s ON m.status_id = s.id LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE $where WHERE $where
@ -296,7 +296,7 @@ function getStatusBadgeInternal($mail) {
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<label class="form-label fw-bold">المرفقات</label> <label class="form-label fw-bold">المرفقات</label>
<input type="file" name="attachments[]" class="form-control border-2" multiple> <input type="file" name="internal_attachments[]" class="form-control border-2" multiple>
</div> </div>
</div> </div>
</div> </div>

View File

@ -86,8 +86,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
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 outbound_mail (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([$$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) {
@ -99,11 +99,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$mail_id = $id; $mail_id = $id;
// Get previous assigned_to to check if it changed // Get previous assigned_to to check if it changed
$stmt_old = db()->prepare("SELECT assigned_to FROM mailbox WHERE id = ?"); $stmt_old = db()->prepare("SELECT assigned_to FROM outbound_mail WHERE id = ?");
$stmt_old->execute([$id]); $stmt_old->execute([$id]);
$old_assigned_to = $stmt_old->fetchColumn(); $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 = db()->prepare("UPDATE outbound_mail SET ref_no = ?, date_registered = ?, due_date = ?, sender = ?, recipient = ?, subject = ?, description = ?, status_id = ?, assigned_to = ? WHERE id = ? ");
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $mail_id]); $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) { if ($assigned_to && $assigned_to != $old_assigned_to) {
@ -123,7 +123,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$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 outbound_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]]);
} }
} }
@ -158,7 +158,7 @@ if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])
$error = 'عذراً، ليس لديك الصلاحية لحذف السجلات'; $error = 'عذراً، ليس لديك الصلاحية لحذف السجلات';
} else { } else {
$id = $_GET['id']; $id = $_GET['id'];
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'outbound'"); $stmt = db()->prepare("DELETE FROM outbound_mail WHERE id = ? ");
$stmt->execute([$id]); $stmt->execute([$id]);
$_SESSION['success'] = 'تم حذف البريد بنجاح'; $_SESSION['success'] = 'تم حذف البريد بنجاح';
redirect('outbound.php'); redirect('outbound.php');
@ -202,7 +202,7 @@ if ($my_tasks) {
$where_sql = implode(" AND ", $where_clauses); $where_sql = implode(" AND ", $where_clauses);
// Get total records for pagination // Get total records for pagination
$count_query = "SELECT COUNT(*) FROM mailbox m WHERE $where_sql"; $count_query = "SELECT COUNT(*) FROM outbound_mail m WHERE $where_sql";
$stmt_count = db()->prepare($count_query); $stmt_count = db()->prepare($count_query);
$stmt_count->execute($params); $stmt_count->execute($params);
$total_records = $stmt_count->fetchColumn(); $total_records = $stmt_count->fetchColumn();
@ -210,7 +210,7 @@ $total_pages = ceil($total_records / $limit);
// Fetch paginated results // Fetch paginated results
$query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name $query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name
FROM mailbox m FROM outbound_mail m
LEFT JOIN mailbox_statuses s ON m.status_id = s.id LEFT JOIN mailbox_statuses s ON m.status_id = s.id
LEFT JOIN users u ON m.assigned_to = u.id LEFT JOIN users u ON m.assigned_to = u.id
WHERE $where_sql WHERE $where_sql
@ -227,7 +227,7 @@ $users_list = db()->query("SELECT id, full_name FROM users ORDER BY full_name")-
$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('outbound')) { if (canEdit('outbound')) {
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'outbound'"); $stmt = db()->prepare("SELECT * FROM outbound_mail WHERE id = ? ");
$stmt->execute([$_GET['id']]); $stmt->execute([$_GET['id']]);
$deepLinkData = $stmt->fetch(); $deepLinkData = $stmt->fetch();
} }
@ -428,7 +428,7 @@ function getStatusBadgeInList($mail) {
</div> </div>
<div class="col-12"> <div class="col-12">
<label class="form-label fw-bold">المرفقات</label> <label class="form-label fw-bold">المرفقات</label>
<input type="file" name="attachments[]" class="form-control" multiple> <input type="file" name="outbound_attachments[]" class="form-control" multiple>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="form-label fw-bold">الحالة</label> <label class="form-label fw-bold">الحالة</label>

View File

@ -8,30 +8,49 @@ if (!canView('reports')) {
$type_filter = $_GET['type'] ?? ''; $type_filter = $_GET['type'] ?? '';
$user_filter = $_GET['user_id'] ?? ''; $user_filter = $_GET['user_id'] ?? '';
$params = []; $overdue_items = [];
$where = ["m.due_date < CURDATE()", "s.name != 'closed'", "m.type != 'internal'"]; $queries = [];
if ($type_filter) { if (!$type_filter || $type_filter === 'inbound') {
$where[] = "m.type = ?"; $where = ["m.due_date < CURDATE()", "s.name != 'closed'"];
$params[] = $type_filter; $params = [];
if ($user_filter) {
$where[] = "m.assigned_to = ?";
$params[] = $user_filter;
}
$where_clause = implode(" AND ", $where);
$sql = "SELECT m.*, 'inbound' as type, u.full_name as assigned_name, s.name as status_name, s.color as status_color
FROM inbound_mail m
LEFT JOIN users u ON m.assigned_to = u.id
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE $where_clause";
$stmt = db()->prepare($sql);
$stmt->execute($params);
$overdue_items = array_merge($overdue_items, $stmt->fetchAll());
} }
if ($user_filter) { if (!$type_filter || $type_filter === 'outbound') {
$where[] = "m.assigned_to = ?"; $where = ["m.due_date < CURDATE()", "s.name != 'closed'"];
$params[] = $user_filter; $params = [];
if ($user_filter) {
$where[] = "m.assigned_to = ?";
$params[] = $user_filter;
}
$where_clause = implode(" AND ", $where);
$sql = "SELECT m.*, 'outbound' as type, u.full_name as assigned_name, s.name as status_name, s.color as status_color
FROM outbound_mail m
LEFT JOIN users u ON m.assigned_to = u.id
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE $where_clause";
$stmt = db()->prepare($sql);
$stmt->execute($params);
$overdue_items = array_merge($overdue_items, $stmt->fetchAll());
} }
$where_clause = implode(" AND ", $where); // Sort by due date
$sql = "SELECT m.*, u.full_name as assigned_name, s.name as status_name, s.color as status_color usort($overdue_items, function($a, $b) {
FROM mailbox m return strtotime($a['due_date']) - strtotime($b['due_date']);
LEFT JOIN users u ON m.assigned_to = u.id });
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
WHERE $where_clause
ORDER BY m.due_date ASC";
$stmt = db()->prepare($sql);
$stmt->execute($params);
$overdue_items = $stmt->fetchAll();
// Fetch all users for filter // Fetch all users for filter
$users = db()->query("SELECT id, full_name FROM users ORDER BY full_name")->fetchAll(); $users = db()->query("SELECT id, full_name FROM users ORDER BY full_name")->fetchAll();
@ -40,7 +59,6 @@ function getStatusBadgeForReport($item) {
$status_name = $item['status_name'] ?? 'غير معروف'; $status_name = $item['status_name'] ?? 'غير معروف';
$status_color = $item['status_color'] ?? '#6c757d'; $status_color = $item['status_color'] ?? '#6c757d';
// Translation for default statuses
$display_name = $status_name; $display_name = $status_name;
if ($status_name == 'received') $display_name = 'تم الاستلام'; if ($status_name == 'received') $display_name = 'تم الاستلام';
if ($status_name == 'in_progress') $display_name = 'قيد المعالجة'; if ($status_name == 'in_progress') $display_name = 'قيد المعالجة';
@ -126,7 +144,7 @@ function getStatusBadgeForReport($item) {
<td class="text-danger fw-bold"><?= $item['due_date'] ?></td> <td class="text-danger fw-bold"><?= $item['due_date'] ?></td>
<td class="text-danger fw-bold"><?= $diff ?> يوم</td> <td class="text-danger fw-bold"><?= $diff ?> يوم</td>
<td> <td>
<a href="view_mail.php?id=<?= $item['id'] ?>" class="btn btn-sm btn-outline-primary"> <a href="view_mail.php?id=<?= $item['id'] ?>&type=<?= $item['type'] ?>" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i> عرض <i class="fas fa-eye"></i> عرض
</a> </a>
</td> </td>

View File

@ -7,73 +7,78 @@ require_once __DIR__ . '/../mail/MailService.php';
echo "[" . date('Y-m-d H:i:s') . "] Starting reminder process..." . PHP_EOL; echo "[" . date('Y-m-d H:i:s') . "] Starting reminder process..." . PHP_EOL;
// 1. Tasks due in 24 hours (exactly 1 day away) $tables = ['inbound_mail', 'outbound_mail']; // internal mail usually doesn't have due dates or reminders the same way
$due_tomorrow = db()->query("
SELECT m.*, u.email, u.full_name
FROM mailbox m
JOIN users u ON m.assigned_to = u.id
JOIN mailbox_statuses s ON m.status_id = s.id
WHERE m.due_date = DATE_ADD(CURDATE(), INTERVAL 1 DAY)
AND s.name != 'closed'
")->fetchAll();
foreach ($due_tomorrow as $task) { foreach ($tables as $table) {
if (!empty($task['email'])) { echo "Processing $table..." . PHP_EOL;
$subject = "تذكير: موعد نهائي لمهمة غداً - " . $task['ref_no'];
$html = " // 1. Tasks due in 24 hours
<div dir='rtl' style='font-family: Arial, sans-serif;'> $stmt = db()->prepare("
<h3>تذكير بموعد نهائي</h3> SELECT m.*, u.email, u.full_name
<p>عزيزي <b>" . htmlspecialchars($task['full_name']) . "</b>،</p> FROM $table m
<p>هذا تذكير بأن المهمة التالية مستحقة غداً:</p> JOIN users u ON m.assigned_to = u.id
<ul> JOIN mailbox_statuses s ON m.status_id = s.id
<li><b>رقم المرجع:</b> " . htmlspecialchars($task['ref_no']) . "</li> WHERE m.due_date = DATE_ADD(CURDATE(), INTERVAL 1 DAY)
<li><b>الموضوع:</b> " . htmlspecialchars($task['subject']) . "</li> AND s.name != 'closed'
<li><b>تاريخ الاستحقاق:</b> " . $task['due_date'] . "</li> ");
</ul> $stmt->execute();
<p>يرجى متابعة المهمة وإغلاقها في الوقت المحدد.</p> $due_tomorrow = $stmt->fetchAll();
</div>
"; foreach ($due_tomorrow as $task) {
$res = MailService::sendMail($task['email'], $subject, $html); if (!empty($task['email'])) {
if ($res['success']) { $subject = "تذكير: موعد نهائي لمهمة غداً - " . $task['ref_no'];
echo "Sent 24h reminder to " . $task['email'] . " for task " . $task['ref_no'] . PHP_EOL; $html = "
} else { <div dir='rtl' style='font-family: Arial, sans-serif;'>
echo "Failed to send 24h reminder to " . $task['email'] . ": " . ($res['error'] ?? 'Unknown error') . PHP_EOL; <h3>تذكير بموعد نهائي</h3>
<p>عزيزي <b>" . htmlspecialchars($task['full_name']) . "</b>،</p>
<p>هذا تذكير بأن المهمة التالية مستحقة غداً:</p>
<ul>
<li><b>رقم المرجع:</b> " . htmlspecialchars($task['ref_no']) . "</li>
<li><b>الموضوع:</b> " . htmlspecialchars($task['subject']) . "</li>
<li><b>تاريخ الاستحقاق:</b> " . $task['due_date'] . "</li>
</ul>
<p>يرجى متابعة المهمة وإغلاقها في الوقت المحدد.</p>
</div>
";
$res = MailService::sendMail($task['email'], $subject, $html);
if ($res['success']) {
echo "Sent 24h reminder to " . $task['email'] . " for task " . $task['ref_no'] . PHP_EOL;
}
} }
} }
}
// 2. Overdue tasks (due date passed and still not closed) - Only send once a week or daily? // 2. Overdue tasks
// For now, let's send daily for overdue tasks to ensure they are handled. $stmt = db()->prepare("
$overdue = db()->query(" SELECT m.*, u.email, u.full_name
SELECT m.*, u.email, u.full_name FROM $table m
FROM mailbox m JOIN users u ON m.assigned_to = u.id
JOIN users u ON m.assigned_to = u.id JOIN mailbox_statuses s ON m.status_id = s.id
JOIN mailbox_statuses s ON m.status_id = s.id WHERE m.due_date < CURDATE()
WHERE m.due_date < CURDATE() AND s.name != 'closed'
AND s.name != 'closed' ");
")->fetchAll(); $stmt->execute();
$overdue = $stmt->fetchAll();
foreach ($overdue as $task) { foreach ($overdue as $task) {
if (!empty($task['email'])) { if (!empty($task['email'])) {
$subject = "تنبيه: مهمة متأخرة! - " . $task['ref_no']; $subject = "تنبيه: مهمة متأخرة! - " . $task['ref_no'];
$html = " $html = "
<div dir='rtl' style='font-family: Arial, sans-serif;'> <div dir='rtl' style='font-family: Arial, sans-serif;'>
<h3 style='color: red;'>تنبيه: مهمة متأخرة</h3> <h3 style='color: red;'>تنبيه: مهمة متأخرة</h3>
<p>عزيزي <b>" . htmlspecialchars($task['full_name']) . "</b>،</p> <p>عزيزي <b>" . htmlspecialchars($task['full_name']) . "</b>،</p>
<p>هذه المهمة قد تجاوزت الموعد النهائي المحدد:</p> <p>هذه المهمة قد تجاوزت الموعد النهائي المحدد:</p>
<ul> <ul>
<li><b>رقم المرجع:</b> " . htmlspecialchars($task['ref_no']) . "</li> <li><b>رقم المرجع:</b> " . htmlspecialchars($task['ref_no']) . "</li>
<li><b>الموضوع:</b> " . htmlspecialchars($task['subject']) . "</li> <li><b>الموضوع:</b> " . htmlspecialchars($task['subject']) . "</li>
<li><b>تاريخ الاستحقاق:</b> <span style='color: red;'>" . $task['due_date'] . "</span></li> <li><b>تاريخ الاستحقاق:</b> <span style='color: red;'>" . $task['due_date'] . "</span></li>
</ul> </ul>
<p>يرجى معالجة هذه المهمة في أقرب وقت ممكن.</p> <p>يرجى معالجة هذه المهمة في أقرب وقت ممكن.</p>
</div> </div>
"; ";
$res = MailService::sendMail($task['email'], $subject, $html); $res = MailService::sendMail($task['email'], $subject, $html);
if ($res['success']) { if ($res['success']) {
echo "Sent overdue reminder to " . $task['email'] . " for task " . $task['ref_no'] . PHP_EOL; echo "Sent overdue reminder to " . $task['email'] . " for task " . $task['ref_no'] . PHP_EOL;
} else { }
echo "Failed to send overdue reminder to " . $task['email'] . ": " . ($res['error'] ?? 'Unknown error') . PHP_EOL;
} }
} }
} }

View File

@ -11,65 +11,69 @@ $user_role = $_SESSION['user_role'];
$is_admin = isAdmin(); $is_admin = isAdmin();
$is_clerk = ($user_role === 'clerk'); $is_clerk = ($user_role === 'clerk');
// Stats for this specific user // Stats for this specific user - Combine from all tables
$stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE assigned_to = ?"); $my_total_assignments = 0;
$stmt->execute([$user_id]); $my_pending_tasks = 0;
$my_total_assignments = $stmt->fetchColumn();
$stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE assigned_to = ? AND status_id IN (SELECT id FROM mailbox_statuses WHERE name != 'closed')"); foreach (['inbound', 'outbound', 'internal'] as $t) {
$stmt->execute([$user_id]); if (canView($t)) {
$my_pending_tasks = $stmt->fetchColumn(); $table = $t . '_mail';
$stmt = db()->prepare("SELECT COUNT(*) FROM $table WHERE assigned_to = ?");
$stmt->execute([$user_id]);
$my_total_assignments += $stmt->fetchColumn();
// Global Stats (for Clerks or if we want to show them) $stmt = db()->prepare("SELECT COUNT(*) FROM $table WHERE assigned_to = ? AND status_id IN (SELECT id FROM mailbox_statuses WHERE name != 'closed')");
$total_inbound = canView('inbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'inbound'")->fetchColumn() : 0; $stmt->execute([$user_id]);
$total_outbound = canView('outbound') ? db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'outbound'")->fetchColumn() : 0; $my_pending_tasks += $stmt->fetchColumn();
}
}
// Global Stats
$total_inbound = canView('inbound') ? db()->query("SELECT COUNT(*) FROM inbound_mail")->fetchColumn() : 0;
$total_outbound = canView('outbound') ? db()->query("SELECT COUNT(*) FROM outbound_mail")->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 = []; $my_assignments = [];
$assignment_types = []; $assignment_queries = [];
if (canView('inbound')) $assignment_types[] = "'inbound'"; if (canView('inbound')) $assignment_queries[] = "SELECT id, 'inbound' as type, ref_no, subject, due_date, status_id, created_at FROM inbound_mail WHERE assigned_to = $user_id";
if (canView('outbound')) $assignment_types[] = "'outbound'"; if (canView('outbound')) $assignment_queries[] = "SELECT id, 'outbound' as type, ref_no, subject, due_date, status_id, created_at FROM outbound_mail WHERE assigned_to = $user_id";
if (canView('internal')) $assignment_types[] = "'internal'"; if (canView('internal')) $assignment_queries[] = "SELECT id, 'internal' as type, ref_no, subject, due_date, status_id, created_at FROM internal_mail WHERE assigned_to = $user_id";
if (!empty($assignment_types)) { if (!empty($assignment_queries)) {
$types_sql = implode(',', $assignment_types); $full_assignment_query = "(" . implode(") UNION ALL (", $assignment_queries) . ") ORDER BY created_at DESC LIMIT 10";
$my_assignments = db()->prepare("SELECT m.*, s.name as status_name, s.color as status_color $stmt = db()->query($full_assignment_query);
FROM mailbox m $my_assignments = $stmt->fetchAll();
LEFT JOIN mailbox_statuses s ON m.status_id = s.id foreach ($my_assignments as &$m) {
WHERE m.assigned_to = ? AND m.type IN ($types_sql) $m['status_name'] = $statuses_data[$m['status_id']]['name'] ?? 'unknown';
ORDER BY m.created_at DESC LIMIT 10"); $m['status_color'] = $statuses_data[$m['status_id']]['color'] ?? '#6c757d';
$my_assignments->execute([$user_id]); }
$my_assignments = $my_assignments->fetchAll();
} }
// Recent Activity // Recent Activity
$recent_activity = []; $recent_activity = [];
$recent_types = []; $recent_queries = [];
if (canView('inbound')) $recent_types[] = "'inbound'"; if (canView('inbound')) $recent_queries[] = "SELECT id, 'inbound' as type, ref_no, subject, status_id, created_by, assigned_to, updated_at FROM inbound_mail";
if (canView('outbound')) $recent_types[] = "'outbound'"; if (canView('outbound')) $recent_queries[] = "SELECT id, 'outbound' as type, ref_no, subject, status_id, created_by, assigned_to, updated_at FROM outbound_mail";
if (canView('internal')) $recent_types[] = "'internal'"; if (canView('internal')) $recent_queries[] = "SELECT id, 'internal' as type, ref_no, subject, status_id, created_by, assigned_to, updated_at FROM internal_mail";
if (!empty($recent_types)) {
$types_sql = implode(',', $recent_types);
$recent_query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name
FROM mailbox m
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
LEFT JOIN users u ON m.assigned_to = u.id
WHERE m.type IN ($types_sql)";
if (!empty($recent_queries)) {
$full_recent_query = "(" . implode(") UNION ALL (", $recent_queries) . ")";
if ($is_admin || $is_clerk) { if ($is_admin || $is_clerk) {
// Admins and Clerks see all recent activity EXCEPT internal mail they are not part of $full_recent_query = "SELECT * FROM ($full_recent_query) AS combined WHERE (type != 'internal' OR assigned_to = $user_id OR created_by = $user_id) ORDER BY updated_at DESC LIMIT 10";
$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 { } else {
// Staff see only theirs $full_recent_query = "SELECT * FROM ($full_recent_query) AS combined WHERE (assigned_to = $user_id OR created_by = $user_id) ORDER BY updated_at DESC LIMIT 10";
$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]);
$stmt = db()->query($full_recent_query);
$recent_activity = $stmt->fetchAll();
foreach ($recent_activity as &$a) {
$a['status_name'] = $statuses_data[$a['status_id']]['name'] ?? 'unknown';
$a['status_color'] = $statuses_data[$a['status_id']]['color'] ?? '#6c757d';
} }
$recent_activity = $recent_stmt->fetchAll();
} }
function getStatusBadge($mail) { function getStatusBadge($mail) {
@ -124,7 +128,6 @@ function getStatusBadge($mail) {
</div> </div>
<div class="row g-4 mb-4"> <div class="row g-4 mb-4">
<!-- Stats for everyone -->
<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"> <div class="d-flex align-items-center">
@ -153,7 +156,6 @@ function getStatusBadge($mail) {
</div> </div>
<?php if ($is_admin || $is_clerk): ?> <?php if ($is_admin || $is_clerk): ?>
<!-- Admin/Clerk specific stats -->
<?php if (canView('inbound')): ?> <?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">
@ -185,7 +187,6 @@ function getStatusBadge($mail) {
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php else: ?> <?php else: ?>
<!-- Staff 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">
@ -195,9 +196,9 @@ function getStatusBadge($mail) {
<div> <div>
<h6 class="text-muted mb-1">وارد من قبلي</h6> <h6 class="text-muted mb-1">وارد من قبلي</h6>
<?php <?php
$stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE created_by = ? AND type = 'inbound'"); $my_in_count = db()->prepare("SELECT COUNT(*) FROM inbound_mail WHERE created_by = ?");
$stmt->execute([$user_id]); $my_in_count->execute([$user_id]);
$my_in_count = $stmt->fetchColumn(); $my_in_count = $my_in_count->fetchColumn();
?> ?>
<h3 class="fw-bold mb-0"><?= $my_in_count ?></h3> <h3 class="fw-bold mb-0"><?= $my_in_count ?></h3>
</div> </div>
@ -213,9 +214,9 @@ function getStatusBadge($mail) {
<div> <div>
<h6 class="text-muted mb-1">صادر من قبلي</h6> <h6 class="text-muted mb-1">صادر من قبلي</h6>
<?php <?php
$stmt = db()->prepare("SELECT COUNT(*) FROM mailbox WHERE created_by = ? AND type = 'outbound'"); $my_out_count = db()->prepare("SELECT COUNT(*) FROM outbound_mail WHERE created_by = ?");
$stmt->execute([$user_id]); $my_out_count->execute([$user_id]);
$my_out_count = $stmt->fetchColumn(); $my_out_count = $my_out_count->fetchColumn();
?> ?>
<h3 class="fw-bold mb-0"><?= $my_out_count ?></h3> <h3 class="fw-bold mb-0"><?= $my_out_count ?></h3>
</div> </div>
@ -226,7 +227,6 @@ function getStatusBadge($mail) {
</div> </div>
<div class="row"> <div class="row">
<!-- Assignments Table -->
<div class="col-lg-8"> <div class="col-lg-8">
<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 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">
@ -236,7 +236,7 @@ function getStatusBadge($mail) {
<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 endif; ?>
<?php if (canAdd('outbound')): ?> <?php if (canAdd('outbound')): ?>
<a href="outbound.php" class="btn btn-sm btn-outline-success">إضافة صادر</a> <a href="outbound.php?action=add" class="btn btn-sm btn-outline-success">إضافة صادر</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
@ -255,7 +255,7 @@ function getStatusBadge($mail) {
<tbody> <tbody>
<?php if (!empty($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'] ?>&type=<?= $mail['type'] ?>'">
<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><?= htmlspecialchars($mail['subject']) ?></td> <td><?= htmlspecialchars($mail['subject']) ?></td>
<td> <td>
@ -269,14 +269,13 @@ function getStatusBadge($mail) {
</td> </td>
<td><?= getStatusBadge($mail) ?></td> <td><?= getStatusBadge($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-light rounded-pill px-3">عرض</a> <a href="view_mail.php?id=<?= $mail['id'] ?>&type=<?= $mail['type'] ?>" class="btn btn-sm btn-light rounded-pill px-3">عرض</a>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php else: ?> <?php else: ?>
<tr> <tr>
<td colspan="5" class="text-center py-5 text-muted"> <td colspan="5" class="text-center py-5 text-muted">
<i class="fas fa-check-double fa-3x mb-3 d-block text-success opacity-25"></i>
أنت على اطلاع بكافة مهامك! لا توجد مهام معلقة. أنت على اطلاع بكافة مهامك! لا توجد مهام معلقة.
</td> </td>
</tr> </tr>
@ -288,7 +287,6 @@ function getStatusBadge($mail) {
</div> </div>
</div> </div>
<!-- Recent Activity Sidebar -->
<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">
@ -298,7 +296,7 @@ function getStatusBadge($mail) {
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
<?php if (!empty($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'] ?>&type=<?= $act['type'] ?>" 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">
<h6 class="mb-1 fw-bold text-truncate" title="<?= htmlspecialchars($act['subject']) ?>"><?= htmlspecialchars($act['subject']) ?></h6> <h6 class="mb-1 fw-bold text-truncate" title="<?= htmlspecialchars($act['subject']) ?>"><?= htmlspecialchars($act['subject']) ?></h6>
<small class="text-muted"><?= date('m-d', strtotime($act['updated_at'])) ?></small> <small class="text-muted"><?= date('m-d', strtotime($act['updated_at'])) ?></small>
@ -319,13 +317,8 @@ function getStatusBadge($mail) {
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
<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>
<?php endif; ?>
</div>
</div> </div>
</div> </div>
</div> </div>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -3,11 +3,32 @@ require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/mail/MailService.php'; require_once __DIR__ . '/mail/MailService.php';
$id = $_GET['id'] ?? 0; $id = $_GET['id'] ?? 0;
$type = $_GET['type'] ?? '';
if (!$id) redirect('index.php'); if (!$id) redirect('index.php');
// If type is not provided, try to find it in any of the tables (for backward compatibility if any links were missed)
if (!$type) {
foreach (['inbound', 'outbound', 'internal'] as $t) {
$table = $t . '_mail';
$check = db()->prepare("SELECT id FROM $table WHERE id = ?");
$check->execute([$id]);
if ($check->fetch()) {
$type = $t;
break;
}
}
}
if (!$type) redirect('index.php');
$table_mail = $type . '_mail';
$table_attachments = $type . '_attachments';
$table_comments = $type . '_comments';
$stmt = db()->prepare("SELECT m.*, u1.full_name as assigned_name, u2.full_name as creator_name, $stmt = db()->prepare("SELECT m.*, u1.full_name as assigned_name, u2.full_name as creator_name,
s.name as status_name, s.color as status_color s.name as status_name, s.color as status_color
FROM mailbox m FROM $table_mail m
LEFT JOIN users u1 ON m.assigned_to = u1.id LEFT JOIN users u1 ON m.assigned_to = u1.id
LEFT JOIN users u2 ON m.created_by = u2.id LEFT JOIN users u2 ON m.created_by = u2.id
LEFT JOIN mailbox_statuses s ON m.status_id = s.id LEFT JOIN mailbox_statuses s ON m.status_id = s.id
@ -17,14 +38,16 @@ $mail = $stmt->fetch();
if (!$mail) redirect('index.php'); if (!$mail) redirect('index.php');
// Add back the type for logic below
$mail['type'] = $type;
// Check if user has view permission for this mail type // Check if user has view permission for this mail type
if (!canView($mail['type'])) { if (!canView($type)) {
redirect('index.php'); redirect('index.php');
} }
// Security check for internal mail: only sender or recipient can view // Security check for internal mail: only sender or recipient can view
// Even admins should only see their own internal mail for privacy if ($type === 'internal') {
if ($mail['type'] === 'internal') {
if ($mail['created_by'] != $_SESSION['user_id'] && $mail['assigned_to'] != $_SESSION['user_id']) { if ($mail['created_by'] != $_SESSION['user_id'] && $mail['assigned_to'] != $_SESSION['user_id']) {
redirect('internal_inbox.php'); redirect('internal_inbox.php');
} }
@ -35,15 +58,14 @@ $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'])) {
// For internal mail, users can always comment if involved. For others, check edit permission. if ($type !== 'internal' && !canEdit($type)) {
if ($mail['type'] !== 'internal' && !canEdit($mail['type'])) {
$error = 'عذراً، ليس لديك الصلاحية لإضافة تعليقات'; $error = 'عذراً، ليس لديك الصلاحية لإضافة تعليقات';
} else { } else {
$comment = $_POST['comment'] ?? ''; $comment = $_POST['comment'] ?? '';
$referred_user_id = $_POST['referred_user_id'] ?: null; $referred_user_id = $_POST['referred_user_id'] ?: null;
if ($comment) { if ($comment) {
$stmt = db()->prepare("INSERT INTO comments (mail_id, user_id, comment, referred_user_id) VALUES (?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO $table_comments (mail_id, user_id, comment, referred_user_id) VALUES (?, ?, ?, ?)");
$stmt->execute([$id, $_SESSION['user_id'], $comment, $referred_user_id]); $stmt->execute([$id, $_SESSION['user_id'], $comment, $referred_user_id]);
// Send email notification if referred // Send email notification if referred
@ -55,7 +77,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) {
if ($referred_user && !empty($referred_user['email'])) { if ($referred_user && !empty($referred_user['email'])) {
$sender_name = $_SESSION['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 . "&type=" . $type;
$html = " $html = "
<div dir='rtl'> <div dir='rtl'>
@ -80,14 +102,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) {
} }
$_SESSION['success'] = 'تم إضافة التعليق بنجاح'; $_SESSION['success'] = 'تم إضافة التعليق بنجاح';
redirect("view_mail.php?id=$id"); redirect("view_mail.php?id=$id&type=$type");
} }
} }
} }
// Handle Attachment upload // Handle Attachment upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) {
if ($mail['type'] !== 'internal' && !canEdit($mail['type'])) { if ($type !== 'internal' && !canEdit($type)) {
$error = 'عذراً، ليس لديك الصلاحية لرفع مرفقات'; $error = 'عذراً، ليس لديك الصلاحية لرفع مرفقات';
} else { } else {
$file = $_FILES['attachment']; $file = $_FILES['attachment'];
@ -100,10 +122,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) {
$target_path = $upload_dir . $file_name; $target_path = $upload_dir . $file_name;
if (move_uploaded_file($file['tmp_name'], $target_path)) { if (move_uploaded_file($file['tmp_name'], $target_path)) {
$stmt = db()->prepare("INSERT INTO attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)"); $stmt = db()->prepare("INSERT INTO $table_attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$id, $display_name, $target_path, $file['name'], $file['size']]); $stmt->execute([$id, $display_name, $target_path, $file['name'], $file['size']]);
$_SESSION['success'] = 'تم رفع الملف بنجاح'; $_SESSION['success'] = 'تم رفع الملف بنجاح';
redirect("view_mail.php?id=$id"); redirect("view_mail.php?id=$id&type=$type");
} else { } else {
$error = 'فشل في رفع الملف'; $error = 'فشل في رفع الملف';
} }
@ -113,26 +135,23 @@ 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 ($mail['type'] !== 'internal' && !canDelete($mail['type'])) { if ($type !== 'internal' && !canDelete($type)) {
$error = 'عذراً، ليس لديك الصلاحية لحذف المرفقات'; $error = 'عذراً، ليس لديك الصلاحية لحذف المرفقات';
} else { } else {
$attachment_id = $_POST['attachment_id'] ?? 0; $attachment_id = $_POST['attachment_id'] ?? 0;
if ($attachment_id) { if ($attachment_id) {
$stmt = db()->prepare("SELECT * FROM attachments WHERE id = ?"); $stmt = db()->prepare("SELECT * FROM $table_attachments WHERE id = ?");
$stmt->execute([$attachment_id]); $stmt->execute([$attachment_id]);
$attachment = $stmt->fetch(); $attachment = $stmt->fetch();
if ($attachment) { if ($attachment) {
// Delete file from disk
if (file_exists($attachment['file_path'])) { if (file_exists($attachment['file_path'])) {
unlink($attachment['file_path']); unlink($attachment['file_path']);
} }
$stmt = db()->prepare("DELETE FROM $table_attachments WHERE id = ?");
// Delete record from DB
$stmt = db()->prepare("DELETE FROM attachments WHERE id = ?");
$stmt->execute([$attachment_id]); $stmt->execute([$attachment_id]);
$_SESSION['success'] = 'تم حذف المرفق بنجاح'; $_SESSION['success'] = 'تم حذف المرفق بنجاح';
redirect("view_mail.php?id=$id"); redirect("view_mail.php?id=$id&type=$type");
} }
} }
} }
@ -148,35 +167,34 @@ if (isset($_SESSION['error'])) {
unset($_SESSION['error']); unset($_SESSION['error']);
} }
$comments = db()->prepare("SELECT c.*, u.full_name, ru.full_name as referred_name $comments_stmt = db()->prepare("SELECT c.*, u.full_name, ru.full_name as referred_name
FROM comments c FROM $table_comments c
LEFT JOIN users u ON c.user_id = u.id LEFT JOIN users u ON c.user_id = u.id
LEFT JOIN users ru ON c.referred_user_id = ru.id LEFT JOIN users ru ON c.referred_user_id = ru.id
WHERE c.mail_id = ? ORDER BY c.created_at DESC"); WHERE c.mail_id = ? ORDER BY c.created_at DESC");
$comments->execute([$id]); $comments_stmt->execute([$id]);
$mail_comments = $comments->fetchAll(); $mail_comments = $comments_stmt->fetchAll();
$attachments = db()->prepare("SELECT * FROM attachments WHERE mail_id = ? ORDER BY created_at DESC"); $attachments_stmt = db()->prepare("SELECT * FROM $table_attachments WHERE mail_id = ? ORDER BY created_at DESC");
$attachments->execute([$id]); $attachments_stmt->execute([$id]);
$mail_attachments = $attachments->fetchAll(); $mail_attachments = $attachments_stmt->fetchAll();
// Fetch all users for referral dropdown (excluding current user) // Fetch all users for referral dropdown
$stmt_users = db()->prepare("SELECT id, full_name, role FROM users WHERE id != ? ORDER BY full_name ASC"); $stmt_users = db()->prepare("SELECT id, full_name, role FROM users WHERE id != ? ORDER BY full_name ASC");
$stmt_users->execute([$_SESSION['user_id']]); $stmt_users->execute([$_SESSION['user_id']]);
$all_users = $stmt_users->fetchAll(); $all_users = $stmt_users->fetchAll();
// Helper to check previewable files
function isPreviewable($fileName) { function isPreviewable($fileName) {
$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
return in_array($ext, ['pdf', 'png', 'jpg', 'jpeg', 'gif', 'webp']); return in_array($ext, ['pdf', 'png', 'jpg', 'jpeg', 'gif', 'webp']);
} }
$type_label = 'بريد وارد'; $type_label = 'بريد وارد';
if ($mail['type'] == 'outbound') $type_label = 'بريد صادر'; if ($type == 'outbound') $type_label = 'بريد صادر';
if ($mail['type'] == 'internal') $type_label = 'رسالة داخلية'; if ($type == 'internal') $type_label = 'رسالة داخلية';
$back_link = $mail['type'] . '.php'; $back_link = $type . '.php';
if ($mail['type'] == 'internal') { if ($type == 'internal') {
$back_link = ($mail['created_by'] == $_SESSION['user_id']) ? 'internal_outbox.php' : 'internal_inbox.php'; $back_link = ($mail['created_by'] == $_SESSION['user_id']) ? 'internal_outbox.php' : 'internal_inbox.php';
} }
?> ?>
@ -185,8 +203,8 @@ 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 ($mail['type'] !== 'internal' && canEdit($mail['type'])): ?> <?php if ($type !== 'internal' && canEdit($type)): ?>
<a href="<?= $mail['type'] ?>.php?action=edit&id=<?= $mail['id'] ?>" class="btn btn-outline-primary">تعديل البيانات</a> <a href="<?= $type ?>.php?action=edit&id=<?= $mail['id'] ?>" class="btn btn-outline-primary">تعديل البيانات</a>
<?php endif; ?> <?php endif; ?>
</div> </div>
</div> </div>
@ -244,9 +262,9 @@ if ($mail['type'] == 'internal') {
$s_name = $mail['status_name'] ?? 'received'; $s_name = $mail['status_name'] ?? 'received';
$s_color = $mail['status_color'] ?? '#6c757d'; $s_color = $mail['status_color'] ?? '#6c757d';
$d_name = $s_name; $d_name = $s_name;
if ($s_name == 'received') $d_name = ($mail['type'] == 'internal' ? 'جديد / مرسل' : 'تم الاستلام'); if ($s_name == 'received') $d_name = ($type == 'internal' ? 'جديد / مرسل' : 'تم الاستلام');
if ($s_name == 'in_progress') $d_name = 'قيد المعالجة'; if ($s_name == 'in_progress') $d_name = 'قيد المعالجة';
if ($s_name == 'closed') $d_name = ($mail['type'] == 'internal' ? 'مؤرشف' : 'مكتمل'); if ($s_name == 'closed') $d_name = ($type == 'internal' ? 'مؤرشف' : 'مكتمل');
?> ?>
<span class="badge" style="background-color: <?= $s_color ?>;"><?= htmlspecialchars($d_name) ?></span> <span class="badge" style="background-color: <?= $s_color ?>;"><?= htmlspecialchars($d_name) ?></span>
</p> </p>
@ -255,7 +273,7 @@ if ($mail['type'] == 'internal') {
<label class="text-muted small">الموضوع</label> <label class="text-muted small">الموضوع</label>
<div class="fw-bold"> <div class="fw-bold">
<?php <?php
if ($mail['type'] == 'outbound' || $mail['type'] == 'internal') { if ($type == 'outbound' || $type == 'internal') {
echo $mail['subject']; echo $mail['subject'];
} else { } else {
echo htmlspecialchars($mail['subject']); echo htmlspecialchars($mail['subject']);
@ -264,7 +282,7 @@ if ($mail['type'] == 'internal') {
</div> </div>
</div> </div>
<?php if ($mail['type'] == 'internal'): ?> <?php if ($type == 'internal'): ?>
<div class="col-md-6"> <div class="col-md-6">
<label class="text-muted small">المرسل</label> <label class="text-muted small">المرسل</label>
<p class="fw-bold text-primary"><?= htmlspecialchars($mail['creator_name']) ?></p> <p class="fw-bold text-primary"><?= htmlspecialchars($mail['creator_name']) ?></p>
@ -275,11 +293,11 @@ if ($mail['type'] == 'internal') {
</div> </div>
<?php else: ?> <?php else: ?>
<div class="col-md-6"> <div class="col-md-6">
<label class="text-muted small"><?= $mail['type'] == 'inbound' ? 'المرسل' : 'المرسل الداخلي' ?></label> <label class="text-muted small"><?= $type == 'inbound' ? 'المرسل' : 'المرسل الداخلي' ?></label>
<p><?= htmlspecialchars($mail['sender'] ?: 'غير محدد') ?></p> <p><?= htmlspecialchars($mail['sender'] ?: 'غير محدد') ?></p>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label class="text-muted small"><?= $mail['type'] == 'inbound' ? 'المستلم الداخلي' : 'الجهة المستلمة' ?></label> <label class="text-muted small"><?= $type == 'inbound' ? 'المستلم الداخلي' : 'الجهة المستلمة' ?></label>
<p><?= htmlspecialchars($mail['recipient'] ?: 'غير محدد') ?></p> <p><?= htmlspecialchars($mail['recipient'] ?: 'غير محدد') ?></p>
</div> </div>
<?php endif; ?> <?php endif; ?>
@ -288,7 +306,7 @@ if ($mail['type'] == 'internal') {
<label class="text-muted small">الرسالة / الوصف</label> <label class="text-muted small">الرسالة / الوصف</label>
<div class="bg-light p-3 rounded border"> <div class="bg-light p-3 rounded border">
<?php <?php
if ($mail['type'] == 'outbound' || $mail['type'] == 'internal') { if ($type == 'outbound' || $type == 'internal') {
echo $mail['description'] ?: '<span class="text-muted">لا يوجد محتوى إضافي</span>'; echo $mail['description'] ?: '<span class="text-muted">لا يوجد محتوى إضافي</span>';
} else { } else {
echo nl2br(htmlspecialchars($mail['description'] ?: 'لا يوجد محتوى إضافي')); echo nl2br(htmlspecialchars($mail['description'] ?: 'لا يوجد محتوى إضافي'));
@ -297,14 +315,14 @@ if ($mail['type'] == 'internal') {
</div> </div>
</div> </div>
<?php if ($mail['type'] != 'internal'): ?> <?php if ($type != 'internal'): ?>
<div class="col-md-6"> <div class="col-md-6">
<label class="text-muted small">الموظف المسؤول</label> <label class="text-muted small">الموظف المسؤول</label>
<p><?= htmlspecialchars($mail['assigned_name'] ?: 'غير معين') ?></p> <p><?= htmlspecialchars($mail['assigned_name'] ?: 'غير معين') ?></p>
</div> </div>
<?php endif; ?> <?php endif; ?>
<div class="col-md-6 <?= $mail['type'] == 'internal' ? 'col-md-12' : '' ?> text-end"> <div class="col-md-6 <?= $type == 'internal' ? 'col-md-12' : '' ?> text-end">
<label class="text-muted small">تاريخ الإنشاء</label> <label class="text-muted small">تاريخ الإنشاء</label>
<p class="text-muted small"><?= $mail['created_at'] ?></p> <p class="text-muted small"><?= $mail['created_at'] ?></p>
</div> </div>
@ -318,13 +336,13 @@ 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'])): ?> <?php if ($type === 'internal' || canEdit($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">إضافة <?= $type == 'internal' ? 'رد' : 'تعليق' ?></label>
<textarea name="comment" class="form-control" rows="2" placeholder="اكتب ردك هنا..." required></textarea> <textarea name="comment" class="form-control" rows="2" placeholder="اكتب ردك هنا..." required></textarea>
</div> </div>
<?php if ($mail['type'] != 'internal'): ?> <?php if ($type != 'internal'): ?>
<div class="mb-3"> <div class="mb-3">
<label class="form-label small fw-bold">إحالة إلى موظف (اختياري)</label> <label class="form-label small fw-bold">إحالة إلى موظف (اختياري)</label>
<select name="referred_user_id" class="form-select form-select-sm"> <select name="referred_user_id" class="form-select form-select-sm">
@ -333,10 +351,9 @@ if ($mail['type'] == 'internal') {
<option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['full_name']) ?> (<?= ucfirst($u['role']) ?>)</option> <option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['full_name']) ?> (<?= ucfirst($u['role']) ?>)</option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<div class="form-text small">سيتم إرسال تنبيه عبر البريد الإلكتروني للموظف المحال إليه.</div>
</div> </div>
<?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">إرسال <?= $type == 'internal' ? 'الرد' : 'التعليق' ?></button>
</form> </form>
<?php endif; ?> <?php endif; ?>
@ -371,7 +388,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'])): ?> <?php if ($type === 'internal' || canEdit($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>
@ -404,7 +421,7 @@ if ($mail['type'] == 'internal') {
</button> </button>
<?php endif; ?> <?php endif; ?>
<?php if ($mail['type'] == 'internal' || canDelete($mail['type'])): ?> <?php if ($type == 'internal' || canDelete($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">
@ -434,13 +451,11 @@ if ($mail['type'] == 'internal') {
<button type="button" class="btn-close ms-0 me-auto" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close ms-0 me-auto" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body p-0 bg-dark d-flex align-items-center justify-content-center" style="min-height: 80vh;"> <div class="modal-body p-0 bg-dark d-flex align-items-center justify-content-center" style="min-height: 80vh;">
<div id="previewContainer" class="w-100 h-100 text-center"> <div id="previewContainer" class="w-100 h-100 text-center"></div>
<!-- Preview content will be loaded here -->
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a id="downloadBtn" href="#" class="btn btn-primary" download>تحميل الملف</a> <a id="downloadBtn" href="#" class="btn btn-primary" download>تحميل الملف</a>
<button type="button" class="btn btn-secondary" data-bs-modal="modal">إغلاق</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إغلاق</button>
</div> </div>
</div> </div>
</div> </div>
@ -482,32 +497,14 @@ if ($mail['type'] == 'internal') {
previewContainer.innerHTML = ''; previewContainer.innerHTML = '';
}); });
// Handle Delete Confirmation
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');
if (typeof Swal !== 'undefined') { if (confirm('هل أنت متأكد من الحذف؟')) {
Swal.fire({ form.submit();
title: 'هل أنت متأكد؟',
text: "سيتم حذف المرفق نهائياً!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'نعم، احذف',
cancelButtonText: 'إلغاء'
}).then((result) => {
if (result.isConfirmed) {
form.submit();
}
});
} else {
if (confirm('هل أنت متأكد من الحذف؟')) {
form.submit();
}
} }
}); });
}); });
</script> </script>
<?php require_once __DIR__ . '/includes/footer.php'; ?> <?php require_once __DIR__ . '/includes/footer.php'; ?>