|
@ -114,6 +114,9 @@ module.exports = class Page extends Model { |
|
|
this.updatedAt = new Date().toISOString() |
|
|
this.updatedAt = new Date().toISOString() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Cache Schema |
|
|
|
|
|
*/ |
|
|
static get cacheSchema() { |
|
|
static get cacheSchema() { |
|
|
return new JSBinType({ |
|
|
return new JSBinType({ |
|
|
id: 'uint', |
|
|
id: 'uint', |
|
@ -142,6 +145,8 @@ module.exports = class Page extends Model { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Inject page metadata into contents |
|
|
* Inject page metadata into contents |
|
|
|
|
|
* |
|
|
|
|
|
* @returns {string} Page Contents with Injected Metadata |
|
|
*/ |
|
|
*/ |
|
|
injectMetadata () { |
|
|
injectMetadata () { |
|
|
return pageHelper.injectPageMetadata(this) |
|
|
return pageHelper.injectPageMetadata(this) |
|
@ -149,6 +154,8 @@ module.exports = class Page extends Model { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Get the page's file extension based on content type |
|
|
* Get the page's file extension based on content type |
|
|
|
|
|
* |
|
|
|
|
|
* @returns {string} File Extension |
|
|
*/ |
|
|
*/ |
|
|
getFileExtension() { |
|
|
getFileExtension() { |
|
|
switch (this.contentType) { |
|
|
switch (this.contentType) { |
|
@ -166,6 +173,7 @@ module.exports = class Page extends Model { |
|
|
* |
|
|
* |
|
|
* @param {String} raw Raw file contents |
|
|
* @param {String} raw Raw file contents |
|
|
* @param {String} contentType Content Type |
|
|
* @param {String} contentType Content Type |
|
|
|
|
|
* @returns {Object} Parsed Page Metadata with Raw Content |
|
|
*/ |
|
|
*/ |
|
|
static parseMetadata (raw, contentType) { |
|
|
static parseMetadata (raw, contentType) { |
|
|
let result |
|
|
let result |
|
@ -204,6 +212,12 @@ module.exports = class Page extends Model { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Create a New Page |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts Page Properties |
|
|
|
|
|
* @returns {Promise} Promise of the Page Model Instance |
|
|
|
|
|
*/ |
|
|
static async createPage(opts) { |
|
|
static async createPage(opts) { |
|
|
const dupCheck = await WIKI.models.pages.query().select('id').where('localeCode', opts.locale).where('path', opts.path).first() |
|
|
const dupCheck = await WIKI.models.pages.query().select('id').where('localeCode', opts.locale).where('path', opts.path).first() |
|
|
if (dupCheck) { |
|
|
if (dupCheck) { |
|
@ -259,9 +273,22 @@ module.exports = class Page extends Model { |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// -> Reconnect Links
|
|
|
|
|
|
await WIKI.models.pages.reconnectLinks({ |
|
|
|
|
|
locale: page.localeCode, |
|
|
|
|
|
path: page.path, |
|
|
|
|
|
mode: 'create' |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
return page |
|
|
return page |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Update an Existing Page |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts Page Properties |
|
|
|
|
|
* @returns {Promise} Promise of the Page Model Instance |
|
|
|
|
|
*/ |
|
|
static async updatePage(opts) { |
|
|
static async updatePage(opts) { |
|
|
const ogPage = await WIKI.models.pages.query().findById(opts.id) |
|
|
const ogPage = await WIKI.models.pages.query().findById(opts.id) |
|
|
if (!ogPage) { |
|
|
if (!ogPage) { |
|
@ -314,6 +341,12 @@ module.exports = class Page extends Model { |
|
|
return page |
|
|
return page |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Delete an Existing Page |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts Page Properties |
|
|
|
|
|
* @returns {Promise} Promise with no value |
|
|
|
|
|
*/ |
|
|
static async deletePage(opts) { |
|
|
static async deletePage(opts) { |
|
|
let page |
|
|
let page |
|
|
if (_.has(opts, 'id')) { |
|
|
if (_.has(opts, 'id')) { |
|
@ -344,8 +377,98 @@ module.exports = class Page extends Model { |
|
|
page |
|
|
page |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// -> Reconnect Links
|
|
|
|
|
|
await WIKI.models.pages.reconnectLinks({ |
|
|
|
|
|
locale: page.localeCode, |
|
|
|
|
|
path: page.path, |
|
|
|
|
|
mode: 'delete' |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Reconnect links to new/updated/deleted page |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts - Page parameters |
|
|
|
|
|
* @param {string} opts.path - Page Path |
|
|
|
|
|
* @param {string} opts.locale - Page Locale Code |
|
|
|
|
|
* @param {string} [opts.sourcePath] - Previous Page Path (move only) |
|
|
|
|
|
* @param {string} [opts.sourceLocale] - Previous Page Locale Code (move only) |
|
|
|
|
|
* @param {string} opts.mode - Page Update mode (new, move, delete) |
|
|
|
|
|
* @returns {Promise} Promise with no value |
|
|
|
|
|
*/ |
|
|
|
|
|
static async reconnectLinks (opts) { |
|
|
|
|
|
const pageHref = `/${opts.locale}/${opts.path}` |
|
|
|
|
|
let replaceArgs = { |
|
|
|
|
|
from: '', |
|
|
|
|
|
to: '' |
|
|
|
|
|
} |
|
|
|
|
|
switch (opts.mode) { |
|
|
|
|
|
case 'create': |
|
|
|
|
|
replaceArgs.from = `<a href="${pageHref}" class="is-internal-link is-invalid-page">` |
|
|
|
|
|
replaceArgs.to = `<a href="${pageHref}" class="is-internal-link is-valid-page">` |
|
|
|
|
|
break |
|
|
|
|
|
case 'move': |
|
|
|
|
|
const prevPageHref = `/${opts.sourceLocale}/${opts.sourcePath}` |
|
|
|
|
|
replaceArgs.from = `<a href="${prevPageHref}" class="is-internal-link is-invalid-page">` |
|
|
|
|
|
replaceArgs.to = `<a href="${pageHref}" class="is-internal-link is-valid-page">` |
|
|
|
|
|
break |
|
|
|
|
|
case 'delete': |
|
|
|
|
|
replaceArgs.from = `<a href="${pageHref}" class="is-internal-link is-valid-page">` |
|
|
|
|
|
replaceArgs.to = `<a href="${pageHref}" class="is-internal-link is-invalid-page">` |
|
|
|
|
|
break |
|
|
|
|
|
default: |
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let affectedHashes = [] |
|
|
|
|
|
// -> Perform replace and return affected page hashes (POSTGRES, MSSQL only)
|
|
|
|
|
|
if (WIKI.config.db.type === 'postgres' || WIKI.config.db.type === 'mssql') { |
|
|
|
|
|
affectedHashes = await WIKI.models.pages.query() |
|
|
|
|
|
.returning('hash') |
|
|
|
|
|
.patch({ |
|
|
|
|
|
render: WIKI.models.knex.raw('REPLACE(??, ?, ?)', ['render', replaceArgs.from, replaceArgs.to]) |
|
|
|
|
|
}) |
|
|
|
|
|
.whereIn('pages.id', function () { |
|
|
|
|
|
this.select('pageLinks.pageId').from('pageLinks').where({ |
|
|
|
|
|
'pageLinks.path': opts.path, |
|
|
|
|
|
'pageLinks.localeCode': opts.locale |
|
|
|
|
|
}) |
|
|
|
|
|
}) |
|
|
|
|
|
.pluck('hash') |
|
|
|
|
|
} else { |
|
|
|
|
|
// -> Perform replace, then query affected page hashes (MYSQL, MARIADB, SQLITE only)
|
|
|
|
|
|
await WIKI.models.pages.query() |
|
|
|
|
|
.patch({ |
|
|
|
|
|
render: WIKI.models.knex.raw('REPLACE(??, ?, ?)', ['render', replaceArgs.from, replaceArgs.to]) |
|
|
|
|
|
}) |
|
|
|
|
|
.whereIn('pages.id', function () { |
|
|
|
|
|
this.select('pageLinks.pageId').from('pageLinks').where({ |
|
|
|
|
|
'pageLinks.path': opts.path, |
|
|
|
|
|
'pageLinks.localeCode': opts.locale |
|
|
|
|
|
}) |
|
|
|
|
|
}) |
|
|
|
|
|
affectedHashes = await WIKI.models.pages.query() |
|
|
|
|
|
.column('hash') |
|
|
|
|
|
.whereIn('pages.id', function () { |
|
|
|
|
|
this.select('pageLinks.pageId').from('pageLinks').where({ |
|
|
|
|
|
'pageLinks.path': opts.path, |
|
|
|
|
|
'pageLinks.localeCode': opts.locale |
|
|
|
|
|
}) |
|
|
|
|
|
}) |
|
|
|
|
|
.pluck('hash') |
|
|
|
|
|
} |
|
|
|
|
|
for (const hash of affectedHashes) { |
|
|
|
|
|
await WIKI.models.pages.deletePageFromCache({ hash }) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Trigger the rendering of a page |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} page Page Model Instance |
|
|
|
|
|
* @returns {Promise} Promise with no value |
|
|
|
|
|
*/ |
|
|
static async renderPage(page) { |
|
|
static async renderPage(page) { |
|
|
const renderJob = await WIKI.scheduler.registerJob({ |
|
|
const renderJob = await WIKI.scheduler.registerJob({ |
|
|
name: 'render-page', |
|
|
name: 'render-page', |
|
@ -355,6 +478,12 @@ module.exports = class Page extends Model { |
|
|
return renderJob.finished |
|
|
return renderJob.finished |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fetch an Existing Page from Cache if possible, from DB otherwise and save render to Cache |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts Page Properties |
|
|
|
|
|
* @returns {Promise} Promise of the Page Model Instance |
|
|
|
|
|
*/ |
|
|
static async getPage(opts) { |
|
|
static async getPage(opts) { |
|
|
// -> Get from cache first
|
|
|
// -> Get from cache first
|
|
|
let page = await WIKI.models.pages.getPageFromCache(opts) |
|
|
let page = await WIKI.models.pages.getPageFromCache(opts) |
|
@ -375,6 +504,12 @@ module.exports = class Page extends Model { |
|
|
return page |
|
|
return page |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fetch an Existing Page from the Database |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts Page Properties |
|
|
|
|
|
* @returns {Promise} Promise of the Page Model Instance |
|
|
|
|
|
*/ |
|
|
static async getPageFromDb(opts) { |
|
|
static async getPageFromDb(opts) { |
|
|
const queryModeID = _.isNumber(opts) |
|
|
const queryModeID = _.isNumber(opts) |
|
|
try { |
|
|
try { |
|
@ -426,6 +561,12 @@ module.exports = class Page extends Model { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Save a Page Model Instance to Cache |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} page Page Model Instance |
|
|
|
|
|
* @returns {Promise} Promise with no value |
|
|
|
|
|
*/ |
|
|
static async savePageToCache(page) { |
|
|
static async savePageToCache(page) { |
|
|
const cachePath = path.join(process.cwd(), `data/cache/${page.hash}.bin`) |
|
|
const cachePath = path.join(process.cwd(), `data/cache/${page.hash}.bin`) |
|
|
await fs.outputFile(cachePath, WIKI.models.pages.cacheSchema.encode({ |
|
|
await fs.outputFile(cachePath, WIKI.models.pages.cacheSchema.encode({ |
|
@ -448,6 +589,12 @@ module.exports = class Page extends Model { |
|
|
})) |
|
|
})) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Fetch an Existing Page from Cache |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts Page Properties |
|
|
|
|
|
* @returns {Promise} Promise of the Page Model Instance |
|
|
|
|
|
*/ |
|
|
static async getPageFromCache(opts) { |
|
|
static async getPageFromCache(opts) { |
|
|
const pageHash = pageHelper.generateHash({ path: opts.path, locale: opts.locale, privateNS: opts.isPrivate ? 'TODO' : '' }) |
|
|
const pageHash = pageHelper.generateHash({ path: opts.path, locale: opts.locale, privateNS: opts.isPrivate ? 'TODO' : '' }) |
|
|
const cachePath = path.join(process.cwd(), `data/cache/${pageHash}.bin`) |
|
|
const cachePath = path.join(process.cwd(), `data/cache/${pageHash}.bin`) |
|
@ -470,14 +617,32 @@ module.exports = class Page extends Model { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Delete an Existing Page from Cache |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} page Page Model Instance |
|
|
|
|
|
* @param {string} page.hash Hash of the Page |
|
|
|
|
|
* @returns {Promise} Promise with no value |
|
|
|
|
|
*/ |
|
|
static async deletePageFromCache(page) { |
|
|
static async deletePageFromCache(page) { |
|
|
return fs.remove(path.join(process.cwd(), `data/cache/${page.hash}.bin`)) |
|
|
return fs.remove(path.join(process.cwd(), `data/cache/${page.hash}.bin`)) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Flush the contents of the Cache |
|
|
|
|
|
*/ |
|
|
static async flushCache() { |
|
|
static async flushCache() { |
|
|
return fs.emptyDir(path.join(process.cwd(), `data/cache`)) |
|
|
return fs.emptyDir(path.join(process.cwd(), `data/cache`)) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Migrate all pages from a source locale to the target locale |
|
|
|
|
|
* |
|
|
|
|
|
* @param {Object} opts Migration properties |
|
|
|
|
|
* @param {string} opts.sourceLocale Source Locale Code |
|
|
|
|
|
* @param {string} opts.targetLocale Target Locale Code |
|
|
|
|
|
* @returns {Promise} Promise with no value |
|
|
|
|
|
*/ |
|
|
static async migrateToLocale({ sourceLocale, targetLocale }) { |
|
|
static async migrateToLocale({ sourceLocale, targetLocale }) { |
|
|
return WIKI.models.pages.query() |
|
|
return WIKI.models.pages.query() |
|
|
.patch({ |
|
|
.patch({ |
|
@ -491,6 +656,12 @@ module.exports = class Page extends Model { |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Clean raw HTML from content for use in search engines |
|
|
|
|
|
* |
|
|
|
|
|
* @param {string} rawHTML Raw HTML |
|
|
|
|
|
* @returns {string} Cleaned Content Text |
|
|
|
|
|
*/ |
|
|
static cleanHTML(rawHTML = '') { |
|
|
static cleanHTML(rawHTML = '') { |
|
|
return striptags(rawHTML || '') |
|
|
return striptags(rawHTML || '') |
|
|
.replace(emojiRegex(), '') |
|
|
.replace(emojiRegex(), '') |
|
|