]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/video-abuse.ts
Translated using Weblate (Arabic)
[github/Chocobozzz/PeerTube.git] / server / models / video / video-abuse.ts
index 285fb1fbc609a23b52915189f1235626c747a491..1319332f0738fa80b645d67f42d71c8fd33b20cc 100644 (file)
@@ -1,6 +1,27 @@
+import * as Bluebird from 'bluebird'
+import { literal, Op } from 'sequelize'
 import {
-  AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt, Scopes
+  AllowNull,
+  BelongsTo,
+  Column,
+  CreatedAt,
+  DataType,
+  Default,
+  ForeignKey,
+  Is,
+  Model,
+  Scopes,
+  Table,
+  UpdatedAt
 } from 'sequelize-typescript'
+import { VideoAbuseVideoIs } from '@shared/models/videos/abuse/video-abuse-video-is.type'
+import {
+  VideoAbuseState,
+  VideoDetails,
+  VideoAbusePredefinedReasons,
+  VideoAbusePredefinedReasonsString,
+  videoAbusePredefinedReasonsMap
+} from '../../../shared'
 import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
 import { VideoAbuse } from '../../../shared/models/videos'
 import {
@@ -8,17 +29,15 @@ import {
   isVideoAbuseReasonValid,
   isVideoAbuseStateValid
 } from '../../helpers/custom-validators/video-abuses'
-import { AccountModel } from '../account/account'
-import { buildBlockedAccountSQL, getSort, throwIfNotValid, searchAttribute } from '../utils'
-import { VideoModel } from './video'
-import { VideoAbuseState, VideoDetails } from '../../../shared'
 import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
-import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models'
-import * as Bluebird from 'bluebird'
-import { literal, Op } from 'sequelize'
+import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../types/models'
+import { AccountModel } from '../account/account'
+import { buildBlockedAccountSQL, getSort, searchAttribute, throwIfNotValid } from '../utils'
 import { ThumbnailModel } from './thumbnail'
+import { VideoModel } from './video'
 import { VideoBlacklistModel } from './video-blacklist'
 import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
+import { invert } from 'lodash'
 
 export enum ScopeNames {
   FOR_API = 'FOR_API'
@@ -26,21 +45,32 @@ export enum ScopeNames {
 
 @Scopes(() => ({
   [ScopeNames.FOR_API]: (options: {
+    // search
     search?: string
     searchReporter?: string
+    searchReportee?: string
     searchVideo?: string
     searchVideoChannel?: string
+
+    // filters
+    id?: number
+    predefinedReasonId?: number
+
+    state?: VideoAbuseState
+    videoIs?: VideoAbuseVideoIs
+
+    // accountIds
     serverAccountId: number
     userAccountId: number
   }) => {
-    let where = {
+    const where = {
       reporterAccountId: {
-        [Op.notIn]: literal('(' + buildBlockedAccountSQL(options.serverAccountId, options.userAccountId) + ')')
+        [Op.notIn]: literal('(' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
       }
     }
 
     if (options.search) {
-      where = Object.assign(where, {
+      Object.assign(where, {
         [Op.or]: [
           {
             [Op.and]: [
@@ -71,6 +101,27 @@ export enum ScopeNames {
       })
     }
 
+    if (options.id) Object.assign(where, { id: options.id })
+    if (options.state) Object.assign(where, { state: options.state })
+
+    if (options.videoIs === 'deleted') {
+      Object.assign(where, {
+        deletedVideo: {
+          [Op.not]: null
+        }
+      })
+    }
+
+    if (options.predefinedReasonId) {
+      Object.assign(where, {
+        predefinedReasons: {
+          [Op.contains]: [ options.predefinedReasonId ]
+        }
+      })
+    }
+
+    const onlyBlacklisted = options.videoIs === 'blacklisted'
+
     return {
       attributes: {
         include: [
@@ -143,7 +194,8 @@ export enum ScopeNames {
                 '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) ` +
+                   `OR CAST("deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) = ` +
+                      `CAST("VideoAbuseModel"."deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) ` +
               ')'
             ),
             'countReportsForReportee__deletedVideo'
@@ -158,7 +210,7 @@ export enum ScopeNames {
         },
         {
           model: VideoModel,
-          required: false,
+          required: !!(onlyBlacklisted || options.searchVideo || options.searchReportee || options.searchVideoChannel),
           where: searchAttribute(options.searchVideo, 'name'),
           include: [
             {
@@ -166,11 +218,18 @@ export enum ScopeNames {
             },
             {
               model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, { withAccount: true } as SummaryOptions ] }),
-              where: searchAttribute(options.searchVideoChannel, 'name')
+              where: searchAttribute(options.searchVideoChannel, 'name'),
+              include: [
+                {
+                  model: AccountModel,
+                  where: searchAttribute(options.searchReportee, 'name')
+                }
+              ]
             },
             {
               attributes: [ 'id', 'reason', 'unfederated' ],
-              model: VideoBlacklistModel
+              model: VideoBlacklistModel,
+              required: onlyBlacklisted
             }
           ]
         }
@@ -215,6 +274,21 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
   @Column(DataType.JSONB)
   deletedVideo: VideoDetails
 
+  @AllowNull(true)
+  @Default(null)
+  @Column(DataType.ARRAY(DataType.INTEGER))
+  predefinedReasons: VideoAbusePredefinedReasons[]
+
+  @AllowNull(true)
+  @Default(null)
+  @Column
+  startAt: number
+
+  @AllowNull(true)
+  @Default(null)
+  @Column
+  endAt: number
+
   @CreatedAt
   createdAt: Date
 
@@ -263,12 +337,40 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
     start: number
     count: number
     sort: string
-    search?: string
+
     serverAccountId: number
     user?: MUserAccountId
+
+    id?: number
+    predefinedReason?: VideoAbusePredefinedReasonsString
+    state?: VideoAbuseState
+    videoIs?: VideoAbuseVideoIs
+
+    search?: string
+    searchReporter?: string
+    searchReportee?: string
+    searchVideo?: string
+    searchVideoChannel?: string
   }) {
-    const { start, count, sort, search, user, serverAccountId } = parameters
+    const {
+      start,
+      count,
+      sort,
+      search,
+      user,
+      serverAccountId,
+      state,
+      videoIs,
+      predefinedReason,
+      searchReportee,
+      searchVideo,
+      searchVideoChannel,
+      searchReporter,
+      id
+    } = parameters
+
     const userAccountId = user ? user.Account.id : undefined
+    const predefinedReasonId = predefinedReason ? videoAbusePredefinedReasonsMap[predefinedReason] : undefined
 
     const query = {
       offset: start,
@@ -279,13 +381,23 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
     }
 
     const filters = {
+      id,
+      predefinedReasonId,
       search,
+      state,
+      videoIs,
+      searchReportee,
+      searchVideo,
+      searchVideoChannel,
+      searchReporter,
       serverAccountId,
       userAccountId
     }
 
     return VideoAbuseModel
-      .scope({ method: [ ScopeNames.FOR_API, filters ] })
+      .scope([
+        { method: [ ScopeNames.FOR_API, filters ] }
+      ])
       .findAndCountAll(query)
       .then(({ rows, count }) => {
         return { total: count, data: rows }
@@ -293,6 +405,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
   }
 
   toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
+    const predefinedReasons = VideoAbuseModel.getPredefinedReasonsStrings(this.predefinedReasons)
     const countReportsForVideo = this.get('countReportsForVideo') as number
     const nthReportForVideo = this.get('nthReportForVideo') as number
     const countReportsForReporterVideo = this.get('countReportsForReporter__video') as number
@@ -307,6 +420,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
     return {
       id: this.id,
       reason: this.reason,
+      predefinedReasons,
       reporterAccount: this.Account.toFormattedJSON(),
       state: {
         id: this.state,
@@ -319,12 +433,14 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
         name: video.name,
         nsfw: video.nsfw,
         deleted: !this.Video,
-        blacklisted: this.Video && this.Video.isBlacklisted(),
+        blacklisted: this.Video?.isBlacklisted() || false,
         thumbnailPath: this.Video?.getMiniatureStaticPath(),
         channel: this.Video?.VideoChannel.toFormattedJSON() || this.deletedVideo?.channel
       },
       createdAt: this.createdAt,
       updatedAt: this.updatedAt,
+      startAt: this.startAt,
+      endAt: this.endAt,
       count: countReportsForVideo || 0,
       nth: nthReportForVideo || 0,
       countReportsForReporter: (countReportsForReporterVideo || 0) + (countReportsForReporterDeletedVideo || 0),
@@ -333,14 +449,31 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
   }
 
   toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
+    const predefinedReasons = VideoAbuseModel.getPredefinedReasonsStrings(this.predefinedReasons)
+
+    const startAt = this.startAt
+    const endAt = this.endAt
+
     return {
       type: 'Flag' as 'Flag',
       content: this.reason,
-      object: this.Video.url
+      object: this.Video.url,
+      tag: predefinedReasons.map(r => ({
+        type: 'Hashtag' as 'Hashtag',
+        name: r
+      })),
+      startAt,
+      endAt
     }
   }
 
   private static getStateLabel (id: number) {
     return VIDEO_ABUSE_STATES[id] || 'Unknown'
   }
+
+  private static getPredefinedReasonsStrings (predefinedReasons: VideoAbusePredefinedReasons[]): VideoAbusePredefinedReasonsString[] {
+    return (predefinedReasons || [])
+      .filter(r => r in VideoAbusePredefinedReasons)
+      .map(r => invert(videoAbusePredefinedReasonsMap)[r] as VideoAbusePredefinedReasonsString)
+  }
 }