diff --git a/navigator/src/components/DirectoryEntry.vue b/navigator/src/components/DirectoryEntry.vue
index 01edff3..270a62a 100644
--- a/navigator/src/components/DirectoryEntry.vue
+++ b/navigator/src/components/DirectoryEntry.vue
@@ -80,7 +80,7 @@
{{ entry.path.split('/').slice(-1 * (level + 1)).join('/') }}:
{{ entry.name }}:
{{ entry.mode.toString(8) }}, {{ entry.owner }}:{{ entry.group }}, {{ entry.sizeHuman }},
- modified: {{ entry.mtime }}
+ modified: {{ entry.mtimeStr }}
@@ -121,10 +121,8 @@ export default {
const doubleClickCallback = () => {
if (props.entry.resolvedType === 'd') {
emit('browserAction', 'cd', props.entry);
- } else if (props.entry.resolvedType === 'f') {
- emit('browserAction', 'openFilePrompt', props.entry);
} else {
- notifications.value.constructNotification('Cannot open or download file', `${props.entry.nameHTML} is a ${props.entry.resolvedTypeHuman}`, 'denied');
+ emit('browserAction', 'openFilePrompt', props.entry);
}
}
diff --git a/navigator/src/components/FileModeMatrix.vue b/navigator/src/components/FileModeMatrix.vue
new file mode 100644
index 0000000..91577ae
--- /dev/null
+++ b/navigator/src/components/FileModeMatrix.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ modeStr }}
+
+
+
+
diff --git a/navigator/src/components/FilePermissions.vue b/navigator/src/components/FilePermissions.vue
new file mode 100644
index 0000000..200906e
--- /dev/null
+++ b/navigator/src/components/FilePermissions.vue
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/navigator/src/functions/getGroups.js b/navigator/src/functions/getGroups.js
new file mode 100644
index 0000000..750acae
--- /dev/null
+++ b/navigator/src/functions/getGroups.js
@@ -0,0 +1,24 @@
+import { useSpawn } from "@45drives/cockpit-helpers";
+
+export async function getGroups() {
+ let groups = [];
+ const groupDB = (await useSpawn(['getent', 'group'], { superuser: 'try' }).promise()).stdout;
+ groupDB.split('\n').forEach((record) => {
+ const fields = record.split(':');
+ const group = fields[0];
+ const gid = fields[2];
+ if (gid >= 1000 || gid === '0')
+ groups.push({ group: group, domain: false, pretty: group });
+ })
+ try {
+ await useSpawn(['realm', 'list'], { superuser: 'try' }).promise(); // throws if not domain
+ const domainGroupsDB = (await useSpawn(['wbinfo', '-g'], { superuser: 'try' }).promise()).stdout
+ domainGroupsDB.split('\n').forEach((record) => {
+ if (/^\s*$/.test(record))
+ return;
+ groups.push({ group: record.replace(/^[^\\]+\\/, ""), domain: true, pretty: record.replace(/^[^\\]+\\/, "") + " (domain)" });
+ })
+ } catch {}
+ groups.sort((a, b) => a.pretty.localeCompare(b.pretty));
+ return groups
+}
diff --git a/navigator/src/functions/getUsers.js b/navigator/src/functions/getUsers.js
new file mode 100644
index 0000000..00edcb6
--- /dev/null
+++ b/navigator/src/functions/getUsers.js
@@ -0,0 +1,24 @@
+import { useSpawn } from "@45drives/cockpit-helpers";
+
+export async function getUsers() {
+ let users = [];
+ const passwdDB = (await useSpawn(['getent', 'passwd'], { superuser: 'try' }).promise()).stdout;
+ passwdDB.split('\n').forEach((record) => {
+ const fields = record.split(':');
+ const user = fields[0];
+ const uid = fields[2];
+ if (uid >= 1000 || uid === '0') // include root
+ users.push({ user: user, domain: false, pretty: user });
+ })
+ try {
+ await useSpawn(['realm', 'list'], { superuser: 'try' }).promise(); // throws if not domain
+ const domainUsersDB = (await useSpawn(['wbinfo', '-u'], { superuser: 'try' }).promise()).stdout;
+ domainUsersDB.split('\n').forEach((record) => {
+ if (/^\s*$/.test(record))
+ return;
+ users.push({ user: record.replace(/^[^\\]+\\/, ""), domain: true, pretty: record.replace(/^[^\\]+\\/, "") + " (domain)" });
+ })
+ } catch {}
+ users.sort((a, b) => a.pretty.localeCompare(b.pretty));
+ return users;
+}
diff --git a/navigator/src/views/Browser.vue b/navigator/src/views/Browser.vue
index 9c5ade2..70b9720 100644
--- a/navigator/src/views/Browser.vue
+++ b/navigator/src/views/Browser.vue
@@ -68,25 +68,31 @@
cd({ path })" @edit="openEditor"
- @browserAction="handleAction"
- ref="directoryViewRef" />
+ @browserAction="handleAction" ref="directoryViewRef" />
- openFilePromptModal.close()">
- What would you like to do with this file?
+ openFilePromptModal.close()" autoWidth>
+ What would you like to do with this {{ openFilePromptModal.entry?.resolvedTypeHuman }}?
-
+
@@ -113,6 +119,7 @@ import { ArrowLeftIcon, ArrowRightIcon, ArrowUpIcon, RefreshIcon, ChevronDownIco
import IconToggle from '../components/IconToggle.vue';
import ModalPopup from '../components/ModalPopup.vue';
import { fileDownload } from '@45drives/cockpit-helpers';
+import FilePermissions from '../components/FilePermissions.vue';
const encodePartial = (string) =>
encodeURIComponent(string)
@@ -179,6 +186,20 @@ export default {
openFilePromptModal.close();
}
});
+ const filePermissions = reactive({
+ show: false,
+ entry: null,
+ resetTimeoutHandle: null,
+ open: (entry) => {
+ clearTimeout(filePermissions.resetTimeoutHandle);
+ filePermissions.entry = entry;
+ filePermissions.show = true;
+ },
+ close: () => {
+ filePermissions.show = false;
+ filePermissions.resetTimeoutHandle = setTimeout(() => filePermissions.resetTimeoutHandle = filePermissions.entry = null, 500);
+ },
+ });
const cd = ({ path, host }) => {
const newHost = host ?? (pathHistory.current().host);
@@ -195,7 +216,7 @@ export default {
}
const up = () => {
- cd({path: pathHistory.current().path + '/..'});
+ cd({ path: pathHistory.current().path + '/..' });
}
const openEditor = ({ path, host }) => {
@@ -213,6 +234,10 @@ export default {
openFilePromptModal.open(entry);
}
+ const openFilePermissions = (entry) => {
+ filePermissions.open(entry);
+ }
+
const getSelected = () => directoryViewRef.value?.getSelected?.() ?? [];
const handleAction = (action, ...args) => {
@@ -223,6 +248,9 @@ export default {
case 'edit':
openEditor(...args);
break;
+ case 'editPermissions':
+ openFilePermissions(...args);
+ break;
case 'openFilePrompt':
openFilePrompt(...args);
break;
@@ -267,6 +295,7 @@ export default {
backHistoryDropdown,
forwardHistoryDropdown,
openFilePromptModal,
+ filePermissions,
cd,
back,
forward,
@@ -274,6 +303,7 @@ export default {
openEditor,
download,
openFilePrompt,
+ openFilePermissions,
getSelected,
handleAction,
}
@@ -295,6 +325,7 @@ export default {
ViewListIcon,
ViewGridIcon,
ModalPopup,
+ FilePermissions,
},
}