Send video comment comments to followers/origin
authorChocobozzz <me@florianbigard.com>
Wed, 27 Dec 2017 09:39:31 +0000 (10:39 +0100)
committerChocobozzz <me@florianbigard.com>
Wed, 27 Dec 2017 09:39:31 +0000 (10:39 +0100)
server/controllers/api/videos/comment.ts
server/lib/activitypub/process/process-create.ts
server/lib/activitypub/send/send-create.ts
server/lib/video-comment.ts
server/models/video/video-comment.ts

index ac64f0154598dc10873c558c2c58196ff42a2ed8..e9dbb6d1b6166bb4dacbefdce62fde4ba6f4f96d 100644 (file)
@@ -78,7 +78,7 @@ function addVideoCommentThread (req: express.Request, res: express.Response) {
   return sequelizeTypescript.transaction(async t => {
     return createVideoComment({
       text: videoCommentInfo.text,
-      inReplyToCommentId: null,
+      inReplyToComment: null,
       video: res.locals.video,
       accountId: res.locals.oauth.token.User.Account.id
     }, t)
@@ -106,7 +106,7 @@ function addVideoCommentReply (req: express.Request, res: express.Response, next
   return sequelizeTypescript.transaction(async t => {
     return createVideoComment({
       text: videoCommentInfo.text,
-      inReplyToCommentId: res.locals.videoComment.id,
+      inReplyToComment: res.locals.videoComment,
       video: res.locals.video,
       accountId: res.locals.oauth.token.User.Account.id
     }, t)
index 6c2ee97ebe09405bb004a27586c1135f77b038f7..628942a58add480335b3cda770ddcee1472b0113 100644 (file)
@@ -257,11 +257,11 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) {
   if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url)
 
   return sequelizeTypescript.transaction(async t => {
-    const video = await VideoModel.loadByUrl(comment.inReplyTo, t)
+    let video = await VideoModel.loadByUrl(comment.inReplyTo, t)
 
     // This is a new thread
     if (video) {
-      return VideoCommentModel.create({
+      await VideoCommentModel.create({
         url: comment.id,
         text: comment.content,
         originCommentId: null,
@@ -269,19 +269,27 @@ function createVideoComment (byActor: ActorModel, activity: ActivityCreate) {
         videoId: video.id,
         accountId: byAccount.id
       }, { transaction: t })
-    }
+    } else {
+      const inReplyToComment = await VideoCommentModel.loadByUrl(comment.inReplyTo, t)
+      if (!inReplyToComment) throw new Error('Unknown replied comment ' + comment.inReplyTo)
 
-    const inReplyToComment = await VideoCommentModel.loadByUrl(comment.inReplyTo, t)
-    if (!inReplyToComment) throw new Error('Unknown replied comment ' + comment.inReplyTo)
+      video = await VideoModel.load(inReplyToComment.videoId)
 
-    const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id
-    return VideoCommentModel.create({
-      url: comment.id,
-      text: comment.content,
-      originCommentId,
-      inReplyToCommentId: inReplyToComment.id,
-      videoId: inReplyToComment.videoId,
-      accountId: byAccount.id
-    }, { transaction: t })
+      const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id
+      await VideoCommentModel.create({
+        url: comment.id,
+        text: comment.content,
+        originCommentId,
+        inReplyToCommentId: inReplyToComment.id,
+        videoId: video.id,
+        accountId: byAccount.id
+      }, { transaction: t })
+    }
+
+    if (video.isOwned()) {
+      // Don't resend the activity to the sender
+      const exceptions = [ byActor ]
+      await forwardActivity(activity, t, exceptions)
+    }
   })
 }
index 27b03c45f79d22b9be0c500d251e0d5b1c8c69bd..ca50460be444229ae89703268b334c137309e53a 100644 (file)
@@ -5,14 +5,10 @@ import { getServerActor } from '../../../helpers'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoModel } from '../../../models/video/video'
 import { VideoAbuseModel } from '../../../models/video/video-abuse'
+import { VideoCommentModel } from '../../../models/video/video-comment'
 import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
 import {
-  audiencify,
-  broadcastToFollowers,
-  getActorsInvolvedInVideo,
-  getAudience,
-  getObjectFollowersAudience,
-  getOriginVideoAudience,
+  audiencify, broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getOriginVideoAudience,
   unicastTo
 } from './misc'
 
@@ -37,24 +33,49 @@ async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel,
   return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
 }
 
+async function sendCreateVideoCommentToOrigin (comment: VideoCommentModel, t: Transaction) {
+  const byActor = comment.Account.Actor
+
+  const actorsInvolvedInVideo = await getActorsInvolvedInVideo(comment.Video, t)
+  const audience = getOriginVideoAudience(comment.Video, actorsInvolvedInVideo)
+
+  const commentObject = comment.toActivityPubObject()
+  const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
+
+  return unicastTo(data, byActor, comment.Video.VideoChannel.Account.Actor.sharedInboxUrl, t)
+}
+
+async function sendCreateVideoCommentToVideoFollowers (comment: VideoCommentModel, t: Transaction) {
+  const byActor = comment.Account.Actor
+
+  const actorsToForwardView = await getActorsInvolvedInVideo(comment.Video, t)
+  const audience = getObjectFollowersAudience(actorsToForwardView)
+
+  const commentObject = comment.toActivityPubObject()
+  const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
+
+  const followersException = [ byActor ]
+  return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException)
+}
+
 async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
   const url = getVideoViewActivityPubUrl(byActor, video)
-  const viewActivity = createViewActivityData(byActor, video)
+  const viewActivityData = createViewActivityData(byActor, video)
 
   const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
   const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
-  const data = await createActivityData(url, byActor, viewActivity, t, audience)
+  const data = await createActivityData(url, byActor, viewActivityData, t, audience)
 
   return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
 }
 
 async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
   const url = getVideoViewActivityPubUrl(byActor, video)
-  const viewActivity = createViewActivityData(byActor, video)
+  const viewActivityData = createViewActivityData(byActor, video)
 
   const actorsToForwardView = await getActorsInvolvedInVideo(video, t)
   const audience = getObjectFollowersAudience(actorsToForwardView)
-  const data = await createActivityData(url, byActor, viewActivity, t, audience)
+  const data = await createActivityData(url, byActor, viewActivityData, t, audience)
 
   // Use the server actor to send the view
   const serverActor = await getServerActor()
@@ -64,22 +85,22 @@ async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: Video
 
 async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) {
   const url = getVideoDislikeActivityPubUrl(byActor, video)
-  const dislikeActivity = createDislikeActivityData(byActor, video)
+  const dislikeActivityData = createDislikeActivityData(byActor, video)
 
   const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t)
   const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
-  const data = await createActivityData(url, byActor, dislikeActivity, t, audience)
+  const data = await createActivityData(url, byActor, dislikeActivityData, t, audience)
 
   return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t)
 }
 
 async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) {
   const url = getVideoDislikeActivityPubUrl(byActor, video)
-  const dislikeActivity = createDislikeActivityData(byActor, video)
+  const dislikeActivityData = createDislikeActivityData(byActor, video)
 
   const actorsToForwardView = await getActorsInvolvedInVideo(video, t)
   const audience = getObjectFollowersAudience(actorsToForwardView)
-  const data = await createActivityData(url, byActor, dislikeActivity, t, audience)
+  const data = await createActivityData(url, byActor, dislikeActivityData, t, audience)
 
   const followersException = [ byActor ]
   return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException)
