diff --git a/backend/open_webui/routers/folders.py b/backend/open_webui/routers/folders.py index f4132222b8..47ee642fd5 100644 --- a/backend/open_webui/routers/folders.py +++ b/backend/open_webui/routers/folders.py @@ -267,19 +267,22 @@ async def get_shared_folders( ] # Also include child folders of shared folders (inheritance) - seen_ids = {folder.id for folder in shared_folders} + result_by_id = {folder['id']: folder for folder in results} for folder in shared_folders: children = await Folders.get_children_folders_by_id_and_user_id(folder.id, folder.user_id, db=db) for child in children or []: - if child.id not in seen_ids: - seen_ids.add(child.id) - results.append( - { - **child.model_dump(), - 'owner_name': owner_names.get(child.user_id, 'Unknown'), - 'permission': 'write' if child.user_id == user.id else folder_perms[folder.id], - } - ) + permission = 'write' if folder.user_id == user.id or child.user_id == user.id else folder_perms[folder.id] + if child.id in result_by_id: + # A weaker direct grant must not hide inherited write access. + if permission == 'write': + result_by_id[child.id]['permission'] = 'write' + else: + result_by_id[child.id] = { + **child.model_dump(), + 'owner_name': owner_names.get(child.user_id, 'Unknown'), + 'permission': permission, + } + results.append(result_by_id[child.id]) return results diff --git a/src/lib/components/layout/Sidebar/Folders/FolderShareModal.svelte b/src/lib/components/layout/Sidebar/Folders/FolderShareModal.svelte index fe34bfbbb4..e75036d534 100644 --- a/src/lib/components/layout/Sidebar/Folders/FolderShareModal.svelte +++ b/src/lib/components/layout/Sidebar/Folders/FolderShareModal.svelte @@ -2,6 +2,7 @@ import type { Writable } from 'svelte/store'; import type { i18n as i18nType } from 'i18next'; import { getContext } from 'svelte'; + import { toast } from 'svelte-sonner'; const i18n = getContext>('i18n'); import Modal from '$lib/components/common/Modal.svelte'; @@ -38,15 +39,17 @@ shareMode = freshFolder.data?.share_mode ?? null; } } catch (e) { - console.error('Failed to load folder access grants', e); + toast.error(`${e}`); accessGrants = folder?.access_grants ?? []; + shareMode = folder?.data?.share_mode ?? null; } finally { loading = false; } }; const handleAccessChange = async () => { - if (!folder) return; + if (!folder || loading) return; + loading = true; try { const res = await updateFolderAccessById( localStorage.token, @@ -56,10 +59,13 @@ ); if (res) { accessGrants = res.access_grants ?? accessGrants; + toast.success($i18n.t('Saved')); } } catch (e) { - console.error('Failed to update folder access', e); + toast.error(`${e}`); await loadAccessGrants(); + } finally { + loading = false; } }; @@ -81,9 +87,13 @@ -
+
- {#if accessGrants.length > 0} - + {#if folder?.parent_id} +

+ {$i18n.t('Access granted to parent folders also applies here.')} +

{/if} +
diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json index fdbda68df3..754b71823c 100644 --- a/src/lib/i18n/locales/en-US/translation.json +++ b/src/lib/i18n/locales/en-US/translation.json @@ -88,6 +88,7 @@ "about the Roman Empire": "", "Accept Autocomplete Generation\nJump to Prompt Variable": "", "Access": "", + "Access granted to parent folders also applies here.": "Access granted to parent folders also applies here.", "Access Control": "", "Access Grants": "", "Access level": "", @@ -1719,6 +1720,7 @@ "Next version": "", "No": "", "No access grants. Private to you.": "", + "No direct access grants.": "No direct access grants.", "No actions found": "", "No activity data": "", "No additional headers are sent unless configured.": "",