Fix Test Plans Modernized - sebiboga/testlink-upgraded GitHub Wiki
Modernization of the Fix Test Plans admin repair utility
(lib/project/fix_tplans.php) β GitHub issue
#1540.
The legacy Smarty screen is replaced by a standalone Dashio page
(gui/templates/projects/fixTPlans.html) backed by a plain-PHP REST BFF
(api/fixplans/index.php). It repairs orphaned test plans (plans whose
testproject_id is 0 or points at a missing test project) by reassigning them
to a chosen project, and β as a 2.0.1 superset β repairs orphaned builds the
same way.
Path: Projects area β link $actions->fixTPlans in lib/functions/common.php
(no ASIDE entry, same as the legacy standalone tool)
URL: gui/templates/projects/fixTPlans.html
BFF API: api/fixplans/index.php
Right: mgt_modify_product (legacy pageAccessCheck parity), enforced on
every route.




| Feature | Legacy behavior | Modern implementation |
|---|---|---|
| Orphan plan list |
getTestPlansWithoutProject() (never defined β PHP Fatal on every load) |
BFF init lists test plans with testproject_id 0 or pointing at a missing testprojects row; display name joined from nodes_hierarchy
|
| Orphan build list | not covered (1.9.x plan-scoped concept) | same orphan rule applied to builds (project-scoped in 2.0.1) β a true superset |
| Target project selector | inherited session context only | per-row <select> of existing projects (name (prefix)), (no change) sentinel |
| Apply action | POST keys trusted blindly |
POST reassign / POST fix_build β every id validated server-side (404 unknown, 400 empty/malformed) before UPDATE testplans/builds SET testproject_id
|
| Confirmation | none | localized Bootstrap modal listing every row (Test plan #N β <project> / Build #N β <project>) |
| Result feedback | legacy message list | success banner + toasts |
| Refresh / empty states | β | toolbar Refresh re-runs init; friendly empty-state panels when nothing to repair |
| Back | legacy return area |
Back β projectsView.html
|
| Locale switcher | none (legacy server locale) | client-side TLi18n switcher (all 10 bundles) |
All routes require an authenticated session and mgt_modify_product on the
system level (401 anonymous, 403 without the right, JSON body).
| Method & path | Purpose | Failure modes |
|---|---|---|
GET /init |
orphan test plans + orphan builds (name/status/testproject_id) and the candidate projects (id, name, prefix) |
401 anonymous; 403 no rights |
POST /reassign {assignments:[{plan_id, project_id}]}
|
validate every pair then UPDATE testplans SET testproject_id
|
400 empty/malformed assignments / unknown project; 404 unknown plan; 403 no rights |
POST /fix_build {build_id, project_id}
|
validate then UPDATE builds SET testproject_id
|
400 missing ids; 404 unknown build; 403 no rights |
The BFF also applies the session same-origin guard (bffSameOriginGuard()), so
a POST without X-Requested-With: XMLHttpRequest is rejected with 403. The
action parameter for POST routes is read from the JSON request body
(first bug found in-browser: reading $_REQUEST only answered Unknown or missing action).
The legacy fix_tplans.php was dead on arrival on the upgraded schema: its
core query called getTestPlansWithoutProject(), a function that has never
existed in the codebase, so every load ended in a PHP Fatal error. The upgraded
schema also moved test plan/build display names into nodes_hierarchy
(testplans/testprojects no longer carry a name column) β the modern BFF
joins the name from there (this misread was the second in-browser bug, a BFF
500 over tp.name).
The legacy POST also wrote testproject_id updates straight from uncleansed
POST keys; the BFF re-checks the right and validates every plan/build/project id
before any write. Repairing orphan builds extends the legacy intent
(1.9.x concept was test-plan-scoped builds.testplan_id).
fixp.* (31 keys) + footers.fixTPlans in all 10 client bundles
(de, en, es, fr, it, ja, pt, ro, ru, zh). Dynamic (JS-generated) table cells
are localized with TLi18n.t() β data-i18n never re-applies to rows created
after page load (third in-browser bug).
- Session authentication +
bffSameOriginGuard()on every route. -
mgt_modify_productgatesinit,reassignandfix_build(403 verified with a role-5 guest user). - Write routes re-validate every plan/build/project id server-side; forged unknown ids are rejected with 400/404 before any write.
- All responses are JSON with the correct HTTP status code (4xx never downgraded to 200; 405 on non-POST/non-GET).
Regression suite Task β Issue #1540 appended to tmp/TLU_Test_Cases.md β
12/12 PASS. Coverage: admin reassign of 2 orphan plans + 1 orphan build
through the confirm modal, cancel path, empty states, ENβRO locale
(ReparΔ planurile de test), guest-403 banner, anonymous-401 banner, BFF
400/404 validation, and a clean Event Viewer (intermediate SQL-error and
CLI-warning rows were removed after the root causes were fixed).