Repository navigation
Feature Issue 1676 usersAssignPlan Name Column First Last Names
Status: implemented and verified (closes #1676)
Screens: gui/templates/usermanagement/usersAssignPlan.html (Assign Test Plan Roles) and its
test-project twin gui/templates/usermanagement/usersAssignProject.html (Assign Test Project Roles)
— the legacy template was shared, so one legacy cell, two modern grids
BFF: api/roles/index.php — GET /api/roles/index.php/meta/tplan-roles
Legacy 1.9.20 gui/templates/dashio/usermanagement/usersAssign.tpl:242 rendered the single
"User" cell as:
{$user->login|escape} ({$user->firstName|escape} {$user->lastName|escape})i.e. the first/last names were always on screen, whatever the configured
$tlCfg->username_format is (config.inc.php:756-764 — '%first% %last%',
'%last%, %first%', '%first% %last% %login%', but also '%login%' / '%email%';
overridable through custom_config.inc.php / config_db.inc.php).
The modern grids split that legacy cell into a Login and a Name column, and the Name
column carried only $u->getDisplayName() (api/roles/index.php, 'name' => $u->getDisplayName();
the same pattern in meta/tproject-roles feeds the Assign Test Project Roles screen, which the
shared legacy template governed with the very same line).
tlUser::getDisplayName() (lib/functions/tlUser.class.php:525-534) expands the format, so for
every format that omits %first%/%last% the Name column became a byte-for-byte duplicate of the
Login column and the names were rendered nowhere — a silent loss of information that legacy
never had.
Measured, same fixture, only the format differs:
username_format |
Login | Name before | Name after |
|---|---|---|---|
%login% |
an1676designer |
an1676designer (== Login) |
an1676designer (Anna Designer) |
%login% |
admin |
admin |
admin (Testlink Administrator) |
%email% |
an1676guest |
an1676guest@localhost |
an1676guest@localhost (Gus Guest) |
%email% |
admin (no e-mail) |
(empty) | Testlink Administrator |
%first% %last% (default) |
an1676designer |
Anna Designer |
Anna Designer (unchanged) |
%first% %last% %login% |
an1676tester |
Tina Tester an1676tester |
unchanged |
| default, user with no names | an1676noname |
(empty) | (empty) (unchanged) |
BFF — both assign payloads ship the raw names next to the display name:
-
api/roles/index.phpmeta/tproject-rolesitem:firstName/lastName(additive, so the twin screenusersAssignProject.htmlcan be closed in its own run without another BFF change); -
api/roles/index.phpmeta/tplan-rolesitem:firstName/lastName.
name stays exactly getDisplayName() — the BFF is not made responsible for composing the cell,
so both columns remain reusable by any other screen.
Screens — a new pure helper userNameCell(u) in usersAssignPlan.html and its identical
twin in usersAssignProject.html (the file-local esc() duplication is this repo's established
per-screen convention, so the helper is too):
- returns the display value unchanged when it already contains both raw names (default format,
'%last%, %first%','%first% %last% %login%', …) — noAnna Designer (Anna Designer); - returns
<display> (<first> <last>)when it does not ('%login%','%email%','%last%', …); - returns the bare
<first> <last>when the display value is empty (no leading()); - returns the display value alone when the user has no first and no last name;
- accepts (and documents) a repeated name for a single-token format whose token is one of the
two names (
'%first%'→Anna (Anna Designer)) — nothing is lost, the other name would otherwise be invisible, and the format was chosen by an administrator; - trims only the outer parts, so a name containing a non-breaking space renders as stored.
The decision is a substring test against the resolved display value, not a guess about the
configured format string — any custom format ('%last% - %first%') is handled by construction.
The Name cell is rendered through esc(), so the appended text is HTML-escaped like the rest of
the cell.
Bonus parity: the appended names are part of the cell text, so the DataTables search on the Name
column matches them again — in 1.9.20 the fused login (first last) cell was the only searchable
user column, so searching by first name was always possible.
No new i18n key: no new user-facing string is introduced (the "Name" header keeps user.fullName,
the appended text is data).

- Automated harness
tmp/suite_1676.py— 32/32 PASS: Part A evaluates the shippeduserNameCell()of both screens (extracted from the HTML, not a copy) over 9 format/edge combinations in node; Part B logs in over real HTTP and asserts the BFF contract for 3 formats (raw names shipped, empty names survive the cast, existing keys untouched,namestill follows the format); Part C asserts the twin screen ships the same helper and renders it throughesc(). - 14 browser cases (
tmp/TLU_Test_Cases.md, suite 1676) — 14/14 PASS, including sort / paging / bulk "Do" / Save regressions on BOTH grids and the console. - Twin screen measured: with
'%login%'→an1676designer (Anna Designer),admin (Testlink Administrator); with the default format →Anna Designer(unchanged), bulk "Do" + Save state still work (5 rows changed, Save enabled), console clean. -
php -l api/roles/index.php,node --checkon the extracted inline script: clean. - Browser console (error + warn) after all reloads: none. Event Viewer: 2 rows, both audit INFO
(
log_level=16), no new Error/Warning.
Fixture used by the suite: php tmp/fixtures_1676.php → tproject AN1676 (id 1), plan
AN1676PLAN (id 2), users an1676designer/Anna Designer, an1676leader/Lars Leader,
an1676tester/Tina Tester, an1676guest/Gus Guest (plus a hand-added an1676noname with both
names empty for the edge case).
| File | Purpose |
|---|---|
api/roles/index.php |
ship firstName / lastName in the tplan-roles (and, additively, tproject-roles) items |
gui/templates/usermanagement/usersAssignPlan.html |
userNameCell() helper, Name cell render, data-model fields |
gui/templates/usermanagement/usersAssignProject.html |
same helper + Name cell render on the test-project twin grid |
tmp/suite_1676.py |
executable harness (client + live BFF) |
tmp/fixtures_1676.php |
throwaway fixture (project/plan/users) |
docs/screenshots/issue-1676-usersAssignPlan-name-column-login-format.png |
fixed state with username_format = '%login%'
|
php tmp/fixtures_1676.php
printf '%s\n' '<?php' '$tlCfg->username_format = "%login%";' > custom_config.inc.php
# browser: admin/admin ->
# http://localhost:8082/gui/templates/usermanagement/usersAssignPlan.html?tproject_id=1&tplan_id=2
rm -f custom_config.inc.php # back to the default format
python3 tmp/suite_1676.py