Modernize Project Req Spec Mgmt projectReqSpecMgmt - sebiboga/testlink-upgraded GitHub Wiki
- Tracking issue: #1833
-
Legacy:
lib/project/project_req_spec_mgmt.php(46 lines) +gui/templates/dashio/requirements/project_req_spec_mgmt.tpl -
Modern screen:
gui/templates/requirements/projectReqSpecMgmt.html -
BFF:
api/projectreqspecmgmt/index.php -
Related bug filed: #1834 โ
pre-existing
requirement_mgrforeach()-on-null E_WARNING
The TODO section of docs/MODERNIZATION-STATUS.md is empty (0 rows), so a full legacy
sweep was run instead.
The first sweep was wrong and was reverted. It looked for a modern twin at
gui/templates/<area>/<basename>.html. lib/plan/planUrgency.php has no such file โ but it
is already modernized as gui/templates/plans/testUrgency.html + api/plans
(Refs #605, recorded DONE in the ledger) and is reached from the plan navigator as the
urgency action. A duplicate BFF, screen and 55 i18n keys were pushed under #1832 and then
reverted (d59145776, 490b37f14).
The corrected sweep treats a legacy controller as done if any modern
gui/templates/**/*.html or api/**/index.php references <name>.php. Exactly five
controllers are unreferenced:
| Legacy controller | Verdict |
|---|---|
lib/events/eventviewer.php |
orphan early PHP+HTML duplicate of the modernized Event Viewer (#1556) |
lib/project/projectView.php |
already ported by api/projects/index.php
|
lib/testcases/tcSearchForm.php |
dead code โ echo __FILE__; die(); before $smarty->display()
|
lib/usermanagement/userInfo.php |
already usermanagement/userInfo.html
|
lib/project/project_req_spec_mgmt.php |
the one real one |
A pure gated launcher. A run-on title
(Test project โบ <name> โบ Requirement Specification) plus four buttons that only navigate
somewhere else:
| Legacy button | Gate | Legacy target |
|---|---|---|
| New requirement specification | mgt_modify_req |
reqSpecEdit.php?doAction=create&tproject_id= |
| Reorder requirement specifications | mgt_modify_req |
reqSpecEdit.php?doAction=reorder&tproject_id= |
| Import | mgt_modify_req |
reqImport.php?scope=tree&tproject_id= |
| Export all | always | reqExport.php?scope=tree&tproject_id= |
Page access: pageAccessCheck with rightsOr = [mgt_view_req, mgt_modify_req].
This is the project-scoped entry point. The requirement-module-scoped sibling
(lib/requirements/reqSpecMgmt.php) is a different screen and was already modernized as
requirements/reqSpecMgmt.html โ which is why 2.0.1 had no equivalent of this one.
Note reqSpecEdit.html / reqSpecReorder.html do not exist: in 2.0.1 New and Reorder
both live as modals inside requirements/reqSpecMgmt.html. Import/Export are
reqImport.html / reqExport.html.

mgt_view_req OR mgt_modify_req on the addressed project. Verified by flipping the
admin role's role_rights rows:
| Role rights | HTTP | Actions returned |
|---|---|---|
| both | 200 | New, Reorder, Import, Export |
mgt_view_req only |
200 | Export only (3 cards locked) |
| neither |
403 no_right
|
โ |
A forbidden card renders locked rather than vanishing, so the page does not change shape with the role โ while staying unclickable, exactly like the button the legacy template simply did not render.
The legacy screen had nothing to show beyond the buttons:
- Counter tiles โ specification and requirement counts scoped to the project.
- Requirements-disabled warning pointing at the project configuration.
- Refresh, and a readable context sub-line instead of the run-on title.
GET /api/projectreqspecmgmt/index.php?action=init[&tproject_id=N]
Session auth (cheap session gate before the DB connect, so an anonymous call cannot leak
a dbms_msg), bffEnforceSession, then rights. Stable machine codes:
400 bad_param ยท 401 session_expired ยท 403 no_right ยท 404 tproject_not_found ยท
405 ยท 500.
Hardening over legacy:
- the addressed project is proven to exist (404 instead of an empty launcher);
- the four action URLs are built server-side from validated ids, so the page cannot be talked into linking to a foreign project;
- access denials are audited.
nodes_hierarchy has no project column and specification specs nest arbitrarily deep, so a
single GROUP BY node_type_id cannot scope to a project. The BFF reads the hierarchy once
and walks down from the project root in PHP with a $seen loop guard โ one query per node
would be an N+1 over every requirement in the installation. A requirement_version node is
never miscounted as a requirement.
| Trap | Consequence | Handling |
|---|---|---|
testprojects.option_reqs is a dead column in 2.0.1 |
first cut reported requirements_enabled: false for a project that demonstrably has requirements |
live flags are the serialized testprojects.options blob via testproject::getOptions()
|
testproject::create() does not apply $item->options
|
the fixture's project silently stayed requirements-disabled | call setOptions() after (same order as lib/project/projectEdit.php) |
the project name lives in nodes_hierarchy
|
testprojects has no name column |
join through nodes_hierarchy
|
logger.class.php has no SECURITY level |
Undefined array key "SECURITY" + a malformed event on every denial |
audit as AUDIT, like every other BFF |
?? binds looser than !==
|
if ($_SERVER['REQUEST_METHOD'] ?? 'GET' !== 'GET') parses as $_SERVER[...] ?? false, so every request 405'd |
parenthesised |
-
Un-namespaced action labels. The BFF returned bare label ids (
btnNewReqSpec,btnImport, โฆ) that match no bundle, soTLi18n.t()would have echoed the raw id into every action button. Caught by cross-checking the BFF's action labels against all 10 bundles. Fixed โ the BFF now emitsprsm.*. -
logLevel = 'SECURITY'. See the table above. Fixed โAUDIT.
Filed, not fixed (pre-existing legacy defect, out of scope for this screen):
-
#1834 โ
requirement_mgr.class.php:643and:664raiseforeach() argument must be of type array|object, null givenwhenever a requirement has no versions, i.e. the normal state of a freshly built requirement tree. Both are one-line(array)guards.
-
$actions->projectReqSpecMgmtinlib/functions/common.php, next to the existing (module-scoped)$actions->reqSpecMgmtso the two entry points sit side by side. -
lib/project/project_req_spec_mgmt.phpreduced to the session-guarded 302 shim pattern ofeventinfo.php(#1556); it honours an explicittproject_idand otherwise the session project. - 29
prsm.*keys +footers.projectReqSpecMgmtin all 10gui/templates/i18n/*.jsonbundles,python3 -m json.toolvalidated.
Suite Issue #1833 โ 15/15 PASS in tmp/TLU_Test_Cases.md.
php tmp/fixtures_1833.php
# http://localhost:8082/gui/templates/requirements/projectReqSpecMgmt.html?tproject_id=90507Covered: anonymous 401, counters 2/3 (nested spec + deep requirement), all four targets HTTP 200, read-only โ 3 locked + Export, no rights โ 403 with no stale tiles left in the DOM, unknown project โ 404, session fallback, Refresh, EN/RO/DE/JA locale switch, zero console messages. The Event Viewer has 0 new Error/Warning rows after the AUDIT fix.
Mandatory code review (subagent): no BLOCKER, no security issue. Its two MAJOR findings
(an unused projectRootOf() helper and an unused $ntRev) were dead code from an earlier
draft and are removed.