Files
tinker_tickets/index.php

268 lines
9.3 KiB
PHP
Raw Normal View History

<?php
// Main entry point for the application
require_once 'config/config.php';
Implement comprehensive improvement plan (Phases 1-6) Security (Phase 1-2): - Add SecurityHeadersMiddleware with CSP, X-Frame-Options, etc. - Add RateLimitMiddleware for API rate limiting - Add security event logging to AuditLogModel - Add ResponseHelper for standardized API responses - Update config.php with security constants Database (Phase 3): - Add migration 014 for additional indexes - Add migration 015 for ticket dependencies - Add migration 016 for ticket attachments - Add migration 017 for recurring tickets - Add migration 018 for custom fields Features (Phase 4-5): - Add ticket dependencies with DependencyModel and API - Add duplicate detection with check_duplicates API - Add file attachments with AttachmentModel and upload/download APIs - Add @mentions with autocomplete and highlighting - Add quick actions on dashboard rows Collaboration (Phase 5): - Add mention extraction in CommentModel - Add mention autocomplete dropdown in ticket.js - Add mention highlighting CSS styles Admin & Export (Phase 6): - Add StatsModel for dashboard widgets - Add dashboard stats cards (open, critical, unassigned, etc.) - Add CSV/JSON export via export_tickets API - Add rich text editor toolbar in markdown.js - Add RecurringTicketModel with cron job - Add CustomFieldModel for per-category fields - Add admin views: RecurringTickets, CustomFields, Workflow, Templates, AuditLog, UserActivity - Add admin APIs: manage_workflows, manage_templates, manage_recurring, custom_fields, get_users - Add admin routes in index.php Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:55:01 -05:00
require_once 'middleware/SecurityHeadersMiddleware.php';
2026-01-01 15:40:32 -05:00
require_once 'middleware/AuthMiddleware.php';
require_once 'models/AuditLogModel.php';
Implement comprehensive improvement plan (Phases 1-6) Security (Phase 1-2): - Add SecurityHeadersMiddleware with CSP, X-Frame-Options, etc. - Add RateLimitMiddleware for API rate limiting - Add security event logging to AuditLogModel - Add ResponseHelper for standardized API responses - Update config.php with security constants Database (Phase 3): - Add migration 014 for additional indexes - Add migration 015 for ticket dependencies - Add migration 016 for ticket attachments - Add migration 017 for recurring tickets - Add migration 018 for custom fields Features (Phase 4-5): - Add ticket dependencies with DependencyModel and API - Add duplicate detection with check_duplicates API - Add file attachments with AttachmentModel and upload/download APIs - Add @mentions with autocomplete and highlighting - Add quick actions on dashboard rows Collaboration (Phase 5): - Add mention extraction in CommentModel - Add mention autocomplete dropdown in ticket.js - Add mention highlighting CSS styles Admin & Export (Phase 6): - Add StatsModel for dashboard widgets - Add dashboard stats cards (open, critical, unassigned, etc.) - Add CSV/JSON export via export_tickets API - Add rich text editor toolbar in markdown.js - Add RecurringTicketModel with cron job - Add CustomFieldModel for per-category fields - Add admin views: RecurringTickets, CustomFields, Workflow, Templates, AuditLog, UserActivity - Add admin APIs: manage_workflows, manage_templates, manage_recurring, custom_fields, get_users - Add admin routes in index.php Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:55:01 -05:00
// 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);
}
2026-01-01 15:40:32 -05:00
// 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);
}
// 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;
Implement comprehensive improvement plan (Phases 1-6) Security (Phase 1-2): - Add SecurityHeadersMiddleware with CSP, X-Frame-Options, etc. - Add RateLimitMiddleware for API rate limiting - Add security event logging to AuditLogModel - Add ResponseHelper for standardized API responses - Update config.php with security constants Database (Phase 3): - Add migration 014 for additional indexes - Add migration 015 for ticket dependencies - Add migration 016 for ticket attachments - Add migration 017 for recurring tickets - Add migration 018 for custom fields Features (Phase 4-5): - Add ticket dependencies with DependencyModel and API - Add duplicate detection with check_duplicates API - Add file attachments with AttachmentModel and upload/download APIs - Add @mentions with autocomplete and highlighting - Add quick actions on dashboard rows Collaboration (Phase 5): - Add mention extraction in CommentModel - Add mention autocomplete dropdown in ticket.js - Add mention highlighting CSS styles Admin & Export (Phase 6): - Add StatsModel for dashboard widgets - Add dashboard stats cards (open, critical, unassigned, etc.) - Add CSV/JSON export via export_tickets API - Add rich text editor toolbar in markdown.js - Add RecurringTicketModel with cron job - Add CustomFieldModel for per-category fields - Add admin views: RecurringTickets, CustomFields, Workflow, Templates, AuditLog, UserActivity - Add admin APIs: manage_workflows, manage_templates, manage_recurring, custom_fields, get_users - Add admin routes in index.php Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:55:01 -05:00
// 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();
}
?>