Bugfix Issue 1916 tcEdit PostAction Render EWarnings - sebiboga/testlink-upgraded GitHub Wiki
Bugfix — Issue #1916: tcEdit.php post-action render emitted 18 E_WARNING rows per request (tcViewViewer.inc.tpl / tcbody.inc.tpl unset vars)
Issue: https://github.com/sebiboga/testlink-upgraded/issues/1916 Files changed:
lib/testcases/testcaseCommands.class.php(grant listg2c)lib/testcases/tcEdit.php(grant listgrant2check)lib/functions/testcase.class.php(other-versionghost_preconditions+ the other-version CF loopswitch)
Symptom
Immediately after #1860 unblocked the tcEdit.php version lifecycle render (it used to die
with a Smarty compile 500), each of these three URLs wrote E_WARNING rows to the Event
Viewer on every request:
.../tcEdit.php?deactivate_this_tcversion=1&tcase_id=T&tcversion_id=V -> 1 E_WARNING
.../tcEdit.php?activate_this_tcversion=1&tcase_id=T&tcversion_id=V -> (same path, 1)
.../tcEdit.php?do_create_new_version=1&tcase_id=T&tcversion_id=V -> 16 E_WARNINGs
Three distinct signatures, all in the legacy test-case viewer partials:
Undefined property: stdClass::$delete_frozen_tcversion—tcViewViewer.inc.tplsrc :238/:286 (compiled:293/:340).Undefined array key "ghost_preconditions"—tcbody.inc.tplsrc :73 (compiled:99).Trying to access array offset on null×6 —tcbody.inc.tplcompiled:31,54,71,86,117,127.
The page still returned HTTP 200 and rendered correctly; the only impact was Event Viewer
pollution (the issue's priority: minor).
Approach (root cause → method → why)
These partials are reached only through the legacy path
testcase::show() → tcView.tpl → tcViewViewer.inc.tpl → tcbody.inc.tpl. The normal
viewing path (archiveData.php?edit=testcase) now 302-redirects to the modern
gui/templates/testcases/tcEdit.html, so tcView.tpl is exercised only by the tcEdit
post-action renders (activate / deactivate / create-new-version / do_copy) — which is why
#1860 unmasked these warnings exactly there.
RC1 — $args_can_do->delete_frozen_tcversion was never populated
testcase::initShowGui() (lib/functions/testcase.class.php:7511-7520) builds
$goo->can_do = getShowViewerActions(...) and then copies delete_frozen_tcversion onto it
only if (property_exists($grantsObj, 'delete_frozen_tcversion')). getShowViewerActions()
(testcase.class.php:4957-4989) does not define that member in its default map.
The two grant builders used by the tcEdit post-action path therefore left it undefined:
testcaseCommands::__construct()$g2clist —lib/testcases/testcaseCommands.class.php:43-46tcEdit::initializeGui()$grant2checklist —lib/testcases/tcEdit.php:657-661
The reference builder testcase::getStandardGrantsNames() (testcase.class.php:156-171)
does include delete_frozen_tcversion (line 169) — that is what testcase::show() builds
when $grantSet is null, i.e. the "healthy" tcView controller. The tcEdit path passes its
own incomplete grants object, so the property_exists branch never fired and
tcViewViewer.inc.tpl:238/:286 read an undefined property.
Why populate the grants instead of guarding the template with {if isset(...)}: the ticket
listed both options. Populating restores the reference (tcView) behaviour rather than hiding
the bug for the next caller. It is behaviour-neutral: admin (users.role_id=8) holds
rights.id=47 testcase_freeze but not rights.id=56 delete_frozen_tcversion (measured via
role_rights), so the member resolves to null, and null == "yes" is false — identical to
the previous unset-null in every == "yes" comparison. Same truth value ⇒ no button
appears/disappears; only the warning disappears. Guarding was rejected because it leaves the
grants gap latent.
RC2 — other versions lacked ghost_preconditions
testcase::show() sets ghost_preconditions only for the current version
(testcase.class.php:1087-1088). The other-versions loop (:1208-1210) set ghost but not
ghost_preconditions. tcView.tpl:290 passes an other-version row as args_testcase, so
tcbody.inc.tpl:73 ({$tco.ghost_preconditions}) hit Undefined array key.
RC3 — $gui->cf_other_versions stayed null (the 6 array-offset warnings)
The current-version CF loop (testcase.class.php:1182-1192) correctly does
foreach($cfPlaces as $cfpKey => ...) switch($cfpKey). The other-versions loop
(:1213-1219) does foreach($cfPlaces as $locKey => ...) switch ($cfpKey) — $cfpKey is the
stale variable left over from the finished outer loop (value hide_because_is_used_as_variable,
confirmed by instrumenting testcase::show() with a temp dump). Every iteration therefore hit
that case's break; and $gui->cf_other_versions[...] was never assigned → null.
tcView.tpl:232-235 then passed args_cf=null → tcbody.inc.tpl read inc_tcbody_cf[...] on
null → 6 warnings per other-version row.
The fix
# lib/testcases/testcaseCommands.class.php (g2c)
+ 'testproject_edit_executed_testcases',
+ 'delete_frozen_tcversion');
# lib/testcases/tcEdit.php (grant2check)
+ 'testproject_delete_executed_testcases',
+ 'delete_frozen_tcversion');
# lib/functions/testcase.class.php (other-versions loop)
$ref[$qdx]['ghost'] = sprintf(self::GHOSTMASK, ...);
+ $ref[$qdx]['ghost_preconditions'] =
+ sprintf(self::GHOSTPRECONDITIONSMASK, $tcvSet[0]['tc_external_id'], $ref[$qdx]['version']);
foreach($cfPlaces as $locKey => $locFilter) {
- switch ($cfpKey) {
+ switch ($locKey) {
Verification
Fresh fixture (php tmp/fixtures_1859.php → tcase 44 / tcversion 45), logged in as
admin/admin, four post-action renders (deactivate, activate, create_new_version ×2 —
the two create_new_version calls produced two other-version viewer rows, the exact RC2/RC3
case):
| Metric | Pre-fix | Post-fix |
|---|---|---|
COUNT(events WHERE log_level=2) (E_WARNING) |
18 | 0 |
COUNT(events WHERE log_level IN (1,2,4,8)) |
≥18 | 0 |
| HTTP status of all three URLs | 200 | 200 |
event log_level histogram |
warnings + audit | {16: 2} (audit only) |
Rendered HTML: bytes=112991, test-case name ×16, new_version ×3, delete_tc_version ×2
buttons intact. Headless-Chrome console: only 1 pre-existing Deprecated feature used notice,
no JS errors. Regression gate
(TLU_REQUIRE_SUITE="Issue #1916" bash ai/verify_test_suites.sh): 7 PASS / 0 FAIL.
Screenshot
Post-action render (create-new-version) after the fix — test-case viewer, steps and the
new_version / delete_tc_version buttons intact, no warning overlay, Event Viewer clean:
