Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wiki-js
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
wiki-js
Commits
7a946ec0
Commit
7a946ec0
authored
May 31, 2020
by
NGPixel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: edit comment
parent
e7460550
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
272 additions
and
34 deletions
+272
-34
comments.vue
client/components/comments.vue
+143
-2
comment.js
server/graph/resolvers/comment.js
+50
-7
comment.graphql
server/graph/schemas/comment.graphql
+7
-2
error.js
server/helpers/error.js
+12
-8
comments.js
server/models/comments.js
+33
-0
comment.js
server/modules/comments/default/comment.js
+27
-15
No files found.
client/components/comments.vue
View file @
7a946ec0
...
...
@@ -76,12 +76,42 @@
span.white--text.title
{{
cm
.
initials
}}
v-card.elevation-1
v-card-text
.comments-post-actions(v-if='permissions.manage && !isBusy')
.comments-post-actions(v-if='permissions.manage && !isBusy
&& commentEditId === 0
')
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-content.mt-3(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)
v-textarea(
outlined
flat
auto-grow
dense
rows='3'
hide-details
v-model='commentEditContent'
color='blue-grey darken-2'
:background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
)
.d-flex.align-center.pt-3
v-spacer
v-btn.mr-3(
dark
color='blue-grey darken-2'
@click='editCommentCancel'
outlined
)
v-icon(left) mdi-close
span.text-none Cancel
v-btn(
dark
color='blue-grey darken-2'
@click='updateComment'
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.
...
...
@@ -113,6 +143,8 @@ export default {
guestName
:
''
,
guestEmail
:
''
,
commentToDelete
:
{},
commentEditId
:
0
,
commentEditContent
:
null
,
deleteCommentDialogShown
:
false
,
isBusy
:
false
,
scrollOpts
:
{
...
...
@@ -286,9 +318,118 @@ export default {
})
}
},
/**
* Show Comment Editing Form
*/
async
editComment
(
cm
)
{
this
.
$store
.
commit
(
`loadingStart`
,
'comments-edit'
)
this
.
isBusy
=
true
try
{
const
results
=
await
this
.
$apollo
.
query
({
query
:
gql
`
query ($id: Int!) {
comments {
single(id: $id) {
content
}
}
}
`
,
variables
:
{
id
:
cm
.
id
},
fetchPolicy
:
'network-only'
})
this
.
commentEditContent
=
_
.
get
(
results
,
'data.comments.single.content'
,
null
)
if
(
this
.
commentEditContent
===
null
)
{
throw
new
Error
(
'Failed to load comment content.'
)
}
}
catch
(
err
)
{
console
.
warn
(
err
)
this
.
$store
.
commit
(
'showNotification'
,
{
style
:
'red'
,
message
:
err
.
message
,
icon
:
'alert'
})
}
this
.
commentEditId
=
cm
.
id
this
.
isBusy
=
false
this
.
$store
.
commit
(
`loadingStop`
,
'comments-edit'
)
},
/**
* Cancel Comment Edit
*/
editCommentCancel
()
{
this
.
commentEditId
=
0
this
.
commentEditContent
=
null
},
/**
* Update Comment with new content
*/
async
updateComment
()
{
this
.
$store
.
commit
(
`loadingStart`
,
'comments-edit'
)
this
.
isBusy
=
true
try
{
if
(
this
.
commentEditContent
.
length
<
2
)
{
throw
new
Error
(
'Comment is empty or too short!'
)
}
const
resp
=
await
this
.
$apollo
.
mutate
({
mutation
:
gql
`
mutation (
$id: Int!
$content: String!
) {
comments {
update (
id: $id,
content: $content
) {
responseResult {
succeeded
errorCode
slug
message
}
render
}
}
}
`
,
variables
:
{
id
:
this
.
commentEditId
,
content
:
this
.
commentEditContent
}
})
if
(
_
.
get
(
resp
,
'data.comments.update.responseResult.succeeded'
,
false
))
{
this
.
$store
.
commit
(
'showNotification'
,
{
style
:
'success'
,
message
:
'Comment was updated successfully.'
,
icon
:
'check'
})
const
cm
=
_
.
find
(
this
.
comments
,
[
'id'
,
this
.
commentEditId
])
cm
.
render
=
_
.
get
(
resp
,
'data.comments.update.render'
,
'-- Failed to load updated comment --'
)
cm
.
updatedAt
=
(
new
Date
()).
toISOString
()
this
.
editCommentCancel
()
}
else
{
throw
new
Error
(
_
.
get
(
resp
,
'data.comments.delete.responseResult.message'
,
'An unexpected error occured.'
))
}
}
catch
(
err
)
{
console
.
warn
(
err
)
this
.
$store
.
commit
(
'showNotification'
,
{
style
:
'red'
,
message
:
err
.
message
,
icon
:
'alert'
})
}
this
.
isBusy
=
false
this
.
$store
.
commit
(
`loadingStop`
,
'comments-edit'
)
},
/**
* Show Delete Comment Confirmation Dialog
*/
deleteCommentConfirm
(
cm
)
{
this
.
commentToDelete
=
cm
this
.
deleteCommentDialogShown
=
true
...
...
server/graph/resolvers/comment.js
View file @
7a946ec0
...
...
@@ -40,13 +40,10 @@ module.exports = {
* Fetch list of comments for a page
*/
async
list
(
obj
,
args
,
context
)
{
const
page
=
await
WIKI
.
models
.
pages
.
getPage
(
args
)
const
page
=
await
WIKI
.
models
.
pages
.
query
().
select
(
'id'
).
findOne
({
localeCode
:
args
.
locale
,
path
:
args
.
path
}
)
if
(
page
)
{
if
(
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'read:comments'
],
{
path
:
page
.
path
,
locale
:
page
.
localeCode
}))
{
const
comments
=
await
WIKI
.
models
.
comments
.
query
().
where
(
'pageId'
,
page
.
id
)
if
(
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'read:comments'
],
args
))
{
const
comments
=
await
WIKI
.
models
.
comments
.
query
().
where
(
'pageId'
,
page
.
id
).
orderBy
(
'createdAt'
)
return
comments
.
map
(
c
=>
({
...
c
,
authorName
:
c
.
name
,
...
...
@@ -54,11 +51,39 @@ module.exports = {
authorIP
:
c
.
ip
}))
}
else
{
throw
new
WIKI
.
Error
.
Page
ViewForbidden
()
throw
new
WIKI
.
Error
.
Comment
ViewForbidden
()
}
}
else
{
return
[]
}
},
/**
* Fetch a single comment
*/
async
single
(
obj
,
args
,
context
)
{
const
cm
=
await
WIKI
.
data
.
commentProvider
.
getCommentById
(
args
.
id
)
if
(
!
cm
||
!
cm
.
pageId
)
{
throw
new
WIKI
.
Error
.
CommentNotFound
()
}
const
page
=
await
WIKI
.
models
.
pages
.
query
().
select
(
'localeCode'
,
'path'
).
findById
(
cm
.
pageId
)
if
(
page
)
{
if
(
WIKI
.
auth
.
checkAccess
(
context
.
req
.
user
,
[
'read:comments'
],
{
path
:
page
.
path
,
locale
:
page
.
localeCode
}))
{
return
{
...
cm
,
authorName
:
cm
.
name
,
authorEmail
:
cm
.
email
,
authorIP
:
cm
.
ip
}
}
else
{
throw
new
WIKI
.
Error
.
CommentViewForbidden
()
}
}
else
{
WIKI
.
logger
.
warn
(
`Comment #
${
cm
.
id
}
is linked to a page #
${
cm
.
pageId
}
that doesn't exist! [ERROR]`
)
throw
new
WIKI
.
Error
.
CommentGenericError
()
}
}
},
CommentMutation
:
{
...
...
@@ -81,6 +106,24 @@ module.exports = {
}
},
/**
* Update an Existing Comment
*/
async
update
(
obj
,
args
,
context
)
{
try
{
const
cmRender
=
await
WIKI
.
models
.
comments
.
updateComment
({
...
args
,
user
:
context
.
req
.
user
,
ip
:
context
.
req
.
ip
})
return
{
responseResult
:
graphHelper
.
generateSuccess
(
'Comment updated successfully'
),
render
:
cmRender
}
}
catch
(
err
)
{
return
graphHelper
.
generateError
(
err
)
}
},
/**
* Delete an Existing Comment
*/
async
delete
(
obj
,
args
,
context
)
{
...
...
server/graph/schemas/comment.graphql
View file @
7a946ec0
...
...
@@ -47,7 +47,7 @@ type CommentMutation {
update
(
id
:
Int
!
content
:
String
!
):
Default
Response
@
auth
(
requires
:
[
"
write
:
comments
"
,
"
manage
:
comments
"
,
"
manage
:
system
"
])
):
CommentUpdate
Response
@
auth
(
requires
:
[
"
write
:
comments
"
,
"
manage
:
comments
"
,
"
manage
:
system
"
])
delete
(
id
:
Int
!
...
...
@@ -77,7 +77,7 @@ input CommentProviderInput {
type
CommentPost
{
id
:
Int
!
content
:
String
!
content
:
String
!
@
auth
(
requires
:
[
"
write
:
comments
"
,
"
manage
:
comments
"
,
"
manage
:
system
"
])
render
:
String
!
authorId
:
Int
!
authorName
:
String
!
...
...
@@ -91,3 +91,8 @@ type CommentCreateResponse {
responseResult
:
ResponseStatus
id
:
Int
}
type
CommentUpdateResponse
{
responseResult
:
ResponseStatus
render
:
String
}
server/helpers/error.js
View file @
7a946ec0
...
...
@@ -97,18 +97,14 @@ module.exports = {
message
:
'Too many attempts! Try again later.'
,
code
:
1008
}),
CommentGenericError
:
CustomError
(
'CommentGenericError'
,
{
message
:
'An unexpected error occured.'
,
code
:
8001
}),
CommentPostForbidden
:
CustomError
(
'CommentPostForbidden'
,
{
message
:
'You are not authorized to post a comment on this page.'
,
code
:
8002
}),
CommentContentMissing
:
CustomError
(
'CommentContentMissing'
,
{
message
:
'Comment content is missing or too short.'
,
code
:
8003
}),
CommentGenericError
:
CustomError
(
'CommentGenericError'
,
{
message
:
'An unexpected error occured.'
,
code
:
8001
}),
CommentManageForbidden
:
CustomError
(
'CommentManageForbidden'
,
{
message
:
'You are not authorized to manage comments on this page.'
,
code
:
8004
...
...
@@ -117,6 +113,14 @@ module.exports = {
message
:
'This comment does not exist.'
,
code
:
8005
}),
CommentPostForbidden
:
CustomError
(
'CommentPostForbidden'
,
{
message
:
'You are not authorized to post a comment on this page.'
,
code
:
8002
}),
CommentViewForbidden
:
CustomError
(
'CommentViewForbidden'
,
{
message
:
'You are not authorized to view comments for this page.'
,
code
:
8006
}),
InputInvalid
:
CustomError
(
'InputInvalid'
,
{
message
:
'Input data is invalid.'
,
code
:
1012
...
...
server/models/comments.js
View file @
7a946ec0
...
...
@@ -124,6 +124,39 @@ module.exports = class Comment extends Model {
}
/**
* Update an Existing Comment
*/
static
async
updateComment
({
id
,
content
,
user
,
ip
})
{
// -> Load Page
const
pageId
=
await
WIKI
.
data
.
commentProvider
.
getPageIdFromCommentId
(
id
)
if
(
!
pageId
)
{
throw
new
WIKI
.
Error
.
CommentNotFound
()
}
const
page
=
await
WIKI
.
models
.
pages
.
getPageFromDb
(
pageId
)
if
(
page
)
{
if
(
!
WIKI
.
auth
.
checkAccess
(
user
,
[
'manage:comments'
],
{
path
:
page
.
path
,
locale
:
page
.
localeCode
}))
{
throw
new
WIKI
.
Error
.
CommentManageForbidden
()
}
}
else
{
throw
new
WIKI
.
Error
.
PageNotFound
()
}
// -> Process by comment provider
return
WIKI
.
data
.
commentProvider
.
update
({
id
,
content
,
page
,
user
:
{
...
user
,
ip
}
})
}
/**
* Delete an Existing Comment
*/
static
async
deleteComment
({
id
,
user
,
ip
})
{
...
...
server/modules/comments/default/comment.js
View file @
7a946ec0
...
...
@@ -13,6 +13,17 @@ const DOMPurify = createDOMPurify(window)
let
akismetClient
=
null
const
mkdown
=
md
({
html
:
false
,
breaks
:
true
,
linkify
:
true
,
highlight
(
str
,
lang
)
{
return
`<pre><code class="language-
${
lang
}
">
${
_
.
escape
(
str
)}
</code></pre>`
}
})
mkdown
.
use
(
mdEmoji
)
// ------------------------------------
// Default Comment Provider
// ------------------------------------
...
...
@@ -51,18 +62,6 @@ module.exports = {
* 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>`
}
})
mkdown
.
use
(
mdEmoji
)
// -> Build New Comment
const
newComment
=
{
content
,
...
...
@@ -121,13 +120,20 @@ module.exports = {
// -> Return Comment ID
return
cm
.
id
},
async
update
({
id
,
content
,
user
,
ip
})
{
/**
* Update an existing comment
*/
async
update
({
id
,
content
,
user
})
{
const
renderedContent
=
DOMPurify
.
sanitize
(
mkdown
.
render
(
content
))
await
WIKI
.
models
.
comments
.
query
().
findById
(
id
).
patch
({
render
:
renderedContent
})
return
renderedContent
},
/**
* Delete an existing comment by ID
*/
async
remove
({
id
,
user
,
ip
})
{
async
remove
({
id
,
user
})
{
return
WIKI
.
models
.
comments
.
query
().
findById
(
id
).
delete
()
},
/**
...
...
@@ -138,6 +144,12 @@ module.exports = {
return
(
result
)
?
result
.
pageId
:
false
},
/**
* Get a comment by ID
*/
async
getCommentById
(
id
)
{
return
WIKI
.
models
.
comments
.
query
().
findById
(
id
)
},
/**
* Get the total comments count for a page ID
*/
async
count
(
pageId
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment