feat: page published state + comments localization

This commit is contained in:
NGPixel 2020-06-20 22:08:59 -04:00
parent 83b83a7510
commit 4855051d87
7 changed files with 105 additions and 70 deletions

View File

@ -3,7 +3,7 @@
v-textarea#discussion-new(
outlined
flat
placeholder='Write a new comment...'
:placeholder='$t(`common:comments.newPlaceholder`)'
auto-grow
dense
rows='3'
@ -19,7 +19,7 @@
outlined
color='blue-grey darken-2'
:background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
placeholder='Your Name'
:placeholder='$t(`common:comments.fieldName`)'
hide-details
dense
autocomplete='name'
@ -30,7 +30,7 @@
outlined
color='blue-grey darken-2'
:background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
placeholder='Your Email Address'
:placeholder='$t(`common:comments.fieldEmail`)'
hide-details
type='email'
dense
@ -39,9 +39,11 @@
)
.d-flex.align-center.pt-3(v-if='permissions.write')
v-icon.mr-1(color='blue-grey') mdi-language-markdown-outline
.caption.blue-grey--text Markdown Format
.caption.blue-grey--text {{$t('common:comments.markdownFormat')}}
v-spacer
.caption.mr-3(v-if='isAuthenticated') Posting as #[strong {{userDisplayName}}]
.caption.mr-3(v-if='isAuthenticated')
i18next(tag='span', path='common:comments.postingAs')
strong(place='bold') {{userDisplayName}}
v-btn(
dark
color='blue-grey darken-2'
@ -49,7 +51,7 @@
depressed
)
v-icon(left) mdi-comment
span.text-none Post Comment
span.text-none {{$t('common:comments.postComment')}}
v-divider.mt-3(v-if='permissions.write')
.pa-5.d-flex.align-center.justify-center(v-if='isLoading && !hasLoadedOnce')
v-progress-circular(
@ -58,7 +60,7 @@
width='1'
color='blue-grey'
)
.caption.blue-grey--text.pl-3: em Loading comments...
.caption.blue-grey--text.pl-3: em {{$t('common:comments.loading')}}
v-timeline(
dense
v-else-if='comments && comments.length > 0'
@ -80,7 +82,7 @@
v-icon.mr-3(small, @click='editComment(cm)') mdi-pencil
v-icon(small, @click='deleteCommentConfirm(cm)') mdi-delete
.comments-post-name.caption: strong {{cm.authorName}}
.comments-post-date.overline.grey--text {{cm.createdAt | moment('from') }} #[em(v-if='cm.createdAt !== cm.updatedAt') - modified {{cm.updatedAt | moment('from') }}]
.comments-post-date.overline.grey--text {{cm.createdAt | moment('from') }} #[em(v-if='cm.createdAt !== cm.updatedAt') - {{$t('common:comments.modified', { reldate: $options.filters.moment(cm.updatedAt, 'from') })}}]
.comments-post-content.mt-3(v-if='commentEditId !== cm.id', v-html='cm.render')
.comments-post-editcontent.mt-3(v-else)
v-textarea(
@ -103,7 +105,7 @@
outlined
)
v-icon(left) mdi-close
span.text-none Cancel
span.text-none {{$t('common:action.cancel')}}
v-btn(
dark
color='blue-grey darken-2'
@ -111,16 +113,16 @@
depressed
)
v-icon(left) mdi-comment
span.text-none Update Comment
.pt-5.text-center.body-2.blue-grey--text(v-else-if='permissions.write') Be the first to comment.
.text-center.body-2.blue-grey--text(v-else) No comments yet.
span.text-none {{$t('common:comments.updateComment')}}
.pt-5.text-center.body-2.blue-grey--text(v-else-if='permissions.write') {{$t('common:comments.beFirst')}}
.text-center.body-2.blue-grey--text(v-else) {{$t('common:comments.none')}}
v-dialog(v-model='deleteCommentDialogShown', max-width='500')
v-card
.dialog-header.is-red Confirm Delete
.dialog-header.is-red {{$t('common:comments.deleteConfirmTitle')}}
v-card-text.pt-5
span Are you sure you want to permanently delete this comment?
.caption: strong This action cannot be undone!
span {{$t('common:comments.deleteWarn')}}
.caption: strong {{$t('common:comments.deletePermanentWarn')}}
v-card-chin
v-spacer
v-btn(text, @click='deleteCommentDialogShown = false') {{$t('common:actions.cancel')}}
@ -298,7 +300,7 @@ export default {
if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
this.$store.commit('showNotification', {
style: 'success',
message: 'New comment posted successfully.',
message: this.$t('common:comments.postSuccess'),
icon: 'check'
})
@ -371,7 +373,7 @@ export default {
this.isBusy = true
try {
if (this.commentEditContent.length < 2) {
throw new Error('Comment is empty or too short!')
throw new Error(this.$t('common:comments.contentMissingError'))
}
const resp = await this.$apollo.mutate({
mutation: gql`
@ -404,7 +406,7 @@ export default {
if (_.get(resp, 'data.comments.update.responseResult.succeeded', false)) {
this.$store.commit('showNotification', {
style: 'success',
message: 'Comment was updated successfully.',
message: this.$t('common:comments.updateSuccess'),
icon: 'check'
})
@ -470,7 +472,7 @@ export default {
if (_.get(resp, 'data.comments.delete.responseResult.succeeded', false)) {
this.$store.commit('showNotification', {
style: 'success',
message: 'Comment was deleted successfully.',
message: this.$t('common:comments.deleteSuccess'),
icon: 'check'
})

View File

@ -115,6 +115,14 @@ export default {
type: String,
default: ''
},
publishStartDate: {
type: String,
default: ''
},
publishEndDate: {
type: String,
default: ''
},
scriptJs: {
type: String,
default: ''
@ -196,6 +204,8 @@ export default {
this.$store.set('page/id', this.pageId)
this.$store.set('page/description', this.description)
this.$store.set('page/isPublished', this.isPublished)
this.$store.set('page/publishStartDate', this.publishStartDate)
this.$store.set('page/publishEndDate', this.publishEndDate)
this.$store.set('page/locale', this.locale)
this.$store.set('page/path', this.path)
this.$store.set('page/tags', this.tags)

View File

@ -136,12 +136,12 @@
)
v-spacer
v-btn(
flat=''
text
color='primary'
@click='isPublishStartShown = false'
) {{$t('common:actions.cancel')}}
v-btn(
flat=''
text
color='primary'
@click='$refs.menuPublishStart.save(publishStartDate)'
) {{$t('common:actions.ok')}}
@ -177,12 +177,12 @@
)
v-spacer
v-btn(
flat=''
text
color='primary'
@click='isPublishEndShown = false'
) {{$t('common:actions.cancel')}}
v-btn(
flat=''
text
color='primary'
@click='$refs.menuPublishEnd.save(publishEndDate)'
) {{$t('common:actions.ok')}}

View File

@ -260,12 +260,14 @@
v-icon(size='20') mdi-trash-can-outline
span {{$t('common:header.delete')}}
span {{$t('common:page.editPage')}}
v-alert.mb-5(v-if='!isPublished', color='red', outlined, icon='mdi-minus-circle', dense)
.caption {{$t('common:page.unpublishedWarning')}}
.contents(ref='container')
slot(name='contents')
.comments-container#discussion(v-if='commentsEnabled && commentsPerms.read')
.comments-header
v-icon.mr-2(dark) mdi-comment-text-outline
span Comments
span {{$t('common:comments.title')}}
.comments-main
slot(name='comments')
nav-footer

View File

@ -3,37 +3,12 @@ const router = express.Router()
const pageHelper = require('../helpers/page')
const _ = require('lodash')
const CleanCSS = require('clean-css')
const moment = require('moment')
/* global WIKI */
const tmplCreateRegex = /^[0-9]+(,[0-9]+)?$/
const getPageEffectivePermissions = (req, page) => {
return {
comments: {
read: WIKI.config.features.featurePageComments ? WIKI.auth.checkAccess(req.user, ['read:comments'], page) : false,
write: WIKI.config.features.featurePageComments ? WIKI.auth.checkAccess(req.user, ['write:comments'], page) : false,
manage: WIKI.config.features.featurePageComments ? WIKI.auth.checkAccess(req.user, ['manage:comments'], page) : false
},
history: {
read: WIKI.auth.checkAccess(req.user, ['read:history'], page)
},
source: {
read: WIKI.auth.checkAccess(req.user, ['read:source'], page)
},
pages: {
write: WIKI.auth.checkAccess(req.user, ['write:pages'], page),
manage: WIKI.auth.checkAccess(req.user, ['manage:pages'], page),
delete: WIKI.auth.checkAccess(req.user, ['delete:pages'], page),
script: WIKI.auth.checkAccess(req.user, ['write:scripts'], page),
style: WIKI.auth.checkAccess(req.user, ['write:styles'], page)
},
system: {
manage: WIKI.auth.checkAccess(req.user, ['manage:system'], page)
}
}
}
/**
* Robots.txt
*/
@ -137,6 +112,9 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
pageArgs.tags = _.get(page, 'tags', [])
// -> Effective Permissions
const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
const injectCode = {
css: WIKI.config.theming.injectCSS,
head: WIKI.config.theming.injectHead,
@ -145,7 +123,7 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
if (page) {
// -> EDIT MODE
if (!WIKI.auth.checkAccess(req.user, ['write:pages', 'manage:pages'], pageArgs)) {
if (!(effectivePermissions.pages.write || effectivePermissions.pages.manage)) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.render('unauthorized', { action: 'edit' })
}
@ -166,7 +144,7 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
page.content = Buffer.from(page.content).toString('base64')
} else {
// -> CREATE MODE
if (!WIKI.auth.checkAccess(req.user, ['write:pages'], pageArgs)) {
if (!effectivePermissions.pages.write) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.render('unauthorized', { action: 'create' })
}
@ -229,9 +207,6 @@ router.get(['/e', '/e/*'], async (req, res, next) => {
}
}
// -> Effective Permissions
const effectivePermissions = getPageEffectivePermissions(req, pageArgs)
res.render('editor', { page, injectCode, effectivePermissions })
})
@ -262,7 +237,9 @@ router.get(['/h', '/h/*'], async (req, res, next) => {
pageArgs.tags = _.get(page, 'tags', [])
if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
if (!effectivePermissions.history.read) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.render('unauthorized', { action: 'history' })
}
@ -271,9 +248,6 @@ router.get(['/h', '/h/*'], async (req, res, next) => {
_.set(res.locals, 'pageMeta.title', page.title)
_.set(res.locals, 'pageMeta.description', page.description)
// -> Effective Permissions
const effectivePermissions = getPageEffectivePermissions(req, pageArgs)
res.render('history', { page, effectivePermissions })
} else {
res.redirect(`/${pageArgs.path}`)
@ -346,16 +320,19 @@ router.get(['/s', '/s/*'], async (req, res, next) => {
return res.redirect(`/s/${pageArgs.locale}/${pageArgs.path}`)
}
// -> Effective Permissions
const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
_.set(res, 'locals.siteConfig.lang', pageArgs.locale)
_.set(res, 'locals.siteConfig.rtl', req.i18n.dir() === 'rtl')
if (versionId > 0) {
if (!WIKI.auth.checkAccess(req.user, ['read:history'], pageArgs)) {
if (!effectivePermissions.history.read) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.render('unauthorized', { action: 'sourceVersion' })
}
} else {
if (!WIKI.auth.checkAccess(req.user, ['read:source'], pageArgs)) {
if (!effectivePermissions.source.read) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.render('unauthorized', { action: 'source' })
}
@ -376,9 +353,6 @@ router.get(['/s', '/s/*'], async (req, res, next) => {
_.set(res.locals, 'pageMeta.title', page.title)
_.set(res.locals, 'pageMeta.description', page.description)
// -> Effective Permissions
const effectivePermissions = getPageEffectivePermissions(req, pageArgs)
res.render('source', { page, effectivePermissions })
}
} else {
@ -419,8 +393,11 @@ router.get('/*', async (req, res, next) => {
})
pageArgs.tags = _.get(page, 'tags', [])
// -> Effective Permissions
const effectivePermissions = WIKI.auth.getEffectivePermissions(req, pageArgs)
// -> Check User Access
if (!WIKI.auth.checkAccess(req.user, ['read:pages'], pageArgs)) {
if (!effectivePermissions.pages.read) {
if (req.user.id === 2) {
res.cookie('loginRedirect', req.path, {
maxAge: 15 * 60 * 1000
@ -442,6 +419,21 @@ router.get('/*', async (req, res, next) => {
_.set(res.locals, 'pageMeta.title', page.title)
_.set(res.locals, 'pageMeta.description', page.description)
// -> Check Publishing State
let pageIsPublished = page.isPublished
if (pageIsPublished && !_.isEmpty(page.publishStartDate)) {
pageIsPublished = moment(page.publishStartDate).isSameOrBefore()
}
if (pageIsPublished && !_.isEmpty(page.publishEndDate)) {
pageIsPublished = moment(page.publishEndDate).isSameOrAfter()
}
if (!pageIsPublished && !effectivePermissions.pages.write) {
_.set(res.locals, 'pageMeta.title', 'Unauthorized')
return res.status(403).render('unauthorized', {
action: 'view'
})
}
// -> Build sidebar navigation
let sdi = 1
const sidebar = (await WIKI.models.navigation.getTree({ cache: true, locale: pageArgs.locale, groups: req.user.groups })).map(n => ({
@ -499,9 +491,6 @@ router.get('/*', async (req, res, next) => {
})
}
// -> Effective Permissions
const effectivePermissions = getPageEffectivePermissions(req, pageArgs)
// -> Render view
res.render('page', {
page,
@ -516,7 +505,7 @@ router.get('/*', async (req, res, next) => {
res.render('welcome', { locale: pageArgs.locale })
} else {
_.set(res.locals, 'pageMeta.title', 'Page Not Found')
if (WIKI.auth.checkAccess(req.user, ['write:pages'], pageArgs)) {
if (effectivePermissions.pages.write) {
res.status(404).render('new', { path: pageArgs.path, locale: pageArgs.locale })
} else {
res.status(404).render('notfound', { action: 'view' })

View File

@ -380,5 +380,35 @@ module.exports = {
WIKI.events.inbound.on('reloadAuthStrategies', () => {
WIKI.auth.activateStrategies()
})
},
/**
* Get all user permissions for a specific page
*/
getEffectivePermissions (req, page) {
return {
comments: {
read: WIKI.config.features.featurePageComments ? WIKI.auth.checkAccess(req.user, ['read:comments'], page) : false,
write: WIKI.config.features.featurePageComments ? WIKI.auth.checkAccess(req.user, ['write:comments'], page) : false,
manage: WIKI.config.features.featurePageComments ? WIKI.auth.checkAccess(req.user, ['manage:comments'], page) : false
},
history: {
read: WIKI.auth.checkAccess(req.user, ['read:history'], page)
},
source: {
read: WIKI.auth.checkAccess(req.user, ['read:source'], page)
},
pages: {
read: WIKI.auth.checkAccess(req.user, ['read:pages'], page),
write: WIKI.auth.checkAccess(req.user, ['write:pages'], page),
manage: WIKI.auth.checkAccess(req.user, ['manage:pages'], page),
delete: WIKI.auth.checkAccess(req.user, ['delete:pages'], page),
script: WIKI.auth.checkAccess(req.user, ['write:scripts'], page),
style: WIKI.auth.checkAccess(req.user, ['write:styles'], page)
},
system: {
manage: WIKI.auth.checkAccess(req.user, ['manage:system'], page)
}
}
}
}

View File

@ -14,6 +14,8 @@ block body
description=page.description
:tags=page.tags
:is-published=page.isPublished
publish-start-date=page.publishStartDate
publish-end-date=page.publishEndDate
script-css=page.extra.css
script-js=page.extra.js
init-mode=page.mode