diff --git a/controllers/TicketController.php b/controllers/TicketController.php index 2e2c0d4..5b3adb0 100644 --- a/controllers/TicketController.php +++ b/controllers/TicketController.php @@ -93,19 +93,27 @@ class TicketController $visibilityGroups = implode(',', array_map('trim', $_POST['visibility_groups'])); } + // Honor the posted status, validated against the app's canonical list + $validStatuses = $GLOBALS['config']['TICKET_STATUSES'] ?? ['Open', 'Pending', 'In Progress', 'Closed']; + $status = $_POST['status'] ?? 'Open'; + if (!in_array($status, $validStatuses, true)) { + $status = 'Open'; + } + $ticketData = [ - 'title' => $_POST['title'] ?? '', + 'title' => trim($_POST['title'] ?? ''), 'description' => $_POST['description'] ?? '', 'priority' => $_POST['priority'] ?? '4', 'category' => $_POST['category'] ?? 'General', 'type' => $_POST['type'] ?? 'Issue', + 'status' => $status, 'visibility' => $_POST['visibility'] ?? 'public', 'visibility_groups' => $visibilityGroups, 'assigned_to' => !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null ]; - // Validate input - if (empty($ticketData['title'])) { + // Validate input (server-side; form is novalidate) + if ($ticketData['title'] === '') { $error = "Title is required"; $templates = $this->templateModel->getAllTemplates(); $allUsers = $this->userModel->getAllUsers(); @@ -114,6 +122,15 @@ class TicketController return; } + if (trim($ticketData['description']) === '') { + $error = "Description is required"; + $templates = $this->templateModel->getAllTemplates(); + $allUsers = $this->userModel->getAllUsers(); + $conn = $this->conn; // Make $conn available to view + include dirname(__DIR__) . '/views/CreateTicketView.php'; + return; + } + // Create ticket with user tracking $result = $this->ticketModel->createTicket($ticketData, $userId); diff --git a/index.php b/index.php index 602f671..f9732ed 100644 --- a/index.php +++ b/index.php @@ -249,8 +249,11 @@ switch (true) { $params = []; $types = ''; - $allowedActionTypes = ['create','update','delete','comment','assign','status_change','login','security', - 'ticket_create','ticket_update','ticket_delete','attachment_delete','attachment_upload']; + // Mirrors AuditLogModel::VALID_ACTION_TYPES so every option offered by the + // audit-log filter dropdown is actually accepted here. + $allowedActionTypes = ['create','update','delete','view','security_event', + 'login','logout','assign','unassign','comment','mention', + 'revoke','attachment_upload','attachment_delete','bulk_update']; if (!empty($_GET['action_type']) && in_array($_GET['action_type'], $allowedActionTypes, true)) { $whereConditions[] = "al.action_type = ?"; $params[] = $_GET['action_type']; @@ -335,9 +338,12 @@ switch (true) { case $requestPath == '/admin/user-activity': requireAdmin($currentUser); + // Validate date params (YYYY-MM-DD) like the audit-log route; fall back to defaults on garbage + $uaFrom = $_GET['date_from'] ?? ''; + $uaTo = $_GET['date_to'] ?? ''; $dateRange = [ - 'from' => $_GET['date_from'] ?? date('Y-m-d', strtotime('-30 days')), - 'to' => $_GET['date_to'] ?? date('Y-m-d') + 'from' => preg_match('/^\d{4}-\d{2}-\d{2}$/', $uaFrom) ? $uaFrom : date('Y-m-d', strtotime('-30 days')), + 'to' => preg_match('/^\d{4}-\d{2}-\d{2}$/', $uaTo) ? $uaTo : date('Y-m-d') ]; // Optimized query using LEFT JOINs with aggregated subqueries instead of correlated subqueries @@ -410,7 +416,7 @@ switch (true) { header("Location: /"); exit; - case preg_match('/^\/ticket\.php/', $requestPath) && isset($_GET['id']): + case preg_match('/^\/ticket\.php$/', $requestPath) && isset($_GET['id']): $legacyId = (string)$_GET['id']; if (ctype_digit($legacyId) && (int)$legacyId > 0) { header("Location: /ticket/" . $legacyId); diff --git a/views/DashboardView.php b/views/DashboardView.php index 4384d98..af6895d 100644 --- a/views/DashboardView.php +++ b/views/DashboardView.php @@ -1317,7 +1317,7 @@ if (advForm) advForm.addEventListener('submit', function(e) { var pLabels = { '1':'P1 — Critical', '2':'P2 — High', '3':'P3 — Medium', '4':'P4 — Low', '5':'P5 — Minimal' }; var dotClass = { 'Open':'lt-dot-up', 'In Progress':'lt-dot-warn', 'Pending':'lt-dot--orange', 'Closed':'lt-dot-idle' }; - function esc(s) { return String(s||'').replace(/&/g,'&').replace(//g,'>'); } + function esc(s) { return String(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,'''); } function fmtAge(dateStr) { var d = new Date(dateStr); diff --git a/views/TicketView.php b/views/TicketView.php index 635c5f9..767ffc0 100644 --- a/views/TicketView.php +++ b/views/TicketView.php @@ -461,8 +461,8 @@ include __DIR__ . '/layout_header.php';