Repository navigation
Test Project Information Modernized
Modernization of the Test Project Information viewer (the legacy
lib/testcases/archiveData.php?edit=testproject project "home" pane) —
GitHub issue
#923, with the
attachment upload/delete gap closed by
#933, and the
attachment upload/delete rights subsequently aligned with legacy
mgt_modify_tc by
#998.
The legacy project homepage is replaced by a standalone Dashio page
(gui/templates/projects/projectInfoView.html) backed by a plain-PHP REST
BFF (api/projectinfo/index.php, session-based auth, JSON I/O). Every in-app
entry point that used archiveData.php?edit=testproject now points to the new
screen: the right frame of the Specification workspace, the common
$actions->projectInfo link, the info button on each project row in
projectsView.html, and the testproject-level "Cancel" targets of
containerEdit.php and tcImport.php.
Path: any project row's Info button, or the project home pane while
working on a test project
URL: gui/templates/projects/projectInfoView.html?tproject_id=<id> (BFF
also accepts ?action=info&id=<id>)
BFF API: api/projectinfo/index.php?action=info
Rights: mgt_view_tc context read; the UI additionally receives grants
mgt_modify_product / mgt_view_tc / mgt_view_req to drive what is
shown. Attachment upload/delete require mgt_modify_tc on the owning
test project, mirroring the legacy testcase_mgmt gate of
containerEdit.php (Refs #933, rights aligned in #998).
Screenshots: docs/screenshots/projectinfo-view.png,
docs/screenshots/projectinfo-projectsview-info-btn.png,
docs/screenshots/projectinfo-view-ro.png,
docs/screenshots/projectinfo-attachments-upload-delete.png,
docs/screenshots/issue-996-export-all-testsuites.png
(also mirrored on the wiki).
Export all test suites (Refs #996): docs/screenshots/issue-996-export-all-testsuites.png.
Generate Test Spec (HTML + Word) (Refs #997):
docs/screenshots/issue-997-testspec-links-admin.png,
docs/screenshots/issue-997-testspec-html-doc.png.
Import test cases / test suites (Refs #995):
docs/screenshots/issue-995-import-button.png,
docs/screenshots/issue-995-import-target.png.
Test Suite Operations — New test suite + Reorder A-Z (Refs #994):
docs/screenshots/issue-994-testsuite-operations.png (toolbar + suites card),
docs/screenshots/issue-994-new-suite-modal.png (New Test Suite modal).
Attachment rights aligned with legacy (Refs #998):
(user with only mgt_modify_tc — upload panel + delete button shown),
(user with only mgt_modify_product — upload panel hidden).
-
Toolbar: locale switcher, Refresh (re-fetches the BFF), Manage project
(
projectsView.html), Export all test suites (shown only when the user holdsmgt_modify_tcand the project has ≥1 direct test-suite child — gated bycanDoExportandmgt_modify_tcfrom the BFF response; opens the modernized export dialogtcExport.html?tproject_id=<id>&containerID=<id>&useRecursion=1which streams the.testproject-deep.xmlfile, Refs #996), Generate Test Spec (HTML) and Generate Test Spec (Word) (both shown only when the user holdsmgt_modify_tc; they openprintDocument.php?type=testspec&level=testproject&allOptionsOn=1&id=<id>&format=0|4in a new tab, generating the full Test Specification document for the whole project — HTML forformat=0, Word/RTF download forformat=4, Refs #997), Import test cases / test suites (shown only when the user holdsmgt_modify_tc; it opens the modernized import screentcImport.html?containerID=<id>&tproject_id=<id>&intoProject=1&useRecursion=1pre-targeted to import into THIS project, mirroring the legacyimportToTProjectAction, Refs #995), New Test Suite and Reorder test suites (A-Z) (both shown only when the user holdsmgt_modify_tc; they call the BFF write routes below — Refs #994), and Dashboard (mainPage.html). - Overview card: name, prefix, status (Active/Inactive), visibility (Public/Private), test-case counter and the project feature options (Requirements, Priority, Automation, Inventory) as chips.
-
Description card: the
notesfield of the project. - Integrations card: Issue tracker, Code tracker and Requirements management system on/off flags.
-
Attachments card: all project attachments (DataTable with search, sort,
pagination and a server-built Download link to
attachmentdownload.php?id=<id>). When the user holdsmgt_modify_tc, an upload form (title + file picker + Upload button) is rendered above the table and every row gains a Delete button (with a confirm dialog) — mirroring the legacycontainerEdit.phpdoAction=fileUpload/doAction=deleteFileactions of the project-level container (Refs #933, rights aligned in #998). The card is hidden when the project has no attachments and the user cannot upload; otherwise it stays visible so the first attachment (or an upload) can be added.
GET /api/projectinfo/index.php?action=info — session auth required.
| Param | Meaning |
|---|---|
id |
explicit project id (highest priority) |
tproject_id |
legacy alias (used by the HTML screen and the aside links) |
| (none) | falls back to the session's testprojectID
|
Response 200: project { id, name, prefix, notes, color, active, is_public, tc_counter, options { requirementsEnabled, testPriorityEnabled, automationEnabled, inventoryEnabled }, flags { issueTrackerEnabled, codeTrackerEnabled, reqmgrIntegrationEnabled } }, plus attachments[]
(title, file_name, file_size, file_type, date_added, download_url),
top-level canDoExport (boolean — the project has ≥1 direct test-suite
child, mirroring legacy testproject.class.php:778) and
grants { mgt_modify_product, mgt_view_tc, mgt_view_req, mgt_modify_tc }.
Write routes (both require the POST method — non-POST → 405 Method not allowed — and mgt_modify_tc on the owning project — 403 No permission otherwise):
-
POST ?action=upload&id=<project_id>— multipartuploadedFile+ optionalfileTitle(defaults to the file name on the client side). Ports the legacyfileUploadManagement($db, tprojectID, fileTitle, 'nodes_hierarchy')path, including the repo's allowed-file-type / extension and size guards.200returns{ status: ok, attachments: [...] }(refreshed list);422with a descriptive message and a machinecodeon rejections (allowed_files,allowed_filenames_regexp,empty_extension,fNameORfTypeOrfSize, ...). The screen maps those codes to localizedpiv.err*bundle keys client-side (unknown codes fall back to the genericpiv.errUploadGeneric). -
POST ?action=delete&id=<project_id>&file_id=<id>— deletes the attachment via the legacydeleteAttachment($db, file_id, false). Before deleting, the BFF verifies the attachment exists and is bound to this project node (fk_id/fk_table = 'nodes_hierarchy'); a forged file_id →404.
Test Suite Operations write routes (Refs #994 — both require POST →
non-POST returns 405, and mgt_modify_tc on the owning project → 403 No permission: modify test cases required):
-
POST ?action=new_suite&id=<project_id>— JSON body{ name, details }. Ports the legacylib/testcases/containerEdit.phpdoAction=new_testsuitepath: empty name →400 code=empty_name; name failing the globalcheck_string($name, $g_ereg_forbidden)(config.inc.php:2257/[\|]/i) →400 code=bad_chars; thentestsuite::create($projectID, $name, $details, null, config_get('check_names_for_duplicates'), 'block')→ duplicate →400 code=duplicate; other failures →400 code=create_failed. On success firesevent_signal('EVENT_TEST_SUITE_CREATE')and returns{ status: ok, id, name, message: 'testsuite_created', suites: [...] }(refreshed list). -
POST ?action=reorder_suites_alpha&id=<project_id>— ports the legacydoAction=reorder_testproject_testsuites_alpha(reorderTestSuitesDictionary()):tree->get_childrenexcluding testplan/requirement/testcase/requirement_spec, mapid => strtolower(name),natsort(),tree->change_order_bulk(array_keys); returns{ status: ok, message: 'suites_reordered', suites: [...] }.
Errors: 401 Not authenticated (no/invalid session), 403 No permission
(upload/delete without mgt_modify_tc; new_suite / reorder without
mgt_modify_tc — messages distinguish the two), 404 Test project not found / Attachment not found on this project, 405 Method not allowed (write
actions via GET), 400 Missing project id / Unknown action.
- Project id resolution order mirrors legacy
archiveData.php:id>tproject_id> sessiontestprojectID. - Attachments use the same
nodes_hierarchyfk_table convention as the legacy project pane and the modernized suite viewer. - Attachment write actions mirror
containerEdit.phpdoAction=fileUploadanddoAction=deleteFilefor thetestprojectcontainer level. The legacy gate istestcase_mgmt(i.e.mgt_modify_tc); the BFF originally usedmgt_modify_product(as suggested in #933) but that was corrected by #998 tomgt_modify_tcfor full parity — including the practical benefit thatmgt_modify_tcis an ordinary per-project right (it resolves correctly for project-role users), whereasmgt_modify_productis a global-property right ($g_propRights_globalinlib/functions/roles.inc.php) whose per-project grants resolve to null inhasRight()unless the user also holds it globally. - Legacy
deleteAttachment(..., $checkOnSession=true)requires thes_lastAttachmentInfossession list which the BFF never populated; the BFF calls it with the session check disabled and instead enforces an explicitfk_id/fk_tableownership guard before deleting. -
execNavigator.php/planAddTCNavigator.phpstill pass?edit=testprojecttoexecSetResults.php/planAddTC.php— that is their own item-level switch, not a call to the retired viewer; left untouched. -
Export all test suites mirrors the legacy
containerView.tplcontrol panel iconbtn_export_all_testsuites(containerView.tpl:150), gated bycanDoExport— set inlib/functions/testproject.class.php:777-778— and by the legacymodify_tc_rightsblock (containerView.tpl:119, i.e.mgt_modify_tc). The modern BFF computescanDoExportby counting the project's direct children that are not testplan/requirement-spec/testcase nodes — exactly the legacyget_children($safeID, ['testcase','me','testplan'=>'me','requirement_spec'=>'me'])semantics (only direct test-suite children make the project exportable). Clicking the button opens the pre-existing modern export dialog (gui/templates/testcases/tcExport.html+api/testcasesexport/) set to project-deep mode (containerID = tproject_id+useRecursion=1), which streams<project>.testproject-deep.xml— the same file the legacylib/testcases/tcExport.phpproduced (Refs #996). -
Generate Test Spec (HTML + Word) mirrors the legacy
containerView.tplreport iconsbtn_gen_test_spec_new_window(containerView.tpl:154-155, HTMLformat=0) andbtn_gen_test_spec_word(containerView.tpl:157-158, Wordformat=4) atlevel=testproject, both under the legacymodify_tc_rightsblock (containerView.tpl:119, i.e.mgt_modify_tc); the modern toolbar links openlib/results/printDocument.phpwith the sametype=testspec&level=testproject&allOptionsOn=1&id=<project>&format=0|4parameters. The backendprintDocument.phpauthenticates via the session (testlinkInitPage) and gates on its owncheckRights()→testplan_metrics; itsinit_args()does not parseform_token, so the legacy HTML link'sform_tokenparam is inert here and the modern links do not need it. No BFF change was required —grants.mgt_modify_tcwas already returned byapi/projectinfo/index.php?action=info(Refs #997). Caveat (pre-existing legacy behavior):printDocument.php:341derives the title/rights context from the session's active project, whileidselects the subtree root — a deep link?id=Xwith a different session project Y keeps the legacy behavior unchanged. -
Import test cases / test suites mirrors the legacy
containerView.tplproject-level control-panel import icon$importToTProjectAction(containerView.tpl:42-43,148), gated by the samemodify_tc_rights == 'yes'block (containerView.tpl:119, i.e.mgt_modify_tc). The modern toolbar link opensgui/templates/testcases/tcImport.html?containerID=<project>&tproject_id=<project>&intoProject=1&useRecursion=1— exactly the parameters the legacy$importToTProjectActionproduced, landing the user on the modern import screen pre-targeted to import into the current project (intoProject=1&useRecursion=1). The moderntcImport.htmlalready readscontainerID/tproject_id/intoProject/useRecursion, so no change to the import screen was needed. The BFF already returnedgrants.mgt_modify_tc(api/projectinfo/index.php:209), so no BFF change was required either — only the toolbar link + i18n key (Refs #995). -
Test Suite Operations — New test suite + Reorder A-Z mirrors the legacy
containerView.tplmanagement panel itemsbtn_new_testsuiteandbtn_reorder_testsuites_alpha(containerView.tpl:116-163), both under the legacymodify_tc_rights == 'yes'gate (containerView.tpl:119, i.e.mgt_modify_tc). Creation is the exactcontainerEdit.phpdoAction=new_testsuiteflow (containerEdit.php:248-255→addTestSuite()→testsuite::create(..., config_get('check_names_for_duplicates'), 'block'),containerEdit.php:729-765), including the global forbidden-char regex$g_ereg_forbiddenapplied viacheck_string()(legacycontainerEdit.php:87-89) and the duplicate guard; on success the legacy firesEVENT_TEST_SUITE_CREATEwhich the BFF preserves. Ordering is the legacydoAction=reorder_testproject_testsuites_alphaalgorithm (containerEdit.php:337-342,1381-1395): direct children excluding testplan/requirement/testcase/requirement_spec,natsorton lowercased names,change_order_bulk. The BFF also exposes the direct-suite list as a new top-levelsuitesarray inaction=info(id, name, node_order, details), which the screen renders as a read-only Test Suites card for ALL viewers (matchesmgt_view_tcread context); only the two operations are gated onmgt_modify_tc(Refs #994).
Namespace piv.* added to all 10 bundles (de, en, es, fr, it, ja, pt, ro, ru, zh) as flat dotted top-level keys (the TLi18n.t() lookup requires
flat keys — see testing note), e.g. piv.title, piv.overview,
piv.attachments, piv.manage, piv.dashboard, piv.optReqs,
piv.infoShort. The Refs #933 write feature adds piv.upload,
piv.uploadTitle, piv.chooseFile, piv.noFileSelected, piv.delete,
piv.deleteConfirm, piv.uploadOk, piv.uploadFail, piv.deleteOk,
piv.deleteFail, piv.addAttachment — also in all 10 bundles. No
user-facing string is hardcoded in the screen. Refs #996 adds piv.exportAll
("Export all test suites") to all 10 bundles. Refs #997 adds
piv.genSpecHtml ("Generate Test Spec (HTML)") and piv.genSpecWord
("Generate Test Spec (Word)") to all 10 bundles. Refs #995 adds piv.import
("Import") to all 10 bundles. Refs #994 adds the Test Suite Operations keys
piv.newSuite ("New Test Suite"), piv.reorderAlpha ("Reorder test suites
(A-Z)"), piv.reorderConfirm, piv.reordered, piv.errReorder,
piv.suiteName, piv.suiteDetails, piv.newSuiteTitle, piv.newSuiteIn
("Create in project:"), piv.createSuite, piv.suiteNameRequired,
piv.suiteCreated ("Test Suite created"), piv.errSuiteDuplicate,
piv.errSuiteEmptyName, piv.errSuiteBadChars, piv.errSuiteCreate,
plus the read-only card piv.testSuites ("Test Suites"), piv.suitesHint,
piv.noSuites, piv.order, and common.working (spinner during ops) — all
to the same 10 bundles.
- Session gate: unauthenticated requests get
401JSON; the page shows thepiv.loadErrorbanner. - All SQL uses intval'd ids (
getDBTables-whitelisted table names); the query input is numeric only. - DB-derived values are rendered with
.text(); the download URL is built server-side from an intval'd id. - Write routes (upload/delete) are gated by
mgt_modify_tcon the owning test project (legacytestcase_mgmt) and by the shared BFF same-origin guard (CSRF). File upload passes the legacy repository validation (allowed filenames regexp, extension allow-list, size limits); the delete guard verifies the attachment belongs to the requested project before issuing the delete.
Regression suite TC-923.* in tmp/TLU_Test_Cases.md (20 cases, all PASS):
row-infobutton navigation, every card, attachments DataTable + download,
hidden-card-on-empty, BFF 200/404/400/401, ?locale=ro rendering, Refresh,
flat-key bundle validation, cache-busted bundle loads, zero console errors,
containerEdit/tcImport cancel-link output, and an Event Viewer sweep that
produced no new Error/Warning entries.
Regression suite TC-933.* in tmp/TLU_Test_Cases.md documents the
attachment upload/delete gap: upload via UI + BFF, refreshed table after
upload, delete via UI + BFF (with confirm), the fixed DataTable re-render
(DOM rows replaced, no stale entries), row removal when the last attachment
is deleted, read-only rendering for a user without mgt_modify_product
(no upload form / no delete buttons), 403 on write routes for that user,
forged file_id → 404, empty upload → 422, disallowed file type → 422, unknown
project → 404, and Event Viewer cleanliness.
The Task — Issue #998 suite in tmp/TLU_Test_Cases.md (8 cases, all PASS)
documents the attachment rights realignment to mgt_modify_tc: a user
with only mgt_modify_tc (tconly) sees the upload panel + delete buttons
and can upload/delete, while a user with only mgt_modify_product
(prodonly, mgt_modify_tc=null) sees the upload panel hidden and gets
403 No permission to upload/delete attachments on both BFF write routes
(pre-fix this same user was allowed by the mgt_modify_product gate);
admin (both rights) still sees the upload panel; Event Viewer stays clean
(only AUDIT rows).
The Task — Issue #997 suite in tmp/TLU_Test_Cases.md (8 cases, all PASS)
documents the Generate Test Spec (HTML + Word) gap: links visible for a
user with mgt_modify_tc (correct format=0/format=4 hrefs), HTML doc
opens in a new tab with the full project spec (TOC + all suites/cases/steps),
Word doc returns the .doc attachment (200, application/vnd.ms-word,
Content-Disposition: attachment), both links hidden for a user without
mgt_modify_tc, piv.genSpecHtml/piv.genSpecWord present in all 10
bundles, and Event Viewer cleanliness (no Error/Warning).
The Task — Issue #995 suite in tmp/TLU_Test_Cases.md documents the
Import test cases / test suites gap: link visible with the correct
tcImport.html?...&intoProject=1&useRecursion=1 href for a user with
mgt_modify_tc, link hidden for a user without mgt_modify_tc, click lands
on the import screen pre-targeted at the current project, piv.import present
in all 10 bundles, and Event Viewer cleanliness (no Error/Warning).
The Task — Issue #994 suite in tmp/TLU_Test_Cases.md (10 cases, all PASS)
documents the Test Suite Operations feature: info returns direct suites
in display order; admin sees the two operations + the read-only card; create
via modal (append + DB row); empty-name / duplicate / forbidden-char
rejections (UI message + BFF 400 codes); reorder A-Z rewrites node_order
(natsort, case-insensitive); a guest without mgt_modify_tc sees no
operations and gets 403 on both write routes; all piv.* keys +
common.working in all 10 bundles; Event Viewer cleanliness.