diff --git a/server/commands/documentBatchImporter.js b/server/commands/documentBatchImporter.js index d0ba6aecc..aa1f12096 100644 --- a/server/commands/documentBatchImporter.js +++ b/server/commands/documentBatchImporter.js @@ -4,7 +4,8 @@ import path from "path"; import File from "formidable/lib/file"; import invariant from "invariant"; import JSZip from "jszip"; -import { Collection, User } from "../models"; +import { values, keys } from "lodash"; +import { Attachment, Document, Collection, User } from "../models"; import attachmentCreator from "./attachmentCreator"; import documentCreator from "./documentCreator"; import documentImporter from "./documentImporter"; @@ -25,9 +26,9 @@ export default async function documentBatchImporter({ const zip = await JSZip.loadAsync(zipData); // store progress and pointers - let attachments = {}; - let collections = {}; - let documents = {}; + let collections: { string: Collection } = {}; + let documents: { string: Document } = {}; + let attachments: { string: Attachment } = {}; // this is so we can use async / await a little easier let folders = []; @@ -46,9 +47,6 @@ export default async function documentBatchImporter({ continue; } - // all top level items must be directories representing collections - console.log("iterating over", itemPath, depth); - if (depth === 0 && item.dir && name) { // check if collection with name exists let [collection, isCreated] = await Collection.findOrCreate({ @@ -79,8 +77,9 @@ export default async function documentBatchImporter({ } if (depth > 0 && !item.dir && item.name.endsWith(".md")) { - const collection = collections[itemDir]; - invariant(collection, "Collection must exist for document", itemDir); + const collectionDir = itemDir.split("/")[0]; + const collection = collections[collectionDir]; + invariant(collection, `Collection must exist for document ${itemDir}`); // we have a document const content = await item.async("string"); @@ -98,9 +97,12 @@ export default async function documentBatchImporter({ ip, }); - // must be a nested document, find the parent + // must be a nested document, find and reference the parent document + let parentDocumentId; if (depth > 1) { - console.log("nested doc", itemDir); + const parentDocument = documents[`${itemDir}.md`] || documents[itemDir]; + invariant(parentDocument, `Document must exist for parent ${itemDir}`); + parentDocumentId = parentDocument.id; } const document = await documentCreator({ @@ -108,7 +110,7 @@ export default async function documentBatchImporter({ text, publish: true, collectionId: collection.id, - parentDocumentId: undefined, + parentDocumentId, user, ip, }); @@ -117,11 +119,6 @@ export default async function documentBatchImporter({ continue; } - if (depth > 0 && item.dir && name !== "uploads") { - // we have a nested document, create if it doesn't exist based on title - continue; - } - if (depth > 0 && !item.dir && itemPath.includes("uploads")) { // we have an attachment const buffer = await item.async("nodebuffer"); @@ -139,6 +136,21 @@ export default async function documentBatchImporter({ console.log(`Skipped ${itemPath}`); } + // All collections, documents, and attachments have been created – time to + // update the documents to point to newly uploaded attachments where possible + for (const attachmentPath of keys(attachments)) { + const attachment = attachments[attachmentPath]; + + for (const document of values(documents)) { + document.text = document.text + .replace(attachmentPath, attachment.redirectUrl) + .replace(`/${attachmentPath}`, attachment.redirectUrl); + + // does nothing if the document text is unchanged + await document.save(); + } + } + return { documents, collections, diff --git a/server/commands/documentBatchImporter.test.js b/server/commands/documentBatchImporter.test.js index cf97c71a4..53b94d963 100644 --- a/server/commands/documentBatchImporter.test.js +++ b/server/commands/documentBatchImporter.test.js @@ -29,7 +29,7 @@ describe("documentBatchImporter", () => { }); expect(Object.keys(response.collections).length).toEqual(1); - expect(Object.keys(response.documents).length).toEqual(15); + expect(Object.keys(response.documents).length).toEqual(8); expect(Object.keys(response.attachments).length).toEqual(6); }); }); diff --git a/server/policies/document.js b/server/policies/document.js index 06356c8b3..203d80551 100644 --- a/server/policies/document.js +++ b/server/policies/document.js @@ -8,7 +8,7 @@ const { allow, cannot } = policy; allow(User, "create", Document); -allow(User, "batchImport", Document, actor => { +allow(User, "batchImport", Document, (actor) => { if (actor.isAdmin) return true; throw new AdminRequiredError(); }); diff --git a/server/test/fixtures/outline.zip b/server/test/fixtures/outline.zip index bc361a7ad..030598dcb 100644 Binary files a/server/test/fixtures/outline.zip and b/server/test/fixtures/outline.zip differ