diff --git a/dbcreate.sql b/dbcreate.sql index c79308a..ca4be61 100644 --- a/dbcreate.sql +++ b/dbcreate.sql @@ -367,10 +367,9 @@ INSERT INTO dbo.EventLevels VALUES (4,'error','Error',1), (8,'throw_exception','Exception',0); -INSERT INTO dbo.Plugins VALUES ('SYSTEM',1,'1.0.0'); INSERT INTO dbo.[Role] (Name,Description,RoleType) -VALUES ('ADMIN','System Administrator','SYSTEM'); +VALUES ('ADMIN','System Administrators','SYSTEM'); INSERT INTO dbo.Permission (Scope,Resource,Action) VALUES ('SYSTEM','ALL','ALL'); @@ -493,18 +492,6 @@ JOIN dbo.[Role] r GO --- ======================================================== --- 3. EFFECTIVE ROLES (DEDUPLICATED) --- ======================================================== -CREATE OR ALTER VIEW dbo.vAuthenticationEffectiveRoles AS -SELECT DISTINCT - Authentication_ObjectGUID, - Role_ID, - RoleName -FROM dbo.vAuthenticationRolesExpanded; -GO - - -- ======================================================== -- 4. PERMISSIONS (DETAILED WITH ROLE SOURCE) -- ======================================================== diff --git a/public/javascript/customModal.js b/public/javascript/customModal.js index 8bce03c..2c5b890 100644 --- a/public/javascript/customModal.js +++ b/public/javascript/customModal.js @@ -149,6 +149,9 @@ function slideOutMessage(message) { /** feedbox({ title: `⚠ Upload abbrechen?`, + lock: true, + primary: 'yes', + replace: false, message: `

Es laufen noch aktive Uploads.

Möchtest du wirklich alle abbrechen?

diff --git a/public/javascript/main.js b/public/javascript/main.js index 5ad1e62..e60a2b1 100644 --- a/public/javascript/main.js +++ b/public/javascript/main.js @@ -1033,6 +1033,7 @@ function virtualTable({ }); // Live-Counter + if(!filterConfig || filterConfig.hideCounter) return if(filterState.counterEl) filterState.counterEl.textContent = `${visibleCount} Treffer`; } @@ -1230,9 +1231,13 @@ window.addEventListener('resize', () => { tableEl.style.setProperty('--filter-height', h + 'px'); }); - filterState.counterEl = document.createElement('div'); + if(!filterConfig.hideCounter +) { +filterState.counterEl = document.createElement('div'); filterState.counterEl.className = 'live-counter'; container.appendChild(filterState.counterEl); + } + syncFilterWidth(container); const wrapperResizeObserver = new ResizeObserver(() => { diff --git a/public/javascript/os.js b/public/javascript/os.js index dd6f720..3e37fca 100644 --- a/public/javascript/os.js +++ b/public/javascript/os.js @@ -11,6 +11,7 @@ const restoreIcon = '🗗'; const startBtn = document.getElementById('start-btn'); const startMenu = document.getElementById('start-menu'); +const taskbar = document.getElementById('taskbar'); const windowsContainer = document.getElementById('windows'); const taskbarWindows = document.getElementById('taskbar-windows'); const ctx = new ContextMenu(); @@ -21,9 +22,27 @@ const MAX_PADDING = { left: 4, top: 4, right: 4, bottom: (56 - 4) }; startBtn.addEventListener('click', (evt) => { evt.stopPropagation(); // verhindert sofortiges Schließen + startBtn.classList.toggle('active'); startMenu.classList.toggle('hidden'); }); +function updateStartMenuPosition() { + const height = taskbar.offsetHeight; + startMenu.style.bottom = (height + 5) + 'px'; +} + +const observer = new ResizeObserver(entries => { + for (let entry of entries) { + const height = entry.contentRect.height; + document.documentElement.style.setProperty('--auto-taskbar-height', height + 'px'); + } +}); + +observer.observe(taskbar); + +window.addEventListener('resize', updateStartMenuPosition); +window.addEventListener('load', updateStartMenuPosition); + // Launch app when clicking start menu item document.addEventListener('click', async (e) => { const target = e.target.closest('.start-item'); @@ -31,6 +50,7 @@ document.addEventListener('click', async (e) => { const clickedButton = startBtn.contains(e.target); if(!clickedInsideMenu && !clickedButton) { + startBtn.classList.remove('active'); startMenu.classList.add('hidden'); return; } diff --git a/public/javascript/rbacAPI.js b/public/javascript/rbacAPI.js index f9bf073..e343a87 100644 --- a/public/javascript/rbacAPI.js +++ b/public/javascript/rbacAPI.js @@ -1,95 +1,229 @@ -const vt = virtualTable({ - tableEl: document.querySelector('#rbacUsersTable'), - data: [], - rowHeight: 20, - buffer: 5, - groupKey: 'ObjectSourceName', // optional zum Gruppieren - rowKey: 'ObjectGUID', - filterConfig: { - exceptedColumns: ['Status_ID', 'Anhänge'], - columnModes: { - ID: 'text', Status: 'dropdown', Objekt: 'text', Priorität: 'dropdown', - Erstelldatum: 'text', Gewerk: 'dropdown', Typ: 'dropdown', - Bedarfsmelder: 'text', Bearbeiter: 'text', Genehmiger: 'text', - Status: 'dropdown' - } - }, - customRender: (row, tr) => { - - createTd(tr, - ``, { - styles: { - 'position': 'sticky', - 'left': '0px', - 'width': '20px', - 'z-index': '2' - }, classes: [ - 'text-align:left' - ], onclick: () => { - sendUserEvent('RBAC', `Benutzer ${row['sn'][0].toUpperCase() + row['sn'].slice(1)}, ${row['givenName'][0].toUpperCase() + row['givenName'].slice(1)} gelöscht`, null, 3); - } - }); +const ctx = new ContextMenu(); - createTd(tr, row['ObjectGUID'], { classes: [ 'text-align:left' ], styles: { 'max-width': '100px' }, attributes: { 'data-tooltip': row['ObjectGUID'] } }); - createTd(tr, row['sAMAccountName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sAMAccountName'] } }); - createTd(tr, row['sn'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sn'] } }); - createTd(tr, row['givenName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['givenName'] } }); - createTd(tr, row['mail'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['mail'] } }); - createTd(tr, row['active'], { classes: [ 'text-align:center' ] }); - createTd(tr, row['online'], { classes: [ 'text-align:center' ] }); - createTd(tr, row['RoleCount'], { classes: [ 'text-align:center' ] }); - createTd(tr, row['GroupCount'], { classes: [ 'text-align:center' ] }); - createTd(tr, row['ObjectSourceName'], { classes: [ 'text-align:right' ] }); - } - }); +////////////////////////////// +// 🌐 API LAYER +////////////////////////////// - -async function api(url, method = 'GET', body) { +const api = async (url, method = 'GET', body) => { const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: body ? JSON.stringify(body) : undefined }); - - return res.json(); -} - -async function createUser() { - const name = document.getElementById('newUserName').value; - const sn = name.split('.')[1]; - const givenName = name.split('.')[0]; - const mail = `${name}@test.com`; - - const user = await api('/api/rbac/auth/create', 'POST', { - sAMAccountName: name, - mail: mail, - sn: sn[0].toUpperCase() + sn.slice(1), - givenName: givenName[0].toUpperCase() + givenName.slice(1) - }); - if(user) { - sendUserEvent('RBAC', `Benutzer ${sn[0].toUpperCase() + sn.slice(1)}, ${givenName[0].toUpperCase() + givenName.slice(1)} angelegt`, null, 0); - loadUsers(); + if(res.status >= 400) { + const text = await res.text(); + sendUserEvent('RBAC', `Hoppla, da ist etwas schief gelaufen:\r\n${text}`, null, 4); } + return res.json(); +}; + +////////////////////////////// +// 🧠 RBAC SERVICE LAYER +////////////////////////////// + +const RBAC = { + + // 👤 USERS + loadUsers: async () => (await api('/api/rbac/auth/get', 'POST')) + .map(({ active, online, ...rest }) => ({ + ...rest, + Aktiv: active + })) + .sort((a, b) => a.sAMAccountName.localeCompare(b.sAMAccountName)), + + createUser: (data) => api('/api/rbac/auth/create', 'POST', data), + + deleteUser: (guid) => api(`/api/rbac/auth/${guid}`, 'DELETE'), + + // 👥 GROUPS + loadGroups: () => api('/api/rbac/group/get', 'POST'), + + createGroup: (name) => api('/api/rbac/group/create', 'POST', { name }), + + deleteGroup: (guid) => api(`/api/rbac/group/${guid}`, 'DELETE'), + + // 🔗 ASSIGNMENTS + addUserToGroup: (authGuid, groupGuid) => + api('/api/rbac/group/add-user', 'POST', { authGuid, groupGuid }), + + addUserToRole: (authGuid, roleId) => + api('/api/rbac/role/add-user', 'POST', { authGuid, roleId }), + + addGroupToRole: (groupGuid, roleId) => + api('/api/rbac/role/add-group', 'POST', { groupGuid, roleId }), + + addPermissionToRole: (roleId, permissionId) => + api('/api/rbac/role/add-permission', 'POST', { roleId, permissionId }) + +}; + +////////////////////////////// +// 🖱️ DRAG & DROP +////////////////////////////// + +function createDragZone(el, data) { + el.draggable = true; + el.addEventListener('dragstart', (evt) => { + evt.dataTransfer.setData('application/json', JSON.stringify(data)); + }); } +function createDropZone(el, type, target) { + const targetValue = target.ObjectGUID || target.Role_ID || target.Permission_ID; + let process = { action: null, response: null, failure: false }; + el.addEventListener('dragover', e => e.preventDefault()); + + el.addEventListener('drop', async (e) => { + e.preventDefault(); + + const data = JSON.parse( + e.dataTransfer.getData('application/json') + ); + + if(!targetValue) { + sendUserEvent('RBAC', 'Deinem Drop-Ziel wurde kein ID-Attribut zugewiesen. Frag einfach Manuel ', null, 2); + return; + } + + switch (type) { + case 'group': + process.action = `Du hast den Benutzer der Gruppe hinzugefügt`; + process.response = await RBAC.addUserToGroup(data.ObjectGUID, targetValue); + loadUsers(); + break; + case 'role': + process.action = `Du hast den Benutzer der Rolle hinzugefügt`; + process.response = await RBAC.addUserToRole(data.ObjectGUID, targetValue); + break; + + case 'group-to-role': + process.action = `Du hast die Gruppe der Rolle hinzugefügt`; + process.response = await RBAC.addGroupToRole(data.ObjectGUID, targetValue); + break; + + case 'permission': + process.action = `Du hast den Berechtigung der Rolle hinzugefügt`; + process.response = await RBAC.addPermissionToRole(targetValue, data.ID); + break; + } + + if(process.failure) return; + sendUserEvent('RBAC', process.action, null, 0); + }); +} + +////////////////////////////// +// 📋 TABLE (USERS) +////////////////////////////// + +const vt = virtualTable({ + tableEl: document.querySelector('#rbacUsersTable'), + data: [], + rowHeight: 20, + buffer: 5, + groupKey: 'ObjectSourceName', + rowKey: 'ObjectGUID', + filterConfig: { + hideCounter: true, + exceptedColumns: ['', 'Rollen', 'Gruppen'], + columnModes: { + Aktiv: 'dropdown', + Online: 'dropdown' + } + }, + + customRender: (row, tr) => { + + createDragZone(tr, row); + + tr.addEventListener('contextmenu', (evt) => { + evt.preventDefault(); + + ctx.setItems([ + { + label: "Details", + onClick: () => showAuthDetails(row.ObjectGUID) + } + ]); + + ctx.show(evt.pageX + 5, { y: evt.pageY + 5 }); + }); + + createTd(tr, + ``, { + styles: { + 'position': 'sticky', + 'left': '0px', + 'max-width': '20px', + 'z-index': '2' + }, classes: [ + 'text-align:left' + ], onclick: () => { + if(row['ObjectGUID'] === '00000000-0000-0000-0000-000000000001') return; + deleteUser(row['ObjectGUID'], row['sAMAccountName']); + } + }); + createTd(tr, row['ObjectGUID'], { classes: [ 'text-align:left' ], styles: { 'max-width': '100px' }, attributes: { 'data-tooltip': row['ObjectGUID'] } }); + createTd(tr, row['sAMAccountName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sAMAccountName'] } }); + createTd(tr, row['RoleCount'], { classes: [ 'text-align:center' ] }); + createTd(tr, row['GroupCount'], { classes: [ 'text-align:center' ] }); + createTd(tr, row['sn'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sn'] } }); + createTd(tr, row['givenName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['givenName'] } }); + createTd(tr, row['mail'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['mail'] } }); + createTd(tr, row['Aktiv'], { classes: [ 'text-align:center' ] }); + } +}); + +////////////////////////////// +// 📥 LOADERS +////////////////////////////// async function loadUsers() { try { - const users = await api('/api/rbac/auth/get', 'POST'); - if(users) { - vt.source(users); - return; - } - sendUserEvent('RBAC', 'Benutzer konnten nicht geladen', null, 4); - } catch(err) { - writeEventLog(4, 'RBAC', err); + vt.source(await RBAC.loadUsers()); + } catch (err) { + console.error(err); } } +async function loadGroups() { + const container = document.getElementById('rbacGroupContainer'); + container.innerHTML = ''; + + const groups = await RBAC.loadGroups(); + + const fragment = document.createDocumentFragment(); + + groups.forEach(group => { + + const section = document.createElement('section'); + const groupName = document.createElement('span'); + groupName.innerHTML = group.Name; + groupName.dataset.tooltip = group.Name; + groupName.dataset.tooltipMode = 'ellipsis'; + section.append(groupName); + + if(group.ObjectGUID !== '00000000-0000-0000-0000-000000000001') { + const removeButton = document.createElement('div'); + removeButton.className = 'removeButton'; + removeButton.innerHTML = 'X'; + removeButton.onclick = async () => { + await deleteGroup(group.ObjectGUID, group.Name); + }; + section.append(removeButton); + } + + createDropZone(section, 'group', group); + + fragment.appendChild(section); + }); + + container.appendChild(fragment); +} async function createGroup() { const name = document.getElementById('newGroupName').value; @@ -101,76 +235,411 @@ async function createGroup() { loadGroups(); } } -// HIER WEITER - GRUPPEN KARTEN MÜSSEN HÜBSCHER WERDEN. -// BENUTZER UND GRUPPEN KÖNNEN NOCH NICHT GELÖSCHT WERDEN. -// GRUPPEN AUCH OBJECTSOURCE_ID 1? -async function loadGroups() { - try { - const rbacGroupContainer = document.getElementById('rbacGroupContainer'); - rbacGroupContainer.innerHTML = ''; - const groups = await api('/api/rbac/group/get', 'POST'); - if(groups) { - let fragment = document.createDocumentFragment(); - groups.forEach(group => { - const section = document.createElement('section'); - section.innerHTML = `${group.Name}
X
`; - section.dataset.tooltip = group.Name; - fragment.appendChild(section); - }); - rbacGroupContainer.innerHTML = ''; - rbacGroupContainer.appendChild(fragment); - return; - } - sendUserEvent('RBAC', 'Gruppen konnten nicht geladen', null, 4); - } catch(err) { - writeEventLog(4, 'RBAC', err); - } + +////////////////////////////// +// 👤 USER ACTIONS +////////////////////////////// + +async function createUser() { + const name = document.getElementById('newUserName').value; + + if (!name || !name.includes('.')) return; + + const [givenName, sn] = name.split('.'); + + const user = await RBAC.createUser({ + sAMAccountName: name, + mail: `${name}@test.com`, + givenName: givenName[0].toUpperCase() + givenName.slice(1), + sn: sn[0].toUpperCase() + sn.slice(1) + }); + + sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName} [${user.ObjectGUID}] angelegt`, null, 0); + loadUsers(); } +async function deleteUser(guid, name) { + feedbox({ + title: `Benutzer löschen`, + message: `Soll der Benutzer ${name} und
alle seine Zugehörigkeiten entfernt werden`, + buttons: { + no: { text: 'Nein' }, + yes: { + text: 'Ja', + onClick: async () => { + const user = await RBAC.deleteUser(guid); + sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName || ''} [${user.ObjectGUID}] gelöscht`, null, 0); + loadUsers(); + } + } + } + }); +} + + +////////////////////////////// +// 👥 GROUP ACTIONS +////////////////////////////// + +async function deleteGroup(guid, name) { + feedbox({ + title: `Gruppe löschen`, + message: `Möchtest du die Gruppe ${name} [${guid}] wirklich löschen`, + buttons: { + no: { text: 'Nein' }, + yes: { + text: 'Ja', + onClick: async () => { + const group = await RBAC.deleteGroup(guid); + sendUserEvent('RBAC', `Du hast die Gruppe ${name || ''} [${group.ObjectGUID}] gelöscht`, null, 0); + loadUsers(); + loadGroups(); + } + } + } + }); +} + + + +////////////////////////////// +// 🚀 INIT +////////////////////////////// + loadUsers(); loadGroups(); -async function createRole() { - const name = document.getElementById('newRoleName').value; - - await api('/api/role', 'POST', { - name - }); - - loadRoles(); -} - -async function loadRoles() { - document.getElementById('roleList').innerHTML = 'Reload roles...'; -} -async function createPermission() { - const scope = document.getElementById('permScope').value; - const resource = document.getElementById('permResource').value; - const action = document.getElementById('permAction').value; +// const ctx = new ContextMenu(); +// const vt = virtualTable({ +// tableEl: document.querySelector('#rbacUsersTable'), +// data: [], +// rowHeight: 20, +// buffer: 5, +// groupKey: 'ObjectSourceName', // optional zum Gruppieren +// rowKey: 'ObjectGUID', +// filterConfig: { +// hideCounter: true, +// exceptedColumns: ['', 'Rollen', 'Gruppen'], +// columnModes: { +// Aktiv: 'dropdown', +// Online: 'dropdown' +// } +// }, +// customRender: (row, tr) => { +// tr.draggable = true; +// tr.dataset.guid = row['ObjectGUID']; +// tr.dataset.sAMAccountName = row['sAMAccountName']; - await api('/permission', 'POST', { - scope, - resource, - action - }); - - alert('Permission created'); -} +// tr.ondragstart = (evt) => { +// const data = { +// guid: tr.dataset.guid, +// sAMAccountName: tr.dataset.sAMAccountName +// }; +// evt.dataTransfer.setData('application/json', JSON.stringify(data)); +// }; -async function addUserToGroup(authId, groupId) { - await api('/api/rbac/group/add-user', 'POST', { - authId, - groupId - }); -} +// tr.oncontextmenu = async evt => { +// if (evt.target.tagName === 'BUTTON') return; +// ctx.setItems([ { label: "Details", onClick: () => showAuthDetails(row['ObjectGUID']) } ]); +// evt.preventDefault(); +// ctx.show(evt.pageX + 5, {y: evt.pageY + 5 }); +// }; + +// createTd(tr, +// ``, { +// styles: { +// 'position': 'sticky', +// 'left': '0px', +// 'width': '20px', +// 'z-index': '2' +// }, classes: [ +// 'text-align:left' +// ], onclick: () => { +// if(row['ObjectGUID'] === '00000000-0000-0000-0000-000000000001') return; +// deleteUser(row['ObjectGUID'], row['sAMAccountName']); +// } +// }); + +// createTd(tr, row['ObjectGUID'], { classes: [ 'text-align:left' ], styles: { 'max-width': '100px' }, attributes: { 'data-tooltip': row['ObjectGUID'] } }); +// createTd(tr, row['sAMAccountName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sAMAccountName'] } }); +// createTd(tr, row['sn'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['sn'] } }); +// createTd(tr, row['givenName'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['givenName'] } }); +// createTd(tr, row['mail'], { classes: [ 'text-align:left' ], attributes: { 'data-tooltip': row['mail'] } }); +// createTd(tr, row['active'], { classes: [ 'text-align:center' ] }); +// createTd(tr, row['online'], { classes: [ 'text-align:center' ] }); +// createTd(tr, row['RoleCount'], { classes: [ 'text-align:center' ] }); +// createTd(tr, row['GroupCount'], { classes: [ 'text-align:center' ] }); +// } +// }); -async function addPermissionToRole(roleId, permissionId) { - await api('/role/add-permission', 'POST', { - roleId, - permissionId - }); -} \ No newline at end of file +// async function api(url, method = 'GET', body) { +// const res = await fetch(url, { +// method, +// headers: { 'Content-Type': 'application/json' }, +// body: body ? JSON.stringify(body) : undefined +// }); + +// return res.json(); +// } + + +// async function showAuthDetails(guid) { +// const details = await api(`/api/rbac/auth/details/${guid}`) +// if(!details) return; +// feedbox({ +// title: `Details`, +// message: Object.entries(details).map(([key, value]) => /*html*/`

${key}:${value}

`).join(''), +// buttons: { +// yes: { +// text: 'Schließen' +// } +// }, +// lock: true, +// primary: 'yes' +// }) +// } + + +// async function createUser() { +// const name = document.getElementById('newUserName').value; +// if(!name || !name.includes('.')) { +// sendUserEvent('RBAC', 'Der sAMAccountName muss einen Punkt enthalten
Der Benutzer wurde nicht angelegt', null, 2); +// return; +// } +// const sn = name.split('.')[1]; +// const givenName = name.split('.')[0]; +// const mail = `${name}@test.com`; + +// const user = await api('/api/rbac/auth/create', 'POST', { +// sAMAccountName: name, +// mail: mail, +// sn: sn[0].toUpperCase() + sn.slice(1), +// givenName: givenName[0].toUpperCase() + givenName.slice(1) +// }); +// if(user) { +// sendUserEvent('RBAC', `Benutzer ${sn[0].toUpperCase() + sn.slice(1)}, ${givenName[0].toUpperCase() + givenName.slice(1)} angelegt`, null, 0); +// loadUsers(); +// } +// } + + +// async function deleteUser(guid, sAMAccountName) { +// feedbox({ +// title: `Benutzer löschen`, +// message: ` +//

