mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
fix: Duplicate paths in export ZIP (#12674)
This commit is contained in:
@@ -150,26 +150,12 @@ export default abstract class ExportDocumentTreeTask extends ExportTask {
|
|||||||
includeAttachments = true
|
includeAttachments = true
|
||||||
) {
|
) {
|
||||||
const pathMap = this.createPathMap(collections, format);
|
const pathMap = this.createPathMap(collections, format);
|
||||||
Logger.debug(
|
await this.addDocumentsToArchive({
|
||||||
"task",
|
zip,
|
||||||
`Start adding ${Object.values(pathMap).length} documents to archive`
|
pathMap,
|
||||||
);
|
format,
|
||||||
|
includeAttachments,
|
||||||
for (const path of pathMap) {
|
});
|
||||||
const documentId = path[0].replace("/doc/", "");
|
|
||||||
const pathInZip = path[1];
|
|
||||||
|
|
||||||
await this.processDocument({
|
|
||||||
zip,
|
|
||||||
pathInZip,
|
|
||||||
documentId,
|
|
||||||
includeAttachments,
|
|
||||||
format,
|
|
||||||
pathMap,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.debug("task", "Completed adding documents to archive");
|
|
||||||
|
|
||||||
return await ZipHelper.toTmpFile(zip);
|
return await ZipHelper.toTmpFile(zip);
|
||||||
}
|
}
|
||||||
@@ -200,28 +186,58 @@ export default abstract class ExportDocumentTreeTask extends ExportTask {
|
|||||||
format
|
format
|
||||||
);
|
);
|
||||||
|
|
||||||
Logger.debug(
|
await this.addDocumentsToArchive({
|
||||||
"task",
|
zip,
|
||||||
`Start adding ${Object.values(pathMap).length} documents to archive`
|
pathMap,
|
||||||
);
|
format,
|
||||||
|
includeAttachments: true,
|
||||||
|
});
|
||||||
|
|
||||||
for (const entry of pathMap) {
|
return await ZipHelper.toTmpFile(zip);
|
||||||
const documentId = entry[0].replace("/doc/", "");
|
}
|
||||||
const pathInZip = entry[1];
|
|
||||||
|
/**
|
||||||
|
* Processes each unique document in the path map and adds it to the zip.
|
||||||
|
*
|
||||||
|
* @param zip The yazl ZipFile to add files to
|
||||||
|
* @param pathMap Map of document urls to their path in the zip
|
||||||
|
* @param format The format to export in
|
||||||
|
* @param includeAttachments Whether to include attachments in the export
|
||||||
|
*/
|
||||||
|
private async addDocumentsToArchive({
|
||||||
|
zip,
|
||||||
|
pathMap,
|
||||||
|
format,
|
||||||
|
includeAttachments,
|
||||||
|
}: {
|
||||||
|
zip: ZipFile;
|
||||||
|
pathMap: Map<string, string>;
|
||||||
|
format: FileOperationFormat;
|
||||||
|
includeAttachments: boolean;
|
||||||
|
}) {
|
||||||
|
const processedPaths = new Set<string>();
|
||||||
|
|
||||||
|
Logger.debug("task", `Start adding documents to archive`);
|
||||||
|
|
||||||
|
for (const [url, pathInZip] of pathMap) {
|
||||||
|
// A document may be keyed by multiple urls in the path map, only
|
||||||
|
// process each file in the zip once.
|
||||||
|
if (processedPaths.has(pathInZip)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
processedPaths.add(pathInZip);
|
||||||
|
|
||||||
await this.processDocument({
|
await this.processDocument({
|
||||||
zip,
|
zip,
|
||||||
pathInZip,
|
pathInZip,
|
||||||
documentId,
|
documentId: url.replace("/doc/", ""),
|
||||||
includeAttachments: true,
|
includeAttachments,
|
||||||
format,
|
format,
|
||||||
pathMap,
|
pathMap,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.debug("task", "Completed adding documents to archive");
|
Logger.debug("task", "Completed adding documents to archive");
|
||||||
|
|
||||||
return await ZipHelper.toTmpFile(zip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import fs from "fs-extra";
|
||||||
|
import ZipHelper from "@server/utils/ZipHelper";
|
||||||
|
import {
|
||||||
|
buildCollection,
|
||||||
|
buildDocument,
|
||||||
|
buildFileOperation,
|
||||||
|
buildTeam,
|
||||||
|
buildUser,
|
||||||
|
} from "@server/test/factories";
|
||||||
|
import ExportMarkdownZipTask from "./ExportMarkdownZipTask";
|
||||||
|
|
||||||
|
describe("ExportMarkdownZipTask", () => {
|
||||||
|
it("should not duplicate documents in the zip file", async () => {
|
||||||
|
const team = await buildTeam();
|
||||||
|
const user = await buildUser({ teamId: team.id });
|
||||||
|
const collection = await buildCollection({
|
||||||
|
teamId: team.id,
|
||||||
|
createdById: user.id,
|
||||||
|
});
|
||||||
|
const documents = await Promise.all([
|
||||||
|
buildDocument({
|
||||||
|
teamId: team.id,
|
||||||
|
userId: user.id,
|
||||||
|
collectionId: collection.id,
|
||||||
|
title: "Test1",
|
||||||
|
}),
|
||||||
|
buildDocument({
|
||||||
|
teamId: team.id,
|
||||||
|
userId: user.id,
|
||||||
|
collectionId: collection.id,
|
||||||
|
title: "Test2",
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
for (const document of documents) {
|
||||||
|
await collection.addDocumentToStructure(document);
|
||||||
|
}
|
||||||
|
const fileOperation = await buildFileOperation({
|
||||||
|
teamId: team.id,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const task = new ExportMarkdownZipTask();
|
||||||
|
const filePath = await task.exportCollections([collection], fileOperation);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileNames: string[] = [];
|
||||||
|
await ZipHelper.walk(filePath, (entry) => {
|
||||||
|
if (!entry.isDirectory) {
|
||||||
|
fileNames.push(entry.fileName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fileNames.sort()).toEqual([
|
||||||
|
`${collection.name}/Test1.md`,
|
||||||
|
`${collection.name}/Test2.md`,
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
await fs.remove(filePath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user