diff --git a/Backend/Locks.py b/Backend/Locks.py index c1d9e71..7308c37 100644 --- a/Backend/Locks.py +++ b/Backend/Locks.py @@ -71,6 +71,19 @@ def RemoveLock(RootName: str, RelativePath: str) -> None: SaveLocks(Locks) +def GetInheritedLock(RootName: str, DirectoryRelativePath: str) -> dict[str, Any] | None: + CurrentPath = DirectoryRelativePath + + while CurrentPath: + Lock = GetLockForPath(RootName, CurrentPath) + if Lock is not None: + return Lock + + CurrentPath = CurrentPath.rsplit("/", 1)[0] if "/" in CurrentPath else "" + + return None + + def IsPathAccessible( RootName: str, RelativePath: str, Username: str, UserGroups: list[str] ) -> bool: diff --git a/Backend/Main.py b/Backend/Main.py index 108a766..5c6c57d 100644 --- a/Backend/Main.py +++ b/Backend/Main.py @@ -52,6 +52,7 @@ from Groups import ( SetGroupMembers, ) from Locks import ( + GetInheritedLock, GetLockForPath, IsPathAccessible, LoadLocks, @@ -332,6 +333,12 @@ def GetLockEndpoint(Root: str, Path: str, CurrentUser: dict = Depends(GetCurrent return GetLockForPath(Root, Path) or {} +@Application.get("/api/lock/inherited") +def GetInheritedLockEndpoint(Root: str, Path: str, CurrentUser: dict = Depends(GetCurrentUser)) -> dict: + RequirePathAccessible(Root, Path, CurrentUser) + return GetInheritedLock(Root, Path) or {} + + class SetLockRequest(BaseModel): Users: list[str] = [] Groups: list[str] = [] diff --git a/Backend/Preview.py b/Backend/Preview.py index 8dafecd..e749dfb 100644 --- a/Backend/Preview.py +++ b/Backend/Preview.py @@ -55,7 +55,13 @@ def StreamBinaryFile(FilePath: Path, RangeHeader: str | None) -> StreamingRespon def IterateChunk(): with FilePath.open("rb") as SourceFile: SourceFile.seek(Start) - yield SourceFile.read(ChunkLength) + Remaining = ChunkLength + while Remaining > 0: + Chunk = SourceFile.read(min(1024 * 1024, Remaining)) + if not Chunk: + break + Remaining -= len(Chunk) + yield Chunk Headers = { "Content-Range": f"bytes {Start}-{End}/{FileSize}", diff --git a/Frontend/src/Api.js b/Frontend/src/Api.js index 2048671..7e7f577 100644 --- a/Frontend/src/Api.js +++ b/Frontend/src/Api.js @@ -117,6 +117,11 @@ export function FetchLock(RootName, RelativePath) { return FetchJson(`/lock?${Params}`); } +export function FetchInheritedLock(RootName, RelativePath) { + const Params = new URLSearchParams({ Root: RootName, Path: RelativePath }); + return FetchJson(`/lock/inherited?${Params}`); +} + export function SetLock(RootName, RelativePath, Users, Groups) { const Params = new URLSearchParams({ Root: RootName, Path: RelativePath }); return FetchJson(`/lock?${Params}`, { method: "PUT", ...JsonBody({ Users, Groups }) }); diff --git a/Frontend/src/App.jsx b/Frontend/src/App.jsx index 58e9787..03a1ed3 100644 --- a/Frontend/src/App.jsx +++ b/Frontend/src/App.jsx @@ -84,7 +84,10 @@ export default function App() { SetCurrentUser(User); SetAuthState("LoggedIn"); }) - .catch(() => SetAuthState("LoggedOut")); + .catch(() => { + SetCurrentUser(GuestUser); + SetAuthState("Guest"); + }); }); }, []); diff --git a/Frontend/src/Components/DirectoryPane.jsx b/Frontend/src/Components/DirectoryPane.jsx index 05ed63c..fd397ad 100644 --- a/Frontend/src/Components/DirectoryPane.jsx +++ b/Frontend/src/Components/DirectoryPane.jsx @@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from "react"; import { CreateEntry, DeleteEntry, + FetchInheritedLock, FetchLock, FetchTree, GetDownloadUrl, @@ -59,12 +60,28 @@ export default function DirectoryPane({ return DirectoryPath ? `${DirectoryPath}/${Name}` : Name; } - function OpenCreateFileModal() { - SetModalConfig({ Mode: "createFile" }); + async function OpenCreateFileModal() { + const Inherited = await FetchInheritedLock(RootName, DirectoryPath).catch(() => ({})); + SetModalConfig({ + Mode: "createFile", + InheritedUsers: Inherited.Users || [], + InheritedGroups: Inherited.Groups || [], + }); } - function OpenCreateDirectoryModal() { - SetModalConfig({ Mode: "createDirectory" }); + async function OpenCreateDirectoryModal() { + const Inherited = await FetchInheritedLock(RootName, DirectoryPath).catch(() => ({})); + SetModalConfig({ + Mode: "createDirectory", + InheritedUsers: Inherited.Users || [], + InheritedGroups: Inherited.Groups || [], + }); + } + + function SetsEqual(FirstList, SecondList) { + if (FirstList.length !== SecondList.length) return false; + const FirstSet = new Set(FirstList); + return SecondList.every((Item) => FirstSet.has(Item)); } async function HandleRequestLockEdit(Event, Entry) { @@ -143,7 +160,10 @@ export default function DirectoryPane({ const FullPath = JoinPath(Name.trim()); await CreateEntry(RootName, FullPath, IsDirectory); - if (Users.length > 0 || Groups.length > 0) { + const MatchesInherited = + SetsEqual(Users, ModalConfig.InheritedUsers) && SetsEqual(Groups, ModalConfig.InheritedGroups); + + if (!MatchesInherited && (Users.length > 0 || Groups.length > 0)) { await SetLockApi(RootName, FullPath, Users, Groups); } @@ -306,8 +326,8 @@ export default function DirectoryPane({ Title={`New file in ${DirectoryPath || "root"}`} ShowNameField NamePlaceholder="Notes.md" - InitialUsers={[]} - InitialGroups={[]} + InitialUsers={ModalConfig.InheritedUsers} + InitialGroups={ModalConfig.InheritedGroups} ConfirmLabel="Create" OnConfirm={HandleModalConfirm} OnCancel={() => SetModalConfig(null)} @@ -318,8 +338,8 @@ export default function DirectoryPane({ Title={`New directory in ${DirectoryPath || "root"}`} ShowNameField NamePlaceholder="MyFolder" - InitialUsers={[]} - InitialGroups={[]} + InitialUsers={ModalConfig.InheritedUsers} + InitialGroups={ModalConfig.InheritedGroups} ConfirmLabel="Create" OnConfirm={HandleModalConfirm} OnCancel={() => SetModalConfig(null)} diff --git a/Frontend/src/Components/Sidebar.jsx b/Frontend/src/Components/Sidebar.jsx index 5652056..913ed29 100644 --- a/Frontend/src/Components/Sidebar.jsx +++ b/Frontend/src/Components/Sidebar.jsx @@ -3,6 +3,7 @@ import React, { useEffect, useRef, useState } from "react"; import { CreateEntry, DeleteEntry, + FetchInheritedLock, FetchLock, FetchTree, GetAvatarUrl, @@ -314,12 +315,28 @@ export default function Sidebar({ return CurrentDirectoryPath ? `${CurrentDirectoryPath}/${Name}` : Name; } - function OpenCreateFileModal() { - SetModalConfig({ Mode: "createFile" }); + async function OpenCreateFileModal() { + const Inherited = await FetchInheritedLock(RootName, CurrentDirectoryPath).catch(() => ({})); + SetModalConfig({ + Mode: "createFile", + InheritedUsers: Inherited.Users || [], + InheritedGroups: Inherited.Groups || [], + }); } - function OpenCreateDirectoryModal() { - SetModalConfig({ Mode: "createDirectory" }); + async function OpenCreateDirectoryModal() { + const Inherited = await FetchInheritedLock(RootName, CurrentDirectoryPath).catch(() => ({})); + SetModalConfig({ + Mode: "createDirectory", + InheritedUsers: Inherited.Users || [], + InheritedGroups: Inherited.Groups || [], + }); + } + + function SetsEqual(FirstList, SecondList) { + if (FirstList.length !== SecondList.length) return false; + const FirstSet = new Set(FirstList); + return SecondList.every((Item) => FirstSet.has(Item)); } async function HandleRequestLockEdit(FullPath, EntryName) { @@ -346,7 +363,10 @@ export default function Sidebar({ const FullPath = JoinPath(Name.trim()); await CreateEntry(RootName, FullPath, IsDirectory); - if (Users.length > 0 || Groups.length > 0) { + const MatchesInherited = + SetsEqual(Users, ModalConfig.InheritedUsers) && SetsEqual(Groups, ModalConfig.InheritedGroups); + + if (!MatchesInherited && (Users.length > 0 || Groups.length > 0)) { await SetLockApi(RootName, FullPath, Users, Groups); } @@ -496,8 +516,8 @@ export default function Sidebar({ Title={`New file in ${CurrentDirectoryPath || "root"}`} ShowNameField NamePlaceholder="Notes.md" - InitialUsers={[]} - InitialGroups={[]} + InitialUsers={ModalConfig.InheritedUsers} + InitialGroups={ModalConfig.InheritedGroups} ConfirmLabel="Create" OnConfirm={HandleModalConfirm} OnCancel={() => SetModalConfig(null)} @@ -508,8 +528,8 @@ export default function Sidebar({ Title={`New directory in ${CurrentDirectoryPath || "root"}`} ShowNameField NamePlaceholder="MyFolder" - InitialUsers={[]} - InitialGroups={[]} + InitialUsers={ModalConfig.InheritedUsers} + InitialGroups={ModalConfig.InheritedGroups} ConfirmLabel="Create" OnConfirm={HandleModalConfirm} OnCancel={() => SetModalConfig(null)}