()
| 10058 | } |
| 10059 | |
| 10060 | async function loadUserPermissionsList() { |
| 10061 | const listContainer = document.getElementById("userPermissionsList"); |
| 10062 | if (!listContainer) return; |
| 10063 | listContainer.innerHTML = `<p>${t("loading")}…</p>`; |
| 10064 | |
| 10065 | try { |
| 10066 | // Load users + groups together (folders separately) |
| 10067 | const [usersRes, groupsMap] = await Promise.all([ |
| 10068 | fetch("/api/admin/getUsers.php", { credentials: "include" }).then(safeJson), |
| 10069 | fetchAllGroups().catch(() => ({})) |
| 10070 | ]); |
| 10071 | |
| 10072 | const users = Array.isArray(usersRes) ? usersRes : (usersRes.users || []); |
| 10073 | const groups = groupsMap && typeof groupsMap === "object" ? groupsMap : {}; |
| 10074 | |
| 10075 | if (!users.length && !Object.keys(groups).length) { |
| 10076 | listContainer.innerHTML = "<p>" + t("no_users_found") + "</p>"; |
| 10077 | return; |
| 10078 | } |
| 10079 | |
| 10080 | // Keep cache in sync with the groups UI |
| 10081 | __groupsCache = groups || {}; |
| 10082 | |
| 10083 | const folders = await getAllFolders(true); |
| 10084 | const orderedFolders = ["root", ...folders.filter(f => f !== "root")]; |
| 10085 | |
| 10086 | // Build map: username -> [groupName, ...] |
| 10087 | const userGroupMap = {}; |
| 10088 | Object.keys(groups).forEach(gName => { |
| 10089 | const g = groups[gName] || {}; |
| 10090 | const members = Array.isArray(g.members) ? g.members : []; |
| 10091 | members.forEach(m => { |
| 10092 | const u = String(m || "").trim(); |
| 10093 | if (!u) return; |
| 10094 | if (!userGroupMap[u]) userGroupMap[u] = []; |
| 10095 | userGroupMap[u].push(gName); |
| 10096 | }); |
| 10097 | }); |
| 10098 | |
| 10099 | // Clear the container and render sections |
| 10100 | listContainer.innerHTML = ""; |
| 10101 | |
| 10102 | // ==================== |
| 10103 | // Groups section (top) |
| 10104 | // ==================== |
| 10105 | const groupNames = Object.keys(groups).sort((a, b) => a.localeCompare(b)); |
| 10106 | if (groupNames.length) { |
| 10107 | const groupHeader = document.createElement("div"); |
| 10108 | groupHeader.className = "muted"; |
| 10109 | groupHeader.style.margin = "4px 0 6px"; |
| 10110 | groupHeader.textContent = tf("groups_header", "Groups"); |
| 10111 | listContainer.appendChild(groupHeader); |
| 10112 | |
| 10113 | groupNames.forEach(name => { |
| 10114 | const g = groups[name] || {}; |
| 10115 | const label = g.label || name; |
| 10116 | const members = Array.isArray(g.members) ? g.members : []; |
| 10117 | const membersSummary = members.length |
nothing calls this directly
no test coverage detected