diff --git a/app/scenes/Settings/ImportExport.js b/app/scenes/Settings/ImportExport.js index 0ba4966a..a25bc652 100644 --- a/app/scenes/Settings/ImportExport.js +++ b/app/scenes/Settings/ImportExport.js @@ -23,17 +23,22 @@ function ImportExport() { const { showToast } = ui; const [isLoading, setLoading] = React.useState(false); const [isImporting, setImporting] = React.useState(false); + const [importedDetails, setImported] = React.useState(false); const [isExporting, setExporting] = React.useState(false); const [file, setFile] = React.useState(); const [importDetails, setImportDetails] = React.useState(); const handleImport = React.useCallback( async (ev) => { + setImported(undefined); setImporting(true); try { - await documents.batchImport(file); + const { documentCount, collectionCount } = await documents.batchImport( + file + ); showToast(t("Import completed")); + setImported({ documentCount, collectionCount }); } catch (err) { showToast(err.message); } finally { @@ -116,6 +121,17 @@ function ImportExport() { accept="application/zip" /> + {importedDetails && ( + + + Import successful, {{ count: importedDetails.documentCount }}{" "} + documents were imported to your knowledge base. + + + )} {file && !isImportable && ( @@ -126,7 +142,7 @@ function ImportExport() { )} {file && importDetails && isImportable ? ( <> - + {{ fileName: file.name }} looks good, the following collections and their documents will be imported: diff --git a/app/stores/DocumentsStore.js b/app/stores/DocumentsStore.js index 5b5a2c72..3c76189e 100644 --- a/app/stores/DocumentsStore.js +++ b/app/stores/DocumentsStore.js @@ -508,6 +508,8 @@ export default class DocumentsStore extends BaseStore { this.addPolicies(res.policies); res.data.collections.forEach(this.rootStore.collections.add); + + return res.data; }; @action diff --git a/server/api/documents.js b/server/api/documents.js index b1a3ec43..d9e642de 100644 --- a/server/api/documents.js +++ b/server/api/documents.js @@ -1124,7 +1124,7 @@ router.post("documents.batchImport", auth(), async (ctx) => { const user = ctx.state.user; authorize(user, "batchImport", Document); - const { collections } = await documentBatchImporter({ + const { documents, attachments, collections } = await documentBatchImporter({ file, user, type, @@ -1133,6 +1133,9 @@ router.post("documents.batchImport", auth(), async (ctx) => { ctx.body = { data: { + attachmentCount: attachments.length, + documentCount: documents.length, + collectionCount: collections.length, collections: collections.map((collection) => presentCollection(collection) ), @@ -1189,6 +1192,7 @@ router.post("documents.import", auth(), async (ctx) => { }); const document = await documentCreator({ + source: "import", title, text, publish, diff --git a/server/commands/attachmentCreator.js b/server/commands/attachmentCreator.js index 46f0215c..906054cb 100644 --- a/server/commands/attachmentCreator.js +++ b/server/commands/attachmentCreator.js @@ -8,12 +8,14 @@ export default async function attachmentCreator({ type, buffer, user, + source, ip, }: { name: string, type: string, buffer: Buffer, user: User, + source?: "import", ip: string, }) { const key = `uploads/${user.id}/${uuid.v4()}/${name}`; @@ -32,7 +34,7 @@ export default async function attachmentCreator({ await Event.create({ name: "attachments.create", - data: { name }, + data: { name, source }, modelId: attachment.id, teamId: user.teamId, actorId: user.id, diff --git a/server/commands/documentBatchImporter.js b/server/commands/documentBatchImporter.js index 884a5822..a34b1502 100644 --- a/server/commands/documentBatchImporter.js +++ b/server/commands/documentBatchImporter.js @@ -114,6 +114,7 @@ export default async function documentBatchImporter({ } const document = await documentCreator({ + source: "import", title, text, publish: true, @@ -134,6 +135,7 @@ export default async function documentBatchImporter({ if (item.type === "attachment") { const buffer = await item.item.async("nodebuffer"); const attachment = await attachmentCreator({ + source: "import", name: item.name, type, buffer, diff --git a/server/commands/documentCreator.js b/server/commands/documentCreator.js index 805af277..3c986c3f 100644 --- a/server/commands/documentCreator.js +++ b/server/commands/documentCreator.js @@ -14,6 +14,7 @@ export default async function documentCreator({ index, user, editorVersion, + source, ip, }: { title: string, @@ -28,6 +29,7 @@ export default async function documentCreator({ index?: number, user: User, editorVersion?: string, + source?: "import", ip: string, }): Document { const templateId = templateDocument ? templateDocument.id : undefined; @@ -53,7 +55,7 @@ export default async function documentCreator({ collectionId: document.collectionId, teamId: document.teamId, actorId: user.id, - data: { title: document.title, templateId }, + data: { source, title: document.title, templateId }, ip, }); @@ -66,7 +68,7 @@ export default async function documentCreator({ collectionId: document.collectionId, teamId: document.teamId, actorId: user.id, - data: { title: document.title }, + data: { source, title: document.title }, ip, }); } diff --git a/server/events.js b/server/events.js index f60181c5..0d671f5d 100644 --- a/server/events.js +++ b/server/events.js @@ -47,6 +47,10 @@ export type DocumentEvent = teamId: string, actorId: string, ip: string, + data: { + title: string, + source?: "import", + }, } | { name: "documents.move", diff --git a/server/services/notifications.js b/server/services/notifications.js index 9629ad40..2cd9d833 100644 --- a/server/services/notifications.js +++ b/server/services/notifications.js @@ -27,6 +27,9 @@ export default class Notifications { } async documentUpdated(event: DocumentEvent) { + // never send notifications when batch importing documents + if (event.data && event.data.source === "import") return; + const document = await Document.findByPk(event.documentId); if (!document) return; diff --git a/server/services/slack.js b/server/services/slack.js index 3b552eb6..fd29ec13 100644 --- a/server/services/slack.js +++ b/server/services/slack.js @@ -55,6 +55,9 @@ export default class Slack { } async documentUpdated(event: DocumentEvent) { + // never send notifications when batch importing documents + if (event.data && event.data.source === "import") return; + const document = await Document.findByPk(event.documentId); if (!document) return; diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index ae74c485..80cfee22 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -279,6 +279,8 @@ "Export in progress…": "Export in progress…", "Import": "Import", "It is possible to import a zip file of folders and Markdown files previously exported from an Outline instance. Support will soon be added for importing from other services.": "It is possible to import a zip file of folders and Markdown files previously exported from an Outline instance. Support will soon be added for importing from other services.", + "importSuccessful": "Import successful, {{ count }} document was imported into your knowledge base.", + "importSuccessful_plural": "Import successful, {{ count }} documents were imported into your knowledge base.", "Sorry, the file <1>{{fileName}} is missing valid collections or documents.": "Sorry, the file <1>{{fileName}} is missing valid collections or documents.", "<0>{{fileName}} looks good, the following collections and their documents will be imported:": "<0>{{fileName}} looks good, the following collections and their documents will be imported:", "Importing": "Importing", @@ -309,4 +311,4 @@ "Suspended": "Suspended", "Edit Profile": "Edit Profile", "{{ userName }} hasn’t updated any documents yet.": "{{ userName }} hasn’t updated any documents yet." -} +} \ No newline at end of file