Repository navigation
Task Issue 944 Assign Plan Roles Inherited Role Display
Task 944 — effective/inherited role display incl. global-role fallback in Assign Test Plan Roles (gap vs legacy)
Issue: #944
Status: IMPLEMENTED & VERIFIED, issue CLOSED (2026-09-21) — branch task/issue-944 (commit a0ba29973)
Legacy lib/functions/roles.inc.php:373-437 get_tplan_effective_role() computes
every user's effective test-plan role through a 3-layer model:
- explicit plan role (
user_testplan_roles) → effective, not inherited; - private plan + non-admin global role →
<no rights>, not inherited; - otherwise inherited, honouring
testplan_role_inheritance_mode(testproject, config.inc.php:227, default → the project effective role — itself a 3-layer result: explicit project role → global role on a public project →<no rights>on a private project;global→ the user's global role).
gui/templates/dashio/usermanagement/usersAssign.tpl:225-232 labels the
<inherited> X select option with $ikx = is_inherited ? effective_role_id : uplayer_role_id (the global role for explicit rows, e.g. a user with only a
global guest role on a public plan sees <inherited> guest).
What modern did instead: api/roles/index.php GET /roles/meta/tplan-roles
(:790-798) set inheritedRoleID/inheritedRoleName only from the project
role, defaulting inheritedRoleName='No' — a user with no project role but a
global role (e.g. global guest) showed No in the Inherited Role column and a
bare -- no override -- 0-option, whereas legacy shows <inherited> guest.
No effectiveRoleID/isInherited was exposed at all (unlike the sibling
getTprojectEffectiveRoleMap + usersAssignProject.html which already ship the
full pattern).
-
api/roles/index.php— newgetTplanEffectiveRoleMap()(:235-309) portingget_tplan_effective_role()+get_tproject_effective_role()verbatim: explicit plan role → private-plan no-rights → inheritance pertestplan_role_inheritance_modefrom the project effective role (its own 3-layer with global fallback) else the global role; derives the legacy$ikx(is_inherited ? effective_role_id : global role). The GET/roles/meta/tplan-roleshandler now fetches both the project's and the plan'sis_publicand returns per usereffectiveRoleID,isInherited,effectiveRoleName, plus the correctedinheritedRoleID/inheritedRoleName(was project-role-only with a hardcoded'No'fallback). -
gui/templates/usermanagement/usersAssignPlan.html—loadUsers()storesisInherited+effectiveRoleID;rolesOptions()labels the value-0 option<inherited> <inheritedRoleName>(i18nassign.inheritedRoleOption, " {role}") when the effective role is inherited — the exact patternusersAssignProject.html:326-331already uses — elseassign.noOverride. -
i18n: no new keys —
assign.inheritedRoleOption,assign.noOverrideare present in all 10 bundles.
- Public plan global-only users: Inherited Role column now
guest/tester(wasNo); the select's default (value-0, selected) option reads<inherited> guest/<inherited> tester. - Explicit-override row (u944lead): select keeps
leaderselected, unchanged; 0-option stays-- no override --(isInherited=0) — no spurious dirty row. - Project-role inherited row (u944designer):
<inherited> test designer. - Private plan: BFF
effectiveRoleID=3 (<no rights>),isInherited=0; DOM keeps-- no override --, Save disabled (no dirty-state regression, issue body matrix case 5). - Save round-trip: changing a global-fallback row to
testerpersistsuser_testplan_roles (uid,plan,7), grid reloads withtesterselected. - Event Viewer: only AUDIT (log_level 16) rows, 0 Error/Warning.
Screenshot: docs/screenshots/issue-944-inherited-role-global-fallback.png
(public plan: column + <inherited> <role> value-0 options for admin, designer,
guest, tester; leader explicit).
Task suite: tmp/TLU_Test_Cases.md — Suite 944, 6/6 PASS.

Public plan: Inherited Role column shows the real inherited role (guest/tester
from the global-role fallback, test designer from the project role) and the
value-0 override option renders <inherited> <role>; u944lead keeps its
explicit leader override with a plain -- no override -- 0-option.
Refs #944.