fix: Allow selection of embeds (#1562)
* feat: Support importing .docx or .html files as new documents (#1551) * Support importing .docx as new documents * Add html file support, build types and interface for easily adding file types to importer * fix: Upload embedded images in docx to storage * refactor: Bulk of logic to command * refactor: Do all importing on server, so we're not splitting logic for import into two places * test: Add documentImporter tests Co-authored-by: Lance Whatley <whatl3y@gmail.com> * fix: Accessibility audit * fix: Quick fix, non editable title closes #1560 * fix: Embed selection Co-authored-by: Lance Whatley <whatl3y@gmail.com>
This commit is contained in:
5
server/utils/__mocks__/s3.js
Normal file
5
server/utils/__mocks__/s3.js
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
|
||||
export const uploadToS3FromBuffer = jest.fn().mockReturnValue("/endpoint/key");
|
||||
|
||||
export const publicS3Endpoint = jest.fn().mockReturnValue("http://mock");
|
||||
20
server/utils/dataURItoBuffer.js
Normal file
20
server/utils/dataURItoBuffer.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// @flow
|
||||
|
||||
export default function dataURItoBuffer(dataURI: string) {
|
||||
const split = dataURI.split(",");
|
||||
|
||||
if (!dataURI.startsWith("data") || split.length <= 1) {
|
||||
throw new Error("Not a dataURI");
|
||||
}
|
||||
|
||||
// separate out the mime component
|
||||
const type = split[0].split(":")[1].split(";")[0];
|
||||
|
||||
// convert base64 to buffer
|
||||
const buffer = Buffer.from(split[1], "base64");
|
||||
|
||||
return {
|
||||
buffer,
|
||||
type,
|
||||
};
|
||||
}
|
||||
20
server/utils/dataURItoBuffer.test.js
Normal file
20
server/utils/dataURItoBuffer.test.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// @flow
|
||||
import dataURItoBuffer from "./dataURItoBuffer";
|
||||
|
||||
it("should parse value data URI", () => {
|
||||
const response = dataURItoBuffer(
|
||||
`data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAB+FBMVEUAAAA/mUPidDHiLi5Cn0XkNTPmeUrkdUg/m0Q0pEfcpSbwaVdKskg+lUP4zA/iLi3msSHkOjVAmETdJSjtYFE/lkPnRj3sWUs8kkLeqCVIq0fxvhXqUkbVmSjwa1n1yBLepyX1xxP0xRXqUkboST9KukpHpUbuvRrzrhF/ljbwaljuZFM4jELaoSdLtElJrUj1xxP6zwzfqSU4i0HYnydMtUlIqUfywxb60AxZqEXaoifgMCXptR9MtklHpEY2iUHWnSjvvRr70QujkC+pUC/90glMuEnlOjVMt0j70QriLS1LtEnnRj3qUUXfIidOjsxAhcZFo0bjNDH0xxNLr0dIrUdmntVTkMoyfL8jcLBRuErhJyrgKyb4zA/5zg3tYFBBmUTmQTnhMinruBzvvhnxwxZ/st+Ktt5zp9hqota2vtK6y9FemNBblc9HiMiTtMbFtsM6gcPV2r6dwroseLrMrbQrdLGdyKoobKbo3Zh+ynrgVllZulTsXE3rV0pIqUf42UVUo0JyjEHoS0HmsiHRGR/lmRz/1hjqnxjvpRWfwtOhusaz0LRGf7FEfbDVmqHXlJeW0pbXq5bec3fX0nTnzmuJuWvhoFFhm0FtrziBsjaAaDCYWC+uSi6jQS3FsSfLJiTirCOkuCG1KiG+wSC+GBvgyhTszQ64Z77KAAAARXRSTlMAIQRDLyUgCwsE6ebm5ubg2dLR0byXl4FDQzU1NDEuLSUgC+vr6urq6ubb29vb2tra2tG8vLu7u7uXl5eXgYGBgYGBLiUALabIAAABsElEQVQoz12S9VPjQBxHt8VaOA6HE+AOzv1wd7pJk5I2adpCC7RUcHd3d3fXf5PvLkxheD++z+yb7GSRlwD/+Hj/APQCZWxM5M+goF+RMbHK594v+tPoiN1uHxkt+xzt9+R9wnRTZZQpXQ0T5uP1IQxToyOAZiQu5HEpjeA4SWIoksRxNiGC1tRZJ4LNxgHgnU5nJZBDvuDdl8lzQRBsQ+s9PZt7s7Pz8wsL39/DkIfZ4xlB2Gqsq62ta9oxVlVrNZpihFRpGO9fzQw1ms0NDWZz07iGkJmIFH8xxkc3a/WWlubmFkv9AB2SEpDvKxbjidN2faseaNV3zoHXvv7wMODJdkOHAegweAfFPx4G67KluxzottCU9n8CUqXzcIQdXOytAHqXxomvykhEKN9EFutG22p//0rbNvHVxiJywa8yS2KDfV1dfbu31H8jF1RHiTKtWYeHxUvq3bn0pyjCRaiRU6aDO+gb3aEfEeVNsDgm8zzLy9egPa7Qt8TSJdwhjplk06HH43ZNJ3s91KKCHQ5x4sw1fRGYDZ0n1L4FKb9/BP5JLYxToheoFCVxz57PPS8UhhEpLBVeAAAAAElFTkSuQmCC`
|
||||
);
|
||||
expect(response.buffer).toBeTruthy();
|
||||
expect(response.type).toBe("image/png");
|
||||
});
|
||||
|
||||
it("should throw an error with junk input", () => {
|
||||
let err;
|
||||
try {
|
||||
dataURItoBuffer("what");
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
expect(err).toBeTruthy();
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable flowtype/require-valid-file-annotation */
|
||||
// @flow
|
||||
import parseDocumentIds from "./parseDocumentIds";
|
||||
|
||||
it("should not return non links", () => {
|
||||
|
||||
26
server/utils/parseImages.js
Normal file
26
server/utils/parseImages.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// @flow
|
||||
import { parser } from "rich-markdown-editor";
|
||||
|
||||
export default function parseImages(text: string): string[] {
|
||||
const value = parser.parse(text);
|
||||
const images = [];
|
||||
|
||||
function findImages(node) {
|
||||
if (node.type.name === "image") {
|
||||
if (!images.includes(node.attrs.src)) {
|
||||
images.push(node.attrs.src);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.content.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
node.content.descendants(findImages);
|
||||
}
|
||||
|
||||
findImages(value);
|
||||
return images;
|
||||
}
|
||||
24
server/utils/parseImages.test.js
Normal file
24
server/utils/parseImages.test.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// @flow
|
||||
import parseImages from "./parseImages";
|
||||
|
||||
it("should not return non images", () => {
|
||||
expect(parseImages(`# Header`).length).toBe(0);
|
||||
});
|
||||
|
||||
it("should return an array of images", () => {
|
||||
const result = parseImages(`# Header
|
||||
|
||||

|
||||
`);
|
||||
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0]).toBe("/attachments/image.png");
|
||||
});
|
||||
|
||||
it("should not return non document links", () => {
|
||||
expect(parseImages(`[google](http://www.google.com)`).length).toBe(0);
|
||||
});
|
||||
|
||||
it("should not return non document relative links", () => {
|
||||
expect(parseImages(`[relative](/developers)`).length).toBe(0);
|
||||
});
|
||||
@@ -89,6 +89,28 @@ export const publicS3Endpoint = (isServerUpload?: boolean) => {
|
||||
}${AWS_S3_UPLOAD_BUCKET_NAME}`;
|
||||
};
|
||||
|
||||
export const uploadToS3FromBuffer = async (
|
||||
buffer: Buffer,
|
||||
contentType: string,
|
||||
key: string,
|
||||
acl: string
|
||||
) => {
|
||||
await s3
|
||||
.putObject({
|
||||
ACL: acl,
|
||||
Bucket: AWS_S3_UPLOAD_BUCKET_NAME,
|
||||
Key: key,
|
||||
ContentType: contentType,
|
||||
ContentLength: buffer.length,
|
||||
ServerSideEncryption: "AES256",
|
||||
Body: buffer,
|
||||
})
|
||||
.promise();
|
||||
|
||||
const endpoint = publicS3Endpoint(true);
|
||||
return `${endpoint}/${key}`;
|
||||
};
|
||||
|
||||
export const uploadToS3FromUrl = async (
|
||||
url: string,
|
||||
key: string,
|
||||
|
||||
Reference in New Issue
Block a user