feat: branch off / create from template
This commit is contained in:
parent
e85de92715
commit
13a995133b
@ -171,7 +171,7 @@ export default {
|
|||||||
|
|
||||||
this.initContentParsed = this.initContent ? Base64.decode(this.initContent) : ''
|
this.initContentParsed = this.initContent ? Base64.decode(this.initContent) : ''
|
||||||
this.$store.set('editor/content', this.initContentParsed)
|
this.$store.set('editor/content', this.initContentParsed)
|
||||||
if (this.mode === 'create') {
|
if (this.mode === 'create' && !this.initEditor) {
|
||||||
_.delay(() => {
|
_.delay(() => {
|
||||||
this.dialogEditorSelector = true
|
this.dialogEditorSelector = true
|
||||||
}, 500)
|
}, 500)
|
||||||
|
@ -474,7 +474,7 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.$store.set('editor/editorKey', 'markdown')
|
this.$store.set('editor/editorKey', 'markdown')
|
||||||
|
|
||||||
if (this.mode === 'create') {
|
if (this.mode === 'create' && !this.$store.get('editor/content')) {
|
||||||
this.$store.set('editor/content', '# Header\nYour content here')
|
this.$store.set('editor/content', '# Header\nYour content here')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +122,8 @@
|
|||||||
v-btn(text, @click='isRestoreConfirmDialogShown = false', :disabled='restoreLoading') {{$t('common:actions.cancel')}}
|
v-btn(text, @click='isRestoreConfirmDialogShown = false', :disabled='restoreLoading') {{$t('common:actions.cancel')}}
|
||||||
v-btn(color='orange darken-2', dark, @click='restoreConfirm', :loading='restoreLoading') {{$t('history:restore.confirmButton')}}
|
v-btn(color='orange darken-2', dark, @click='restoreConfirm', :loading='restoreLoading') {{$t('history:restore.confirmButton')}}
|
||||||
|
|
||||||
|
page-selector(mode='create', v-model='branchOffOpts.modal', :open-handler='branchOffHandle', :path='branchOffOpts.path', :locale='branchOffOpts.locale')
|
||||||
|
|
||||||
nav-footer
|
nav-footer
|
||||||
notify
|
notify
|
||||||
search-results
|
search-results
|
||||||
@ -211,6 +213,12 @@ export default {
|
|||||||
versionId: 0,
|
versionId: 0,
|
||||||
versionDate: ''
|
versionDate: ''
|
||||||
},
|
},
|
||||||
|
branchOffOpts: {
|
||||||
|
versionId: 0,
|
||||||
|
locale: 'en',
|
||||||
|
path: 'new-page',
|
||||||
|
modal: false
|
||||||
|
},
|
||||||
isRestoreConfirmDialogShown: false,
|
isRestoreConfirmDialogShown: false,
|
||||||
restoreLoading: false
|
restoreLoading: false
|
||||||
}
|
}
|
||||||
@ -408,7 +416,16 @@ export default {
|
|||||||
this.restoreLoading = false
|
this.restoreLoading = false
|
||||||
},
|
},
|
||||||
branchOff (versionId) {
|
branchOff (versionId) {
|
||||||
|
const pathParts = this.path.split('/')
|
||||||
|
this.branchOffOpts = {
|
||||||
|
versionId: versionId,
|
||||||
|
locale: this.locale,
|
||||||
|
path: (pathParts.length > 1) ? _.initial(pathParts).join('/') + `/new-page` : `new-page`,
|
||||||
|
modal: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
branchOffHandle ({ locale, path }) {
|
||||||
|
window.location.assign(`/e/${locale}/${path}?from=${this.pageId},${this.branchOffOpts.versionId}`)
|
||||||
},
|
},
|
||||||
toggleViewMode () {
|
toggleViewMode () {
|
||||||
this.viewMode = (this.viewMode === 'line-by-line') ? 'side-by-side' : 'line-by-line'
|
this.viewMode = (this.viewMode === 'line-by-line') ? 'side-by-side' : 'line-by-line'
|
||||||
|
@ -5,6 +5,8 @@ const _ = require('lodash')
|
|||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
|
const tmplCreateRegex = /^[0-9]+(,[0-9]+)?$/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Robots.txt
|
* Robots.txt
|
||||||
*/
|
*/
|
||||||
@ -89,13 +91,16 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
|
|||||||
return res.redirect(`/e/${pageArgs.locale}/${pageArgs.path}`)
|
return res.redirect(`/e/${pageArgs.locale}/${pageArgs.path}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -> Set Editor Lang
|
||||||
_.set(res, 'locals.siteConfig.lang', pageArgs.locale)
|
_.set(res, 'locals.siteConfig.lang', pageArgs.locale)
|
||||||
_.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
|
_.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
|
||||||
|
|
||||||
|
// -> Check for reserved path
|
||||||
if (pageHelper.isReservedPath(pageArgs.path)) {
|
if (pageHelper.isReservedPath(pageArgs.path)) {
|
||||||
return next(new Error('Cannot create this page because it starts with a system reserved path.'))
|
return next(new Error('Cannot create this page because it starts with a system reserved path.'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -> Get page data from DB
|
||||||
let page = await WIKI.models.pages.getPageFromDb({
|
let page = await WIKI.models.pages.getPageFromDb({
|
||||||
path: pageArgs.path,
|
path: pageArgs.path,
|
||||||
locale: pageArgs.locale,
|
locale: pageArgs.locale,
|
||||||
@ -112,11 +117,13 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (page) {
|
if (page) {
|
||||||
|
// -> EDIT MODE
|
||||||
if (!WIKI.auth.checkAccess(req.user, ['write:pages', 'manage:pages'], pageArgs)) {
|
if (!WIKI.auth.checkAccess(req.user, ['write:pages', 'manage:pages'], pageArgs)) {
|
||||||
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
|
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
|
||||||
return res.render('unauthorized', { action: 'edit' })
|
return res.render('unauthorized', { action: 'edit' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -> Get page tags
|
||||||
await page.$relatedQuery('tags')
|
await page.$relatedQuery('tags')
|
||||||
page.tags = _.map(page.tags, 'tag')
|
page.tags = _.map(page.tags, 'tag')
|
||||||
|
|
||||||
@ -126,6 +133,7 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
|
|||||||
page.isPublished = (page.isPublished === true || page.isPublished === 1) ? 'true' : 'false'
|
page.isPublished = (page.isPublished === true || page.isPublished === 1) ? 'true' : 'false'
|
||||||
page.content = Buffer.from(page.content).toString('base64')
|
page.content = Buffer.from(page.content).toString('base64')
|
||||||
} else {
|
} else {
|
||||||
|
// -> CREATE MODE
|
||||||
if (!WIKI.auth.checkAccess(req.user, ['write:pages'], pageArgs)) {
|
if (!WIKI.auth.checkAccess(req.user, ['write:pages'], pageArgs)) {
|
||||||
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
|
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
|
||||||
return res.render('unauthorized', { action: 'create' })
|
return res.render('unauthorized', { action: 'create' })
|
||||||
@ -137,7 +145,54 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
|
|||||||
localeCode: pageArgs.locale,
|
localeCode: pageArgs.locale,
|
||||||
editorKey: null,
|
editorKey: null,
|
||||||
mode: 'create',
|
mode: 'create',
|
||||||
content: null
|
content: null,
|
||||||
|
title: null,
|
||||||
|
description: null
|
||||||
|
}
|
||||||
|
|
||||||
|
// -> From Template
|
||||||
|
if (req.query.from && tmplCreateRegex.test(req.query.from)) {
|
||||||
|
let tmplPageId = 0
|
||||||
|
let tmplVersionId = 0
|
||||||
|
if (req.query.from.indexOf(',')) {
|
||||||
|
const q = req.query.from.split(',')
|
||||||
|
tmplPageId = _.toSafeInteger(q[0])
|
||||||
|
tmplVersionId = _.toSafeInteger(q[1])
|
||||||
|
} else {
|
||||||
|
tmplPageId = _.toSafeInteger(req.query.from)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmplVersionId > 0) {
|
||||||
|
// -> From Page Version
|
||||||
|
const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: tmplPageId, versionId: tmplVersionId })
|
||||||
|
if (!pageVersion) {
|
||||||
|
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
|
||||||
|
return res.status(404).render('notfound', { action: 'template' })
|
||||||
|
}
|
||||||
|
if (!WIKI.auth.checkAccess(req.user, ['read:history'], { path: pageVersion.path, locale: pageVersion.locale })) {
|
||||||
|
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
|
||||||
|
return res.render('unauthorized', { action: 'sourceVersion' })
|
||||||
|
}
|
||||||
|
page.content = Buffer.from(pageVersion.content).toString('base64')
|
||||||
|
page.editorKey = pageVersion.editor
|
||||||
|
page.title = pageVersion.title
|
||||||
|
page.description = pageVersion.description
|
||||||
|
} else {
|
||||||
|
// -> From Page Live
|
||||||
|
const pageOriginal = await WIKI.models.pages.query().findById(tmplPageId)
|
||||||
|
if (!pageOriginal) {
|
||||||
|
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
|
||||||
|
return res.status(404).render('notfound', { action: 'template' })
|
||||||
|
}
|
||||||
|
if (!WIKI.auth.checkAccess(req.user, ['read:source'], { path: pageOriginal.path, locale: pageOriginal.locale })) {
|
||||||
|
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
|
||||||
|
return res.render('unauthorized', { action: 'source' })
|
||||||
|
}
|
||||||
|
page.content = Buffer.from(pageOriginal.content).toString('base64')
|
||||||
|
page.editorKey = pageOriginal.editorKey
|
||||||
|
page.title = pageOriginal.title
|
||||||
|
page.description = pageOriginal.description
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.render('editor', { page, injectCode })
|
res.render('editor', { page, injectCode })
|
||||||
@ -163,6 +218,11 @@ router.get(['/h', '/h/*'], async (req, res, next) => {
|
|||||||
isPrivate: false
|
isPrivate: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!page) {
|
||||||
|
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
|
||||||
|
return res.status(404).render('notfound', { action: 'history' })
|
||||||
|
}
|
||||||
|
|
||||||
pageArgs.tags = _.get(page, 'tags', [])
|
pageArgs.tags = _.get(page, 'tags', [])
|
||||||
|
|
||||||
if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
|
if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
|
||||||
|
@ -15,20 +15,36 @@ module.exports = {
|
|||||||
* PAGE HISTORY
|
* PAGE HISTORY
|
||||||
*/
|
*/
|
||||||
async history(obj, args, context, info) {
|
async history(obj, args, context, info) {
|
||||||
return WIKI.models.pageHistory.getHistory({
|
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.id)
|
||||||
pageId: args.id,
|
if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
|
||||||
offsetPage: args.offsetPage || 0,
|
path: page.path,
|
||||||
offsetSize: args.offsetSize || 100
|
locale: page.localeCode
|
||||||
})
|
})) {
|
||||||
|
return WIKI.models.pageHistory.getHistory({
|
||||||
|
pageId: args.id,
|
||||||
|
offsetPage: args.offsetPage || 0,
|
||||||
|
offsetSize: args.offsetSize || 100
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new WIKI.Error.PageHistoryForbidden()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* PAGE VERSION
|
* PAGE VERSION
|
||||||
*/
|
*/
|
||||||
async version(obj, args, context, info) {
|
async version(obj, args, context, info) {
|
||||||
return WIKI.models.pageHistory.getVersion({
|
const page = await WIKI.models.pages.query().select('path', 'localeCode').findById(args.pageId)
|
||||||
pageId: args.pageId,
|
if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
|
||||||
versionId: args.versionId
|
path: page.path,
|
||||||
})
|
locale: page.localeCode
|
||||||
|
})) {
|
||||||
|
return WIKI.models.pageHistory.getVersion({
|
||||||
|
pageId: args.pageId,
|
||||||
|
versionId: args.versionId
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw new WIKI.Error.PageHistoryForbidden()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* SEARCH PAGES
|
* SEARCH PAGES
|
||||||
@ -123,10 +139,17 @@ module.exports = {
|
|||||||
async single (obj, args, context, info) {
|
async single (obj, args, context, info) {
|
||||||
let page = await WIKI.models.pages.getPageFromDb(args.id)
|
let page = await WIKI.models.pages.getPageFromDb(args.id)
|
||||||
if (page) {
|
if (page) {
|
||||||
return {
|
if (WIKI.auth.checkAccess(context.req.user, ['read:history'], {
|
||||||
...page,
|
path: page.path,
|
||||||
locale: page.localeCode,
|
locale: page.localeCode
|
||||||
editor: page.editorKey
|
})) {
|
||||||
|
return {
|
||||||
|
...page,
|
||||||
|
locale: page.localeCode,
|
||||||
|
editor: page.editorKey
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new WIKI.Error.PageViewForbidden()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new WIKI.Error.PageNotFound()
|
throw new WIKI.Error.PageNotFound()
|
||||||
|
@ -137,6 +137,10 @@ module.exports = {
|
|||||||
message: 'Page content cannot be empty.',
|
message: 'Page content cannot be empty.',
|
||||||
code: 6004
|
code: 6004
|
||||||
}),
|
}),
|
||||||
|
PageHistoryForbidden: CustomError('PageHistoryForbidden', {
|
||||||
|
message: 'You are not authorized to view the history of this page.',
|
||||||
|
code: 6012
|
||||||
|
}),
|
||||||
PageIllegalPath: CustomError('PageIllegalPath', {
|
PageIllegalPath: CustomError('PageIllegalPath', {
|
||||||
message: 'Page path cannot contains illegal characters.',
|
message: 'Page path cannot contains illegal characters.',
|
||||||
code: 6005
|
code: 6005
|
||||||
@ -161,6 +165,10 @@ module.exports = {
|
|||||||
message: 'You are not authorized to update this page.',
|
message: 'You are not authorized to update this page.',
|
||||||
code: 6009
|
code: 6009
|
||||||
}),
|
}),
|
||||||
|
PageViewForbidden: CustomError('PageViewForbidden', {
|
||||||
|
message: 'You are not authorized to view this page.',
|
||||||
|
code: 6013
|
||||||
|
}),
|
||||||
SearchActivationFailed: CustomError('SearchActivationFailed', {
|
SearchActivationFailed: CustomError('SearchActivationFailed', {
|
||||||
message: 'Search Engine activation failed.',
|
message: 'Search Engine activation failed.',
|
||||||
code: 4002
|
code: 4002
|
||||||
|
@ -84,6 +84,9 @@ module.exports = class PageHistory extends Model {
|
|||||||
this.createdAt = new Date().toISOString()
|
this.createdAt = new Date().toISOString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Page Version
|
||||||
|
*/
|
||||||
static async addVersion(opts) {
|
static async addVersion(opts) {
|
||||||
await WIKI.models.pageHistory.query().insert({
|
await WIKI.models.pageHistory.query().insert({
|
||||||
pageId: opts.id,
|
pageId: opts.id,
|
||||||
@ -105,6 +108,9 @@ module.exports = class PageHistory extends Model {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Page Version
|
||||||
|
*/
|
||||||
static async getVersion({ pageId, versionId }) {
|
static async getVersion({ pageId, versionId }) {
|
||||||
const version = await WIKI.models.pageHistory.query()
|
const version = await WIKI.models.pageHistory.query()
|
||||||
.column([
|
.column([
|
||||||
@ -134,13 +140,20 @@ module.exports = class PageHistory extends Model {
|
|||||||
'pageHistory.id': versionId,
|
'pageHistory.id': versionId,
|
||||||
'pageHistory.pageId': pageId
|
'pageHistory.pageId': pageId
|
||||||
}).first()
|
}).first()
|
||||||
return {
|
if (version) {
|
||||||
...version,
|
return {
|
||||||
updatedAt: version.createdAt,
|
...version,
|
||||||
tags: []
|
updatedAt: version.createdAt || null,
|
||||||
|
tags: []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get History Trail of a Page
|
||||||
|
*/
|
||||||
static async getHistory({ pageId, offsetPage = 0, offsetSize = 100 }) {
|
static async getHistory({ pageId, offsetPage = 0, offsetSize = 100 }) {
|
||||||
const history = await WIKI.models.pageHistory.query()
|
const history = await WIKI.models.pageHistory.query()
|
||||||
.column([
|
.column([
|
||||||
|
Loading…
Reference in New Issue
Block a user