Repository navigation
Task Issue 926 Effective Inherited Role Display
Issue: #926
Status: IMPLEMENTED (2026-09-17) — branch task/issue-926-effective-role-display
The modern usersAssignProject.html "Assign Test Project Roles" screen built the
per-user role select from the full roles list plus the id-0
TL_ROLES_INHERITED pseudo-role injected by tlRole::getAll(). The BFF only
returned roleID (0 when nothing explicit was assigned), so:
-
Two value-0 options per select —
-- no role --plus the<inherited>pseudo-role — corrupt duplicate, and neither one revealed the user's real effective role. -
No inheritance info — inherited users (no explicit project role) saw a
blank selection instead of the legacy
<inherited> <globalRole>. -
<inherited>/ reserved-system-role labels rendered blank (unescaped angle brackets parsed as HTML).

| Case | Legacy usersAssign.tpl / roles.inc.php
|
Modern (2.0.1) |
|---|---|---|
| No explicit project role, public project |
<inherited> <globalRoleName> option (value 0) selected, INHERITED nature |
Same — value-0 option labelled <inherited> <inheritedRoleName>, INHERITED ROLE badge |
| Explicit project role | assigned role selected | Same — selected by effectiveRoleID
|
| Global admin | select disabled + hintImg
|
Same — disabled select + info-icon tooltip |
| Private project, non-admin, no assignment | effective TL_ROLES_NO_RIGHTS, row not_authorized_user, <no rights> selected |
Same — row class + <no rights> selected |
Legacy source of truth:
gui/templates/dashio/usermanagement/usersAssign.tpl:226-269 ($ikx label),
lib/functions/roles.inc.php:298-343 (get_tproject_effective_role() — 3-layer
model, admin exception, private-project no-rights, explicit assignment wins).
BFF — api/roles/index.php
-
getTprojectEffectiveRoleMap()(api/roles/index.php:142-183) — direct port ofget_tproject_effective_role(): per user returnseffective_role_id,is_inherited,uplayer_role_id, plus the legacy$ikxfields (inherited_role_id,inherited_role_name). -
GET /roles/meta/tproject-roles(api/roles/index.php:505-560) filters the id-0 pseudo-role out of therolesoption list (fixes duplicate value-0), fetches the projectis_publicflag and returnseffectiveRoleID,isInherited,inheritedRoleID,inheritedRoleName,isAdminper user.
Screen — gui/templates/usermanagement/usersAssignProject.html
-
esc()HTML-escaping for all dynamic labels (fixes blank reserved-system-role /<inherited>options). - Exactly one value-0 option per user:
<inherited> {role}(selected) when inherited,-- no role --otherwise; real roles selected byeffectiveRoleID. - Admin selects disabled + tooltip;
INHERITED ROLEbadge with tooltip;not_authorized_userrow foreffectiveRoleID == TL_ROLES_NO_RIGHTS.
i18n — all 10 locale bundles (de/en/es/fr/it/ja/pt/ro/ru/zh):
assign.inheritedRoleOption (<inherited> {role}), assign.inheritedRoleHint,
assign.adminRoleLocked. (A parallel CI bundle rewrite clobbered the first
insertion; the keys were re-added and re-verified per-locale.)

- Public Alpha: alice→
<inherited> tester, bob→<inherited> leader, carol→<inherited> guest, erin→<inherited> senior tester, frank→<inherited> <no rights>+not_authorized_user; admin select disabled; exactly one value-0 option per user; no rawassign.*keys in the DOM. - Private Beta: non-admin users show selected
<no rights>+not_authorized_userrows (legacy parity); admin select disabled. - Assign flow: alice→leader → save → "leader" selected, no inherited badge, DB row
(2,1,9); unassign → row deleted →<inherited> tester+ badge restored. - Locale:
?locale=rorenders<mostenit> testerwith Romanian tooltips. -
tplan-rolessibling endpoint unaffected (200, empty plans). - Event Viewer: no new Error/Warning (audit level 16 only).
- Test suite:
tmp/TLU_Test_Cases.md"Task — Issue #926" 12/12 PASS. - CHANGELOG updated (NEW FEATURES → Assign Test Project Roles, Refs #926).
Branch: task/issue-926-effective-role-display — CI lands it on the default
branch after the run.