diff --git a/models/card/src/index.ts b/models/card/src/index.ts index 2b031d7a67..54c7797fb6 100644 --- a/models/card/src/index.ts +++ b/models/card/src/index.ts @@ -22,6 +22,8 @@ import { type CardViewDefaults, type CreateCardExtension, DOMAIN_CARD, + type ExportExtension, + type ExportFunc, type FavoriteCard, type FavoriteType, type MasterTag, @@ -202,6 +204,11 @@ export class TCreateCardExtension extends TMasterTag implements CreateCardExtens canCreate?: CanCreateCardResource } +@Model(card.class.ExportExtension, core.class.Doc, DOMAIN_MODEL) +export class TExportExtension extends TDoc implements ExportExtension { + func!: Resource +} + export * from './migration' const listConfig: (BuildModelKey | string)[] = [ @@ -387,7 +394,8 @@ export function createModel (builder: Builder): void { TCardViewDefaults, TFavoriteCard, TFavoriteType, - TCreateCardExtension + TCreateCardExtension, + TExportExtension ) builder.createDoc( diff --git a/models/process/src/index.ts b/models/process/src/index.ts index b575769d4e..e68e792d07 100644 --- a/models/process/src/index.ts +++ b/models/process/src/index.ts @@ -606,6 +606,10 @@ export function createModel (builder: Builder): void { props: {} }) + builder.createDoc(card.class.ExportExtension, core.space.Model, { + func: process.function.ExportProcess + }) + builder.createDoc( card.class.CardSection, core.space.Model, diff --git a/packages/presentation/src/components/DocPopup.svelte b/packages/presentation/src/components/DocPopup.svelte index 76e12b247f..9568beeb81 100644 --- a/packages/presentation/src/components/DocPopup.svelte +++ b/packages/presentation/src/components/DocPopup.svelte @@ -315,7 +315,7 @@ {/if} {#if (allowDeselect && selected) || multiSelect || selected} -
+
{#if obj._id === selected || selectedElements.has(obj._id)} {#if loading} diff --git a/plugins/card-assets/lang/cs.json b/plugins/card-assets/lang/cs.json index 639e8907f8..783339f716 100644 --- a/plugins/card-assets/lang/cs.json +++ b/plugins/card-assets/lang/cs.json @@ -68,6 +68,8 @@ "EnableVersioning": "Povolit verzování", "EnableVersioningConfirm": "Chcete povolit verzování?", "NewVersionConfirmation": "Chcete vytvořit novou verzi?", - "RelationCopyDescr": "Které vztahy chcete zkopírovat?" + "RelationCopyDescr": "Které vztahy chcete zkopírovat?", + "Import": "Importovat", + "Export": "Exportovat" } } diff --git a/plugins/card-assets/lang/de.json b/plugins/card-assets/lang/de.json index 191e41b96e..1b22b233d2 100644 --- a/plugins/card-assets/lang/de.json +++ b/plugins/card-assets/lang/de.json @@ -68,6 +68,8 @@ "EnableVersioning": "Versionsverwaltung aktivieren", "EnableVersioningConfirm": "Möchten Sie die Versionsverwaltung aktivieren?", "NewVersionConfirmation": "Möchten Sie eine neue Version erstellen?", - "RelationCopyDescr": "Welche Beziehungen möchten Sie kopieren?" + "RelationCopyDescr": "Welche Beziehungen möchten Sie kopieren?", + "Import": "Importieren", + "Export": "Exportieren" } } diff --git a/plugins/card-assets/lang/en.json b/plugins/card-assets/lang/en.json index 0887a553c9..6acb2dd4e6 100644 --- a/plugins/card-assets/lang/en.json +++ b/plugins/card-assets/lang/en.json @@ -68,6 +68,8 @@ "EnableVersioning": "Enable versioning", "EnableVersioningConfirm": "Do you want to enable versioning?", "NewVersionConfirmation": "Do you want to create a new version?", - "RelationCopyDescr": "Which relations do you want to copy?" + "RelationCopyDescr": "Which relations do you want to copy?", + "Import": "Import", + "Export": "Export" } } diff --git a/plugins/card-assets/lang/es.json b/plugins/card-assets/lang/es.json index 93ad0d3987..982de7eb9a 100644 --- a/plugins/card-assets/lang/es.json +++ b/plugins/card-assets/lang/es.json @@ -68,6 +68,8 @@ "EnableVersioning": "Habilitar control de versiones", "EnableVersioningConfirm": "¿Desea habilitar el control de versiones?", "NewVersionConfirmation": "¿Desea crear una nueva versión?", - "RelationCopyDescr": "¿Qué relaciones quieres copiar?" + "RelationCopyDescr": "¿Qué relaciones quieres copiar?", + "Import": "Importar", + "Export": "Exportar" } } diff --git a/plugins/card-assets/lang/fr.json b/plugins/card-assets/lang/fr.json index 47f51d5a62..606e8f8d40 100644 --- a/plugins/card-assets/lang/fr.json +++ b/plugins/card-assets/lang/fr.json @@ -68,6 +68,8 @@ "EnableVersioning": "Activer la gestion des versions", "EnableVersioningConfirm": "Voulez-vous activer la gestion des versions ?", "NewVersionConfirmation": "Voulez-vous créer une nouvelle version ?", - "RelationCopyDescr": "Quelles relations souhaitez-vous copier ?" + "RelationCopyDescr": "Quelles relations souhaitez-vous copier ?", + "Import": "Importer", + "Export": "Exporter" } } diff --git a/plugins/card-assets/lang/it.json b/plugins/card-assets/lang/it.json index c058e3d28d..23874559a4 100644 --- a/plugins/card-assets/lang/it.json +++ b/plugins/card-assets/lang/it.json @@ -68,6 +68,8 @@ "EnableVersioning": "Abilita versionamento", "EnableVersioningConfirm": "Vuoi abilitare il versionamento?", "NewVersionConfirmation": "Vuoi creare una nuova versione?", - "RelationCopyDescr": "Quali relazioni vuoi copiare?" + "RelationCopyDescr": "Quali relazioni vuoi copiare?", + "Import": "Importa", + "Export": "Esporta" } } diff --git a/plugins/card-assets/lang/ja.json b/plugins/card-assets/lang/ja.json index 60dbc16168..c4be63a3eb 100644 --- a/plugins/card-assets/lang/ja.json +++ b/plugins/card-assets/lang/ja.json @@ -68,6 +68,8 @@ "EnableVersioning": "バージョン管理を有効にする", "EnableVersioningConfirm": "バージョン管理を有効にしますか?", "NewVersionConfirmation": "新しいバージョンを作成しますか?", - "RelationCopyDescr": "どの関連をコピーしますか?" + "RelationCopyDescr": "どの関連をコピーしますか?", + "Import": "インポート", + "Export": "エクスポート" } } diff --git a/plugins/card-assets/lang/pt.json b/plugins/card-assets/lang/pt.json index eabd7990b9..f312a0a9e9 100644 --- a/plugins/card-assets/lang/pt.json +++ b/plugins/card-assets/lang/pt.json @@ -68,6 +68,8 @@ "EnableVersioning": "Ativar controle de versões", "EnableVersioningConfirm": "Deseja ativar o controle de versões?", "NewVersionConfirmation": "Deseja criar uma nova versão?", - "RelationCopyDescr": "Quais relações você deseja copiar?" + "RelationCopyDescr": "Quais relações você deseja copiar?", + "Import": "Importar", + "Export": "Exportar" } } diff --git a/plugins/card-assets/lang/ru.json b/plugins/card-assets/lang/ru.json index 6d4d168e0b..7396e58c09 100644 --- a/plugins/card-assets/lang/ru.json +++ b/plugins/card-assets/lang/ru.json @@ -68,6 +68,8 @@ "EnableVersioning": "Включить управление версиями", "EnableVersioningConfirm": "Вы хотите включить управление версиями?", "NewVersionConfirmation": "Вы хотите создать новую версию?", - "RelationCopyDescr": "Какие связи вы хотите скопировать?" + "RelationCopyDescr": "Какие связи вы хотите скопировать?", + "Import": "Импортировать", + "Export": "Экспортировать" } } diff --git a/plugins/card-assets/lang/tr.json b/plugins/card-assets/lang/tr.json index 22eaf933da..e1ec5b12e0 100644 --- a/plugins/card-assets/lang/tr.json +++ b/plugins/card-assets/lang/tr.json @@ -68,6 +68,8 @@ "EnableVersioning": "Sürümlemeyi etkinleştir", "EnableVersioningConfirm": "Sürümlemeyi etkinleştirmek istiyor musunuz?", "NewVersionConfirmation": "Yeni bir sürüm oluşturmak istiyor musunuz?", - "RelationCopyDescr": "Hangi ilişkileri kopyalamak istiyorsunuz?" + "RelationCopyDescr": "Hangi ilişkileri kopyalamak istiyorsunuz?", + "Import": "İçe aktar", + "Export": "Dışa aktar" } } diff --git a/plugins/card-assets/lang/zh.json b/plugins/card-assets/lang/zh.json index aff47ee3c9..ae13656e6a 100644 --- a/plugins/card-assets/lang/zh.json +++ b/plugins/card-assets/lang/zh.json @@ -68,6 +68,8 @@ "EnableVersioning": "启用版本控制", "EnableVersioningConfirm": "您要启用版本控制吗?", "NewVersionConfirmation": "您想创建一个新版本吗?", - "RelationCopyDescr": "您想复制哪些关系?" + "RelationCopyDescr": "您想复制哪些关系?", + "Import": "导入", + "Export": "导出" } } diff --git a/plugins/card-resources/src/components/CardPresenter.svelte b/plugins/card-resources/src/components/CardPresenter.svelte index 54501d4be8..6ef9980ada 100644 --- a/plugins/card-resources/src/components/CardPresenter.svelte +++ b/plugins/card-resources/src/components/CardPresenter.svelte @@ -108,7 +108,7 @@ {noSelect} inline component={card.component.EditCard} - shrink={1} + shrink={0} title={cardObj?.title} > {#if shouldShowAvatar} @@ -136,7 +136,7 @@ {noSelect} inline component={card.component.EditCard} - shrink={1} + shrink={0} title={cardObj?.title} > {#if shouldShowAvatar} diff --git a/plugins/card-resources/src/components/CardSelector.svelte b/plugins/card-resources/src/components/CardSelector.svelte index 9d33eb3d39..f3bb54fc52 100644 --- a/plugins/card-resources/src/components/CardSelector.svelte +++ b/plugins/card-resources/src/components/CardSelector.svelte @@ -14,13 +14,13 @@ -->
{#if !h.isMixin(masterTag._id)} -
+
+ diff --git a/plugins/card-resources/src/exporter.ts b/plugins/card-resources/src/exporter.ts new file mode 100644 index 0000000000..4cd46dc088 --- /dev/null +++ b/plugins/card-resources/src/exporter.ts @@ -0,0 +1,185 @@ +import core, { + type AnyAttribute, + type Class, + type Doc, + type EnumOf, + type ModelDb, + type Hierarchy, + type Ref, + type RefTo, + type ArrOf, + type Data, + type AttachedDoc, + type AttachedData, + type TypeIdentifier +} from '@hcengineering/core' +import { getClient } from '@hcengineering/presentation' +import view from '@hcengineering/view' +import card from './plugin' +import { getResource } from '@hcengineering/platform' + +export async function importModule (json: string): Promise { + try { + const data = JSON.parse(json) as Doc[] + const client = getClient() + const m = client.getModel() + const h = client.getHierarchy() + const apply = client.apply('Import') + for (const elem of data) { + if (m.findObject(elem._id) !== undefined) continue + if (h.isDerived(elem._class, core.class.AttachedDoc)) { + const attachedDoc = elem as AttachedDoc + await apply.addCollection( + attachedDoc._class, + attachedDoc.space, + attachedDoc.attachedTo, + attachedDoc.attachedToClass, + attachedDoc.collection, + stripAttached(attachedDoc), + attachedDoc._id + ) + } else { + await apply.createDoc(elem._class, elem.space, stripData(elem), elem._id) + } + } + await apply.commit(true) + } catch (e) { + console.error('Failed to import process module:', e) + } +} + +function stripAttached (doc: AttachedDoc): AttachedData { + const { collection, attachedTo, attachedToClass, ...rest } = strip(doc) + return rest +} + +function stripData (doc: T): Data { + const { _id, _class, space, ...rest } = strip(doc) + return rest as Data +} + +export async function exportModule (_id: Ref>): Promise { + const processed = new Set>() + const res = await exportType(_id, processed) + const clear = res.map(strip) + const str = JSON.stringify(clear) + return str +} + +function strip (doc: T): Omit { + const { modifiedBy, modifiedOn, createdBy, createdOn, ...rest } = doc + return rest +} + +async function exportAttribute ( + attr: AnyAttribute, + m: ModelDb, + h: Hierarchy +): Promise<{ + docs: Doc[] + required: Array>> + }> { + const client = getClient() + const docs: Doc[] = [] + const required: Array>> = [] + docs.push(attr) + + docs.push(...m.findAllSync(core.class.AttributePermission, { attribute: attr._id })) + if (attr.type._class === core.class.TypeIdentifier) { + const of = (attr.type as TypeIdentifier).of + const seq = await client.findOne(core.class.Sequence, { _id: of }) + if (seq !== undefined) { + seq.sequence = 0 + docs.push(seq) + } + } + if (attr.type._class === core.class.EnumOf) { + const of = (attr.type as EnumOf).of + const val = m.findObject(of) + if (val !== undefined) docs.push(val) + } + if (attr.type._class === core.class.RefTo) { + const to = (attr.type as RefTo).to + if (h.isDerived(to, card.class.Card)) { + required.push(to) + } + } + if (attr.type._class === core.class.ArrOf) { + const of = (attr.type as ArrOf).of + if (of._class === core.class.RefTo) { + const to = (of as RefTo).to + if (h.isDerived(to, card.class.Card)) { + required.push(to) + } + } + if (of._class === core.class.EnumOf) { + const val = m.findObject((of as EnumOf).of) + if (val !== undefined) docs.push(val) + } + } + return { docs, required } +} + +async function exportType (_id: Ref>, processed: Set>): Promise { + if (processed.has(_id)) return [] + const res: Doc[] = [] + const required: Array>> = [] + const client = getClient() + const m = client.getModel() + const h = client.getHierarchy() + const type = m.findObject(_id) + processed.add(_id) + if (type === undefined) return res + res.push(type) + + res.push(...m.findAllSync(core.class.ClassPermission, { targetClass: _id })) + + const attrs = h.getOwnAttributes(_id) + for (const attr of attrs) { + const at = await exportAttribute(attr[1], m, h) + res.push(...at.docs) + required.push(...at.required) + } + + const descendants = h.getDescendants(_id) + for (const desc of descendants) { + if (h.getClass(desc).extends !== _id) continue + required.push(desc) + } + + res.push(...m.findAllSync(card.class.Role, { types: _id })) + + res.push(...m.findAllSync(view.class.Viewlet, { attachedTo: _id })) + + const assocA = m.findAllSync(core.class.Association, { classA: _id }) + for (const assoc of assocA) { + res.push(assoc) + required.push(assoc.classB) + } + + const assocB = m.findAllSync(core.class.Association, { classB: _id }) + for (const assoc of assocB) { + res.push(assoc) + required.push(assoc.classA) + } + + const extensions = m.findAllSync(card.class.ExportExtension, {}) + for (const ext of extensions) { + const f = await getResource(ext.func) + const extRes = f(_id) + res.push(...extRes.docs) + required.push(...extRes.required) + } + + const parent = h.getClass(_id).extends + if (parent !== undefined && h.isDerived(parent, card.class.Card) && parent !== card.class.Card) { + if (!processed.has(parent)) { + required.push(parent) + } + } + + for (const req of required) { + res.push(...(await exportType(req, processed))) + } + return res +} diff --git a/plugins/card-resources/src/plugin.ts b/plugins/card-resources/src/plugin.ts index 8f244400a3..c38c655038 100644 --- a/plugins/card-resources/src/plugin.ts +++ b/plugins/card-resources/src/plugin.ts @@ -128,6 +128,8 @@ export default mergeIds(cardId, card, { Post: '' as IntlString, ShowLess: '' as IntlString, Duplicate: '' as IntlString, + Export: '' as IntlString, + Import: '' as IntlString, NewVersion: '' as IntlString, Versioning: '' as IntlString, EnableVersioning: '' as IntlString, diff --git a/plugins/card/src/index.ts b/plugins/card/src/index.ts index 84a8bc7795..3b8c538ba7 100644 --- a/plugins/card/src/index.ts +++ b/plugins/card/src/index.ts @@ -119,6 +119,15 @@ export interface PermissionObjectClass extends Doc { export type CanCreateCardFn = (space: Ref, data: Partial>) => Promise> export type CanCreateCardResource = Resource +export interface ExportExtension extends Doc { + func: Resource +} + +export type ExportFunc = (id: Ref) => { + docs: Doc[] + required: Ref>[] +} + /** * @public */ @@ -140,7 +149,8 @@ const cardPlugin = plugin(cardId, { CardSection: '' as Ref>, FavoriteCard: '' as Ref>, FavoriteType: '' as Ref>, - PermissionObjectClass: '' as Ref> + PermissionObjectClass: '' as Ref>, + ExportExtension: '' as Ref> }, mixin: { CardViewDefaults: '' as Ref>, diff --git a/plugins/process-resources/src/exporter.ts b/plugins/process-resources/src/exporter.ts new file mode 100644 index 0000000000..033a84bbcf --- /dev/null +++ b/plugins/process-resources/src/exporter.ts @@ -0,0 +1,22 @@ +import { type MasterTag } from '@hcengineering/card' +import { type Class, type Doc, type Ref } from '@hcengineering/core' +import { getClient } from '@hcengineering/presentation' +import process from './plugin' + +export function exportProcess (_id: Ref): { + docs: Doc[] + required: Array>> +} { + const docs: Doc[] = [] + const client = getClient() + const m = client.getModel() + const processes = m.findAllSync(process.class.Process, { masterTag: _id }) + for (const proc of processes) { + docs.push(proc) + + docs.push(...m.findAllSync(process.class.State, { process: proc._id })) + + docs.push(...m.findAllSync(process.class.Transition, { process: proc._id })) + } + return { docs, required: [] } +} diff --git a/plugins/process-resources/src/index.ts b/plugins/process-resources/src/index.ts index a857b2cbf0..e197a46f80 100644 --- a/plugins/process-resources/src/index.ts +++ b/plugins/process-resources/src/index.ts @@ -89,6 +89,7 @@ import { eventCheck } from './utils' import FieldChangesEditor from './components/settings/FieldChangesEditor.svelte' +import { exportProcess } from './exporter' export * from './query' @@ -178,6 +179,7 @@ export default async (): Promise => ({ OnEventCheck: eventCheck }, function: { + ExportProcess: exportProcess, ShowDoneQuery: showDoneQuery, // eslint-disable-next-line @typescript-eslint/unbound-method CreateMiddleware: ProcessMiddleware.create diff --git a/plugins/process/src/index.ts b/plugins/process/src/index.ts index f2cb613c2a..53f67009ea 100644 --- a/plugins/process/src/index.ts +++ b/plugins/process/src/index.ts @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Card, MasterTag, Tag } from '@hcengineering/card' +import { Card, ExportFunc, MasterTag, Tag } from '@hcengineering/card' import { Association, Class, Client, Doc, DocumentUpdate, ObjQueryType, Rank, Ref, Tx, Type } from '@hcengineering/core' import { Asset, IntlString, Plugin, plugin, Resource } from '@hcengineering/platform' import { ToDo } from '@hcengineering/time' @@ -339,6 +339,7 @@ export default plugin(processId, { ExecutionEmployeeInitiator: '' as Ref, ExecutionInitiator: '' as Ref, EmptyArray: '' as Ref, - CurrentDate: '' as Ref + CurrentDate: '' as Ref, + ExportProcess: '' as Resource } })