浏览代码

feat: page published state + comments localization

NGPixel 5 年之前
父节点
当前提交
4855051d87

+ 21 - 19
client/components/comments.vue

@@ -3,7 +3,7 @@
     v-textarea#discussion-new(
     v-textarea#discussion-new(
       outlined
       outlined
       flat
       flat
-      placeholder='Write a new comment...'
+      :placeholder='$t(`common:comments.newPlaceholder`)'
       auto-grow
       auto-grow
       dense
       dense
       rows='3'
       rows='3'
@@ -19,7 +19,7 @@
           outlined
           outlined
           color='blue-grey darken-2'
           color='blue-grey darken-2'
           :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
           :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
-          placeholder='Your Name'
+          :placeholder='$t(`common:comments.fieldName`)'
           hide-details
           hide-details
           dense
           dense
           autocomplete='name'
           autocomplete='name'
@@ -30,7 +30,7 @@
           outlined
           outlined
           color='blue-grey darken-2'
           color='blue-grey darken-2'
           :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
           :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
-          placeholder='Your Email Address'
+          :placeholder='$t(`common:comments.fieldEmail`)'
           hide-details
           hide-details
           type='email'
           type='email'
           dense
           dense
@@ -39,9 +39,11 @@
         )
         )
     .d-flex.align-center.pt-3(v-if='permissions.write')
     .d-flex.align-center.pt-3(v-if='permissions.write')
       v-icon.mr-1(color='blue-grey') mdi-language-markdown-outline
       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
       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(
       v-btn(
         dark
         dark
         color='blue-grey darken-2'
         color='blue-grey darken-2'
@@ -49,7 +51,7 @@
         depressed
         depressed
         )
         )
         v-icon(left) mdi-comment
         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')
     v-divider.mt-3(v-if='permissions.write')
     .pa-5.d-flex.align-center.justify-center(v-if='isLoading && !hasLoadedOnce')
     .pa-5.d-flex.align-center.justify-center(v-if='isLoading && !hasLoadedOnce')
       v-progress-circular(
       v-progress-circular(
@@ -58,7 +60,7 @@
         width='1'
         width='1'
         color='blue-grey'
         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(
     v-timeline(
       dense
       dense
       v-else-if='comments && comments.length > 0'
       v-else-if='comments && comments.length > 0'
@@ -80,7 +82,7 @@
               v-icon.mr-3(small, @click='editComment(cm)') mdi-pencil
               v-icon.mr-3(small, @click='editComment(cm)') mdi-pencil
               v-icon(small, @click='deleteCommentConfirm(cm)') mdi-delete
               v-icon(small, @click='deleteCommentConfirm(cm)') mdi-delete
             .comments-post-name.caption: strong {{cm.authorName}}
             .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-content.mt-3(v-if='commentEditId !== cm.id', v-html='cm.render')
             .comments-post-editcontent.mt-3(v-else)
             .comments-post-editcontent.mt-3(v-else)
               v-textarea(
               v-textarea(
@@ -103,7 +105,7 @@
                   outlined
                   outlined
                   )
                   )
                   v-icon(left) mdi-close
                   v-icon(left) mdi-close
-                  span.text-none Cancel
+                  span.text-none {{$t('common:action.cancel')}}
                 v-btn(
                 v-btn(
                   dark
                   dark
                   color='blue-grey darken-2'
                   color='blue-grey darken-2'
@@ -111,16 +113,16 @@
                   depressed
                   depressed
                   )
                   )
                   v-icon(left) mdi-comment
                   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-dialog(v-model='deleteCommentDialogShown', max-width='500')
       v-card
       v-card
-        .dialog-header.is-red Confirm Delete
+        .dialog-header.is-red {{$t('common:comments.deleteConfirmTitle')}}
         v-card-text.pt-5
         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-card-chin
           v-spacer
           v-spacer
           v-btn(text, @click='deleteCommentDialogShown = false') {{$t('common:actions.cancel')}}
           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)) {
         if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
           this.$store.commit('showNotification', {
           this.$store.commit('showNotification', {
             style: 'success',
             style: 'success',
-            message: 'New comment posted successfully.',
+            message: this.$t('common:comments.postSuccess'),
             icon: 'check'
             icon: 'check'
           })
           })
 
 
@@ -371,7 +373,7 @@ export default {
       this.isBusy = true
       this.isBusy = true
       try {
       try {
         if (this.commentEditContent.length < 2) {
         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({
         const resp = await this.$apollo.mutate({
           mutation: gql`
           mutation: gql`
@@ -404,7 +406,7 @@ export default {
         if (_.get(resp, 'data.comments.update.responseResult.succeeded', false)) {
         if (_.get(resp, 'data.comments.update.responseResult.succeeded', false)) {
           this.$store.commit('showNotification', {
           this.$store.commit('showNotification', {
             style: 'success',
             style: 'success',
-            message: 'Comment was updated successfully.',
+            message: this.$t('common:comments.updateSuccess'),
             icon: 'check'
             icon: 'check'
           })
           })
 
 
@@ -470,7 +472,7 @@ export default {
         if (_.get(resp, 'data.comments.delete.responseResult.succeeded', false)) {
         if (_.get(resp, 'data.comments.delete.responseResult.succeeded', false)) {
           this.$store.commit('showNotification', {
           this.$store.commit('showNotification', {
             style: 'success',
             style: 'success',
-            message: 'Comment was deleted successfully.',
+            message: this.$t('common:comments.deleteSuccess'),
             icon: 'check'
             icon: 'check'
           })
           })
 
 

+ 10 - 0
client/components/editor.vue

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

+ 4 - 4
client/components/editor/editor-modal-properties.vue

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

+ 4 - 2
client/themes/default/components/page.vue

@@ -260,12 +260,14 @@
                         v-icon(size='20') mdi-trash-can-outline
                         v-icon(size='20') mdi-trash-can-outline
                     span {{$t('common:header.delete')}}
                     span {{$t('common:header.delete')}}
               span {{$t('common:page.editPage')}}
               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')
             .contents(ref='container')
               slot(name='contents')
               slot(name='contents')
             .comments-container#discussion(v-if='commentsEnabled && commentsPerms.read')
             .comments-container#discussion(v-if='commentsEnabled && commentsPerms.read')
               .comments-header
               .comments-header
                 v-icon.mr-2(dark) mdi-comment-text-outline
                 v-icon.mr-2(dark) mdi-comment-text-outline
-                span Comments
+                span {{$t('common:comments.title')}}
               .comments-main
               .comments-main
                 slot(name='comments')
                 slot(name='comments')
     nav-footer
     nav-footer
@@ -503,7 +505,7 @@ export default {
     this.$store.set('page/title', this.title)
     this.$store.set('page/title', this.title)
     this.$store.set('page/updatedAt', this.updatedAt)
     this.$store.set('page/updatedAt', this.updatedAt)
     if (this.effectivePermissions) {
     if (this.effectivePermissions) {
-      this.$store.set('page/effectivePermissions',JSON.parse(Buffer.from(this.effectivePermissions, 'base64').toString()))
+      this.$store.set('page/effectivePermissions', JSON.parse(Buffer.from(this.effectivePermissions, 'base64').toString()))
     }
     }
 
 
     this.$store.set('page/mode', 'view')
     this.$store.set('page/mode', 'view')

+ 34 - 45
server/controllers/common.js

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

+ 30 - 0
server/core/auth.js

@@ -380,5 +380,35 @@ module.exports = {
     WIKI.events.inbound.on('reloadAuthStrategies', () => {
     WIKI.events.inbound.on('reloadAuthStrategies', () => {
       WIKI.auth.activateStrategies()
       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)
+      }
+    }
   }
   }
 }
 }

+ 2 - 0
server/views/editor.pug

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