Use checkbox permission editor
This commit is contained in:
+1
-1
@@ -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
@@ -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();
|
||||||
|
|||||||
@@ -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
@@ -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">
|
||||||
|
|||||||
Reference in New Issue
Block a user