feat: Add HTML export option (#4056)

* tidy

* Add title to HTML export

* fix: Add compatability for documents without collab state

* Add HTML download option to UI

* docs

* fix nodes that required document to render

* Refactor to allow for styling of HTML export

* div>article for easier programatic content extraction
This commit is contained in:
Tom Moor
2022-09-07 13:34:39 +02:00
committed by GitHub
parent eb5126335c
commit e8a6de3f18
30 changed files with 1756 additions and 1790 deletions

View File

@@ -1,4 +1,3 @@
import { updateYFragment } from "@getoutline/y-prosemirror";
import removeMarkdown from "@tommoor/remove-markdown";
import invariant from "invariant";
import { compact, find, map, uniq } from "lodash";
@@ -34,14 +33,12 @@ import {
} from "sequelize-typescript";
import MarkdownSerializer from "slate-md-serializer";
import isUUID from "validator/lib/isUUID";
import * as Y from "yjs";
import { DateFilter } from "@shared/types";
import getTasks from "@shared/utils/getTasks";
import parseTitle from "@shared/utils/parseTitle";
import unescape from "@shared/utils/unescape";
import { SLUG_URL_REGEX } from "@shared/utils/urlHelpers";
import { DocumentValidation } from "@shared/validations";
import { parser } from "@server/editor";
import slugify from "@server/utils/slugify";
import Backlink from "./Backlink";
import Collection from "./Collection";
@@ -482,7 +479,7 @@ class Document extends ParanoidModel {
query: string,
options: SearchOptions = {}
): Promise<SearchResponse> {
const wildcardQuery = `${escape(query)}:*`;
const wildcardQuery = `${escapeQuery(query)}:*`;
const {
snippetMinWords = 20,
snippetMaxWords = 30,
@@ -610,7 +607,7 @@ class Document extends ParanoidModel {
limit = 15,
offset = 0,
} = options;
const wildcardQuery = `${escape(query)}:*`;
const wildcardQuery = `${escapeQuery(query)}:*`;
// Ensure we're filtering by the users accessible collections. If
// collectionId is passed as an option it is assumed that the authorization
@@ -731,38 +728,6 @@ class Document extends ParanoidModel {
// instance methods
updateFromMarkdown = (text: string, append = false) => {
this.text = append ? this.text + text : text;
if (this.state) {
const ydoc = new Y.Doc();
Y.applyUpdate(ydoc, this.state);
const type = ydoc.get("default", Y.XmlFragment) as Y.XmlFragment;
const doc = parser.parse(this.text);
if (!type.doc) {
throw new Error("type.doc not found");
}
// apply new document to existing ydoc
updateYFragment(type.doc, type, doc, new Map());
const state = Y.encodeStateAsUpdate(ydoc);
this.state = Buffer.from(state);
this.changed("state", true);
}
};
toMarkdown = () => {
const text = unescape(this.text);
if (this.version) {
return `# ${this.title}\n\n${text}`;
}
return text;
};
migrateVersion = () => {
let migrated = false;
@@ -1054,7 +1019,7 @@ class Document extends ParanoidModel {
};
}
function escape(query: string): string {
function escapeQuery(query: string): string {
// replace "\" with escaped "\\" because sequelize.escape doesn't do it
// https://github.com/sequelize/sequelize/issues/2950
return Document.sequelize!.escape(query).replace(/\\/g, "\\\\");