]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video-comment.ts
Implement abuses check params
[github/Chocobozzz/PeerTube.git] / server / models / video / video-comment.ts
index dfeb1c4e79d2b8c8281d76f714ba023a0fb7e51f..fb6078ed889411496331fda3be8118b0fb547b05 100644 (file)
@@ -1,9 +1,24 @@
 import * as Bluebird from 'bluebird'
 import { uniq } from 'lodash'
 import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
-import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
+import {
+  AllowNull,
+  BeforeDestroy,
+  BelongsTo,
+  Column,
+  CreatedAt,
+  DataType,
+  ForeignKey,
+  HasMany,
+  Is,
+  Model,
+  Scopes,
+  Table,
+  UpdatedAt
+} from 'sequelize-typescript'
+import { logger } from '@server/helpers/logger'
 import { getServerActor } from '@server/models/application/application'
-import { MAccount, MAccountId, MUserAccountId } from '@server/typings/models'
+import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
 import { VideoPrivacy } from '@shared/models'
 import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects'
 import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
@@ -21,8 +36,10 @@ import {
   MCommentOwnerReplyVideoLight,
   MCommentOwnerVideo,
   MCommentOwnerVideoFeed,
-  MCommentOwnerVideoReply
-} from '../../typings/models/video'
+  MCommentOwnerVideoReply,
+  MVideoImmutable
+} from '../../types/models/video'
+import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
 import { AccountModel } from '../account/account'
 import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
 import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils'
