X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo-abuse.ts;h=1319332f0738fa80b645d67f42d71c8fd33b20cc;hb=e234debc4d62e1f58b34f8af5a6139074fb7724d;hp=6cd2c041808e125d3d9ce551c39b4f839ca32a66;hpb=fc8aabd0bf38441c0591f21b9b435b52e99ffc23;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts index 6cd2c0418..1319332f0 100644 --- a/server/models/video/video-abuse.ts +++ b/server/models/video/video-abuse.ts @@ -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, parseQueryStringFilter } 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' @@ -35,21 +54,23 @@ export enum ScopeNames { // filters id?: number + predefinedReasonId?: number + state?: VideoAbuseState - is?: 'deleted' | 'blacklisted' + 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]: [ @@ -80,27 +101,27 @@ export enum ScopeNames { }) } - if (options.id) { - where = Object.assign(where, { - id: options.id - }) - } + if (options.id) Object.assign(where, { id: options.id }) + if (options.state) Object.assign(where, { state: options.state }) - if (options.state) { - where = Object.assign(where, { - state: options.state + if (options.videoIs === 'deleted') { + Object.assign(where, { + deletedVideo: { + [Op.not]: null + } }) } - let onlyBlacklisted = false - if (options.is === 'deleted') { - where = Object.assign(where, { - deletedVideo: { [Op.not]: null } + if (options.predefinedReasonId) { + Object.assign(where, { + predefinedReasons: { + [Op.contains]: [ options.predefinedReasonId ] + } }) - } else if (options.is === 'blacklisted') { - onlyBlacklisted = true } + const onlyBlacklisted = options.videoIs === 'blacklisted' + return { attributes: { include: [ @@ -189,7 +210,7 @@ export enum ScopeNames { }, { model: VideoModel, - required: onlyBlacklisted, + required: !!(onlyBlacklisted || options.searchVideo || options.searchReportee || options.searchVideoChannel), where: searchAttribute(options.searchVideo, 'name'), include: [ { @@ -253,6 +274,21 @@ export class VideoAbuseModel extends Model { @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 @@ -301,12 +337,40 @@ export class VideoAbuseModel extends Model { 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, @@ -317,43 +381,23 @@ export class VideoAbuseModel extends Model { } const filters = { - ...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 - } - }), + 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 } @@ -361,6 +405,7 @@ export class VideoAbuseModel extends Model { } 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 @@ -375,6 +420,7 @@ export class VideoAbuseModel extends Model { return { id: this.id, reason: this.reason, + predefinedReasons, reporterAccount: this.Account.toFormattedJSON(), state: { id: this.state, @@ -387,12 +433,14 @@ export class VideoAbuseModel extends Model { 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), @@ -401,14 +449,31 @@ export class VideoAbuseModel extends Model { } 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) + } }