Repository navigation
Task Issue 1641 usersAssignPlan Config Driven Pagination
Task — Issue #1641: config-driven usersAssign pagination (enabled + lengthMenu) in usersAssignPlan.html
Status: implemented and verified (closes #1641)
Screen: gui/templates/usermanagement/usersAssignPlan.html — Assign Test Plan Roles
BFF: api/roles/index.php — GET /api/roles/index.php/meta/tplan-roles
Commits: fa2cece23 (feature) + 87e438419 (modified-marker follow-up), branch task/issue-1641
Legacy 1.9.20 gui/templates/dashio/usermanagement/usersAssign.tpl (deleted in ab387af72;
the template was shared by the test-project and test-plan contexts) built its user grid
conditionally on configuration:
{if $tlCfg->gui->usersAssign->pagination->enabled}
{$ll = $tlCfg->gui->usersAssign->pagination->length}
<script>
addToDataTablesConfig = { "columnDefs": [...], "language": { "search": "{$labels.User}" } };
</script>
{include file="DataTables.inc.tpl" DataTablesSelector="#item_view" DataTablesLengthMenu=$ll}
{/if}The grid itself is a bare <table id="item_view"> (usersAssign.tpl:213). The {if} is the
only thing that turns it into a DataTable, so:
$tlCfg->gui->usersAssign->pagination |
Legacy result |
|---|---|
enabled = true, length = '[20, 40, 60, -1], [20, 40, 60, "All"]' (shipped, config.inc.php:663-666) |
DataTable: search box, sortable headers, 20/40/60/All menu, paging |
enabled = false |
plain table — no search, no sort, no length menu, no paging |
any other length
|
menu and starting page size follow the configured set |
The modern screen honoured none of it: lengthMenu / pageLength were hard-coded and
renderUsersTable() unconditionally called $('#assignTable').DataTable({...}), so an
administrator who turned pagination off still got a searchable, sortable, paged grid — the
exact opposite of what legacy rendered.
Two hops were broken, and the BFF half turned out to be already implemented:
-
config.inc.php:663-666→$tlCfg->gui->usersAssign->pagination -
api/roles/index.php:449-471getUsersAssignPaginationConfig()— parsesenabledand the legacy JS-arraylengthstring into a numericlengthMenu(added by #930) ✔ -
api/roles/index.php:820ships the block on the tproject route ✘ — the tplan route'sout([...])never called it -
usersAssignPlan.htmlhard-coded the menu and always built a DataTable ✘
Measured at runtime before any code change: meta/tplan-roles returned keys
["demoMode","items","planIsPublic","plans","projectIsPublic","projects","roleColouring","roles","status","totalPlans"]
— no pagination — while meta/tproject-roles returned
{"enabled":true,"lengthMenu":[[20,40,60,-1],[20,40,60,"All"]]}.
One line on the meta/tplan-roles payload, reusing the existing helper:
'pagination' => getUsersAssignPaginationConfig()]);Modelled on the already-correct sibling usersAssignProject.html:161/490/632-641/654-672/725.
| Change | Location | Purpose |
|---|---|---|
var paginationCfg = {enabled:true, lengthMenu:[[20,40,60,-1],[20,40,60,'All']]} |
module state | defaults = the shipped config.inc.php values, so a payload without the block degrades to the previous behaviour, not to a bare table |
applyPaginationCfg(r) |
called from all three tplan-roles loaders (bootstrap tproject_id=0, plan combo tplan_id=0, loadUsers()) |
a config flip lands on the first paint, not only after a project/plan is picked |
localizedLengthMenu() |
helper | maps the configured labels, translating the shipped English sentinel "All" → assign.all; admin-supplied custom labels pass through untouched |
defaultPageLength() |
helper | first configured length, 20 only as a no-config fallback |
if (!paginationCfg.enabled) { … return; } |
immediately before the DataTable({…}) construct |
the {if} of usersAssign.tpl:111-120: no construct ⇒ no wrapper ⇒ no search / menu / paging / sortable headers. The authorized-only filter falls back to hiding the not_authorized_user <tr> (state → DOM), which is exactly what the screen's pre-existing-but-unreachable disabled-fallback code was written for |
lengthMenu: localizedLengthMenu(), pageLength: defaultPageLength()
|
DataTable options | replaces the hard-coded [20,40,60,-1] / 20
|
keep.len || defaultPageLength() |
bulk-"Do" re-render | a custom [10,30] config no longer loses the administrator's page size |
st.length === defaultPageLength() |
notifyRestoredState() |
the "is this the default view" baseline is the configured first entry, so a [10,30] screen does not fire a bogus "saved view restored" toast on its own untouched default view |
changed class + common.modified badge in the row-build loop, gated on !paginationCfg.enabled
|
row builder | see the defect found below |
No new i18n key: the only user-facing label is the pre-existing assign.all, already present
in all 10 locale bundles, so no bundle was modified.
The "modified" highlight is normally re-applied by DataTables' createdRow hook, which
re-runs on every redraw. With pagination disabled there is no DataTable and therefore no
hook — the first measurement of the bulk-"Do" case gave:
changedRows: 0, badges: 0, saveDisabled: false
i.e. the rows were changed and Save was enabled, but nothing on screen said which rows had
been touched. The class + badge are now emitted in the row-build loop, gated on
!paginationCfg.enabled so the DataTable path keeps its single badge (measured: 19 badges,
0 cells with more than one).
This also revived previously dead code in the screen — .authz-col { display:none } ("in
the pagination-disabled fallback there is no DataTables at all"), the empty-state note, and
onAuthzFilterToggle()'s else if (users.length) renderUsersTable(); branch — all written for
a branch that had no trigger. #1641 is that trigger.
Fixture tmp/fixtures_1641.php → test project TQ1641 (id 7), test plan TQ1641-P1 (id 8),
25 Tester users tlu1641_01..25, plus two global-role-3 users (tlu1641_nr1 uid 27,
tlu1641_nr2 uid 28) so <no rights> rows exist — a public project with only global-Tester
users produces zero of them, which would leave the authorized-only filter unobservable.
Suite: tmp/TLU_Test_Cases.md, "Task — Issue #1641", 16/16 PASS.
| Case | Measured |
|---|---|
| payload | pagination: {enabled:true, lengthMenu:[[20,40,60,-1],[20,40,60,"All"]]} |
| default config | menu ["20","40","60","-1"] / labels ["20","40","60","All"], Showing 1 to 20 of 28 entries, search → 1 of 1 (filtered from 28), 5/5 headers sortable |
custom length '[10,30,-1]', state cleared |
menu ["10","30","-1"], pageLength 10, Showing 1 to 10 of 28 entries, no restore toast |
| custom length with a saved 20-row page |
stateSave restores the user's 20 (legacy DataTables.inc.tpl:100-104 parity) |
enabled = false |
isDataTable:false; no wrapper / search box / length menu / paginate / info; all headers sorting:false; all 28 rows in the DOM
|
| disabled → Save | role 9 persisted, toast User Roles updated, Save re-disabled |
| disabled → bulk "Do" | 28 rows, 27 changed + 27 badges (admin row skipped), no DataTable created |
| disabled → authorized-only filter | 26 visible / 2 hidden, hidden uids exactly [27,28], footer 28 users — 2 unauthorized user(s) hidden; untick → 28 |
| enabled → authorized-only filter |
26 entries (filtered from 28 total), untick → 28 |
| enabled → bulk "Do" | 19 changed, 19 badges, 0 double badges |
| hygiene | console 0 error/warn; events 0 rows at log_level IN ('ERROR','WARNING')
|
| config hygiene |
git diff config.inc.php → empty; config_db.inc.php never staged |
config.inc.php was toggled per test case and restored afterwards; no configuration change
is part of the commits.
- The sibling
usersAssignProject.htmlpasses the configured menu labels to DataTables raw, so it renders the English literalAllinstead of the localized word.localizedLengthMenu()here avoids that. Deliberately not changed by #1641 (it is a separate, cosmetic gap on a different screen). -
config.inc.phpis the administrator-facing switch and is not exposed in the modern Configuration Management screen; this task ports the behaviour, not a new settings UI.
Pagination enabled (the shipped config.inc.php:663-666 default) — DataTable with the
20 / 40 / 60 / All menu, the "User" search box, sortable headers and paging
(Showing 1 to 20 of 28 entries):

Pagination disabled ($tlCfg->gui->usersAssign->pagination->enabled = false) — the bare
legacy table: no wrapper, no search box, no length menu, no paging, all 28 rows present,
and the "Modified" badges still on every changed row after a bulk "Do"
(the fix in 87e438419):
