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:
Tom Moor
2020-09-20 22:27:11 -07:00
committed by GitHub
parent e67d319e2b
commit 4ffc04bc5d
53 changed files with 735 additions and 218 deletions

View 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");

View 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,
};
}

View 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();
});

View File

@@ -1,4 +1,4 @@
/* eslint-disable flowtype/require-valid-file-annotation */
// @flow
import parseDocumentIds from "./parseDocumentIds";
it("should not return non links", () => {

View 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;
}

View 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
![internal](/attachments/image.png)
`);
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);
});

View File

@@ -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,