Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions codex/serializers/admin/libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class AdminFolderListSerializer(Serializer):
"""Get a list of dirs."""

root_folder = CharField(read_only=True)
# Null at a filesystem root, which has no parent.
parent_folder = CharField(read_only=True, allow_null=True)
folders = ListField(child=CharField(read_only=True))


Expand Down
18 changes: 12 additions & 6 deletions codex/views/admin/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,6 @@ def _get_dirs(root_path, show_hidden) -> tuple[str, ...]:
``Path.resolve().is_dir()`` (which chains a full readlink walk
before stat-ing). Broken symlinks are skipped silently.
"""
dirs: list[str] = []
if root_path.parent != root_path:
dirs.append("..")
subdirs: list[str] = []
with os.scandir(root_path) as it:
for entry in it:
Expand All @@ -180,8 +177,7 @@ def _get_dirs(root_path, show_hidden) -> tuple[str, ...]:
continue
if is_dir:
subdirs.append(entry.name)
dirs.extend(sorted(subdirs))
return tuple(dirs)
return tuple(sorted(subdirs, key=str.casefold))

@staticmethod
def _default_root_path() -> Path:
Expand Down Expand Up @@ -234,10 +230,20 @@ def get(self, *_args, **_kwargs) -> Response:

dirs = self._get_dirs(root_path, show_hidden)

data = {"root_folder": str(root_path), "folders": dirs}
parent = root_path.parent
data = {
"root_folder": str(root_path),
"parent_folder": str(parent) if parent != root_path else None,
"folders": dirs,
}
serializer = self.get_serializer(data)
except ValidationError:
raise
except PermissionError as exc:
# A folder the server may see but not list. Keyed to the path,
# like "Not a directory", so the envelope carries the reason.
reason = {"path": ["Permission denied"]}
raise ValidationError(reason) from exc
except Exception as exc:
logger.exception("get admin folder list view")
reason = "Server Error"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<section>
<AdminServerFolderPicker
<AdminServerFolderTree
v-if="!oldRow"
:rules="rules.path"
autofocus
Expand Down Expand Up @@ -52,8 +52,9 @@ import { mapState } from "pinia";
import LIMITS from "@/choices/limits.json";
import DurationInput from "@/components/admin/create-update-dialog/duration-input.vue";
import AdminRelationPicker from "@/components/admin/create-update-dialog/relation-picker.vue";
import AdminServerFolderPicker from "@/components/admin/create-update-dialog/server-folder-picker.vue";
import createUpdateInputsMixin from "@/components/admin/create-update-dialog/create-update-inputs-mixin.js";
import { isPathParent } from "@/components/admin/create-update-dialog/path-parent";
import AdminServerFolderTree from "@/components/admin/create-update-dialog/server-folder-tree.vue";
import { useAdminStore } from "@/stores/admin";

const UPDATE_KEYS = Object.freeze([
Expand All @@ -72,29 +73,12 @@ const EMPTY_ROW = Object.freeze({
groups: [],
});

const isPathParent = (path, potentialChildPath) => {
// Normalize the paths to avoid issues with different directory separator characters
path = path.replaceAll("\\", "/");
potentialChildPath = potentialChildPath.replaceAll("\\", "/");

// Ensure that both paths end with a slash to avoid false positives
if (!path.endsWith("/")) {
path += "/";
}
if (!potentialChildPath.endsWith("/")) {
potentialChildPath += "/";
}

// Check if the potential child path starts with the parent path
return potentialChildPath.startsWith(path);
};

export default {
// eslint-disable-next-line no-secrets/no-secrets
name: "AdminLibraryCreateUpdateInputs",
components: {
AdminRelationPicker,
AdminServerFolderPicker,
AdminServerFolderTree,
DurationInput,
},
mixins: [createUpdateInputsMixin],
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/components/admin/create-update-dialog/path-parent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Whether ``potentialChildPath`` is ``path`` or inside it. Shared by the
* library form's path rules and the folder tree, which disables the
* folders an existing library already covers.
*/
export const isPathParent = (path, potentialChildPath) => {
// Normalize the paths to avoid issues with different directory separator characters
path = path.replaceAll("\\", "/");
potentialChildPath = potentialChildPath.replaceAll("\\", "/");

// Ensure that both paths end with a slash to avoid false positives
if (!path.endsWith("/")) {
path += "/";
}
if (!potentialChildPath.endsWith("/")) {
potentialChildPath += "/";
}

// Check if the potential child path starts with the parent path
return potentialChildPath.startsWith(path);
};

This file was deleted.

Loading