Files
tinker_tickets/api/export_tickets.php
Jared Vititoe be505b7312 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

149 lines
4.8 KiB
PHP

<?php
/**
* Export Tickets API
*
* Exports tickets to CSV format with optional filtering
*/
// Disable error display in the output
ini_set('display_errors', 0);
error_reporting(E_ALL);
// Apply rate limiting
require_once dirname(__DIR__) . '/middleware/RateLimitMiddleware.php';
RateLimitMiddleware::apply('api');
try {
// Include required files
require_once dirname(__DIR__) . '/config/config.php';
require_once dirname(__DIR__) . '/models/TicketModel.php';
// Check authentication via session
session_start();
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['user_id'])) {
header('Content-Type: application/json');
http_response_code(401);
echo json_encode(['success' => false, 'error' => 'Authentication required']);
exit;
}
$currentUser = $_SESSION['user'];
// Create database connection
$conn = new mysqli(
$GLOBALS['config']['DB_HOST'],
$GLOBALS['config']['DB_USER'],
$GLOBALS['config']['DB_PASS'],
$GLOBALS['config']['DB_NAME']
);
if ($conn->connect_error) {
throw new Exception("Database connection failed");
}
// Get filter parameters
$status = isset($_GET['status']) ? $_GET['status'] : null;
$category = isset($_GET['category']) ? $_GET['category'] : null;
$type = isset($_GET['type']) ? $_GET['type'] : null;
$search = isset($_GET['search']) ? trim($_GET['search']) : null;
$format = isset($_GET['format']) ? $_GET['format'] : 'csv';
// Initialize model
$ticketModel = new TicketModel($conn);
// Get all tickets (no pagination for export)
$result = $ticketModel->getAllTickets(1, 10000, $status, 'created_at', 'desc', $category, $type, $search);
$tickets = $result['tickets'];
if ($format === 'csv') {
// CSV Export
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="tickets_export_' . date('Y-m-d_His') . '.csv"');
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
// Create output stream
$output = fopen('php://output', 'w');
// Add BOM for Excel UTF-8 compatibility
fprintf($output, chr(0xEF) . chr(0xBB) . chr(0xBF));
// CSV Headers
$headers = [
'Ticket ID',
'Title',
'Status',
'Priority',
'Category',
'Type',
'Created By',
'Assigned To',
'Created At',
'Updated At',
'Description'
];
fputcsv($output, $headers);
// CSV Data
foreach ($tickets as $ticket) {
$row = [
$ticket['ticket_id'],
$ticket['title'],
$ticket['status'],
'P' . $ticket['priority'],
$ticket['category'],
$ticket['type'],
$ticket['creator_display_name'] ?? $ticket['creator_username'] ?? 'System',
$ticket['assigned_display_name'] ?? $ticket['assigned_username'] ?? 'Unassigned',
$ticket['created_at'],
$ticket['updated_at'],
$ticket['description']
];
fputcsv($output, $row);
}
fclose($output);
exit;
} elseif ($format === 'json') {
// JSON Export
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="tickets_export_' . date('Y-m-d_His') . '.json"');
echo json_encode([
'exported_at' => date('c'),
'total_tickets' => count($tickets),
'tickets' => array_map(function($t) {
return [
'ticket_id' => $t['ticket_id'],
'title' => $t['title'],
'status' => $t['status'],
'priority' => $t['priority'],
'category' => $t['category'],
'type' => $t['type'],
'description' => $t['description'],
'created_by' => $t['creator_display_name'] ?? $t['creator_username'],
'assigned_to' => $t['assigned_display_name'] ?? $t['assigned_username'],
'created_at' => $t['created_at'],
'updated_at' => $t['updated_at']
];
}, $tickets)
], JSON_PRETTY_PRINT);
exit;
} else {
header('Content-Type: application/json');
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Invalid format. Use csv or json.']);
exit;
}
} catch (Exception $e) {
header('Content-Type: application/json');
http_response_code(500);
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}