aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
Diffstat (limited to 'server/models')
-rw-r--r--server/models/abuse/abuse.ts91
-rw-r--r--server/models/abuse/video-comment-abuse.ts2
-rw-r--r--server/models/video/video-comment.ts65
-rw-r--r--server/models/video/video.ts4
4 files changed, 142 insertions, 20 deletions
diff --git a/server/models/abuse/abuse.ts b/server/models/abuse/abuse.ts
index 4f99f9c9b..087c77bd3 100644
--- a/server/models/abuse/abuse.ts
+++ b/server/models/abuse/abuse.ts
@@ -19,16 +19,17 @@ import {
19import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' 19import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
20import { 20import {
21 Abuse, 21 Abuse,
22 AbuseFilter,
22 AbuseObject, 23 AbuseObject,
23 AbusePredefinedReasons, 24 AbusePredefinedReasons,
24 abusePredefinedReasonsMap, 25 abusePredefinedReasonsMap,
25 AbusePredefinedReasonsString, 26 AbusePredefinedReasonsString,
26 AbuseState, 27 AbuseState,
27 AbuseVideoIs, 28 AbuseVideoIs,
28 VideoAbuse 29 VideoAbuse,
30 VideoCommentAbuse
29} from '@shared/models' 31} from '@shared/models'
30import { AbuseFilter } from '@shared/models/moderation/abuse/abuse-filter' 32import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants'
31import { CONSTRAINTS_FIELDS, ABUSE_STATES } from '../../initializers/constants'
32import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models' 33import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models'
33import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' 34import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
34import { buildBlockedAccountSQL, getSort, searchAttribute, throwIfNotValid } from '../utils' 35import { buildBlockedAccountSQL, getSort, searchAttribute, throwIfNotValid } from '../utils'
@@ -38,6 +39,7 @@ import { VideoBlacklistModel } from '../video/video-blacklist'
38import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' 39import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
39import { VideoAbuseModel } from './video-abuse' 40import { VideoAbuseModel } from './video-abuse'
40import { VideoCommentAbuseModel } from './video-comment-abuse' 41import { VideoCommentAbuseModel } from './video-comment-abuse'
42import { VideoCommentModel } from '../video/video-comment'
41 43
42export enum ScopeNames { 44export enum ScopeNames {
43 FOR_API = 'FOR_API' 45 FOR_API = 'FOR_API'
@@ -66,19 +68,18 @@ export enum ScopeNames {
66 serverAccountId: number 68 serverAccountId: number
67 userAccountId: number 69 userAccountId: number
68 }) => { 70 }) => {
69 const onlyBlacklisted = options.videoIs === 'blacklisted' 71 const whereAnd: WhereOptions[] = []
70 const videoRequired = !!(onlyBlacklisted || options.searchVideo || options.searchVideoChannel)
71 72
72 const where = { 73 whereAnd.push({
73 reporterAccountId: { 74 reporterAccountId: {
74 [Op.notIn]: literal('(' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')') 75 [Op.notIn]: literal('(' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
75 } 76 }
76 } 77 })
77 78
78 if (options.search) { 79 if (options.search) {
79 const escapedSearch = AbuseModel.sequelize.escape('%' + options.search + '%') 80 const escapedSearch = AbuseModel.sequelize.escape('%' + options.search + '%')
80 81
81 Object.assign(where, { 82 whereAnd.push({
82 [Op.or]: [ 83 [Op.or]: [
83 { 84 {
84 [Op.and]: [ 85 [Op.and]: [
@@ -110,11 +111,11 @@ export enum ScopeNames {
110 }) 111 })
111 } 112 }
112 113
113 if (options.id) Object.assign(where, { id: options.id }) 114 if (options.id) whereAnd.push({ id: options.id })
114 if (options.state) Object.assign(where, { state: options.state }) 115 if (options.state) whereAnd.push({ state: options.state })
115 116
116 if (options.videoIs === 'deleted') { 117 if (options.videoIs === 'deleted') {
117 Object.assign(where, { 118 whereAnd.push({
118 '$VideoAbuse.deletedVideo$': { 119 '$VideoAbuse.deletedVideo$': {
119 [Op.not]: null 120 [Op.not]: null
120 } 121 }
@@ -122,13 +123,23 @@ export enum ScopeNames {
122 } 123 }
123 124
124 if (options.predefinedReasonId) { 125 if (options.predefinedReasonId) {
125 Object.assign(where, { 126 whereAnd.push({
126 predefinedReasons: { 127 predefinedReasons: {
127 [Op.contains]: [ options.predefinedReasonId ] 128 [Op.contains]: [ options.predefinedReasonId ]
128 } 129 }
129 }) 130 })
130 } 131 }
131 132
133 if (options.filter === 'account') {
134 whereAnd.push({
135 videoId: null,
136 commentId: null
137 })
138 }
139
140 const onlyBlacklisted = options.videoIs === 'blacklisted'
141 const videoRequired = !!(onlyBlacklisted || options.searchVideo || options.searchVideoChannel)
142
132 return { 143 return {
133 attributes: { 144 attributes: {
134 include: [ 145 include: [
@@ -223,6 +234,23 @@ export enum ScopeNames {
223 where: searchAttribute(options.searchReportee, 'name') 234 where: searchAttribute(options.searchReportee, 'name')
224 }, 235 },
225 { 236 {
237 model: VideoCommentAbuseModel.unscoped(),
238 required: options.filter === 'comment',
239 include: [
240 {
241 model: VideoCommentModel.unscoped(),
242 required: false,
243 include: [
244 {
245 model: VideoModel.unscoped(),
246 attributes: [ 'name', 'id', 'uuid' ],
247 required: true
248 }
249 ]
250 }
251 ]
252 },
253 {
226 model: VideoAbuseModel, 254 model: VideoAbuseModel,
227 required: options.filter === 'video' || !!options.videoIs || videoRequired, 255 required: options.filter === 'video' || !!options.videoIs || videoRequired,
228 include: [ 256 include: [
@@ -241,8 +269,7 @@ export enum ScopeNames {
241 include: [ 269 include: [
242 { 270 {
243 model: AccountModel.scope(AccountScopeNames.SUMMARY), 271 model: AccountModel.scope(AccountScopeNames.SUMMARY),
244 required: true, 272 required: true
245 where: searchAttribute(options.searchReportee, 'name')
246 } 273 }
247 ] 274 ]
248 }, 275 },
@@ -256,7 +283,9 @@ export enum ScopeNames {
256 ] 283 ]
257 } 284 }
258 ], 285 ],
259 where 286 where: {
287 [Op.and]: whereAnd
288 }
260 } 289 }
261 } 290 }
262})) 291}))
@@ -348,6 +377,7 @@ export class AbuseModel extends Model<AbuseModel> {
348 }) 377 })
349 VideoAbuse: VideoAbuseModel 378 VideoAbuse: VideoAbuseModel
350 379
380 // FIXME: deprecated in 2.3. Remove these validators
351 static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MAbuse> { 381 static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MAbuse> {
352 const videoWhere: WhereOptions = {} 382 const videoWhere: WhereOptions = {}
353 383
@@ -369,6 +399,16 @@ export class AbuseModel extends Model<AbuseModel> {
369 return AbuseModel.findOne(query) 399 return AbuseModel.findOne(query)
370 } 400 }
371 401
402 static loadById (id: number): Bluebird<MAbuse> {
403 const query = {
404 where: {
405 id
406 }
407 }
408
409 return AbuseModel.findOne(query)
410 }
411
372 static listForApi (parameters: { 412 static listForApi (parameters: {
373 start: number 413 start: number
374 count: number 414 count: number
@@ -454,6 +494,7 @@ export class AbuseModel extends Model<AbuseModel> {
454 const countReportsForReporteeDeletedVideo = this.get('countReportsForReportee__deletedVideo') as number 494 const countReportsForReporteeDeletedVideo = this.get('countReportsForReportee__deletedVideo') as number
455 495
456 let video: VideoAbuse 496 let video: VideoAbuse
497 let comment: VideoCommentAbuse
457 498
458 if (this.VideoAbuse) { 499 if (this.VideoAbuse) {
459 const abuseModel = this.VideoAbuse 500 const abuseModel = this.VideoAbuse
@@ -475,6 +516,24 @@ export class AbuseModel extends Model<AbuseModel> {
475 } 516 }
476 } 517 }
477 518
519 if (this.VideoCommentAbuse) {
520 const abuseModel = this.VideoCommentAbuse
521 const entity = abuseModel.VideoComment || abuseModel.deletedComment
522
523 comment = {
524 id: entity.id,
525 text: entity.text,
526
527 deleted: !abuseModel.VideoComment,
528
529 video: {
530 id: entity.Video.id,
531 name: entity.Video.name,
532 uuid: entity.Video.uuid
533 }
534 }
535 }
536
478 return { 537 return {
479 id: this.id, 538 id: this.id,
480 reason: this.reason, 539 reason: this.reason,
@@ -490,7 +549,7 @@ export class AbuseModel extends Model<AbuseModel> {
490 moderationComment: this.moderationComment, 549 moderationComment: this.moderationComment,
491 550
492 video, 551 video,
493 comment: null, 552 comment,
494 553
495 createdAt: this.createdAt, 554 createdAt: this.createdAt,
496 updatedAt: this.updatedAt, 555 updatedAt: this.updatedAt,
diff --git a/server/models/abuse/video-comment-abuse.ts b/server/models/abuse/video-comment-abuse.ts
index b4cc2762e..de9f4d5fd 100644
--- a/server/models/abuse/video-comment-abuse.ts
+++ b/server/models/abuse/video-comment-abuse.ts
@@ -25,7 +25,7 @@ export class VideoCommentAbuseModel extends Model<VideoCommentAbuseModel> {
25 @AllowNull(true) 25 @AllowNull(true)
26 @Default(null) 26 @Default(null)
27 @Column(DataType.JSONB) 27 @Column(DataType.JSONB)
28 deletedComment: VideoComment 28 deletedComment: VideoComment & { Video: { name: string, id: number, uuid: string }}
29 29
30 @ForeignKey(() => AbuseModel) 30 @ForeignKey(() => AbuseModel)
31 @Column 31 @Column
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index 90625d987..fb6078ed8 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,7 +1,22 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { uniq } from 'lodash' 2import { uniq } from 'lodash'
3import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' 3import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 4import {
5 AllowNull,
6 BeforeDestroy,
7 BelongsTo,
8 Column,
9 CreatedAt,
10 DataType,
11 ForeignKey,
12 HasMany,
13 Is,
14 Model,
15 Scopes,
16 Table,
17 UpdatedAt
18} from 'sequelize-typescript'
19import { logger } from '@server/helpers/logger'
5import { getServerActor } from '@server/models/application/application' 20import { getServerActor } from '@server/models/application/application'
6import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' 21import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
7import { VideoPrivacy } from '@shared/models' 22import { VideoPrivacy } from '@shared/models'
@@ -24,6 +39,7 @@ import {
24 MCommentOwnerVideoReply, 39 MCommentOwnerVideoReply,
25 MVideoImmutable 40 MVideoImmutable
26} from '../../types/models/video' 41} from '../../types/models/video'
42import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
27import { AccountModel } from '../account/account' 43import { AccountModel } from '../account/account'
28import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' 44import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
29import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils' 45import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils'
@@ -224,6 +240,53 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
224 }) 240 })
225 Account: AccountModel 241 Account: AccountModel
226 242
243 @HasMany(() => VideoCommentAbuseModel, {
244 foreignKey: {
245 name: 'commentId',
246 allowNull: true
247 },
248 onDelete: 'set null'
249 })
250 CommentAbuses: VideoCommentAbuseModel[]
251
252 @BeforeDestroy
253 static async saveEssentialDataToAbuses (instance: VideoCommentModel, options) {
254 const tasks: Promise<any>[] = []
255
256 if (!Array.isArray(instance.CommentAbuses)) {
257 instance.CommentAbuses = await instance.$get('CommentAbuses')
258
259 if (instance.CommentAbuses.length === 0) return undefined
260 }
261
262 if (!instance.Video) {
263 instance.Video = await instance.$get('Video')
264 }
265
266 logger.info('Saving video comment %s for abuse.', instance.url)
267
268 const details = Object.assign(instance.toFormattedJSON(), {
269 Video: {
270 id: instance.Video.id,
271 name: instance.Video.name,
272 uuid: instance.Video.uuid
273 }
274 })
275
276 for (const abuse of instance.CommentAbuses) {
277 abuse.deletedComment = details
278
279 tasks.push(abuse.save({ transaction: options.transaction }))
280 }
281
282 Promise.all(tasks)
283 .catch(err => {
284 logger.error('Some errors when saving details of comment %s in its abuses before destroy hook.', instance.url, { err })
285 })
286
287 return undefined
288 }
289
227 static loadById (id: number, t?: Transaction): Bluebird<MComment> { 290 static loadById (id: number, t?: Transaction): Bluebird<MComment> {
228 const query: FindOptions = { 291 const query: FindOptions = {
229 where: { 292 where: {
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 272bba0e1..43609587c 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -803,14 +803,14 @@ export class VideoModel extends Model<VideoModel> {
803 static async saveEssentialDataToAbuses (instance: VideoModel, options) { 803 static async saveEssentialDataToAbuses (instance: VideoModel, options) {
804 const tasks: Promise<any>[] = [] 804 const tasks: Promise<any>[] = []
805 805
806 logger.info('Saving video abuses details of video %s.', instance.url)
807
808 if (!Array.isArray(instance.VideoAbuses)) { 806 if (!Array.isArray(instance.VideoAbuses)) {
809 instance.VideoAbuses = await instance.$get('VideoAbuses') 807 instance.VideoAbuses = await instance.$get('VideoAbuses')
810 808
811 if (instance.VideoAbuses.length === 0) return undefined 809 if (instance.VideoAbuses.length === 0) return undefined
812 } 810 }
813 811
812 logger.info('Saving video abuses details of video %s.', instance.url)
813
814 const details = instance.toFormattedDetailsJSON() 814 const details = instance.toFormattedDetailsJSON()
815 815
816 for (const abuse of instance.VideoAbuses) { 816 for (const abuse of instance.VideoAbuses) {