feat: source + history (wip)
This commit is contained in:
parent
076aeaf749
commit
78ba895eee
@ -158,6 +158,7 @@ Vue.prototype.Velocity = Velocity
|
|||||||
Vue.component('admin', () => import(/* webpackChunkName: "admin" */ './components/admin.vue'))
|
Vue.component('admin', () => import(/* webpackChunkName: "admin" */ './components/admin.vue'))
|
||||||
Vue.component('editor', () => import(/* webpackPrefetch: -100, webpackChunkName: "editor" */ './components/editor.vue'))
|
Vue.component('editor', () => import(/* webpackPrefetch: -100, webpackChunkName: "editor" */ './components/editor.vue'))
|
||||||
Vue.component('history', () => import(/* webpackChunkName: "history" */ './components/history.vue'))
|
Vue.component('history', () => import(/* webpackChunkName: "history" */ './components/history.vue'))
|
||||||
|
Vue.component('page-source', () => import(/* webpackChunkName: "source" */ './components/source.vue'))
|
||||||
Vue.component('login', () => import(/* webpackPrefetch: true, webpackChunkName: "login" */ './components/login.vue'))
|
Vue.component('login', () => import(/* webpackPrefetch: true, webpackChunkName: "login" */ './components/login.vue'))
|
||||||
Vue.component('nav-header', () => import(/* webpackMode: "eager" */ './components/common/nav-header.vue'))
|
Vue.component('nav-header', () => import(/* webpackMode: "eager" */ './components/common/nav-header.vue'))
|
||||||
Vue.component('page-selector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue'))
|
Vue.component('page-selector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue'))
|
||||||
|
@ -31,17 +31,7 @@
|
|||||||
v-flex(xs8)
|
v-flex(xs8)
|
||||||
v-toolbar(color='grey darken-2', dark, dense, flat)
|
v-toolbar(color='grey darken-2', dark, dense, flat)
|
||||||
.body-2 Pages
|
.body-2 Pages
|
||||||
v-divider.ml-4(vertical)
|
v-spacer
|
||||||
v-text-field(
|
|
||||||
prepend-inner-icon='search'
|
|
||||||
label='Search...'
|
|
||||||
hide-details
|
|
||||||
solo
|
|
||||||
background-color='grey darken-2'
|
|
||||||
flat
|
|
||||||
clearable
|
|
||||||
)
|
|
||||||
v-divider.mx-3(vertical)
|
|
||||||
v-btn(icon): v-icon forward
|
v-btn(icon): v-icon forward
|
||||||
v-btn(icon): v-icon delete
|
v-btn(icon): v-icon delete
|
||||||
v-list(dense)
|
v-list(dense)
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
v-toolbar(color='primary', dark)
|
v-toolbar(color='primary', dark)
|
||||||
.subheading Viewing history of page #[strong /{{path}}]
|
.subheading Viewing history of page #[strong /{{path}}]
|
||||||
v-spacer
|
v-spacer
|
||||||
.caption.blue--text.text--lighten-3 ID {{id}}
|
.caption.blue--text.text--lighten-3 ID {{pageId}}
|
||||||
v-btn.ml-4(depressed, color='blue darken-1', @click='goLive') Return to Live Version
|
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(xs5)
|
v-flex(xs4)
|
||||||
v-chip.ma-0.grey--text.text--darken-2(
|
v-chip.ma-0.grey--text.text--darken-2(
|
||||||
label
|
label
|
||||||
small
|
small
|
||||||
@ -20,86 +20,92 @@
|
|||||||
dense
|
dense
|
||||||
)
|
)
|
||||||
v-timeline-item(
|
v-timeline-item(
|
||||||
|
v-for='ph in trail'
|
||||||
|
:key='ph.versionId'
|
||||||
|
:small='ph.actionType === `edit`'
|
||||||
fill-dot
|
fill-dot
|
||||||
color='primary'
|
:color='trailColor(ph.actionType)'
|
||||||
icon='edit'
|
:icon='trailIcon(ph.actionType)'
|
||||||
)
|
)
|
||||||
v-card.grey.lighten-3.radius-7(flat)
|
v-card.radius-7(flat, :class='trailBgColor(ph.actionType)')
|
||||||
v-card-text
|
v-toolbar(flat, :color='trailBgColor(ph.actionType)')
|
||||||
v-layout(justify-space-between)
|
v-chip.ml-0.mr-3(
|
||||||
v-flex(xs7)
|
v-if='diffSource === ph.versionId'
|
||||||
v-chip.ml-0.mr-3(
|
label
|
||||||
label
|
small
|
||||||
small
|
color='pink'
|
||||||
color='primary'
|
)
|
||||||
)
|
.caption.white--text Source
|
||||||
span.white--text Viewing
|
v-chip.ml-0.mr-3(
|
||||||
span Edited by John Doe
|
v-if='diffTarget === ph.versionId'
|
||||||
v-flex(xs5, text-xs-right, align-center, d-flex)
|
label
|
||||||
.caption Today at 12:34 PM
|
small
|
||||||
|
color='pink'
|
||||||
|
)
|
||||||
|
.caption.white--text Target
|
||||||
|
.caption(v-if='ph.actionType === `edit`') Edited by {{ ph.authorName }}
|
||||||
|
.caption(v-else-if='ph.actionType === `move`') Moved from #[strong {{ph.valueBefore}}] to #[strong {{ph.valueAfter}}] by {{ ph.authorName }}
|
||||||
|
.caption(v-else-if='ph.actionType === `initial`') Created by {{ ph.authorName }}
|
||||||
|
.caption(v-else) Unknown Action by {{ ph.authorName }}
|
||||||
|
v-spacer
|
||||||
|
.caption {{ ph.createdAt | moment('calendar') }}
|
||||||
|
v-menu(offset-x, left)
|
||||||
|
v-btn(icon, slot='activator'): v-icon more_horiz
|
||||||
|
v-list(dense).history-promptmenu
|
||||||
|
v-list-tile(@click='setDiffTarget(ph.versionId)')
|
||||||
|
v-list-tile-avatar: v-icon call_made
|
||||||
|
v-list-tile-title Set as Differencing Target
|
||||||
|
v-divider
|
||||||
|
v-list-tile(@click='setDiffSource(ph.versionId)')
|
||||||
|
v-list-tile-avatar: v-icon call_received
|
||||||
|
v-list-tile-title Set as Differencing Source
|
||||||
|
v-divider
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon code
|
||||||
|
v-list-tile-title View Source
|
||||||
|
v-divider
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon cloud_download
|
||||||
|
v-list-tile-title Download Version
|
||||||
|
v-divider
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon restore
|
||||||
|
v-list-tile-title Restore
|
||||||
|
v-divider
|
||||||
|
v-list-tile
|
||||||
|
v-list-tile-avatar: v-icon call_split
|
||||||
|
v-list-tile-title Branch off from here
|
||||||
|
|
||||||
v-timeline-item(
|
|
||||||
fill-dot
|
|
||||||
small
|
|
||||||
color='primary'
|
|
||||||
icon='edit'
|
|
||||||
)
|
|
||||||
v-card.grey.lighten-3.radius-7(flat)
|
|
||||||
v-card-text
|
|
||||||
v-layout(justify-space-between)
|
|
||||||
v-flex(xs7)
|
|
||||||
span Edited by Jane Doe
|
|
||||||
v-flex(xs5, text-xs-right, align-center, d-flex)
|
|
||||||
.caption Today at 12:27 PM
|
|
||||||
|
|
||||||
v-timeline-item(
|
|
||||||
fill-dot
|
|
||||||
small
|
|
||||||
color='purple'
|
|
||||||
icon='forward'
|
|
||||||
)
|
|
||||||
v-card.purple.lighten-5.radius-7(flat)
|
|
||||||
v-card-text
|
|
||||||
v-layout(justify-space-between)
|
|
||||||
v-flex(xs7)
|
|
||||||
span Moved page from #[strong /test] to #[strong /home] by John Doe
|
|
||||||
v-flex(xs5, text-xs-right, align-center, d-flex)
|
|
||||||
.caption Yesterday at 10:45 AM
|
|
||||||
|
|
||||||
v-timeline-item(
|
|
||||||
fill-dot
|
|
||||||
color='teal'
|
|
||||||
icon='add'
|
|
||||||
)
|
|
||||||
v-card.teal.lighten-5.radius-7(flat)
|
|
||||||
v-card-text
|
|
||||||
v-layout(justify-space-between)
|
|
||||||
v-flex(xs7): span Initial page creation by John Doe
|
|
||||||
v-flex(xs5, text-xs-right, align-center, d-flex)
|
|
||||||
.caption Last Tuesday at 7:56 PM
|
|
||||||
v-chip.ma-0.grey--text.text--darken-2(
|
v-chip.ma-0.grey--text.text--darken-2(
|
||||||
label
|
label
|
||||||
small
|
small
|
||||||
color='grey lighten-2'
|
color='grey lighten-2'
|
||||||
) End of history
|
) End of history trail
|
||||||
|
|
||||||
v-flex(xs7)
|
v-flex(xs8)
|
||||||
v-card.radius-7
|
v-card.radius-7
|
||||||
v-card-text
|
v-card-text
|
||||||
v-card.grey.lighten-4.radius-7(flat)
|
v-card.grey.lighten-4.radius-7(flat)
|
||||||
v-card-text
|
v-card-text
|
||||||
.subheading Page Title
|
.subheading Page Title
|
||||||
.caption Some page description
|
.caption Some page description
|
||||||
|
.mt-3(v-html='diffHTML')
|
||||||
|
|
||||||
nav-footer
|
nav-footer
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { Diff2Html } from 'diff2html'
|
||||||
|
import { createPatch } from 'diff'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
import historyTrailQuery from 'gql/history/history-trail-query.gql'
|
||||||
|
|
||||||
/* global siteConfig */
|
/* global siteConfig */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
id: {
|
pageId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
@ -110,13 +116,43 @@ export default {
|
|||||||
path: {
|
path: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'home'
|
default: 'home'
|
||||||
|
},
|
||||||
|
liveContent: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {
|
||||||
|
sourceText: '',
|
||||||
|
targetText: '',
|
||||||
|
trail: [],
|
||||||
|
diffSource: 0,
|
||||||
|
diffTarget: 0,
|
||||||
|
offset: 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
darkMode() { return siteConfig.darkMode }
|
darkMode() { return siteConfig.darkMode },
|
||||||
|
diffs() {
|
||||||
|
return createPatch(`/${this.path}`, this.sourceText, this.targetText)
|
||||||
|
},
|
||||||
|
diffHTML() {
|
||||||
|
return Diff2Html.getPrettyHtml(this.diffs, {
|
||||||
|
inputFormat: 'diff',
|
||||||
|
showFiles: false,
|
||||||
|
matching: 'lines',
|
||||||
|
outputFormat: 'line-by-line'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
trail(newValue, oldValue) {
|
||||||
|
if (newValue && newValue.length > 0) {
|
||||||
|
this.diffTarget = _.get(_.head(newValue), 'versionId', 0)
|
||||||
|
this.diffSource = _.get(_.nth(newValue, 1), 'versionId', 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.$store.commit('page/SET_ID', this.id)
|
this.$store.commit('page/SET_ID', this.id)
|
||||||
@ -124,10 +160,67 @@ export default {
|
|||||||
this.$store.commit('page/SET_PATH', this.path)
|
this.$store.commit('page/SET_PATH', this.path)
|
||||||
|
|
||||||
this.$store.commit('page/SET_MODE', 'history')
|
this.$store.commit('page/SET_MODE', 'history')
|
||||||
|
|
||||||
|
this.targetText = this.liveContent
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
goLive() {
|
goLive() {
|
||||||
window.location.assign(`/${this.path}`)
|
window.location.assign(`/${this.path}`)
|
||||||
|
},
|
||||||
|
setDiffSource(versionId) {
|
||||||
|
this.diffSource = versionId
|
||||||
|
},
|
||||||
|
setDiffTarget(versionId) {
|
||||||
|
this.diffTarget = versionId
|
||||||
|
},
|
||||||
|
trailColor(actionType) {
|
||||||
|
switch (actionType) {
|
||||||
|
case 'edit':
|
||||||
|
return 'primary'
|
||||||
|
case 'move':
|
||||||
|
return 'purple'
|
||||||
|
case 'initial':
|
||||||
|
return 'teal'
|
||||||
|
default:
|
||||||
|
return 'grey'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trailIcon(actionType) {
|
||||||
|
switch (actionType) {
|
||||||
|
case 'edit':
|
||||||
|
return 'edit'
|
||||||
|
case 'move':
|
||||||
|
return 'forward'
|
||||||
|
case 'initial':
|
||||||
|
return 'add'
|
||||||
|
default:
|
||||||
|
return 'warning'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trailBgColor(actionType) {
|
||||||
|
switch (actionType) {
|
||||||
|
case 'move':
|
||||||
|
return 'purple lighten-5'
|
||||||
|
case 'initial':
|
||||||
|
return 'teal lighten-5'
|
||||||
|
default:
|
||||||
|
return 'grey lighten-3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apollo: {
|
||||||
|
trail: {
|
||||||
|
query: historyTrailQuery,
|
||||||
|
variables() {
|
||||||
|
return {
|
||||||
|
id: this.pageId,
|
||||||
|
offset: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
update: (data) => data.pages.history,
|
||||||
|
watchLoading (isLoading) {
|
||||||
|
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'history-trail-refresh')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,4 +228,10 @@ export default {
|
|||||||
|
|
||||||
<style lang='scss'>
|
<style lang='scss'>
|
||||||
|
|
||||||
|
.history {
|
||||||
|
&-promptmenu {
|
||||||
|
border-top: 5px solid mc('blue', '700');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
76
client/components/source.vue
Normal file
76
client/components/source.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template lang='pug'>
|
||||||
|
v-app(:dark='darkMode').source
|
||||||
|
nav-header
|
||||||
|
v-content
|
||||||
|
v-toolbar(color='primary', dark)
|
||||||
|
.subheading Viewing source of page #[strong /{{path}}]
|
||||||
|
v-spacer
|
||||||
|
.caption.blue--text.text--lighten-3 ID {{pageId}}
|
||||||
|
v-btn.ml-4(depressed, color='blue darken-1', @click='goLive') Return to Normal View
|
||||||
|
v-card(tile)
|
||||||
|
v-card-text
|
||||||
|
v-card.grey.lighten-4.radius-7(flat)
|
||||||
|
v-card-text
|
||||||
|
pre
|
||||||
|
code
|
||||||
|
slot
|
||||||
|
|
||||||
|
nav-footer
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* global siteConfig */
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
pageId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
locale: {
|
||||||
|
type: String,
|
||||||
|
default: 'en'
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
default: 'home'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
darkMode() { return siteConfig.darkMode }
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.$store.commit('page/SET_ID', this.id)
|
||||||
|
this.$store.commit('page/SET_LOCALE', this.locale)
|
||||||
|
this.$store.commit('page/SET_PATH', this.path)
|
||||||
|
|
||||||
|
this.$store.commit('page/SET_MODE', 'history')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
goLive() {
|
||||||
|
window.location.assign(`/${this.path}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss'>
|
||||||
|
|
||||||
|
.source {
|
||||||
|
pre > code {
|
||||||
|
box-shadow: none;
|
||||||
|
color: mc('grey', '800');
|
||||||
|
font-family: 'Source Code Pro', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 1rem;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
13
client/graph/history/history-trail-query.gql
Normal file
13
client/graph/history/history-trail-query.gql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
query($id: Int!, $offset: Int) {
|
||||||
|
pages {
|
||||||
|
history(id:$id, offset:$offset) {
|
||||||
|
versionId
|
||||||
|
authorId
|
||||||
|
authorName
|
||||||
|
actionType
|
||||||
|
valueBefore
|
||||||
|
valueAfter
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
@import "../libs/animate/animate";
|
@import "../libs/animate/animate";
|
||||||
@import '~vue2-animate/src/sass/vue2-animate';
|
@import '~vue2-animate/src/sass/vue2-animate';
|
||||||
|
@import '~diff2html/dist/diff2html.min.css';
|
||||||
|
|
||||||
@import 'components/v-btn';
|
@import 'components/v-btn';
|
||||||
@import 'components/v-data-table';
|
@import 'components/v-data-table';
|
||||||
|
@ -20,7 +20,7 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@for $i from 1 through 25 {
|
@for $i from 0 through 25 {
|
||||||
.radius-#{$i} {
|
.radius-#{$i} {
|
||||||
border-radius: #{$i}px;
|
border-radius: #{$i}px;
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,13 @@
|
|||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem;
|
||||||
|
height: 1px;
|
||||||
|
border: none;
|
||||||
|
background-color: mc('grey', '400');
|
||||||
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
padding: 0 0 1rem 0;
|
padding: 0 0 1rem 0;
|
||||||
border: 1px solid mc('blue', '500');
|
border: 1px solid mc('blue', '500');
|
||||||
@ -204,17 +211,18 @@
|
|||||||
|
|
||||||
.task-list-item {
|
.task-list-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
list-style-type: none;
|
||||||
|
|
||||||
&-checkbox[disabled] {
|
&-checkbox[disabled] {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
& + label {
|
& + label {
|
||||||
padding-left: 1.4rem;
|
padding-left: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
& + label::before {
|
& + label::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 1rem;
|
left: 0;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
content: ' ';
|
content: ' ';
|
||||||
display: block;
|
display: block;
|
||||||
@ -233,6 +241,10 @@
|
|||||||
content: '✓';
|
content: '✓';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contains-task-list {
|
||||||
|
padding: .5rem 0 0 1.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"dependency-graph": "0.7.2",
|
"dependency-graph": "0.7.2",
|
||||||
|
"diff": "3.5.0",
|
||||||
"diff2html": "2.5.0",
|
"diff2html": "2.5.0",
|
||||||
"dotize": "^0.2.0",
|
"dotize": "^0.2.0",
|
||||||
"execa": "1.0.0",
|
"execa": "1.0.0",
|
||||||
|
@ -46,11 +46,11 @@ router.get(['/p', '/p/*'], (req, res, next) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View document
|
* History
|
||||||
*/
|
*/
|
||||||
router.get(['/h', '/h/*'], async (req, res, next) => {
|
router.get(['/h', '/h/*'], async (req, res, next) => {
|
||||||
const pageArgs = pageHelper.parsePath(req.path)
|
const pageArgs = pageHelper.parsePath(req.path)
|
||||||
const page = await WIKI.models.pages.getPage({
|
const page = await WIKI.models.pages.getPageFromDb({
|
||||||
path: pageArgs.path,
|
path: pageArgs.path,
|
||||||
locale: pageArgs.locale,
|
locale: pageArgs.locale,
|
||||||
userId: req.user.id,
|
userId: req.user.id,
|
||||||
@ -63,6 +63,24 @@ router.get(['/h', '/h/*'], async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source
|
||||||
|
*/
|
||||||
|
router.get(['/s', '/s/*'], async (req, res, next) => {
|
||||||
|
const pageArgs = pageHelper.parsePath(req.path)
|
||||||
|
const page = await WIKI.models.pages.getPageFromDb({
|
||||||
|
path: pageArgs.path,
|
||||||
|
locale: pageArgs.locale,
|
||||||
|
userId: req.user.id,
|
||||||
|
isPrivate: false
|
||||||
|
})
|
||||||
|
if (page) {
|
||||||
|
res.render('source', { page })
|
||||||
|
} else {
|
||||||
|
res.redirect(`/${pageArgs.path}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View document
|
* View document
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +10,12 @@ module.exports = {
|
|||||||
async pages() { return {} }
|
async pages() { return {} }
|
||||||
},
|
},
|
||||||
PageQuery: {
|
PageQuery: {
|
||||||
|
async history(obj, args, context, info) {
|
||||||
|
return WIKI.models.pageHistory.getHistory({
|
||||||
|
pageId: args.id,
|
||||||
|
offset: args.offset || 0
|
||||||
|
})
|
||||||
|
},
|
||||||
async list(obj, args, context, info) {
|
async list(obj, args, context, info) {
|
||||||
return WIKI.models.pages.query().select(
|
return WIKI.models.pages.query().select(
|
||||||
'pages.*',
|
'pages.*',
|
||||||
|
@ -15,6 +15,11 @@ extend type Mutation {
|
|||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
||||||
type PageQuery {
|
type PageQuery {
|
||||||
|
history(
|
||||||
|
id: Int!
|
||||||
|
offset: Int
|
||||||
|
): [PageHistory]
|
||||||
|
|
||||||
list(
|
list(
|
||||||
filter: String
|
filter: String
|
||||||
orderBy: String
|
orderBy: String
|
||||||
@ -92,3 +97,13 @@ type Page {
|
|||||||
createdAt: Date!
|
createdAt: Date!
|
||||||
updatedAt: Date!
|
updatedAt: Date!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PageHistory {
|
||||||
|
versionId: Int!
|
||||||
|
authorId: Int!
|
||||||
|
authorName: String!
|
||||||
|
actionType: String!
|
||||||
|
valueBefore: String
|
||||||
|
valueAfter: String
|
||||||
|
createdAt: Date!
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const Model = require('objection').Model
|
const Model = require('objection').Model
|
||||||
|
const _ = require('lodash')
|
||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
@ -101,4 +102,53 @@ module.exports = class PageHistory extends Model {
|
|||||||
title: opts.title
|
title: opts.title
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getHistory({ pageId, offset = 0 }) {
|
||||||
|
const history = await WIKI.models.pageHistory.query()
|
||||||
|
.column([
|
||||||
|
'pageHistory.id',
|
||||||
|
'pageHistory.path',
|
||||||
|
'pageHistory.authorId',
|
||||||
|
'pageHistory.createdAt',
|
||||||
|
{
|
||||||
|
authorName: 'author.name'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.joinRelation('author')
|
||||||
|
.where({
|
||||||
|
'pageHistory.pageId': pageId
|
||||||
|
})
|
||||||
|
.orderBy('pageHistory.createdAt', 'asc')
|
||||||
|
.offset(offset)
|
||||||
|
.limit(20)
|
||||||
|
|
||||||
|
let prevPh = null
|
||||||
|
|
||||||
|
return _.reduce(history, (res, ph) => {
|
||||||
|
let actionType = 'edit'
|
||||||
|
let valueBefore = null
|
||||||
|
let valueAfter = null
|
||||||
|
|
||||||
|
if (!prevPh && offset === 0) {
|
||||||
|
actionType = 'initial'
|
||||||
|
} else if (_.get(prevPh, 'path', '') !== ph.path) {
|
||||||
|
actionType = 'move'
|
||||||
|
valueBefore = _.get(prevPh, 'path', '')
|
||||||
|
valueAfter = ph.path
|
||||||
|
}
|
||||||
|
|
||||||
|
res.unshift({
|
||||||
|
versionId: ph.id,
|
||||||
|
authorId: ph.authorId,
|
||||||
|
authorName: ph.authorName,
|
||||||
|
actionType,
|
||||||
|
valueBefore,
|
||||||
|
valueAfter,
|
||||||
|
createdAt: ph.createdAt
|
||||||
|
})
|
||||||
|
|
||||||
|
prevPh = ph
|
||||||
|
return res
|
||||||
|
}, [])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ module.exports = class Page extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async getPageFromDb(opts) {
|
static async getPageFromDb(opts) {
|
||||||
const page = await WIKI.models.pages.query()
|
return WIKI.models.pages.query()
|
||||||
.column([
|
.column([
|
||||||
'pages.*',
|
'pages.*',
|
||||||
{
|
{
|
||||||
@ -227,7 +227,6 @@ module.exports = class Page extends Model {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.first()
|
.first()
|
||||||
return page
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async savePageToCache(page) {
|
static async savePageToCache(page) {
|
||||||
|
@ -5,7 +5,8 @@ block head
|
|||||||
block body
|
block body
|
||||||
#root
|
#root
|
||||||
history(
|
history(
|
||||||
id=page.id
|
:page-id=page.id
|
||||||
locale=page.localeCode
|
locale=page.localeCode
|
||||||
path=page.path
|
path=page.path
|
||||||
|
live-content=page.content
|
||||||
)
|
)
|
||||||
|
11
server/views/source.pug
Normal file
11
server/views/source.pug
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
extends master.pug
|
||||||
|
|
||||||
|
block head
|
||||||
|
|
||||||
|
block body
|
||||||
|
#root
|
||||||
|
page-source(
|
||||||
|
page-id=page.id
|
||||||
|
locale=page.localeCode
|
||||||
|
path=page.path
|
||||||
|
)= page.content
|
@ -4934,7 +4934,7 @@ diff2html@2.5.0:
|
|||||||
lodash "^4.17.11"
|
lodash "^4.17.11"
|
||||||
whatwg-fetch "^3.0.0"
|
whatwg-fetch "^3.0.0"
|
||||||
|
|
||||||
diff@^3.1.0, diff@^3.5.0:
|
diff@3.5.0, diff@^3.1.0, diff@^3.5.0:
|
||||||
version "3.5.0"
|
version "3.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||||
|
Loading…
Reference in New Issue
Block a user