Use checkbox permission editor

This commit is contained in:
Mikei386
2026-06-23 15:03:02 +02:00
parent 5f014911a5
commit a050531f63
6 changed files with 137 additions and 25 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
set -eu set -eu
PLUGIN="u-navigator" PLUGIN="u-navigator"
VERSION="2026.06.23.r039" VERSION="2026.06.23.r040"
PKG_DIR="packages" PKG_DIR="packages"
WORK_DIR=".plugin-build" WORK_DIR=".plugin-build"
PKG_NAME="${PLUGIN}-${VERSION}.tgz" PKG_NAME="${PLUGIN}-${VERSION}.tgz"
Binary file not shown.
Binary file not shown.
+96 -16
View File
@@ -1025,7 +1025,7 @@ function openPermissionsDialog(win, entry) {
state.permissionsDialog = { state.permissionsDialog = {
winId: win.id, winId: win.id,
entries, entries,
mode: entry.permissions ? `0${entry.permissions}`.slice(-4) : '0755', mode: normalizeMode(entry.permissions || '0755'),
owner: entry.owner || '', owner: entry.owner || '',
group: entry.group || '', group: entry.group || '',
recursive: false, recursive: false,
@@ -1048,15 +1048,13 @@ function renderPermissionsDialog() {
<p class="muted">${dialog.entries.length === 1 ? escapeHtml(dialog.entries[0].name) : `${dialog.entries.length} Objekte ausgewählt`}</p> <p class="muted">${dialog.entries.length === 1 ? escapeHtml(dialog.entries[0].name) : `${dialog.entries.length} Objekte ausgewählt`}</p>
<label class="check-row"> <label class="check-row">
<input type="checkbox" name="changeMode" checked> <input type="checkbox" name="changeMode" checked>
<span>Modus ändern</span> <span>Berechtigungen ändern</span>
</label>
${permissionMatrixMarkup(dialog.mode)}
<label class="field-row">
<span>Numerischer Wert</span>
<input name="mode" class="mode-input" inputmode="numeric" pattern="[0-7]{3,4}" value="${escapeAttr(dialog.mode)}" aria-label="Numerischer Wert">
</label> </label>
<input name="mode" inputmode="numeric" pattern="[0-7]{3,4}" value="${escapeAttr(dialog.mode)}" aria-label="Oktalmodus">
<div class="permission-presets" aria-label="Schnellauswahl">
<button type="button" data-mode="0644">0644</button>
<button type="button" data-mode="0755">0755</button>
<button type="button" data-mode="0775">0775</button>
<button type="button" data-mode="0777">0777</button>
</div>
<label class="check-row"> <label class="check-row">
<input type="checkbox" name="changeOwner"> <input type="checkbox" name="changeOwner">
<span>Besitzer ändern</span> <span>Besitzer ändern</span>
@@ -1094,13 +1092,7 @@ function renderPermissionsDialog() {
if (!input.disabled) input.focus(); if (!input.disabled) input.focus();
}); });
} }
for (const button of overlay.querySelectorAll('[data-mode]')) { bindPermissionMatrix(form);
button.addEventListener('click', () => {
form.elements.changeMode.checked = true;
form.elements.mode.disabled = false;
form.elements.mode.value = button.dataset.mode;
});
}
form.addEventListener('submit', (event) => { form.addEventListener('submit', (event) => {
event.preventDefault(); event.preventDefault();
applyPermissionsDialog(form).catch(showError); applyPermissionsDialog(form).catch(showError);
@@ -1108,6 +1100,94 @@ function renderPermissionsDialog() {
return overlay; return overlay;
} }
function permissionMatrixMarkup(mode) {
const digits = modeToDigits(mode);
const rows = [
['owner', 'Besitzer-Berechtigungen', digits[0]],
['group', 'Gruppen-Berechtigungen', digits[1]],
['public', 'Öffentliche Berechtigungen', digits[2]]
];
return rows.map(([scope, label, digit]) => `
<fieldset class="permission-scope" data-scope="${scope}">
<legend>${label}</legend>
${permissionCheck(scope, 'read', 'Lesen', digit & 4)}
${permissionCheck(scope, 'write', 'Schreiben', digit & 2)}
${permissionCheck(scope, 'execute', 'Ausführen', digit & 1)}
</fieldset>
`).join('');
}
function permissionCheck(scope, bit, label, checked) {
return `
<label>
<input type="checkbox" data-permission="${scope}.${bit}" ${checked ? 'checked' : ''}>
<span>${label}</span>
</label>
`;
}
function bindPermissionMatrix(form) {
const changeMode = form.elements.changeMode;
const modeInput = form.elements.mode;
const permissionInputs = [...form.querySelectorAll('[data-permission]')];
const setModeDisabled = () => {
modeInput.disabled = !changeMode.checked;
for (const input of permissionInputs) {
input.disabled = !changeMode.checked;
}
};
changeMode.addEventListener('change', setModeDisabled);
for (const input of permissionInputs) {
input.addEventListener('change', () => {
changeMode.checked = true;
setModeDisabled();
modeInput.value = modeFromPermissionInputs(form);
});
}
modeInput.addEventListener('input', () => {
changeMode.checked = true;
setModeDisabled();
syncPermissionInputsFromMode(form, modeInput.value);
});
setModeDisabled();
}
function modeFromPermissionInputs(form) {
const scopes = ['owner', 'group', 'public'];
const values = scopes.map((scope) => {
let value = 0;
if (form.querySelector(`[data-permission="${scope}.read"]`)?.checked) value += 4;
if (form.querySelector(`[data-permission="${scope}.write"]`)?.checked) value += 2;
if (form.querySelector(`[data-permission="${scope}.execute"]`)?.checked) value += 1;
return String(value);
});
return values.join('');
}
function syncPermissionInputsFromMode(form, mode) {
if (!/^[0-7]{3,4}$/.test(mode)) return;
const digits = modeToDigits(mode);
for (const [index, scope] of ['owner', 'group', 'public'].entries()) {
const digit = digits[index];
const map = { read: 4, write: 2, execute: 1 };
for (const [bit, value] of Object.entries(map)) {
const input = form.querySelector(`[data-permission="${scope}.${bit}"]`);
if (input) input.checked = Boolean(digit & value);
}
}
}
function normalizeMode(mode) {
const value = String(mode || '');
if (!/^[0-7]{3,4}$/.test(value)) return '755';
return value.length === 4 ? value.slice(1) : value;
}
function modeToDigits(mode) {
const normalized = normalizeMode(mode);
return normalized.split('').slice(-3).map((digit) => Number(digit));
}
function closePermissionsDialog() { function closePermissionsDialog() {
state.permissionsDialog = null; state.permissionsDialog = null;
render(); render();
+38 -6
View File
@@ -586,15 +586,47 @@
opacity: 0.5; opacity: 0.5;
} }
.u-nav .permission-presets { .u-nav .permission-scope {
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: 6px;
display: grid; display: grid;
gap: 6px; gap: 8px;
grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
margin: 0;
padding: 10px;
} }
.u-nav .permission-presets button { .u-nav .permission-scope legend {
min-width: 0; color: var(--text);
padding: 0 6px; font-size: 12px;
font-weight: 600;
padding: 0 4px;
}
.u-nav .permission-scope label {
align-items: center;
display: flex;
gap: 6px;
white-space: nowrap;
}
.u-nav .permission-scope input {
accent-color: var(--accent);
height: 17px;
margin: 0;
width: 17px;
}
.u-nav .field-row {
align-items: center;
display: grid;
gap: 8px;
grid-template-columns: max-content minmax(0, 1fr);
}
.u-nav .mode-input {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
} }
.u-nav .transfer-panel { .u-nav .transfer-panel {
+2 -2
View File
@@ -1,7 +1,7 @@
<!DOCTYPE PLUGIN [ <!DOCTYPE PLUGIN [
<!ENTITY name "u-navigator"> <!ENTITY name "u-navigator">
<!ENTITY author "Michael Roll"> <!ENTITY author "Michael Roll">
<!ENTITY version "2026.06.23.r039"> <!ENTITY version "2026.06.23.r040">
<!ENTITY package "&name;-&version;.tgz"> <!ENTITY package "&name;-&version;.tgz">
<!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg"> <!ENTITY pluginURL "https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/u-navigator.plg">
<!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator"> <!ENTITY support "https://git.casaderoll.de/michael/Unraid-Navigator">
@@ -24,7 +24,7 @@
<FILE Name="/boot/config/plugins/&name;/&package;"> <FILE Name="/boot/config/plugins/&name;/&package;">
<URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL> <URL>https://git.casaderoll.de/michael/Unraid-Navigator/raw/branch/main/packages/&package;</URL>
<MD5>bee22dc7b1842112f37fe2513fd413d0</MD5> <MD5>7c89a55e074a1345cf67197cfcdb54c7</MD5>
</FILE> </FILE>
<FILE Run="/bin/bash"> <FILE Run="/bin/bash">