mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
Id override (#10847)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -264,7 +264,13 @@ export function createActions (builder: Builder): void {
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: card.actionImpl.DuplicateCard,
|
||||
action: view.actionImpl.ShowPopup,
|
||||
actionProps: {
|
||||
component: card.component.DuplicateCard,
|
||||
fillProps: {
|
||||
_object: 'value'
|
||||
}
|
||||
},
|
||||
label: card.string.Duplicate,
|
||||
icon: card.icon.Duplicate,
|
||||
input: 'focus',
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
import activity from '@hcengineering/activity'
|
||||
import communication from '@hcengineering/communication'
|
||||
import {
|
||||
type CanCreateCardResource,
|
||||
type Card,
|
||||
@@ -23,6 +22,7 @@ import {
|
||||
type CardViewDefaults,
|
||||
type CreateCardExtension,
|
||||
DOMAIN_CARD,
|
||||
type DuplicateSetting,
|
||||
type ExportExtension,
|
||||
type ExportFunc,
|
||||
type FavoriteCard,
|
||||
@@ -34,6 +34,8 @@ import {
|
||||
type Tag
|
||||
} from '@hcengineering/card'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import communication from '@hcengineering/communication'
|
||||
import converter from '@hcengineering/converter'
|
||||
import core, {
|
||||
AccountRole,
|
||||
type Blobs,
|
||||
@@ -46,6 +48,7 @@ import core, {
|
||||
DOMAIN_SPACE,
|
||||
IndexKind,
|
||||
type MarkupBlobRef,
|
||||
type Mixin as MixinType,
|
||||
type MixinData,
|
||||
type Rank,
|
||||
type Ref,
|
||||
@@ -77,7 +80,7 @@ import presentation from '@hcengineering/model-presentation'
|
||||
import setting from '@hcengineering/model-setting'
|
||||
import view, { type Viewlet } from '@hcengineering/model-view'
|
||||
import workbench, { WidgetType } from '@hcengineering/model-workbench'
|
||||
import converter from '@hcengineering/converter'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { type Asset, getEmbeddedLabel, type IntlString, type Resource } from '@hcengineering/platform'
|
||||
import time, { type ToDo } from '@hcengineering/time'
|
||||
import { PaletteColorIndexes } from '@hcengineering/ui/src/colors'
|
||||
@@ -86,7 +89,6 @@ import { type BuildModelKey, type ViewOptionModel } from '@hcengineering/view'
|
||||
import { createActions } from './actions'
|
||||
import { defineActionPermissions, definePermissions } from './permissions'
|
||||
import card from './plugin'
|
||||
import notification from '@hcengineering/notification'
|
||||
|
||||
export { cardId } from '@hcengineering/card'
|
||||
|
||||
@@ -223,6 +225,13 @@ export class TExportExtension extends TDoc implements ExportExtension {
|
||||
func!: Resource<ExportFunc>
|
||||
}
|
||||
|
||||
@Mixin(card.mixin.DuplicateSetting, card.class.MasterTag)
|
||||
export class TDuplicateSetting extends TMasterTag implements DuplicateSetting {
|
||||
excludedProperties?: string[]
|
||||
excludedRelations?: string[] // ${associationId}_${a|b}
|
||||
excludeMixins?: Ref<MixinType<Doc>>[]
|
||||
}
|
||||
|
||||
export * from './migration'
|
||||
|
||||
const showAllVersionsOption: ViewOptionModel = {
|
||||
@@ -441,7 +450,8 @@ export function createModel (builder: Builder): void {
|
||||
TFavoriteCard,
|
||||
TFavoriteType,
|
||||
TCreateCardExtension,
|
||||
TExportExtension
|
||||
TExportExtension,
|
||||
TDuplicateSetting
|
||||
)
|
||||
|
||||
builder.createDoc(
|
||||
|
||||
@@ -30,7 +30,6 @@ export default mergeIds(cardId, card, {
|
||||
},
|
||||
actionImpl: {
|
||||
DeleteMasterTag: '' as ViewAction,
|
||||
DuplicateCard: '' as ViewAction,
|
||||
EditSpace: '' as ViewAction,
|
||||
CreateChild: '' as ViewAction
|
||||
},
|
||||
@@ -39,7 +38,7 @@ export default mergeIds(cardId, card, {
|
||||
SetParent: '' as Ref<Action<Doc, any>>,
|
||||
UnsetParent: '' as Ref<Action<Doc, any>>,
|
||||
PublicLink: '' as Ref<Action<Doc, any>>,
|
||||
Duplicate: '' as Ref<Action>,
|
||||
Duplicate: '' as Ref<Action<Doc, any>>,
|
||||
CreateChild: '' as Ref<Action>
|
||||
},
|
||||
category: {
|
||||
|
||||
@@ -71,26 +71,14 @@
|
||||
<div class="divider" />
|
||||
{/if}
|
||||
<div class="masterTag">
|
||||
<MasterTagAttributes
|
||||
bind:this={masterTagAttributes}
|
||||
readonly={readonly || h.getAncestors(value._class).some((p) => value.readonlySections?.includes(p))}
|
||||
{value}
|
||||
{ignoreKeys}
|
||||
fourRows={columns === 2}
|
||||
/>
|
||||
<MasterTagAttributes bind:this={masterTagAttributes} {readonly} {value} {ignoreKeys} fourRows={columns === 2} />
|
||||
</div>
|
||||
{#if mixins.length > 0}
|
||||
<div class="divider" />
|
||||
<Grid column={columns} columnGap={0} rowGap={0} alignItems={'start'}>
|
||||
{#each mixins as tag, i (tag._id)}
|
||||
<div class="tag" class:withoutBorder={Math.ceil((i + 1) / columns) === Math.ceil(mixins.length / columns)}>
|
||||
<TagAttributes
|
||||
bind:this={tagAttributes[i]}
|
||||
readonly={readonly || value.readonlySections?.includes(tag._id)}
|
||||
{tag}
|
||||
{value}
|
||||
{ignoreKeys}
|
||||
/>
|
||||
<TagAttributes bind:this={tagAttributes[i]} {readonly} {tag} {value} {ignoreKeys} />
|
||||
</div>
|
||||
{/each}
|
||||
</Grid>
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
<!--
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { DropdownLabelsIntl, Label, Toggle } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import card from '../plugin'
|
||||
import { duplicateCard } from '../utils'
|
||||
import { Card as CardType } from '@hcengineering/card'
|
||||
|
||||
export let value: CardType
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const ancestors = hierarchy.getAncestors(value._class)
|
||||
const mixins = hierarchy.findAllMixins(value)
|
||||
const current = hierarchy.classHierarchyMixin(value._class, card.mixin.DuplicateSetting)
|
||||
|
||||
const mixinsItems = mixins.map((it) => {
|
||||
const cl = hierarchy.getClass(it)
|
||||
return { label: cl.label, id: it }
|
||||
})
|
||||
|
||||
let excludedRelations = new Set<string>(current?.excludedRelations ?? [])
|
||||
let excludedProperties = new Set<string>(current?.excludedProperties ?? [])
|
||||
let excludeMixins = new Set<Ref<Mixin<Doc>>>(current?.excludeMixins ?? [])
|
||||
|
||||
$: selectedMixins = mixins.filter((it) => !excludeMixins.has(it))
|
||||
|
||||
const systemFields = [
|
||||
'_class',
|
||||
'id',
|
||||
'createdOn',
|
||||
'modifiedOn',
|
||||
'modifiedBy',
|
||||
'createdBy',
|
||||
'createdOn',
|
||||
'rank',
|
||||
'title',
|
||||
'space',
|
||||
'version',
|
||||
'icon',
|
||||
'color',
|
||||
'todos',
|
||||
'comments'
|
||||
]
|
||||
const allProperties = hierarchy
|
||||
.getAllAttributes(value._class, core.class.Doc)
|
||||
.values()
|
||||
.toArray()
|
||||
.filter((it) => {
|
||||
if (systemFields.includes(it.name)) return false
|
||||
if (it.readonly === true || it.hidden === true) return false
|
||||
if (it.type._class === core.class.TypeIdentifier) return false
|
||||
return true
|
||||
})
|
||||
.map((p) => {
|
||||
return { label: p.label, id: p.name }
|
||||
})
|
||||
|
||||
$: selectedProperties = allProperties.filter((it) => !excludedProperties.has(it.id)).map((it) => it.id)
|
||||
|
||||
const targets = new Set([...ancestors, ...mixins])
|
||||
const associations = client.getModel().findAllSync(core.class.Association, {})
|
||||
const relationsA = associations.filter((it) => targets.has(it.classB))
|
||||
const relationsB = associations.filter((it) => targets.has(it.classA))
|
||||
|
||||
async function save (): Promise<void> {
|
||||
await duplicateCard(value, {
|
||||
excludedProperties: [...excludedProperties],
|
||||
excludedRelations: [...excludedRelations],
|
||||
excludeMixins: [...excludeMixins]
|
||||
})
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
function propertiesSelected (event: CustomEvent): void {
|
||||
const selectedProperties = event.detail as string[]
|
||||
allProperties.forEach((it) => {
|
||||
if (!selectedProperties.includes(it.id)) {
|
||||
excludedProperties.add(it.id)
|
||||
} else {
|
||||
excludedProperties.delete(it.id)
|
||||
}
|
||||
})
|
||||
excludedProperties = excludedProperties
|
||||
}
|
||||
|
||||
function mixinsSelected (event: CustomEvent): void {
|
||||
const selectedMixins = event.detail as string[]
|
||||
mixins.forEach((it) => {
|
||||
if (!selectedMixins.includes(it)) {
|
||||
excludeMixins.add(it)
|
||||
} else {
|
||||
excludeMixins.delete(it)
|
||||
}
|
||||
})
|
||||
excludeMixins = excludeMixins
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card label={card.string.Duplicate} canSave={true} width={'medium'} okAction={save} on:close>
|
||||
<div class="flex-col flex-gap-2">
|
||||
{#if allProperties.length > 0}
|
||||
<div class="flex-between">
|
||||
<Label label={setting.string.Properties} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={allProperties}
|
||||
multiselect
|
||||
selected={selectedProperties}
|
||||
on:selected={propertiesSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if mixins.length > 0}
|
||||
<div class="flex-between">
|
||||
<Label label={card.string.Tags} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={mixinsItems}
|
||||
multiselect
|
||||
selected={selectedMixins}
|
||||
on:selected={mixinsSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if relationsA.length > 0 || relationsB.length > 0}
|
||||
<div class="divider" />
|
||||
<div>
|
||||
<Label label={card.string.RelationCopyDescr} />
|
||||
</div>
|
||||
<div class="grid">
|
||||
{#each relationsB as assoc}
|
||||
{@const id = `${assoc._id}_b`}
|
||||
<span>{assoc.nameB}</span>
|
||||
<Toggle
|
||||
on={!excludedRelations.has(id)}
|
||||
on:change={(e) => {
|
||||
if (e.detail === true) {
|
||||
excludedRelations.delete(id)
|
||||
} else {
|
||||
excludedRelations.add(id)
|
||||
}
|
||||
excludedRelations = excludedRelations
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{#each relationsA as assoc}
|
||||
{@const id = `${assoc._id}_a`}
|
||||
<span>{assoc.nameA}</span>
|
||||
<Toggle
|
||||
on={!excludedRelations.has(id)}
|
||||
on:change={(e) => {
|
||||
if (e.detail === true) {
|
||||
excludedRelations.delete(id)
|
||||
} else {
|
||||
excludedRelations.add(id)
|
||||
}
|
||||
excludedRelations = excludedRelations
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-auto-rows: minmax(1rem, max-content);
|
||||
justify-content: start;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
row-gap: 0.5rem;
|
||||
column-gap: 1rem;
|
||||
height: min-content;
|
||||
}
|
||||
</style>
|
||||
@@ -277,7 +277,7 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
{#if !_readonly}
|
||||
{#if !readonly}
|
||||
<Button
|
||||
icon={IconMoreH}
|
||||
iconProps={{ size: 'medium' }}
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
}
|
||||
|
||||
$: isLocked = hierarchy.getAncestors(value._class).some((p) => value.readonlySections?.includes(p)) ?? false
|
||||
$: _readonly = readonly || isLocked
|
||||
$: canLock = canLockSection(value.space, $permissionsStore)
|
||||
$: canUnlock = canUnlockSection(value.space, $permissionsStore)
|
||||
|
||||
@@ -85,6 +86,7 @@
|
||||
icon={isLocked ? Lock : Unlock}
|
||||
kind={'link'}
|
||||
size={'medium'}
|
||||
disabled={readonly}
|
||||
showTooltip={{ label: isLocked ? card.string.UnLockSection : card.string.LockSection }}
|
||||
on:click={toggleLock}
|
||||
/>
|
||||
@@ -120,8 +122,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<CardAttributes object={value} _class={value._class} {readonly} {ignoreKeys} {fourRows} showCollaborators />
|
||||
<MarkupProperties doc={value} {readonly} tag={undefined} />
|
||||
<CardAttributes object={value} _class={value._class} readonly={_readonly} {ignoreKeys} {fourRows} showCollaborators />
|
||||
<MarkupProperties doc={value} readonly={_readonly} tag={undefined} />
|
||||
</ExpandCollapse>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
$: isLocked = value.readonlySections?.includes(tag._id) ?? false
|
||||
$: canLock = canLockSection(value.space, $permissionsStore)
|
||||
$: canUnlock = canUnlockSection(value.space, $permissionsStore)
|
||||
$: _readonly = readonly || isLocked
|
||||
|
||||
async function toggleLock (ev: MouseEvent): Promise<void> {
|
||||
ev.stopPropagation()
|
||||
@@ -126,8 +127,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<ExpandCollapse isExpanded={!isCollapsed}>
|
||||
<CardAttributes object={value} _class={tag._id} to={tag.extends} {readonly} {ignoreKeys} />
|
||||
<MarkupProperties doc={value} {readonly} {tag} />
|
||||
<CardAttributes object={value} _class={tag._id} to={tag.extends} readonly={_readonly} {ignoreKeys} />
|
||||
<MarkupProperties doc={value} readonly={_readonly} {tag} />
|
||||
</ExpandCollapse>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
<!--
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { MasterTag } from '@hcengineering/card'
|
||||
import core, { Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import presentation, { Card, getClient } from '@hcengineering/presentation'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { DropdownLabelsIntl, Label, Toggle } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import card from '../../plugin'
|
||||
|
||||
export let masterTag: Ref<MasterTag>
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const ancestors = hierarchy.getAncestors(masterTag)
|
||||
const mixins = hierarchy.getAllPossibleMixins(masterTag)
|
||||
const current = hierarchy.classHierarchyMixin(masterTag, card.mixin.DuplicateSetting)
|
||||
|
||||
const mixinsItems = mixins.map((it) => {
|
||||
const cl = hierarchy.getClass(it)
|
||||
return { label: cl.label, id: it }
|
||||
})
|
||||
|
||||
let excludedRelations = new Set<string>(current?.excludedRelations ?? [])
|
||||
let excludedProperties = new Set<string>(current?.excludedProperties ?? [])
|
||||
let excludeMixins = new Set<Ref<Mixin<Doc>>>(current?.excludeMixins ?? [])
|
||||
|
||||
$: selectedMixins = mixins.filter((it) => !excludeMixins.has(it))
|
||||
|
||||
const systemFields = [
|
||||
'_class',
|
||||
'id',
|
||||
'createdOn',
|
||||
'modifiedOn',
|
||||
'modifiedBy',
|
||||
'createdBy',
|
||||
'createdOn',
|
||||
'rank',
|
||||
'title',
|
||||
'space',
|
||||
'version',
|
||||
'icon',
|
||||
'color',
|
||||
'todos',
|
||||
'comments'
|
||||
]
|
||||
const allProperties = hierarchy
|
||||
.getAllAttributes(masterTag, core.class.Doc)
|
||||
.values()
|
||||
.toArray()
|
||||
.filter((it) => {
|
||||
if (systemFields.includes(it.name)) return false
|
||||
if (it.readonly === true || it.hidden === true) return false
|
||||
if (it.type._class === core.class.TypeIdentifier) return false
|
||||
return true
|
||||
})
|
||||
.map((p) => {
|
||||
return { label: p.label, id: p.name }
|
||||
})
|
||||
|
||||
$: selectedProperties = allProperties.filter((it) => !excludedProperties.has(it.id)).map((it) => it.id)
|
||||
|
||||
const targets = new Set([...ancestors, ...mixins])
|
||||
const associations = client.getModel().findAllSync(core.class.Association, {})
|
||||
const relationsA = associations.filter((it) => targets.has(it.classB))
|
||||
const relationsB = associations.filter((it) => targets.has(it.classA))
|
||||
|
||||
async function save (): Promise<void> {
|
||||
if (current?._id === masterTag) {
|
||||
await client.updateMixin(masterTag, card.class.MasterTag, core.space.Model, card.mixin.DuplicateSetting, {
|
||||
excludedRelations: [...excludedRelations],
|
||||
excludedProperties: [...excludedProperties],
|
||||
excludeMixins: [...excludeMixins]
|
||||
})
|
||||
} else {
|
||||
await client.createMixin(masterTag, card.class.MasterTag, core.space.Model, card.mixin.DuplicateSetting, {
|
||||
excludedRelations: [...excludedRelations],
|
||||
excludedProperties: [...excludedProperties],
|
||||
excludeMixins: [...excludeMixins]
|
||||
})
|
||||
}
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
function propertiesSelected (event: CustomEvent): void {
|
||||
const selectedProperties = event.detail as string[]
|
||||
allProperties.forEach((it) => {
|
||||
if (!selectedProperties.includes(it.id)) {
|
||||
excludedProperties.add(it.id)
|
||||
} else {
|
||||
excludedProperties.delete(it.id)
|
||||
}
|
||||
})
|
||||
excludedProperties = excludedProperties
|
||||
}
|
||||
|
||||
function mixinsSelected (event: CustomEvent): void {
|
||||
const selectedMixins = event.detail as string[]
|
||||
mixins.forEach((it) => {
|
||||
if (!selectedMixins.includes(it)) {
|
||||
excludeMixins.add(it)
|
||||
} else {
|
||||
excludeMixins.delete(it)
|
||||
}
|
||||
})
|
||||
excludeMixins = excludeMixins
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={card.string.Duplicate}
|
||||
okLabel={presentation.string.Save}
|
||||
canSave={true}
|
||||
width={'medium'}
|
||||
okAction={save}
|
||||
on:close
|
||||
>
|
||||
<div class="flex-col flex-gap-2">
|
||||
{#if allProperties.length > 0}
|
||||
<div class="flex-between">
|
||||
<Label label={setting.string.Properties} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={allProperties}
|
||||
multiselect
|
||||
selected={selectedProperties}
|
||||
on:selected={propertiesSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if mixins.length > 0}
|
||||
<div class="flex-between">
|
||||
<Label label={card.string.Tags} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={mixinsItems}
|
||||
multiselect
|
||||
selected={selectedMixins}
|
||||
on:selected={mixinsSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if relationsA.length > 0 || relationsB.length > 0}
|
||||
<div class="divider" />
|
||||
<div>
|
||||
<Label label={card.string.RelationCopyDescr} />
|
||||
</div>
|
||||
<div class="grid">
|
||||
{#each relationsB as assoc}
|
||||
{@const id = `${assoc._id}_b`}
|
||||
<span>{assoc.nameB}</span>
|
||||
<Toggle
|
||||
on={!excludedRelations.has(id)}
|
||||
on:change={(e) => {
|
||||
if (e.detail === true) {
|
||||
excludedRelations.delete(id)
|
||||
} else {
|
||||
excludedRelations.add(id)
|
||||
}
|
||||
excludedRelations = excludedRelations
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{#each relationsA as assoc}
|
||||
{@const id = `${assoc._id}_a`}
|
||||
<span>{assoc.nameA}</span>
|
||||
<Toggle
|
||||
on={!excludedRelations.has(id)}
|
||||
on:change={(e) => {
|
||||
if (e.detail === true) {
|
||||
excludedRelations.delete(id)
|
||||
} else {
|
||||
excludedRelations.add(id)
|
||||
}
|
||||
excludedRelations = excludedRelations
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-auto-rows: minmax(1rem, max-content);
|
||||
justify-content: start;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
row-gap: 0.5rem;
|
||||
column-gap: 1rem;
|
||||
height: min-content;
|
||||
}
|
||||
</style>
|
||||
@@ -41,6 +41,7 @@
|
||||
import card from '../../plugin'
|
||||
import { deleteMasterTag } from '../../utils'
|
||||
import VersioningSetting from './VersioningSetting.svelte'
|
||||
import DuplicateSetting from './DuplicateSetting.svelte'
|
||||
|
||||
export let masterTag: MasterTag
|
||||
|
||||
@@ -153,6 +154,12 @@
|
||||
})
|
||||
}
|
||||
|
||||
function duplicateSetting (): void {
|
||||
showPopup(DuplicateSetting, {
|
||||
masterTag: masterTag._id
|
||||
})
|
||||
}
|
||||
|
||||
let versioningEnabled = h.classHierarchyMixin(masterTag._id, core.mixin.VersionableClass)?.enabled
|
||||
$: versioningEnabled = h.classHierarchyMixin(masterTag._id, core.mixin.VersionableClass)?.enabled
|
||||
</script>
|
||||
@@ -217,6 +224,12 @@
|
||||
on:change={(e) => attributeUpdated('singleColumn', e.detail)}
|
||||
/>
|
||||
</div>
|
||||
<div class="mx-2 flex-between items-center">
|
||||
<Label label={card.string.Duplicate} />
|
||||
<div class="flex items-center gap-1">
|
||||
<ButtonIcon icon={setting.icon.Setting} size="extra-small" on:click={duplicateSetting} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<script lang="ts">
|
||||
import { MasterTag } from '@hcengineering/card'
|
||||
import core, { Doc, Mixin, Ref } from '@hcengineering/core'
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import { DropdownLabels, DropdownLabelsIntl, Label, Toggle } from '@hcengineering/ui'
|
||||
import presentation, { Card, getClient } from '@hcengineering/presentation'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { DropdownLabelsIntl, Label, Toggle } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import card from '../../plugin'
|
||||
import setting from '@hcengineering/setting'
|
||||
|
||||
export let masterTag: Ref<MasterTag>
|
||||
|
||||
@@ -124,32 +124,43 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card label={card.string.NewVersion} canSave={true} width={'medium'} okAction={save} on:close>
|
||||
<Card
|
||||
label={card.string.NewVersion}
|
||||
okLabel={presentation.string.Save}
|
||||
canSave={true}
|
||||
width={'medium'}
|
||||
okAction={save}
|
||||
on:close
|
||||
>
|
||||
<div class="flex-col flex-gap-2">
|
||||
<div class="flex-between">
|
||||
<Label label={setting.string.Properties} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={allProperties}
|
||||
multiselect
|
||||
selected={selectedProperties}
|
||||
on:selected={propertiesSelected}
|
||||
/>
|
||||
{#if allProperties.length > 0}
|
||||
<div class="flex-between">
|
||||
<Label label={setting.string.Properties} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={allProperties}
|
||||
multiselect
|
||||
selected={selectedProperties}
|
||||
on:selected={propertiesSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-between">
|
||||
<Label label={card.string.Tags} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={mixinsItems}
|
||||
multiselect
|
||||
selected={selectedMixins}
|
||||
on:selected={mixinsSelected}
|
||||
/>
|
||||
{/if}
|
||||
{#if mixins.length > 0}
|
||||
<div class="flex-between">
|
||||
<Label label={card.string.Tags} />
|
||||
<div class="max-w-40">
|
||||
<DropdownLabelsIntl
|
||||
width="100%"
|
||||
items={mixinsItems}
|
||||
multiselect
|
||||
selected={selectedMixins}
|
||||
on:selected={mixinsSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if relationsA.length > 0 || relationsB.length > 0}
|
||||
<div class="divider" />
|
||||
<div>
|
||||
|
||||
@@ -35,7 +35,6 @@ import {
|
||||
getSpaceAccessPublicLink,
|
||||
canGetSpaceAccessPublicLink,
|
||||
cardFactory,
|
||||
duplicateCard,
|
||||
checkChildrenSectionVisibility,
|
||||
createChildAction,
|
||||
showAllVersions
|
||||
@@ -69,6 +68,7 @@ import CardWidget from './components/CardWidget.svelte'
|
||||
import CreateSpace from './components/navigator/CreateSpace.svelte'
|
||||
import CardHeaderButton from './components/navigator/CardHeaderButton.svelte'
|
||||
import MyCards from './components/navigator/MyCards.svelte'
|
||||
import DuplicateCard from './components/DuplicateCard.svelte'
|
||||
|
||||
// Card Sections
|
||||
import AttachmentsCardSection from './components/sections/AttachmentsSection.svelte'
|
||||
@@ -141,7 +141,8 @@ export default async (): Promise<Resources> => ({
|
||||
CreateSpace,
|
||||
CardHeaderButton,
|
||||
CreateRolePopup,
|
||||
MyCards
|
||||
MyCards,
|
||||
DuplicateCard
|
||||
},
|
||||
sectionComponent: {
|
||||
AttachmentsSection: AttachmentsCardSection,
|
||||
@@ -161,7 +162,6 @@ export default async (): Promise<Resources> => ({
|
||||
},
|
||||
actionImpl: {
|
||||
DeleteMasterTag: deleteMasterTag,
|
||||
DuplicateCard: duplicateCard,
|
||||
EditSpace: editSpace,
|
||||
CreateChild: createChildAction
|
||||
},
|
||||
|
||||
@@ -28,6 +28,7 @@ export default mergeIds(cardId, card, {
|
||||
ManageMasterTagsTools: '' as AnyComponent,
|
||||
MasterTags: '' as AnyComponent,
|
||||
CreateTag: '' as AnyComponent,
|
||||
DuplicateCard: '' as AnyComponent,
|
||||
CardPresenter: '' as AnyComponent,
|
||||
CardsPresenter: '' as AnyComponent,
|
||||
FavoriteCardPresenter: '' as AnyComponent,
|
||||
|
||||
@@ -34,12 +34,12 @@ import core, {
|
||||
makeDocCollabId,
|
||||
type Markup,
|
||||
type MarkupBlobRef,
|
||||
type Mixin,
|
||||
type Ref,
|
||||
type RelatedDocument,
|
||||
type Space,
|
||||
toRank,
|
||||
type TxOperations,
|
||||
type VersionableClass,
|
||||
type WithLookup
|
||||
} from '@hcengineering/core'
|
||||
import login from '@hcengineering/login'
|
||||
@@ -70,12 +70,12 @@ import workbench, { type LocationData, type Widget, type WidgetTab } from '@hcen
|
||||
import { createWidgetTab } from '@hcengineering/workbench-resources'
|
||||
|
||||
import attachment from '@hcengineering/attachment'
|
||||
import { makeRank } from '@hcengineering/rank'
|
||||
import { writable } from 'svelte/store'
|
||||
import CardSearchItem from './components/CardSearchItem.svelte'
|
||||
import CreateSpace from './components/navigator/CreateSpace.svelte'
|
||||
import card from './plugin'
|
||||
import { type NavigatorConfig } from './types'
|
||||
import { writable } from 'svelte/store'
|
||||
import { makeRank } from '@hcengineering/rank'
|
||||
|
||||
export async function deleteMasterTag (tag: MasterTag | undefined, onDelete?: () => void): Promise<void> {
|
||||
if (tag !== undefined) {
|
||||
@@ -279,10 +279,16 @@ export async function createTypePermissions (masterTag: MasterTag | Tag): Promis
|
||||
}
|
||||
}
|
||||
|
||||
interface CopySettings {
|
||||
excludedProperties?: string[]
|
||||
excludedRelations?: string[] // ${associationId}_${a|b}
|
||||
excludeMixins?: Array<Ref<Mixin<Doc>>>
|
||||
}
|
||||
|
||||
async function cloneCard (
|
||||
origin: Card,
|
||||
overrideProps: Record<string, any>,
|
||||
versionableClass?: VersionableClass,
|
||||
config?: CopySettings,
|
||||
copyIds: boolean = false
|
||||
): Promise<Ref<Card>> {
|
||||
const client = getClient()
|
||||
@@ -297,7 +303,7 @@ async function cloneCard (
|
||||
const systemFields = ['_class', 'id', 'createdOn', 'modifiedOn', 'modifiedBy', 'createdBy', 'createdOn', 'rank']
|
||||
|
||||
for (const [key, attr] of attrs) {
|
||||
if (versionableClass?.excludedProperties?.includes(key) === true || systemFields.includes(key)) {
|
||||
if (config?.excludedProperties?.includes(key) === true || systemFields.includes(key)) {
|
||||
continue
|
||||
}
|
||||
if (attr.type._class === core.class.Collection) {
|
||||
@@ -314,7 +320,7 @@ async function cloneCard (
|
||||
const relationsA = await client.findAll(core.class.Relation, { docA: origin._id })
|
||||
const relationsB = await client.findAll(core.class.Relation, { docB: origin._id })
|
||||
|
||||
if (versionableClass?.excludedProperties?.includes('content') !== true) {
|
||||
if (config?.excludedProperties?.includes('content') !== true) {
|
||||
const markup = await getMarkup(makeDocCollabId(origin, 'content'), origin.content)
|
||||
if (!isEmptyMarkup(markup)) {
|
||||
const collabId = makeCollabId(base, targetId, 'content')
|
||||
@@ -325,7 +331,7 @@ async function cloneCard (
|
||||
const ops = client.apply(`Duplicate_card_${origin._id}`)
|
||||
await ops.createDoc(base, origin.space, props, targetId)
|
||||
for (const mixin of mixins) {
|
||||
if (versionableClass?.excludeMixins?.includes(mixin) === true) {
|
||||
if (config?.excludeMixins?.includes(mixin) === true) {
|
||||
continue
|
||||
}
|
||||
const mixinAttrs = h.getOwnAttributes(mixin)
|
||||
@@ -338,7 +344,7 @@ async function cloneCard (
|
||||
}
|
||||
|
||||
for (const rel of relationsA) {
|
||||
if (versionableClass?.excludedRelations?.includes(`${rel.association}_b`) !== true) {
|
||||
if (config?.excludedRelations?.includes(`${rel.association}_b`) !== true) {
|
||||
await ops.createDoc(core.class.Relation, core.space.Workspace, {
|
||||
docA: targetId,
|
||||
docB: rel.docB,
|
||||
@@ -347,7 +353,7 @@ async function cloneCard (
|
||||
}
|
||||
}
|
||||
for (const rel of relationsB) {
|
||||
if (versionableClass?.excludedRelations?.includes(`${rel.association}_a`) !== true) {
|
||||
if (config?.excludedRelations?.includes(`${rel.association}_a`) !== true) {
|
||||
await ops.createDoc(core.class.Relation, core.space.Workspace, {
|
||||
docA: rel.docA,
|
||||
docB: targetId,
|
||||
@@ -357,7 +363,7 @@ async function cloneCard (
|
||||
}
|
||||
await ops.commit()
|
||||
|
||||
if (versionableClass?.excludedProperties?.includes('attachments') !== true) {
|
||||
if (config?.excludedProperties?.includes('attachments') !== true) {
|
||||
const attachments = await client.findAll(attachment.class.Attachment, { attachedTo: origin._id })
|
||||
const attachmentOps = client.apply(`Duplicate_attachments_${origin._id}`)
|
||||
for (const att of attachments) {
|
||||
@@ -370,10 +376,14 @@ async function cloneCard (
|
||||
return targetId
|
||||
}
|
||||
|
||||
export async function duplicateCard (origin: Card): Promise<void> {
|
||||
const targetId = await cloneCard(origin, {
|
||||
title: `${origin.title} (Copy)`
|
||||
})
|
||||
export async function duplicateCard (origin: Card, config?: CopySettings): Promise<void> {
|
||||
const targetId = await cloneCard(
|
||||
origin,
|
||||
{
|
||||
title: `${origin.title} (Copy)`
|
||||
},
|
||||
config
|
||||
)
|
||||
|
||||
const loc = getCurrentLocation()
|
||||
loc.path[2] = cardId
|
||||
|
||||
@@ -48,6 +48,12 @@ export interface Role extends BaseRole {
|
||||
types: Ref<MasterTag | Tag>[]
|
||||
}
|
||||
|
||||
export interface DuplicateSetting extends Class<MasterTag> {
|
||||
excludedProperties?: string[]
|
||||
excludedRelations?: string[] // ${associationId}_${a|b}
|
||||
excludeMixins?: Ref<Mixin<Doc>>[]
|
||||
}
|
||||
|
||||
export interface Card extends Doc, IconProps, VersionableDoc {
|
||||
_class: Ref<MasterTag>
|
||||
title: string
|
||||
@@ -157,7 +163,8 @@ const cardPlugin = plugin(cardId, {
|
||||
},
|
||||
mixin: {
|
||||
CardViewDefaults: '' as Ref<Mixin<CardViewDefaults>>,
|
||||
CreateCardExtension: '' as Ref<Mixin<CreateCardExtension>>
|
||||
CreateCardExtension: '' as Ref<Mixin<CreateCardExtension>>,
|
||||
DuplicateSetting: '' as Ref<Mixin<DuplicateSetting>>
|
||||
},
|
||||
space: {
|
||||
Default: '' as Ref<CardSpace>
|
||||
|
||||
@@ -237,6 +237,7 @@
|
||||
"TwoFactorAuthEnabled": "Dvoufaktorové ověřování je povoleno",
|
||||
"TwoFactorAuthDisabled": "Dvoufaktorové ověřování je zakázáno",
|
||||
"ShowQRCode": "Zobrazit QR kód",
|
||||
"EnterVerificationCode": "Zadejte ověřovací kód"
|
||||
"EnterVerificationCode": "Zadejte ověřovací kód",
|
||||
"OverrideAttribute": "Přepsat atribut"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "Zweistufige Authentifizierung ist aktiviert",
|
||||
"TwoFactorAuthDisabled": "Zweistufige Authentifizierung ist deaktiviert",
|
||||
"ShowQRCode": "QR-Code anzeigen",
|
||||
"EnterVerificationCode": "Verifizierungscode eingeben"
|
||||
"EnterVerificationCode": "Verifizierungscode eingeben",
|
||||
"OverrideAttribute": "Überschreibattribut"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "Two-factor authentication is enabled",
|
||||
"TwoFactorAuthDisabled": "Two-factor authentication is disabled",
|
||||
"ShowQRCode": "Show QR code",
|
||||
"EnterVerificationCode": "Enter verification code"
|
||||
"EnterVerificationCode": "Enter verification code",
|
||||
"OverrideAttribute": "Override attribute"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"TwoFactorAuthEnabled": "La autenticación de dos factores está habilitada",
|
||||
"TwoFactorAuthDisabled": "La autenticación de dos factores está deshabilitada",
|
||||
"ShowQRCode": "Mostrar código QR",
|
||||
"EnterVerificationCode": "Introducir código de verificación"
|
||||
"EnterVerificationCode": "Introducir código de verificación",
|
||||
"OverrideAttribute": "Sobreescribir atributo"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "L'authentification à deux facteurs est activée",
|
||||
"TwoFactorAuthDisabled": "L'authentification à deux facteurs est désactivée",
|
||||
"ShowQRCode": "Afficher le code QR",
|
||||
"EnterVerificationCode": "Entrer le code de vérification"
|
||||
"EnterVerificationCode": "Entrer le code de vérification",
|
||||
"OverrideAttribute": "Surcharger l'attribut"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "L'autenticazione a due fattori è abilitata",
|
||||
"TwoFactorAuthDisabled": "L'autenticazione a due fattori è disabilitata",
|
||||
"ShowQRCode": "Mostra codice QR",
|
||||
"EnterVerificationCode": "Inserisci codice di verifica"
|
||||
"EnterVerificationCode": "Inserisci codice di verifica",
|
||||
"OverrideAttribute": "Sovrascrivi attributo"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "二要素認証は有効です",
|
||||
"TwoFactorAuthDisabled": "二要素認証は無効です",
|
||||
"ShowQRCode": "QRコードを表示",
|
||||
"EnterVerificationCode": "確認コードを入力"
|
||||
"EnterVerificationCode": "確認コードを入力",
|
||||
"OverrideAttribute": "属性を上書き"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "2단계 인증이 활성화됨",
|
||||
"TwoFactorAuthDisabled": "2단계 인증이 비활성화됨",
|
||||
"ShowQRCode": "QR 코드 표시",
|
||||
"EnterVerificationCode": "인증 코드 입력"
|
||||
"EnterVerificationCode": "인증 코드 입력",
|
||||
"OverrideAttribute": "속성 재정의"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"TwoFactorAuthEnabled": "Autenticação de dois fatores está ativada",
|
||||
"TwoFactorAuthDisabled": "Autenticação de dois fatores está desativada",
|
||||
"ShowQRCode": "Mostrar código QR",
|
||||
"EnterVerificationCode": "Inserir código de verificação"
|
||||
"EnterVerificationCode": "Inserir código de verificação",
|
||||
"OverrideAttribute": "Sobrescrever atributo"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"TwoFactorAuthEnabled": "Autenticação de dois fatores está ativada",
|
||||
"TwoFactorAuthDisabled": "Autenticação de dois fatores está desativada",
|
||||
"ShowQRCode": "Mostrar código QR",
|
||||
"EnterVerificationCode": "Inserir código de verificação"
|
||||
"EnterVerificationCode": "Inserir código de verificação",
|
||||
"OverrideAttribute": "Sobrescrever atributo"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "Двухфакторная аутентификация включена",
|
||||
"TwoFactorAuthDisabled": "Двухфакторная аутентификация отключена",
|
||||
"ShowQRCode": "Показать QR-код",
|
||||
"EnterVerificationCode": "Введите код подтверждения"
|
||||
"EnterVerificationCode": "Введите код подтверждения",
|
||||
"OverrideAttribute": "Переопределить атрибут"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "İki faktörlü kimlik doğrulama etkin",
|
||||
"TwoFactorAuthDisabled": "İki faktörlü kimlik doğrulama devre dışı",
|
||||
"ShowQRCode": "QR kodu göster",
|
||||
"EnterVerificationCode": "Doğrulama kodunu gir"
|
||||
"EnterVerificationCode": "Doğrulama kodunu gir",
|
||||
"OverrideAttribute": "Özniteliği geçersiz kıl"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
"TwoFactorAuthEnabled": "双因素认证已启用",
|
||||
"TwoFactorAuthDisabled": "双因素认证已禁用",
|
||||
"ShowQRCode": "显示QR码",
|
||||
"EnterVerificationCode": "输入验证码"
|
||||
"EnterVerificationCode": "输入验证码",
|
||||
"OverrideAttribute": "覆盖属性"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
import {
|
||||
Action,
|
||||
AnySvelteComponent,
|
||||
IconCopy,
|
||||
IconDelete,
|
||||
IconEdit,
|
||||
Menu,
|
||||
@@ -42,6 +43,7 @@
|
||||
import ClassAttributeRow from './ClassAttributeRow.svelte'
|
||||
import { makeRank } from '@hcengineering/rank'
|
||||
import EditAttribute from './EditAttribute.svelte'
|
||||
import { TypeIdentifier } from '@hcengineering/model'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let ofClass: Ref<Class<Doc>> | undefined = undefined
|
||||
@@ -97,6 +99,23 @@
|
||||
showPopup(EditAttribute, { attribute, exist }, 'top', update)
|
||||
}
|
||||
|
||||
export async function overrideAttribute (source: AnyAttribute): Promise<void> {
|
||||
const newSeq = await client.createDoc(core.class.CustomSequence, core.space.Workspace, {
|
||||
prefix: '',
|
||||
sequence: 0,
|
||||
attachedTo: core.class.CustomSequence
|
||||
})
|
||||
const _id = await client.createDoc(core.class.Attribute, core.space.Model, {
|
||||
...source,
|
||||
type: TypeIdentifier(newSeq),
|
||||
attributeOf: _class
|
||||
})
|
||||
const attribute = await client.findOne(core.class.Attribute, _id)
|
||||
if (attribute !== undefined) {
|
||||
showPopup(EditAttribute, { attribute, exist: true }, 'top', update)
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeAttribute (attribute: AnyAttribute, exist: boolean): Promise<void> {
|
||||
showPopup(
|
||||
MessageBox,
|
||||
@@ -125,6 +144,15 @@
|
||||
}
|
||||
]
|
||||
if (attribute.isCustom === true) {
|
||||
if (attribute.attributeOf !== _class && attribute.type._class === core.class.TypeIdentifier) {
|
||||
actions.push({
|
||||
label: settings.string.OverrideAttribute,
|
||||
icon: IconCopy,
|
||||
action: async () => {
|
||||
await overrideAttribute(attribute)
|
||||
}
|
||||
})
|
||||
}
|
||||
actions.push({
|
||||
label: presentation.string.Remove,
|
||||
icon: IconDelete,
|
||||
|
||||
@@ -166,6 +166,7 @@ export default mergeIds(settingId, setting, {
|
||||
ShowInTitle: '' as IntlString,
|
||||
SpaceMembersOnly: '' as IntlString,
|
||||
LastOwnerLeaveTitle: '' as IntlString,
|
||||
LastOwnerLeaveMessage: '' as IntlString
|
||||
LastOwnerLeaveMessage: '' as IntlString,
|
||||
OverrideAttribute: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user