From 184b4ec08fb40822b5483620a04fb85010dd3f89 Mon Sep 17 00:00:00 2001 From: Artyom Savchenko Date: Mon, 6 Jul 2026 13:29:21 +0700 Subject: [PATCH] Allow to export documents without children (#10909) * Allow to export documents without children Signed-off-by: Artem Savchenko * Fix tests Signed-off-by: Artem Savchenko --------- Signed-off-by: Artem Savchenko --- plugins/export-assets/lang/cs.json | 2 + plugins/export-assets/lang/de.json | 2 + plugins/export-assets/lang/en.json | 2 + plugins/export-assets/lang/es.json | 2 + plugins/export-assets/lang/fr.json | 2 + plugins/export-assets/lang/it.json | 2 + plugins/export-assets/lang/ja.json | 2 + plugins/export-assets/lang/ko.json | 2 + plugins/export-assets/lang/pt-br.json | 2 + plugins/export-assets/lang/pt.json | 2 + plugins/export-assets/lang/ru.json | 2 + plugins/export-assets/lang/tr.json | 2 + plugins/export-assets/lang/zh.json | 2 + .../components/ExportToWorkspaceModal.svelte | 23 ++- plugins/export-resources/src/export.ts | 6 +- plugins/export-resources/src/plugin.ts | 4 +- .../src/__tests__/relation-exporter.test.ts | 9 +- .../src/__tests__/workspace-exporter.test.ts | 143 ++++++++++++++++++ services/export/pod-export/src/server.ts | 5 +- .../src/workspace/document-exporter.ts | 32 ++-- .../src/workspace/relation-exporter.ts | 42 +++-- .../export/pod-export/src/workspace/types.ts | 4 + .../src/workspace/workspace-exporter.ts | 10 +- 23 files changed, 268 insertions(+), 36 deletions(-) diff --git a/plugins/export-assets/lang/cs.json b/plugins/export-assets/lang/cs.json index 64de0ca22c..f464a9fc35 100644 --- a/plugins/export-assets/lang/cs.json +++ b/plugins/export-assets/lang/cs.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Pouze platné dokumenty", "ExportFilterSkipArchivedObsolete": "Vše kromě archivovaných a zastaralých", "ExportFilterAll": "Všechny dokumenty", + "ExportChildDocuments": "Exportovat podřízené dokumenty", + "ExportChildDocumentsDescription": "Také exportovat dokumenty připojené k vybraným", "ExportResultRecordTitle": "Export dokumentů z pracovního prostoru {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/de.json b/plugins/export-assets/lang/de.json index 1fe8f24d0c..721ab90b39 100644 --- a/plugins/export-assets/lang/de.json +++ b/plugins/export-assets/lang/de.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Nur wirksame Dokumente exportieren", "ExportFilterSkipArchivedObsolete": "Alle Dokumente außer archivierten und veralteten", "ExportFilterAll": "Alle Dokumente", + "ExportChildDocuments": "Untergeordnete Dokumente exportieren", + "ExportChildDocumentsDescription": "Auch Dokumente exportieren, die an die ausgewählten angehängt sind", "ExportResultRecordTitle": "Export von Dokumenten aus dem Arbeitsbereich {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/en.json b/plugins/export-assets/lang/en.json index 4d54dc5009..c7dae4bbd5 100644 --- a/plugins/export-assets/lang/en.json +++ b/plugins/export-assets/lang/en.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Export only effective documents", "ExportFilterSkipArchivedObsolete": "All documents except archived and obsolete", "ExportFilterAll": "All documents", + "ExportChildDocuments": "Export child documents", + "ExportChildDocumentsDescription": "Also export documents attached to the selected ones", "ExportResultRecordTitle": "Export of documents from {workspace} workspace ({date})" } } diff --git a/plugins/export-assets/lang/es.json b/plugins/export-assets/lang/es.json index db2c203cea..dbf975aa75 100644 --- a/plugins/export-assets/lang/es.json +++ b/plugins/export-assets/lang/es.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Solo documentos efectivos", "ExportFilterSkipArchivedObsolete": "Todos excepto archivados y obsoletos", "ExportFilterAll": "Todos los documentos", + "ExportChildDocuments": "Exportar documentos secundarios", + "ExportChildDocumentsDescription": "Exportar también los documentos adjuntos a los seleccionados", "ExportResultRecordTitle": "Exportación de documentos del espacio de trabajo {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/fr.json b/plugins/export-assets/lang/fr.json index dfeb48f579..2587e73342 100644 --- a/plugins/export-assets/lang/fr.json +++ b/plugins/export-assets/lang/fr.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Seulement les documents effectifs", "ExportFilterSkipArchivedObsolete": "Tous sauf archivés et obsolètes", "ExportFilterAll": "Tous les documents", + "ExportChildDocuments": "Exporter les documents enfants", + "ExportChildDocumentsDescription": "Exporter aussi les documents rattachés aux éléments sélectionnés", "ExportResultRecordTitle": "Export de documents depuis l'espace de travail {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/it.json b/plugins/export-assets/lang/it.json index 1098bac228..7972c44049 100644 --- a/plugins/export-assets/lang/it.json +++ b/plugins/export-assets/lang/it.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Solo documenti effettivi", "ExportFilterSkipArchivedObsolete": "Tutti tranne archiviati e obsoleti", "ExportFilterAll": "Tutti i documenti", + "ExportChildDocuments": "Esporta documenti figlio", + "ExportChildDocumentsDescription": "Esporta anche i documenti collegati a quelli selezionati", "ExportResultRecordTitle": "Esportazione di documenti dallo spazio di lavoro {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/ja.json b/plugins/export-assets/lang/ja.json index 015535c90f..5bc3f4de6e 100644 --- a/plugins/export-assets/lang/ja.json +++ b/plugins/export-assets/lang/ja.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "有効なドキュメントのみ", "ExportFilterSkipArchivedObsolete": "アーカイブ・廃止を除くすべて", "ExportFilterAll": "すべてのドキュメント", + "ExportChildDocuments": "子ドキュメントをエクスポート", + "ExportChildDocumentsDescription": "選択したドキュメントに添付されたドキュメントもエクスポートします", "ExportResultRecordTitle": "ワークスペース {workspace} からのドキュメントのエクスポート ({date})" } } diff --git a/plugins/export-assets/lang/ko.json b/plugins/export-assets/lang/ko.json index 5147c679c3..0812b3cc03 100644 --- a/plugins/export-assets/lang/ko.json +++ b/plugins/export-assets/lang/ko.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "유효한 문서만", "ExportFilterSkipArchivedObsolete": "보관/폐기 제외 전체", "ExportFilterAll": "모든 문서", + "ExportChildDocuments": "하위 문서 내보내기", + "ExportChildDocumentsDescription": "선택한 문서에 첨부된 문서도 함께 내보냅니다", "ExportResultRecordTitle": "{workspace} 워크스페이스 문서 내보내기 ({date})" } } diff --git a/plugins/export-assets/lang/pt-br.json b/plugins/export-assets/lang/pt-br.json index 5db9373361..aa2b67a7cb 100644 --- a/plugins/export-assets/lang/pt-br.json +++ b/plugins/export-assets/lang/pt-br.json @@ -54,6 +54,8 @@ "ExportFilterEffectiveOnly": "Apenas documentos efetivos", "ExportFilterSkipArchivedObsolete": "Todos exceto arquivados e obsoletos", "ExportFilterAll": "Todos os documentos", + "ExportChildDocuments": "Exportar documentos filhos", + "ExportChildDocumentsDescription": "Exportar também os documentos anexados aos selecionados", "ExportResultRecordTitle": "Exportação de documentos do espaço de trabalho {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/pt.json b/plugins/export-assets/lang/pt.json index 883ecd1ddf..ab3e3ed599 100644 --- a/plugins/export-assets/lang/pt.json +++ b/plugins/export-assets/lang/pt.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Apenas documentos efetivos", "ExportFilterSkipArchivedObsolete": "Todos exceto arquivados e obsoletos", "ExportFilterAll": "Todos os documentos", + "ExportChildDocuments": "Exportar documentos filhos", + "ExportChildDocumentsDescription": "Exportar também os documentos anexados aos selecionados", "ExportResultRecordTitle": "Exportação de documentos do espaço de trabalho {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/ru.json b/plugins/export-assets/lang/ru.json index ff3b62de91..590bfa996f 100644 --- a/plugins/export-assets/lang/ru.json +++ b/plugins/export-assets/lang/ru.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Только действующие документы", "ExportFilterSkipArchivedObsolete": "Все кроме архивных и устаревших", "ExportFilterAll": "Все документы", + "ExportChildDocuments": "Экспортировать дочерние документы", + "ExportChildDocumentsDescription": "Также экспортировать документы, прикреплённые к выбранным", "ExportResultRecordTitle": "Экспорт документов из пространства {workspace} ({date})" } } diff --git a/plugins/export-assets/lang/tr.json b/plugins/export-assets/lang/tr.json index 3f897ebf75..c4295c1ba7 100644 --- a/plugins/export-assets/lang/tr.json +++ b/plugins/export-assets/lang/tr.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "Yalnızca geçerli belgeler", "ExportFilterSkipArchivedObsolete": "Arşivlenmiş ve geçersiz hariç tümü", "ExportFilterAll": "Tüm belgeler", + "ExportChildDocuments": "Alt belgeleri dışa aktar", + "ExportChildDocumentsDescription": "Seçili belgelere ekli belgeleri de dışa aktar", "ExportResultRecordTitle": "{workspace} çalışma alanından belge dışa aktarma ({date})" } } diff --git a/plugins/export-assets/lang/zh.json b/plugins/export-assets/lang/zh.json index e8968fe0a0..52fed6ed51 100644 --- a/plugins/export-assets/lang/zh.json +++ b/plugins/export-assets/lang/zh.json @@ -53,6 +53,8 @@ "ExportFilterEffectiveOnly": "仅有效文档", "ExportFilterSkipArchivedObsolete": "除已归档和已过时外的全部", "ExportFilterAll": "全部文档", + "ExportChildDocuments": "导出子文档", + "ExportChildDocumentsDescription": "同时导出附加到所选文档上的文档", "ExportResultRecordTitle": "从工作区 {workspace} 导出文档 ({date})" } } diff --git a/plugins/export-resources/src/components/ExportToWorkspaceModal.svelte b/plugins/export-resources/src/components/ExportToWorkspaceModal.svelte index 19dfc4a85a..2d9d39b7d0 100644 --- a/plugins/export-resources/src/components/ExportToWorkspaceModal.svelte +++ b/plugins/export-resources/src/components/ExportToWorkspaceModal.svelte @@ -25,7 +25,7 @@ type Class } from '@hcengineering/core' import { Card, getClient, getCurrentWorkspaceUuid } from '@hcengineering/presentation' - import { DropdownLabels, DropdownLabelsIntl, Label } from '@hcengineering/ui' + import { DropdownLabels, DropdownLabelsIntl, Label, ToggleWithLabel } from '@hcengineering/ui' import { getResource } from '@hcengineering/platform' import login from '@hcengineering/login' import { shouldSkipDocument, isEffectiveDocument } from '@hcengineering/export' @@ -52,6 +52,11 @@ type ExportFilterMode = 'effectiveOnly' | 'skipArchivedObsolete' | 'all' let exportFilterMode: ExportFilterMode = 'effectiveOnly' + // Whether to recursively export child (collection) documents of the selected docs. + // Hidden and forced to true when exporting an entire space — the space already + // enumerates every document inside it. + let includeChildren: boolean = false + const exportFilterItems = [ { id: 'effectiveOnly' as const, label: plugin.string.ExportFilterEffectiveOnly }, { id: 'skipArchivedObsolete' as const, label: plugin.string.ExportFilterSkipArchivedObsolete }, @@ -142,6 +147,10 @@ const effectiveDocs = projectDocExport === true ? await getExportDocuments() : filteredSelectedDocs + // When exporting an entire space, child documents are picked up by the + // space-wide traversal — force the flag on regardless of UI state. + const effectiveIncludeChildren = spaceExport === true ? true : includeChildren + void exportToWorkspace( _class, exportQuery, @@ -149,7 +158,8 @@ targetWorkspace, undefined, exportFilterMode === 'skipArchivedObsolete', - exportFilterMode === 'effectiveOnly' + exportFilterMode === 'effectiveOnly', + effectiveIncludeChildren ) loading = false dispatch('close', true) @@ -191,5 +201,14 @@