@@ -193,6 +193,8 @@ function addNewUser() {
193193
194194function addRowUser ( userid , name ) {
195195
196+ userid = sanitizeId ( userid ) ;
197+
196198 let table = document . getElementById ( "usertable" ) ;
197199 let row = table . insertRow ( 1 ) ;
198200 row . id = "row-" + userid ;
@@ -215,28 +217,66 @@ function addRowUser(userid, name) {
215217 cellGroup . innerText = "User" ;
216218 cellLastOnline . innerText = "Never" ;
217219 cellUploads . innerText = "0" ;
218- let buttonResetPw = '<button id="pwchange-' + userid + '" type="button" class="btn btn-outline-light btn-sm" onclick="showResetPwModal(\'' + userid + '\', \'' + name + '\')" title="Reset Password"><i class="bi bi-key-fill"></i></button> ' ;
219- cellActions . innerHTML = '<button id="changeRank_' + userid + '" type="button" onclick="changeRank( ' + userid + ' , \'ADMIN\', \'changeRank_' + userid + '\')" title="Promote User" class="btn btn-outline-light btn-sm"><i class="bi bi-chevron-double-up"></i></button> <button id="delete-' + userid + '" type="button" class="btn btn-outline-danger btn-sm" onclick="showDeleteModal(' + userid + ', \'' + name + '\')" title="Delete"><i class="bi bi-trash3"></i></button>' ;
220+
221+ // Create one button group
222+ const btnGroup = document . createElement ( "div" ) ;
223+ btnGroup . className = "btn-group" ;
224+ btnGroup . setAttribute ( "role" , "group" ) ;
225+
226+ // Password reset button (optional)
220227 if ( isInternalAuth ) {
221- cellActions . innerHTML = buttonResetPw + cellActions . innerHTML ;
228+ const btnResetPw = document . createElement ( "button" ) ;
229+ btnResetPw . id = `pwchange-${ userid } ` ;
230+ btnResetPw . type = "button" ;
231+ btnResetPw . className = "btn btn-outline-light btn-sm" ;
232+ btnResetPw . title = "Reset Password" ;
233+ btnResetPw . onclick = ( ) => showResetPwModal ( userid , name ) ;
234+ btnResetPw . innerHTML = `<i class="bi bi-key-fill"></i>` ;
235+ btnGroup . appendChild ( btnResetPw ) ;
222236 }
223237
238+ // Promote button
239+ const btnPromote = document . createElement ( "button" ) ;
240+ btnPromote . id = `changeRank_${ userid } ` ;
241+ btnPromote . type = "button" ;
242+ btnPromote . className = "btn btn-outline-light btn-sm" ;
243+ btnPromote . title = "Promote User" ;
244+ btnPromote . onclick = ( ) => changeRank ( userid , 'ADMIN' , `changeRank_${ userid } ` ) ;
245+ btnPromote . innerHTML = `<i class="bi bi-chevron-double-up"></i>` ;
246+ btnGroup . appendChild ( btnPromote ) ;
247+
248+ // Delete button
249+ const btnDelete = document . createElement ( "button" ) ;
250+ btnDelete . id = `delete-${ userid } ` ;
251+ btnDelete . type = "button" ;
252+ btnDelete . className = "btn btn-outline-danger btn-sm" ;
253+ btnDelete . title = "Delete" ;
254+ btnDelete . onclick = ( ) => showDeleteModal ( userid , name ) ;
255+ btnDelete . innerHTML = `<i class="bi bi-trash3"></i>` ;
256+ btnGroup . appendChild ( btnDelete ) ;
257+
258+ // Insert button group into cellActions
259+ cellActions . innerHTML = '' ;
260+ cellActions . appendChild ( btnGroup ) ;
261+
262+ // Permissions
224263 cellPermissions . innerHTML = `
225- <i id="perm_replace_` + userid + `" class="bi bi-recycle perm-notgranted " title="Replace own uploads" onclick='changeUserPermission(` + userid + `,"PERM_REPLACE", "perm_replace_` + userid + `");'></i>
226-
227- <i id="perm_list_` + userid + `" class="bi bi-eye perm-notgranted " title="List other uploads" onclick='changeUserPermission(` + userid + `,"PERM_LIST", "perm_list_` + userid + `");'></i>
228-
229- <i id="perm_edit_` + userid + `" class="bi bi-pencil perm-notgranted " title="Edit other uploads" onclick='changeUserPermission(` + userid + `,"PERM_EDIT", "perm_edit_` + userid + `");'></i>
230-
231- <i id="perm_delete_` + userid + `" class="bi bi-trash3 perm-notgranted " title="Delete other uploads" onclick='changeUserPermission(` + userid + `,"PERM_DELETE", "perm_delete_` + userid + `");'></i>
232-
233- <i id="perm_replace_other_` + userid + `" class="bi bi-arrow-left-right perm-notgranted " title="Replace other uploads" onclick='changeUserPermission(` + userid + `,"PERM_REPLACE_OTHER", "perm_replace_other_` + userid + `");'></i>
234-
235- <i id="perm_logs_` + userid + `" class="bi bi-card-list perm-notgranted " title="Manage system logs" onclick='changeUserPermission(` + userid + `,"PERM_LOGS", "perm_logs_` + userid + `");'></i>
236-
237- <i id="perm_users_` + userid + `" class="bi bi-people perm-notgranted " title="Manage users" onclick='changeUserPermission(` + userid + `,"PERM_USERS", "perm_users_` + userid + `");'></i>
238-
239- <i id="perm_api_` + userid + `" class="bi bi-sliders2 perm-notgranted " title="Manage API keys" onclick='changeUserPermission(` + userid + `,"PERM_API", "perm_api_` + userid + `");'></i>` ;
264+ <i id="perm_replace_${ userid } " class="bi bi-recycle perm-notgranted " title="Replace own uploads" onclick='changeUserPermission(${ userid } ,"PERM_REPLACE", "perm_replace_${ userid } ");'></i>
265+
266+ <i id="perm_list_${ userid } " class="bi bi-eye perm-notgranted " title="List other uploads" onclick='changeUserPermission(${ userid } ,"PERM_LIST", "perm_list_${ userid } ");'></i>
267+
268+ <i id="perm_edit_${ userid } " class="bi bi-pencil perm-notgranted " title="Edit other uploads" onclick='changeUserPermission(${ userid } ,"PERM_EDIT", "perm_edit_${ userid } ");'></i>
269+
270+ <i id="perm_delete_${ userid } " class="bi bi-trash3 perm-notgranted " title="Delete other uploads" onclick='changeUserPermission(${ userid } ,"PERM_DELETE", "perm_delete_${ userid } ");'></i>
271+
272+ <i id="perm_replace_other_${ userid } " class="bi bi-arrow-left-right perm-notgranted " title="Replace other uploads" onclick='changeUserPermission(${ userid } ,"PERM_REPLACE_OTHER", "perm_replace_other_${ userid } ");'></i>
273+
274+ <i id="perm_logs_${ userid } " class="bi bi-card-list perm-notgranted " title="Manage system logs" onclick='changeUserPermission(${ userid } ,"PERM_LOGS", "perm_logs_${ userid } ");'></i>
275+
276+ <i id="perm_users_${ userid } " class="bi bi-people perm-notgranted " title="Manage users" onclick='changeUserPermission(${ userid } ,"PERM_USERS", "perm_users_${ userid } ");'></i>
277+
278+ <i id="perm_api_${ userid } " class="bi bi-sliders2 perm-notgranted " title="Manage API keys" onclick='changeUserPermission(${ userid } ,"PERM_API", "perm_api_${ userid } ");'></i>` ;
279+
240280
241281 setTimeout ( ( ) => {
242282
@@ -247,5 +287,12 @@ function addRowUser(userid, name) {
247287 cellPermissions . classList . remove ( "newUser" ) ;
248288 cellActions . classList . remove ( "newUser" ) ;
249289 } , 700 ) ;
290+ }
250291
292+ function sanitizeId ( id ) {
293+ const numericId = id . toString ( ) . trim ( ) ;
294+ if ( ! / ^ \d + $ / . test ( numericId ) ) {
295+ throw new Error ( "Invalid ID: must contain only digits." ) ;
296+ }
297+ return numericId ;
251298}
0 commit comments