Möchtest du den Benutzer ${sAMAccountName} [${guid}] wirklich löschen

+// `, +// buttons: { +// no: { +// text: 'Nein' +// }, +// yes: { +// text: 'Ja', +// onClick: async () => { +// const user = await api(`/api/rbac/auth/${guid}`, 'DELETE', { +// guid +// }); +// if(user) { +// loadUsers(); +// sendUserEvent('RBAC', `Benutzer ${user.sAMAccountName || ''} [${user.ObjectGUID}] gelöscht`, null, 0); +// } +// } +// } +// } +// }); +// } + + +// async function loadUsers() { +// try { +// const users = await api('/api/rbac/auth/get', 'POST'); +// if(users) { +// vt.source(users); +// return; +// } +// sendUserEvent('RBAC', 'Benutzer konnten nicht geladen', null, 4); +// } catch(err) { +// writeEventLog(4, 'RBAC', err); +// } +// } + + +// async function createGroup() { +// const name = document.getElementById('newGroupName').value; +// const group = await api('/api/rbac/group/create', 'POST', { +// name +// }); +// if(group) { +// sendUserEvent('RBAC', `Gruppe ${name} angelegt`, null, 0); +// loadGroups(); +// } +// } + + +// async function deleteGroup(guid, name) { +// feedbox({ +// title: `Gruppe löschen`, +// message: ` +//

