feat: add drive permissions (#10489)

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov
2026-02-07 17:34:43 +05:00
committed by GitHub
parent c06ded12e2
commit 89f7445005
26 changed files with 484 additions and 84 deletions
+54 -3
View File
@@ -234,6 +234,12 @@ function defineDrive (builder: Builder): void {
icon: drive.icon.Drive,
baseClass: drive.class.Drive,
availablePermissions: [
drive.permission.CreateFolder,
drive.permission.UpdateFolder,
drive.permission.RemoveFolder,
drive.permission.CreateFile,
drive.permission.UpdateFile,
drive.permission.RemoveFile,
core.permission.UpdateSpace,
core.permission.ArchiveSpace,
core.permission.ForbidDeleteObject
@@ -326,7 +332,8 @@ function defineDrive (builder: Builder): void {
mode: ['context', 'browser'],
application: drive.app.Drive,
group: 'create'
}
},
visibilityTester: drive.function.CanCreateFolder
},
drive.action.CreateRootFolder
)
@@ -475,6 +482,7 @@ function defineFolder (builder: Builder): void {
actions: [
view.action.Open,
view.action.OpenInNewTab,
view.action.Delete,
print.action.Print,
tracker.action.EditRelatedTargets,
tracker.action.NewRelatedIssue
@@ -494,7 +502,8 @@ function defineFolder (builder: Builder): void {
mode: ['context', 'browser'],
application: drive.app.Drive,
group: 'create'
}
},
visibilityTester: drive.function.CanCreateFolder
},
drive.action.CreateChildFolder
)
@@ -518,6 +527,25 @@ function defineFolder (builder: Builder): void {
drive.action.RenameFolder
)
createAction(
builder,
{
action: view.actionImpl.Delete,
label: view.string.Delete,
icon: view.icon.Delete,
category: drive.category.Drive,
input: 'none',
target: drive.class.Folder,
context: {
mode: ['context', 'browser'],
application: drive.app.Drive,
group: 'edit'
},
visibilityTester: drive.function.CanDeleteFolder
},
drive.action.DeleteFolder
)
createAction(builder, {
...actionTemplates.move,
action: view.actionImpl.ShowPopup,
@@ -529,6 +557,7 @@ function defineFolder (builder: Builder): void {
}
},
target: drive.class.Folder,
visibilityTester: drive.function.CanUpdateFolder,
context: {
mode: ['browser', 'context'],
group: 'tools'
@@ -569,7 +598,8 @@ function defineFileVersion (builder: Builder): void {
mode: ['context', 'browser'],
application: drive.app.Drive,
group: 'edit'
}
},
visibilityTester: drive.function.CanUpdateFileVersion
},
drive.action.RestoreFileVersion
)
@@ -649,6 +679,7 @@ function defineFile (builder: Builder): void {
actions: [
view.action.Open,
view.action.OpenInNewTab,
view.action.Delete,
print.action.Print,
tracker.action.EditRelatedTargets,
tracker.action.NewRelatedIssue
@@ -692,6 +723,25 @@ function defineFile (builder: Builder): void {
drive.action.RenameFile
)
createAction(
builder,
{
action: view.actionImpl.Delete,
label: view.string.Delete,
icon: view.icon.Delete,
category: drive.category.Drive,
input: 'none',
target: drive.class.File,
context: {
mode: ['context', 'browser'],
application: drive.app.Drive,
group: 'edit'
},
visibilityTester: drive.function.CanDeleteFile
},
drive.action.DeleteFile
)
// createAction(
// builder,
// {
@@ -721,6 +771,7 @@ function defineFile (builder: Builder): void {
}
},
target: drive.class.File,
visibilityTester: drive.function.CanUpdateFile,
context: {
mode: ['browser', 'context'],
group: 'tools'
+2 -2
View File
@@ -14,7 +14,7 @@
//
import core, { type Blob, type Ref, DOMAIN_BLOB, generateId, toIdMap } from '@hcengineering/core'
import type { Drive, File, FileVersion, Resource } from '@hcengineering/drive'
import type { File, FileVersion, Resource } from '@hcengineering/drive'
import {
type MigrateOperation,
type MigrationClient,
@@ -56,7 +56,7 @@ async function migrateFileVersions (client: MigrationClient): Promise<void> {
collection: 'versions',
modifiedOn: file.modifiedOn,
modifiedBy: file.modifiedBy,
space: file.space as Ref<Drive>,
space: file.space,
title: exfile.title,
file: blob._id,
size: blob.size,
+78
View File
@@ -3,6 +3,84 @@ import core from '@hcengineering/core'
import drive from '@hcengineering/drive'
export function definePermissions (builder: Builder): void {
builder.createDoc(
core.class.Permission,
core.space.Model,
{
scope: 'space',
label: drive.string.CreateFilePermission,
description: drive.string.CreateFilePermissionDescription,
txClass: core.class.TxCreateDoc,
objectClass: drive.class.File
},
drive.permission.CreateFile
)
builder.createDoc(
core.class.Permission,
core.space.Model,
{
scope: 'space',
label: drive.string.UpdateFilePermission,
description: drive.string.UpdateFilePermissionDescription,
txClass: core.class.TxUpdateDoc,
objectClass: drive.class.File
},
drive.permission.UpdateFile
)
builder.createDoc(
core.class.Permission,
core.space.Model,
{
scope: 'space',
label: drive.string.RemoveFilePermission,
description: drive.string.RemoveFilePermissionDescription,
txClass: core.class.TxRemoveDoc,
objectClass: drive.class.File
},
drive.permission.RemoveFile
)
builder.createDoc(
core.class.Permission,
core.space.Model,
{
scope: 'space',
label: drive.string.CreateFolderPermission,
description: drive.string.CreateFolderPermissionDescription,
txClass: core.class.TxCreateDoc,
objectClass: drive.class.Folder
},
drive.permission.CreateFolder
)
builder.createDoc(
core.class.Permission,
core.space.Model,
{
scope: 'space',
label: drive.string.UpdateFolderPermission,
description: drive.string.UpdateFolderPermissionDescription,
txClass: core.class.TxUpdateDoc,
objectClass: drive.class.Folder
},
drive.permission.UpdateFolder
)
builder.createDoc(
core.class.Permission,
core.space.Model,
{
scope: 'space',
label: drive.string.RemoveFolderPermission,
description: drive.string.RemoveFolderPermissionDescription,
txClass: core.class.TxRemoveDoc,
objectClass: drive.class.Folder
},
drive.permission.RemoveFolder
)
builder.createDoc(
core.class.Permission,
core.space.Model,
+8
View File
@@ -53,8 +53,14 @@ export default mergeIds(driveId, drive, {
DriveLinkProvider: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
FolderLinkProvider: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
FileLinkProvider: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
CanCreateFolder: '' as Resource<ViewActionAvailabilityFunction>,
CanUpdateFile: '' as Resource<ViewActionAvailabilityFunction>,
CanUpdateFolder: '' as Resource<ViewActionAvailabilityFunction>,
CanDeleteFile: '' as Resource<ViewActionAvailabilityFunction>,
CanDeleteFolder: '' as Resource<ViewActionAvailabilityFunction>,
CanRenameFile: '' as Resource<ViewActionAvailabilityFunction>,
CanRenameFolder: '' as Resource<ViewActionAvailabilityFunction>,
CanUpdateFileVersion: '' as Resource<ViewActionAvailabilityFunction>,
CanDeleteFileVersion: '' as Resource<ViewActionAvailabilityFunction>,
FileTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>,
FolderTitleProvider: '' as Resource<(client: Client, ref: Ref<Doc>, doc?: Doc) => Promise<string>>
@@ -79,6 +85,8 @@ export default mergeIds(driveId, drive, {
CreateRootFolder: '' as Ref<Action>,
EditDrive: '' as Ref<Action>,
DownloadFile: '' as Ref<Action>,
DeleteFile: '' as Ref<Action>,
DeleteFolder: '' as Ref<Action>,
RenameFile: '' as Ref<Action>,
RenameFolder: '' as Ref<Action>,
DeleteFileVersion: '' as Ref<Action>,
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Obnovit",
"Root": "/",
"ForbidCreateDrivePermission": "Zakázat vytvoření disku",
"ForbidCreateDrivePermissionDescription": "Zakazuje uživatelům vytvářet nové disky"
"ForbidCreateDrivePermissionDescription": "Zakazuje uživatelům vytvářet nové disky",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Wiederherstellen",
"Root": "/",
"ForbidCreateDrivePermission": "Laufwerkserstellung verbieten",
"ForbidCreateDrivePermissionDescription": "Verbietet Benutzern die Erstellung neuer Laufwerke"
"ForbidCreateDrivePermissionDescription": "Verbietet Benutzern die Erstellung neuer Laufwerke",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Restore",
"Root": "/",
"ForbidCreateDrivePermission": "Forbid create drive",
"ForbidCreateDrivePermissionDescription": "Forbid users creating new drives"
"ForbidCreateDrivePermissionDescription": "Forbid users creating new drives",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Restaurar",
"Root": "/",
"ForbidCreateDrivePermission": "Prohibir crear unidad",
"ForbidCreateDrivePermissionDescription": "Prohíbe a los usuarios crear nuevas unidades"
"ForbidCreateDrivePermissionDescription": "Prohíbe a los usuarios crear nuevas unidades",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Restaurer",
"Root": "/",
"ForbidCreateDrivePermission": "Interdire la création de disque",
"ForbidCreateDrivePermissionDescription": "Interdit aux utilisateurs de créer de nouveaux disques"
"ForbidCreateDrivePermissionDescription": "Interdit aux utilisateurs de créer de nouveaux disques",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Ripristina",
"Root": "/",
"ForbidCreateDrivePermission": "Vieta creazione drive",
"ForbidCreateDrivePermissionDescription": "Vieta agli utenti di creare nuovi drive"
"ForbidCreateDrivePermissionDescription": "Vieta agli utenti di creare nuovi drive",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "復元",
"Root": "/",
"ForbidCreateDrivePermission": "ドライブの作成禁止",
"ForbidCreateDrivePermissionDescription": "ユーザーが新しいドライブを作成することを禁止します"
"ForbidCreateDrivePermissionDescription": "ユーザーが新しいドライブを作成することを禁止します",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Restaurar",
"Root": "/",
"ForbidCreateDrivePermission": "Proibir criação de unidade",
"ForbidCreateDrivePermissionDescription": "Proíbe os utilizadores de criar novas unidades"
"ForbidCreateDrivePermissionDescription": "Proíbe os utilizadores de criar novas unidades",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "Восстановить",
"Root": "/",
"ForbidCreateDrivePermission": "Запретить создание диска",
"ForbidCreateDrivePermissionDescription": "Запрещает пользователям создавать новые диски"
"ForbidCreateDrivePermissionDescription": "Запрещает пользователям создавать новые диски",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -26,6 +26,18 @@
"EditDrive": "Sürücüyü Düzenle",
"Rename": "Yeniden adlandır",
"Restore": "Geri yükle",
"Root": "/"
"Root": "/",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
+13 -1
View File
@@ -28,6 +28,18 @@
"Restore": "恢复",
"Root": "/",
"ForbidCreateDrivePermission": "禁止创建磁盘",
"ForbidCreateDrivePermissionDescription": "禁止用户创建新磁盘"
"ForbidCreateDrivePermissionDescription": "禁止用户创建新磁盘",
"CreateFilePermission": "Create File",
"CreateFilePermissionDescription": "Grants users ability to create files",
"UpdateFilePermission": "Update File",
"UpdateFilePermissionDescription": "Grants users ability to update files",
"RemoveFilePermission": "Remove File",
"RemoveFilePermissionDescription": "Grants users ability to remove files",
"CreateFolderPermission": "Create Folder",
"CreateFolderPermissionDescription": "Grants users ability to create folders",
"UpdateFolderPermission": "Update Folder",
"UpdateFolderPermissionDescription": "Grants users ability to update folders",
"RemoveFolderPermission": "Remove Folder",
"RemoveFolderPermissionDescription": "Grants users ability to remove folders"
}
}
@@ -18,7 +18,6 @@
import { AccountArrayEditor, employeeRefByAccountUuidStore } from '@hcengineering/contact-resources'
import core, {
Data,
DocumentUpdate,
RolesAssignment,
Ref,
Role,
@@ -48,6 +47,8 @@
let description: string = drive?.description ?? ''
let isPrivate: boolean = drive?.private ?? false
let autoJoin = drive?.autoJoin ?? false
let restricted: boolean = drive?.restricted ?? false
let members: AccountUuid[] =
drive?.members !== undefined ? hierarchy.clone(drive.members) : [getCurrentAccount().uuid]
let owners: AccountUuid[] = drive?.owners !== undefined ? hierarchy.clone(drive.owners) : [getCurrentAccount().uuid]
@@ -102,7 +103,9 @@
private: isPrivate,
members,
owners,
archived: false
autoJoin,
archived: false,
restricted
}
}
@@ -111,41 +114,8 @@
return
}
const data = getDriveData()
const update: DocumentUpdate<Drive> = {}
if (data.name !== drive?.name) {
update.name = data.name
}
if (data.description !== drive?.description) {
update.description = data.description
}
if (data.private !== drive?.private) {
update.private = data.private
}
if (data.members.length !== drive?.members.length) {
update.members = data.members
} else {
for (const member of data.members) {
if (drive.members.findIndex((p) => p === member) === -1) {
update.members = data.members
break
}
}
}
if (data.owners?.length !== drive?.owners?.length) {
update.owners = data.owners
} else {
for (const owner of data.owners ?? []) {
if (drive.owners?.findIndex((p) => p === owner) === -1) {
update.owners = data.owners
break
}
}
}
if (Object.keys(update).length > 0) {
await client.update(drive, update)
}
const update = getDriveData()
await client.diffUpdate(drive, update)
if (!deepEqual(rolesAssignment, getRolesAssignment())) {
await client.updateMixin(
@@ -307,6 +277,22 @@
/>
</div>
<div class="antiGrid-row">
<div class="antiGrid-row__header withDesciption">
<Label label={core.string.AutoJoin} />
<span><Label label={core.string.AutoJoinDescr} /></span>
</div>
<Toggle id={'space-autoJoin'} bind:on={autoJoin} />
</div>
<div class="antiGrid-row">
<div class="antiGrid-row__header withDesciption">
<Label label={core.string.RBAC} />
<span><Label label={core.string.RBACDescr} /></span>
</div>
<Toggle id={'space-restricted'} bind:on={restricted} />
</div>
{#each roles as role}
<div class="antiGrid-row">
<div class="antiGrid-row__header">
@@ -14,6 +14,7 @@
-->
<script lang="ts">
import { AccountRole, Ref, getCurrentAccount } from '@hcengineering/core'
import { checkMyPermission, permissionsStore } from '@hcengineering/contact-resources'
import { type Drive } from '@hcengineering/drive'
import { getResource } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
@@ -23,6 +24,7 @@
import { getFolderIdFromFragment } from '../navigation'
import { showCreateDrivePopup, showCreateFolderPopup, getUploadOptionsByFragment } from '../utils'
import { onMount } from 'svelte'
import { canCreateObject } from '@hcengineering/view-resources'
export let currentSpace: Ref<Drive> | undefined
export let currentFragment: string | undefined
@@ -44,6 +46,8 @@
]
let uploadActions: HeaderButtonAction[] = []
let filteredUploadActions: HeaderButtonAction[] = []
let filteredBasicActions: HeaderButtonAction[] = []
let allActions: HeaderButtonAction[] = []
const myAcc = getCurrentAccount()
@@ -84,6 +88,12 @@
)
$: parent = getFolderIdFromFragment(currentFragment ?? '') ?? drive.ids.Root
$: canCreateFolder =
currentSpace !== undefined && canCreateObject(drive.class.Folder, currentSpace, $permissionsStore)
$: canUpload = currentSpace !== undefined && canCreateObject(drive.class.File, currentSpace, $permissionsStore)
$: filteredBasicActions = [basicActions[0], ...(canCreateFolder ? [basicActions[1]] : [])]
$: filteredUploadActions = canUpload ? uploadActions : []
function handleCreateDrive (): void {
void showCreateDrivePopup()
@@ -94,7 +104,11 @@
}
let visibleActions: (string | number | null)[] = []
function updateActions (hasSpace: boolean, uploadActions: HeaderButtonAction[]): void {
function updateActions (
hasSpace: boolean,
uploadActions: HeaderButtonAction[],
basicActions: HeaderButtonAction[]
): void {
allActions = [...basicActions, ...uploadActions]
if (hasSpace) {
visibleActions = allActions.map((a) => a.id)
@@ -103,7 +117,13 @@
}
}
$: updateActions(hasDrive, uploadActions)
$: updateActions(hasDrive, filteredUploadActions, filteredBasicActions)
</script>
<HeaderButton loading={false} {client} mainActionId={uploadActions[0]?.id} {visibleActions} actions={allActions} />
<HeaderButton
loading={false}
{client}
mainActionId={filteredUploadActions[0]?.id}
{visibleActions}
actions={allActions}
/>
@@ -13,19 +13,14 @@
// limitations under the License.
-->
<script lang="ts">
import { type Drive, type Folder } from '@hcengineering/drive'
import { type Folder } from '@hcengineering/drive'
import { type Ref } from '@hcengineering/core'
import FolderBrowser from './FolderBrowser.svelte'
export let object: Folder
export let readonly: boolean = false
function getSpace (object: Folder): Ref<Drive> {
return object.space as Ref<Drive>
}
</script>
{#if object}
<FolderBrowser space={getSpace(object)} parent={object._id} {readonly} type={'folder'} />
<FolderBrowser space={object.space} parent={object._id} {readonly} type={'folder'} />
{/if}
@@ -14,13 +14,14 @@
-->
<script lang="ts">
import { type Ref, type WithLookup } from '@hcengineering/core'
import { checkMyPermission, permissionsStore } from '@hcengineering/contact-resources'
import { createFileVersion, type File as DriveFile, type FileVersion } from '@hcengineering/drive'
import { Panel } from '@hcengineering/panel'
import { createQuery, getClient, getFileUrl } from '@hcengineering/presentation'
import { Button, IconMoreH } from '@hcengineering/ui'
import { FileUploadCallbackParams, showFilesUploadPopup } from '@hcengineering/uploader'
import view from '@hcengineering/view'
import { showMenu } from '@hcengineering/view-resources'
import { canChangeDoc, showMenu } from '@hcengineering/view-resources'
import EditFile from './EditFile.svelte'
import FileAside from './FileAside.svelte'
@@ -58,6 +59,7 @@
}
}
)
$: canUploadVersion = object !== undefined && canChangeDoc(drive.class.File, object.space, $permissionsStore)
function handleDownloadFile (): void {
if (object != null && download != null) {
@@ -66,7 +68,7 @@
}
function handleUploadFile (): void {
if (object != null) {
if (object != null && canUploadVersion) {
const target = { objectId: object._id, objectClass: object._class }
void showFilesUploadPopup(
{
@@ -123,13 +125,15 @@
on:click={handleDownloadFile}
/>
</a>
<Button
icon={IconUpload}
iconProps={{ size: 'medium' }}
kind={'icon'}
showTooltip={{ label: drive.string.Upload }}
on:click={handleUploadFile}
/>
{#if canUploadVersion}
<Button
icon={IconUpload}
iconProps={{ size: 'medium' }}
kind={'icon'}
showTooltip={{ label: drive.string.Upload }}
on:click={handleUploadFile}
/>
{/if}
<Button
icon={IconMoreH}
iconProps={{ size: 'medium' }}
@@ -13,7 +13,8 @@
// limitations under the License.
-->
<script lang="ts">
import { type Doc, type DocumentQuery, type Space, type Ref, type WithLookup } from '@hcengineering/core'
import { type Doc, type DocumentQuery, type Ref, type WithLookup } from '@hcengineering/core'
import { permissionsStore } from '@hcengineering/contact-resources'
import drive, { type Drive, type Folder } from '@hcengineering/drive'
import { Scroller, SearchInput, Panel, Button, IconMoreH } from '@hcengineering/ui'
import view, { Viewlet, ViewOptions } from '@hcengineering/view'
@@ -24,7 +25,8 @@
ViewletSelector,
ViewletSettingButton,
DocAttributeBar,
showMenu
showMenu,
canCreateObject
} from '@hcengineering/view-resources'
import DrivePresenter from './DrivePresenter.svelte'
@@ -42,6 +44,7 @@
$: object = type === 'drive' ? (object as Drive) : (object as Folder)
$: query = { space, parent }
$: canUpload = canCreateObject(drive.class.File, space, $permissionsStore)
let viewlet: WithLookup<Viewlet> | undefined = undefined
let viewOptions: ViewOptions | undefined
@@ -101,7 +104,7 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="popupPanel-body" on:contextmenu>
{#if viewlet}
<FileDropArea {space} {parent} canDrop={() => !readonly}>
<FileDropArea {space} {parent} canDrop={() => !readonly && canUpload}>
<Scroller horizontal={true}>
<ViewletContentView {_class} {viewlet} query={resultQuery} {space} {viewOptions} />
</Scroller>
@@ -29,7 +29,7 @@
const dispatch = createEventDispatcher()
let space: Ref<Drive> = value.space as Ref<Drive>
let space: Ref<Drive> = value.space
let parent: Ref<Folder> = value.parent as Ref<Folder>
async function save (): Promise<void> {
+87 -4
View File
@@ -13,11 +13,14 @@
// limitations under the License.
//
import { permissionsStore } from '@hcengineering/contact-resources'
import type { Class, Client, Doc, DocumentQuery, Ref, RelatedDocument, WithLookup } from '@hcengineering/core'
import drive, { type Drive, type File, type FileVersion, type Folder } from '@hcengineering/drive'
import { type Resources } from '@hcengineering/platform'
import { type ObjectSearchResult, getFileUrl } from '@hcengineering/presentation'
import { showPopup, type Location } from '@hcengineering/ui'
import { canChangeDoc, canCreateObject, canDeleteObject, canRemoveDoc } from '@hcengineering/view-resources'
import { get } from 'svelte/store'
import CreateDrive from './components/CreateDrive.svelte'
import DrivePanel from './components/DrivePanel.svelte'
@@ -153,22 +156,96 @@ async function RestoreFileVersion (doc: FileVersion | FileVersion[]): Promise<vo
}
export async function CanRenameFile (doc: File | File[] | undefined): Promise<boolean> {
return doc !== undefined && !Array.isArray(doc)
if (doc === undefined || Array.isArray(doc)) {
return false
}
return canChangeDoc(drive.class.File, doc.space, get(permissionsStore))
}
export async function CanRenameFolder (doc: Folder | Folder[] | undefined): Promise<boolean> {
return doc !== undefined && !Array.isArray(doc)
if (doc === undefined || Array.isArray(doc)) {
return false
}
return canChangeDoc(drive.class.Folder, doc.space, get(permissionsStore))
}
export async function CanUpdateFileVersion (doc: FileVersion | FileVersion[] | undefined): Promise<boolean> {
if (doc === undefined || Array.isArray(doc)) {
return false
}
return canChangeDoc(drive.class.File, doc.space, get(permissionsStore))
}
export async function CanDeleteFileVersion (
doc: WithLookup<FileVersion> | Array<WithLookup<FileVersion>> | undefined
): Promise<boolean> {
if (doc === undefined) {
if (doc === undefined || Array.isArray(doc)) {
return false
}
const docs = Array.isArray(doc) ? doc : [doc]
return docs.every((p) => p.$lookup?.attachedTo !== undefined && p.$lookup?.attachedTo.file !== p._id)
const canDeleteByVersion = docs.every(
(p) => p.$lookup?.attachedTo !== undefined && p.$lookup?.attachedTo.file !== p._id
)
if (!canDeleteByVersion) {
return false
}
const permissions = get(permissionsStore)
return docs.every((p) => canChangeDoc(drive.class.File, doc.space, permissions))
}
export async function CanCreateFolder (doc: Drive | Folder | Array<Drive | Folder> | undefined): Promise<boolean> {
if (doc === undefined || Array.isArray(doc)) {
return false
}
const space = ((doc as Drive).space ?? doc._id) as Ref<Drive>
return canCreateObject(drive.class.Folder, space, get(permissionsStore))
}
export async function CanUpdateFile (doc: File | File[] | undefined): Promise<boolean> {
if (doc === undefined || Array.isArray(doc)) {
return false
}
return canChangeDoc(drive.class.File, doc.space, get(permissionsStore))
}
export async function CanUpdateFolder (doc: Folder | Folder[] | undefined): Promise<boolean> {
if (doc === undefined || Array.isArray(doc)) {
return false
}
return canChangeDoc(drive.class.Folder, doc.space, get(permissionsStore))
}
export async function CanDeleteFile (doc: File | File[] | undefined): Promise<boolean> {
if (doc === undefined) return false
doc = Array.isArray(doc) ? doc : [doc]
const permissions = get(permissionsStore)
const results = await Promise.all(
doc.map(async (p) => {
return permissions.restrictedSpaces.has(p.space)
? canRemoveDoc(drive.class.File, p.space, permissions)
: await canDeleteObject(doc)
})
)
return results.every(Boolean)
}
export async function CanDeleteFolder (doc: Folder | Folder[] | undefined): Promise<boolean> {
if (doc === undefined) return false
doc = Array.isArray(doc) ? doc : [doc]
const permissions = get(permissionsStore)
const results = await Promise.all(
doc.map(async (p) => {
return permissions.restrictedSpaces.has(p.space)
? canRemoveDoc(drive.class.Folder, p.space, permissions)
: await canDeleteObject(doc)
})
)
return results.every(Boolean)
}
export async function FileTitleProvider (client: Client, ref: Ref<File>, doc?: File): Promise<string> {
@@ -222,8 +299,14 @@ export default async (): Promise<Resources> => ({
DriveLinkProvider,
FileLinkProvider,
FolderLinkProvider,
CanCreateFolder,
CanUpdateFile,
CanUpdateFolder,
CanDeleteFile,
CanDeleteFolder,
CanRenameFile,
CanRenameFolder,
CanUpdateFileVersion,
CanDeleteFileVersion,
FileTitleProvider,
FolderTitleProvider
+1 -1
View File
@@ -161,7 +161,7 @@ export async function resolveParents (object: Resource): Promise<Doc[]> {
}
}
const root = await client.findOne(drive.class.Drive, { _id: object.space as Ref<Drive> })
const root = await client.findOne(drive.class.Drive, { _id: object.space })
if (root !== undefined) {
parents.push(root)
}
+20 -2
View File
@@ -66,7 +66,19 @@ export const drivePlugin = plugin(driveId, {
Folder: '' as IntlString,
Resource: '' as IntlString,
ForbidCreateDrivePermission: '' as IntlString,
ForbidCreateDrivePermissionDescription: '' as IntlString
ForbidCreateDrivePermissionDescription: '' as IntlString,
CreateFilePermission: '' as IntlString,
CreateFilePermissionDescription: '' as IntlString,
UpdateFilePermission: '' as IntlString,
UpdateFilePermissionDescription: '' as IntlString,
RemoveFilePermission: '' as IntlString,
RemoveFilePermissionDescription: '' as IntlString,
CreateFolderPermission: '' as IntlString,
CreateFolderPermissionDescription: '' as IntlString,
UpdateFolderPermission: '' as IntlString,
UpdateFolderPermissionDescription: '' as IntlString,
RemoveFolderPermission: '' as IntlString,
RemoveFolderPermissionDescription: '' as IntlString
},
descriptor: {
DriveType: '' as Ref<SpaceTypeDescriptor>
@@ -75,7 +87,13 @@ export const drivePlugin = plugin(driveId, {
DefaultDrive: '' as Ref<SpaceType>
},
permission: {
ForbidCreateDrive: '' as Ref<Permission>
ForbidCreateDrive: '' as Ref<Permission>,
CreateFile: '' as Ref<Permission>,
UpdateFile: '' as Ref<Permission>,
RemoveFile: '' as Ref<Permission>,
CreateFolder: '' as Ref<Permission>,
UpdateFolder: '' as Ref<Permission>,
RemoveFolder: '' as Ref<Permission>
}
})
+1 -1
View File
@@ -26,7 +26,7 @@ export function TypeFileVersion (): Type<number> {
export interface Drive extends TypedSpace {}
/** @public */
export interface Resource extends Doc {
export interface Resource extends Doc<Drive> {
title: string
parent: Ref<Resource>
path: Ref<Resource>[]
+22
View File
@@ -85,6 +85,28 @@ export function canChangeDoc (_class: Ref<Class<Doc>>, space: Ref<Space>, store:
return !store.restrictedSpaces.has(space)
}
export function canRemoveDoc (_class: Ref<Class<Doc>>, space: Ref<Space>, store: PermissionsStore): boolean {
const arePermissionsDisabled = getMetadata(core.metadata.DisablePermissions) ?? false
if (arePermissionsDisabled) return true
if (store.whitelist.has(space)) return true
if (store.ps[space] !== undefined) {
const client = getClient()
const h = client.getHierarchy()
const ancestors = h.getAncestors(_class)
const permissions = client.getModel().findAllSync(core.class.Permission, { txClass: core.class.TxRemoveDoc })
for (const ancestor of ancestors) {
const curr = permissions.filter((p) => p.objectClass === ancestor && p.txMatch === undefined)
for (const permission of curr) {
if (store.ps[space]?.has(permission._id)) {
return permission.forbid !== true
}
}
}
}
return !store.restrictedSpaces.has(space)
}
export function canCreateObject (_class: Ref<Class<Doc>>, space: Ref<Space>, store: PermissionsStore): boolean {
const arePermissionsDisabled = getMetadata(core.metadata.DisablePermissions) ?? false
if (arePermissionsDisabled) return true