exists('SESSION.user')){ $f3->reroute('/login'); } } // list attachments public function index($f3){ $this->check_access($f3); $ticket_id = (int) $f3->get('PARAMS.id'); $db = $f3->get('DB'); // fetch attachments $attachments = $db->exec( 'SELECT a.*, u.username FROM attachments a LEFT JOIN users u ON u.id = a.uploaded_by WHERE a.ticket_id = ? ORDER BY a.created_at DESC', [$ticket_id] ); $f3->set('ticket_id', $ticket_id); $f3->set('attachments', $attachments); $f3->set('content', '../ui/views/attachment/index.html'); // echo \Template::instance()->render('../ui/templates/layout.html'); echo \Template::instance()->render($f3->get('content')); } // handle file upload public function upload($f3){ $this->check_access($f3); $ticket_id = (int) $f3->get('PARAMS.id'); $uploaded_by = $f3->get('SESSION.user.id'); if(!isset($_FILES['attachment']) || $_FILES['attachment']['error'] !== UPLOAD_ERR_OK){ $f3->reroute('/ticket/'.$ticket_id.'/attachments'); } $file_info = $_FILES['attachment']; $original_name = $file_info['name']; $tmp_path = $file_info['tmp_name']; // create a unique file path $upload_dir = '../storage/attachments/tickets/'.$ticket_id.'/'; if(!is_dir($upload_dir)){ mkdir($upload_dir, 0777, true); } // if file exists increment version $db = $f3->get('DB'); $existing = $db->exec( 'SELECT * FROM attachments WHERE ticket_id =? AND file_name = ? ORDER BY version_number DESC LIMIT 1', [$ticket_id, $original_name] ); $new_version = 1; if($existing){ $new_version = $existing[0]['version_number'] + 1; } $final_path = $upload_dir.$new_version.'_'.$original_name; // move file move_uploaded_file($tmp_path, $final_path); // store meta data in DB $db->exec( 'INSERT INTO attachments (ticket_id, path, file_name, version_number, uploaded_by, created_at) VALUES (?,?,?,?,?,NOW())', [$ticket_id, $final_path, $original_name, $new_version, $uploaded_by] ); $f3->reroute('/ticket/'.$ticket_id.''); } // download attachment public function download($f3){ $this->check_access($f3); $attachment_id = (int) $f3->get('PARAMS.id'); $db = $f3->get('DB'); $rows = $db->exec('SELECT * FROM attachments WHERE id = ?', [$attachment_id]); if(!$rows){ $f3->error(404, "File not found"); return; } $attachment = $rows[0]; $file_path = $attachment['path']; $file_name = $attachment['file_name']; // validate file exists if(!file_exists($file_path)){ $f3->error(404, "File not found"); return; } // output headers for download header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($file_name).'"'); header('Content-Length: '. filesize($file_path)); // flush headers flush(); // read file readfile($file_path); exit; } // delete an attachment public function delete($f3){ $this->check_access($f3); $attachment_id = (int) $f3->get('PARAMS.id'); $current_user = $f3->get('SESSION.user'); $db = $f3->get('DB'); $rows = $db->exec('SELECT * FROM attachments WHERE id =? LIMIT 1', [$attachment_id]); if(!$rows){ $f3->error(404, "Attachment not found"); return; } $attachment = $rows[0]; // TODO: role or ownership if(file_exists($attachment['path'])){ unlink($attachment['path']); } // remove DB row $db->exec('DELETE FROM attachments WHERE id =?', [$attachment_id]); } }