aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/helpers/custom-validators/activitypub/video-comments.ts18
-rw-r--r--server/lib/activitypub/send/send-delete.ts5
-rw-r--r--server/lib/activitypub/video-comments.ts5
-rw-r--r--server/middlewares/validators/videos/video-comments.ts7
-rw-r--r--server/tests/api/server/follows.ts149
-rw-r--r--server/tests/api/videos/multiple-servers.ts46
6 files changed, 131 insertions, 99 deletions
diff --git a/server/helpers/custom-validators/activitypub/video-comments.ts b/server/helpers/custom-validators/activitypub/video-comments.ts
index 96655c3f8..ea780ca46 100644
--- a/server/helpers/custom-validators/activitypub/video-comments.ts
+++ b/server/helpers/custom-validators/activitypub/video-comments.ts
@@ -3,18 +3,10 @@ import { ACTIVITY_PUB } from '../../../initializers/constants'
3import { exists, isArray, isDateValid } from '../misc' 3import { exists, isArray, isDateValid } from '../misc'
4import { isActivityPubUrlValid } from './misc' 4import { isActivityPubUrlValid } from './misc'
5 5
6function isTypeValid (comment: any): boolean {
7 if (comment.type === 'Note') return true
8
9 if (comment.type === 'Tombstone' && comment.formerType === 'Note') return true
10
11 return false
12}
13
14function sanitizeAndCheckVideoCommentObject (comment: any) { 6function sanitizeAndCheckVideoCommentObject (comment: any) {
15 if (!comment) return false 7 if (!comment) return false
16 8
17 if (!isTypeValid(comment)) return false 9 if (!isCommentTypeValid(comment)) return false
18 10
19 normalizeComment(comment) 11 normalizeComment(comment)
20 12
@@ -59,3 +51,11 @@ function normalizeComment (comment: any) {
59 51
60 return 52 return
61} 53}
54
55function isCommentTypeValid (comment: any): boolean {
56 if (comment.type === 'Note') return true
57
58 if (comment.type === 'Tombstone' && comment.formerType === 'Note') return true
59
60 return false
61}
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts
index a91756ff4..3225ebf32 100644
--- a/server/lib/activitypub/send/send-delete.ts
+++ b/server/lib/activitypub/send/send-delete.ts
@@ -53,16 +53,17 @@ async function sendDeleteVideoComment (videoComment: MCommentOwnerVideoReply, t:
53 : videoComment.Video.VideoChannel.Account.Actor 53 : videoComment.Video.VideoChannel.Account.Actor
54 54
55 const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, t) 55 const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, t)
56 const threadParentCommentsFiltered = threadParentComments.filter(c => !c.isDeleted())
56 57
57 const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, t) 58 const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, t)
58 actorsInvolvedInComment.push(byActor) // Add the actor that commented the video 59 actorsInvolvedInComment.push(byActor) // Add the actor that commented the video
59 60
60 const audience = getVideoCommentAudience(videoComment, threadParentComments, actorsInvolvedInComment, isVideoOrigin) 61 const audience = getVideoCommentAudience(videoComment, threadParentCommentsFiltered, actorsInvolvedInComment, isVideoOrigin)
61 const activity = buildDeleteActivity(url, videoComment.url, byActor, audience) 62 const activity = buildDeleteActivity(url, videoComment.url, byActor, audience)
62 63
63 // This was a reply, send it to the parent actors 64 // This was a reply, send it to the parent actors
64 const actorsException = [ byActor ] 65 const actorsException = [ byActor ]
65 await broadcastToActors(activity, byActor, threadParentComments.map(c => c.Account.Actor), t, actorsException) 66 await broadcastToActors(activity, byActor, threadParentCommentsFiltered.map(c => c.Account.Actor), t, actorsException)
66 67
67 // Broadcast to our followers 68 // Broadcast to our followers
68 await broadcastToFollowers(activity, byActor, [ byActor ], t) 69 await broadcastToFollowers(activity, byActor, [ byActor ], t)
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 1a15842cf..d5c078a29 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -141,7 +141,10 @@ async function resolveParentComment (params: ResolveThreadParams) {
141 throw new Error(`Comment url ${url} host is different from the AP object id ${body.id}`) 141 throw new Error(`Comment url ${url} host is different from the AP object id ${body.id}`)
142 } 142 }
143 143
144 const actor = actorUrl ? await getOrCreateActorAndServerAndModel(actorUrl, 'all') : null 144 const actor = actorUrl
145 ? await getOrCreateActorAndServerAndModel(actorUrl, 'all')
146 : null
147
145 const comment = new VideoCommentModel({ 148 const comment = new VideoCommentModel({
146 url: body.id, 149 url: body.id,
147 text: body.content ? body.content : '', 150 text: body.content ? body.content : '',
diff --git a/server/middlewares/validators/videos/video-comments.ts b/server/middlewares/validators/videos/video-comments.ts
index 1d81eb5d8..eb07d9430 100644
--- a/server/middlewares/validators/videos/video-comments.ts
+++ b/server/middlewares/validators/videos/video-comments.ts
@@ -189,6 +189,13 @@ function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
189} 189}
190 190
191function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) { 191function checkUserCanDeleteVideoComment (user: MUser, videoComment: MCommentOwner, res: express.Response) {
192 if (videoComment.isDeleted()) {
193 res.status(409)
194 .json({ error: 'This comment is already deleted' })
195 .end()
196 return false
197 }
198
192 const account = videoComment.Account 199 const account = videoComment.Account
193 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) { 200 if (user.hasRight(UserRight.REMOVE_ANY_VIDEO_COMMENT) === false && account.userId !== user.id) {
194 res.status(403) 201 res.status(403)
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index dd85722a0..60dbe2e6d 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -4,7 +4,7 @@ import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { Video, VideoPrivacy } from '../../../../shared/models/videos' 5import { Video, VideoPrivacy } from '../../../../shared/models/videos'
6import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' 6import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
7import { cleanupTests, completeVideoCheck } from '../../../../shared/extra-utils' 7import { cleanupTests, completeVideoCheck, deleteVideoComment } from '../../../../shared/extra-utils'
8import { 8import {
9 flushAndRunMultipleServers, 9 flushAndRunMultipleServers,
10 getVideosList, 10 getVideosList,
@@ -356,19 +356,40 @@ describe('Test follows', function () {
356 } 356 }
357 357
358 { 358 {
359 const text = 'my super first comment' 359 {
360 const res = await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, text) 360 const text = 'my super first comment'
361 const threadId = res.body.comment.id 361 const res = await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, text)
362 const threadId = res.body.comment.id
363
364 const text1 = 'my super answer to thread 1'
365 const childCommentRes = await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text1)
366 const childCommentId = childCommentRes.body.comment.id
367
368 const text2 = 'my super answer to answer of thread 1'
369 await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, childCommentId, text2)
370
371 const text3 = 'my second answer to thread 1'
372 await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text3)
373 }
374
375 {
376 const text = 'will be deleted'
377 const res = await addVideoCommentThread(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, text)
378 const threadId = res.body.comment.id
379
380 const text1 = 'answer to deleted'
381 await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text1)
362 382
363 const text1 = 'my super answer to thread 1' 383 const text2 = 'will also be deleted'
364 const childCommentRes = await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text1) 384 const childCommentRes = await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text2)
365 const childCommentId = childCommentRes.body.comment.id 385 const childCommentId = childCommentRes.body.comment.id
366 386
367 const text2 = 'my super answer to answer of thread 1' 387 const text3 = 'my second answer to deleted'
368 await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, childCommentId, text2) 388 await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, childCommentId, text3)
369 389
370 const text3 = 'my second answer to thread 1' 390 await deleteVideoComment(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId)
371 await addVideoCommentReply(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, threadId, text3) 391 await deleteVideoComment(servers[ 2 ].url, servers[ 2 ].accessToken, video4.id, childCommentId)
392 }
372 } 393 }
373 394
374 { 395 {
@@ -453,42 +474,80 @@ describe('Test follows', function () {
453 }) 474 })
454 475
455 it('Should have propagated comments', async function () { 476 it('Should have propagated comments', async function () {
456 const res1 = await getVideoCommentThreads(servers[0].url, video4.id, 0, 5) 477 const res1 = await getVideoCommentThreads(servers[0].url, video4.id, 0, 5, 'createdAt')
457 478
458 expect(res1.body.total).to.equal(1) 479 expect(res1.body.total).to.equal(2)
459 expect(res1.body.data).to.be.an('array') 480 expect(res1.body.data).to.be.an('array')
460 expect(res1.body.data).to.have.lengthOf(1) 481 expect(res1.body.data).to.have.lengthOf(2)
461 482
462 const comment: VideoComment = res1.body.data[0] 483 {
463 expect(comment.inReplyToCommentId).to.be.null 484 const comment: VideoComment = res1.body.data[ 0 ]
464 expect(comment.text).equal('my super first comment') 485 expect(comment.inReplyToCommentId).to.be.null
465 expect(comment.videoId).to.equal(video4.id) 486 expect(comment.text).equal('my super first comment')
466 expect(comment.id).to.equal(comment.threadId) 487 expect(comment.videoId).to.equal(video4.id)
467 expect(comment.account.name).to.equal('root') 488 expect(comment.id).to.equal(comment.threadId)
468 expect(comment.account.host).to.equal('localhost:' + servers[2].port) 489 expect(comment.account.name).to.equal('root')
469 expect(comment.totalReplies).to.equal(3) 490 expect(comment.account.host).to.equal('localhost:' + servers[ 2 ].port)
470 expect(dateIsValid(comment.createdAt as string)).to.be.true 491 expect(comment.totalReplies).to.equal(3)
471 expect(dateIsValid(comment.updatedAt as string)).to.be.true 492 expect(dateIsValid(comment.createdAt as string)).to.be.true
472 493 expect(dateIsValid(comment.updatedAt as string)).to.be.true
473 const threadId = comment.threadId 494
474 495 const threadId = comment.threadId
475 const res2 = await getVideoThreadComments(servers[0].url, video4.id, threadId) 496
476 497 const res2 = await getVideoThreadComments(servers[ 0 ].url, video4.id, threadId)
477 const tree: VideoCommentThreadTree = res2.body 498
478 expect(tree.comment.text).equal('my super first comment') 499 const tree: VideoCommentThreadTree = res2.body
479 expect(tree.children).to.have.lengthOf(2) 500 expect(tree.comment.text).equal('my super first comment')
480 501 expect(tree.children).to.have.lengthOf(2)
481 const firstChild = tree.children[0] 502
482 expect(firstChild.comment.text).to.equal('my super answer to thread 1') 503 const firstChild = tree.children[ 0 ]
483 expect(firstChild.children).to.have.lengthOf(1) 504 expect(firstChild.comment.text).to.equal('my super answer to thread 1')
484 505 expect(firstChild.children).to.have.lengthOf(1)
485 const childOfFirstChild = firstChild.children[0] 506
486 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') 507 const childOfFirstChild = firstChild.children[ 0 ]
487 expect(childOfFirstChild.children).to.have.lengthOf(0) 508 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
488 509 expect(childOfFirstChild.children).to.have.lengthOf(0)
489 const secondChild = tree.children[1] 510
490 expect(secondChild.comment.text).to.equal('my second answer to thread 1') 511 const secondChild = tree.children[ 1 ]
491 expect(secondChild.children).to.have.lengthOf(0) 512 expect(secondChild.comment.text).to.equal('my second answer to thread 1')
513 expect(secondChild.children).to.have.lengthOf(0)
514 }
515
516 {
517 const deletedComment: VideoComment = res1.body.data[1]
518 expect(deletedComment).to.not.be.undefined
519 expect(deletedComment.isDeleted).to.be.true
520 expect(deletedComment.deletedAt).to.not.be.null
521 expect(deletedComment.text).to.equal('')
522 expect(deletedComment.inReplyToCommentId).to.be.null
523 expect(deletedComment.account).to.be.null
524 expect(deletedComment.totalReplies).to.equal(3)
525 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
526
527 const res2 = await getVideoThreadComments(servers[0].url, video4.id, deletedComment.threadId)
528
529 const tree: VideoCommentThreadTree = res2.body
530 const [ commentRoot, deletedChildRoot ] = tree.children
531
532 expect(deletedChildRoot).to.not.be.undefined
533 expect(deletedChildRoot.comment.isDeleted).to.be.true
534 expect(deletedChildRoot.comment.deletedAt).to.not.be.null
535 expect(deletedChildRoot.comment.text).to.equal('')
536 expect(deletedChildRoot.comment.inReplyToCommentId).to.equal(deletedComment.id)
537 expect(deletedChildRoot.comment.account).to.be.null
538 expect(deletedChildRoot.children).to.have.lengthOf(1)
539
540 const answerToDeletedChild = deletedChildRoot.children[0]
541 expect(answerToDeletedChild.comment).to.not.be.undefined
542 expect(answerToDeletedChild.comment.inReplyToCommentId).to.equal(deletedChildRoot.comment.id)
543 expect(answerToDeletedChild.comment.text).to.equal('my second answer to deleted')
544 expect(answerToDeletedChild.comment.account.name).to.equal('root')
545
546 expect(commentRoot.comment).to.not.be.undefined
547 expect(commentRoot.comment.inReplyToCommentId).to.equal(deletedComment.id)
548 expect(commentRoot.comment.text).to.equal('answer to deleted')
549 expect(commentRoot.comment.account.name).to.equal('root')
550 }
492 }) 551 })
493 552
494 it('Should have propagated captions', async function () { 553 it('Should have propagated captions', async function () {
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index 836bdc658..22d87b88c 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -517,6 +517,8 @@ describe('Test multiple servers', function () {
517 // Wait the repeatable job 517 // Wait the repeatable job
518 await wait(6000) 518 await wait(6000)
519 519
520 await waitJobs(servers)
521
520 for (const server of servers) { 522 for (const server of servers) {
521 const res = await getVideosList(server.url) 523 const res = await getVideosList(server.url)
522 524
@@ -551,6 +553,8 @@ describe('Test multiple servers', function () {
551 // Wait the repeatable job 553 // Wait the repeatable job
552 await wait(16000) 554 await wait(16000)
553 555
556 await waitJobs(servers)
557
554 let baseVideos = null 558 let baseVideos = null
555 559
556 for (const server of servers) { 560 for (const server of servers) {
@@ -939,48 +943,6 @@ describe('Test multiple servers', function () {
939 } 943 }
940 }) 944 })
941 945
942 it('Should retrieve all comments when subscribing to a new server', async function () {
943 this.timeout(120000)
944
945 const newServer = await flushAndRunServer(4)
946
947 await setAccessTokensToServers([newServer])
948 await doubleFollow(newServer, servers[0])
949 await doubleFollow(newServer, servers[2])
950 await waitJobs([newServer, ...servers])
951
952 const res = await getVideoCommentThreads(newServer.url, videoUUID, 0, 5)
953
954 expect(res.body.total).to.equal(2)
955 expect(res.body.data).to.be.an('array')
956 expect(res.body.data).to.have.lengthOf(2)
957
958 {
959 const comment: VideoComment = res.body.data[0]
960 expect(comment).to.not.be.undefined
961 expect(comment.inReplyToCommentId).to.be.null
962 expect(comment.account.name).to.equal('root')
963 expect(comment.account.host).to.equal('localhost:' + servers[2].port)
964 expect(comment.totalReplies).to.equal(0)
965 expect(dateIsValid(comment.createdAt as string)).to.be.true
966 expect(dateIsValid(comment.updatedAt as string)).to.be.true
967 }
968
969 {
970 const deletedComment: VideoComment = res.body.data[1]
971 expect(deletedComment).to.not.be.undefined
972 expect(deletedComment.isDeleted).to.be.true
973 expect(deletedComment.deletedAt).to.not.be.null
974 expect(deletedComment.text).to.equal('')
975 expect(deletedComment.inReplyToCommentId).to.be.null
976 expect(deletedComment.account).to.be.null
977 expect(deletedComment.totalReplies).to.equal(3)
978 expect(dateIsValid(deletedComment.createdAt as string)).to.be.true
979 expect(dateIsValid(deletedComment.updatedAt as string)).to.be.true
980 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
981 }
982 })
983
984 it('Should delete a remote thread by the origin server', async function () { 946 it('Should delete a remote thread by the origin server', async function () {
985 this.timeout(5000) 947 this.timeout(5000)
986 948