feat: page history - download version

This commit is contained in:
NGPixel 2020-02-24 21:10:43 -05:00 committed by Nicolas Giard
parent 95f01cdeb8
commit 2ac9131244
4 changed files with 89 additions and 27 deletions

View File

@ -4,10 +4,11 @@
v-content v-content
v-toolbar(color='primary', dark) v-toolbar(color='primary', dark)
.subheading Viewing history of #[strong /{{path}}] .subheading Viewing history of #[strong /{{path}}]
v-spacer template(v-if='$vuetify.breakpoint.mdAndUp')
.caption.blue--text.text--lighten-3.mr-4 Trail Length: {{total}} v-spacer
.caption.blue--text.text--lighten-3 ID: {{pageId}} .caption.blue--text.text--lighten-3.mr-4 Trail Length: {{total}}
v-btn.ml-4(depressed, color='blue darken-1', @click='goLive') Return to Live Version .caption.blue--text.text--lighten-3 ID: {{pageId}}
v-btn.ml-4(depressed, color='blue darken-1', @click='goLive') Return to Live Version
v-container(fluid, grid-list-xl) v-container(fluid, grid-list-xl)
v-layout(row, wrap) v-layout(row, wrap)
v-flex(xs12, md4) v-flex(xs12, md4)
@ -42,22 +43,22 @@
template(v-slot:activator='{ on }') template(v-slot:activator='{ on }')
v-btn.mr-2.radius-4(icon, v-on='on', small, tile): v-icon mdi-dots-horizontal v-btn.mr-2.radius-4(icon, v-on='on', small, tile): v-icon mdi-dots-horizontal
v-list(dense, nav).history-promptmenu v-list(dense, nav).history-promptmenu
v-list-item(@click='setDiffSource(ph.versionId)') v-list-item(@click='setDiffSource(ph.versionId)', :disabled='ph.versionId >= diffTarget')
v-list-item-avatar(size='24'): v-icon mdi-call-made v-list-item-avatar(size='24'): v-avatar A
v-list-item-title Set as Differencing Source (A) v-list-item-title Set as Differencing Source
v-list-item(@click='setDiffTarget(ph.versionId)') v-list-item(@click='setDiffTarget(ph.versionId)', :disabled='ph.versionId <= diffSource')
v-list-item-avatar(size='24'): v-icon mdi-call-received v-list-item-avatar(size='24'): v-avatar B
v-list-item-title Set as Differencing Target (B) v-list-item-title Set as Differencing Target
v-list-item v-list-item(@click='viewSource(ph.versionId)')
v-list-item-avatar(size='24'): v-icon mdi-code-tags v-list-item-avatar(size='24'): v-icon mdi-code-tags
v-list-item-title View Source v-list-item-title View Source
v-list-item v-list-item(@click='download(ph.versionId)')
v-list-item-avatar(size='24'): v-icon mdi-cloud-download-outline v-list-item-avatar(size='24'): v-icon mdi-cloud-download-outline
v-list-item-title Download Version v-list-item-title Download Version
v-list-item v-list-item(@click='restore(ph.versionId)')
v-list-item-avatar(size='24'): v-icon mdi-history v-list-item-avatar(size='24'): v-icon mdi-history
v-list-item-title Restore v-list-item-title Restore
v-list-item v-list-item(@click='branchOff(ph.versionId)')
v-list-item-avatar(size='24'): v-icon mdi-source-branch v-list-item-avatar(size='24'): v-icon mdi-source-branch
v-list-item-title Branch off from here v-list-item-title Branch off from here
v-btn.mr-2.radius-4( v-btn.mr-2.radius-4(
@ -66,7 +67,8 @@
small small
depressed depressed
tile tile
:class='diffSource === ph.versionId ? `pink white--text` : `grey lighten-2`' :class='diffSource === ph.versionId ? `pink white--text` : ($vuetify.theme.dark ? `grey darken-2` : `grey lighten-2`)'
:disabled='ph.versionId >= diffTarget'
): strong A ): strong A
v-btn.mr-0.radius-4( v-btn.mr-0.radius-4(
@click='setDiffTarget(ph.versionId)' @click='setDiffTarget(ph.versionId)'
@ -74,13 +76,14 @@
small small
depressed depressed
tile tile
:class='diffTarget === ph.versionId ? `pink white--text` : `grey lighten-2`' :class='diffTarget === ph.versionId ? `pink white--text` : ($vuetify.theme.dark ? `grey darken-2` : `grey lighten-2`)'
:disabled='ph.versionId <= diffSource'
): strong B ): strong B
v-btn.ma-0.radius-7( v-btn.ma-0.radius-7(
v-if='total > trail.length' v-if='total > trail.length'
block block
color='grey darken-2' color='primary'
@click='loadMore' @click='loadMore'
) )
.caption.white--text Load More... .caption.white--text Load More...
@ -94,16 +97,16 @@
) End of history trail ) End of history trail
v-flex(xs12, md8) v-flex(xs12, md8)
v-card.radius-7.mt-8 v-card.radius-7(:class='$vuetify.breakpoint.mdAndUp ? `mt-8` : ``')
v-card-text v-card-text
v-card.grey.radius-7(flat, :class='darkMode ? `darken-2` : `lighten-4`') v-card.grey.radius-7(flat, :class='darkMode ? `darken-2` : `lighten-4`')
v-row(no-gutters, align='center') v-row(no-gutters, align='center')
v-col(cols='11') v-col
v-card-text v-card-text
.subheading {{target.title}} .subheading {{target.title}}
.caption {{target.description}} .caption {{target.description}}
v-col.text-right.py-3 v-col.text-right.py-3(cols='2', v-if='$vuetify.breakpoint.mdAndUp')
v-btn.mr-3(color='primary', small, dark, outlined, @click='toggleViewMode') v-btn.mr-3(:color='$vuetify.theme.dark ? `white` : `grey darken-3`', small, dark, outlined, @click='toggleViewMode')
v-icon(left) mdi-eye v-icon(left) mdi-eye
.overline View Mode .overline View Mode
v-card.mt-3(light, v-html='diffHTML', flat) v-card.mt-3(light, v-html='diffHTML', flat)
@ -256,6 +259,18 @@ export default {
} else { } else {
return { content: '' } return { content: '' }
} }
},
viewSource (versionId) {
window.location.assign(`/s/${this.locale}/${this.path}?v=${versionId}`)
},
download (versionId) {
window.location.assign(`/d/${this.locale}/${this.path}?v=${versionId}`)
},
restore (versionId) {
},
branchOff (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'
@ -275,7 +290,7 @@ export default {
variables: { variables: {
id: this.pageId, id: this.pageId,
offsetPage: this.offsetPage, offsetPage: this.offsetPage,
offsetSize: 25 offsetSize: this.$vuetify.breakpoint.mdAndUp ? 25 : 5
}, },
updateQuery: (previousResult, { fetchMoreResult }) => { updateQuery: (previousResult, { fetchMoreResult }) => {
return { return {
@ -350,7 +365,7 @@ export default {
return { return {
id: this.pageId, id: this.pageId,
offsetPage: 0, offsetPage: 0,
offsetSize: 25 offsetSize: this.$vuetify.breakpoint.mdAndUp ? 25 : 5
} }
}, },
manual: true, manual: true,

View File

@ -36,6 +36,49 @@ router.get(['/a', '/a/*'], (req, res, next) => {
res.render('admin') res.render('admin')
}) })
/**
* Download Page / Version
*/
router.get(['/d', '/d/*'], async (req, res, next) => {
const pageArgs = pageHelper.parsePath(req.path, { stripExt: true })
const versionId = (req.query.v) ? _.toSafeInteger(req.query.v) : 0
const page = await WIKI.models.pages.getPageFromDb({
path: pageArgs.path,
locale: pageArgs.locale,
userId: req.user.id,
isPrivate: false
})
pageArgs.tags = _.get(page, 'tags', [])
if (versionId > 0) {
if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.render('unauthorized', { action: 'downloadVersion' })
}
} else {
if (!WIKI.auth.checkAccess(req.user, ['read:source'], pageArgs)) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.render('unauthorized', { action: 'download' })
}
}
if (page) {
const fileName = _.last(page.path.split('/')) + '.' + pageHelper.getFileExtension(page.contentType)
res.attachment(fileName)
if (versionId > 0) {
const pageVersion = await WIKI.models.pageHistory.getVersion({ pageId: page.id, versionId })
res.send(pageHelper.injectPageMetadata(pageVersion))
} else {
res.send(pageHelper.injectPageMetadata(page))
}
} else {
res.status(404).end()
}
})
/** /**
* Create/Edit document * Create/Edit document
*/ */

View File

@ -73,11 +73,14 @@ module.exports = {
['date', page.updatedAt], ['date', page.updatedAt],
['tags', page.tags ? page.tags.map(t => t.tag).join(', ') : ''] ['tags', page.tags ? page.tags.map(t => t.tag).join(', ') : '']
] ]
const inject = { switch (page.contentType) {
'markdown': '---\n' + meta.map(mt => `${mt[0]}: ${mt[1]}`).join('\n') + '\n---\n\n' + page.content, case 'markdown':
'html': '<!--\n' + meta.map(mt => `${mt[0]}: ${mt[1]}`).join('\n') + '\n-->\n\n' + page.content return '---\n' + meta.map(mt => `${mt[0]}: ${mt[1]}`).join('\n') + '\n---\n\n' + page.content
case 'html':
return '<!--\n' + meta.map(mt => `${mt[0]}: ${mt[1]}`).join('\n') + '\n-->\n\n' + page.content
default:
return page.content
} }
return _.get(inject, page.contentType, page.content)
}, },
/** /**
* Check if path is a reserved path * Check if path is a reserved path

View File

@ -134,6 +134,7 @@ module.exports = class PageHistory extends Model {
}).first() }).first()
return { return {
...version, ...version,
updatedAt: version.createdAt,
tags: [] tags: []
} }
} }