Fix views/controllers/router: command palette, create form, admin views

- Consolidate the duplicated command palette to a single overlay + init in
  the footer; fix New Ticket to route to /ticket/create (was a 404 /create);
  keep the CSP nonce and all commands
- TicketController create(): trim title, require a non-empty description,
  and honor the posted status (validated against the canonical list) instead
  of silently discarding it
- UserActivityView: 'Active Users' counts only users active in the selected
  range, not every registered user
- layout_footer/DashboardView: local esc() now escapes quotes so values used
  in HTML attributes can't break out
- TicketView: comments tab badge shows the true total, not just page one
- layout_header: gate the 'View activity log' link behind the admin flag
- index.php: validate /admin/user-activity date params; anchor the legacy
  /ticket.php route; align the audit action-type whitelist with the dropdown
- ApiKeysView: correct the external API sample to /create_ticket_api.php

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 15:15:40 -04:00
co-authored by Claude Opus 4.8
parent 113b7f9d3f
commit 27a5db8c85
9 changed files with 76 additions and 79 deletions
+11 -5
View File
@@ -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);