feat: convert page

This commit is contained in:
NGPixel
2021-04-02 00:39:11 -04:00
parent d75c5532d1
commit 26f1c0f372
10 changed files with 331 additions and 2 deletions

View File

@@ -9,6 +9,9 @@ const striptags = require('striptags')
const emojiRegex = require('emoji-regex')
const he = require('he')
const CleanCSS = require('clean-css')
const TurndownService = require('turndown')
const turndownPluginGfm = require('@joplin/turndown-plugin-gfm').gfm
const cheerio = require('cheerio')
/* global WIKI */
@@ -140,6 +143,7 @@ module.exports = class Page extends Model {
creatorId: 'uint',
creatorName: 'string',
description: 'string',
editorKey: 'string',
isPrivate: 'boolean',
isPublished: 'boolean',
publishEndDate: 'string',
@@ -471,6 +475,134 @@ module.exports = class Page extends Model {
return page
}
/**
* Convert an Existing Page
*
* @param {Object} opts Page Properties
* @returns {Promise} Promise of the Page Model Instance
*/
static async convertPage(opts) {
// -> Fetch original page
const ogPage = await WIKI.models.pages.query().findById(opts.id)
if (!ogPage) {
throw new Error('Invalid Page Id')
}
// -> Check for page access
if (!WIKI.auth.checkAccess(opts.user, ['write:pages'], {
locale: ogPage.localeCode,
path: ogPage.path
})) {
throw new WIKI.Error.PageUpdateForbidden()
}
// -> Check content type
const sourceContentType = ogPage.contentType
const targetContentType = _.get(_.find(WIKI.data.editors, ['key', opts.editor]), `contentType`, 'text')
const shouldConvert = sourceContentType !== targetContentType
let convertedContent = null
// -> Convert content
if (shouldConvert) {
// -> Markdown => HTML
if (sourceContentType === 'markdown' && targetContentType === 'html') {
if (!ogPage.render) {
throw new Error('Aborted conversion because rendered page content is empty!')
}
convertedContent = ogPage.render
const $ = cheerio.load(convertedContent, {
decodeEntities: true
})
if ($.root().children().length > 0) {
$('.toc-anchor').remove()
convertedContent = $.html('body').replace('<body>', '').replace('</body>', '').replace(/&#x([0-9a-f]{1,6});/ig, (entity, code) => {
code = parseInt(code, 16)
// Don't unescape ASCII characters, assuming they're encoded for a good reason
if (code < 0x80) return entity
return String.fromCodePoint(code)
})
}
// -> HTML => Markdown
} else if (sourceContentType === 'html' && targetContentType === 'markdown') {
const td = new TurndownService({
bulletListMarker: '-',
codeBlockStyle: 'fenced',
emDelimiter: '*',
fence: '```',
headingStyle: 'atx',
hr: '---',
linkStyle: 'inlined',
preformattedCode: true,
strongDelimiter: '**'
})
td.use(turndownPluginGfm)
td.keep(['kbd'])
td.addRule('subscript', {
filter: ['sub'],
replacement: c => `~${c}~`
})
td.addRule('superscript', {
filter: ['sup'],
replacement: c => `^${c}^`
})
td.addRule('underline', {
filter: ['u'],
replacement: c => `_${c}_`
})
td.addRule('removeTocAnchors', {
filter: (n, o) => {
return n.nodeName === 'A' && n.classList.contains('toc-anchor')
},
replacement: c => ''
})
convertedContent = td.turndown(ogPage.content)
// -> Unsupported
} else {
throw new Error('Unsupported source / destination content types combination.')
}
}
// -> Create version snapshot
if (shouldConvert) {
await WIKI.models.pageHistory.addVersion({
...ogPage,
isPublished: ogPage.isPublished === true || ogPage.isPublished === 1,
action: 'updated',
versionDate: ogPage.updatedAt
})
}
// -> Update page
await WIKI.models.pages.query().patch({
contentType: targetContentType,
editorKey: opts.editor,
...(convertedContent ? { content: convertedContent } : {})
}).where('id', ogPage.id)
const page = await WIKI.models.pages.getPageFromDb(ogPage.id)
await WIKI.models.pages.deletePageFromCache(page.hash)
WIKI.events.outbound.emit('deletePageFromCache', page.hash)
// -> Update on Storage
await WIKI.models.storage.pageEvent({
event: 'updated',
page
})
}
/**
* Move a Page
*
@@ -872,6 +1004,7 @@ module.exports = class Page extends Model {
creatorId: page.creatorId,
creatorName: page.creatorName,
description: page.description,
editorKey: page.editorKey,
extra: {
css: _.get(page, 'extra.css', ''),
js: _.get(page, 'extra.js', '')