Möchtest du die Gruppe ${name} [${guid}] wirklich löschen

+// `, +// buttons: { +// no: { +// text: 'Nein' +// }, +// yes: { +// text: 'Ja', +// onClick: async () => { +// const group = await api(`/api/rbac/group/${guid}`, 'DELETE', { +// guid +// }); +// if(group) { +// sendUserEvent('RBAC', `Gruppe ${group.Name || ''} [${group.ObjectGUID}] gelöscht`, null, 0); +// loadGroups(); +// } +// } +// } +// } +// }); +// // const name = document.getElementById('newGroupName').value; +// // const group = await api('/api/rbac/group/create', 'POST', { +// // name +// // }); +// // if(group) { +// // sendUserEvent('RBAC', `Gruppe ${name} angelegt`, null, 0); +// // loadGroups(); +// // } +// } + + +// // HIER WEITER - GRUPPEN KARTEN MÜSSEN HÜBSCHER WERDEN. +// // BENUTZER UND GRUPPEN KÖNNEN NOCH NICHT GELÖSCHT WERDEN. +// // GRUPPEN AUCH OBJECTSOURCE_ID 1? +// async function loadGroups() { +// try { +// const rbacGroupContainer = document.getElementById('rbacGroupContainer'); +// rbacGroupContainer.innerHTML = ''; +// const groups = await api('/api/rbac/group/get', 'POST'); +// if(groups) { +// let fragment = document.createDocumentFragment(); +// groups.forEach(group => { +// const section = document.createElement('section'); + +// createDropZone(section, 'group', group.ObjectGUID); + +// section.innerHTML = `${group.Name}
X
`; +// section.dataset.tooltip = group.Name; +// fragment.appendChild(section); +// }); +// rbacGroupContainer.innerHTML = ''; +// rbacGroupContainer.appendChild(fragment); +// return; +// } +// sendUserEvent('RBAC', 'Gruppen konnten nicht geladen', null, 4); +// } catch(err) { +// writeEventLog(4, 'RBAC', err); +// } +// } + +// loadUsers(); +// loadGroups(); + +// async function createRole() { +// const name = document.getElementById('newRoleName').value; + +// await api('/api/role', 'POST', { +// name +// }); + +// loadRoles(); +// } + +// async function loadRoles() { +// document.getElementById('roleList').innerHTML = 'Reload roles...'; +// } + + +// async function createPermission() { +// const scope = document.getElementById('permScope').value; +// const resource = document.getElementById('permResource').value; +// const action = document.getElementById('permAction').value; + +// await api('/permission', 'POST', { +// scope, +// resource, +// action +// }); + +// alert('Permission created'); +// } + + + +// function createDropZone(element, direction, keyAttribute) { +// element.addEventListener('dragover', (evt) => { +// evt.preventDefault(); +// }); + +// // 📥 Drop verarbeiten +// element.addEventListener('drop', (evt) => { +// evt.preventDefault(); +// const attributeValue = element.dataset[keyAttribute]; // ObjectGuid or ID + +// const data = JSON.parse( +// evt.dataTransfer.getData('application/json') +// ); +// switch (direction) { +// case 'user-to-group': +// addUserToGroup(data.guid, attributeValue); +// break; +// case 'user-to-role': +// addPermissionToRole(data.guid, attributeValue); +// break; +// case 'group-to-role': +// addGroupToRole(data.guid, attributeValue); +// break; +// case 'permission-to-role': +// addPermissionToRole(data.roleId, attributeValue) +// default: +// break; +// } +// }); +// } + + + +// async function addUserToGroup(authGuid, groupGuid) { +// await api('/api/rbac/group/add-user', 'POST', { +// authGuid, +// groupGuid +// }); +// } + +// async function addUserToRole(authGuid, roleId) { +// await api('/api/rbac/role/add-user', 'POST', { +// authGuid, +// roleId +// }); +// } + +// async function addGroupToRole(groupGuid, roleId) { +// await api('/api/rbac/role/add-group', 'POST', { +// groupGuid, +// roleId +// }); +// } + + +// async function addPermissionToRole(roleId, permissionId) { +// await api('/api/rbac/role/add-permission', 'POST', { +// roleId, +// permissionId +// }); +// } \ No newline at end of file diff --git a/public/javascript/loadOnce.js b/public/javascript/uiEvents.js similarity index 90% rename from public/javascript/loadOnce.js rename to public/javascript/uiEvents.js index ed9c33d..334baa4 100644 --- a/public/javascript/loadOnce.js +++ b/public/javascript/uiEvents.js @@ -41,11 +41,11 @@ function writeEventLog(levelId, pluginName, message) { // sendToParams: where clause to find objectGUIDs to send function sendUserEvent(pluginName, message, sendToParams, levelId = -1) { mainSocket.emit('event', { - objectGuid: getCookie('ObjectGUID'), - levelId: levelId, - pluginName: pluginName, - message: message.stack === undefined ? message : { message: message.message }, - sendToParams: sendToParams + objectGuid: getCookie('ObjectGUID'), + levelId: levelId, + pluginName: pluginName, + message: message.stack === undefined ? message : { message: message.message }, + sendToParams: sendToParams }); } diff --git a/public/styles/colors.css b/public/styles/colors.css index a8f18fe..88ebfba 100644 --- a/public/styles/colors.css +++ b/public/styles/colors.css @@ -5,11 +5,11 @@ #tutorial-tooltip img {filter: invert(1);} #start-btn { background:var(--theme-accent-default-backcolor); color:var(--theme-accent-default-color); } +#start-btn.active { background:var(--theme-accent-active-backcolor); color:var(--theme-accent-active-color); } #start-btn:hover { background:var(--theme-accent-hover-backcolor); color:var(--theme-accent-hover-color); } -#start-btn:active { background:var(--theme-accent-active-backcolor); color:var(--theme-accent-active-color); } /* #start-menu, .submenu { background:var(--theme-taskbar-backcolor); color:var(--theme-taskbar-color); } */ -#start-menu { padding-bottom:10px; background:var(--theme-startmenu-backcolor); color:var(--theme-startmenu-color); border:3px solid rgb(128,128,128); } +#start-menu { padding-bottom:10px; background:var(--theme-startmenu-backcolor); color:var(--theme-startmenu-color); border:1px solid rgb(10,10,10); } .start-submenu-head { background:var(--theme-startmenu-submenu-header-backcolor); color:var(--theme-startmenu-submenu-header-backcolor) } .start-icon { background:var(--theme-accent-default-backcolor); } diff --git a/public/styles/default.css b/public/styles/default.css index a75b940..ceb4a96 100644 --- a/public/styles/default.css +++ b/public/styles/default.css @@ -6,9 +6,10 @@ } -.selectable { user-select: text !important; cursor: var(--theme-cursor-pointer) 0 16, pointer; } +.selectable { user-select: text !important; cursor: var(--theme-cursor-default) 1 1, auto; } -input, input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="date"], textarea, select { border-width:2px; border-style:solid; font-size: var(--fontSize); font-family: var(--fontFamily); border-radius:10px; padding:10px 12px; outline:none; transition:all var(--times-transition-colors) ease; width:auto; } +/* input, input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="date"], textarea, select { border-width:2px; border-style:solid; font-size: var(--fontSize); font-family: var(--fontFamily); border-radius:10px; padding:10px 12px; outline:none; transition:all var(--times-transition-colors) ease; width:auto; } */ +input, input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="date"], textarea, select { border-width:2px; border-style:solid; font-size: var(--fontSize); font-family: var(--fontFamily); border-radius:10px; padding:4px 6px; outline:none; transition:all var(--times-transition-colors) ease; width:auto; } input.width\:100px { width:100px; } input.width\:90px { width:90px; } input.width\:75px { width:75px; } diff --git a/public/styles/os.css b/public/styles/os.css index f3b5f7c..0673c33 100644 --- a/public/styles/os.css +++ b/public/styles/os.css @@ -31,7 +31,7 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va .window-resize-se { bottom: -6px;right: -6px;cursor: var(--theme-cursor-resize-270); } .window-resize-sw { bottom: -6px;left: -6px;cursor: var(--theme-cursor-resize-45); } -#taskbar { z-index: 2; position: absolute; width:100%; bottom:0; left:0; height:auto; overflow:visible; display:flex; flex: 0 0 auto; min-width:0; align-items:center; padding:0 8px; box-sizing:border-box; } +#taskbar { z-index: 2; position: absolute; width:100%; bottom:0; left:0; height:var(--auto-taskbar-height); overflow:visible; display:flex; flex: 0 0 auto; min-width:0; align-items:center; padding:0 8px; box-sizing:border-box; } #start-btn { transition: background-color var(--times-transition-colors) ease; padding: 8px 12px; border-radius: 5px; border: none; margin-right:8px; } #taskbar-windows { display:flex; gap:6px; align-items:center; flex:1; overflow-y:hidden;overflow-x: auto; min-width: 0;scrollbar-width: thin; } .taskbar-item { display: flex; position: relative; padding:4px 10px; border-radius:4px; } @@ -70,7 +70,7 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va .start-item.has-submenu { position: relative; display: flex; flex-direction: column; padding-bottom: 8px; } .start-item.has-submenu > .submenu { width: 100%; list-style: none; padding-left: 2px; max-height: 0; overflow: hidden; transition: max-height var(--times-transition-transform) ease; margin: 2px 0 0 8px; } -.start-item-sys-container { position: relative; left:0; bottom: -8px; padding: 5px 0px; width:100%; display:flex; height:30px; flex-direction:row; justify-content:flex-end; } +.start-item-sys-container { position: relative; left:0; bottom: -10px; padding: 5px 0px; width:100%; display:flex; height:30px; flex-direction:row; justify-content:flex-end; } .start-sys-item { margin: 0 10px 0 8px !important; } .start-submenu-head { position: relative; margin-left: 16px; height:32px; display:flex; flex-direction: row; align-items: center; gap:8px; } @@ -123,6 +123,7 @@ img.icon { width: auto; height:20px; object-fit: contain; filter: var(--theme-no #taskbar { padding: 0 6px; overflow: hidden; + z-index: 9999; } #start-btn { diff --git a/public/views/desktop.hbs b/public/views/desktop.hbs index 7e7f99b..869d6ea 100644 --- a/public/views/desktop.hbs +++ b/public/views/desktop.hbs @@ -82,7 +82,7 @@
- +