feat: comments - default provider create (wip) + permissions
This commit is contained in:
parent
2fe2e6210d
commit
1222355046
@ -126,7 +126,7 @@ export default {
|
|||||||
permission: 'write:comments',
|
permission: 'write:comments',
|
||||||
hint: 'Can post new comments, as specified in the Page Rules',
|
hint: 'Can post new comments, as specified in the Page Rules',
|
||||||
warning: false,
|
warning: false,
|
||||||
restrictedForSystem: true,
|
restrictedForSystem: false,
|
||||||
disabled: false
|
disabled: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
div(v-intersect.once.quiet='onIntersect')
|
div(v-intersect.once='onIntersect')
|
||||||
v-textarea#discussion-new(
|
v-textarea#discussion-new(
|
||||||
outlined
|
outlined
|
||||||
flat
|
flat
|
||||||
@ -11,11 +11,37 @@
|
|||||||
v-model='newcomment'
|
v-model='newcomment'
|
||||||
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`'
|
||||||
|
v-if='permissions.write'
|
||||||
)
|
)
|
||||||
.d-flex.align-center.pt-3
|
v-row.mt-2(dense, v-if='!isAuthenticated && permissions.write')
|
||||||
|
v-col(cols='12', lg='6')
|
||||||
|
v-text-field(
|
||||||
|
outlined
|
||||||
|
color='blue-grey darken-2'
|
||||||
|
:background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
|
||||||
|
placeholder='Your Name'
|
||||||
|
hide-details
|
||||||
|
dense
|
||||||
|
autocomplete='name'
|
||||||
|
v-model='guestName'
|
||||||
|
)
|
||||||
|
v-col(cols='12', lg='6')
|
||||||
|
v-text-field(
|
||||||
|
outlined
|
||||||
|
color='blue-grey darken-2'
|
||||||
|
:background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
|
||||||
|
placeholder='Your Email Address'
|
||||||
|
hide-details
|
||||||
|
type='email'
|
||||||
|
dense
|
||||||
|
autocomplete='email'
|
||||||
|
v-model='guestEmail'
|
||||||
|
)
|
||||||
|
.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 Markdown Format
|
||||||
v-spacer
|
v-spacer
|
||||||
|
.caption.mr-3(v-if='isAuthenticated') Posting as #[strong {{userDisplayName}}]
|
||||||
v-btn(
|
v-btn(
|
||||||
dark
|
dark
|
||||||
color='blue-grey darken-2'
|
color='blue-grey darken-2'
|
||||||
@ -24,7 +50,7 @@
|
|||||||
)
|
)
|
||||||
v-icon(left) mdi-comment
|
v-icon(left) mdi-comment
|
||||||
span.text-none Post Comment
|
span.text-none Post Comment
|
||||||
v-divider.mt-3
|
v-divider.mt-3(v-if='permissions.write')
|
||||||
.pa-5.d-flex.align-center.justify-center(v-if='isLoading')
|
.pa-5.d-flex.align-center.justify-center(v-if='isLoading')
|
||||||
v-progress-circular(
|
v-progress-circular(
|
||||||
indeterminate
|
indeterminate
|
||||||
@ -48,15 +74,18 @@
|
|||||||
v-img(src='http://i.pravatar.cc/64')
|
v-img(src='http://i.pravatar.cc/64')
|
||||||
v-card.elevation-1
|
v-card.elevation-1
|
||||||
v-card-text
|
v-card-text
|
||||||
.caption: strong John Smith
|
.caption: strong {{cm.authorName}}
|
||||||
.overline.grey--text 3 minutes ago
|
.overline.grey--text 3 minutes ago
|
||||||
.mt-3 {{cm.render}}
|
.mt-3 {{cm.render}}
|
||||||
.pt-5.text-center.body-2.blue-grey--text(v-else) Be the first to 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.
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { get } from 'vuex-pathify'
|
import { get } from 'vuex-pathify'
|
||||||
|
import validate from 'validate.js'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
@ -64,19 +93,118 @@ export default {
|
|||||||
newcomment: '',
|
newcomment: '',
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
canFetch: false,
|
canFetch: false,
|
||||||
comments: []
|
comments: [],
|
||||||
|
guestName: '',
|
||||||
|
guestEmail: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
pageId: get('page/id')
|
pageId: get('page/id'),
|
||||||
|
permissions: get('page/commentsPermissions'),
|
||||||
|
isAuthenticated: get('user/authenticated'),
|
||||||
|
userDisplayName: get('user/name')
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onIntersect () {
|
onIntersect (entries, observer, isIntersecting) {
|
||||||
this.isLoading = true
|
if (isIntersecting) {
|
||||||
this.canFetch = true
|
this.isLoading = true
|
||||||
|
this.canFetch = true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async postComment () {
|
async postComment () {
|
||||||
|
let rules = {
|
||||||
|
comment: {
|
||||||
|
presence: {
|
||||||
|
allowEmpty: false
|
||||||
|
},
|
||||||
|
length: {
|
||||||
|
minimum: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.isAuthenticated && this.permissions.write) {
|
||||||
|
rules.name = {
|
||||||
|
presence: {
|
||||||
|
allowEmpty: false
|
||||||
|
},
|
||||||
|
length: {
|
||||||
|
minimum: 2,
|
||||||
|
maximum: 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rules.email = {
|
||||||
|
presence: {
|
||||||
|
allowEmpty: false
|
||||||
|
},
|
||||||
|
email: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const validationResults = validate({
|
||||||
|
comment: this.newcomment,
|
||||||
|
name: this.guestName,
|
||||||
|
email: this.guestEmail
|
||||||
|
}, rules, { format: 'flat' })
|
||||||
|
|
||||||
|
if (validationResults) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
style: 'red',
|
||||||
|
message: validationResults[0],
|
||||||
|
icon: 'alert'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const resp = await this.$apollo.mutate({
|
||||||
|
mutation: gql`
|
||||||
|
mutation (
|
||||||
|
$pageId: Int!
|
||||||
|
$replyTo: Int
|
||||||
|
$content: String!
|
||||||
|
$guestName: String
|
||||||
|
$guestEmail: String
|
||||||
|
) {
|
||||||
|
comments {
|
||||||
|
create (
|
||||||
|
pageId: $pageId
|
||||||
|
replyTo: $replyTo
|
||||||
|
content: $content
|
||||||
|
guestName: $guestName
|
||||||
|
guestEmail: $guestEmail
|
||||||
|
) {
|
||||||
|
responseResult {
|
||||||
|
succeeded
|
||||||
|
errorCode
|
||||||
|
slug
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: {
|
||||||
|
pageId: this.pageId,
|
||||||
|
replyTo: 0,
|
||||||
|
content: this.newcomment,
|
||||||
|
guestName: this.guestName,
|
||||||
|
guestEmail: this.guestEmail
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
style: 'success',
|
||||||
|
message: 'New comment posted successfully.',
|
||||||
|
icon: 'check'
|
||||||
|
})
|
||||||
|
|
||||||
|
this.newcomment = ''
|
||||||
|
} else {
|
||||||
|
this.$store.commit('showNotification', {
|
||||||
|
style: 'red',
|
||||||
|
message: _.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occured.'),
|
||||||
|
icon: 'alert'
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
|
@ -15,9 +15,9 @@ const state = {
|
|||||||
title: '',
|
title: '',
|
||||||
updatedAt: '',
|
updatedAt: '',
|
||||||
mode: '',
|
mode: '',
|
||||||
comments: {
|
commentsPermissions: {
|
||||||
view: false,
|
read: false,
|
||||||
post: false,
|
write: false,
|
||||||
manage: false
|
manage: false
|
||||||
},
|
},
|
||||||
commentsCount: 0
|
commentsCount: 0
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
)
|
)
|
||||||
v-icon(:color='$vuetify.theme.dark ? `teal lighten-3` : `teal`', size='20') mdi-tag-multiple
|
v-icon(:color='$vuetify.theme.dark ? `teal lighten-3` : `teal`', size='20') mdi-tag-multiple
|
||||||
|
|
||||||
v-card.mb-5(v-if='commentsEnabled')
|
v-card.mb-5(v-if='commentsEnabled && commentsPerms.read')
|
||||||
.pa-5
|
.pa-5
|
||||||
.overline.pb-2.blue-grey--text.d-flex.align-center(:class='$vuetify.theme.dark ? `text--lighten-3` : `text--darken-2`')
|
.overline.pb-2.blue-grey--text.d-flex.align-center(:class='$vuetify.theme.dark ? `text--lighten-3` : `text--darken-2`')
|
||||||
span Talk
|
span Talk
|
||||||
@ -113,7 +113,7 @@
|
|||||||
small
|
small
|
||||||
)
|
)
|
||||||
span.blue-grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') View Discussion
|
span.blue-grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') View Discussion
|
||||||
v-tooltip(right, v-if='isAuthenticated')
|
v-tooltip(right, v-if='commentsPerms.write')
|
||||||
template(v-slot:activator='{ on }')
|
template(v-slot:activator='{ on }')
|
||||||
v-btn.ml-2(
|
v-btn.ml-2(
|
||||||
@click='goToComments(true)'
|
@click='goToComments(true)'
|
||||||
@ -261,7 +261,7 @@
|
|||||||
span {{$t('common:page.editPage')}}
|
span {{$t('common:page.editPage')}}
|
||||||
.contents(ref='container')
|
.contents(ref='container')
|
||||||
slot(name='contents')
|
slot(name='contents')
|
||||||
.comments-container#discussion
|
.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 Comments
|
||||||
@ -446,6 +446,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
isAuthenticated: get('user/authenticated'),
|
isAuthenticated: get('user/authenticated'),
|
||||||
commentsCount: get('page/commentsCount'),
|
commentsCount: get('page/commentsCount'),
|
||||||
|
commentsPerms: get('page/commentsPermissions'),
|
||||||
rating: {
|
rating: {
|
||||||
get () {
|
get () {
|
||||||
return 3.5
|
return 3.5
|
||||||
@ -491,7 +492,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.commentsPermissions) {
|
if (this.commentsPermissions) {
|
||||||
this.$store.set('page/comments', JSON.parse(atob(this.commentsPermissions)))
|
this.$store.set('page/commentsPermissions', JSON.parse(atob(this.commentsPermissions)))
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.set('page/mode', 'view')
|
this.$store.set('page/mode', 'view')
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
"@root/keypairs": "0.9.0",
|
"@root/keypairs": "0.9.0",
|
||||||
"@root/pem": "1.0.4",
|
"@root/pem": "1.0.4",
|
||||||
"acme": "3.0.3",
|
"acme": "3.0.3",
|
||||||
|
"akismet-api": "5.0.0",
|
||||||
"algoliasearch": "4.2.0",
|
"algoliasearch": "4.2.0",
|
||||||
"apollo-fetch": "0.7.0",
|
"apollo-fetch": "0.7.0",
|
||||||
"apollo-server": "2.13.1",
|
"apollo-server": "2.13.1",
|
||||||
|
@ -447,12 +447,24 @@ router.get('/*', async (req, res, next) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -> Comments Permissions
|
||||||
|
const commentsPermissions = WIKI.config.features.featurePageComments ? {
|
||||||
|
read: WIKI.auth.checkAccess(req.user, ['read:comments'], pageArgs),
|
||||||
|
write: WIKI.auth.checkAccess(req.user, ['write:comments'], pageArgs),
|
||||||
|
manage: WIKI.auth.checkAccess(req.user, ['manage:comments'], pageArgs)
|
||||||
|
} : {
|
||||||
|
read: false,
|
||||||
|
write: false,
|
||||||
|
manage: false
|
||||||
|
}
|
||||||
|
|
||||||
// -> Render view
|
// -> Render view
|
||||||
res.render('page', {
|
res.render('page', {
|
||||||
page,
|
page,
|
||||||
sidebar,
|
sidebar,
|
||||||
injectCode,
|
injectCode,
|
||||||
comments: WIKI.data.commentProvider
|
comments: WIKI.data.commentProvider,
|
||||||
|
commentsPermissions
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (pageArgs.path === 'home') {
|
} else if (pageArgs.path === 'home') {
|
||||||
|
8
server/db/migrations-sqlite/2.4.61.js
Normal file
8
server/db/migrations-sqlite/2.4.61.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
exports.up = knex => {
|
||||||
|
return knex.schema
|
||||||
|
.alterTable('comments', table => {
|
||||||
|
table.integer('replyTo').unsigned().notNullable().defaultTo(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = knex => { }
|
8
server/db/migrations/2.4.61.js
Normal file
8
server/db/migrations/2.4.61.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
exports.up = knex => {
|
||||||
|
return knex.schema
|
||||||
|
.alterTable('comments', table => {
|
||||||
|
table.integer('replyTo').unsigned().notNullable().defaultTo(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = knex => { }
|
@ -11,6 +11,9 @@ module.exports = {
|
|||||||
async comments() { return {} }
|
async comments() { return {} }
|
||||||
},
|
},
|
||||||
CommentQuery: {
|
CommentQuery: {
|
||||||
|
/**
|
||||||
|
* Fetch list of Comments Providers
|
||||||
|
*/
|
||||||
async providers(obj, args, context, info) {
|
async providers(obj, args, context, info) {
|
||||||
const providers = await WIKI.models.commentProviders.getProviders()
|
const providers = await WIKI.models.commentProviders.getProviders()
|
||||||
return providers.map(provider => {
|
return providers.map(provider => {
|
||||||
@ -33,11 +36,33 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Fetch list of comments for a page
|
||||||
|
*/
|
||||||
async list (obj, args, context) {
|
async list (obj, args, context) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CommentMutation: {
|
CommentMutation: {
|
||||||
|
/**
|
||||||
|
* Create New Comment
|
||||||
|
*/
|
||||||
|
async create (obj, args, context) {
|
||||||
|
try {
|
||||||
|
// WIKI.data.commentProvider.create({
|
||||||
|
// ...args,
|
||||||
|
// user: context.req.user
|
||||||
|
// })
|
||||||
|
return {
|
||||||
|
responseResult: graphHelper.generateSuccess('New comment posted successfully')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return graphHelper.generateError(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Update Comments Providers
|
||||||
|
*/
|
||||||
async updateProviders(obj, args, context) {
|
async updateProviders(obj, args, context) {
|
||||||
try {
|
try {
|
||||||
for (let provider of args.providers) {
|
for (let provider of args.providers) {
|
||||||
|
@ -39,12 +39,18 @@ type CommentMutation {
|
|||||||
pageId: Int!
|
pageId: Int!
|
||||||
replyTo: Int
|
replyTo: Int
|
||||||
content: String!
|
content: String!
|
||||||
|
guestName: String
|
||||||
|
guestEmail: String
|
||||||
): DefaultResponse @auth(requires: ["write:comments", "manage:system"])
|
): DefaultResponse @auth(requires: ["write:comments", "manage:system"])
|
||||||
|
|
||||||
update(
|
update(
|
||||||
id: Int!
|
id: Int!
|
||||||
content: String!
|
content: String!
|
||||||
): DefaultResponse @auth(requires: ["write:comments", "manage:comments", "manage:system"])
|
): DefaultResponse @auth(requires: ["write:comments", "manage:comments", "manage:system"])
|
||||||
|
|
||||||
|
delete(
|
||||||
|
id: Int!
|
||||||
|
): DefaultResponse @auth(requires: ["manage:comments", "manage:system"])
|
||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
55
server/models/comments.js
Normal file
55
server/models/comments.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
const Model = require('objection').Model
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comments model
|
||||||
|
*/
|
||||||
|
module.exports = class Comment extends Model {
|
||||||
|
static get tableName() { return 'comments' }
|
||||||
|
|
||||||
|
static get jsonSchema () {
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
required: [],
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
id: {type: 'integer'},
|
||||||
|
content: {type: 'string'},
|
||||||
|
render: {type: 'string'},
|
||||||
|
name: {type: 'string'},
|
||||||
|
email: {type: 'string'},
|
||||||
|
ip: {type: 'string'},
|
||||||
|
createdAt: {type: 'string'},
|
||||||
|
updatedAt: {type: 'string'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get relationMappings() {
|
||||||
|
return {
|
||||||
|
author: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: require('./users'),
|
||||||
|
join: {
|
||||||
|
from: 'comments.authorId',
|
||||||
|
to: 'users.id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
page: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: require('./pages'),
|
||||||
|
join: {
|
||||||
|
from: 'comments.pageId',
|
||||||
|
to: 'pages.id'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$beforeUpdate() {
|
||||||
|
this.updatedAt = new Date().toISOString()
|
||||||
|
}
|
||||||
|
$beforeInsert() {
|
||||||
|
this.createdAt = new Date().toISOString()
|
||||||
|
this.updatedAt = new Date().toISOString()
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,115 @@
|
|||||||
|
const md = require('markdown-it')
|
||||||
|
const mdEmoji = require('markdown-it-emoji')
|
||||||
|
const { JSDOM } = require('jsdom')
|
||||||
|
const createDOMPurify = require('dompurify')
|
||||||
|
const _ = require('lodash')
|
||||||
|
const { AkismetClient } = require('akismet-api')
|
||||||
|
|
||||||
/* global WIKI */
|
/* global WIKI */
|
||||||
|
|
||||||
|
const window = new JSDOM('').window
|
||||||
|
const DOMPurify = createDOMPurify(window)
|
||||||
|
|
||||||
|
md.use(mdEmoji)
|
||||||
|
|
||||||
|
let akismetClient = null
|
||||||
|
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
// Default Comment Provider
|
// Default Comment Provider
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
add (args) {
|
/**
|
||||||
|
* Init
|
||||||
|
*/
|
||||||
|
async init (config) {
|
||||||
|
if (WIKI.data.commentProvider.config.akismet && WIKI.data.commentProvider.config.akismet.length > 2) {
|
||||||
|
akismetClient = new AkismetClient({
|
||||||
|
key: WIKI.data.commentProvider.config.akismet,
|
||||||
|
blog: WIKI.config.host,
|
||||||
|
lang: WIKI.config.lang.namespacing ? WIKI.config.lang.namespaces.join(', ') : WIKI.config.lang.code,
|
||||||
|
charset: 'UTF-8'
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const isValid = await akismetClient.verifyKey()
|
||||||
|
if (!isValid) {
|
||||||
|
WIKI.logger.warn('Akismet Key is invalid!')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
WIKI.logger.warn('Unable to verify Akismet Key: ' + err.message)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
akismetClient = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Create New Comment
|
||||||
|
*/
|
||||||
|
async create ({ page, replyTo, content, user }) {
|
||||||
|
// -> Render Markdown
|
||||||
|
const mkdown = md({
|
||||||
|
html: false,
|
||||||
|
breaks: true,
|
||||||
|
linkify: true,
|
||||||
|
highlight(str, lang) {
|
||||||
|
return `<pre><code class="language-${lang}">${_.escape(str)}</code></pre>`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// -> Build New Comment
|
||||||
|
const newComment = {
|
||||||
|
content,
|
||||||
|
render: DOMPurify.sanitize(mkdown.render(content)),
|
||||||
|
replyTo,
|
||||||
|
pageId: page.id,
|
||||||
|
authorId: user.id,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
ip: user.ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for Spam with Akismet
|
||||||
|
if (akismetClient) {
|
||||||
|
let userRole = 'user'
|
||||||
|
if (user.groups.indexOf(1) >= 0) {
|
||||||
|
userRole = 'administrator'
|
||||||
|
} else if (user.groups.indexOf(2) >= 0) {
|
||||||
|
userRole = 'guest'
|
||||||
|
}
|
||||||
|
|
||||||
|
let isSpam = false
|
||||||
|
try {
|
||||||
|
isSpam = await akismetClient.checkSpam({
|
||||||
|
ip: user.ip,
|
||||||
|
useragent: user.agentagent,
|
||||||
|
content,
|
||||||
|
name: user.name,
|
||||||
|
email: user.email,
|
||||||
|
permalink: `${WIKI.config.host}/${page.localeCode}/${page.path}`,
|
||||||
|
permalinkDate: page.updatedAt,
|
||||||
|
type: (replyTo > 0) ? 'reply' : 'comment',
|
||||||
|
role: userRole
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
WIKI.logger.warn('Akismet Comment Validation: [ FAILED ]')
|
||||||
|
WIKI.logger.warn(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSpam) {
|
||||||
|
throw new Error('Comment was rejected because it is marked as spam.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save Comment
|
||||||
|
await WIKI.models.comments.query().insert(newComment)
|
||||||
|
},
|
||||||
|
async update ({ id, content, user, ip }) {
|
||||||
|
|
||||||
|
},
|
||||||
|
async remove ({ id, user, ip }) {
|
||||||
|
|
||||||
|
},
|
||||||
|
async count ({ pageId }) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ block body
|
|||||||
sidebar=Buffer.from(JSON.stringify(sidebar)).toString('base64')
|
sidebar=Buffer.from(JSON.stringify(sidebar)).toString('base64')
|
||||||
nav-mode=config.nav.mode
|
nav-mode=config.nav.mode
|
||||||
comments-enabled=config.features.featurePageComments
|
comments-enabled=config.features.featurePageComments
|
||||||
comments-provider=comments.key
|
comments-permissions=Buffer.from(JSON.stringify(commentsPermissions)).toString('base64')
|
||||||
comments-external=comments.codeTemplate
|
comments-external=comments.codeTemplate
|
||||||
)
|
)
|
||||||
template(slot='contents')
|
template(slot='contents')
|
||||||
|
46
yarn.lock
46
yarn.lock
@ -3172,6 +3172,14 @@ ajv@^6.12.0:
|
|||||||
json-schema-traverse "^0.4.1"
|
json-schema-traverse "^0.4.1"
|
||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
|
akismet-api@5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/akismet-api/-/akismet-api-5.0.0.tgz#35fe88815486b552bde8c04e3f3256f430b46ce7"
|
||||||
|
integrity sha512-GdFa93txqJM3gGaeXJs0gAXNCIpcBFnCs8ylmPDGCRUecqkfl4+n9AqE0izQv5hWwvM1b47JQjD3pj0BR+zK5Q==
|
||||||
|
dependencies:
|
||||||
|
bluebird "^3.1.1"
|
||||||
|
superagent "^5.1.1"
|
||||||
|
|
||||||
algoliasearch@4.2.0:
|
algoliasearch@4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.2.0.tgz#dd81a1a0c57eb9f74af6db70b0c11f256692d1e6"
|
resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.2.0.tgz#dd81a1a0c57eb9f74af6db70b0c11f256692d1e6"
|
||||||
@ -5229,7 +5237,7 @@ commondir@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||||
|
|
||||||
component-emitter@^1.2.0, component-emitter@^1.2.1:
|
component-emitter@^1.2.0, component-emitter@^1.2.1, component-emitter@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||||
@ -5389,7 +5397,7 @@ cookie@0.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
||||||
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
||||||
|
|
||||||
cookiejar@^2.1.0:
|
cookiejar@^2.1.0, cookiejar@^2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
|
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
|
||||||
integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==
|
integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==
|
||||||
@ -7368,6 +7376,11 @@ fast-safe-stringify@^2.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2"
|
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2"
|
||||||
integrity sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==
|
integrity sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==
|
||||||
|
|
||||||
|
fast-safe-stringify@^2.0.7:
|
||||||
|
version "2.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
||||||
|
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
||||||
|
|
||||||
fb-watchman@^2.0.0:
|
fb-watchman@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
|
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
|
||||||
@ -7653,6 +7666,11 @@ formidable@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659"
|
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659"
|
||||||
integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==
|
integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==
|
||||||
|
|
||||||
|
formidable@^1.2.1:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
|
||||||
|
integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==
|
||||||
|
|
||||||
forwarded@~0.1.2:
|
forwarded@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||||
@ -10410,7 +10428,7 @@ mermaid@8.5.0:
|
|||||||
moment-mini "^2.22.1"
|
moment-mini "^2.22.1"
|
||||||
scope-css "^1.2.1"
|
scope-css "^1.2.1"
|
||||||
|
|
||||||
methods@^1.1.1, methods@~1.1.2:
|
methods@^1.1.1, methods@^1.1.2, methods@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||||
@ -13523,6 +13541,11 @@ qs@^6.5.1:
|
|||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.8.0.tgz#87b763f0d37ca54200334cd57bb2ef8f68a1d081"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.8.0.tgz#87b763f0d37ca54200334cd57bb2ef8f68a1d081"
|
||||||
integrity sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w==
|
integrity sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w==
|
||||||
|
|
||||||
|
qs@^6.9.1:
|
||||||
|
version "6.9.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
|
||||||
|
integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
|
||||||
|
|
||||||
qs@~6.5.2:
|
qs@~6.5.2:
|
||||||
version "6.5.2"
|
version "6.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||||
@ -15230,6 +15253,23 @@ superagent@^3.8.3:
|
|||||||
qs "^6.5.1"
|
qs "^6.5.1"
|
||||||
readable-stream "^2.3.5"
|
readable-stream "^2.3.5"
|
||||||
|
|
||||||
|
superagent@^5.1.1:
|
||||||
|
version "5.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.2.2.tgz#6ff726c5642795b2c27009e92687c8e69a6bb07d"
|
||||||
|
integrity sha512-pMWBUnIllK4ZTw7p/UaobiQPwAO5w/1NRRTDpV0FTVNmECztsxKspj3ZWEordVEaqpZtmOQJJna4yTLyC/q7PQ==
|
||||||
|
dependencies:
|
||||||
|
component-emitter "^1.3.0"
|
||||||
|
cookiejar "^2.1.2"
|
||||||
|
debug "^4.1.1"
|
||||||
|
fast-safe-stringify "^2.0.7"
|
||||||
|
form-data "^3.0.0"
|
||||||
|
formidable "^1.2.1"
|
||||||
|
methods "^1.1.2"
|
||||||
|
mime "^2.4.4"
|
||||||
|
qs "^6.9.1"
|
||||||
|
readable-stream "^3.4.0"
|
||||||
|
semver "^6.3.0"
|
||||||
|
|
||||||
supports-color@6.1.0, supports-color@^6.1.0:
|
supports-color@6.1.0, supports-color@^6.1.0:
|
||||||
version "6.1.0"
|
version "6.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
|
||||||
|
Loading…
Reference in New Issue
Block a user