Repository navigation
Custom Fields Exchange Modernized
Modernization of the legacy Custom Fields export/import screens
(lib/cfields/cfieldsExport.php + lib/cfields/cfieldsImport.php) — GitHub issue
#1411.
The two legacy Smarty screens are replaced by a single standalone Dashio page
(gui/templates/cfields/cfieldsExchange.html) backed by a plain-PHP REST BFF
API (api/cfieldsx/index.php). The Custom Fields screen (cfieldsView.html)
toolbar now has Export and Upload File buttons pointing at the modern
screen, and $actions->cfieldsExchange was added in lib/functions/common.php.
-
URL:
gui/templates/cfields/cfieldsExchange.html -
BFF API:
GET /api/cfieldsx/(info) ·POST /api/cfieldsx/export(download) ·POST /api/cfieldsx/import(multipart upload) -
Rights: export —
cfield_view; import —cfield_management(parity with legacy). Users without a right see the affected card greyed out and receive HTTP 403 from the BFF.
| Section | Description |
|---|---|
| Context strip | Existing custom field count + export/import rights + max upload size |
| Export card | Filename input (default customFields.xml), Format select (XML only), View File Format Document link, Export button (downloads the XML attachment) |
| Import card | Format select (XML only), file input, max-size hint, Upload File button |
| Result box | Green list of Imported names and red list of Not imported (already-existing) names, plus a totals summary |

The BFF mirrors the legacy ADODB_XML output exactly: root <custom_fields>,
one <custom_field> row per custom_fields ⋈ cfield_node_types row, columns
name, label, type, possible_values, default_value, valid_regexp, length_min, length_max, show_on_design, enable_on_design, show_on_execution, enable_on_execution, show_on_testplan_design, enable_on_testplan_design, node_type_id. Streamed as an attachment, default filename customFields.xml.
Right checked: cfield_view.
Rows are parsed XXE-safe (simplexml_load_string after disabling the entity
loader, LIBXML_NONET). For each field, if no field with that name exists
(cfield_mgr->get_by_name), it is created via cfield_mgr->create(); otherwise
it is skipped and reported as not imported. Upload size limited by
import_file_max_size_bytes (default 800 KB). Right checked:
cfield_management.

| Case | Client message |
|---|---|
| Empty export filename | Export name can not be empty! |
| No file selected | Please choose a file to import |
| File over the size limit | The file exceeds the maximum allowed size of {kb} KB |
| Malformed/empty XML | Could not load XML content… (+ libxml detail) |
Session auth + per-route rights, same-origin proof required on all POSTs
(api/_guard.php), XXE protection (entity loader disabled + LIBXML_NONET),
export filename sanitized (basename() + [a-zA-Z0-9 _.-] allow-list) before
echoing in the Content-Disposition header. #1487 parity fix (2026-09-13):
the old [a-zA-Z0-9_.] allow-list silently reverted filenames with spaces or
hyphens to the default customFields.xml (legacy honored them); spaces/hyphens
are accepted now and CRLF injection attempts still fall back safely.
32 cfx.* keys added to all 10 locale bundles (en, de, es, fr, it, ja, pt, ro, ru, zh), each validated with python3 -m json.tool.
15/15 PASS — see tmp/TLU_Test_Cases.md → Task — Issue #1411.
Re-verification 17/17 PASS (2026-09-13, fresh DB) → Task — Issue #1487
(export-filename parity fix recorded in docs/MODERNIZATION-STATUS.md as row
#7b / extras entry).