aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/api/videos/comment.ts4
-rw-r--r--server/lib/activitypub/process/process-create.ts36
-rw-r--r--server/lib/activitypub/send/send-create.ts71
-rw-r--r--server/lib/video-comment.ts26
-rw-r--r--server/models/video/video-comment.ts32
5 files changed, 118 insertions, 51 deletions
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index ac64f0154..e9dbb6d1b 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -78,7 +78,7 @@ function addVideoCommentThread (req: express.Request, res: express.Response) {
78 return sequelizeTypescript.transaction(async t => { 78 return sequelizeTypescript.transaction(async t => {
79 return createVideoComment({ 79 return createVideoComment({
80 text: videoCommentInfo.text, 80 text: videoCommentInfo.text,
81 inReplyToCommentId: null, 81 inReplyToComment: null,
82 video: res.locals.video, 82 video: res.locals.video,
83 accountId: res.locals.oauth.token.User.Account.id 83 accountId: res.locals.oauth.token.User.Account.id
84 }, t) 84 }, t)
@@ -106,7 +106,7 @@ function addVideoCommentReply (req: express.Request, res: express.Response, next
106 return sequelizeTypescript.transaction(async t => { 106 return sequelizeTypescript.transaction(async t => {
107 return createVideoComment({ 107 return createVideoComment({
108 text: videoCommentInfo.text, 108 text: videoCommentInfo.text,
109 inReplyToCommentId: res.locals.videoComment.id, 109 inReplyToComment: res.locals.videoComment,
110 video: res.locals.video, 110 video: res.locals.video,
111 accountId: res.locals.oauth.token.User.Account.id 111 accountId: res.locals.oauth.token.User.Account.id
112 }, t) 112 }, t)
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index 6c2ee97eb..628942a58 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -257,11 +257,11 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) {
257 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) 257 if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
258 258
259 return sequelizeTypescript.transaction(async t => { 259 return sequelizeTypescript.transaction(async t => {
260 const video = await VideoModel.loadByUrl(comment.inReplyTo, t) 260 let video = await VideoModel.loadByUrl(comment.inReplyTo, t)
261 261
262 // This is a new thread 262 // This is a new thread
263 if (video) { 263 if (video) {
264 return VideoCommentModel.create({ 264 await VideoCommentModel.create({
265 url: comment.id, 265 url: comment.id,
266 text: comment.content, 266 text: comment.content,
267 originCommentId: null, 267 originCommentId: null,
@@ -269,19 +269,27 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) {
269 videoId: video.id, 269 videoId: video.id,
270 accountId: byAccount.id 270 accountId: byAccount.id
271 }, { transaction: t }) 271 }, { transaction: t })
272 } 272 } else {
273 const inReplyToComment = await VideoCommentModel.loadByUrl(comment.inReplyTo, t)
274 if (!inReplyToComment) throw new Error('Unknown replied comment ' + comment.inReplyTo)
273 275
274 const inReplyToComment = await VideoCommentModel.loadByUrl(comment.inReplyTo, t) 276 video = await VideoModel.load(inReplyToComment.videoId)
275 if (!inReplyToComment) throw new Error('Unknown replied comment ' + comment.inReplyTo)
276 277
277 const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id 278 const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id
278 return VideoCommentModel.create({ 279 await VideoCommentModel.create({
279 url: comment.id, 280 url: comment.id,
280 text: comment.content, 281 text: comment.content,
281 originCommentId, 282 originCommentId,
282 inReplyToCommentId: inReplyToComment.id, 283 inReplyToCommentId: inReplyToComment.id,
283 videoId: inReplyToComment.videoId, 284 videoId: video.id,
284 accountId: byAccount.id 285 accountId: byAccount.id
285 }, { transaction: t }) 286 }, { transaction: t })
287 }
288
289 if (video.isOwned()) {
290 // Don't resend the activity to the sender
291 const exceptions = [ byActor ]
292 await forwardActivity(activity, t, exceptions)
293 }
286 }) 294 })
287} 295}
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
index 27b03c45f..ca50460be 100644
--- a/server/lib/activitypub/send/send-create.ts
+++ b/server/lib/activitypub/send/send-create.ts
@@ -5,14 +5,10 @@ import { getServerActor } from '../../../helpers'
5import { ActorModel } from '../../../models/activitypub/actor' 5import { ActorModel } from '../../../models/activitypub/actor'
6import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
7import { VideoAbuseModel } from '../../../models/video/video-abuse' 7import { VideoAbuseModel } from '../../../models/video/video-abuse'
8import { VideoCommentModel } from '../../../models/video/video-comment'
8import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url' 9import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
9import { 10import {
10 audiencify, 11 audiencify, broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getOriginVideoAudience,
11 broadcastToFollowers,
12 getActorsInvolvedInVideo,
13 getAudience,
14 getObjectFollowersAudience,
15 getOriginVideoAudience,
16 unicastTo 12 unicastTo
17} from './misc' 13} from './misc'
18 14
@@ -37,24 +33,49 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
37 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) 33 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
38} 34}
39 35
36async function sendCreateVideoCommentToOrigin (comment: VideoCommentModel, t: Transaction) {
37 const byActor = comment.Account.Actor
38
39 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(comment.Video, t)
40 const audience = getOriginVideoAudience(comment.Video, actorsInvolvedInVideo)
41
42 const commentObject = comment.toActivityPubObject()
43 const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
44
45 return unicastTo(data, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl, t)
46}
47
48async function sendCreateVideoCommentToVideoFollowers (comment: VideoCommentModel, t: Transaction) {
49 const byActor = comment.Account.Actor
50
51 const actorsToForwardView = await getActorsInvolvedInVideo(comment.Video, t)
52 const audience = getObjectFollowersAudience(actorsToForwardView)
53
54 const commentObject = comment.toActivityPubObject()
55 const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
56
57 const followersException = [ byActor ]
58 return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException)
59}
60
40async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { 61async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
41 const url = getVideoViewActivityPubUrl(byActor, video) 62 const url = getVideoViewActivityPubUrl(byActor, video)
42 const viewActivity = createViewActivityData(byActor, video) 63 const viewActivityData = createViewActivityData(byActor, video)
43 64
44 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 65 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
45 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) 66 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
46 const data = await createActivityData(url, byActor, viewActivity, t, audience) 67 const data = await createActivityData(url, byActor, viewActivityData, t, audience)
47 68
48 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) 69 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
49} 70}
50 71
51async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { 72async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
52 const url = getVideoViewActivityPubUrl(byActor, video) 73 const url = getVideoViewActivityPubUrl(byActor, video)
53 const viewActivity = createViewActivityData(byActor, video) 74 const viewActivityData = createViewActivityData(byActor, video)
54 75
55 const actorsToForwardView = await getActorsInvolvedInVideo(video, t) 76 const actorsToForwardView = await getActorsInvolvedInVideo(video, t)
56 const audience = getObjectFollowersAudience(actorsToForwardView) 77 const audience = getObjectFollowersAudience(actorsToForwardView)
57 const data = await createActivityData(url, byActor, viewActivity, t, audience) 78 const data = await createActivityData(url, byActor, viewActivityData, t, audience)
58 79
59 // Use the server actor to send the view 80 // Use the server actor to send the view
60 const serverActor = await getServerActor() 81 const serverActor = await getServerActor()
@@ -64,22 +85,22 @@ async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: Video
64 85
65async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { 86async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
66 const url = getVideoDislikeActivityPubUrl(byActor, video) 87 const url = getVideoDislikeActivityPubUrl(byActor, video)
67 const dislikeActivity = createDislikeActivityData(byActor, video) 88 const dislikeActivityData = createDislikeActivityData(byActor, video)
68 89
69 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) 90 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
70 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) 91 const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
71 const data = await createActivityData(url, byActor, dislikeActivity, t, audience) 92 const data = await createActivityData(url, byActor, dislikeActivityData, t, audience)
72 93
73 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) 94 return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
74} 95}
75 96
76async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { 97async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
77 const url = getVideoDislikeActivityPubUrl(byActor, video) 98 const url = getVideoDislikeActivityPubUrl(byActor, video)
78 const dislikeActivity = createDislikeActivityData(byActor, video) 99 const dislikeActivityData = createDislikeActivityData(byActor, video)
79 100
80 const actorsToForwardView = await getActorsInvolvedInVideo(video, t) 101 const actorsToForwardView = await getActorsInvolvedInVideo(video, t)
81 const audience = getObjectFollowersAudience(actorsToForwardView) 102 const audience = getObjectFollowersAudience(actorsToForwardView)
82 const data = await createActivityData(url, byActor, dislikeActivity, t, audience) 103 const data = await createActivityData(url, byActor, dislikeActivityData, t, audience)
83 104
84 const followersException = [ byActor ] 105 const followersException = [ byActor ]
85 return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException) 106 return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException)
@@ -112,6 +133,14 @@ function createDislikeActivityData (byActor: ActorModel, video: VideoModel) {
112 } 133 }
113} 134}
114 135
136function createViewActivityData (byActor: ActorModel, video: VideoModel) {
137 return {
138 type: 'View',
139 actor: byActor.url,
140 object: video.url
141 }
142}
143
115// --------------------------------------------------------------------------- 144// ---------------------------------------------------------------------------
116 145
117export { 146export {
@@ -122,15 +151,7 @@ export {
122 sendCreateViewToVideoFollowers, 151 sendCreateViewToVideoFollowers,
123 sendCreateDislikeToOrigin, 152 sendCreateDislikeToOrigin,
124 sendCreateDislikeToVideoFollowers, 153 sendCreateDislikeToVideoFollowers,
125 createDislikeActivityData 154 createDislikeActivityData,
126} 155 sendCreateVideoCommentToOrigin,
127 156 sendCreateVideoCommentToVideoFollowers
128// ---------------------------------------------------------------------------
129
130function createViewActivityData (byActor: ActorModel, video: VideoModel) {
131 return {
132 type: 'View',
133 actor: byActor.url,
134 object: video.url
135 }
136} 157}
diff --git a/server/lib/video-comment.ts b/server/lib/video-comment.ts
index e3fe26e35..ef6a8f097 100644
--- a/server/lib/video-comment.ts
+++ b/server/lib/video-comment.ts
@@ -3,27 +3,25 @@ import { ResultList } from '../../shared/models'
3import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model' 3import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
4import { VideoModel } from '../models/video/video' 4import { VideoModel } from '../models/video/video'
5import { VideoCommentModel } from '../models/video/video-comment' 5import { VideoCommentModel } from '../models/video/video-comment'
6import { getVideoCommentActivityPubUrl } from './activitypub' 6import { getVideoCommentActivityPubUrl, sendVideoRateChangeToFollowers } from './activitypub'
7import { sendCreateVideoCommentToOrigin, sendCreateVideoCommentToVideoFollowers } from './activitypub/send'
7 8
8async function createVideoComment (obj: { 9async function createVideoComment (obj: {
9 text: string, 10 text: string,
10 inReplyToCommentId: number, 11 inReplyToComment: VideoCommentModel,
11 video: VideoModel 12 video: VideoModel
12 accountId: number 13 accountId: number
13}, t: Sequelize.Transaction) { 14}, t: Sequelize.Transaction) {
14 let originCommentId: number = null 15 let originCommentId: number = null
15 16
16 if (obj.inReplyToCommentId) { 17 if (obj.inReplyToComment) {
17 const repliedComment = await VideoCommentModel.loadById(obj.inReplyToCommentId) 18 originCommentId = obj.inReplyToComment.originCommentId || obj.inReplyToComment.id
18 if (!repliedComment) throw new Error('Unknown replied comment.')
19
20 originCommentId = repliedComment.originCommentId || repliedComment.id
21 } 19 }
22 20
23 const comment = await VideoCommentModel.create({ 21 const comment = await VideoCommentModel.create({
24 text: obj.text, 22 text: obj.text,
25 originCommentId, 23 originCommentId,
26 inReplyToCommentId: obj.inReplyToCommentId, 24 inReplyToCommentId: obj.inReplyToComment.id,
27 videoId: obj.video.id, 25 videoId: obj.video.id,
28 accountId: obj.accountId, 26 accountId: obj.accountId,
29 url: 'fake url' 27 url: 'fake url'
@@ -31,7 +29,17 @@ async function createVideoComment (obj: {
31 29
32 comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment)) 30 comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment))
33 31
34 return comment.save({ transaction: t }) 32 const savedComment = await comment.save({ transaction: t })
33 savedComment.InReplyToVideoComment = obj.inReplyToComment
34 savedComment.Video = obj.video
35
36 if (savedComment.Video.isOwned()) {
37 await sendCreateVideoCommentToVideoFollowers(savedComment, t)
38 } else {
39 await sendCreateVideoCommentToOrigin(savedComment, t)
40 }
41
42 return savedComment
35} 43}
36 44
37function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>): VideoCommentThreadTree { 45function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>): VideoCommentThreadTree {
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 8e84bfc06..25cd6d563 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -3,6 +3,7 @@ import {
3 AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, 3 AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table,
4 UpdatedAt 4 UpdatedAt
5} from 'sequelize-typescript' 5} from 'sequelize-typescript'
6import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
6import { VideoComment } from '../../../shared/models/videos/video-comment.model' 7import { VideoComment } from '../../../shared/models/videos/video-comment.model'
7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub' 8import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
8import { CONSTRAINTS_FIELDS } from '../../initializers' 9import { CONSTRAINTS_FIELDS } from '../../initializers'
@@ -11,7 +12,8 @@ import { getSort, throwIfNotValid } from '../utils'
11import { VideoModel } from './video' 12import { VideoModel } from './video'
12 13
13enum ScopeNames { 14enum ScopeNames {
14 WITH_ACCOUNT = 'WITH_ACCOUNT' 15 WITH_ACCOUNT = 'WITH_ACCOUNT',
16 WITH_IN_REPLY_TO = 'WITH_IN_REPLY_TO'
15} 17}
16 18
17@Scopes({ 19@Scopes({
@@ -19,6 +21,14 @@ enum ScopeNames {
19 include: [ 21 include: [
20 () => AccountModel 22 () => AccountModel
21 ] 23 ]
24 },
25 [ScopeNames.WITH_IN_REPLY_TO]: {
26 include: [
27 {
28 model: () => VideoCommentModel,
29 as: 'InReplyTo'
30 }
31 ]
22 } 32 }
23}) 33})
24@Table({ 34@Table({
@@ -68,6 +78,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
68 foreignKey: { 78 foreignKey: {
69 allowNull: true 79 allowNull: true
70 }, 80 },
81 as: 'InReplyTo',
71 onDelete: 'CASCADE' 82 onDelete: 'CASCADE'
72 }) 83 })
73 InReplyToVideoComment: VideoCommentModel 84 InReplyToVideoComment: VideoCommentModel
@@ -180,4 +191,23 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
180 } 191 }
181 } as VideoComment 192 } as VideoComment
182 } 193 }
194
195 toActivityPubObject (): VideoCommentObject {
196 let inReplyTo: string
197 // New thread, so in AS we reply to the video
198 if (this.inReplyToCommentId === null) {
199 inReplyTo = this.Video.url
200 } else {
201 inReplyTo = this.InReplyToVideoComment.url
202 }
203
204 return {
205 type: 'Note' as 'Note',
206 id: this.url,
207 content: this.text,
208 inReplyTo,
209 published: this.createdAt.toISOString(),
210 url: this.url
211 }
212 }
183} 213}