diff options
-rw-r--r-- | server/lib/activitypub/process/process-delete.ts | 15 | ||||
-rw-r--r-- | server/lib/activitypub/send/misc.ts | 6 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-create.ts | 8 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-delete.ts | 40 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-like.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-undo.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-update.ts | 2 | ||||
-rw-r--r-- | server/tests/api/videos/multiple-servers.ts | 31 |
8 files changed, 80 insertions, 26 deletions
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 03eadcbfc..b58f6c04a 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -8,6 +8,7 @@ import { VideoModel } from '../../../models/video/video' | |||
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | 8 | import { VideoChannelModel } from '../../../models/video/video-channel' |
9 | import { VideoCommentModel } from '../../../models/video/video-comment' | 9 | import { VideoCommentModel } from '../../../models/video/video-comment' |
10 | import { getOrCreateActorAndServerAndModel } from '../actor' | 10 | import { getOrCreateActorAndServerAndModel } from '../actor' |
11 | import { forwardActivity } from '../send/misc' | ||
11 | 12 | ||
12 | async function processDeleteActivity (activity: ActivityDelete) { | 13 | async function processDeleteActivity (activity: ActivityDelete) { |
13 | const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id | 14 | const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id |
@@ -33,7 +34,7 @@ async function processDeleteActivity (activity: ActivityDelete) { | |||
33 | { | 34 | { |
34 | const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl) | 35 | const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl) |
35 | if (videoCommentInstance) { | 36 | if (videoCommentInstance) { |
36 | return processDeleteVideoComment(actor, videoCommentInstance) | 37 | return processDeleteVideoComment(actor, videoCommentInstance, activity) |
37 | } | 38 | } |
38 | } | 39 | } |
39 | 40 | ||
@@ -116,21 +117,27 @@ async function deleteRemoteVideoChannel (videoChannelToRemove: VideoChannelModel | |||
116 | logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid) | 117 | logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid) |
117 | } | 118 | } |
118 | 119 | ||
119 | async function processDeleteVideoComment (actor: ActorModel, videoComment: VideoCommentModel) { | 120 | async function processDeleteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { |
120 | const options = { | 121 | const options = { |
121 | arguments: [ actor, videoComment ], | 122 | arguments: [ byActor, videoComment, activity ], |
122 | errorMessage: 'Cannot remove the remote video comment with many retries.' | 123 | errorMessage: 'Cannot remove the remote video comment with many retries.' |
123 | } | 124 | } |
124 | 125 | ||
125 | await retryTransactionWrapper(deleteRemoteVideoComment, options) | 126 | await retryTransactionWrapper(deleteRemoteVideoComment, options) |
126 | } | 127 | } |
127 | 128 | ||
128 | function deleteRemoteVideoComment (actor: ActorModel, videoComment: VideoCommentModel) { | 129 | function deleteRemoteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { |
129 | logger.debug('Removing remote video comment "%s".', videoComment.url) | 130 | logger.debug('Removing remote video comment "%s".', videoComment.url) |
130 | 131 | ||
131 | return sequelizeTypescript.transaction(async t => { | 132 | return sequelizeTypescript.transaction(async t => { |
132 | await videoComment.destroy({ transaction: t }) | 133 | await videoComment.destroy({ transaction: t }) |
133 | 134 | ||
135 | if (videoComment.Video.isOwned()) { | ||
136 | // Don't resend the activity to the sender | ||
137 | const exceptions = [ byActor ] | ||
138 | await forwardActivity(activity, t, exceptions) | ||
139 | } | ||
140 | |||
134 | logger.info('Remote video comment %s removed.', videoComment.url) | 141 | logger.info('Remote video comment %s removed.', videoComment.url) |
135 | }) | 142 | }) |
136 | } | 143 | } |
diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts index b20fcf124..b2d968c9c 100644 --- a/server/lib/activitypub/send/misc.ts +++ b/server/lib/activitypub/send/misc.ts | |||
@@ -96,7 +96,7 @@ function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor | |||
96 | } | 96 | } |
97 | } | 97 | } |
98 | 98 | ||
99 | function getOriginVideoCommentAudience ( | 99 | function getVideoCommentAudience ( |
100 | videoComment: VideoCommentModel, | 100 | videoComment: VideoCommentModel, |
101 | threadParentComments: VideoCommentModel[], | 101 | threadParentComments: VideoCommentModel[], |
102 | actorsInvolvedInVideo: ActorModel[], | 102 | actorsInvolvedInVideo: ActorModel[], |
@@ -160,7 +160,7 @@ function buildAudience (followerInboxUrls: string[], isPublic = true) { | |||
160 | return { to, cc } | 160 | return { to, cc } |
161 | } | 161 | } |
162 | 162 | ||
163 | function audiencify (object: any, audience: ActivityAudience) { | 163 | function audiencify <T> (object: T, audience: ActivityAudience) { |
164 | return Object.assign(object, audience) | 164 | return Object.assign(object, audience) |
165 | } | 165 | } |
166 | 166 | ||
@@ -192,7 +192,7 @@ export { | |||
192 | getObjectFollowersAudience, | 192 | getObjectFollowersAudience, |
193 | forwardActivity, | 193 | forwardActivity, |
194 | audiencify, | 194 | audiencify, |
195 | getOriginVideoCommentAudience, | 195 | getVideoCommentAudience, |
196 | computeUris, | 196 | computeUris, |
197 | broadcastToActors | 197 | broadcastToActors |
198 | } | 198 | } |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 2fa008453..d73dd8d24 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -15,7 +15,7 @@ import { | |||
15 | getAudience, | 15 | getAudience, |
16 | getObjectFollowersAudience, | 16 | getObjectFollowersAudience, |
17 | getOriginVideoAudience, | 17 | getOriginVideoAudience, |
18 | getOriginVideoCommentAudience, | 18 | getVideoCommentAudience, |
19 | unicastTo | 19 | unicastTo |
20 | } from './misc' | 20 | } from './misc' |
21 | 21 | ||
@@ -47,7 +47,7 @@ async function sendCreateVideoCommentToOrigin (comment: VideoCommentModel, t: Tr | |||
47 | 47 | ||
48 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) | 48 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) |
49 | actorsInvolvedInComment.push(byActor) | 49 | actorsInvolvedInComment.push(byActor) |
50 | const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) | 50 | const audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) |
51 | 51 | ||
52 | const data = await createActivityData(comment.url, byActor, commentObject, t, audience) | 52 | const data = await createActivityData(comment.url, byActor, commentObject, t, audience) |
53 | 53 | ||
@@ -70,7 +70,7 @@ async function sendCreateVideoCommentToVideoFollowers (comment: VideoCommentMode | |||
70 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) | 70 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) |
71 | actorsInvolvedInComment.push(byActor) | 71 | actorsInvolvedInComment.push(byActor) |
72 | 72 | ||
73 | const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) | 73 | const audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment, true) |
74 | const data = await createActivityData(comment.url, byActor, commentObject, t, audience) | 74 | const data = await createActivityData(comment.url, byActor, commentObject, t, audience) |
75 | 75 | ||
76 | // This was a reply, send it to the parent actors | 76 | // This was a reply, send it to the parent actors |
@@ -144,7 +144,7 @@ async function createActivityData ( | |||
144 | } | 144 | } |
145 | 145 | ||
146 | return audiencify({ | 146 | return audiencify({ |
147 | type: 'Create', | 147 | type: 'Create' as 'Create', |
148 | id: url + '/activity', | 148 | id: url + '/activity', |
149 | actor: byActor.url, | 149 | actor: byActor.url, |
150 | object: audiencify(object, audience) | 150 | object: audiencify(object, audience) |
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index 9f1ea3bd0..bb5d6913c 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityDelete } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' |
3 | import { ActorModel } from '../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { VideoModel } from '../../../models/video/video' | 4 | import { VideoModel } from '../../../models/video/video' |
5 | import { VideoCommentModel } from '../../../models/video/video-comment' | 5 | import { VideoCommentModel } from '../../../models/video/video-comment' |
6 | import { VideoShareModel } from '../../../models/video/video-share' | 6 | import { VideoShareModel } from '../../../models/video/video-share' |
7 | import { getDeleteActivityPubUrl } from '../url' | 7 | import { getDeleteActivityPubUrl } from '../url' |
8 | import { broadcastToFollowers } from './misc' | 8 | import { audiencify, broadcastToActors, broadcastToFollowers, getActorsInvolvedInVideo, getVideoCommentAudience, unicastTo } from './misc' |
9 | 9 | ||
10 | async function sendDeleteVideo (video: VideoModel, t: Transaction) { | 10 | async function sendDeleteVideo (video: VideoModel, t: Transaction) { |
11 | const url = getDeleteActivityPubUrl(video.url) | 11 | const url = getDeleteActivityPubUrl(video.url) |
@@ -30,16 +30,30 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { | 32 | async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { |
33 | const url = getDeleteActivityPubUrl(videoComment.url) | 33 | const isVideoOrigin = videoComment.Video.isOwned() |
34 | 34 | ||
35 | const url = getDeleteActivityPubUrl(videoComment.url) | ||
35 | const byActor = videoComment.Account.Actor | 36 | const byActor = videoComment.Account.Actor |
36 | const data = deleteActivityData(url, videoComment.url, byActor) | 37 | const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, t) |
37 | 38 | ||
38 | const actorsInvolved = await VideoShareModel.loadActorsByShare(videoComment.Video.id, t) | 39 | const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, t) |
39 | actorsInvolved.push(videoComment.Video.VideoChannel.Account.Actor) | 40 | actorsInvolvedInComment.push(byActor) |
40 | actorsInvolved.push(byActor) | ||
41 | 41 | ||
42 | return broadcastToFollowers(data, byActor, actorsInvolved, t) | 42 | const audience = getVideoCommentAudience(videoComment, threadParentComments, actorsInvolvedInComment, isVideoOrigin) |
43 | const data = deleteActivityData(url, videoComment.url, byActor, audience) | ||
44 | |||
45 | // This was a reply, send it to the parent actors | ||
46 | const actorsException = [ byActor ] | ||
47 | await broadcastToActors(data, byActor, threadParentComments.map(c => c.Account.Actor), actorsException) | ||
48 | |||
49 | // Broadcast to our followers | ||
50 | await broadcastToFollowers(data, byActor, [ byActor ], t) | ||
51 | |||
52 | // Send to actors involved in the comment | ||
53 | if (isVideoOrigin) return broadcastToFollowers(data, byActor, actorsInvolvedInComment, t, actorsException) | ||
54 | |||
55 | // Send to origin | ||
56 | return unicastTo(data, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl) | ||
43 | } | 57 | } |
44 | 58 | ||
45 | // --------------------------------------------------------------------------- | 59 | // --------------------------------------------------------------------------- |
@@ -52,11 +66,15 @@ export { | |||
52 | 66 | ||
53 | // --------------------------------------------------------------------------- | 67 | // --------------------------------------------------------------------------- |
54 | 68 | ||
55 | function deleteActivityData (url: string, object: string, byActor: ActorModel): ActivityDelete { | 69 | function deleteActivityData (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete { |
56 | return { | 70 | const activity = { |
57 | type: 'Delete', | 71 | type: 'Delete' as 'Delete', |
58 | id: url, | 72 | id: url, |
59 | actor: byActor.url, | 73 | actor: byActor.url, |
60 | object | 74 | object |
61 | } | 75 | } |
76 | |||
77 | if (audience) return audiencify(activity, audience) | ||
78 | |||
79 | return activity | ||
62 | } | 80 | } |
diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index 78ed1aaf2..b01249e69 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts | |||
@@ -46,7 +46,7 @@ async function likeActivityData ( | |||
46 | } | 46 | } |
47 | 47 | ||
48 | return audiencify({ | 48 | return audiencify({ |
49 | type: 'Like', | 49 | type: 'Like' as 'Like', |
50 | id: url, | 50 | id: url, |
51 | actor: byActor.url, | 51 | actor: byActor.url, |
52 | object: video.url | 52 | object: video.url |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 4a08b5ca1..41a500384 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -108,7 +108,7 @@ async function undoActivityData ( | |||
108 | } | 108 | } |
109 | 109 | ||
110 | return audiencify({ | 110 | return audiencify({ |
111 | type: 'Undo', | 111 | type: 'Undo' as 'Undo', |
112 | id: url, | 112 | id: url, |
113 | actor: byActor.url, | 113 | actor: byActor.url, |
114 | object | 114 | object |
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 622648308..e15fecff6 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts | |||
@@ -67,7 +67,7 @@ async function updateActivityData ( | |||
67 | } | 67 | } |
68 | 68 | ||
69 | return audiencify({ | 69 | return audiencify({ |
70 | type: 'Update', | 70 | type: 'Update' as 'Update', |
71 | id: url, | 71 | id: url, |
72 | actor: byActor.url, | 72 | actor: byActor.url, |
73 | object: audiencify(object, audience) | 73 | object: audiencify(object, audience) |
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index 74c6b8462..e31c7febd 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts | |||
@@ -728,6 +728,8 @@ describe('Test multiple servers', function () { | |||
728 | }) | 728 | }) |
729 | 729 | ||
730 | describe('Should comment these videos', function () { | 730 | describe('Should comment these videos', function () { |
731 | let childOfFirstChild: VideoCommentThreadTree | ||
732 | |||
731 | it('Should add comment (threads and replies)', async function () { | 733 | it('Should add comment (threads and replies)', async function () { |
732 | this.timeout(25000) | 734 | this.timeout(25000) |
733 | 735 | ||
@@ -821,7 +823,7 @@ describe('Test multiple servers', function () { | |||
821 | expect(firstChild.comment.account.host).equal('localhost:9002') | 823 | expect(firstChild.comment.account.host).equal('localhost:9002') |
822 | expect(firstChild.children).to.have.lengthOf(1) | 824 | expect(firstChild.children).to.have.lengthOf(1) |
823 | 825 | ||
824 | const childOfFirstChild = firstChild.children[0] | 826 | childOfFirstChild = firstChild.children[0] |
825 | expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') | 827 | expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') |
826 | expect(childOfFirstChild.comment.account.name).equal('root') | 828 | expect(childOfFirstChild.comment.account.name).equal('root') |
827 | expect(childOfFirstChild.comment.account.host).equal('localhost:9003') | 829 | expect(childOfFirstChild.comment.account.host).equal('localhost:9003') |
@@ -835,6 +837,33 @@ describe('Test multiple servers', function () { | |||
835 | } | 837 | } |
836 | }) | 838 | }) |
837 | 839 | ||
840 | it('Should delete a reply', async function () { | ||
841 | this.timeout(10000) | ||
842 | |||
843 | await deleteVideoComment(servers[2].url, servers[2].accessToken, videoUUID, childOfFirstChild.comment.id) | ||
844 | |||
845 | await wait(5000) | ||
846 | }) | ||
847 | |||
848 | it('Should not have this comment anymore', async function () { | ||
849 | for (const server of servers) { | ||
850 | const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) | ||
851 | const threadId = res1.body.data.find(c => c.text === 'my super first comment').id | ||
852 | |||
853 | const res2 = await getVideoThreadComments(server.url, videoUUID, threadId) | ||
854 | |||
855 | const tree: VideoCommentThreadTree = res2.body | ||
856 | expect(tree.comment.text).equal('my super first comment') | ||
857 | |||
858 | const firstChild = tree.children[0] | ||
859 | expect(firstChild.comment.text).to.equal('my super answer to thread 1') | ||
860 | expect(firstChild.children).to.have.lengthOf(0) | ||
861 | |||
862 | const secondChild = tree.children[1] | ||
863 | expect(secondChild.comment.text).to.equal('my second answer to thread 1') | ||
864 | } | ||
865 | }) | ||
866 | |||
838 | it('Should delete the thread comments', async function () { | 867 | it('Should delete the thread comments', async function () { |
839 | this.timeout(10000) | 868 | this.timeout(10000) |
840 | 869 | ||