Test Plan Report Print Modernized - sebiboga/testlink-upgraded GitHub Wiki
Modernization of the Test Plan Report print output — the standalone
entry point that testPlanReport.html (the modernized navigator) previously
opened via the legacy controller /lib/results/printDocument.php in a new
window, and which per-build report links pointed at through lnl.php. It is
replaced by a new Dashio print popup
(gui/templates/results/reportPrint.html) backed by a plain-PHP REST BFF
(api/reportsprint/index.php), reusing the exact same battle-tested legacy
document generator so every printed document stays byte-for-byte the classic
TestLink report. GitHub issue
#845.
URL: gui/templates/results/reportPrint.html?type=<testplan|testreport|testreport_onbuild>&level=<testproject|testsuite>&id=<id>&tproject_id=<id>&tplan_id=<id>&format=<n>&build_id=<id>&opts=<urlencoded print opts>
BFF API: api/reportsprint/index.php?action=print&... / ?action=download&...
Rights: testplan_metrics enforced server-side (same gate as legacy
checkRights() in printDocument.php). Anonymous/direct public links use the
legacy apikey parameters (32-char remote-user script key, 64-char anonymous
testplan/testproject key) — see below.


- What the screen does
- REST API Reference
- Anonymous & remote-user apikey access
- Legacy parity notes
- i18n Keys
- Security
- Testing
| Feature | Legacy behavior | Modern implementation |
|---|---|---|
| Entry point |
testPlanReport.html opened /lib/results/printDocument.php in a new window (full standalone page) |
opens reportPrint.html (new tab) with the same semantics (type / level / id / tproject_id / tplan_id / format / build_id + print options) |
| Per-build report links | navigator per-build report_url used lnl.php?apikey=...
|
report_url re-points to reportPrint.html?type=testreport_onbuild&...&build_id=<bid>&format=0
|
| Document generation | Smarty/PHP rendered a standalone page via printDocument.php
|
BFF action=print re-includes the untouched legacy generator and returns the body as JSON; the screen renders it into the popup |
| browser print of the standalone page |
Print toolbar button issues window.print() on the generated document |
|
| Download | (legacy n/a — opened copies of the standalone page) |
Download opens the BFF action=download endpoint, streaming the generated document as an attachment (Content-Disposition: attachment; filename="RP8-test_plan-….html") |
| Close | close the window | Close closes the popup |
| Locale | PHP $g_lang
|
client-side TLi18n switcher; keys in rpt.* namespace |
| Errors | legacy pages errored out / anonymous apikey path | i18n error banners (not authorized / invalid plan / generation failed) with Print/Download disabled |
| Public links |
lnl.php redirected printDocument.php?apikey=... (anonymous & remote-user) to the generated report |
restored: lnl.php now redirects the same URLs to reportPrint.html?...&apikey=... and the BFF handles apikey — anonymous and remote-user documents generate again (Refs #1408) |
The BFF is session-authenticated JSON (safe verb GET, so no Origin guard
needed) and — since #1408 — also accepts the legacy apikey query
parameter for direct/public access (see section 3).
| Method | Route | Query | Returns |
|---|---|---|---|
| GET | ?action=print |
type (testplan|testreport|testreport_onbuild), level (testproject|testsuite), id, tproject_id, tplan_id, format (0=HTML), build_id (for onbuild), plus print options (toc, headerNumbering, header, summary, body, author, keyword, cfields, requirement) — optional apikey
|
{status, level, id, doc_type, body_html} |
| GET | ?action=download |
same params as print — optional apikey
|
streamed attachment: Content-Type: text/html, Content-Disposition: attachment; filename="RP8-<doc>-<date>.html"
|
- Missing/invalid session and missing/invalid
apikey→ HTTP 401. - Unknown
apikey(64-char anonymous / 32-char remote-user lookup fails) → HTTP 401Unknown api key. - Anonymous
apikeywhose bound context (anonymous testplan/testproject) does not match the request'stproject_id/tplan_id→ HTTP 400Invalid context for api key. - No
testplan_metricsright on the project/plan (remote-user 32-char key) → HTTP 403No permission. - Plan that does not belong to the given test project → HTTP 400
Invalid test plan for this test project. - Unknown
action→ HTTP 404Unknown action.
The legacy apikey flows are fully restored (Refs #1408, mirrors the pattern
already used by api/reports #1220 and api/reportsexport #1246):
-
Remote-user (32 chars — user
script_key): the BFF maps the key to the user, applies the samecheckRights/testplan_metricsgate as a logged-in user and generates the document in that user's context. -
Anonymous (64 chars — testplan
api_key, projectapi_keyfallback): the BFF resolves the testplan/the owning testproject by api key, binds the document to that context (a mismatchedtproject_id/tplan_id→ 400), skips the rights check entirely (isAnon) and generates the document. Print options not supplied by the caller default to the legacy public-link set (header, summary, toc, body, passfail, cfields, metrics, notes, numbering, …), exactly likelnl.php?type=test_plan/?type=test_report&build_id=did. -
Forwarding: the BFF forwards the
apikeyinto the included legacy controller, so its owninit_args()re-runssetUpEnvForRemoteAccess()/setUpEnvForAnonymousAccess()natively. -
lnl.phpre-pointing:test_plan,test_reportandtestreport_onbuildcases now redirect toreportPrint.html?type=...&level=testproject&id=<tpid>&tproject_id=<tpid>&tplan_id=<tplan>&format=0&apikey=...&opts=<urlencoded full flag set>instead of the legacylib/results/printDocument.php. -
cfg/reports.cfg.phpre-pointing:test_plan/test_reportdirectLinkentries were updated with the numbered sprintf placeholders%1$s(basehref),%2$s(apikey),%3$s(tproject_id),%4$s(tplan_id) so self-generated legacy links keep working. - The
reportPrint.htmlpopup reads theapikeyquery parameter (URLSearchParams) and forwards it on everyaction=print/action=downloadquery it builds, so the popup works sessionless after a public-link redirect.
- The BFF re-includes the untouched legacy generator
lib/results/printDocument.phpinside an output buffer and returns the produced body as JSON — the same strategy used byapi/executionprint(Refs #844) andapi/reqdoc(Refs #755), so every report format (test plan, test report, per-build report) renders exactly as it did in 1.9.20/2.0.1. - Two scoping/loading gotchas had to be handled (documented in the BFF header):
- The legacy controller must be
required at TOP-LEVEL script scope afterchdir('lib/results')(its nested relative requires resolve against CWD, and$tlCfgmust be the global). Including it from a helper function scope makes itscfg/reports.cfg.phpinclude run where global$tlCfgis invisible (Attempt to assign property on null). - The BFF does NOT pre-load
cfg/reports.cfg.php(the legacyrequires it itself); pre-loading causedConstant FORMAT_* already definedE_WARNINGs. The BFF uses literal values:FORMAT_HTML=0 and plan-based doc types (testplan,testreport,testreport_onbuild).
- The legacy controller must be
- Extra scoping detail for the apikey branch: for an apikey caller, the legacy
init_args()requires$dband session-independent env, so the remote-user path callssetPaths()before the legacy include whenbasehrefis not set, andCHECK_LEVEL/rights checks are skipped for the anonymous path (the legacy controller performs its own rights gate for 32-char keys).
All labels are client-side via TLi18n; keys under the rpt. namespace
(rpt.header, rpt.project, rpt.btnPrint, rpt.btnDownload,
rpt.btnClose, rpt.errLoad, rpt.empty). Present in all 10 bundles
(en ro de es fr it ja pt ru zh). No new keys were required for the apikey
feature — the BFF error responses are plain-English JSON (Unknown api key,
Invalid context for api key), consistent with the other report BFFs.
- Session-authenticated BFF plus legacy apikey support (public links):
- anonymous 64-char key is bound to its testplan/testproject and the request context must match (400 otherwise);
- remote-user 32-char key inherits that user's rights (
testplan_metricsenforced; a no-rights user → 403); - an unknown/invalid apikey → 401
Unknown api key.
- Server-side right re-check on every session-authenticated request
(
testplan_metricson the context project/plan); a user without that right gets 403 even with a valid session. - The
id/tproject_id/tplan_id/build_idvalues are int-cast and the plan is validated as belonging to the project before generation, so a hand-crafted bad id returns a clean 400 instead of a fatal. - Supported
type/levelvalues are white-listed; anything unexpected is rejected before generation.
See Suite 845 — Test Plan Report print endpoint in
tmp/TLU_Test_Cases.md (15/15 PASS): whole-plan / one-suite /
testreport_onbuild per-build / testreport generation; download headers; bogus
plan 400; unknown action 404; no-rights user 403; guest-with-right user 200;
navigator "Print whole test plan report" button opens the popup with the proper
opts; per-build report_url re-pointing; print popup render; locale loads;
Event Viewer clean; console clean.
See Suite 1408 — reportPrint apikey / public-link anonymous access in
tmp/TLU_Test_Cases.md for the apikey coverage: anonymous 64-char print +
download + onbuild public links render in a sessionless browser context; the
32-char remote-user key renders (admin) and 403s (no-rights user); mismatched
anonymous context → 400; invalid key → 401; lnl.php redirects arrive at the
modern popup with the full legacy opts; authenticated navigator flows still
work after the re-pointing.