@@ -38,14 +55,14 @@ enum ScopeNames {
 }
 
 @Scopes(() => ({
-  [ScopeNames.ATTRIBUTES_FOR_API]: (serverAccountId: number, userAccountId?: number) => {
+  [ScopeNames.ATTRIBUTES_FOR_API]: (blockerAccountIds: number[]) => {
     return {
       attributes: {
         include: [
           [
             Sequelize.literal(
               '(' +
-                'WITH "blocklist" AS (' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' +
+                'WITH "blocklist" AS (' + buildBlockedAccountSQL(blockerAccountIds) + ')' +
                 'SELECT COUNT("replies"."id") - (' +
                   'SELECT COUNT("replies"."id") ' +
                   'FROM "videoComment" AS "replies" ' +
@@ -143,6 +160,11 @@ enum ScopeNames {
     },
     {
       fields: [ 'accountId' ]
+    },
+    {
+      fields: [
+        { name: 'createdAt', order: 'DESC' }
+      ]
     }
   ]
 })
@@ -218,6 +240,53 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
   })
   Account: AccountModel
 
+  @HasMany(() => VideoCommentAbuseModel, {
+    foreignKey: {
+      name: 'commentId',
+      allowNull: true
+    },
+    onDelete: 'set null'
+  })
+  CommentAbuses: VideoCommentAbuseModel[]
+
+  @BeforeDestroy
+  static async saveEssentialDataToAbuses (instance: VideoCommentModel, options) {
+    const tasks: Promise<any>[] = []
+
+    if (!Array.isArray(instance.CommentAbuses)) {
+      instance.CommentAbuses = await instance.$get('CommentAbuses')
+
+      if (instance.CommentAbuses.length === 0) return undefined
+    }
+
+    if (!instance.Video) {
+      instance.Video = await instance.$get('Video')
+    }
+
+    logger.info('Saving video comment %s for abuse.', instance.url)
+
+    const details = Object.assign(instance.toFormattedJSON(), {
+      Video: {
+        id: instance.Video.id,
+        name: instance.Video.name,
+        uuid: instance.Video.uuid
+      }
+    })
+
+    for (const abuse of instance.CommentAbuses) {
+      abuse.deletedComment = details
+
+      tasks.push(abuse.save({ transaction: options.transaction }))
+    }
+
+    Promise.all(tasks)
+           .catch(err => {
+             logger.error('Some errors when saving details of comment %s in its abuses before destroy hook.', instance.url, { err })
+           })
+
+    return undefined
+  }
+
   static loadById (id: number, t?: Transaction): Bluebird<MComment> {
     const query: FindOptions = {
       where: {
@@ -276,16 +345,15 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
 
   static async listThreadsForApi (parameters: {
     videoId: number
+    isVideoOwned: boolean
     start: number
     count: number
     sort: string
     user?: MUserAccountId
   }) {
-    const { videoId, start, count, sort, user } = parameters
+    const { videoId, isVideoOwned, start, count, sort, user } = parameters
 
-    const serverActor = await getServerActor()
-    const serverAccountId = serverActor.Account.id
-    const userAccountId = user ? user.Account.id : undefined
+    const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({ videoId, user, isVideoOwned })
 
     const query = {
       offset: start,
@@ -304,7 +372,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
               {
                 accountId: {
                   [Op.notIn]: Sequelize.literal(
-                    '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
+                    '(' + buildBlockedAccountSQL(blockerAccountIds) + ')'
                   )
                 }
               },
@@ -320,7 +388,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
     const scopes: (string | ScopeOptions)[] = [
       ScopeNames.WITH_ACCOUNT_FOR_API,
       {
-        method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
+        method: [ ScopeNames.ATTRIBUTES_FOR_API, blockerAccountIds ]
       }
     ]
 
@@ -334,14 +402,13 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
 
   static async listThreadCommentsForApi (parameters: {
     videoId: number
+    isVideoOwned: boolean
     threadId: number
     user?: MUserAccountId
   }) {
-    const { videoId, threadId, user } = parameters
+    const { videoId, threadId, user, isVideoOwned } = parameters
 
-    const serverActor = await getServerActor()
-    const serverAccountId = serverActor.Account.id
-    const userAccountId = user ? user.Account.id : undefined
+    const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({ videoId, user, isVideoOwned })
 
     const query = {
       order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ] as Order,
@@ -353,7 +420,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
         ],
         accountId: {
           [Op.notIn]: Sequelize.literal(
-            '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')'
+            '(' + buildBlockedAccountSQL(blockerAccountIds) + ')'
           )
         }
       }
@@ -362,7 +429,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
     const scopes: any[] = [
       ScopeNames.WITH_ACCOUNT_FOR_API,
       {
-        method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ]
+        method: [ ScopeNames.ATTRIBUTES_FOR_API, blockerAccountIds ]
       }
     ]
 
@@ -399,13 +466,23 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
       .findAll(query)
   }
 
-  static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Transaction, order: 'ASC' | 'DESC' = 'ASC') {
+  static async listAndCountByVideoForAP (video: MVideoImmutable, start: number, count: number, t?: Transaction) {
+    const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({
+      videoId: video.id,
+      isVideoOwned: video.isOwned()
+    })
+
     const query = {
-      order: [ [ 'createdAt', order ] ] as Order,
+      order: [ [ 'createdAt', 'ASC' ] ] as Order,
       offset: start,
       limit: count,
       where: {
-        videoId
+        videoId: video.id,
+        accountId: {
+          [Op.notIn]: Sequelize.literal(
+            '(' + buildBlockedAccountSQL(blockerAccountIds) + ')'
+          )
+        }
       },
       transaction: t
     }
@@ -413,8 +490,31 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
     return VideoCommentModel.findAndCountAll<MComment>(query)
   }
 
-  static async listForFeed (start: number, count: number, videoId?: number): Promise<MCommentOwnerVideoFeed[]> {
+  static async listForFeed (parameters: {
+    start: number
+    count: number
+    videoId?: number
+    accountId?: number
+    videoChannelId?: number
+  }): Promise<MCommentOwnerVideoFeed[]> {
     const serverActor = await getServerActor()
+    const { start, count, videoId, accountId, videoChannelId } = parameters
+
+    const accountExclusion = {
+      [Op.notIn]: Sequelize.literal(
+        '(' + buildBlockedAccountSQL([ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ]) + ')'
+      )
+    }
+    const accountWhere = accountId
+      ? {
+        [Op.and]: {
+          ...accountExclusion,
+          [Op.eq]: accountId
+        }
+      }
+      : accountExclusion
+
+    const videoChannelWhere = videoChannelId ? { id: videoChannelId } : undefined
 
     const query = {
       order: [ [ 'createdAt', 'DESC' ] ] as Order,
@@ -422,11 +522,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
       limit: count,
       where: {
         deletedAt: null,
-        accountId: {
-          [Op.notIn]: Sequelize.literal(
-            '(' + buildBlockedAccountSQL(serverActor.Account.id) + ')'
-          )
-        }
+        accountId: accountWhere
       },
       include: [
         {
@@ -435,7 +531,15 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
           required: true,
           where: {
             privacy: VideoPrivacy.PUBLIC
-          }
+          },
+          include: [
+            {
+              attributes: [ 'accountId' ],
+              model: VideoChannelModel.unscoped(),
+              required: true,
+              where: videoChannelWhere
+            }
+          ]
         }
       ]
     }
@@ -650,4 +754,24 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
       tag
     }
   }
+
+  private static async buildBlockerAccountIds (options: {
+    videoId: number
+    isVideoOwned: boolean
+    user?: MUserAccountId
+  }) {
+    const { videoId, user, isVideoOwned } = options
+
+    const serverActor = await getServerActor()
+    const blockerAccountIds = [ serverActor.Account.id ]
+
+    if (user) blockerAccountIds.push(user.Account.id)
+
+    if (isVideoOwned) {
+      const videoOwnerAccount = await AccountModel.loadAccountIdFromVideo(videoId)
+      blockerAccountIds.push(videoOwnerAccount.id)
+    }
+
+    return blockerAccountIds
+  }
 }