mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Backport plugin permissions in the workspace (#9966)
* Apply patch Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com> * Fix merge errors Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com> * Fix drive header Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com> * Fix formatting Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com> * Fix tests Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com> --------- Signed-off-by: Anton Alexeyev <alexeyev.anton@gmail.com>
This commit is contained in:
@@ -12,6 +12,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ReviewDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.ReviewDocumentDescription
|
||||
},
|
||||
documents.permission.ReviewDocument
|
||||
@@ -22,6 +23,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ApproveDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.ApproveDocumentDescription
|
||||
},
|
||||
documents.permission.ApproveDocument
|
||||
@@ -32,6 +34,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.ArchiveDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.ArchiveDocumentDescription
|
||||
},
|
||||
documents.permission.ArchiveDocument
|
||||
@@ -42,6 +45,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CoAuthorDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.CoAuthorDocumentDescription
|
||||
},
|
||||
documents.permission.CoAuthorDocument
|
||||
@@ -52,6 +56,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CreateDocumentPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.CreateDocumentDescription
|
||||
},
|
||||
documents.permission.CreateDocument
|
||||
@@ -62,6 +67,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.UpdateDocumentOwnerPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.UpdateDocumentOwnerDescription
|
||||
},
|
||||
documents.permission.UpdateDocumentOwner
|
||||
@@ -72,6 +78,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.CreateDocumentCategoryPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.CreateDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.CreateDocumentCategory
|
||||
@@ -82,6 +89,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.UpdateDocumentCategoryPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.UpdateDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.UpdateDocumentCategory
|
||||
@@ -92,6 +100,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: documents.string.DeleteDocumentCategoryPermission,
|
||||
scope: 'space',
|
||||
description: documents.string.DeleteDocumentCategoryDescription
|
||||
},
|
||||
documents.permission.DeleteDocumentCategory
|
||||
|
||||
@@ -23,6 +23,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.CreateObject,
|
||||
scope: 'space',
|
||||
description: core.string.CreateObjectDescription
|
||||
},
|
||||
core.permission.CreateObject
|
||||
@@ -33,6 +34,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.UpdateObject,
|
||||
scope: 'space',
|
||||
description: core.string.UpdateObjectDescription
|
||||
},
|
||||
core.permission.UpdateObject
|
||||
@@ -43,6 +45,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.DeleteObject,
|
||||
scope: 'space',
|
||||
description: core.string.DeleteObjectDescription
|
||||
},
|
||||
core.permission.DeleteObject
|
||||
@@ -53,36 +56,20 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.ForbidDeleteObject,
|
||||
txClass: core.class.TxRemoveDoc,
|
||||
forbid: true,
|
||||
scope: 'space',
|
||||
description: core.string.ForbidDeleteObjectDescription
|
||||
},
|
||||
core.permission.ForbidDeleteObject
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.UpdateObject,
|
||||
description: core.string.UpdateObjectDescription
|
||||
},
|
||||
core.permission.UpdateObject
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.DeleteObject,
|
||||
description: core.string.DeleteObjectDescription
|
||||
},
|
||||
core.permission.DeleteObject
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.UpdateSpace,
|
||||
scope: 'space',
|
||||
description: core.string.UpdateSpaceDescription
|
||||
},
|
||||
core.permission.UpdateSpace
|
||||
@@ -93,6 +80,7 @@ export function definePermissions (builder: Builder): void {
|
||||
core.space.Model,
|
||||
{
|
||||
label: core.string.ArchiveSpace,
|
||||
scope: 'space',
|
||||
description: core.string.ArchiveSpaceDescription
|
||||
},
|
||||
core.permission.ArchiveSpace
|
||||
|
||||
@@ -29,7 +29,9 @@ import {
|
||||
type SpaceTypeDescriptor,
|
||||
type TypedSpace,
|
||||
type AccountUuid,
|
||||
type TxAccessLevel
|
||||
type TxAccessLevel,
|
||||
type Tx,
|
||||
type Doc
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
ArrOf,
|
||||
@@ -155,6 +157,10 @@ export class TRole extends TAttachedDoc implements Role {
|
||||
@UX(core.string.Permission)
|
||||
export class TPermission extends TDoc implements Permission {
|
||||
label!: IntlString
|
||||
txClass?: Ref<Class<Tx>>
|
||||
forbid?: boolean
|
||||
objectClass?: Ref<Class<Doc<Space>>>
|
||||
scope?: 'space' | 'workspace'
|
||||
description?: IntlString
|
||||
icon?: Asset
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ import { type Asset, getEmbeddedLabel } from '@hcengineering/platform'
|
||||
import tags from '@hcengineering/tags'
|
||||
import time, { type ToDo, type Todoable } from '@hcengineering/time'
|
||||
import document from './plugin'
|
||||
import { definePermissions } from './permissions'
|
||||
|
||||
export { documentId } from '@hcengineering/document'
|
||||
|
||||
@@ -550,6 +551,7 @@ export function createModel (builder: Builder): void {
|
||||
defineDocument(builder)
|
||||
|
||||
defineApplication(builder)
|
||||
definePermissions(builder)
|
||||
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
domain: DOMAIN_DOCUMENT,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import document from '@hcengineering/document'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: document.string.ForbidCreateTeamspacePermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: document.class.Teamspace,
|
||||
forbid: true,
|
||||
description: document.string.ForbidCreateTeamspacePermissionDescription
|
||||
},
|
||||
document.permission.ForbidCreateTeamspace
|
||||
)
|
||||
}
|
||||
@@ -63,6 +63,7 @@ import workbench from '@hcengineering/model-workbench'
|
||||
import { getEmbeddedLabel } from '@hcengineering/platform'
|
||||
|
||||
import drive from './plugin'
|
||||
import { definePermissions } from './permissions'
|
||||
|
||||
export { driveId } from '@hcengineering/drive'
|
||||
export { driveOperation } from './migration'
|
||||
@@ -790,4 +791,5 @@ export function createModel (builder: Builder): void {
|
||||
defineFile(builder)
|
||||
defineFileVersion(builder)
|
||||
defineApplication(builder)
|
||||
definePermissions(builder)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import drive from '@hcengineering/drive'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: drive.string.ForbidCreateDrivePermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: drive.class.Drive,
|
||||
forbid: true,
|
||||
description: drive.string.ForbidCreateDrivePermissionDescription
|
||||
},
|
||||
drive.permission.ForbidCreateDrive
|
||||
)
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import { type ViewOptionsModel } from '@hcengineering/view'
|
||||
|
||||
import lead from './plugin'
|
||||
import { defineSpaceType } from './spaceType'
|
||||
import { definePermissions } from './permissions'
|
||||
import { TCustomer, TFunnel, TLead } from './types'
|
||||
|
||||
export { leadId } from '@hcengineering/lead'
|
||||
@@ -660,4 +661,5 @@ export function createModel (builder: Builder): void {
|
||||
})
|
||||
|
||||
defineSpaceType(builder)
|
||||
definePermissions(builder)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import lead from '@hcengineering/lead'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: lead.string.ForbidCreateFunnelPermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: lead.class.Funnel,
|
||||
forbid: true,
|
||||
description: lead.string.ForbidCreateFunnelPermissionDescription
|
||||
},
|
||||
lead.permission.ForbidCreateFunnel
|
||||
)
|
||||
}
|
||||
+23
-17
@@ -37,6 +37,7 @@ import { type KeyBinding, type ViewOptionModel, type ViewOptionsModel } from '@h
|
||||
import recruit from './plugin'
|
||||
import { createReviewModel, reviewTableConfig, reviewTableOptions } from './review'
|
||||
import { defineSpaceType } from './spaceType'
|
||||
import { definePermissions } from './permissions'
|
||||
import { TApplicant, TApplicantMatch, TCandidate, TOpinion, TReview, TVacancy, TVacancyList } from './types'
|
||||
|
||||
export { recruitId } from '@hcengineering/recruit'
|
||||
@@ -1019,24 +1020,28 @@ export function createModel (builder: Builder): void {
|
||||
},
|
||||
override: [recruit.action.CreateGlobalApplication]
|
||||
})
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: recruit.component.CreateCandidate,
|
||||
element: 'top'
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: recruit.component.CreateCandidate,
|
||||
element: 'top'
|
||||
},
|
||||
label: recruit.string.CreateTalent,
|
||||
icon: recruit.icon.Create,
|
||||
keyBinding: ['keyC'],
|
||||
input: 'none',
|
||||
category: recruit.category.Recruit,
|
||||
target: core.class.Doc,
|
||||
context: {
|
||||
mode: ['workbench', 'browser'],
|
||||
application: recruit.app.Recruit,
|
||||
group: 'create'
|
||||
}
|
||||
},
|
||||
label: recruit.string.CreateTalent,
|
||||
icon: recruit.icon.Create,
|
||||
keyBinding: ['keyC'],
|
||||
input: 'none',
|
||||
category: recruit.category.Recruit,
|
||||
target: core.class.Doc,
|
||||
context: {
|
||||
mode: ['workbench', 'browser'],
|
||||
application: recruit.app.Recruit,
|
||||
group: 'create'
|
||||
}
|
||||
})
|
||||
recruit.action.CreateTalent
|
||||
)
|
||||
|
||||
createAction(builder, {
|
||||
action: view.actionImpl.ShowPopup,
|
||||
@@ -1596,4 +1601,5 @@ export function createModel (builder: Builder): void {
|
||||
)
|
||||
|
||||
defineSpaceType(builder)
|
||||
definePermissions(builder)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import recruit from '@hcengineering/recruit'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: recruit.string.ForbidCreateVacancyPermission,
|
||||
scope: 'workspace',
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: recruit.class.Vacancy,
|
||||
forbid: true,
|
||||
description: recruit.string.ForbidCreateVacancyPermissionDescription
|
||||
},
|
||||
recruit.permission.ForbidCreateVacancy
|
||||
)
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||
import { createActions as defineActions } from './actions'
|
||||
import tracker from './plugin'
|
||||
import { definePresenters } from './presenters'
|
||||
import { definePermissions } from './permissions'
|
||||
import {
|
||||
DOMAIN_TRACKER,
|
||||
TClassicProjectTypeData,
|
||||
@@ -709,6 +710,7 @@ export function createModel (builder: Builder): void {
|
||||
]
|
||||
})
|
||||
|
||||
definePermissions(builder)
|
||||
defineSpaceType(builder)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { Builder } from '@hcengineering/model'
|
||||
import core from '@hcengineering/core'
|
||||
import tracker from '@hcengineering/tracker'
|
||||
|
||||
export function definePermissions (builder: Builder): void {
|
||||
builder.createDoc(
|
||||
core.class.Permission,
|
||||
core.space.Model,
|
||||
{
|
||||
label: tracker.string.ForbidCreateProjectPermission,
|
||||
txClass: core.class.TxCreateDoc,
|
||||
objectClass: tracker.class.Project,
|
||||
forbid: true,
|
||||
scope: 'workspace',
|
||||
description: tracker.string.ForbidCreateProjectPermissionDescription
|
||||
},
|
||||
tracker.permission.ForbidCreateProject
|
||||
)
|
||||
}
|
||||
@@ -354,18 +354,22 @@ function defineTraining (builder: Builder): void {
|
||||
|
||||
definePermission(builder, training.permission.ChangeSomeoneElsesTrainingOwner, {
|
||||
label: training.string.Permission_ChangeSomeoneElsesTrainingOwner,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ChangeSomeoneElsesTrainingOwner_Description
|
||||
})
|
||||
definePermission(builder, training.permission.CreateTraining, {
|
||||
label: training.string.Permission_CreateTraining,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_CreateTraining_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTrainingOverview, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTrainingOverview,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesTrainingOverview_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTrainingQuestions, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTrainingQuestions,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesTrainingQuestions_Description
|
||||
})
|
||||
}
|
||||
@@ -491,14 +495,17 @@ function defineTrainingRequest (builder: Builder): void {
|
||||
|
||||
definePermission(builder, training.permission.ChangeSomeoneElsesSentRequestOwner, {
|
||||
label: training.string.Permission_ChangeSomeoneElsesSentRequestOwner,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ChangeSomeoneElsesSentRequestOwner_Description
|
||||
})
|
||||
definePermission(builder, training.permission.CreateRequestOnSomeoneElsesTraining, {
|
||||
label: training.string.Permission_CreateRequestOnSomeoneElsesTraining,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_CreateRequestOnSomeoneElsesTraining_Description
|
||||
})
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesSentRequest, {
|
||||
label: training.string.Permission_ViewSomeoneElsesSentRequest,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesSentRequest_Description
|
||||
})
|
||||
})()
|
||||
@@ -753,6 +760,7 @@ function defineTrainingAttempt (builder: Builder): void {
|
||||
|
||||
definePermission(builder, training.permission.ViewSomeoneElsesTraineesResults, {
|
||||
label: training.string.Permission_ViewSomeoneElsesTraineesResults,
|
||||
scope: 'space',
|
||||
description: training.string.Permission_ViewSomeoneElsesTraineesResults_Description
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,4 +69,4 @@
|
||||
"PersonId": "Osoba",
|
||||
"AccountId": "Účet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,4 +69,4 @@
|
||||
"PersonId": "Id de personne",
|
||||
"AccountId": "Compte"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
|
||||
import type { DocumentQuery } from './storage'
|
||||
import { type WorkspaceDataId, type WorkspaceUuid } from './utils'
|
||||
import { Tx } from '.'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -529,6 +530,10 @@ export type RolesAssignment = Record<Ref<Role>, AccountUuid[] | undefined>
|
||||
*/
|
||||
export interface Permission extends Doc {
|
||||
label: IntlString
|
||||
txClass?: Ref<Class<Tx>>
|
||||
forbid?: boolean
|
||||
objectClass?: Ref<Class<Doc>>
|
||||
scope?: 'space' | 'workspace'
|
||||
description?: IntlString
|
||||
icon?: Asset
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<!--
|
||||
// Copyright © 2025 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { HeaderButtonAction, SelectPopupValueType } from '../types'
|
||||
import { checkPermission, Client, getCurrentAccount, hasAccountRole, TxOperations } from '@hcengineering/core'
|
||||
import { ButtonWithDropdown, Button, Loading, IconAdd, IconDropdown } from '../index'
|
||||
|
||||
export let mainActionId: number | string | null = null
|
||||
export let loading = false
|
||||
export let client: TxOperations & Client
|
||||
export let actions: HeaderButtonAction[] = []
|
||||
export let visibleActions: (string | number | null)[] = []
|
||||
|
||||
let allowedActions: HeaderButtonAction[] = []
|
||||
let items: HeaderButtonAction[] = []
|
||||
let mainAction: HeaderButtonAction | undefined = undefined
|
||||
$: filterVisibleActions(allowedActions, visibleActions)
|
||||
$: filterAllowedActions(actions).catch(() => {})
|
||||
|
||||
function filterVisibleActions (allowed: HeaderButtonAction[], visible: (string | number | null)[]): void {
|
||||
items = allowed.filter((action) => visible.includes(action.id))
|
||||
mainAction = items.find((a) => a.id === mainActionId)
|
||||
if (mainAction === undefined && items.length > 0) {
|
||||
mainAction = items[0]
|
||||
}
|
||||
}
|
||||
|
||||
async function filterAllowedActions (actions: HeaderButtonAction[]): Promise<SelectPopupValueType[]> {
|
||||
const result: HeaderButtonAction[] = []
|
||||
for (const action of actions) {
|
||||
if (await isActionAllowed(action)) {
|
||||
result.push(action)
|
||||
}
|
||||
action.keyBinding = await action.keyBindingPromise
|
||||
}
|
||||
allowedActions = result
|
||||
return result
|
||||
}
|
||||
|
||||
async function isActionAllowed (action: HeaderButtonAction): Promise<boolean> {
|
||||
if (action.accountRole === undefined && action.permissions === undefined) return true
|
||||
if (action.accountRole !== undefined && hasAccountRole(getCurrentAccount(), action.accountRole)) return true
|
||||
if (action.permissions !== undefined) {
|
||||
for (const permission of action.permissions) {
|
||||
if (await checkPermission(client, permission.id, permission.space)) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if mainAction !== undefined}
|
||||
{#if loading}
|
||||
<Loading shrink />
|
||||
{:else}
|
||||
<div class="antiNav-subheader">
|
||||
{#if items.length === 1}
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
justify="left"
|
||||
kind="primary"
|
||||
label={mainAction.label}
|
||||
width="100%"
|
||||
on:click={mainAction.callback}
|
||||
showTooltip={{
|
||||
direction: 'bottom',
|
||||
label: mainAction.label,
|
||||
keys: mainAction.keyBinding
|
||||
}}
|
||||
>
|
||||
<div slot="content" class="draft-circle-container">
|
||||
{#if mainAction.draft === true}
|
||||
<div class="draft-circle" />
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
{:else}
|
||||
<ButtonWithDropdown
|
||||
icon={IconAdd}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
label={mainAction.label}
|
||||
dropdownItems={items}
|
||||
dropdownIcon={IconDropdown}
|
||||
on:dropdown-selected={(ev) => {
|
||||
items.find((a) => a.id === ev.detail)?.callback()
|
||||
}}
|
||||
on:click={mainAction.callback}
|
||||
mainButtonId={mainAction.id !== null ? String(mainAction.id).replaceAll(':', '-') : undefined}
|
||||
showTooltipMain={{
|
||||
direction: 'bottom',
|
||||
label: mainAction.label,
|
||||
keys: mainAction.keyBinding
|
||||
}}
|
||||
>
|
||||
<div slot="content" class="draft-circle-container">
|
||||
{#if mainAction.draft === true}
|
||||
<div class="draft-circle" />
|
||||
{/if}
|
||||
</div>
|
||||
</ButtonWithDropdown>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.draft-circle-container {
|
||||
margin-left: auto;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.draft-circle {
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
background-color: var(--primary-bg-color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
@@ -56,6 +56,7 @@ export { getCurrentLocation, locationToUrl, navigate, location, setLocationStora
|
||||
export { default as EditBox } from './components/EditBox.svelte'
|
||||
export { default as Label } from './components/Label.svelte'
|
||||
export { default as Button } from './components/Button.svelte'
|
||||
export { default as HeaderButton } from './components/HeaderButton.svelte'
|
||||
export { default as ButtonWithDropdown } from './components/ButtonWithDropdown.svelte'
|
||||
export { default as ButtonGroup } from './components/ButtonGroup.svelte'
|
||||
export { default as FilterButton } from './components/FilterButton.svelte'
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
import type { Blob, Ref, Timestamp } from '@hcengineering/core'
|
||||
import type { AccountRole, Blob, Permission, Ref, Timestamp, TypedSpace } from '@hcengineering/core'
|
||||
import type {
|
||||
Asset,
|
||||
IntlString,
|
||||
@@ -519,6 +519,21 @@ export interface SelectPopupValueType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface HeaderButtonAction extends SelectPopupValueType {
|
||||
callback: () => void
|
||||
keyBindingPromise?: Promise<string[] | undefined>
|
||||
keyBinding?: string[] | undefined
|
||||
draft?: boolean
|
||||
accountRole?: AccountRole
|
||||
permissions?: Array<{
|
||||
id: Ref<Permission>
|
||||
space: Ref<TypedSpace>
|
||||
}>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
|
||||
"Icon": "Ikona",
|
||||
"Color": "Barva",
|
||||
"RoleLabel": "Role: {role}"
|
||||
"RoleLabel": "Role: {role}",
|
||||
"ForbidCreateTeamspacePermission": "Zakázat vytvoření týmového prostoru",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Zakazuje uživatelům vytvářet nové týmové prostory"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
"ConfigDescription": "Erweiterung für kollaborative Dokumentbearbeitung",
|
||||
"Icon": "Symbol",
|
||||
"Color": "Farbe",
|
||||
"RoleLabel": "Rolle: {role}"
|
||||
"RoleLabel": "Rolle: {role}",
|
||||
"ForbidCreateTeamspacePermission": "Arbeitsbereichserstellung verbieten",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Verbietet Benutzern das Erstellen neuer Arbeitsbereiche"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
|
||||
"Icon": "Icon",
|
||||
"Color": "Color",
|
||||
"RoleLabel": "Role: {role}"
|
||||
"RoleLabel": "Role: {role}",
|
||||
"ForbidCreateTeamspacePermission": "Forbid create teamspace",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Forbid users creating new teamspaces"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
"ConfigDescription": "Extensión para edición colaborativo de documentos",
|
||||
"Icon": "Icono",
|
||||
"Color": "Color",
|
||||
"RoleLabel": "Role: {role}"
|
||||
"RoleLabel": "Role: {role}",
|
||||
"ForbidCreateTeamspacePermission": "Prohibir crear espacio de trabajo",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Prohíbe a los usuarios crear nuevos espacios de trabajo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
"ConfigDescription": "Extension pour l'édition collaborative de documents",
|
||||
"Icon": "Icône",
|
||||
"Color": "Couleur",
|
||||
"RoleLabel": "Rôle : {role}"
|
||||
"RoleLabel": "Rôle : {role}",
|
||||
"ForbidCreateTeamspacePermission": "Interdire la création d'espace d'équipe",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Interdit aux utilisateurs de créer de nouveaux espaces d'équipe"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
"ConfigDescription": "Estensione per la modifica collaborativa dei documenti",
|
||||
"Icon": "Icona",
|
||||
"Color": "Colore",
|
||||
"RoleLabel": "Ruolo: {role}"
|
||||
"RoleLabel": "Ruolo: {role}",
|
||||
"ForbidCreateTeamspacePermission": "Vieta creazione teamspace",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Vieta agli utenti di creare nuovi teamspaces"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
"ConfigDescription": "共同ドキュメント編集のための拡張機能",
|
||||
"Icon": "アイコン",
|
||||
"Color": "色",
|
||||
"RoleLabel": "ロール: {role}"
|
||||
"RoleLabel": "ロール: {role}",
|
||||
"ForbidCreateTeamspacePermission": "チームスペースの作成禁止",
|
||||
"ForbidCreateTeamspacePermissionDescription": "ユーザーが新しいチームスペースを作成することを禁止します"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
"ConfigDescription": "Extensão para edição colaborativa de documentos",
|
||||
"Icon": "Ícone",
|
||||
"Color": "Cor",
|
||||
"RoleLabel": "Papel: {role}"
|
||||
"RoleLabel": "Papel: {role}",
|
||||
"ForbidCreateTeamspacePermission": "Proibir criação de espaço de trabalho",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Proíbe os utilizadores de criar novos espaços de trabalho"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
|
||||
"Icon": "Иконка",
|
||||
"Color": "Цвет",
|
||||
"RoleLabel": "Роль: {role}"
|
||||
"RoleLabel": "Роль: {role}",
|
||||
"ForbidCreateTeamspacePermission": "Запретить создание пространства",
|
||||
"ForbidCreateTeamspacePermissionDescription": "Запрещает пользователям создавать новые пространства"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@
|
||||
"ReassignToDoConfirm": "您要更改待办事项被指派人吗?待办事项将从当前被指派人的计划中移除。",
|
||||
"Icon": "图标",
|
||||
"Color": "颜色",
|
||||
"RoleLabel": "角色:{role}"
|
||||
"RoleLabel": "角色:{role}",
|
||||
"ForbidCreateTeamspacePermission": "禁止创建团队空间",
|
||||
"ForbidCreateTeamspacePermissionDescription": "禁止用户创建新团队空间"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,17 +13,9 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AccountRole, Ref, Space, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import core, { AccountRole, Ref, Space, getCurrentAccount } from '@hcengineering/core'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
Button,
|
||||
ButtonWithDropdown,
|
||||
IconAdd,
|
||||
IconDropdown,
|
||||
Loading,
|
||||
SelectPopupValueType,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { HeaderButton, showPopup } from '@hcengineering/ui'
|
||||
import { openDoc } from '@hcengineering/view-resources'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { DocumentEvents } from '@hcengineering/document'
|
||||
@@ -70,51 +62,37 @@
|
||||
showPopup(CreateTeamspace, {}, 'top')
|
||||
}
|
||||
|
||||
async function dropdownItemSelected (res?: SelectPopupValueType['id']): Promise<void> {
|
||||
if (res === document.string.CreateDocument) {
|
||||
await newDocument()
|
||||
} else if (res === document.string.CreateTeamspace) {
|
||||
await newTeamspace()
|
||||
let mainActionId: string | undefined = undefined
|
||||
let visibleActions: string[] = []
|
||||
function updateActions (teamspace: boolean): void {
|
||||
mainActionId = document.string.CreateDocument
|
||||
if (teamspace) {
|
||||
visibleActions = [document.string.CreateTeamspace, document.string.CreateDocument]
|
||||
} else {
|
||||
visibleActions = [document.string.CreateTeamspace]
|
||||
}
|
||||
}
|
||||
|
||||
const dropdownItems = hasAccountRole(myAcc, AccountRole.User)
|
||||
? [
|
||||
{ id: document.string.CreateDocument, label: document.string.CreateDocument },
|
||||
{ id: document.string.CreateTeamspace, label: document.string.CreateTeamspace }
|
||||
]
|
||||
: [{ id: document.string.CreateDocument, label: document.string.CreateDocument }]
|
||||
$: updateActions(hasTeamspace)
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
<Loading shrink />
|
||||
{:else}
|
||||
<div class="antiNav-subheader">
|
||||
{#if hasTeamspace}
|
||||
<ButtonWithDropdown
|
||||
icon={IconAdd}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
label={document.string.CreateDocument}
|
||||
on:click={newDocument}
|
||||
mainButtonId={'new-document'}
|
||||
dropdownIcon={IconDropdown}
|
||||
{dropdownItems}
|
||||
on:dropdown-selected={(ev) => {
|
||||
void dropdownItemSelected(ev.detail)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
id={'new-teamspace'}
|
||||
icon={IconAdd}
|
||||
label={document.string.CreateTeamspace}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
kind={'primary'}
|
||||
gap={'large'}
|
||||
on:click={newTeamspace}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<HeaderButton
|
||||
{loading}
|
||||
{client}
|
||||
{mainActionId}
|
||||
{visibleActions}
|
||||
actions={[
|
||||
{
|
||||
id: document.string.CreateTeamspace,
|
||||
label: document.string.CreateTeamspace,
|
||||
accountRole: AccountRole.User,
|
||||
callback: newTeamspace
|
||||
},
|
||||
{
|
||||
id: document.string.CreateDocument,
|
||||
label: document.string.CreateDocument,
|
||||
accountRole: AccountRole.User,
|
||||
callback: newDocument
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Class, Doc, Mixin, Ref, SpaceType, SpaceTypeDescriptor } from '@hcengineering/core'
|
||||
import type { Class, Doc, Mixin, Ref, SpaceType, SpaceTypeDescriptor, Permission } from '@hcengineering/core'
|
||||
import { NotificationGroup, NotificationType } from '@hcengineering/notification'
|
||||
import type { Asset, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { IntlString, plugin } from '@hcengineering/platform'
|
||||
@@ -69,7 +69,9 @@ export const documentPlugin = plugin(documentId, {
|
||||
string: {
|
||||
ConfigLabel: '' as IntlString,
|
||||
CreateDocument: '' as IntlString,
|
||||
Documents: '' as IntlString
|
||||
Documents: '' as IntlString,
|
||||
ForbidCreateTeamspacePermission: '' as IntlString,
|
||||
ForbidCreateTeamspacePermissionDescription: '' as IntlString
|
||||
},
|
||||
ids: {
|
||||
NoParent: '' as Ref<Document>,
|
||||
@@ -81,6 +83,9 @@ export const documentPlugin = plugin(documentId, {
|
||||
},
|
||||
spaceType: {
|
||||
DefaultTeamspaceType: '' as Ref<SpaceType>
|
||||
},
|
||||
permission: {
|
||||
ForbidCreateTeamspace: '' as Ref<Permission>
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Přejmenovat",
|
||||
"Restore": "Obnovit",
|
||||
"RoleLabel": "Role",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Zakázat vytvoření disku",
|
||||
"ForbidCreateDrivePermissionDescription": "Zakazuje uživatelům vytvářet nové disky"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Umbenennen",
|
||||
"Restore": "Wiederherstellen",
|
||||
"RoleLabel": "Rolle",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Laufwerkserstellung verbieten",
|
||||
"ForbidCreateDrivePermissionDescription": "Verbietet Benutzern die Erstellung neuer Laufwerke"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Rename",
|
||||
"Restore": "Restore",
|
||||
"RoleLabel": "Role",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Forbid create drive",
|
||||
"ForbidCreateDrivePermissionDescription": "Forbid users creating new drives"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Renombrar",
|
||||
"Restore": "Restaurar",
|
||||
"RoleLabel": "Rol",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Prohibir crear unidad",
|
||||
"ForbidCreateDrivePermissionDescription": "Prohíbe a los usuarios crear nuevas unidades"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Renommer",
|
||||
"Restore": "Restaurer",
|
||||
"RoleLabel": "Rôle",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Interdire la création de disque",
|
||||
"ForbidCreateDrivePermissionDescription": "Interdit aux utilisateurs de créer de nouveaux disques"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Rinomina",
|
||||
"Restore": "Ripristina",
|
||||
"RoleLabel": "Ruolo",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Vieta creazione drive",
|
||||
"ForbidCreateDrivePermissionDescription": "Vieta agli utenti di creare nuovi drive"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "名前を変更",
|
||||
"Restore": "復元",
|
||||
"RoleLabel": "ロール",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "ドライブの作成禁止",
|
||||
"ForbidCreateDrivePermissionDescription": "ユーザーが新しいドライブを作成することを禁止します"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Renomear",
|
||||
"Restore": "Restaurar",
|
||||
"RoleLabel": "Papel",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Proibir criação de unidade",
|
||||
"ForbidCreateDrivePermissionDescription": "Proíbe os utilizadores de criar novas unidades"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "Переименовать",
|
||||
"Restore": "Восстановить",
|
||||
"RoleLabel": "Роль",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "Запретить создание диска",
|
||||
"ForbidCreateDrivePermissionDescription": "Запрещает пользователям создавать новые диски"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"Rename": "重命名",
|
||||
"Restore": "恢复",
|
||||
"RoleLabel": "角色",
|
||||
"Root": "/"
|
||||
"Root": "/",
|
||||
"ForbidCreateDrivePermission": "禁止创建磁盘",
|
||||
"ForbidCreateDrivePermissionDescription": "禁止用户创建新磁盘"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,44 +13,66 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { AccountRole, Ref, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import { AccountRole, Ref, getCurrentAccount } from '@hcengineering/core'
|
||||
import { type Drive } from '@hcengineering/drive'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Button, ButtonWithDropdown, IconAdd, IconDropdown, Loading, SelectPopupValueType } from '@hcengineering/ui'
|
||||
import { FileUploadOptions, getUploadHandlers, UploadHandler } from '@hcengineering/uploader'
|
||||
import { HeaderButton, HeaderButtonAction } from '@hcengineering/ui'
|
||||
import { getUploadHandlers } from '@hcengineering/uploader'
|
||||
import drive from '../plugin'
|
||||
import { getFolderIdFromFragment } from '../navigation'
|
||||
import {
|
||||
showCreateDrivePopup,
|
||||
showCreateFolderPopup,
|
||||
uploadFilesToDrivePopup,
|
||||
getUploadOptionsByFragment
|
||||
} from '../utils'
|
||||
import { showCreateDrivePopup, showCreateFolderPopup, getUploadOptionsByFragment } from '../utils'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let currentSpace: Ref<Drive> | undefined
|
||||
export let currentFragment: string | undefined
|
||||
|
||||
const basicActions: HeaderButtonAction[] = [
|
||||
{
|
||||
id: drive.string.CreateDrive,
|
||||
label: drive.string.CreateDrive,
|
||||
icon: drive.icon.Drive,
|
||||
accountRole: AccountRole.User,
|
||||
callback: handleCreateDrive
|
||||
},
|
||||
{
|
||||
id: drive.string.CreateFolder,
|
||||
label: drive.string.CreateFolder,
|
||||
icon: drive.icon.Folder,
|
||||
callback: handleCreateFolder
|
||||
}
|
||||
]
|
||||
|
||||
let uploadActions: HeaderButtonAction[] = []
|
||||
let allActions: HeaderButtonAction[] = []
|
||||
|
||||
const myAcc = getCurrentAccount()
|
||||
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
const actionWithExtensionMap = new Map<string, UploadHandler>()
|
||||
|
||||
onMount(() => {
|
||||
const handlers = getUploadHandlers(client)
|
||||
const newUploadActions: HeaderButtonAction[] = []
|
||||
for (const handler of handlers) {
|
||||
dropdownItems.push({ id: handler._id, label: handler.label, icon: handler.icon })
|
||||
const uploadHandler = async (opts: FileUploadOptions): Promise<void> => {
|
||||
const uploadHandler = async (): Promise<void> => {
|
||||
if (currentSpace === undefined) return
|
||||
const fn = await getResource(handler.handler)
|
||||
const opts = await getUploadOptionsByFragment(currentSpace, currentFragment ?? '')
|
||||
await fn(opts)
|
||||
}
|
||||
actionWithExtensionMap.set(handler._id, uploadHandler)
|
||||
newUploadActions.push({
|
||||
id: handler.label,
|
||||
label: handler.label,
|
||||
icon: handler.icon,
|
||||
callback: () => {
|
||||
void uploadHandler()
|
||||
}
|
||||
})
|
||||
}
|
||||
uploadActions = newUploadActions
|
||||
})
|
||||
|
||||
let loading = true
|
||||
let hasDrive = false
|
||||
query.query(
|
||||
drive.class.Drive,
|
||||
@@ -63,74 +85,25 @@
|
||||
|
||||
$: parent = getFolderIdFromFragment(currentFragment ?? '') ?? drive.ids.Root
|
||||
|
||||
async function handleDropdownItemSelected (res?: SelectPopupValueType['id']): Promise<void> {
|
||||
if (res === drive.string.CreateDrive) {
|
||||
await handleCreateDrive()
|
||||
} else if (res === drive.string.CreateFolder) {
|
||||
await handleCreateFolder()
|
||||
} else if (typeof res === 'string' && currentSpace !== undefined) {
|
||||
const opts = await getUploadOptionsByFragment(currentSpace, currentFragment ?? '')
|
||||
const uploadFn = actionWithExtensionMap.get(res)
|
||||
if (uploadFn === undefined) {
|
||||
return
|
||||
}
|
||||
await uploadFn(opts)
|
||||
function handleCreateDrive (): void {
|
||||
void showCreateDrivePopup()
|
||||
}
|
||||
|
||||
function handleCreateFolder (): void {
|
||||
void showCreateFolderPopup(currentSpace, parent, true)
|
||||
}
|
||||
|
||||
let visibleActions: (string | number | null)[] = []
|
||||
function updateActions (hasSpace: boolean, uploadActions: HeaderButtonAction[]): void {
|
||||
allActions = [...basicActions, ...uploadActions]
|
||||
if (hasSpace) {
|
||||
visibleActions = allActions.map((a) => a.id)
|
||||
} else {
|
||||
visibleActions = [drive.string.CreateDrive]
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCreateDrive (): Promise<void> {
|
||||
await showCreateDrivePopup()
|
||||
}
|
||||
|
||||
async function handleCreateFolder (): Promise<void> {
|
||||
await showCreateFolderPopup(currentSpace, parent, true)
|
||||
}
|
||||
|
||||
async function handleUploadFile (): Promise<void> {
|
||||
if (currentSpace !== undefined) {
|
||||
await uploadFilesToDrivePopup(currentSpace, parent)
|
||||
}
|
||||
}
|
||||
|
||||
const dropdownItems: SelectPopupValueType[] = hasAccountRole(myAcc, AccountRole.User)
|
||||
? [
|
||||
{ id: drive.string.CreateDrive, label: drive.string.CreateDrive, icon: drive.icon.Drive },
|
||||
{ id: drive.string.CreateFolder, label: drive.string.CreateFolder, icon: drive.icon.Folder }
|
||||
]
|
||||
: [{ id: drive.string.CreateFolder, label: drive.string.CreateFolder, icon: drive.icon.Folder }]
|
||||
|
||||
loading = false
|
||||
$: updateActions(hasDrive, uploadActions)
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
<Loading shrink />
|
||||
{:else}
|
||||
<div class="antiNav-subheader">
|
||||
{#if hasDrive}
|
||||
<ButtonWithDropdown
|
||||
icon={IconAdd}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
label={drive.string.UploadFile}
|
||||
mainButtonId={'new-document'}
|
||||
dropdownIcon={IconDropdown}
|
||||
{dropdownItems}
|
||||
disabled={currentSpace === undefined}
|
||||
on:click={handleUploadFile}
|
||||
on:dropdown-selected={(ev) => {
|
||||
void handleDropdownItemSelected(ev.detail)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={drive.string.CreateDrive}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
kind={'primary'}
|
||||
gap={'large'}
|
||||
on:click={handleCreateDrive}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<HeaderButton loading={false} {client} mainActionId={uploadActions[0]?.id} {visibleActions} actions={allActions} />
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Class, Doc, Mixin, Ref, SpaceType, SpaceTypeDescriptor, Type } from '@hcengineering/core'
|
||||
import type { Class, Doc, Mixin, Ref, SpaceType, SpaceTypeDescriptor, Type, Permission } from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Plugin, Resource as PlatformResource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { Location, ResolvedLocation } from '@hcengineering/ui/src/types'
|
||||
@@ -64,13 +64,18 @@ export const drivePlugin = plugin(driveId, {
|
||||
File: '' as IntlString,
|
||||
FileVersion: '' as IntlString,
|
||||
Folder: '' as IntlString,
|
||||
Resource: '' as IntlString
|
||||
Resource: '' as IntlString,
|
||||
ForbidCreateDrivePermission: '' as IntlString,
|
||||
ForbidCreateDrivePermissionDescription: '' as IntlString
|
||||
},
|
||||
descriptor: {
|
||||
DriveType: '' as Ref<SpaceTypeDescriptor>
|
||||
},
|
||||
spaceType: {
|
||||
DefaultDrive: '' as Ref<SpaceType>
|
||||
},
|
||||
permission: {
|
||||
ForbidCreateDrive: '' as Ref<Permission>
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Rozšíření pro řízení vztahů se zákazníky",
|
||||
"EditFunnel": "Upravit trychtýř",
|
||||
"FunnelMembers": "Členové",
|
||||
"RoleLabel": "Role: {role}"
|
||||
"RoleLabel": "Role: {role}",
|
||||
"ForbidCreateFunnelPermission": "Zakázat vytvoření trychtýře",
|
||||
"ForbidCreateFunnelPermissionDescription": "Zakazuje uživatelům vytvářet nové trychtýře"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Erweiterung für Customer Relationship Management",
|
||||
"EditFunnel": "Trichter bearbeiten",
|
||||
"FunnelMembers": "Mitglieder",
|
||||
"RoleLabel": "Rolle: {role}"
|
||||
"RoleLabel": "Rolle: {role}",
|
||||
"ForbidCreateFunnelPermission": "Trichtererstellung verbieten",
|
||||
"ForbidCreateFunnelPermissionDescription": "Verbietet Benutzern das Erstellen neuer Trichter"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Extension for Customer relation management",
|
||||
"EditFunnel": "Edit Funnel",
|
||||
"FunnelMembers": "Members",
|
||||
"RoleLabel": "Role: {role}"
|
||||
"RoleLabel": "Role: {role}",
|
||||
"ForbidCreateFunnelPermission": "Forbid create funnel",
|
||||
"ForbidCreateFunnelPermissionDescription": "Forbid users creating new funnels"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Extensión para la gestión de las relaciones con los Clientes",
|
||||
"EditFunnel": "Editar Embudo",
|
||||
"FunnelMembers": "Miembros",
|
||||
"RoleLabel": "Role: {role}"
|
||||
"RoleLabel": "Role: {role}",
|
||||
"ForbidCreateFunnelPermission": "Prohibir crear embudo",
|
||||
"ForbidCreateFunnelPermissionDescription": "Prohíbe a los usuarios crear nuevos embudos"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Extension pour la gestion de la relation client",
|
||||
"EditFunnel": "Modifier le pipeline",
|
||||
"FunnelMembers": "Membres",
|
||||
"RoleLabel": "Rôle : {role}"
|
||||
"RoleLabel": "Rôle : {role}",
|
||||
"ForbidCreateFunnelPermission": "Interdire la création de pipeline",
|
||||
"ForbidCreateFunnelPermissionDescription": "Interdit aux utilisateurs de créer de nouveaux pipelines"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Estensione per la gestione delle relazioni con i clienti",
|
||||
"EditFunnel": "Modifica Funnel",
|
||||
"FunnelMembers": "Membri",
|
||||
"RoleLabel": "Ruolo: {role}"
|
||||
"RoleLabel": "Ruolo: {role}",
|
||||
"ForbidCreateFunnelPermission": "Vieta creazione funnel",
|
||||
"ForbidCreateFunnelPermissionDescription": "Vieta agli utenti di creare nuovi funnel"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "顧客関係管理のための拡張機能",
|
||||
"EditFunnel": "ファネルを編集",
|
||||
"FunnelMembers": "メンバー",
|
||||
"RoleLabel": "ロール: {role}"
|
||||
"RoleLabel": "ロール: {role}",
|
||||
"ForbidCreateFunnelPermission": "ファネルの作成禁止",
|
||||
"ForbidCreateFunnelPermissionDescription": "ユーザーが新しいファネルを作成することを禁止します"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Extensão para gestão de relacionamento com o Cliente",
|
||||
"EditFunnel": "Editar Funil",
|
||||
"FunnelMembers": "Membros",
|
||||
"RoleLabel": "Cargo: {role}"
|
||||
"RoleLabel": "Cargo: {role}",
|
||||
"ForbidCreateFunnelPermission": "Proibir criação de funil",
|
||||
"ForbidCreateFunnelPermissionDescription": "Proíbe os utilizadores de criar novos funis"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "Расширение по работе с клиентами",
|
||||
"EditFunnel": "Редактировать воронку",
|
||||
"FunnelMembers": "Участники",
|
||||
"RoleLabel": "Роль: {role}"
|
||||
"RoleLabel": "Роль: {role}",
|
||||
"ForbidCreateFunnelPermission": "Запретить создание воронки",
|
||||
"ForbidCreateFunnelPermissionDescription": "Запрещает пользователям создавать новые воронки"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"ConfigDescription": "用于客户关系管理的扩展",
|
||||
"EditFunnel": "编辑漏斗",
|
||||
"FunnelMembers": "成员",
|
||||
"RoleLabel": "角色:{role}"
|
||||
"RoleLabel": "角色:{role}",
|
||||
"ForbidCreateFunnelPermission": "禁止创建漏斗",
|
||||
"ForbidCreateFunnelPermissionDescription": "禁止用户创建新漏斗"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,29 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Button, showPopup, IconAdd } from '@hcengineering/ui'
|
||||
import { showPopup, HeaderButton } from '@hcengineering/ui'
|
||||
import lead from '../plugin'
|
||||
import CreateCustomer from './CreateCustomer.svelte'
|
||||
import { AccountRole } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
const client = getClient()
|
||||
|
||||
async function newIssue (): Promise<void> {
|
||||
showPopup(CreateCustomer, {}, 'top')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader">
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={lead.string.CreateCustomerLabel}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
kind={'primary'}
|
||||
gap={'large'}
|
||||
on:click={newIssue}
|
||||
/>
|
||||
</div>
|
||||
<HeaderButton
|
||||
{client}
|
||||
mainActionId={lead.string.CreateCustomerLabel}
|
||||
visibleActions={[lead.string.CreateCustomerLabel]}
|
||||
actions={[
|
||||
{
|
||||
id: lead.string.CreateCustomerLabel,
|
||||
label: lead.string.CreateCustomerLabel,
|
||||
accountRole: AccountRole.User,
|
||||
callback: newIssue
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -15,7 +15,17 @@
|
||||
//
|
||||
|
||||
import type { Contact } from '@hcengineering/contact'
|
||||
import type { Attribute, Class, MarkupBlobRef, Doc, Markup, Ref, Status, Timestamp } from '@hcengineering/core'
|
||||
import type {
|
||||
Attribute,
|
||||
Class,
|
||||
MarkupBlobRef,
|
||||
Doc,
|
||||
Markup,
|
||||
Ref,
|
||||
Status,
|
||||
Timestamp,
|
||||
Permission
|
||||
} from '@hcengineering/core'
|
||||
import { Mixin } from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
@@ -73,7 +83,9 @@ const lead = plugin(leadId, {
|
||||
},
|
||||
string: {
|
||||
Lead: '' as IntlString,
|
||||
ConfigLabel: '' as IntlString
|
||||
ConfigLabel: '' as IntlString,
|
||||
ForbidCreateFunnelPermission: '' as IntlString,
|
||||
ForbidCreateFunnelPermissionDescription: '' as IntlString
|
||||
},
|
||||
attribute: {
|
||||
State: '' as Ref<Attribute<Status>>
|
||||
@@ -96,6 +108,9 @@ const lead = plugin(leadId, {
|
||||
},
|
||||
space: {
|
||||
DefaultFunnel: '' as Ref<Funnel>
|
||||
},
|
||||
permission: {
|
||||
ForbidCreateFunnel: '' as Ref<Permission>
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -122,11 +122,13 @@
|
||||
"HideArchivedVacancies": "Skrýt archivované pozice",
|
||||
"HideApplicantsFromArchivedVacancies": "Skrýt z archivovaných pozic",
|
||||
"CreateNewSkills": "Vytvořit nové dovednosti, pokud neexistují",
|
||||
"SwapFirstAndLastNames": "Vyměňte jméno a příjmení"
|
||||
"SwapFirstAndLastNames": "Vyměňte jméno a příjmení",
|
||||
"ForbidCreateVacancyPermission": "Zakázat vytvoření pozice",
|
||||
"ForbidCreateVacancyPermissionDescription": "Zakazuje uživatelům vytvářet nové volné pozice"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "Přihláška již existuje",
|
||||
"TalentRequired": "Vyberte talent",
|
||||
"VacancyRequired": "Vyberte pozici"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,24 +105,26 @@
|
||||
"MoveApplication": "Zu anderer Stelle verschieben",
|
||||
"SearchVacancy": "Stelle suchen...",
|
||||
"Organizations": "Unternehmen",
|
||||
|
||||
|
||||
"TemplateReplace": "Möchten Sie die neue Vorlage anwenden?",
|
||||
"TemplateReplaceConfirm": "Alle Felder werden mit den Werten der neuen Vorlage überschrieben",
|
||||
"Apply": "Anwenden",
|
||||
|
||||
|
||||
"OpenVacancyList": "Liste öffnen",
|
||||
"Export": "Exportieren",
|
||||
"ConfigLabel": "Rekrutierung",
|
||||
"ConfigDescription": "Erweiterung zur Verwaltung von Talenten/Bewerbern und Stellen.",
|
||||
"MyApplications": "Meine Bewerbungen",
|
||||
|
||||
|
||||
"ShowApplications": "Bewerbungen anzeigen",
|
||||
"GetTalentIds": "Talent-IDs abrufen",
|
||||
"HideDoneState": "Abgeschlossene Bewerbungen ausblenden",
|
||||
"HideArchivedVacancies": "Archivierte Stellen ausblenden",
|
||||
"HideApplicantsFromArchivedVacancies": "Aus archivierten Stellen ausblenden",
|
||||
"CreateNewSkills": "Neue Fähigkeiten erstellen, wenn keine bestehenden gefunden werden",
|
||||
"SwapFirstAndLastNames": "Vor- und Nachnamen tauschen"
|
||||
"SwapFirstAndLastNames": "Vor- und Nachnamen tauschen",
|
||||
"ForbidCreateVacancyPermission": "Stellenerstellung verbieten",
|
||||
"ForbidCreateVacancyPermissionDescription": "Verbietet Benutzern das Erstellen neuer Stellenangebote"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "Bewerbung existiert bereits",
|
||||
|
||||
@@ -122,7 +122,9 @@
|
||||
"HideArchivedVacancies": "Hide archived Vacancies",
|
||||
"HideApplicantsFromArchivedVacancies": "Hide from archived Vacancies",
|
||||
"CreateNewSkills": "Create new skills if existing not found",
|
||||
"SwapFirstAndLastNames": "Swap first and last names"
|
||||
"SwapFirstAndLastNames": "Swap first and last names",
|
||||
"ForbidCreateVacancyPermission": "Forbid create vacancy",
|
||||
"ForbidCreateVacancyPermissionDescription": "Forbid users creating new vacancies"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "Application already exists",
|
||||
|
||||
@@ -119,7 +119,9 @@
|
||||
"HideArchivedVacancies": "Ocultar vacantes archivadas",
|
||||
"HideApplicantsFromArchivedVacancies": "Ocultar de vacantes archivadas",
|
||||
"CreateNewSkills": "Crear nuevas habilidades si no se encuentran las existentes",
|
||||
"SwapFirstAndLastNames": "Intercambie nombres y apellidos"
|
||||
"SwapFirstAndLastNames": "Intercambie nombres y apellidos",
|
||||
"ForbidCreateVacancyPermission": "Prohibir crear vacante",
|
||||
"ForbidCreateVacancyPermissionDescription": "Prohíbe a los usuarios crear nuevas vacantes"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "La solicitud ya existe",
|
||||
|
||||
@@ -119,11 +119,13 @@
|
||||
"HideArchivedVacancies": "Masquer les postes vacants archivés",
|
||||
"HideApplicantsFromArchivedVacancies": "Masquer les candidats des postes vacants archivés",
|
||||
"CreateNewSkills": "Créer de nouvelles compétences si les existantes ne sont pas trouvées",
|
||||
"SwapFirstAndLastNames": "Permuter le prénom et le nom"
|
||||
"SwapFirstAndLastNames": "Permuter le prénom et le nom",
|
||||
"ForbidCreateVacancyPermission": "Interdire la création de poste vacant",
|
||||
"ForbidCreateVacancyPermissionDescription": "Interdit aux utilisateurs de créer de nouveaux postes vacants"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "La candidature existe déjà",
|
||||
"TalentRequired": "Veuillez sélectionner un talent",
|
||||
"VacancyRequired": "Veuillez sélectionner un poste vacant"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,9 @@
|
||||
"HideArchivedVacancies": "Nascondi posizioni archiviate",
|
||||
"HideApplicantsFromArchivedVacancies": "Nascondi da posizioni archiviate",
|
||||
"CreateNewSkills": "Crea nuove competenze se quelle esistenti non vengono trovate",
|
||||
"SwapFirstAndLastNames": "Scambia nome e cognome"
|
||||
"SwapFirstAndLastNames": "Scambia nome e cognome",
|
||||
"ForbidCreateVacancyPermission": "Vieta creazione posizione aperta",
|
||||
"ForbidCreateVacancyPermissionDescription": "Vieta agli utenti di creare nuove posizioni aperte"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "La candidatura esiste già",
|
||||
|
||||
@@ -119,7 +119,9 @@
|
||||
"HideArchivedVacancies": "アーカイブされた求人を非表示",
|
||||
"HideApplicantsFromArchivedVacancies": "アーカイブされた求人から非表示",
|
||||
"CreateNewSkills": "既存のスキルが見つからない場合は新しいスキルを作成",
|
||||
"SwapFirstAndLastNames": "名と姓を入れ替える"
|
||||
"SwapFirstAndLastNames": "名と姓を入れ替える",
|
||||
"ForbidCreateVacancyPermission": "求人の作成禁止",
|
||||
"ForbidCreateVacancyPermissionDescription": "ユーザーが新しい求人を作成することを禁止します"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "応募はすでに存在します",
|
||||
|
||||
@@ -119,7 +119,9 @@
|
||||
"HideArchivedVacancies": "Ocultar vagas arquivadas",
|
||||
"HideApplicantsFromArchivedVacancies": "Ocultar de vagas arquivadas",
|
||||
"CreateNewSkills": "Criar novas competências se as existentes não forem encontradas",
|
||||
"SwapFirstAndLastNames": "Trocar primeiro e último nome"
|
||||
"SwapFirstAndLastNames": "Trocar primeiro e último nome",
|
||||
"ForbidCreateVacancyPermission": "Proibir criação de vaga",
|
||||
"ForbidCreateVacancyPermissionDescription": "Proíbe os utilizadores de criar novas vagas"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "A candidatura já existe",
|
||||
|
||||
@@ -122,7 +122,9 @@
|
||||
"HideArchivedVacancies": "Скрыть архивные вакансии",
|
||||
"HideApplicantsFromArchivedVacancies": "Скрыть из архивных вакансии",
|
||||
"CreateNewSkills": "Создать навыки, если не найдены существующие",
|
||||
"SwapFirstAndLastNames": "Поменять местами имя и фамилию"
|
||||
"SwapFirstAndLastNames": "Поменять местами имя и фамилию",
|
||||
"ForbidCreateVacancyPermission": "Запретить создание вакансии",
|
||||
"ForbidCreateVacancyPermissionDescription": "Запрещает пользователям создавать новые вакансии"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "Кандидат уже существует",
|
||||
|
||||
@@ -122,7 +122,9 @@
|
||||
"HideArchivedVacancies": "隐藏已归档的职位",
|
||||
"HideApplicantsFromArchivedVacancies": "从已归档职位中隐藏",
|
||||
"CreateNewSkills": "如果未找到现有技能,则创建新技能",
|
||||
"SwapFirstAndLastNames": "交换名字和姓氏"
|
||||
"SwapFirstAndLastNames": "交换名字和姓氏",
|
||||
"ForbidCreateVacancyPermission": "禁止创建职位",
|
||||
"ForbidCreateVacancyPermissionDescription": "禁止用户创建新职位"
|
||||
},
|
||||
"status": {
|
||||
"ApplicationExists": "申请已存在",
|
||||
|
||||
@@ -13,17 +13,24 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { MultipleDraftController } from '@hcengineering/presentation'
|
||||
import { Button, IconAdd, showPopup } from '@hcengineering/ui'
|
||||
import { getClient, MultipleDraftController } from '@hcengineering/presentation'
|
||||
import { HeaderButton, showPopup } from '@hcengineering/ui'
|
||||
import { onDestroy } from 'svelte'
|
||||
import recruit from '../plugin'
|
||||
import CreateCandidate from './CreateCandidate.svelte'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { RecruitEvents } from '@hcengineering/recruit'
|
||||
import view from '@hcengineering/view'
|
||||
import { AccountRole } from '@hcengineering/core'
|
||||
|
||||
let draftExists = false
|
||||
|
||||
const client = getClient()
|
||||
const draftController = new MultipleDraftController(recruit.mixin.Candidate)
|
||||
const newRecruitKeyBindingPromise = client
|
||||
.findOne(view.class.Action, { _id: recruit.action.CreateTalent })
|
||||
.then((p) => p?.keyBinding)
|
||||
|
||||
onDestroy(
|
||||
draftController.hasNext((res) => {
|
||||
draftExists = res
|
||||
@@ -34,35 +41,36 @@
|
||||
showPopup(CreateCandidate, { shouldSaveDraft: true }, 'top')
|
||||
Analytics.handleEvent(RecruitEvents.NewTalentButtonClicked)
|
||||
}
|
||||
|
||||
let mainActionId: string | undefined = undefined
|
||||
let visibleActions: string[] = []
|
||||
function updateActions (draft: boolean): void {
|
||||
mainActionId = draft ? recruit.string.ResumeDraft : recruit.string.CreateTalent
|
||||
visibleActions = [mainActionId]
|
||||
}
|
||||
|
||||
$: updateActions(draftExists)
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader">
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={draftExists ? recruit.string.ResumeDraft : recruit.string.CreateTalent}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
width={'100%'}
|
||||
gap={'large'}
|
||||
on:click={newCandidate}
|
||||
>
|
||||
<div slot="content" class="draft-circle-container">
|
||||
{#if draftExists}
|
||||
<div class="draft-circle" />
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.draft-circle-container {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.draft-circle {
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
background-color: var(--primary-bg-color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
<HeaderButton
|
||||
{client}
|
||||
{mainActionId}
|
||||
{visibleActions}
|
||||
actions={[
|
||||
{
|
||||
id: recruit.string.CreateTalent,
|
||||
label: recruit.string.CreateTalent,
|
||||
accountRole: AccountRole.User,
|
||||
keyBindingPromise: newRecruitKeyBindingPromise,
|
||||
callback: newCandidate
|
||||
},
|
||||
{
|
||||
id: recruit.string.ResumeDraft,
|
||||
label: recruit.string.ResumeDraft,
|
||||
draft: true,
|
||||
accountRole: AccountRole.User,
|
||||
keyBindingPromise: newRecruitKeyBindingPromise,
|
||||
callback: newCandidate
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
"@hcengineering/task": "^0.6.20",
|
||||
"@hcengineering/calendar": "^0.6.24",
|
||||
"@hcengineering/ui": "^0.6.15",
|
||||
"@hcengineering/tags": "^0.6.16"
|
||||
"@hcengineering/tags": "^0.6.16",
|
||||
"@hcengineering/view": "^0.6.13"
|
||||
},
|
||||
"repository": "https://github.com/hcengineering/platform",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Attribute, Class, Doc, Mixin, Ref, Status } from '@hcengineering/core'
|
||||
import type { Attribute, Class, Doc, Mixin, Permission, Ref, Status } from '@hcengineering/core'
|
||||
import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { ProjectTypeDescriptor, TaskType } from '@hcengineering/task'
|
||||
import { AnyComponent, Location, ResolvedLocation } from '@hcengineering/ui'
|
||||
import type { Applicant, ApplicantMatch, Candidate, Opinion, Review, Vacancy, VacancyList } from './types'
|
||||
import { Action } from '@hcengineering/view'
|
||||
|
||||
export * from './types'
|
||||
export * from './analytics'
|
||||
@@ -45,6 +46,9 @@ const recruit = plugin(recruitId, {
|
||||
descriptors: {
|
||||
VacancyType: '' as Ref<ProjectTypeDescriptor>
|
||||
},
|
||||
action: {
|
||||
CreateTalent: '' as Ref<Action<Doc, any>>
|
||||
},
|
||||
mixin: {
|
||||
Candidate: '' as Ref<Mixin<Candidate>>,
|
||||
VacancyList: '' as Ref<Mixin<VacancyList>>,
|
||||
@@ -63,7 +67,9 @@ const recruit = plugin(recruitId, {
|
||||
Application: '' as IntlString,
|
||||
Vacancy: '' as IntlString,
|
||||
Review: '' as IntlString,
|
||||
Talent: '' as IntlString
|
||||
Talent: '' as IntlString,
|
||||
ForbidCreateVacancyPermission: '' as IntlString,
|
||||
ForbidCreateVacancyPermissionDescription: '' as IntlString
|
||||
},
|
||||
icon: {
|
||||
RecruitApplication: '' as Asset,
|
||||
@@ -87,6 +93,9 @@ const recruit = plugin(recruitId, {
|
||||
},
|
||||
taskTypes: {
|
||||
Applicant: '' as Ref<TaskType>
|
||||
},
|
||||
permission: {
|
||||
ForbidCreateVacancy: '' as Ref<Permission>
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -42,6 +42,14 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
let spacePermissions = descriptor.availablePermissions
|
||||
if (spaceType._id === core.spaceType.SpacesType) {
|
||||
const additionalPermissionsQuery = createQuery()
|
||||
additionalPermissionsQuery.query(core.class.Permission, { scope: 'workspace' }, (res) => {
|
||||
spacePermissions = descriptor.availablePermissions.concat(res.map((r) => r._id))
|
||||
})
|
||||
}
|
||||
|
||||
let role: Role | undefined
|
||||
const roleQuery = createQuery()
|
||||
$: roleQuery.query(core.class.Role, { _id: objectId }, (res) => {
|
||||
@@ -66,7 +74,7 @@
|
||||
ObjectBoxPopup,
|
||||
{
|
||||
_class: core.class.Permission,
|
||||
docQuery: { _id: { $in: descriptor.availablePermissions } },
|
||||
docQuery: { _id: { $in: spacePermissions } },
|
||||
multiSelect: true,
|
||||
allowDeselect: true,
|
||||
selectedObjects: role.permissions
|
||||
|
||||
@@ -277,7 +277,9 @@
|
||||
"IssueStatus": "Stav",
|
||||
"Extensions": "Rozšíření",
|
||||
"RoleLabel": "Role: {role}",
|
||||
"UnsetParentIssue": "Odebrat nadřazený úkol"
|
||||
"UnsetParentIssue": "Odebrat nadřazený úkol",
|
||||
"ForbidCreateProjectPermission": "Zakázat vytvoření projektu",
|
||||
"ForbidCreateProjectPermissionDescription": "Zakazuje uživatelům vytvářet nové projekty"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,9 @@
|
||||
"IssueStatus": "Status",
|
||||
"Extensions": "Erweiterungen",
|
||||
"RoleLabel": "Rolle: {role}",
|
||||
"UnsetParentIssue": "Übergeordnete Aufgabe entfernen"
|
||||
"UnsetParentIssue": "Übergeordnete Aufgabe entfernen",
|
||||
"ForbidCreateProjectPermission": "Projekterstellung verbieten",
|
||||
"ForbidCreateProjectPermissionDescription": "Verbietet Benutzern das Erstellen neuer Projekte"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,9 @@
|
||||
"IssueStatus": "Status",
|
||||
"Extensions": "Extensions",
|
||||
"RoleLabel": "Role: {role}",
|
||||
"UnsetParentIssue": "Unset parent issue"
|
||||
"UnsetParentIssue": "Unset parent issue",
|
||||
"ForbidCreateProjectPermission": "Forbid create project",
|
||||
"ForbidCreateProjectPermissionDescription": "Forbid users creating new projects"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,9 @@
|
||||
"IssueStatus": "Estado",
|
||||
"Extensions": "Extensions",
|
||||
"RoleLabel": "Role: {role}",
|
||||
"UnsetParentIssue": "Unset parent issue"
|
||||
"UnsetParentIssue": "Unset parent issue",
|
||||
"ForbidCreateProjectPermission": "Prohibir crear proyecto",
|
||||
"ForbidCreateProjectPermissionDescription": "Prohíbe a los usuarios crear nuevos proyectos"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,9 @@
|
||||
"IssueStatus": "Statut",
|
||||
"Extensions": "Extensions",
|
||||
"RoleLabel": "Rôle : {role}",
|
||||
"UnsetParentIssue": "Désélectionner l'issue parent"
|
||||
"UnsetParentIssue": "Désélectionner l'issue parent",
|
||||
"ForbidCreateProjectPermission": "Interdire la création de projet",
|
||||
"ForbidCreateProjectPermissionDescription": "Interdit aux utilisateurs de créer de nouveaux projets"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,9 @@
|
||||
"IssueStatus": "Stato",
|
||||
"Extensions": "Estensioni",
|
||||
"RoleLabel": "Ruolo: {role}",
|
||||
"UnsetParentIssue": "Annulla l'issue genitore"
|
||||
"UnsetParentIssue": "Annulla l'issue genitore",
|
||||
"ForbidCreateProjectPermission": "Vieta creazione progetto",
|
||||
"ForbidCreateProjectPermissionDescription": "Vieta agli utenti di creare nuovi progetti"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,9 @@
|
||||
"IssueStatus": "ステータス",
|
||||
"Extensions": "拡張機能",
|
||||
"RoleLabel": "役割: {role}",
|
||||
"UnsetParentIssue": "親イシューの設定を解除"
|
||||
"UnsetParentIssue": "親イシューの設定を解除",
|
||||
"ForbidCreateProjectPermission": "プロジェクト作成禁止",
|
||||
"ForbidCreateProjectPermissionDescription": "ユーザーが新しいプロジェクトを作成することを禁止します"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,9 @@
|
||||
"IssueStatus": "Estado",
|
||||
"Extensions": "Extensions",
|
||||
"RoleLabel": "Cargo: {role}",
|
||||
"UnsetParentIssue": "Desmarcar problema pai"
|
||||
"UnsetParentIssue": "Desmarcar problema pai",
|
||||
"ForbidCreateProjectPermission": "Proibir criação de projeto",
|
||||
"ForbidCreateProjectPermissionDescription": "Proíbe os usuários de criar novos projetos"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,9 @@
|
||||
"IssueStatus": "Статус",
|
||||
"Extensions": "Дополнительно",
|
||||
"RoleLabel": "Роль: {role}",
|
||||
"UnsetParentIssue": "Снять родительскую задачу"
|
||||
"UnsetParentIssue": "Снять родительскую задачу",
|
||||
"ForbidCreateProjectPermission": "Запретить создание проекта",
|
||||
"ForbidCreateProjectPermissionDescription": "Запрещает пользователям создавать новые проекты"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
||||
@@ -287,7 +287,9 @@
|
||||
"IssueStatus": "状态",
|
||||
"Extensions": "扩展",
|
||||
"RoleLabel": "角色:{role}",
|
||||
"UnsetParentIssue": "取消父问题"
|
||||
"UnsetParentIssue": "取消父问题",
|
||||
"ForbidCreateProjectPermission": "禁止创建项目",
|
||||
"ForbidCreateProjectPermissionDescription": "禁止用户创建新项目"
|
||||
},
|
||||
"status": {}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { AccountRole, Ref, Space, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
|
||||
import core, { AccountRole, Ref, Space } from '@hcengineering/core'
|
||||
import { MultipleDraftController, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { TrackerEvents } from '@hcengineering/tracker'
|
||||
import { Button, ButtonWithDropdown, IconAdd, IconDropdown, SelectPopupValueType, showPopup } from '@hcengineering/ui'
|
||||
import { HeaderButton, showPopup } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
import { onDestroy } from 'svelte'
|
||||
@@ -27,16 +27,36 @@
|
||||
export let currentSpace: Ref<Space> | undefined
|
||||
|
||||
let closed = true
|
||||
|
||||
let draftExists = false
|
||||
let projectExists = false
|
||||
let loading = true
|
||||
|
||||
const query = createQuery()
|
||||
const client = getClient()
|
||||
const draftController = new MultipleDraftController(tracker.ids.IssueDraft)
|
||||
const newIssueKeyBindingPromise = client
|
||||
.findOne(view.class.Action, { _id: tracker.action.NewIssue })
|
||||
.then((p) => p?.keyBinding)
|
||||
|
||||
onDestroy(
|
||||
draftController.hasNext((res) => {
|
||||
draftExists = res
|
||||
})
|
||||
)
|
||||
async function newIssue (): Promise<void> {
|
||||
|
||||
query.query(tracker.class.Project, {}, (res) => {
|
||||
projectExists = res.length > 0
|
||||
loading = false
|
||||
})
|
||||
|
||||
function newProject (): void {
|
||||
closed = false
|
||||
showPopup(tracker.component.CreateProject, {}, 'top', () => {
|
||||
closed = true
|
||||
})
|
||||
}
|
||||
|
||||
function newIssue (): void {
|
||||
closed = false
|
||||
Analytics.handleEvent(TrackerEvents.NewIssueButtonClicked)
|
||||
showPopup(CreateIssue, { space: currentSpace, shouldSaveDraft: true }, 'top', () => {
|
||||
@@ -44,103 +64,50 @@
|
||||
})
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let projectExists = false
|
||||
|
||||
query.query(tracker.class.Project, {}, (res) => {
|
||||
projectExists = res.length > 0
|
||||
})
|
||||
|
||||
$: label = draftExists || !closed ? tracker.string.ResumeDraft : tracker.string.NewIssue
|
||||
$: dropdownItems = hasAccountRole(getCurrentAccount(), AccountRole.User)
|
||||
? [
|
||||
{
|
||||
id: tracker.string.CreateProject,
|
||||
label: tracker.string.CreateProject
|
||||
},
|
||||
{
|
||||
id: tracker.string.NewIssue,
|
||||
label
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
id: tracker.string.NewIssue,
|
||||
label
|
||||
}
|
||||
]
|
||||
const client = getClient()
|
||||
|
||||
let keys: string[] | undefined = undefined
|
||||
async function dropdownItemSelected (res?: SelectPopupValueType['id']): Promise<void> {
|
||||
if (res == null) return
|
||||
|
||||
if (res === tracker.string.CreateProject) {
|
||||
closed = false
|
||||
showPopup(tracker.component.CreateProject, {}, 'top', () => {
|
||||
closed = true
|
||||
})
|
||||
let mainActionId: string | undefined = undefined
|
||||
let visibleActions: string[] = []
|
||||
function updateActions (draft: boolean, project: boolean, closed: boolean): void {
|
||||
mainActionId = draft || !closed ? tracker.string.ResumeDraft : tracker.string.NewIssue
|
||||
if (project) {
|
||||
visibleActions = [tracker.string.CreateProject, mainActionId, tracker.string.Import]
|
||||
} else {
|
||||
await newIssue()
|
||||
visibleActions = [tracker.string.CreateProject]
|
||||
}
|
||||
}
|
||||
|
||||
void client.findOne(view.class.Action, { _id: tracker.action.NewIssue }).then((p) => (keys = p?.keyBinding))
|
||||
$: updateActions(draftExists, projectExists, closed)
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader">
|
||||
{#if projectExists}
|
||||
<ButtonWithDropdown
|
||||
icon={IconAdd}
|
||||
justify={'left'}
|
||||
kind={'primary'}
|
||||
{label}
|
||||
on:click={newIssue}
|
||||
{dropdownItems}
|
||||
dropdownIcon={IconDropdown}
|
||||
on:dropdown-selected={(ev) => {
|
||||
dropdownItemSelected(ev.detail)
|
||||
}}
|
||||
mainButtonId={'new-issue'}
|
||||
showTooltipMain={{
|
||||
direction: 'bottom',
|
||||
label,
|
||||
keys
|
||||
}}
|
||||
>
|
||||
<div slot="content" class="draft-circle-container">
|
||||
{#if draftExists}
|
||||
<div class="draft-circle" />
|
||||
{/if}
|
||||
</div>
|
||||
</ButtonWithDropdown>
|
||||
{:else}
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
justify="left"
|
||||
kind="primary"
|
||||
label={tracker.string.CreateProject}
|
||||
width="100%"
|
||||
on:click={() => {
|
||||
showPopup(tracker.component.CreateProject, {}, 'top', () => {
|
||||
closed = true
|
||||
})
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.draft-circle-container {
|
||||
margin-left: auto;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.draft-circle {
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
background-color: var(--primary-bg-color);
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
<HeaderButton
|
||||
{loading}
|
||||
{client}
|
||||
{mainActionId}
|
||||
{visibleActions}
|
||||
actions={[
|
||||
{
|
||||
id: tracker.string.CreateProject,
|
||||
label: tracker.string.CreateProject,
|
||||
accountRole: AccountRole.User,
|
||||
callback: newProject
|
||||
},
|
||||
{
|
||||
id: tracker.string.ResumeDraft,
|
||||
label: tracker.string.ResumeDraft,
|
||||
draft: true,
|
||||
keyBindingPromise: newIssueKeyBindingPromise,
|
||||
callback: newIssue
|
||||
},
|
||||
{
|
||||
id: tracker.string.NewIssue,
|
||||
label: tracker.string.NewIssue,
|
||||
keyBindingPromise: newIssueKeyBindingPromise,
|
||||
callback: newIssue
|
||||
},
|
||||
{
|
||||
id: tracker.string.Import,
|
||||
label: tracker.string.Import,
|
||||
accountRole: AccountRole.User,
|
||||
callback: newIssue
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -29,7 +29,8 @@ import {
|
||||
Space,
|
||||
Status,
|
||||
Timestamp,
|
||||
Type
|
||||
Type,
|
||||
type Permission
|
||||
} from '@hcengineering/core'
|
||||
import { Asset, IntlString, Plugin, Resource, plugin } from '@hcengineering/platform'
|
||||
import { Preference } from '@hcengineering/preference'
|
||||
@@ -519,7 +520,9 @@ const pluginState = plugin(trackerId, {
|
||||
RelatedIssues: '' as IntlString,
|
||||
Issue: '' as IntlString,
|
||||
NewProject: '' as IntlString,
|
||||
UnsetParentIssue: '' as IntlString
|
||||
UnsetParentIssue: '' as IntlString,
|
||||
ForbidCreateProjectPermission: '' as IntlString,
|
||||
ForbidCreateProjectPermissionDescription: '' as IntlString
|
||||
},
|
||||
extensions: {
|
||||
IssueListHeader: '' as ComponentExtensionId,
|
||||
@@ -529,6 +532,9 @@ const pluginState = plugin(trackerId, {
|
||||
taskTypes: {
|
||||
Issue: '' as Ref<TaskType>,
|
||||
SubIssue: '' as Ref<TaskType>
|
||||
},
|
||||
permission: {
|
||||
ForbidCreateProject: '' as Ref<Permission>
|
||||
}
|
||||
})
|
||||
export default pluginState
|
||||
|
||||
@@ -45,7 +45,7 @@ import { BaseMiddleware } from '@hcengineering/server-core'
|
||||
export class SpacePermissionsMiddleware extends BaseMiddleware implements Middleware {
|
||||
private whitelistSpaces = new Set<Ref<Space>>()
|
||||
private assignmentBySpace: Record<Ref<Space>, RolesAssignment> = {}
|
||||
private permissionsBySpace: Record<Ref<Space>, Record<AccountUuid, Set<Ref<Permission>>>> = {}
|
||||
private permissionsBySpace: Record<Ref<Space>, Record<AccountUuid, Set<Permission>>> = {}
|
||||
private typeBySpace: Record<Ref<Space>, Ref<SpaceType>> = {}
|
||||
wasInit: Promise<void> | boolean = false
|
||||
|
||||
@@ -80,11 +80,20 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
}
|
||||
}
|
||||
|
||||
private getPermissions (): Permission[] {
|
||||
return this.context.modelDb.findAllSync(core.class.Permission, {})
|
||||
}
|
||||
|
||||
private getRoles (spaceTypeId: Ref<SpaceType>): Role[] {
|
||||
return this.context.modelDb.findAllSync(core.class.Role, { attachedTo: spaceTypeId })
|
||||
}
|
||||
|
||||
private setPermissions (spaceId: Ref<Space>, roles: Role[], assignment: RolesAssignment): void {
|
||||
private setPermissions (
|
||||
spaceId: Ref<Space>,
|
||||
roles: Role[],
|
||||
assignment: RolesAssignment,
|
||||
permissions: Permission[]
|
||||
): void {
|
||||
for (const role of roles) {
|
||||
const roleMembers: AccountUuid[] = assignment[role._id] ?? []
|
||||
|
||||
@@ -94,7 +103,9 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
}
|
||||
|
||||
for (const permission of role.permissions) {
|
||||
this.permissionsBySpace[spaceId][member].add(permission)
|
||||
const p = permissions.find((p) => p._id === permission)
|
||||
if (p === undefined) continue
|
||||
this.permissionsBySpace[spaceId][member].add(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +138,7 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
|
||||
this.assignmentBySpace[space._id] = requiredValues
|
||||
|
||||
this.setPermissions(space._id, this.getRoles(spaceType._id), asMixin)
|
||||
this.setPermissions(space._id, this.getRoles(spaceType._id), asMixin, this.getPermissions())
|
||||
}
|
||||
|
||||
private isTypedSpaceClass (_class: Ref<Class<Space>>): boolean {
|
||||
@@ -145,11 +156,16 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
*
|
||||
* Checks if the required permission is present in the space for the given context
|
||||
*/
|
||||
private checkPermission (ctx: MeasureContext<SessionData>, space: Ref<TypedSpace>, id: Ref<Permission>): boolean {
|
||||
private checkPermission (ctx: MeasureContext<SessionData>, space: Ref<TypedSpace>, tx: TxCUD<Doc>): boolean {
|
||||
const account = ctx.contextData.account
|
||||
const permissions = this.permissionsBySpace[space]?.[account.uuid] ?? null
|
||||
const permissions = this.permissionsBySpace[space]?.[account.uuid] ?? []
|
||||
for (const permission of permissions) {
|
||||
if (permission.txClass === undefined || permission.txClass !== tx._class) continue
|
||||
if (permission.objectClass !== undefined && permission.objectClass !== tx.objectClass) continue
|
||||
return permission.forbid !== undefined ? !permission.forbid : true
|
||||
}
|
||||
|
||||
return permissions !== null && permissions.has(id)
|
||||
return true
|
||||
}
|
||||
|
||||
private throwForbidden (): void {
|
||||
@@ -222,7 +238,7 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
this.assignmentBySpace[spaceId] = requiredValues
|
||||
|
||||
this.permissionsBySpace[tx.objectId] = {}
|
||||
this.setPermissions(spaceId, this.getRoles(spaceType._id), assignment)
|
||||
this.setPermissions(spaceId, this.getRoles(spaceType._id), assignment, this.getPermissions())
|
||||
}
|
||||
|
||||
private handleRemove (tx: TxCUD<Space>): void {
|
||||
@@ -293,7 +309,7 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
targetRole.permissions = updateTx.operations.permissions
|
||||
|
||||
this.permissionsBySpace[spaceId] = {}
|
||||
this.setPermissions(spaceId, roles, assignment)
|
||||
this.setPermissions(spaceId, roles, assignment, this.getPermissions())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,20 +365,21 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
const cudTx = tx as TxCUD<Doc>
|
||||
const h = this.context.hierarchy
|
||||
const isSpace = h.isDerived(cudTx.objectClass, core.class.Space)
|
||||
// NOTE: in assumption that we want to control permissions for space itself on that space level
|
||||
// and not on the system's spaces space level for now
|
||||
const targetSpaceId = (isSpace ? cudTx.objectId : cudTx.objectSpace) as Ref<Space>
|
||||
|
||||
this.checkSpacePermissions(ctx, cudTx, cudTx.objectSpace)
|
||||
if (isSpace) {
|
||||
this.checkSpacePermissions(ctx, cudTx, cudTx.objectId as Ref<Space>)
|
||||
}
|
||||
}
|
||||
|
||||
private checkSpacePermissions (ctx: MeasureContext, cudTx: TxCUD<Doc>, targetSpaceId: Ref<Space>): void {
|
||||
if (this.whitelistSpaces.has(targetSpaceId)) {
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE: move this checking logic later to be defined in some server plugins?
|
||||
// so they can contribute checks into the middleware for their custom permissions?
|
||||
if (tx._class === core.class.TxRemoveDoc) {
|
||||
if (this.checkPermission(ctx, targetSpaceId as Ref<TypedSpace>, core.permission.ForbidDeleteObject)) {
|
||||
this.throwForbidden()
|
||||
}
|
||||
if (!this.checkPermission(ctx, targetSpaceId as Ref<TypedSpace>, cudTx)) {
|
||||
this.throwForbidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export class DocumentsPage extends CommonPage {
|
||||
readonly popupMoveDocument: DocumentMovePopup
|
||||
|
||||
readonly buttonCreateDocument = (): Locator =>
|
||||
this.page.locator('div[data-float="navigator"] button[id="new-document"]')
|
||||
this.page.locator('div[data-float="navigator"] button[id="document-string-CreateDocument"]')
|
||||
|
||||
readonly buttonDocumentWrapper = (name: string): Locator =>
|
||||
this.page.locator(`button.hulyNavItem-container:has-text("${name}")`)
|
||||
|
||||
@@ -10,7 +10,8 @@ export class IssuesPage extends CommonTrackerPage {
|
||||
modelSelectorAll = (): Locator => this.page.locator('label[data-id="tab-all"]')
|
||||
issues = (): Locator => this.page.locator('.antiPanel-navigator').locator('text="Issues"')
|
||||
subIssues = (): Locator => this.page.locator('button:has-text("Add sub-issue")')
|
||||
newIssue = (): Locator => this.page.locator('#new-issue')
|
||||
newIssue = (): Locator => this.page.locator('#tracker-string-NewIssue')
|
||||
draftIssue = (): Locator => this.page.locator('#tracker-string-ResumeDraft')
|
||||
modelSelectorActive = (): Locator => this.page.locator('label[data-id="tab-active"]')
|
||||
modelSelectorBacklog = (): Locator => this.page.locator('label[data-id="tab-backlog"]')
|
||||
buttonCreateNewIssue = (): Locator => this.page.locator('button > div', { hasText: 'New issue' })
|
||||
@@ -156,7 +157,7 @@ export class IssuesPage extends CommonTrackerPage {
|
||||
|
||||
estimationSpan = (): Locator => this.page.locator('.estimation-container >> span').first()
|
||||
okButton = (): Locator => this.page.getByRole('button', { name: 'Ok', exact: true })
|
||||
newIssueButton = (): Locator => this.page.locator('#new-issue')
|
||||
newIssueButton = (): Locator => this.page.locator('#tracker-string-NewIssue')
|
||||
issueNameInput = (): Locator => this.page.locator('#issue-name >> input')
|
||||
issueDescriptionInput = (): Locator => this.page.locator('#issue-description >> [contenteditable]')
|
||||
statusEditor = (): Locator => this.page.locator('#status-editor')
|
||||
@@ -232,6 +233,10 @@ export class IssuesPage extends CommonTrackerPage {
|
||||
await this.newIssue().click()
|
||||
}
|
||||
|
||||
async clickOnDraftIssue (): Promise<void> {
|
||||
await this.draftIssue().click()
|
||||
}
|
||||
|
||||
async navigateToMyIssues (): Promise<void> {
|
||||
await this.myIssuesButton().click()
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ test.describe('Tracker sub-issues tests', () => {
|
||||
await fillIssueForm(page, props)
|
||||
await page.keyboard.press('Escape')
|
||||
await page.keyboard.press('Escape')
|
||||
await issuesPage.clickOnNewIssue()
|
||||
await issuesPage.clickOnDraftIssue()
|
||||
await checkIssueDraft(page, props)
|
||||
})
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ test.describe('Tracker tests', () => {
|
||||
await issuesPage.inputTextPlaceholderFill('1')
|
||||
await issuesPage.setDueDate('19')
|
||||
await issuesPage.pressEscapeTwice()
|
||||
await issuesPage.clickOnNewIssue()
|
||||
await issuesPage.clickOnDraftIssue()
|
||||
await checkIssueDraft(page, {
|
||||
name: issueName,
|
||||
description: issueName,
|
||||
|
||||
Reference in New Issue
Block a user