styles and startmenuItems
This commit is contained in:
@@ -2,14 +2,17 @@
|
|||||||
createJsonTree({
|
createJsonTree({
|
||||||
container: domElement,
|
container: domElement,
|
||||||
data: jsonData,
|
data: jsonData,
|
||||||
|
expandInitially = true // optional: expand all nodes
|
||||||
onChange: (data) => { }, // optional: callback on change
|
onChange: (data) => { }, // optional: callback on change
|
||||||
onSave: (data) => { } // optional: if set, a save button will be added
|
onSave: (data) => { } // optional: if set, a save button will be added
|
||||||
});
|
});
|
||||||
*/function createJsonTree({
|
*/
|
||||||
|
function createJsonTree({
|
||||||
container,
|
container,
|
||||||
data,
|
data,
|
||||||
onChange = () => {},
|
onChange = () => {},
|
||||||
onSave = null
|
onSave = null,
|
||||||
|
expandInitially = true
|
||||||
}) {
|
}) {
|
||||||
container.innerHTML = "";
|
container.innerHTML = "";
|
||||||
container.classList.add("json-tree-root");
|
container.classList.add("json-tree-root");
|
||||||
@@ -19,6 +22,8 @@ createJsonTree({
|
|||||||
|
|
||||||
let lastSnapshot = clone(data);
|
let lastSnapshot = clone(data);
|
||||||
|
|
||||||
|
const expandedPaths = new Set();
|
||||||
|
|
||||||
function clone(obj) {
|
function clone(obj) {
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
@@ -104,34 +109,53 @@ createJsonTree({
|
|||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectPaths(obj, path = "root") {
|
||||||
|
if (typeof obj !== "object" || obj === null) return;
|
||||||
|
|
||||||
|
expandedPaths.add(path);
|
||||||
|
|
||||||
|
const entries = Array.isArray(obj)
|
||||||
|
? obj.map((v, i) => [i, v])
|
||||||
|
: Object.entries(obj);
|
||||||
|
|
||||||
|
for (const [k, v] of entries) {
|
||||||
|
collectPaths(v, `${path}.${k}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expandInitially) {
|
||||||
|
collectPaths(data);
|
||||||
|
}
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
container.innerHTML = "";
|
container.innerHTML = "";
|
||||||
|
|
||||||
const controls = document.createElement("div");
|
|
||||||
controls.className = "json-controls";
|
|
||||||
|
|
||||||
const undoBtn = document.createElement("button");
|
|
||||||
undoBtn.className = "monolyth";
|
|
||||||
undoBtn.textContent = "Undo";
|
|
||||||
undoBtn.onclick = undo;
|
|
||||||
|
|
||||||
const redoBtn = document.createElement("button");
|
|
||||||
redoBtn.className = "monolyth";
|
|
||||||
redoBtn.textContent = "Redo";
|
|
||||||
redoBtn.onclick = redo;
|
|
||||||
|
|
||||||
controls.appendChild(undoBtn);
|
|
||||||
controls.appendChild(redoBtn);
|
|
||||||
|
|
||||||
if (onSave) {
|
if (onSave) {
|
||||||
|
const controls = document.createElement("div");
|
||||||
|
controls.className = "json-controls";
|
||||||
|
|
||||||
|
const undoBtn = document.createElement("button");
|
||||||
|
undoBtn.className = "monolyth";
|
||||||
|
undoBtn.textContent = "Undo";
|
||||||
|
undoBtn.onclick = undo;
|
||||||
|
|
||||||
|
const redoBtn = document.createElement("button");
|
||||||
|
redoBtn.className = "monolyth";
|
||||||
|
redoBtn.textContent = "Redo";
|
||||||
|
redoBtn.onclick = redo;
|
||||||
|
|
||||||
const saveBtn = document.createElement("button");
|
const saveBtn = document.createElement("button");
|
||||||
saveBtn.className = "monolyth";
|
saveBtn.className = "monolyth";
|
||||||
saveBtn.textContent = "Save";
|
saveBtn.textContent = "Save";
|
||||||
saveBtn.onclick = save;
|
saveBtn.onclick = save;
|
||||||
|
|
||||||
|
controls.appendChild(undoBtn);
|
||||||
|
controls.appendChild(redoBtn);
|
||||||
controls.appendChild(saveBtn);
|
controls.appendChild(saveBtn);
|
||||||
|
|
||||||
|
container.appendChild(controls);
|
||||||
}
|
}
|
||||||
|
|
||||||
container.appendChild(controls);
|
|
||||||
container.appendChild(renderNode(data, null, null, "root", 0));
|
container.appendChild(renderNode(data, null, null, "root", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,10 +201,24 @@ createJsonTree({
|
|||||||
const children = document.createElement("div");
|
const children = document.createElement("div");
|
||||||
children.className = "json-children";
|
children.className = "json-children";
|
||||||
|
|
||||||
keyLabel.onclick = toggle.onclick = () => {
|
const toggleState = () => {
|
||||||
children.classList.toggle("collapsed");
|
if (expandedPaths.has(path)) {
|
||||||
|
expandedPaths.delete(path);
|
||||||
|
children.classList.add("collapsed");
|
||||||
|
} else {
|
||||||
|
expandedPaths.add(path);
|
||||||
|
children.classList.remove("collapsed");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
keyLabel.onclick = toggle.onclick = toggleState;
|
||||||
|
|
||||||
|
if (expandedPaths.has(path)) {
|
||||||
|
children.classList.remove("collapsed");
|
||||||
|
} else {
|
||||||
|
children.classList.add("collapsed");
|
||||||
|
}
|
||||||
|
|
||||||
addBtn.onclick = () => {
|
addBtn.onclick = () => {
|
||||||
let newKey = "";
|
let newKey = "";
|
||||||
let newValue;
|
let newValue;
|
||||||
@@ -210,7 +248,6 @@ createJsonTree({
|
|||||||
if (!isArray) {
|
if (!isArray) {
|
||||||
const input = document.querySelector('#newJsonKeyName');
|
const input = document.querySelector('#newJsonKeyName');
|
||||||
newKey = input?.value?.trim();
|
newKey = input?.value?.trim();
|
||||||
|
|
||||||
if (!newKey) return;
|
if (!newKey) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,6 +368,7 @@ createJsonTree({
|
|||||||
|
|
||||||
wrapper.appendChild(keySpan);
|
wrapper.appendChild(keySpan);
|
||||||
wrapper.appendChild(span);
|
wrapper.appendChild(span);
|
||||||
|
|
||||||
if (key !== null) wrapper.appendChild(removeBtn);
|
if (key !== null) wrapper.appendChild(removeBtn);
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
|
|||||||
@@ -14,16 +14,19 @@ body, html { margin:0; padding:0; height:100%; overflow: hidden; font-family: va
|
|||||||
.window-content { display: flex; flex-direction: column; flex:1; padding:8px; overflow: auto; }
|
.window-content { display: flex; flex-direction: column; flex:1; padding:8px; overflow: auto; }
|
||||||
.window[class="max"] .window-resize-handle { display: none; }
|
.window[class="max"] .window-resize-handle { display: none; }
|
||||||
|
|
||||||
.window-resize-n, .window-resize-s { position: absolute; left: 8px; right: 8px; height: 8px; z-index: 10; }
|
.window-resize-s { position: absolute; left: 8px; right: 8px; height: 8px; z-index: 10; }
|
||||||
|
.window-resize-n { position: absolute; left: 8px; right: 16px; height: 8px; z-index: 10; }
|
||||||
.window-resize-n { top: -4px;cursor: var(--theme-cursor-resize-vertical); }
|
.window-resize-n { top: -4px;cursor: var(--theme-cursor-resize-vertical); }
|
||||||
.window-resize-s { bottom: -4px;cursor: var(--theme-cursor-resize-vertical); }
|
.window-resize-s { bottom: -4px;cursor: var(--theme-cursor-resize-vertical); }
|
||||||
|
|
||||||
.window-resize-e, .window-resize-w { position: absolute;top: 8px;bottom: 8px;width: 8px;z-index: 10; }
|
.window-resize-w { position: absolute;top: 8px;bottom: 8px;width: 8px;z-index: 10; }
|
||||||
|
.window-resize-e { position: absolute;top: 16px;bottom: 8px;width: 8px;z-index: 10; }
|
||||||
.window-resize-e { right: -4px;cursor: var(--theme-cursor-resize-horizontal); }
|
.window-resize-e { right: -4px;cursor: var(--theme-cursor-resize-horizontal); }
|
||||||
.window-resize-w { left: -4px;cursor: var(--theme-cursor-resize-horizontal); }
|
.window-resize-w { left: -4px;cursor: var(--theme-cursor-resize-horizontal); }
|
||||||
|
|
||||||
.window-resize-ne, .window-resize-nw, .window-resize-se, .window-resize-sw { position: absolute;width: 16px;height: 16px;z-index: 11; }
|
.window-resize-nw, .window-resize-se, .window-resize-sw { position: absolute;width: 16px;height: 16px;z-index: 11; }
|
||||||
.window-resize-ne { top: -6px;right: -6px;cursor: var(--theme-cursor-resize-45); }
|
.window-resize-ne { position: absolute;width: 8px;height: 8x;z-index: 11; }
|
||||||
|
.window-resize-ne { top: -3px;right: -3px;cursor: var(--theme-cursor-resize-45); }
|
||||||
.window-resize-nw { top: -6px;left: -6px;cursor: var(--theme-cursor-resize-270); }
|
.window-resize-nw { top: -6px;left: -6px;cursor: var(--theme-cursor-resize-270); }
|
||||||
.window-resize-se { bottom: -6px;right: -6px;cursor: var(--theme-cursor-resize-270); }
|
.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); }
|
.window-resize-sw { bottom: -6px;left: -6px;cursor: var(--theme-cursor-resize-45); }
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ table.border * { border:1px solid white; }
|
|||||||
|
|
||||||
|
|
||||||
/* #region performance optimizing */
|
/* #region performance optimizing */
|
||||||
table thead { position:sticky; top:0; z-index:20; will-change:transform; transform:translateZ(0);}
|
table thead { position:sticky; top:0; z-index:20; }
|
||||||
|
|
||||||
|
|
||||||
/* #endregion */
|
/* #endregion */
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
<span class="copy-icon" onclick="copyToClipboard(`${document.getElementById('pid').textContent}`)">⧉</span>
|
<span class="copy-icon" onclick="copyToClipboard(`${document.getElementById('pid').textContent}`)">⧉</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card" styles="max-height:350px;">
|
||||||
<div class="table-wrapper" style="max-height: 350px;">
|
<div class="table-wrapper">
|
||||||
<table id="releaseNotes">
|
<table id="releaseNotes">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card">
|
<div class="card static" style="min-height:150px">
|
||||||
<div id="package" style="overflow:auto"></div>
|
<div id="package" style="overflow:auto"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -88,14 +88,13 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
vt.addData(json.releaseNotes)
|
vt.addData(json.releaseNotes)
|
||||||
document.querySelector('#package').innerHTML = JSON.stringify(json.package).split(',').join('<br>');
|
//document.querySelector('#package').innerHTML = JSON.stringify(json.package).split(',').join('<br>');
|
||||||
/*
|
|
||||||
createJsonTree({
|
createJsonTree({
|
||||||
container: document.getElementById("package-json"),
|
container: document.getElementById("package"),
|
||||||
data: json,
|
data: json.package,
|
||||||
expandInitially: true,
|
expandInitially: true
|
||||||
onSave: () => { }
|
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script defer type="text/javascript">
|
||||||
|
|
||||||
createNewPlugin.onclick = () => {
|
createNewPlugin.onclick = () => {
|
||||||
feedbox({
|
feedbox({
|
||||||
@@ -62,16 +62,42 @@
|
|||||||
"active":true
|
"active":true
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
fetch('/api/plugins/integrated', { method: 'POST' })
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
|
||||||
|
createTab(pluginTabs, 'Integrierte Plugins', { onclick:
|
||||||
|
async (tabElement) => {
|
||||||
|
pluginsTabWrapper.innerHTML = '';
|
||||||
|
|
||||||
|
const objectsContainer = document.createElement('div');
|
||||||
|
objectsContainer.className = 'card';
|
||||||
|
|
||||||
|
createJsonTree({
|
||||||
|
container: objectsContainer,
|
||||||
|
data: data,
|
||||||
|
expandInitially: true,
|
||||||
|
onSave: () => {}
|
||||||
|
})
|
||||||
|
pluginsTabWrapper.appendChild(objectsContainer);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fetch('/api/plugins/getAll', { method: 'POST' })
|
fetch('/api/plugins/getAll', { method: 'POST' })
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
data.forEach(async metaData => {
|
data.forEach(async metaData => {
|
||||||
createTab(pluginTabs, metaData.name, async (tabElement) => {
|
createTab(pluginTabs, metaData.name, { onclick:
|
||||||
|
async (tabElement) => {
|
||||||
pluginsTabWrapper.innerHTML = '';
|
pluginsTabWrapper.innerHTML = '';
|
||||||
|
|
||||||
const objectsContainer = document.createElement('div');
|
const objectsContainer = document.createElement('div');
|
||||||
objectsContainer.className = 'card grid';
|
objectsContainer.className = 'card static';
|
||||||
objectsContainer.style = ``;
|
objectsContainer.style = ``;
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'card static row';
|
card.className = 'card static row';
|
||||||
@@ -140,8 +166,8 @@
|
|||||||
|
|
||||||
if(typeof value === 'object') {
|
if(typeof value === 'object') {
|
||||||
const objectCard = document.createElement('div');
|
const objectCard = document.createElement('div');
|
||||||
objectCard.className = 'card';
|
objectCard.className = 'card static';
|
||||||
objectCard.style = `height:100%`;
|
objectCard.style = `min-height:400px;`;
|
||||||
objectCard.innerHTML = `
|
objectCard.innerHTML = `
|
||||||
<label style="font-weight:bold">${key}</label>
|
<label style="font-weight:bold">${key}</label>
|
||||||
<div style="overflow:auto" id="${metaData.name}-${key}"></div>
|
<div style="overflow:auto" id="${metaData.name}-${key}"></div>
|
||||||
@@ -151,9 +177,6 @@
|
|||||||
const menu = createJsonTree({
|
const menu = createJsonTree({
|
||||||
container: document.getElementById(`${metaData.name}-${key}`),
|
container: document.getElementById(`${metaData.name}-${key}`),
|
||||||
data: metaData[key],
|
data: metaData[key],
|
||||||
schema: {
|
|
||||||
"active": { type: "boolean" }
|
|
||||||
},
|
|
||||||
onChange: (data) => { },
|
onChange: (data) => { },
|
||||||
onSave: async (data) => {
|
onSave: async (data) => {
|
||||||
await pluginAPI.update(metaData.name, { [key]: data });
|
await pluginAPI.update(metaData.name, { [key]: data });
|
||||||
@@ -163,80 +186,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
pluginsTabWrapper.appendChild(objectsContainer);
|
pluginsTabWrapper.appendChild(objectsContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*pluginsTabWrapper.innerHTML = `
|
|
||||||
<div class="card static row" style="grid-column: span 3;height:50px;">
|
|
||||||
<label class="cb cb-modern">
|
|
||||||
<label for="plugin-${plugin.name}" style="font-weight:bold">Aktiv</label>
|
|
||||||
<input onchange="pluginAPI.activation('${plugin.name}', this.checked);" id="plugin-${plugin.name}" data-status="${plugin.name}" type="checkbox" ${plugin.active ? 'checked' : ''}>
|
|
||||||
<span class="cb-box" aria-hidden="true"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card static row" style="gap:0 10px;min-height:fit-content;">
|
|
||||||
<div style="flex:0 0 auto">
|
|
||||||
<label style="font-weight:bold">Name</label>
|
|
||||||
<input type="text" data-name="${plugin.name}" data-field="name" value="${plugin.name}" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="flex:0 0 auto">
|
|
||||||
<label style="font-weight:bold">Version</label>
|
|
||||||
<input type="text" data-name="${plugin.name}" data-field="version" value="${plugin.version}" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="flex:1 1 auto">
|
|
||||||
<label style="font-weight:bold">Beschreibung</label>
|
|
||||||
<input type="text" data-name="${plugin.name}" data-field="description" style="width:100%" value="${plugin.description}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card static" style="grid-column: span 3;">
|
|
||||||
<label style="font-weight:bold">Startmenüeinträge</label>
|
|
||||||
<div style="overflow:auto" id="${plugin.name}-menu"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
createJsonTree({
|
|
||||||
container: document.getElementById(`${plugin.name}-menu`),
|
|
||||||
data: plugin,
|
|
||||||
schema: {
|
|
||||||
"active": { type: "boolean" }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"name": "TEST",
|
|
||||||
"description": "Remote Desktop Verbindungen über MSRA realisieren",
|
|
||||||
"version": "0.2025.11.07",
|
|
||||||
"config": {},
|
|
||||||
"menu": {
|
|
||||||
"label": "TEST",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"label": "Remoteunterstützung",
|
|
||||||
"view": "index",
|
|
||||||
"icon": "remoteServer.png",
|
|
||||||
"permissions": [
|
|
||||||
"Administration"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"active": true
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
new AttachOnBlurChange(document.querySelector(`[data-name="${plugin.name}"][data-field="name"]`), async (newValue, oldValue) => { await pluginAPI.rename(`${plugin.name}`, newValue); });
|
|
||||||
new AttachOnBlurChange(document.querySelector(`[data-name="${plugin.name}"][data-field="version"]`), async (newValue, oldValue) => await pluginAPI.update(`${plugin.name}`, { version: newValue }))
|
|
||||||
new AttachOnBlurChange(document.querySelector(`[data-name="${plugin.name}"][data-field="description"]`), async (newValue, oldValue) => await pluginAPI.update(`${plugin.name}`, { description: newValue }))
|
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
pluginTabs.querySelectorAll('.tab')[0]?.click();
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
@@ -48,7 +48,7 @@ module.exports = ([
|
|||||||
{
|
{
|
||||||
label: 'Plugins',
|
label: 'Plugins',
|
||||||
view: 'plugindashboard',
|
view: 'plugindashboard',
|
||||||
defaultSize: { width: "1000px", height: "400px" },
|
defaultSize: { width: "900px", height: "800px" },
|
||||||
icon: "plugins.png",
|
icon: "plugins.png",
|
||||||
permissions: [ 'Administration' ]
|
permissions: [ 'Administration' ]
|
||||||
}
|
}
|
||||||
154
src/models/integratedStartMenuItems.json
Normal file
154
src/models/integratedStartMenuItems.json
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"section": "System",
|
||||||
|
"name": "Server",
|
||||||
|
"active": true,
|
||||||
|
"menu": {
|
||||||
|
"label": "Server",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "Styles",
|
||||||
|
"view": "styleconfig",
|
||||||
|
"icon": "brush.png",
|
||||||
|
"permissions": [
|
||||||
|
"Administration"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Configs",
|
||||||
|
"view": "serverconfig",
|
||||||
|
"icon": "app.png",
|
||||||
|
"permissions": [
|
||||||
|
"Administration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"defaultSize": {
|
||||||
|
"width": 800,
|
||||||
|
"height": 600
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "System",
|
||||||
|
"name": "EventLog",
|
||||||
|
"active": true,
|
||||||
|
"menu": {
|
||||||
|
"label": "EventLog",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "EventLog",
|
||||||
|
"view": "eventlog",
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "1200px",
|
||||||
|
"height": "1200px"
|
||||||
|
},
|
||||||
|
"icon": "eventlog.ico",
|
||||||
|
"permissions": [
|
||||||
|
"Administration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "1200px",
|
||||||
|
"height": "1200px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "System",
|
||||||
|
"name": "Plugins",
|
||||||
|
"active": true,
|
||||||
|
"menu": {
|
||||||
|
"label": "Plugins",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "Plugins",
|
||||||
|
"view": "plugindashboard",
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "900px",
|
||||||
|
"height": "800px"
|
||||||
|
},
|
||||||
|
"icon": "plugins.png",
|
||||||
|
"permissions": [
|
||||||
|
"Administration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "900px",
|
||||||
|
"height": "800px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "System",
|
||||||
|
"name": "Info",
|
||||||
|
"active": true,
|
||||||
|
"menu": {
|
||||||
|
"label": "Info",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "Info",
|
||||||
|
"view": "serverinfo",
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "900px",
|
||||||
|
"height": "500px"
|
||||||
|
},
|
||||||
|
"icon": "serverinfo.png",
|
||||||
|
"permissions": [
|
||||||
|
"Administration"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "900px",
|
||||||
|
"height": "500px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "Benutzer",
|
||||||
|
"name": "Einstellungen",
|
||||||
|
"active": true,
|
||||||
|
"menu": {
|
||||||
|
"label": "Einstellungen",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "Einstellungen",
|
||||||
|
"view": "usersettings",
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "460px",
|
||||||
|
"height": "515px"
|
||||||
|
},
|
||||||
|
"icon": "app.png",
|
||||||
|
"permissions": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"defaultSize": {
|
||||||
|
"width": "460px",
|
||||||
|
"height": "515px"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "Benutzer",
|
||||||
|
"name": "Hilfe",
|
||||||
|
"active": true,
|
||||||
|
"menu": {
|
||||||
|
"label": "Hilfe",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"label": "Hilfe",
|
||||||
|
"view": "help",
|
||||||
|
"icon": "help.png",
|
||||||
|
"permissions": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -122,6 +122,13 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/api/plugins/integrated', async (req, res) => {
|
||||||
|
try {
|
||||||
|
res.status(200).json(global.json.startMenuItems.live);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/api/plugins/:name/update', async (req, res) => {
|
app.post('/api/plugins/:name/update', async (req, res) => {
|
||||||
const { name } = req.params;
|
const { name } = req.params;
|
||||||
|
|||||||
@@ -31,132 +31,131 @@ class FileSystemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Liest rekursiv Dateien und gibt nur die gewünschten Attribute zurück.
|
* Liest rekursiv Dateien und gibt nur die gewünschten Attribute zurück.
|
||||||
*
|
*
|
||||||
* @param {string} dirPath - Startverzeichnis
|
* @param {string} dirPath - Startverzeichnis
|
||||||
* @param {string[]} attributes - gewünschte Attribute:
|
* @param {string[]} attributes - gewünschte Attribute:
|
||||||
* ['name','fullPath','size','lastModified','isDirectory','extension', ...]
|
* ['name','fullPath','size','lastModified','isDirectory','extension', ...]
|
||||||
* @param {string|null} sortBy - Attribut zum Sortieren (z.B. 'lastModified' oder 'name')
|
* @param {string|null} sortBy - Attribut zum Sortieren (z.B. 'lastModified' oder 'name')
|
||||||
* @param {string} order - 'asc' oder 'desc'
|
* @param {string} order - 'asc' oder 'desc'
|
||||||
*/
|
*/
|
||||||
readFiles(dirPath, attributes = ['name', 'fullPath'], sortBy = null, order = 'asc') {
|
readFiles(dirPath, attributes = ['name', 'fullPath'], sortBy = null, order = 'asc') {
|
||||||
let results = [];
|
let results = [];
|
||||||
|
|
||||||
const items = fs.readdirSync(dirPath);
|
const items = fs.readdirSync(dirPath);
|
||||||
|
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const fullPath = path.join(dirPath, item);
|
const fullPath = path.join(dirPath, item);
|
||||||
const stats = fs.statSync(fullPath);
|
const stats = fs.statSync(fullPath);
|
||||||
const isDir = stats.isDirectory();
|
const isDir = stats.isDirectory();
|
||||||
|
|
||||||
// Objekt mit ALLEN möglichen Infos
|
// Objekt mit ALLEN möglichen Infos
|
||||||
const allInfo = {
|
const allInfo = {
|
||||||
name: item,
|
name: item,
|
||||||
nameWithoutExt: item.substring(0, item.indexOf('.')),
|
nameWithoutExt: item.substring(0, item.indexOf('.')),
|
||||||
fullPath: fullPath,
|
fullPath: fullPath,
|
||||||
size: stats.size,
|
size: stats.size,
|
||||||
lastModified: stats.mtime,
|
lastModified: stats.mtime,
|
||||||
created: stats.birthtime,
|
created: stats.birthtime,
|
||||||
isDirectory: isDir,
|
isDirectory: isDir,
|
||||||
extension: isDir ? null : path.extname(item)
|
extension: isDir ? null : path.extname(item)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isDir) {
|
if (isDir) {
|
||||||
// rekursiv weitermachen
|
// rekursiv weitermachen
|
||||||
results = results.concat(this.readFiles(fullPath, attributes, null, order));
|
results = results.concat(this.readFiles(fullPath, attributes, null, order));
|
||||||
} else {
|
} else {
|
||||||
// nur gewünschte Attribute ausgeben
|
// nur gewünschte Attribute ausgeben
|
||||||
const filtered = {};
|
const filtered = {};
|
||||||
for (const attr of attributes) {
|
for (const attr of attributes) {
|
||||||
if (allInfo[attr] !== undefined) {
|
if (allInfo[attr] !== undefined) {
|
||||||
filtered[attr] = allInfo[attr];
|
filtered[attr] = allInfo[attr];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
results.push(filtered);
|
||||||
}
|
}
|
||||||
results.push(filtered);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sortieren, falls gewünscht
|
||||||
|
if (sortBy) {
|
||||||
|
results.sort((a, b) => {
|
||||||
|
if (a[sortBy] < b[sortBy]) return order === 'asc' ? -1 : 1;
|
||||||
|
if (a[sortBy] > b[sortBy]) return order === 'asc' ? 1 : -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sortieren, falls gewünscht
|
|
||||||
if (sortBy) {
|
|
||||||
results.sort((a, b) => {
|
|
||||||
if (a[sortBy] < b[sortBy]) return order === 'asc' ? -1 : 1;
|
|
||||||
if (a[sortBy] > b[sortBy]) return order === 'asc' ? 1 : -1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sammelt verschiedene Pattern-Ergebnisse aus Dateien mehrerer Ordner.
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {string|string[]} options.folderPaths - Pfad oder Array von Pfaden zu Ordnern
|
||||||
|
* @param {string} [options.extension='.js'] - Dateiendung
|
||||||
|
* @param {Array<{ name: string, pattern: RegExp, mapFn?: Function }>} options.patterns - Liste von Pattern-Definitionen
|
||||||
|
* @param {boolean} [options.recursive=true] - Unterordner durchsuchen?
|
||||||
|
* @returns {Array} - kombinierte Ergebnisse aller Pattern
|
||||||
|
*/
|
||||||
|
collectFromFiles({
|
||||||
|
folderPaths,
|
||||||
|
extension = '.js',
|
||||||
|
patterns,
|
||||||
|
recursive = true
|
||||||
|
}) {
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
// if only one path selected
|
||||||
|
const paths = Array.isArray(folderPaths) ? folderPaths : [folderPaths];
|
||||||
|
|
||||||
|
function readDir(dir) {
|
||||||
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
|
||||||
/**
|
if (entry.isDirectory() && recursive) {
|
||||||
* Sammelt verschiedene Pattern-Ergebnisse aus Dateien mehrerer Ordner.
|
readDir(fullPath);
|
||||||
*
|
} else if (entry.isFile() && entry.name.endsWith(extension)) {
|
||||||
* @param {Object} options
|
const content = fs.readFileSync(fullPath, 'utf8');
|
||||||
* @param {string|string[]} options.folderPaths - Pfad oder Array von Pfaden zu Ordnern
|
|
||||||
* @param {string} [options.extension='.js'] - Dateiendung
|
|
||||||
* @param {Array<{ name: string, pattern: RegExp, mapFn?: Function }>} options.patterns - Liste von Pattern-Definitionen
|
|
||||||
* @param {boolean} [options.recursive=true] - Unterordner durchsuchen?
|
|
||||||
* @returns {Array} - kombinierte Ergebnisse aller Pattern
|
|
||||||
*/
|
|
||||||
collectFromFiles({
|
|
||||||
folderPaths,
|
|
||||||
extension = '.js',
|
|
||||||
patterns,
|
|
||||||
recursive = true
|
|
||||||
}) {
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
// if only one path selected
|
// 👉 NEU: fallback wenn keine patterns
|
||||||
const paths = Array.isArray(folderPaths) ? folderPaths : [folderPaths];
|
if (!patterns || patterns.length === 0) {
|
||||||
|
results.push({
|
||||||
|
file: entry.name,
|
||||||
|
fullPath
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
function readDir(dir) {
|
for (const { name, pattern, mapFn } of patterns) {
|
||||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
let match;
|
||||||
|
while ((match = pattern.exec(content)) !== null) {
|
||||||
for (const entry of entries) {
|
const mapped = mapFn
|
||||||
const fullPath = path.join(dir, entry.name);
|
? mapFn(match, entry.name, fullPath, name)
|
||||||
|
: { file: entry.name, type: name, match: match[0] };
|
||||||
if (entry.isDirectory() && recursive) {
|
results.push(mapped);
|
||||||
readDir(fullPath);
|
}
|
||||||
} else if (entry.isFile() && entry.name.endsWith(extension)) {
|
|
||||||
const content = fs.readFileSync(fullPath, 'utf8');
|
|
||||||
|
|
||||||
// 👉 NEU: fallback wenn keine patterns
|
|
||||||
if (!patterns || patterns.length === 0) {
|
|
||||||
results.push({
|
|
||||||
file: entry.name,
|
|
||||||
fullPath
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { name, pattern, mapFn } of patterns) {
|
|
||||||
let match;
|
|
||||||
while ((match = pattern.exec(content)) !== null) {
|
|
||||||
const mapped = mapFn
|
|
||||||
? mapFn(match, entry.name, fullPath, name)
|
|
||||||
: { file: entry.name, type: name, match: match[0] };
|
|
||||||
results.push(mapped);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Run through multiple paths
|
// Run through multiple paths
|
||||||
for (const dir of paths) {
|
for (const dir of paths) {
|
||||||
if (fs.existsSync(dir)) {
|
if (fs.existsSync(dir)) {
|
||||||
readDir(path.resolve(dir));
|
readDir(path.resolve(dir));
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Ordner nicht gefunden: ${dir}`);
|
console.warn(`Ordner nicht gefunden: ${dir}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -171,6 +170,16 @@ collectFromFiles({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadFile(path) {
|
||||||
|
try {
|
||||||
|
const rawData = fs.readFileSync(path, 'utf8');
|
||||||
|
return rawData;
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Check file-/path
|
// Check file-/path
|
||||||
exists(path) {
|
exists(path) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const startMenuItemContext = require('@models/integratedStartmenuItems.js')
|
|
||||||
|
|
||||||
module.exports = (app, socketManager, namespace, pluginManager, authenticationModel, fileSystemManager, eventManager, activeDirectory) => {
|
module.exports = (app, socketManager, namespace, pluginManager, authenticationModel, fileSystemManager, eventManager, activeDirectory) => {
|
||||||
const mainSocket = socketManager.namespaces.get(namespace);
|
const mainSocket = socketManager.namespaces.get(namespace);
|
||||||
|
|||||||
82
utils.js
82
utils.js
@@ -4,7 +4,7 @@ const { permission } = require('process');
|
|||||||
const { dirname } = require('path');
|
const { dirname } = require('path');
|
||||||
const { File: HotReload } = require(`@services/hotReload.js`);
|
const { File: HotReload } = require(`@services/hotReload.js`);
|
||||||
const { service } = require(`@root/server.js`);
|
const { service } = require(`@root/server.js`);
|
||||||
let integratedStartmenuItems = require('@models/integratedStartmenuItems');
|
// let integratedStartmenuItems = require('@models/integratedStartmenuItems');
|
||||||
|
|
||||||
|
|
||||||
global.path = {
|
global.path = {
|
||||||
@@ -20,17 +20,17 @@ global.json = {
|
|||||||
configuration: new HotReload(path.join(global.path.source, 'models', 'configuration.json')),
|
configuration: new HotReload(path.join(global.path.source, 'models', 'configuration.json')),
|
||||||
stylesheet: new HotReload(path.join(global.path.source, 'models', 'stylesheet.json')),
|
stylesheet: new HotReload(path.join(global.path.source, 'models', 'stylesheet.json')),
|
||||||
indexRoutes: new HotReload(path.join(global.path.source, 'routes', 'indexRoutes.js'), { historyLimit: 50, fileType: 'js' }),
|
indexRoutes: new HotReload(path.join(global.path.source, 'routes', 'indexRoutes.js'), { historyLimit: 50, fileType: 'js' }),
|
||||||
startMenuItems: new HotReload(path.join(global.path.source, 'models', 'integratedStartmenuItems.js'), { historyLimit: 50, fileType: 'js' })
|
startMenuItems: new HotReload(path.join(global.path.source, 'models', 'integratedStartMenuItems.json'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = startMenuItems = async function(app, sAMAccountName) {
|
module.exports = startMenuItems = async function (app, sAMAccountName) {
|
||||||
function safeClone(obj) {
|
|
||||||
return JSON.parse(JSON.stringify(obj));
|
|
||||||
}
|
|
||||||
delete integratedStartmenuItems;
|
|
||||||
|
|
||||||
integratedStartmenuItems = safeClone(json.startMenuItems.live);
|
function safeClone(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
const integratedStartmenuItems = safeClone(global.json.startMenuItems.live);
|
||||||
|
|
||||||
const plugins = service
|
const plugins = service
|
||||||
.get('pluginManager')
|
.get('pluginManager')
|
||||||
@@ -50,13 +50,18 @@ function safeClone(obj) {
|
|||||||
const authorized =
|
const authorized =
|
||||||
item.label === 'hr' ||
|
item.label === 'hr' ||
|
||||||
item.permissions.includes('Administration')
|
item.permissions.includes('Administration')
|
||||||
? global.json.configuration.live.administration.some(name => name.toLowerCase() === sAMAccountName.toLowerCase())
|
? global.json.configuration.live.administration.some(
|
||||||
|
name => name.toLowerCase() === sAMAccountName.toLowerCase()
|
||||||
|
)
|
||||||
: item.permissions.includes('*') ||
|
: item.permissions.includes('*') ||
|
||||||
(
|
(
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
item.permissions.map(async permission =>
|
item.permissions.map(async permission =>
|
||||||
(await service.get('activeDirectoryManager').getGroup(permission)) &&
|
(await service.get('activeDirectoryManager').getGroup(permission)) &&
|
||||||
(await service.get('activeDirectoryManager').isUserMemberOfRecursive(sAMAccountName, permission))
|
(await service.get('activeDirectoryManager').isUserMemberOfRecursive(
|
||||||
|
sAMAccountName,
|
||||||
|
permission
|
||||||
|
))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).some(Boolean);
|
).some(Boolean);
|
||||||
@@ -81,6 +86,63 @@ function safeClone(obj) {
|
|||||||
|
|
||||||
return [...getAllPlugins];
|
return [...getAllPlugins];
|
||||||
};
|
};
|
||||||
|
// module.exports = startMenuItems = async function(app, sAMAccountName) {
|
||||||
|
// function safeClone(obj) {
|
||||||
|
// return JSON.parse(JSON.stringify(obj));
|
||||||
|
// }
|
||||||
|
// delete integratedStartmenuItems;
|
||||||
|
|
||||||
|
// integratedStartmenuItems = safeClone(json.startMenuItems.live);
|
||||||
|
|
||||||
|
// const plugins = service
|
||||||
|
// .get('pluginManager')
|
||||||
|
// .getStatus()
|
||||||
|
// .map(({ config, ...plugin }) => ({
|
||||||
|
// ...safeClone(plugin),
|
||||||
|
// section: 'Plugin'
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// let getAllPlugins = [...plugins, ...integratedStartmenuItems];
|
||||||
|
|
||||||
|
// for (const plugin of getAllPlugins) {
|
||||||
|
|
||||||
|
// plugin.menu.items = await Promise.all(
|
||||||
|
// (plugin.menu.items || []).map(async item => {
|
||||||
|
|
||||||
|
// const authorized =
|
||||||
|
// item.label === 'hr' ||
|
||||||
|
// item.permissions.includes('Administration')
|
||||||
|
// ? global.json.configuration.live.administration.some(name => name.toLowerCase() === sAMAccountName.toLowerCase())
|
||||||
|
// : item.permissions.includes('*') ||
|
||||||
|
// (
|
||||||
|
// await Promise.all(
|
||||||
|
// item.permissions.map(async permission =>
|
||||||
|
// (await service.get('activeDirectoryManager').getGroup(permission)) &&
|
||||||
|
// (await service.get('activeDirectoryManager').isUserMemberOfRecursive(sAMAccountName, permission))
|
||||||
|
// )
|
||||||
|
// )
|
||||||
|
// ).some(Boolean);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...safeClone(item),
|
||||||
|
// authorized
|
||||||
|
// };
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
|
||||||
|
// plugin.onlyAdministration =
|
||||||
|
// plugin.menu.items.every(item => !item.authorized) &&
|
||||||
|
// !global.json.configuration.live.administration.includes(sAMAccountName);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// getAllPlugins = getAllPlugins
|
||||||
|
// .filter(plugin => !plugin.onlyAdministration)
|
||||||
|
// .filter(plugin => plugin.active);
|
||||||
|
|
||||||
|
// app.locals.startMenuItems = getAllPlugins;
|
||||||
|
|
||||||
|
// return [...getAllPlugins];
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user