Files
tinker_tickets/index.php
Jared Vititoe c32e9c871b feat: Add timezone setting in preferences + clickable logo
- Add timezone dropdown to settings modal with common timezones
- Save/load timezone preference per user
- Apply user's timezone preference after authentication
- Override system default with user preference if set
- Make dashboard logo clickable (returns to default filters)
- Show current timezone in settings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 21:54:04 -05:00

283 lines
10 KiB
PHP

<?php
// Main entry point for the application
require_once 'config/config.php';
require_once 'middleware/SecurityHeadersMiddleware.php';
require_once 'middleware/AuthMiddleware.php';
require_once 'models/AuditLogModel.php';
// Apply security headers early
SecurityHeadersMiddleware::apply();
// Parse the URL - no need to remove base path since we're at document root
$request = $_SERVER['REQUEST_URI'];
// Remove query string for routing (but keep it available)
$requestPath = strtok($request, '?');
// Create database connection for non-API routes
if (!str_starts_with($requestPath, '/api/')) {
$conn = new mysqli(
$GLOBALS['config']['DB_HOST'],
$GLOBALS['config']['DB_USER'],
$GLOBALS['config']['DB_PASS'],
$GLOBALS['config']['DB_NAME']
);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Authenticate user via Authelia forward auth
$authMiddleware = new AuthMiddleware($conn);
$currentUser = $authMiddleware->authenticate();
// Store current user in globals for controllers
$GLOBALS['currentUser'] = $currentUser;
// Initialize audit log model
$GLOBALS['auditLog'] = new AuditLogModel($conn);
// Check if user has a timezone preference and apply it
if ($currentUser && isset($currentUser['user_id'])) {
require_once 'models/UserPreferencesModel.php';
$prefsModel = new UserPreferencesModel($conn);
$userTimezone = $prefsModel->getPreference($currentUser['user_id'], 'timezone', null);
if ($userTimezone) {
// Override system timezone with user preference
date_default_timezone_set($userTimezone);
$GLOBALS['config']['TIMEZONE'] = $userTimezone;
$now = new DateTime('now', new DateTimeZone($userTimezone));
$GLOBALS['config']['TIMEZONE_OFFSET'] = $now->getOffset() / 60;
$GLOBALS['config']['TIMEZONE_ABBREV'] = $now->format('T');
}
}
}
// Simple router
switch (true) {
case $requestPath == '/' || $requestPath == '':
require_once 'controllers/DashboardController.php';
$controller = new DashboardController($conn);
$controller->index();
break;
case preg_match('/^\/ticket\/(\d+)$/', $requestPath, $matches):
require_once 'controllers/TicketController.php';
$controller = new TicketController($conn);
$controller->view($matches[1]);
break;
case $requestPath == '/ticket/create':
require_once 'controllers/TicketController.php';
$controller = new TicketController($conn);
$controller->create();
break;
// API Routes - these handle their own database connections
case $requestPath == '/api/update_ticket.php':
require_once 'api/update_ticket.php';
break;
case $requestPath == '/api/add_comment.php':
require_once 'api/add_comment.php';
break;
// Admin Routes - require admin privileges
case $requestPath == '/admin/recurring-tickets':
if (!$currentUser || !$currentUser['is_admin']) {
header("HTTP/1.0 403 Forbidden");
echo 'Admin access required';
break;
}
require_once 'models/RecurringTicketModel.php';
$recurringModel = new RecurringTicketModel($conn);
$recurringTickets = $recurringModel->getAll(true);
include 'views/admin/RecurringTicketsView.php';
break;
case $requestPath == '/admin/custom-fields':
if (!$currentUser || !$currentUser['is_admin']) {
header("HTTP/1.0 403 Forbidden");
echo 'Admin access required';
break;
}
require_once 'models/CustomFieldModel.php';
$fieldModel = new CustomFieldModel($conn);
$customFields = $fieldModel->getAllDefinitions(null, false);
include 'views/admin/CustomFieldsView.php';
break;
case $requestPath == '/admin/workflow':
if (!$currentUser || !$currentUser['is_admin']) {
header("HTTP/1.0 403 Forbidden");
echo 'Admin access required';
break;
}
$result = $conn->query("SELECT * FROM status_transitions ORDER BY from_status, to_status");
$workflows = [];
while ($row = $result->fetch_assoc()) {
$workflows[] = $row;
}
include 'views/admin/WorkflowDesignerView.php';
break;
case $requestPath == '/admin/templates':
if (!$currentUser || !$currentUser['is_admin']) {
header("HTTP/1.0 403 Forbidden");
echo 'Admin access required';
break;
}
$result = $conn->query("SELECT * FROM ticket_templates ORDER BY template_name");
$templates = [];
while ($row = $result->fetch_assoc()) {
$templates[] = $row;
}
include 'views/admin/TemplatesView.php';
break;
case $requestPath == '/admin/audit-log':
if (!$currentUser || !$currentUser['is_admin']) {
header("HTTP/1.0 403 Forbidden");
echo 'Admin access required';
break;
}
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$perPage = 50;
$offset = ($page - 1) * $perPage;
$filters = [];
$whereConditions = [];
$params = [];
$types = '';
if (!empty($_GET['action_type'])) {
$whereConditions[] = "al.action_type = ?";
$params[] = $_GET['action_type'];
$types .= 's';
$filters['action_type'] = $_GET['action_type'];
}
if (!empty($_GET['user_id'])) {
$whereConditions[] = "al.user_id = ?";
$params[] = (int)$_GET['user_id'];
$types .= 'i';
$filters['user_id'] = $_GET['user_id'];
}
if (!empty($_GET['date_from'])) {
$whereConditions[] = "DATE(al.created_at) >= ?";
$params[] = $_GET['date_from'];
$types .= 's';
$filters['date_from'] = $_GET['date_from'];
}
if (!empty($_GET['date_to'])) {
$whereConditions[] = "DATE(al.created_at) <= ?";
$params[] = $_GET['date_to'];
$types .= 's';
$filters['date_to'] = $_GET['date_to'];
}
$where = !empty($whereConditions) ? 'WHERE ' . implode(' AND ', $whereConditions) : '';
$countSql = "SELECT COUNT(*) as total FROM audit_log al $where";
if (!empty($params)) {
$stmt = $conn->prepare($countSql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
$countResult = $stmt->get_result();
} else {
$countResult = $conn->query($countSql);
}
$totalLogs = $countResult->fetch_assoc()['total'];
$totalPages = ceil($totalLogs / $perPage);
$sql = "SELECT al.*, u.display_name, u.username
FROM audit_log al
LEFT JOIN users u ON al.user_id = u.user_id
$where
ORDER BY al.created_at DESC
LIMIT $perPage OFFSET $offset";
if (!empty($params)) {
$stmt = $conn->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
$result = $stmt->get_result();
} else {
$result = $conn->query($sql);
}
$auditLogs = [];
while ($row = $result->fetch_assoc()) {
$auditLogs[] = $row;
}
$usersResult = $conn->query("SELECT user_id, username, display_name FROM users ORDER BY display_name");
$users = [];
while ($row = $usersResult->fetch_assoc()) {
$users[] = $row;
}
include 'views/admin/AuditLogView.php';
break;
case $requestPath == '/admin/user-activity':
if (!$currentUser || !$currentUser['is_admin']) {
header("HTTP/1.0 403 Forbidden");
echo 'Admin access required';
break;
}
$dateRange = [
'from' => $_GET['date_from'] ?? date('Y-m-d', strtotime('-30 days')),
'to' => $_GET['date_to'] ?? date('Y-m-d')
];
$sql = "SELECT
u.user_id, u.username, u.display_name, u.is_admin,
(SELECT COUNT(*) FROM tickets t WHERE t.created_by = u.user_id AND DATE(t.created_at) BETWEEN ? AND ?) as tickets_created,
(SELECT COUNT(*) FROM tickets t WHERE t.assigned_to = u.user_id AND t.status = 'Closed' AND DATE(t.updated_at) BETWEEN ? AND ?) as tickets_resolved,
(SELECT COUNT(*) FROM ticket_comments tc WHERE tc.user_id = u.user_id AND DATE(tc.created_at) BETWEEN ? AND ?) as comments_added,
(SELECT COUNT(*) FROM tickets t WHERE t.assigned_to = u.user_id AND DATE(t.created_at) BETWEEN ? AND ?) as tickets_assigned,
(SELECT MAX(al.created_at) FROM audit_log al WHERE al.user_id = u.user_id) as last_activity
FROM users u
ORDER BY tickets_created DESC, tickets_resolved DESC";
$stmt = $conn->prepare($sql);
$stmt->bind_param('ssssssss',
$dateRange['from'], $dateRange['to'],
$dateRange['from'], $dateRange['to'],
$dateRange['from'], $dateRange['to'],
$dateRange['from'], $dateRange['to']
);
$stmt->execute();
$result = $stmt->get_result();
$userStats = [];
while ($row = $result->fetch_assoc()) {
$userStats[] = $row;
}
$stmt->close();
include 'views/admin/UserActivityView.php';
break;
// Legacy support for old URLs
case $requestPath == '/dashboard.php':
header("Location: /");
exit;
case preg_match('/^\/ticket\.php/', $requestPath) && isset($_GET['id']):
header("Location: /ticket/" . $_GET['id']);
exit;
default:
// 404 Not Found
header("HTTP/1.0 404 Not Found");
echo '404 Page Not Found';
break;
}
// Close database connection if it was opened
if (isset($conn)) {
$conn->close();
}
?>