mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Reuse default invite settings in link generation (#10651)
* Reuse default invite settings in link generation Signed-off-by: Artem Savchenko <armisav@gmail.com> * Clean up Signed-off-by: Artem Savchenko <armisav@gmail.com> * Capability check Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix warning Signed-off-by: Artem Savchenko <armisav@gmail.com> --------- Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
import { AccountRole, getCurrentAccount, hasAccountRole, Timestamp } from '@hcengineering/core'
|
||||
import { copyTextToClipboard, createQuery } from '@hcengineering/presentation'
|
||||
import setting, { RoleCapability } from '@hcengineering/setting'
|
||||
import { getDefaultInviteRole, resolveInviteSettings } from '@hcengineering/setting-resources'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { AnySvelteComponent, Button, EditBox, Grid, Label, Loading, MiniToggle, ticker } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -38,24 +39,18 @@
|
||||
limit: number | undefined
|
||||
}
|
||||
|
||||
const defaultInviteRole: AccountRole = getDefaultInviteRole()
|
||||
|
||||
$: !ignoreSettings &&
|
||||
query.query(setting.class.InviteSettings, {}, (set) => {
|
||||
if (set !== undefined && set.length > 0) {
|
||||
expHours = set[0].expirationTime
|
||||
emailMask = set[0].emailMask
|
||||
limit = set[0].limit
|
||||
if (role == null) {
|
||||
role = set[0].defaultInviteRole ?? AccountRole.User
|
||||
}
|
||||
} else {
|
||||
expHours = 48
|
||||
limit = -1
|
||||
if (role == null) {
|
||||
role = AccountRole.User
|
||||
}
|
||||
const state = resolveInviteSettings(set?.[0])
|
||||
expHours = state.expirationTime
|
||||
emailMask = state.emailMask
|
||||
limit = state.limit
|
||||
if (role == null) {
|
||||
role = state.defaultInviteRole
|
||||
}
|
||||
|
||||
if (limit === -1) noLimit = true
|
||||
if (state.noLimit) noLimit = true
|
||||
|
||||
defaultValues = {
|
||||
expirationTime: expHours,
|
||||
@@ -163,7 +158,7 @@
|
||||
{#if userRoleSelectComponent}
|
||||
<svelte:component
|
||||
this={userRoleSelectComponent}
|
||||
selected={role ?? AccountRole.User}
|
||||
selected={role ?? defaultInviteRole}
|
||||
on:selected={handleInviteRoleSelected}
|
||||
/>
|
||||
{/if}
|
||||
@@ -201,7 +196,7 @@
|
||||
if (!canGenerateInviteLinks) return
|
||||
const effectiveLimit = limit ?? 0
|
||||
if (effectiveLimit > 0 || noLimit) {
|
||||
void getLink(expHours, emailMask, limit, role ?? AccountRole.User)
|
||||
void getLink(expHours, emailMask, limit, role ?? defaultInviteRole)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
"build:watch": "compile ui",
|
||||
"_phase:build": "compile ui",
|
||||
"_phase:format": "format src",
|
||||
"_phase:validate": "compile validate"
|
||||
"_phase:validate": "compile validate",
|
||||
"_phase:test": "jest --passWithNoTests --silent",
|
||||
"test": "jest --passWithNoTests --silent"
|
||||
},
|
||||
"devDependencies": {
|
||||
"svelte-loader": "^3.2.0",
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
//
|
||||
// Copyright © 2026 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.
|
||||
//
|
||||
|
||||
import { AccountRole } from '@hcengineering/core'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import type { InviteSettings } from '@hcengineering/setting'
|
||||
import {
|
||||
DEFAULT_INVITE_LINK_GENERATOR_ROLES,
|
||||
getDefaultInviterRoles,
|
||||
getDefaultInviteRole,
|
||||
INVITE_SETTINGS_DEFAULT_EXPIRATION_HOURS,
|
||||
INVITE_SETTINGS_DEFAULT_LIMIT,
|
||||
normalizeInviteRole,
|
||||
normalizeInviteRoles,
|
||||
resolveInviteSettings
|
||||
} from '../inviteSettingsUtils'
|
||||
|
||||
jest.mock('@hcengineering/platform', () => {
|
||||
const actual = jest.requireActual('@hcengineering/platform')
|
||||
return {
|
||||
...actual,
|
||||
getMetadata: jest.fn()
|
||||
}
|
||||
})
|
||||
|
||||
const mockGetMetadata = getMetadata as jest.MockedFunction<typeof getMetadata>
|
||||
|
||||
function inviteDoc (
|
||||
partial: Partial<InviteSettings> & Pick<InviteSettings, 'expirationTime' | 'emailMask' | 'limit'>
|
||||
): InviteSettings {
|
||||
return partial as InviteSettings
|
||||
}
|
||||
|
||||
describe('inviteSettingsUtils', () => {
|
||||
beforeEach(() => {
|
||||
mockGetMetadata.mockReturnValue(undefined)
|
||||
})
|
||||
|
||||
describe('normalizeInviteRole', () => {
|
||||
it('maps string role names case-insensitively', () => {
|
||||
expect(normalizeInviteRole('GUEST', AccountRole.User)).toBe(AccountRole.Guest)
|
||||
expect(normalizeInviteRole('User', AccountRole.Guest)).toBe(AccountRole.User)
|
||||
expect(normalizeInviteRole('MAINTAINER', AccountRole.Guest)).toBe(AccountRole.Maintainer)
|
||||
expect(normalizeInviteRole('owner', AccountRole.Guest)).toBe(AccountRole.Owner)
|
||||
})
|
||||
|
||||
it('returns valid AccountRole numbers as-is', () => {
|
||||
expect(normalizeInviteRole(AccountRole.Maintainer, AccountRole.Guest)).toBe(AccountRole.Maintainer)
|
||||
})
|
||||
|
||||
it('returns fallback for unknown string', () => {
|
||||
expect(normalizeInviteRole('admin', AccountRole.User)).toBe(AccountRole.User)
|
||||
})
|
||||
|
||||
it('returns fallback for undefined', () => {
|
||||
expect(normalizeInviteRole(undefined, AccountRole.Owner)).toBe(AccountRole.Owner)
|
||||
})
|
||||
|
||||
it('returns fallback for invalid number', () => {
|
||||
expect(normalizeInviteRole(999 as unknown as AccountRole, AccountRole.Guest)).toBe(AccountRole.Guest)
|
||||
})
|
||||
})
|
||||
|
||||
describe('normalizeInviteRoles', () => {
|
||||
it('returns fallback copy when values missing or empty', () => {
|
||||
const fallback = [AccountRole.Guest, AccountRole.User]
|
||||
expect(normalizeInviteRoles(undefined, fallback)).toEqual(fallback)
|
||||
expect(normalizeInviteRoles([], fallback)).toEqual(fallback)
|
||||
expect(normalizeInviteRoles(undefined, fallback)).not.toBe(fallback)
|
||||
})
|
||||
|
||||
it('maps and deduplicates roles', () => {
|
||||
expect(normalizeInviteRoles(['user', 'USER', 'maintainer'], [AccountRole.Guest])).toEqual([
|
||||
AccountRole.User,
|
||||
AccountRole.Maintainer
|
||||
])
|
||||
})
|
||||
|
||||
it('maps unrecognized strings to User (inner fallback)', () => {
|
||||
expect(normalizeInviteRoles(['nope', 'x'], [AccountRole.Guest])).toEqual([AccountRole.User])
|
||||
})
|
||||
})
|
||||
|
||||
describe('getDefaultInviteRole / getDefaultInviterRoles', () => {
|
||||
it('uses User and default generator list when metadata unset', () => {
|
||||
mockGetMetadata.mockReturnValue(undefined)
|
||||
expect(getDefaultInviteRole()).toBe(AccountRole.User)
|
||||
expect(getDefaultInviterRoles()).toEqual(DEFAULT_INVITE_LINK_GENERATOR_ROLES)
|
||||
})
|
||||
|
||||
it('reads default invite role from metadata string', () => {
|
||||
mockGetMetadata.mockReturnValue('maintainer')
|
||||
expect(getDefaultInviteRole()).toBe(AccountRole.Maintainer)
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveInviteSettings', () => {
|
||||
it('returns defaults when doc is undefined', () => {
|
||||
mockGetMetadata.mockReturnValue(undefined)
|
||||
const r = resolveInviteSettings(undefined)
|
||||
expect(r).toEqual({
|
||||
expirationTime: INVITE_SETTINGS_DEFAULT_EXPIRATION_HOURS,
|
||||
emailMask: '',
|
||||
limit: INVITE_SETTINGS_DEFAULT_LIMIT,
|
||||
defaultInviteRole: AccountRole.User,
|
||||
inviteLinkGeneratorRoles: DEFAULT_INVITE_LINK_GENERATOR_ROLES,
|
||||
noLimit: true
|
||||
})
|
||||
})
|
||||
|
||||
it('uses doc fields and noLimit when limit is not -1', () => {
|
||||
const doc = inviteDoc({
|
||||
expirationTime: 12,
|
||||
emailMask: '*@corp.test',
|
||||
limit: 100,
|
||||
defaultInviteRole: AccountRole.Guest,
|
||||
inviteLinkGeneratorRoles: [AccountRole.Owner]
|
||||
})
|
||||
const r = resolveInviteSettings(doc)
|
||||
expect(r.expirationTime).toBe(12)
|
||||
expect(r.emailMask).toBe('*@corp.test')
|
||||
expect(r.limit).toBe(100)
|
||||
expect(r.defaultInviteRole).toBe(AccountRole.Guest)
|
||||
expect(r.inviteLinkGeneratorRoles).toEqual([AccountRole.Owner])
|
||||
expect(r.noLimit).toBe(false)
|
||||
})
|
||||
|
||||
it('sets noLimit true when doc.limit is -1', () => {
|
||||
const doc = inviteDoc({
|
||||
expirationTime: 48,
|
||||
emailMask: '',
|
||||
limit: -1,
|
||||
defaultInviteRole: AccountRole.User,
|
||||
inviteLinkGeneratorRoles: [AccountRole.User]
|
||||
})
|
||||
expect(resolveInviteSettings(doc).noLimit).toBe(true)
|
||||
})
|
||||
|
||||
it('uses DEFAULT_INVITE_LINK_GENERATOR_ROLES copy when doc list empty', () => {
|
||||
const doc = inviteDoc({
|
||||
expirationTime: 48,
|
||||
emailMask: '',
|
||||
limit: -1,
|
||||
defaultInviteRole: AccountRole.User,
|
||||
inviteLinkGeneratorRoles: []
|
||||
})
|
||||
const r = resolveInviteSettings(doc)
|
||||
expect(r.inviteLinkGeneratorRoles).toEqual(DEFAULT_INVITE_LINK_GENERATOR_ROLES)
|
||||
expect(r.inviteLinkGeneratorRoles).not.toBe(DEFAULT_INVITE_LINK_GENERATOR_ROLES)
|
||||
})
|
||||
|
||||
it('normalizes string defaultInviteRole using metadata fallback', () => {
|
||||
mockGetMetadata.mockReturnValue('user')
|
||||
const doc = inviteDoc({
|
||||
expirationTime: 1,
|
||||
emailMask: '',
|
||||
limit: -1,
|
||||
defaultInviteRole: 'guest' as unknown as AccountRole,
|
||||
inviteLinkGeneratorRoles: [AccountRole.User]
|
||||
})
|
||||
expect(resolveInviteSettings(doc).defaultInviteRole).toBe(AccountRole.Guest)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright © 2026 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.
|
||||
//
|
||||
|
||||
import { AccountRole, type Account } from '@hcengineering/core'
|
||||
import { RoleCapability } from '@hcengineering/setting'
|
||||
import { DEFAULT_INVITE_LINK_GENERATOR_ROLES } from '../inviteSettingsUtils'
|
||||
import { getRolesForCapability, hasRoleCapability } from '../roleCapability'
|
||||
|
||||
function account (role: AccountRole): Account {
|
||||
return { role } as any
|
||||
}
|
||||
|
||||
describe('roleCapability (Generate invite link permission)', () => {
|
||||
describe('getRolesForCapability', () => {
|
||||
it('uses RoleCapabilitySettings when set', () => {
|
||||
expect(
|
||||
getRolesForCapability(RoleCapability.GenerateInviteLink, {
|
||||
[RoleCapability.GenerateInviteLink]: [AccountRole.Owner]
|
||||
})
|
||||
).toEqual([AccountRole.Owner])
|
||||
})
|
||||
|
||||
it('uses inviteLinkGeneratorRoles when capability map missing', () => {
|
||||
expect(
|
||||
getRolesForCapability(RoleCapability.GenerateInviteLink, undefined, [AccountRole.Guest, AccountRole.User])
|
||||
).toEqual([AccountRole.Guest, AccountRole.User])
|
||||
})
|
||||
|
||||
it('falls back to DEFAULT_INVITE_LINK_GENERATOR_ROLES (shared with invite settings)', () => {
|
||||
expect(getRolesForCapability(RoleCapability.GenerateInviteLink, undefined, undefined)).toBe(
|
||||
DEFAULT_INVITE_LINK_GENERATOR_ROLES
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasRoleCapability', () => {
|
||||
it('allows User when falling back to default generator roles', () => {
|
||||
expect(
|
||||
hasRoleCapability(account(AccountRole.User), RoleCapability.GenerateInviteLink, undefined, undefined)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('denies User when only Owner may generate', () => {
|
||||
expect(
|
||||
hasRoleCapability(account(AccountRole.User), RoleCapability.GenerateInviteLink, undefined, [AccountRole.Owner])
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('allows Owner when only Owner may generate', () => {
|
||||
expect(
|
||||
hasRoleCapability(account(AccountRole.Owner), RoleCapability.GenerateInviteLink, undefined, [AccountRole.Owner])
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -18,7 +18,8 @@
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import setting, { type InviteSettings, type RoleCapabilitySettings, RoleCapability } from '@hcengineering/setting'
|
||||
import { hasRoleCapability } from '../roleCapability'
|
||||
import { getMetadata, translate } from '@hcengineering/platform'
|
||||
import { getDefaultInviterRoles, getDefaultInviteRole, resolveInviteSettings } from '../inviteSettingsUtils'
|
||||
import { translate } from '@hcengineering/platform'
|
||||
import {
|
||||
Breadcrumb,
|
||||
DropdownLabels,
|
||||
@@ -36,41 +37,13 @@
|
||||
|
||||
const client = getClient()
|
||||
|
||||
const configDefaultInviteRole = getMetadata(setting.metadata.DefaultInviteRole)
|
||||
const configInviteLinkGeneratorRoles = getMetadata(setting.metadata.DefaultInviteLinkGeneratorRoles)
|
||||
|
||||
function normalizeRole (value: string | undefined, fallback: AccountRole): AccountRole {
|
||||
if (typeof value === 'string') {
|
||||
const normalizedValue = value.toLowerCase()
|
||||
switch (normalizedValue) {
|
||||
case 'guest':
|
||||
return AccountRole.Guest
|
||||
case 'user':
|
||||
return AccountRole.User
|
||||
case 'maintainer':
|
||||
return AccountRole.Maintainer
|
||||
case 'owner':
|
||||
return AccountRole.Owner
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
function normalizeRoles (values: Array<string> | undefined, fallback: AccountRole[]): AccountRole[] {
|
||||
if (!Array.isArray(values) || values.length === 0) return [...fallback]
|
||||
const mapped = values
|
||||
.map((v) => normalizeRole(v, AccountRole.User))
|
||||
.filter((role, index, arr) => arr.indexOf(role) === index)
|
||||
return mapped.length > 0 ? mapped : [...fallback]
|
||||
}
|
||||
let loading = true
|
||||
let expTime: number = 48
|
||||
let mask: string = ''
|
||||
let limit: number | undefined = -1
|
||||
const defaultGeneratorRoles: AccountRole[] = [AccountRole.User, AccountRole.Maintainer, AccountRole.Owner]
|
||||
|
||||
let defaultInviteRole: AccountRole = normalizeRole(configDefaultInviteRole, AccountRole.User)
|
||||
let inviteLinkGeneratorRoles: AccountRole[] = normalizeRoles(configInviteLinkGeneratorRoles, defaultGeneratorRoles)
|
||||
let defaultInviteRole: AccountRole = getDefaultInviteRole()
|
||||
let inviteLinkGeneratorRoles: AccountRole[] = getDefaultInviterRoles()
|
||||
let noLimit: boolean = true
|
||||
let existingInviteSettings: InviteSettings[] = []
|
||||
let existingRoleCapabilitySettings: {
|
||||
@@ -108,24 +81,13 @@
|
||||
|
||||
function applyInviteSettings (set: InviteSettings[]): void {
|
||||
existingInviteSettings = set
|
||||
if (existingInviteSettings.length > 0) {
|
||||
const first = existingInviteSettings[0]
|
||||
expTime = first.expirationTime
|
||||
mask = first.emailMask
|
||||
limit = first.limit
|
||||
defaultInviteRole = normalizeRole(first.defaultInviteRole, defaultInviteRole)
|
||||
inviteLinkGeneratorRoles =
|
||||
first.inviteLinkGeneratorRoles != null && first.inviteLinkGeneratorRoles.length > 0
|
||||
? normalizeRoles(first.inviteLinkGeneratorRoles, defaultGeneratorRoles)
|
||||
: [...defaultGeneratorRoles]
|
||||
} else {
|
||||
expTime = 48
|
||||
mask = ''
|
||||
limit = -1
|
||||
defaultInviteRole = normalizeRole(configDefaultInviteRole, AccountRole.User)
|
||||
inviteLinkGeneratorRoles = normalizeRoles(configInviteLinkGeneratorRoles, defaultGeneratorRoles)
|
||||
}
|
||||
noLimit = limit === -1
|
||||
const state = resolveInviteSettings(set[0])
|
||||
expTime = state.expirationTime
|
||||
mask = state.emailMask
|
||||
limit = state.limit
|
||||
defaultInviteRole = state.defaultInviteRole
|
||||
inviteLinkGeneratorRoles = state.inviteLinkGeneratorRoles
|
||||
noLimit = state.noLimit
|
||||
loading = false
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { getCurrentAccount } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import setting, { RoleCapability } from '@hcengineering/setting'
|
||||
import { hasRoleCapability } from './roleCapability'
|
||||
import { resolveInviteSettings } from './inviteSettingsUtils'
|
||||
|
||||
/**
|
||||
* Returns whether the current account has the given role capability.
|
||||
@@ -32,10 +33,8 @@ export async function hasRoleCapabilityAsync (capabilityId: RoleCapabilityId | s
|
||||
const firstInvite = inviteSettings[0] as InviteSettings | undefined
|
||||
const firstRoleCap = roleCapabilitySettings[0] as RoleCapabilitySettings | undefined
|
||||
const inviteLinkGeneratorRoles =
|
||||
capabilityId === RoleCapability.GenerateInviteLink &&
|
||||
firstInvite?.inviteLinkGeneratorRoles != null &&
|
||||
firstInvite.inviteLinkGeneratorRoles.length > 0
|
||||
? firstInvite.inviteLinkGeneratorRoles
|
||||
capabilityId === RoleCapability.GenerateInviteLink
|
||||
? resolveInviteSettings(firstInvite).inviteLinkGeneratorRoles
|
||||
: undefined
|
||||
const roleByCapability = firstRoleCap?.roleByCapability
|
||||
const account = getCurrentAccount()
|
||||
|
||||
@@ -82,6 +82,7 @@ import { hasRoleCapabilityAsync } from './hasRoleCapabilityAsync'
|
||||
export * from './store'
|
||||
export { hasRoleCapability, getRolesForCapability } from './roleCapability'
|
||||
export { hasRoleCapabilityAsync } from './hasRoleCapabilityAsync'
|
||||
export * from './inviteSettingsUtils'
|
||||
export {
|
||||
ClassAttributes,
|
||||
ClassAttributesList,
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// Copyright © 2026 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.
|
||||
//
|
||||
|
||||
import { AccountRole } from '@hcengineering/core'
|
||||
import { getMetadata, type Metadata } from '@hcengineering/platform'
|
||||
import setting, { type InviteSettings } from '@hcengineering/setting'
|
||||
|
||||
const settingInviteMetadata = setting.metadata as unknown as {
|
||||
DefaultInviteRole: Metadata<string | undefined>
|
||||
DefaultInviteLinkGeneratorRoles: Metadata<string[] | undefined>
|
||||
}
|
||||
|
||||
const numericAccountRoles = new Set<number>(Object.values(AccountRole).filter((v) => typeof v === 'number') as number[])
|
||||
|
||||
export function normalizeInviteRole (value: string | AccountRole | undefined, fallback: AccountRole): AccountRole {
|
||||
if (value !== undefined && typeof value === 'number' && numericAccountRoles.has(value)) {
|
||||
return value as AccountRole
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const n = value.toLowerCase()
|
||||
switch (n) {
|
||||
case 'guest':
|
||||
return AccountRole.Guest
|
||||
case 'user':
|
||||
return AccountRole.User
|
||||
case 'maintainer':
|
||||
return AccountRole.Maintainer
|
||||
case 'owner':
|
||||
return AccountRole.Owner
|
||||
}
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
export function normalizeInviteRoles (
|
||||
values: Array<string | AccountRole> | undefined,
|
||||
fallback: AccountRole[]
|
||||
): AccountRole[] {
|
||||
if (!Array.isArray(values) || values.length === 0) return [...fallback]
|
||||
const mapped = values.map((v) => normalizeInviteRole(v, AccountRole.User)).filter((r, i, arr) => arr.indexOf(r) === i)
|
||||
return mapped.length > 0 ? mapped : [...fallback]
|
||||
}
|
||||
|
||||
export const DEFAULT_INVITE_LINK_GENERATOR_ROLES: AccountRole[] = [
|
||||
AccountRole.User,
|
||||
AccountRole.Maintainer,
|
||||
AccountRole.Owner
|
||||
]
|
||||
|
||||
export const INVITE_SETTINGS_DEFAULT_EXPIRATION_HOURS = 48
|
||||
export const INVITE_SETTINGS_DEFAULT_LIMIT = -1
|
||||
|
||||
export function getDefaultInviteRole (): AccountRole {
|
||||
return normalizeInviteRole(getMetadata(settingInviteMetadata.DefaultInviteRole), AccountRole.User)
|
||||
}
|
||||
|
||||
export function getDefaultInviterRoles (): AccountRole[] {
|
||||
return normalizeInviteRoles(
|
||||
getMetadata(settingInviteMetadata.DefaultInviteLinkGeneratorRoles),
|
||||
DEFAULT_INVITE_LINK_GENERATOR_ROLES
|
||||
)
|
||||
}
|
||||
|
||||
export interface ResolvedInviteSettings {
|
||||
expirationTime: number
|
||||
emailMask: string
|
||||
limit: number
|
||||
defaultInviteRole: AccountRole
|
||||
inviteLinkGeneratorRoles: AccountRole[]
|
||||
noLimit: boolean
|
||||
}
|
||||
|
||||
export function resolveInviteSettings (doc: InviteSettings | undefined): ResolvedInviteSettings {
|
||||
const metaRole = getDefaultInviteRole()
|
||||
const metaGenerators = getDefaultInviterRoles()
|
||||
|
||||
if (doc != null) {
|
||||
return {
|
||||
expirationTime: doc.expirationTime,
|
||||
emailMask: doc.emailMask,
|
||||
limit: doc.limit,
|
||||
defaultInviteRole: normalizeInviteRole(doc.defaultInviteRole as string | AccountRole | undefined, metaRole),
|
||||
inviteLinkGeneratorRoles:
|
||||
doc.inviteLinkGeneratorRoles != null && doc.inviteLinkGeneratorRoles.length > 0
|
||||
? normalizeInviteRoles(
|
||||
doc.inviteLinkGeneratorRoles as Array<string | AccountRole>,
|
||||
DEFAULT_INVITE_LINK_GENERATOR_ROLES
|
||||
)
|
||||
: [...DEFAULT_INVITE_LINK_GENERATOR_ROLES],
|
||||
noLimit: doc.limit === -1
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
expirationTime: INVITE_SETTINGS_DEFAULT_EXPIRATION_HOURS,
|
||||
emailMask: '',
|
||||
limit: INVITE_SETTINGS_DEFAULT_LIMIT,
|
||||
defaultInviteRole: metaRole,
|
||||
inviteLinkGeneratorRoles: metaGenerators,
|
||||
noLimit: true
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,17 @@ import type { Account } from '@hcengineering/core'
|
||||
import { AccountRole, hasAccountRole } from '@hcengineering/core'
|
||||
import { RoleCapability, type RoleCapabilityId } from '@hcengineering/setting'
|
||||
|
||||
import { DEFAULT_INVITE_LINK_GENERATOR_ROLES } from './inviteSettingsUtils'
|
||||
|
||||
/** Default roles that have each capability when no RoleCapabilitySettings is set */
|
||||
const DEFAULT_ROLES_BY_CAPABILITY: Record<string, AccountRole[]> = {
|
||||
[RoleCapability.GenerateInviteLink]: [AccountRole.User, AccountRole.Maintainer, AccountRole.Owner],
|
||||
[RoleCapability.GenerateInviteLink]: DEFAULT_INVITE_LINK_GENERATOR_ROLES,
|
||||
[RoleCapability.ManageInviteSettings]: [AccountRole.Maintainer, AccountRole.Owner]
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given account has the specified capability.
|
||||
* Uses roleByCapability if provided; otherwise for GenerateInviteLink falls back to inviteLinkGeneratorRoles; otherwise uses built-in defaults.
|
||||
* Uses roleByCapability if provided; otherwise for GenerateInviteLink falls back to inviteLinkGeneratorRoles (from workspace settings / resolved defaults); otherwise {@link DEFAULT_INVITE_LINK_GENERATOR_ROLES}.
|
||||
* @public
|
||||
*/
|
||||
export function hasRoleCapability (
|
||||
|
||||
Reference in New Issue
Block a user