@@ -112,6 +133,14 @@ function createDislikeActivityData (byActor: ActorModel, video: VideoModel) {
   }
 }
 
+function createViewActivityData (byActor: ActorModel, video: VideoModel) {
+  return {
+    type: 'View',
+    actor: byActor.url,
+    object: video.url
+  }
+}
+
 // ---------------------------------------------------------------------------
 
 export {
@@ -122,15 +151,7 @@ export {
   sendCreateViewToVideoFollowers,
   sendCreateDislikeToOrigin,
   sendCreateDislikeToVideoFollowers,
-  createDislikeActivityData
-}
-
-// ---------------------------------------------------------------------------
-
-function createViewActivityData (byActor: ActorModel, video: VideoModel) {
-  return {
-    type: 'View',
-    actor: byActor.url,
-    object: video.url
-  }
+  createDislikeActivityData,
+  sendCreateVideoCommentToOrigin,
+  sendCreateVideoCommentToVideoFollowers
 }
index e3fe26e355d2d8851b0bdf3a5697ed0ab5a0263c..ef6a8f097b7c222fa37bf143fad14f29cb651802 100644 (file)
@@ -3,27 +3,25 @@ import { ResultList } from '../../shared/models'
 import { VideoCommentThreadTree } from '../../shared/models/videos/video-comment.model'
 import { VideoModel } from '../models/video/video'
 import { VideoCommentModel } from '../models/video/video-comment'
-import { getVideoCommentActivityPubUrl } from './activitypub'
+import { getVideoCommentActivityPubUrl, sendVideoRateChangeToFollowers } from './activitypub'
+import { sendCreateVideoCommentToOrigin, sendCreateVideoCommentToVideoFollowers } from './activitypub/send'
 
 async function createVideoComment (obj: {
   text: string,
-  inReplyToCommentId: number,
+  inReplyToComment: VideoCommentModel,
   video: VideoModel
   accountId: number
 }, t: Sequelize.Transaction) {
   let originCommentId: number = null
 
-  if (obj.inReplyToCommentId) {
-    const repliedComment = await VideoCommentModel.loadById(obj.inReplyToCommentId)
-    if (!repliedComment) throw new Error('Unknown replied comment.')
-
-    originCommentId = repliedComment.originCommentId || repliedComment.id
+  if (obj.inReplyToComment) {
+    originCommentId = obj.inReplyToComment.originCommentId || obj.inReplyToComment.id
   }
 
   const comment = await VideoCommentModel.create({
     text: obj.text,
     originCommentId,
-    inReplyToCommentId: obj.inReplyToCommentId,
+    inReplyToCommentId: obj.inReplyToComment.id,
     videoId: obj.video.id,
     accountId: obj.accountId,
     url: 'fake url'
@@ -31,7 +29,17 @@ async function createVideoComment (obj: {
 
   comment.set('url', getVideoCommentActivityPubUrl(obj.video, comment))
 
-  return comment.save({ transaction: t })
+  const savedComment = await comment.save({ transaction: t })
+  savedComment.InReplyToVideoComment = obj.inReplyToComment
+  savedComment.Video = obj.video
+
+  if (savedComment.Video.isOwned()) {
+    await sendCreateVideoCommentToVideoFollowers(savedComment, t)
+  } else {
+    await sendCreateVideoCommentToOrigin(savedComment, t)
+  }
+
+  return savedComment
 }
 
 function buildFormattedCommentTree (resultList: ResultList<VideoCommentModel>): VideoCommentThreadTree {
index 8e84bfc06ab0603429b4811c8837a73c8f93136f..25cd6d5635eee82787d59838137a805aaa79fa22 100644 (file)
@@ -3,6 +3,7 @@ import {
   AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table,
   UpdatedAt
 } from 'sequelize-typescript'
+import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
 import { VideoComment } from '../../../shared/models/videos/video-comment.model'
 import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
 import { CONSTRAINTS_FIELDS } from '../../initializers'
@@ -11,7 +12,8 @@ import { getSort, throwIfNotValid } from '../utils'
 import { VideoModel } from './video'
 
 enum ScopeNames {
-  WITH_ACCOUNT = 'WITH_ACCOUNT'
+  WITH_ACCOUNT = 'WITH_ACCOUNT',
+  WITH_IN_REPLY_TO = 'WITH_IN_REPLY_TO'
 }
 
 @Scopes({
@@ -19,6 +21,14 @@ enum ScopeNames {
     include: [
       () => AccountModel
     ]
+  },
+  [ScopeNames.WITH_IN_REPLY_TO]: {
+    include: [
+      {
+        model: () => VideoCommentModel,
+        as: 'InReplyTo'
+      }
+    ]
   }
 })
 @Table({
@@ -68,6 +78,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
     foreignKey: {
       allowNull: true
     },
+    as: 'InReplyTo',
     onDelete: 'CASCADE'
   })
   InReplyToVideoComment: VideoCommentModel
@@ -180,4 +191,23 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
       }
     } as VideoComment
   }
+
+  toActivityPubObject (): VideoCommentObject {
+    let inReplyTo: string
+    // New thread, so in AS we reply to the video
+    if (this.inReplyToCommentId === null) {
+      inReplyTo = this.Video.url
+    } else {
+      inReplyTo = this.InReplyToVideoComment.url
+    }
+
+    return {
+      type: 'Note' as 'Note',
+      id: this.url,
+      content: this.text,
+      inReplyTo,
+      published: this.createdAt.toISOString(),
+      url: this.url
+    }
+  }
 }