]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video-abuse.ts
Support is:blacklisted as video-abuse-list filter
[github/Chocobozzz/PeerTube.git] / server / models / video / video-abuse.ts
index d68608ca668cc33a757c1d49b0a1b42b43db8803..f6b5468251d826429c67f0d950a0d665a849aa8f 100644 (file)
@@ -9,7 +9,7 @@ import {
   isVideoAbuseStateValid
 } from '../../helpers/custom-validators/video-abuses'
 import { AccountModel } from '../account/account'
-import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
+import { buildBlockedAccountSQL, getSort, throwIfNotValid, searchAttribute, parseQueryStringFilter } from '../utils'
 import { VideoModel } from './video'
 import { VideoAbuseState, VideoDetails } from '../../../shared'
 import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
@@ -17,8 +17,8 @@ import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo }
 import * as Bluebird from 'bluebird'
 import { literal, Op } from 'sequelize'
 import { ThumbnailModel } from './thumbnail'
-import { VideoChannelModel } from './video-channel'
 import { VideoBlacklistModel } from './video-blacklist'
+import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
 
 export enum ScopeNames {
   FOR_API = 'FOR_API'
@@ -26,19 +26,20 @@ export enum ScopeNames {
 
 @Scopes(() => ({
   [ScopeNames.FOR_API]: (options: {
+    // search
     search?: string
     searchReporter?: string
+    searchReportee?: string
     searchVideo?: string
     searchVideoChannel?: string
+    // filters
+    id?: number
+    state?: VideoAbuseState
+    is?: any
+    // accountIds
     serverAccountId: number
-    userAccountId: any
+    userAccountId: number
   }) => {
-    const search = (sourceField, targetField) => sourceField ? ({
-      [targetField]: {
-        [Op.iLike]: `%${sourceField}%`
-      }
-    }) : {}
-
     let where = {
       reporterAccountId: {
         [Op.notIn]: literal('(' + buildBlockedAccountSQL(options.serverAccountId, options.userAccountId) + ')')
@@ -51,50 +52,69 @@ export enum ScopeNames {
           {
             [Op.and]: [
               { videoId: { [Op.not]: null } },
-              { '$Video.name$': { [Op.iLike]: `%${options.search}%` } }
+              searchAttribute(options.search, '$Video.name$')
             ]
           },
           {
             [Op.and]: [
               { videoId: { [Op.not]: null } },
-              { '$Video.VideoChannel.name$': { [Op.iLike]: `%${options.search}%` } }
+              searchAttribute(options.search, '$Video.VideoChannel.name$')
             ]
           },
           {
             [Op.and]: [
               { deletedVideo: { [Op.not]: null } },
-              { deletedVideo: { name: { [Op.iLike]: `%${options.search}%` } } }
+              { deletedVideo: searchAttribute(options.search, 'name') }
             ]
           },
           {
             [Op.and]: [
               { deletedVideo: { [Op.not]: null } },
-              { deletedVideo: { channel: { displayName: { [Op.iLike]: `%${options.search}%` } } } }
+              { deletedVideo: { channel: searchAttribute(options.search, 'displayName') } }
             ]
           },
-          { '$Account.name$': { [Op.iLike]: `%${options.search}%` } }
+          searchAttribute(options.search, '$Account.name$')
         ]
       })
     }
 
+    if (options.id) {
+      where = Object.assign(where, {
+        id: options.id
+      })
+    }
+
+    if (options.state) {
+      where = Object.assign(where, {
+        state: options.state
+      })
+    }
+
+    let onlyBlacklisted = false
+    if (options.is === "deleted") {
+      where = Object.assign(where, {
+        deletedVideo: { [Op.not]: null }
+      })
+    } else if (options.is === "blacklisted") {
+      onlyBlacklisted = true
+    }
+
     return {
       attributes: {
         include: [
           [
+            // we don't care about this count for deleted videos, so there are not included
             literal(
               '(' +
-                'SELECT t.count ' +
-                'FROM ( ' +
-                  'SELECT id, ' +
-                         'count(id) OVER (PARTITION BY "videoId") ' +
-                  'FROM "videoAbuse" ' +
-                ') t ' +
-                'WHERE t.id = "VideoAbuseModel".id ' +
+                'SELECT count(*) ' +
+                'FROM "videoAbuse" ' +
+                'WHERE "videoId" = "VideoAbuseModel"."videoId" ' +
               ')'
             ),
             'countReportsForVideo'
           ],
           [
+            // we don't care about this count for deleted videos, so there are not included
             literal(
               '(' +
                 'SELECT t.nth ' +
@@ -119,28 +139,43 @@ export enum ScopeNames {
                 'WHERE "account"."id" = "VideoAbuseModel"."reporterAccountId" ' +
               ')'
             ),
-            'countReportsForReporter'
+            'countReportsForReporter__video'
           ],
           [
             literal(
               '(' +
-                'WITH ' +
-                  'ids AS ( ' +
-                    'SELECT "account"."id" ' +
-                    'FROM "account" ' +
-                    'INNER JOIN "videoChannel" ON "videoChannel"."accountId" = "account"."id" ' +
-                    'INNER JOIN "video" ON "video"."channelId" = "videoChannel"."id" ' +
-                    'WHERE "video"."id" = "VideoAbuseModel"."videoId" ' +
-                  ') ' +
-                'SELECT count("videoAbuse"."id") ' +
+                'SELECT count(DISTINCT "videoAbuse"."id") ' +
+                'FROM "videoAbuse" ' +
+                `WHERE CAST("deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) = "VideoAbuseModel"."reporterAccountId" ` +
+              ')'
+            ),
+            'countReportsForReporter__deletedVideo'
+          ],
+          [
+            literal(
+              '(' +
+                'SELECT count(DISTINCT "videoAbuse"."id") ' +
                 'FROM "videoAbuse" ' +
                 'INNER JOIN "video" ON "video"."id" = "videoAbuse"."videoId" ' +
                 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
-                'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' +
-                'INNER JOIN ids ON "account"."id" = ids.id ' +
+                'INNER JOIN "account" ON ' +
+                      '"videoChannel"."accountId" = "Video->VideoChannel"."accountId" ' +
+                   `OR "videoChannel"."accountId" = CAST("VideoAbuseModel"."deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) ` +
+              ')'
+            ),
+            'countReportsForReportee__video'
+          ],
+          [
+            literal(
+              '(' +
+                'SELECT count(DISTINCT "videoAbuse"."id") ' +
+                'FROM "videoAbuse" ' +
+                `WHERE CAST("deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) = "Video->VideoChannel"."accountId" ` +
+                   `OR CAST("deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) = ` +
+                      `CAST("VideoAbuseModel"."deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) ` +
               ')'
             ),
-            'countReportsForReportee'
+            'countReportsForReportee__deletedVideo'
           ]
         ]
       },
@@ -148,23 +183,30 @@ export enum ScopeNames {
         {
           model: AccountModel,
           required: true,
-          where: { ...search(options.searchReporter, 'name') }
+          where: searchAttribute(options.searchReporter, 'name')
         },
         {
           model: VideoModel,
-          required: false,
-          where: { ...search(options.searchVideo, 'name') },
+          required: onlyBlacklisted,
+          where: searchAttribute(options.searchVideo, 'name'),
           include: [
             {
               model: ThumbnailModel
             },
             {
-              model: VideoChannelModel.scope([ 'WITH_ACTOR', 'WITH_ACCOUNT' ]),
-              where: { ...search(options.searchVideoChannel, 'name') }
+              model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, { withAccount: true } as SummaryOptions ] }),
+              where: searchAttribute(options.searchVideoChannel, 'name'),
+              include: [
+                {
+                  model: AccountModel,
+                  where: searchAttribute(options.searchReportee, 'name')
+                }
+              ]
             },
             {
               attributes: [ 'id', 'reason', 'unfederated' ],
-              model: VideoBlacklistModel
+              model: VideoBlacklistModel,
+              required: onlyBlacklisted
             }
           ]
         }
@@ -273,7 +315,37 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
     }
 
     const filters = {
-      search,
+      ...parseQueryStringFilter(search, {
+        id: {
+          prefix: '#',
+          handler: v => v
+        },
+        state: {
+          prefix: 'state:',
+          handler: v => {
+            if (v === "accepted") return VideoAbuseState.ACCEPTED
+            if (v === "pending") return VideoAbuseState.PENDING
+            if (v === "rejected") return VideoAbuseState.REJECTED
+            return undefined
+          }
+        },
+        is: {
+          prefix: 'is:',
+          handler: v => {
+            if (v === "deleted") return v
+            if (v === "blacklisted") return v
+            return undefined
+          }
+        },
+        searchReporter: {
+          prefix: 'reporter:',
+          handler: v => v
+        },
+        searchReportee: {
+          prefix: 'reportee:',
+          handler: v => v
+        }
+      }),
       serverAccountId,
       userAccountId
     }
@@ -289,8 +361,10 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
   toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
     const countReportsForVideo = this.get('countReportsForVideo') as number
     const nthReportForVideo = this.get('nthReportForVideo') as number
-    const countReportsForReporter = this.get('countReportsForReporter') as number
-    const countReportsForReportee = this.get('countReportsForReportee') as number
+    const countReportsForReporterVideo = this.get('countReportsForReporter__video') as number
+    const countReportsForReporterDeletedVideo = this.get('countReportsForReporter__deletedVideo') as number
+    const countReportsForReporteeVideo = this.get('countReportsForReportee__video') as number
+    const countReportsForReporteeDeletedVideo = this.get('countReportsForReportee__deletedVideo') as number
 
     const video = this.Video
       ? this.Video
@@ -319,8 +393,8 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
       updatedAt: this.updatedAt,
       count: countReportsForVideo || 0,
       nth: nthReportForVideo || 0,
-      countReportsForReporter: countReportsForReporter || 0,
-      countReportsForReportee: countReportsForReportee || 0
+      countReportsForReporter: (countReportsForReporterVideo || 0) + (countReportsForReporterDeletedVideo || 0),
+      countReportsForReportee: (countReportsForReporteeVideo || 0) + (countReportsForReporteeDeletedVideo || 0)
     }
   }