Repository navigation
Modernize Launch Test Case Print ltcp
The public Launch Test Case Print share-link gateway — the link an external system hands a colleague so they can print one specific test case version without a TestLink login — is modernized.
Issue: Refs #1623
Legacy: ltcp.php (repo root) — it resolved the share link inline and 302-redirected
into lib/testcases/tcPrint.php, which was still a full legacy renderer
(renderHTMLHeader() + renderTestCaseForPrinting()).
Modern: gui/templates/testcases/tcLaunchPrint.html + BFF api/tcprintlaunch/index.php
ltcp.php is a public share-link gateway: an external system (ticket, mail, chat)
hands a colleague a link that prints one specific test case version, and the recipient
does not have a TestLink login:
ltcp.php?apikey=<32-char user API key>&testcase=<PREFIX>-<NUMBER>-<VERSION>
The legacy controller did all of this inline and then redirected into the last
legacy-rendered page the gateway could reach. It also reported every failure by
echo-ing a raw, untranslated marker straight into the browser body
(LTCP-01 … LTCP-05, plus a completely empty body for a missing version).
It is now a Dashio resolver screen that hands over to the already-modern print
screen gui/templates/testcases/tcPrint.html:
-
Resolved test case card — external id (
L16-1), test case name, test suite, version, test project with its prefix, an auth-mode chip (API key or Session) and the resolved print-screen URL. - Version pills — every version of the test case, the current one marked active; clicking another re-resolves it, so a recipient can move from v1 to v2 without editing the link.
-
State cards — Access denied (bad key / no
mgt_view_tc/ not logged in), Test case not found (unknown prefix / unknown test case / unknown version) and a generic Error. Each keeps the legacyLTCP-0xmarker visible in alegacy-tagchip, so every old outcome stays recognizable while the text itself is now localized in all 10 shipped locales. - Tools — Open test case print (disabled until a link resolves), Resolve again, Close; TLi18n locale switcher.
ltcp.php is kept as a public 302 shim forwarding the whole query string to
the modern screen — the same pattern as the lnl.php publicLink shim
(#1541) — so every
existing share link keeps working unchanged.
| legacy | condition | modern |
|---|---|---|
LTCP-01 |
apikey length != 32 | 400 bad_apikey |
LTCP-01 |
32-char key matching no user | 403 bad_apikey |
LTCP-02 |
testcase is not PREFIX-NUM-VERSION
|
400 bad_external_id |
LTCP-02 |
testcase parameter missing |
400 missing_testcase |
LTCP-03 |
prefix is not a known test project | 404 unknown_prefix |
LTCP-04 |
no mgt_view_tc on the owning project |
403 no_rights |
LTCP-05 |
external id does not resolve | 404 testcase_not_found |
(empty die()) |
version not in the test case id set | 404 version_not_found |
| 302 | all checks pass |
200 + resolved ids + print_url
|
The prefix is resolved against the whole testprojects map before any lookup
(same SQL-injection mitigation as the legacy controller) and the version must be
one of the test case's own tcversion_ids.
-
GET ?action=resolve[&apikey=K][&testcase=P-N-V]Authorization — a 32-char
apikeyauthorizes as its owner (remote access,tlUser::getByAPIKey()), and the remote-access session is established withsetUpEnvForRemoteAccess()so the session-based print BFF (api/testcasesprint) accepts the follow-uptcPrint.htmlnavigation. The legacyclearSession => trueis deliberately not reproduced: the identity of an existing session is switched to the link owner instead of logging an unrelated user out. With noapikey, an authenticated session user is accepted (a convenience superset of the legacy apikey-only contract).Payload —
auth_mode,print_urland atestcase{}object withtestcase_id,tcversion_id,external_id,version,name,suite,tproject_id,tproject_name,prefixandavailable_versions[]. -
Contract —
200resolved ·400bad parameters / unknown action ·401no apikey and no session ·403bad key / no rights ·404unknown prefix / test case / version ·405non-GET.GETonly, so the read-only endpoint needs no CSRF proof; session-basedsame-originis inherited from the apikey path of the shared BFF conventions.
-
tlUser::getByAPIKey()returnsnullon no-match, and the legacy code didcount($user)on it — a PHP 8count(null)TypeError, i.e. a 500 for any well-formed-but-unknown key. The BFF guards it (same guard asapi/publiclinkandapi/executionprint). - The prefix is looked up in a pre-loaded map, never interpolated into SQL.
-
setUpEnvForRemoteAccess()is only ever reached after the key resolved to exactly one user, so a bogus key cannot mutate a session. - The 32-char user key and the 64-char test-plan object key are not interchangeable here: only a user key is accepted, as in legacy. (The 64-char object-key public share links are served by the publicLink / report gateways.)
- The apikey column is
users.script_key(32 chars) — there is nousers.apikeycolumn in this schema. -
testprojectshas nonamecolumn and there is notestcasestable: names live innodes_hierarchy, and a test case version links to its node by node id. Readingtestprojects.nameis a1054 Unknown columnerror.
Suite 1623 in tmp/TLU_Test_Cases.md: 36 API assertions + 10 browser cases, all
PASS — every legacy branch above, the 400/401/403/404/405 contract, the rights
path, the shim 302 with the query string preserved, i18n coverage (28 keys × 10
bundles plus every key the screen references), php -l, a clean Event Viewer
(audit rows only), and in the browser the resolved card, the print hand-over, the
version pill, all three state cards, the RO locale with zero raw keys and the
critical anonymous share-link path (a recipient with no TestLink login
resolves and prints).
One real bug was found and fixed during the run: non-2xx BFF responses never
reached the state-card handler (jQuery routes them to .fail()), so every error
card lost its localized message and its LTCP-0x marker.




