Repository navigation
Attachment Upload Download Modernized
Modernization of the Attachment Upload / Download flow
(lib/attachments/attachmentupload.php + attachmentdelete.php +
attachmentdownload.php) — GitHub issue
#1525.
These were the last standalone legacy screens still referenced from the modern
UI. The Smarty popup + download endpoints are replaced by a standalone Dashio
page (gui/templates/attachments/attachmentUpload.html) backed by a plain-PHP
REST BFF (api/attachments/index.php). The legacy lib files are kept for
external/API callers but are no longer referenced by any modern screen.
Path: opened from the attachment link/button of any object (test case spec,
execution, requirement spec, …) that carries attachments
URL: gui/templates/attachments/attachmentUpload.html?table=<fk_table>&id=<fk_id>&name=<object name>
BFF API: api/attachments/index.php (actions list | upload | delete | download)
Right: legacy gate = attachment feature enabled (config_get('attachments')->enabled), mirrored by the BFF.
| Feature | Legacy behavior | Modern implementation |
|---|---|---|
| Object header | popup title + fk_id
|
Dashio title bar: object name in brackets + Refresh toolbar button |
| Max size |
TL_REPOSITORY_MAXFILESIZE server-side only |
shown live above the table (Max file size: 1.0 MB) and enforced by the BFF |
| List | Smarty table | DataTable with attachment name (download link), file name + human size, per-row Delete |
| Upload |
doAction=upload0 single file |
multi-file picker + optional title, Upload submits multipart to the BFF |
| Download | attachmentdownload.php?id=.. |
BFF action=download streams the stored bytes (inline), honoring the legacy XSS-safe SVG handling |
| Delete |
attachmentdelete.php + confirm |
Bootstrap confirm modal (Delete this attachment?); BFF verifies the attachment belongs to the requested fk_table+fk_id
|
| Empty state | — | localized No attachments notice |
| Locale switcher | none (legacy server locale) | client-side TLi18n switcher (all 10 bundles) |
Modern download links switched from lib/attachments/attachmentdownload.php to
/api/attachments/index.php?action=download&id= in:
api/execute, api/execsetresults, api/reqspec, api/projectinfo,
gui/templates/testcases/testSpec.html (attachmentDownloadUrl()) and
gui/templates/execute/execTest.html. openFileUploadWindow() in
gui/javascript/testlink_library.js now opens the modern screen.
Base: POST / GET http://…/api/attachments/index.php, session auth, JSON I/O.
| Route | Params | Right / gate | Response |
|---|---|---|---|
action=list |
table (fk_table), id (fk_id) |
session + attachments->enabled
|
{status, attachments:[{id,title,fk_table,fk_id,path,file_name,file_type,file_size,max_size_text,download_url}], max_size} |
action=upload |
multipart uploadedFile[], table, id, optional title
|
session + CSRF proof + attachments->enabled
|
{status, uploaded:n, errors:[]} |
action=delete |
table, id, file_id
|
session + CSRF proof + attachments->enabled
|
{status, message, deleted_id} |
action=download |
id (attachment id) |
session | streamed file bytes or error JSON |
Validation: table must be in the allowed fk_tables list (executions, tcsteps,
tcversions, testcases, requirement_specs, req_specs, requirements,
testprojects, testsuites, nodes_hierarchy, testplans); id positive int.
Errors: 401 anonymous, 403 no same-origin proof, 400 bad table/id, 404 attachment
not found for the object, 405 unknown action / bad HTTP method, 413 upload too
large, 500 internal.
- The legacy upload popup was the last Smarty screen still reachable from the modern testSpec/execTest UIs — replaced wholesale.
- Delete is now hardened: the legacy
attachmentdelete.phponly checkedid; the BFF requires the attachment to belong to the requestedfk_table/fk_id(forged delete → 404), matching the modern multi-object architecture.
att.* (14 keys in all 10 locale bundles en/ro/de/es/fr/it/ja/pt/ru/zh):
att.confirmDelete, att.deleted, att.errorDelete, att.errorList, att.errorUpload,
att.header, att.invalidFile, att.maxSize, att.noAttachments, att.objectAttachments,
att.selectFile, att.title, att.upload, att.uploaded.
- Session auth required on every action (401 anonymous — verified raw-curl).
- CSRF same-origin proof (
X-Requested-With: XMLHttpRequest) — 403 without it. - fk_table whitelist prevents table-name injection; id validated positive int.
- Download replicates the legacy XSS-safe SVG handling (
text/plainforimage/svg+xml). - Extension allow-list checked server-side before storing.
Browser suite 1525 21/21 PASS appended to tmp/TLU_Test_Cases.md:
BFF matrix (list/upload/download/delete happy path, forged delete 404, bogus table
400, id=0 400, .php extension rejected, CSRF 403, anonymous 401, binary PNG
round-trip cmp-identical + Content-Length), browser flows (upload + title,
download link, confirm-modal delete, empty state, Max file size 1.0 MB), php -l
clean on the BFF + 4 patched emitters, JSON-valid 10 bundles, no modern reference
to lib/attachments/attachmentdownload.php, clean console, Event Viewer clean
(only pre-existing AUDIT/INFO rows).