diff options
34 files changed, 675 insertions, 183 deletions
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts index 1e137e63e..87ed33a45 100644 --- a/client/src/app/+admin/admin.component.ts +++ b/client/src/app/+admin/admin.component.ts | |||
@@ -45,7 +45,7 @@ export class AdminComponent implements OnInit { | |||
45 | children: [] | 45 | children: [] |
46 | } | 46 | } |
47 | 47 | ||
48 | if (this.hasVideoAbusesRight()) { | 48 | if (this.hasAbusesRight()) { |
49 | moderationItems.children.push({ | 49 | moderationItems.children.push({ |
50 | label: this.i18n('Video reports'), | 50 | label: this.i18n('Video reports'), |
51 | routerLink: '/admin/moderation/video-abuses/list', | 51 | routerLink: '/admin/moderation/video-abuses/list', |
@@ -76,7 +76,7 @@ export class AdminComponent implements OnInit { | |||
76 | 76 | ||
77 | if (this.hasUsersRight()) this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) | 77 | if (this.hasUsersRight()) this.menuEntries.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) |
78 | if (this.hasServerFollowRight()) this.menuEntries.push(federationItems) | 78 | if (this.hasServerFollowRight()) this.menuEntries.push(federationItems) |
79 | if (this.hasVideoAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems) | 79 | if (this.hasAbusesRight() || this.hasVideoBlocklistRight()) this.menuEntries.push(moderationItems) |
80 | if (this.hasConfigRight()) this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) | 80 | if (this.hasConfigRight()) this.menuEntries.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) |
81 | if (this.hasPluginsRight()) this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) | 81 | if (this.hasPluginsRight()) this.menuEntries.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) |
82 | if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' }) | 82 | if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.menuEntries.push({ label: this.i18n('System'), routerLink: '/admin/system' }) |
@@ -90,7 +90,7 @@ export class AdminComponent implements OnInit { | |||
90 | return this.auth.getUser().hasRight(UserRight.MANAGE_SERVER_FOLLOW) | 90 | return this.auth.getUser().hasRight(UserRight.MANAGE_SERVER_FOLLOW) |
91 | } | 91 | } |
92 | 92 | ||
93 | hasVideoAbusesRight () { | 93 | hasAbusesRight () { |
94 | return this.auth.getUser().hasRight(UserRight.MANAGE_ABUSES) | 94 | return this.auth.getUser().hasRight(UserRight.MANAGE_ABUSES) |
95 | } | 95 | } |
96 | 96 | ||
diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html index 297e6104c..2e7b322ca 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.html +++ b/client/src/app/+admin/users/user-edit/user-edit.component.html | |||
@@ -37,14 +37,14 @@ | |||
37 | </a> | 37 | </a> |
38 | </div> | 38 | </div> |
39 | <div> | 39 | <div> |
40 | <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reportee:"' + user?.account.displayName + '"' }"> | 40 | <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'reportee:"' + user?.account.displayName + '"' }"> |
41 | <div class="dashboard-num">{{ user.videoAbusesCount }}</div> | 41 | <div class="dashboard-num">{{ user.abusesCount }}</div> |
42 | <div class="dashboard-label" i18n>Incriminated in reports</div> | 42 | <div class="dashboard-label" i18n>Incriminated in reports</div> |
43 | </a> | 43 | </a> |
44 | </div> | 44 | </div> |
45 | <div> | 45 | <div> |
46 | <a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'reporter:"' + user?.account.displayName + '" state:accepted' }"> | 46 | <a [routerLink]="[ '/admin/moderation/abuses/list' ]" [queryParams]="{ 'search': 'reporter:"' + user?.account.displayName + '" state:accepted' }"> |
47 | <div class="dashboard-num">{{ user.videoAbusesAcceptedCount }} / {{ user.videoAbusesCreatedCount }}</div> | 47 | <div class="dashboard-num">{{ user.abusesAcceptedCount }} / {{ user.abusesCreatedCount }}</div> |
48 | <div class="dashboard-label" i18n>Authored reports accepted</div> | 48 | <div class="dashboard-label" i18n>Authored reports accepted</div> |
49 | </a> | 49 | </a> |
50 | </div> | 50 | </div> |
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts index adc18b587..8562e564b 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts | |||
@@ -33,7 +33,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { | |||
33 | this.labelNotifications = { | 33 | this.labelNotifications = { |
34 | newVideoFromSubscription: this.i18n('New video from your subscriptions'), | 34 | newVideoFromSubscription: this.i18n('New video from your subscriptions'), |
35 | newCommentOnMyVideo: this.i18n('New comment on your video'), | 35 | newCommentOnMyVideo: this.i18n('New comment on your video'), |
36 | videoAbuseAsModerator: this.i18n('New video abuse'), | 36 | abuseAsModerator: this.i18n('New abuse'), |
37 | videoAutoBlacklistAsModerator: this.i18n('Video blocked automatically waiting review'), | 37 | videoAutoBlacklistAsModerator: this.i18n('Video blocked automatically waiting review'), |
38 | blacklistOnMyVideo: this.i18n('One of your video is blocked/unblocked'), | 38 | blacklistOnMyVideo: this.i18n('One of your video is blocked/unblocked'), |
39 | myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'), | 39 | myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'), |
@@ -47,7 +47,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { | |||
47 | this.notificationSettingKeys = Object.keys(this.labelNotifications) as (keyof UserNotificationSetting)[] | 47 | this.notificationSettingKeys = Object.keys(this.labelNotifications) as (keyof UserNotificationSetting)[] |
48 | 48 | ||
49 | this.rightNotifications = { | 49 | this.rightNotifications = { |
50 | videoAbuseAsModerator: UserRight.MANAGE_ABUSES, | 50 | abuseAsModerator: UserRight.MANAGE_ABUSES, |
51 | videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLACKLIST, | 51 | videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLACKLIST, |
52 | newUserRegistration: UserRight.MANAGE_USERS, | 52 | newUserRegistration: UserRight.MANAGE_USERS, |
53 | newInstanceFollower: UserRight.MANAGE_SERVER_FOLLOW, | 53 | newInstanceFollower: UserRight.MANAGE_SERVER_FOLLOW, |
diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index 8ecdf9fcd..31b9c2152 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts | |||
@@ -51,12 +51,14 @@ export class User implements UserServerModel { | |||
51 | videoQuotaDaily: number | 51 | videoQuotaDaily: number |
52 | videoQuotaUsed?: number | 52 | videoQuotaUsed?: number |
53 | videoQuotaUsedDaily?: number | 53 | videoQuotaUsedDaily?: number |
54 | |||
54 | videosCount?: number | 55 | videosCount?: number |
55 | videoAbusesCount?: number | ||
56 | videoAbusesAcceptedCount?: number | ||
57 | videoAbusesCreatedCount?: number | ||
58 | videoCommentsCount?: number | 56 | videoCommentsCount?: number |
59 | 57 | ||
58 | abusesCount?: number | ||
59 | abusesAcceptedCount?: number | ||
60 | abusesCreatedCount?: number | ||
61 | |||
60 | theme: string | 62 | theme: string |
61 | 63 | ||
62 | account: Account | 64 | account: Account |
@@ -89,9 +91,9 @@ export class User implements UserServerModel { | |||
89 | this.videoQuotaUsed = hash.videoQuotaUsed | 91 | this.videoQuotaUsed = hash.videoQuotaUsed |
90 | this.videoQuotaUsedDaily = hash.videoQuotaUsedDaily | 92 | this.videoQuotaUsedDaily = hash.videoQuotaUsedDaily |
91 | this.videosCount = hash.videosCount | 93 | this.videosCount = hash.videosCount |
92 | this.videoAbusesCount = hash.videoAbusesCount | 94 | this.abusesCount = hash.abusesCount |
93 | this.videoAbusesAcceptedCount = hash.videoAbusesAcceptedCount | 95 | this.abusesAcceptedCount = hash.abusesAcceptedCount |
94 | this.videoAbusesCreatedCount = hash.videoAbusesCreatedCount | 96 | this.abusesCreatedCount = hash.abusesCreatedCount |
95 | this.videoCommentsCount = hash.videoCommentsCount | 97 | this.videoCommentsCount = hash.videoCommentsCount |
96 | 98 | ||
97 | this.nsfwPolicy = hash.nsfwPolicy | 99 | this.nsfwPolicy = hash.nsfwPolicy |
diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index 017f5219e..0be51c128 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts | |||
@@ -68,7 +68,7 @@ async function updateNotificationSettings (req: express.Request, res: express.Re | |||
68 | const values: UserNotificationSetting = { | 68 | const values: UserNotificationSetting = { |
69 | newVideoFromSubscription: body.newVideoFromSubscription, | 69 | newVideoFromSubscription: body.newVideoFromSubscription, |
70 | newCommentOnMyVideo: body.newCommentOnMyVideo, | 70 | newCommentOnMyVideo: body.newCommentOnMyVideo, |
71 | videoAbuseAsModerator: body.videoAbuseAsModerator, | 71 | abuseAsModerator: body.abuseAsModerator, |
72 | videoAutoBlacklistAsModerator: body.videoAutoBlacklistAsModerator, | 72 | videoAutoBlacklistAsModerator: body.videoAutoBlacklistAsModerator, |
73 | blacklistOnMyVideo: body.blacklistOnMyVideo, | 73 | blacklistOnMyVideo: body.blacklistOnMyVideo, |
74 | myVideoPublished: body.myVideoPublished, | 74 | myVideoPublished: body.myVideoPublished, |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 8f86bbbef..2e9d3956e 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -23,7 +23,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | 24 | // --------------------------------------------------------------------------- |
25 | 25 | ||
26 | const LAST_MIGRATION_VERSION = 515 | 26 | const LAST_MIGRATION_VERSION = 520 |
27 | 27 | ||
28 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
29 | 29 | ||
diff --git a/server/initializers/migrations/0470-cleaup-indexes.ts b/server/initializers/migrations/0470-cleanup-indexes.ts index 7365c30f8..7365c30f8 100644 --- a/server/initializers/migrations/0470-cleaup-indexes.ts +++ b/server/initializers/migrations/0470-cleanup-indexes.ts | |||
diff --git a/server/initializers/migrations/0520-abuses-split.ts b/server/initializers/migrations/0520-abuses-split.ts new file mode 100644 index 000000000..5898d501f --- /dev/null +++ b/server/initializers/migrations/0520-abuses-split.ts | |||
@@ -0,0 +1,92 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction | ||
5 | queryInterface: Sequelize.QueryInterface | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | }): Promise<void> { | ||
8 | await utils.queryInterface.renameTable('videoAbuse', 'abuse') | ||
9 | |||
10 | await utils.sequelize.query(` | ||
11 | ALTER TABLE "abuse" | ||
12 | ADD COLUMN "flaggedAccountId" INTEGER REFERENCES "account" ("id") ON DELETE SET NULL ON UPDATE CASCADE | ||
13 | `) | ||
14 | |||
15 | await utils.sequelize.query(` | ||
16 | UPDATE "abuse" SET "videoId" = NULL | ||
17 | WHERE "videoId" NOT IN (SELECT "id" FROM "video") | ||
18 | `) | ||
19 | |||
20 | await utils.sequelize.query(` | ||
21 | UPDATE "abuse" SET "flaggedAccountId" = "videoChannel"."accountId" | ||
22 | FROM "video" INNER JOIN "videoChannel" ON "video"."channelId" = "videoChannel"."id" | ||
23 | WHERE "abuse"."videoId" = "video"."id" | ||
24 | `) | ||
25 | |||
26 | await utils.sequelize.query('DROP INDEX IF EXISTS video_abuse_video_id;') | ||
27 | await utils.sequelize.query('DROP INDEX IF EXISTS video_abuse_reporter_account_id;') | ||
28 | |||
29 | await utils.sequelize.query(` | ||
30 | CREATE TABLE IF NOT EXISTS "videoAbuse" ( | ||
31 | "id" serial, | ||
32 | "startAt" integer DEFAULT NULL, | ||
33 | "endAt" integer DEFAULT NULL, | ||
34 | "deletedVideo" jsonb DEFAULT NULL, | ||
35 | "abuseId" integer NOT NULL REFERENCES "abuse" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
36 | "videoId" integer REFERENCES "video" ("id") ON DELETE SET NULL ON UPDATE CASCADE, | ||
37 | "createdAt" TIMESTAMP WITH time zone NOT NULL, | ||
38 | "updatedAt" timestamp WITH time zone NOT NULL, | ||
39 | PRIMARY KEY ("id") | ||
40 | ); | ||
41 | `) | ||
42 | |||
43 | await utils.sequelize.query(` | ||
44 | CREATE TABLE IF NOT EXISTS "commentAbuse" ( | ||
45 | "id" serial, | ||
46 | "deletedComment" jsonb DEFAULT NULL, | ||
47 | "abuseId" integer NOT NULL REFERENCES "abuse" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
48 | "videoCommentId" integer REFERENCES "videoComment" ("id") ON DELETE SET NULL ON UPDATE CASCADE, | ||
49 | "createdAt" timestamp WITH time zone NOT NULL, | ||
50 | "updatedAt" timestamp WITH time zone NOT NULL, | ||
51 | "commentId" integer REFERENCES "videoComment" ("id") ON DELETE SET NULL ON UPDATE CASCADE, | ||
52 | PRIMARY KEY ("id") | ||
53 | ); | ||
54 | `) | ||
55 | |||
56 | await utils.sequelize.query(` | ||
57 | INSERT INTO "videoAbuse" ("startAt", "endAt", "deletedVideo", "abuseId", "videoId", "createdAt", "updatedAt") | ||
58 | SELECT "abuse"."startAt", "abuse"."endAt", "abuse"."deletedVideo", "abuse"."id", "abuse"."videoId", | ||
59 | "abuse"."createdAt", "abuse"."updatedAt" | ||
60 | FROM "abuse" | ||
61 | `) | ||
62 | |||
63 | await utils.queryInterface.removeColumn('abuse', 'startAt') | ||
64 | await utils.queryInterface.removeColumn('abuse', 'endAt') | ||
65 | await utils.queryInterface.removeColumn('abuse', 'deletedVideo') | ||
66 | await utils.queryInterface.removeColumn('abuse', 'videoId') | ||
67 | |||
68 | await utils.sequelize.query('DROP INDEX IF EXISTS user_notification_video_abuse_id') | ||
69 | await utils.queryInterface.renameColumn('userNotification', 'videoAbuseId', 'abuseId') | ||
70 | await utils.sequelize.query( | ||
71 | 'ALTER TABLE "userNotification" RENAME CONSTRAINT "userNotification_videoAbuseId_fkey" TO "userNotification_abuseId_fkey"' | ||
72 | ) | ||
73 | |||
74 | await utils.sequelize.query( | ||
75 | 'ALTER TABLE "abuse" RENAME CONSTRAINT "videoAbuse_reporterAccountId_fkey" TO "abuse_reporterAccountId_fkey"' | ||
76 | ) | ||
77 | |||
78 | await utils.sequelize.query( | ||
79 | 'ALTER INDEX IF EXISTS "videoAbuse_pkey" RENAME TO "abuse_pkey"' | ||
80 | ) | ||
81 | |||
82 | await utils.queryInterface.renameColumn('userNotificationSetting', 'videoAbuseAsModerator', 'abuseAsModerator') | ||
83 | } | ||
84 | |||
85 | function down (options) { | ||
86 | throw new Error('Not implemented.') | ||
87 | } | ||
88 | |||
89 | export { | ||
90 | up, | ||
91 | down | ||
92 | } | ||
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index e821aea5f..a5664408d 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -320,7 +320,7 @@ class Emailer { | |||
320 | const commentUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath() + ';threadId=' + comment.getThreadId() | 320 | const commentUrl = WEBSERVER.URL + comment.Video.getWatchStaticPath() + ';threadId=' + comment.getThreadId() |
321 | 321 | ||
322 | emailPayload = { | 322 | emailPayload = { |
323 | template: 'comment-abuse-new', | 323 | template: 'video-comment-abuse-new', |
324 | to, | 324 | to, |
325 | subject: `New comment abuse report from ${reporter}`, | 325 | subject: `New comment abuse report from ${reporter}`, |
326 | locals: { | 326 | locals: { |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index 40cff66d2..969e393fa 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -18,7 +18,7 @@ import { CONFIG } from '../initializers/config' | |||
18 | import { AccountBlocklistModel } from '../models/account/account-blocklist' | 18 | import { AccountBlocklistModel } from '../models/account/account-blocklist' |
19 | import { UserModel } from '../models/account/user' | 19 | import { UserModel } from '../models/account/user' |
20 | import { UserNotificationModel } from '../models/account/user-notification' | 20 | import { UserNotificationModel } from '../models/account/user-notification' |
21 | import { MAbuseFull, MAbuseVideo, MAccountServer, MActorFollowFull } from '../types/models' | 21 | import { MAbuseFull, MAccountServer, MActorFollowFull } from '../types/models' |
22 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' | 22 | import { MCommentOwnerVideo, MVideoAccountLight, MVideoFullLight } from '../types/models/video' |
23 | import { isBlockedByServerOrAccount } from './blocklist' | 23 | import { isBlockedByServerOrAccount } from './blocklist' |
24 | import { Emailer } from './emailer' | 24 | import { Emailer } from './emailer' |
@@ -359,12 +359,14 @@ class Notifier { | |||
359 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES) | 359 | const moderators = await UserModel.listWithRight(UserRight.MANAGE_ABUSES) |
360 | if (moderators.length === 0) return | 360 | if (moderators.length === 0) return |
361 | 361 | ||
362 | const url = abuseInstance.VideoAbuse?.Video?.url || abuseInstance.VideoCommentAbuse?.VideoComment?.url | 362 | const url = abuseInstance.VideoAbuse?.Video?.url || |
363 | abuseInstance.VideoCommentAbuse?.VideoComment?.url || | ||
364 | abuseInstance.FlaggedAccount.Actor.url | ||
363 | 365 | ||
364 | logger.info('Notifying %s user/moderators of new abuse %s.', moderators.length, url) | 366 | logger.info('Notifying %s user/moderators of new abuse %s.', moderators.length, url) |
365 | 367 | ||
366 | function settingGetter (user: MUserWithNotificationSetting) { | 368 | function settingGetter (user: MUserWithNotificationSetting) { |
367 | return user.NotificationSetting.videoAbuseAsModerator | 369 | return user.NotificationSetting.abuseAsModerator |
368 | } | 370 | } |
369 | 371 | ||
370 | async function notificationCreator (user: MUserWithNotificationSetting) { | 372 | async function notificationCreator (user: MUserWithNotificationSetting) { |
diff --git a/server/lib/user.ts b/server/lib/user.ts index 43eef8ab1..642549879 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -133,7 +133,7 @@ function createDefaultUserNotificationSettings (user: MUserId, t: Transaction | | |||
133 | newCommentOnMyVideo: UserNotificationSettingValue.WEB, | 133 | newCommentOnMyVideo: UserNotificationSettingValue.WEB, |
134 | myVideoImportFinished: UserNotificationSettingValue.WEB, | 134 | myVideoImportFinished: UserNotificationSettingValue.WEB, |
135 | myVideoPublished: UserNotificationSettingValue.WEB, | 135 | myVideoPublished: UserNotificationSettingValue.WEB, |
136 | videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 136 | abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
137 | videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 137 | videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
138 | blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 138 | blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
139 | newUserRegistration: UserNotificationSettingValue.WEB, | 139 | newUserRegistration: UserNotificationSettingValue.WEB, |
diff --git a/server/middlewares/validators/user-notifications.ts b/server/middlewares/validators/user-notifications.ts index fbfcb0a4c..21a7be08d 100644 --- a/server/middlewares/validators/user-notifications.ts +++ b/server/middlewares/validators/user-notifications.ts | |||
@@ -25,8 +25,8 @@ const updateNotificationSettingsValidator = [ | |||
25 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video from subscription notification setting'), | 25 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video from subscription notification setting'), |
26 | body('newCommentOnMyVideo') | 26 | body('newCommentOnMyVideo') |
27 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new comment on my video notification setting'), | 27 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new comment on my video notification setting'), |
28 | body('videoAbuseAsModerator') | 28 | body('abuseAsModerator') |
29 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid new video abuse as moderator notification setting'), | 29 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid abuse as moderator notification setting'), |
30 | body('videoAutoBlacklistAsModerator') | 30 | body('videoAutoBlacklistAsModerator') |
31 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid video auto blacklist notification setting'), | 31 | .custom(isUserNotificationSettingValid).withMessage('Should have a valid video auto blacklist notification setting'), |
32 | body('blacklistOnMyVideo') | 32 | body('blacklistOnMyVideo') |
diff --git a/server/models/abuse/abuse.ts b/server/models/abuse/abuse.ts index 087c77bd3..9c17c4d51 100644 --- a/server/models/abuse/abuse.ts +++ b/server/models/abuse/abuse.ts | |||
@@ -31,15 +31,15 @@ import { | |||
31 | } from '@shared/models' | 31 | } from '@shared/models' |
32 | import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants' | 32 | import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants' |
33 | import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models' | 33 | import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models' |
34 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' | 34 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' |
35 | import { buildBlockedAccountSQL, getSort, searchAttribute, throwIfNotValid } from '../utils' | 35 | import { buildBlockedAccountSQL, getSort, searchAttribute, throwIfNotValid } from '../utils' |
36 | import { ThumbnailModel } from '../video/thumbnail' | 36 | import { ThumbnailModel } from '../video/thumbnail' |
37 | import { VideoModel } from '../video/video' | 37 | import { VideoModel } from '../video/video' |
38 | import { VideoBlacklistModel } from '../video/video-blacklist' | 38 | import { VideoBlacklistModel } from '../video/video-blacklist' |
39 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel' | 39 | import { ScopeNames as VideoChannelScopeNames, SummaryOptions as ChannelSummaryOptions, VideoChannelModel } from '../video/video-channel' |
40 | import { VideoCommentModel } from '../video/video-comment' | ||
40 | import { VideoAbuseModel } from './video-abuse' | 41 | import { VideoAbuseModel } from './video-abuse' |
41 | import { VideoCommentAbuseModel } from './video-comment-abuse' | 42 | import { VideoCommentAbuseModel } from './video-comment-abuse' |
42 | import { VideoCommentModel } from '../video/video-comment' | ||
43 | 43 | ||
44 | export enum ScopeNames { | 44 | export enum ScopeNames { |
45 | FOR_API = 'FOR_API' | 45 | FOR_API = 'FOR_API' |
@@ -149,7 +149,7 @@ export enum ScopeNames { | |||
149 | '(' + | 149 | '(' + |
150 | 'SELECT count(*) ' + | 150 | 'SELECT count(*) ' + |
151 | 'FROM "videoAbuse" ' + | 151 | 'FROM "videoAbuse" ' + |
152 | 'WHERE "videoId" = "VideoAbuse"."videoId" ' + | 152 | 'WHERE "videoId" = "VideoAbuse"."videoId" AND "videoId" IS NOT NULL' + |
153 | ')' | 153 | ')' |
154 | ), | 154 | ), |
155 | 'countReportsForVideo' | 155 | 'countReportsForVideo' |
@@ -164,7 +164,7 @@ export enum ScopeNames { | |||
164 | 'row_number() OVER (PARTITION BY "videoId" ORDER BY "createdAt") AS nth ' + | 164 | 'row_number() OVER (PARTITION BY "videoId" ORDER BY "createdAt") AS nth ' + |
165 | 'FROM "videoAbuse" ' + | 165 | 'FROM "videoAbuse" ' + |
166 | ') t ' + | 166 | ') t ' + |
167 | 'WHERE t.id = "VideoAbuse".id' + | 167 | 'WHERE t.id = "VideoAbuse".id AND t.id IS NOT NULL' + |
168 | ')' | 168 | ')' |
169 | ), | 169 | ), |
170 | 'nthReportForVideo' | 170 | 'nthReportForVideo' |
@@ -172,51 +172,22 @@ export enum ScopeNames { | |||
172 | [ | 172 | [ |
173 | literal( | 173 | literal( |
174 | '(' + | 174 | '(' + |
175 | 'SELECT count("videoAbuse"."id") ' + | 175 | 'SELECT count("abuse"."id") ' + |
176 | 'FROM "videoAbuse" ' + | 176 | 'FROM "abuse" ' + |
177 | 'INNER JOIN "video" ON "video"."id" = "videoAbuse"."videoId" ' + | 177 | 'WHERE "abuse"."reporterAccountId" = "AbuseModel"."reporterAccountId"' + |
178 | 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + | ||
179 | 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + | ||
180 | 'WHERE "account"."id" = "AbuseModel"."reporterAccountId" ' + | ||
181 | ')' | ||
182 | ), | ||
183 | 'countReportsForReporter__video' | ||
184 | ], | ||
185 | [ | ||
186 | literal( | ||
187 | '(' + | ||
188 | 'SELECT count(DISTINCT "videoAbuse"."id") ' + | ||
189 | 'FROM "videoAbuse" ' + | ||
190 | `WHERE CAST("deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) = "AbuseModel"."reporterAccountId" ` + | ||
191 | ')' | ||
192 | ), | ||
193 | 'countReportsForReporter__deletedVideo' | ||
194 | ], | ||
195 | [ | ||
196 | literal( | ||
197 | '(' + | ||
198 | 'SELECT count(DISTINCT "videoAbuse"."id") ' + | ||
199 | 'FROM "videoAbuse" ' + | ||
200 | 'INNER JOIN "video" ON "video"."id" = "videoAbuse"."videoId" ' + | ||
201 | 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + | ||
202 | 'INNER JOIN "account" ON ' + | ||
203 | '"videoChannel"."accountId" = "VideoAbuse->Video->VideoChannel"."accountId" ' + | ||
204 | `OR "videoChannel"."accountId" = CAST("VideoAbuse"."deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) ` + | ||
205 | ')' | 178 | ')' |
206 | ), | 179 | ), |
207 | 'countReportsForReportee__video' | 180 | 'countReportsForReporter' |
208 | ], | 181 | ], |
209 | [ | 182 | [ |
210 | literal( | 183 | literal( |
211 | '(' + | 184 | '(' + |
212 | 'SELECT count(DISTINCT "videoAbuse"."id") ' + | 185 | 'SELECT count("abuse"."id") ' + |
213 | 'FROM "videoAbuse" ' + | 186 | 'FROM "abuse" ' + |
214 | `WHERE CAST("deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) = "VideoAbuse->Video->VideoChannel"."accountId" ` + | 187 | 'WHERE "abuse"."flaggedAccountId" = "AbuseModel"."flaggedAccountId"' + |
215 | `OR CAST("deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) = ` + | ||
216 | `CAST("VideoAbuse"."deletedVideo"->'channel'->'ownerAccount'->>'id' AS INTEGER) ` + | ||
217 | ')' | 188 | ')' |
218 | ), | 189 | ), |
219 | 'countReportsForReportee__deletedVideo' | 190 | 'countReportsForReportee' |
220 | ] | 191 | ] |
221 | ] | 192 | ] |
222 | }, | 193 | }, |
@@ -224,13 +195,18 @@ export enum ScopeNames { | |||
224 | { | 195 | { |
225 | model: AccountModel.scope(AccountScopeNames.SUMMARY), | 196 | model: AccountModel.scope(AccountScopeNames.SUMMARY), |
226 | as: 'ReporterAccount', | 197 | as: 'ReporterAccount', |
227 | required: true, | 198 | required: !!options.searchReporter, |
228 | where: searchAttribute(options.searchReporter, 'name') | 199 | where: searchAttribute(options.searchReporter, 'name') |
229 | }, | 200 | }, |
230 | { | 201 | { |
231 | model: AccountModel.scope(AccountScopeNames.SUMMARY), | 202 | model: AccountModel.scope({ |
203 | method: [ | ||
204 | AccountScopeNames.SUMMARY, | ||
205 | { actorRequired: false } as AccountSummaryOptions | ||
206 | ] | ||
207 | }), | ||
232 | as: 'FlaggedAccount', | 208 | as: 'FlaggedAccount', |
233 | required: true, | 209 | required: !!options.searchReportee, |
234 | where: searchAttribute(options.searchReportee, 'name') | 210 | where: searchAttribute(options.searchReportee, 'name') |
235 | }, | 211 | }, |
236 | { | 212 | { |
@@ -243,35 +219,36 @@ export enum ScopeNames { | |||
243 | include: [ | 219 | include: [ |
244 | { | 220 | { |
245 | model: VideoModel.unscoped(), | 221 | model: VideoModel.unscoped(), |
246 | attributes: [ 'name', 'id', 'uuid' ], | 222 | attributes: [ 'name', 'id', 'uuid' ] |
247 | required: true | ||
248 | } | 223 | } |
249 | ] | 224 | ] |
250 | } | 225 | } |
251 | ] | 226 | ] |
252 | }, | 227 | }, |
253 | { | 228 | { |
254 | model: VideoAbuseModel, | 229 | model: VideoAbuseModel.unscoped(), |
255 | required: options.filter === 'video' || !!options.videoIs || videoRequired, | 230 | required: options.filter === 'video' || !!options.videoIs || videoRequired, |
256 | include: [ | 231 | include: [ |
257 | { | 232 | { |
258 | model: VideoModel, | 233 | attributes: [ 'id', 'uuid', 'name', 'nsfw' ], |
234 | model: VideoModel.unscoped(), | ||
259 | required: videoRequired, | 235 | required: videoRequired, |
260 | where: searchAttribute(options.searchVideo, 'name'), | 236 | where: searchAttribute(options.searchVideo, 'name'), |
261 | include: [ | 237 | include: [ |
262 | { | 238 | { |
239 | attributes: [ 'filename', 'fileUrl' ], | ||
263 | model: ThumbnailModel | 240 | model: ThumbnailModel |
264 | }, | 241 | }, |
265 | { | 242 | { |
266 | model: VideoChannelModel.scope({ method: [ VideoChannelScopeNames.SUMMARY, { withAccount: false } as SummaryOptions ] }), | 243 | model: VideoChannelModel.scope({ |
244 | method: [ | ||
245 | VideoChannelScopeNames.SUMMARY, | ||
246 | { withAccount: false, actorRequired: false } as ChannelSummaryOptions | ||
247 | ] | ||
248 | }), | ||
249 | |||
267 | where: searchAttribute(options.searchVideoChannel, 'name'), | 250 | where: searchAttribute(options.searchVideoChannel, 'name'), |
268 | required: true, | 251 | required: !!options.searchVideoChannel |
269 | include: [ | ||
270 | { | ||
271 | model: AccountModel.scope(AccountScopeNames.SUMMARY), | ||
272 | required: true | ||
273 | } | ||
274 | ] | ||
275 | }, | 252 | }, |
276 | { | 253 | { |
277 | attributes: [ 'id', 'reason', 'unfederated' ], | 254 | attributes: [ 'id', 'reason', 'unfederated' ], |
@@ -304,19 +281,19 @@ export class AbuseModel extends Model<AbuseModel> { | |||
304 | 281 | ||
305 | @AllowNull(false) | 282 | @AllowNull(false) |
306 | @Default(null) | 283 | @Default(null) |
307 | @Is('VideoAbuseReason', value => throwIfNotValid(value, isAbuseReasonValid, 'reason')) | 284 | @Is('AbuseReason', value => throwIfNotValid(value, isAbuseReasonValid, 'reason')) |
308 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ABUSES.REASON.max)) | 285 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ABUSES.REASON.max)) |
309 | reason: string | 286 | reason: string |
310 | 287 | ||
311 | @AllowNull(false) | 288 | @AllowNull(false) |
312 | @Default(null) | 289 | @Default(null) |
313 | @Is('VideoAbuseState', value => throwIfNotValid(value, isAbuseStateValid, 'state')) | 290 | @Is('AbuseState', value => throwIfNotValid(value, isAbuseStateValid, 'state')) |
314 | @Column | 291 | @Column |
315 | state: AbuseState | 292 | state: AbuseState |
316 | 293 | ||
317 | @AllowNull(true) | 294 | @AllowNull(true) |
318 | @Default(null) | 295 | @Default(null) |
319 | @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isAbuseModerationCommentValid, 'moderationComment', true)) | 296 | @Is('AbuseModerationComment', value => throwIfNotValid(value, isAbuseModerationCommentValid, 'moderationComment', true)) |
320 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ABUSES.MODERATION_COMMENT.max)) | 297 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ABUSES.MODERATION_COMMENT.max)) |
321 | moderationComment: string | 298 | moderationComment: string |
322 | 299 | ||
@@ -486,12 +463,12 @@ export class AbuseModel extends Model<AbuseModel> { | |||
486 | 463 | ||
487 | toFormattedJSON (this: MAbuseFormattable): Abuse { | 464 | toFormattedJSON (this: MAbuseFormattable): Abuse { |
488 | const predefinedReasons = AbuseModel.getPredefinedReasonsStrings(this.predefinedReasons) | 465 | const predefinedReasons = AbuseModel.getPredefinedReasonsStrings(this.predefinedReasons) |
466 | |||
489 | const countReportsForVideo = this.get('countReportsForVideo') as number | 467 | const countReportsForVideo = this.get('countReportsForVideo') as number |
490 | const nthReportForVideo = this.get('nthReportForVideo') as number | 468 | const nthReportForVideo = this.get('nthReportForVideo') as number |
491 | const countReportsForReporterVideo = this.get('countReportsForReporter__video') as number | 469 | |
492 | const countReportsForReporterDeletedVideo = this.get('countReportsForReporter__deletedVideo') as number | 470 | const countReportsForReporter = this.get('countReportsForReporter') as number |
493 | const countReportsForReporteeVideo = this.get('countReportsForReportee__video') as number | 471 | const countReportsForReportee = this.get('countReportsForReportee') as number |
494 | const countReportsForReporteeDeletedVideo = this.get('countReportsForReportee__deletedVideo') as number | ||
495 | 472 | ||
496 | let video: VideoAbuse | 473 | let video: VideoAbuse |
497 | let comment: VideoCommentAbuse | 474 | let comment: VideoCommentAbuse |
@@ -512,7 +489,11 @@ export class AbuseModel extends Model<AbuseModel> { | |||
512 | deleted: !abuseModel.Video, | 489 | deleted: !abuseModel.Video, |
513 | blacklisted: abuseModel.Video?.isBlacklisted() || false, | 490 | blacklisted: abuseModel.Video?.isBlacklisted() || false, |
514 | thumbnailPath: abuseModel.Video?.getMiniatureStaticPath(), | 491 | thumbnailPath: abuseModel.Video?.getMiniatureStaticPath(), |
515 | channel: abuseModel.Video?.VideoChannel.toFormattedJSON() || abuseModel.deletedVideo?.channel | 492 | |
493 | channel: abuseModel.Video?.VideoChannel.toFormattedJSON() || abuseModel.deletedVideo?.channel, | ||
494 | |||
495 | countReports: countReportsForVideo, | ||
496 | nthReport: nthReportForVideo | ||
516 | } | 497 | } |
517 | } | 498 | } |
518 | 499 | ||
@@ -539,7 +520,13 @@ export class AbuseModel extends Model<AbuseModel> { | |||
539 | reason: this.reason, | 520 | reason: this.reason, |
540 | predefinedReasons, | 521 | predefinedReasons, |
541 | 522 | ||
542 | reporterAccount: this.ReporterAccount.toFormattedJSON(), | 523 | reporterAccount: this.ReporterAccount |
524 | ? this.ReporterAccount.toFormattedJSON() | ||
525 | : null, | ||
526 | |||
527 | flaggedAccount: this.FlaggedAccount | ||
528 | ? this.FlaggedAccount.toFormattedJSON() | ||
529 | : null, | ||
543 | 530 | ||
544 | state: { | 531 | state: { |
545 | id: this.state, | 532 | id: this.state, |
@@ -553,14 +540,15 @@ export class AbuseModel extends Model<AbuseModel> { | |||
553 | 540 | ||
554 | createdAt: this.createdAt, | 541 | createdAt: this.createdAt, |
555 | updatedAt: this.updatedAt, | 542 | updatedAt: this.updatedAt, |
556 | count: countReportsForVideo || 0, | 543 | |
557 | nth: nthReportForVideo || 0, | 544 | countReportsForReporter: (countReportsForReporter || 0), |
558 | countReportsForReporter: (countReportsForReporterVideo || 0) + (countReportsForReporterDeletedVideo || 0), | 545 | countReportsForReportee: (countReportsForReportee || 0), |
559 | countReportsForReportee: (countReportsForReporteeVideo || 0) + (countReportsForReporteeDeletedVideo || 0), | ||
560 | 546 | ||
561 | // FIXME: deprecated in 2.3, remove this | 547 | // FIXME: deprecated in 2.3, remove this |
562 | startAt: null, | 548 | startAt: null, |
563 | endAt: null | 549 | endAt: null, |
550 | count: countReportsForVideo || 0, | ||
551 | nth: nthReportForVideo || 0 | ||
564 | } | 552 | } |
565 | } | 553 | } |
566 | 554 | ||
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 466d6258e..f97519b14 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -42,6 +42,7 @@ export enum ScopeNames { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | export type SummaryOptions = { | 44 | export type SummaryOptions = { |
45 | actorRequired?: boolean // Default: true | ||
45 | whereActor?: WhereOptions | 46 | whereActor?: WhereOptions |
46 | withAccountBlockerIds?: number[] | 47 | withAccountBlockerIds?: number[] |
47 | } | 48 | } |
@@ -65,12 +66,12 @@ export type SummaryOptions = { | |||
65 | } | 66 | } |
66 | 67 | ||
67 | const query: FindOptions = { | 68 | const query: FindOptions = { |
68 | attributes: [ 'id', 'name' ], | 69 | attributes: [ 'id', 'name', 'actorId' ], |
69 | include: [ | 70 | include: [ |
70 | { | 71 | { |
71 | attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], | 72 | attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], |
72 | model: ActorModel.unscoped(), | 73 | model: ActorModel.unscoped(), |
73 | required: true, | 74 | required: options.actorRequired ?? true, |
74 | where: whereActor, | 75 | where: whereActor, |
75 | include: [ | 76 | include: [ |
76 | serverInclude, | 77 | serverInclude, |
diff --git a/server/models/account/user-notification-setting.ts b/server/models/account/user-notification-setting.ts index b69b47265..d8f3f13da 100644 --- a/server/models/account/user-notification-setting.ts +++ b/server/models/account/user-notification-setting.ts | |||
@@ -51,11 +51,11 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM | |||
51 | @AllowNull(false) | 51 | @AllowNull(false) |
52 | @Default(null) | 52 | @Default(null) |
53 | @Is( | 53 | @Is( |
54 | 'UserNotificationSettingVideoAbuseAsModerator', | 54 | 'UserNotificationSettingAbuseAsModerator', |
55 | value => throwIfNotValid(value, isUserNotificationSettingValid, 'videoAbuseAsModerator') | 55 | value => throwIfNotValid(value, isUserNotificationSettingValid, 'abuseAsModerator') |
56 | ) | 56 | ) |
57 | @Column | 57 | @Column |
58 | videoAbuseAsModerator: UserNotificationSettingValue | 58 | abuseAsModerator: UserNotificationSettingValue |
59 | 59 | ||
60 | @AllowNull(false) | 60 | @AllowNull(false) |
61 | @Default(null) | 61 | @Default(null) |
@@ -166,7 +166,7 @@ export class UserNotificationSettingModel extends Model<UserNotificationSettingM | |||
166 | return { | 166 | return { |
167 | newCommentOnMyVideo: this.newCommentOnMyVideo, | 167 | newCommentOnMyVideo: this.newCommentOnMyVideo, |
168 | newVideoFromSubscription: this.newVideoFromSubscription, | 168 | newVideoFromSubscription: this.newVideoFromSubscription, |
169 | videoAbuseAsModerator: this.videoAbuseAsModerator, | 169 | abuseAsModerator: this.abuseAsModerator, |
170 | videoAutoBlacklistAsModerator: this.videoAutoBlacklistAsModerator, | 170 | videoAutoBlacklistAsModerator: this.videoAutoBlacklistAsModerator, |
171 | blacklistOnMyVideo: this.blacklistOnMyVideo, | 171 | blacklistOnMyVideo: this.blacklistOnMyVideo, |
172 | myVideoPublished: this.myVideoPublished, | 172 | myVideoPublished: this.myVideoPublished, |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index f21eff04b..5f45f8e7c 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -168,28 +168,26 @@ enum ScopeNames { | |||
168 | '(' + | 168 | '(' + |
169 | `SELECT concat_ws(':', "abuses", "acceptedAbuses") ` + | 169 | `SELECT concat_ws(':', "abuses", "acceptedAbuses") ` + |
170 | 'FROM (' + | 170 | 'FROM (' + |
171 | 'SELECT COUNT("videoAbuse"."id") AS "abuses", ' + | 171 | 'SELECT COUNT("abuse"."id") AS "abuses", ' + |
172 | `COUNT("videoAbuse"."id") FILTER (WHERE "videoAbuse"."state" = ${AbuseState.ACCEPTED}) AS "acceptedAbuses" ` + | 172 | `COUNT("abuse"."id") FILTER (WHERE "abuse"."state" = ${AbuseState.ACCEPTED}) AS "acceptedAbuses" ` + |
173 | 'FROM "videoAbuse" ' + | 173 | 'FROM "abuse" ' + |
174 | 'INNER JOIN "video" ON "videoAbuse"."videoId" = "video"."id" ' + | 174 | 'INNER JOIN "account" ON "account"."id" = "abuse"."flaggedAccountId" ' + |
175 | 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + | ||
176 | 'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' + | ||
177 | 'WHERE "account"."userId" = "UserModel"."id"' + | 175 | 'WHERE "account"."userId" = "UserModel"."id"' + |
178 | ') t' + | 176 | ') t' + |
179 | ')' | 177 | ')' |
180 | ), | 178 | ), |
181 | 'videoAbusesCount' | 179 | 'abusesCount' |
182 | ], | 180 | ], |
183 | [ | 181 | [ |
184 | literal( | 182 | literal( |
185 | '(' + | 183 | '(' + |
186 | 'SELECT COUNT("videoAbuse"."id") ' + | 184 | 'SELECT COUNT("abuse"."id") ' + |
187 | 'FROM "videoAbuse" ' + | 185 | 'FROM "abuse" ' + |
188 | 'INNER JOIN "account" ON "account"."id" = "videoAbuse"."reporterAccountId" ' + | 186 | 'INNER JOIN "account" ON "account"."id" = "abuse"."reporterAccountId" ' + |
189 | 'WHERE "account"."userId" = "UserModel"."id"' + | 187 | 'WHERE "account"."userId" = "UserModel"."id"' + |
190 | ')' | 188 | ')' |
191 | ), | 189 | ), |
192 | 'videoAbusesCreatedCount' | 190 | 'abusesCreatedCount' |
193 | ], | 191 | ], |
194 | [ | 192 | [ |
195 | literal( | 193 | literal( |
@@ -780,8 +778,8 @@ export class UserModel extends Model<UserModel> { | |||
780 | const videoQuotaUsed = this.get('videoQuotaUsed') | 778 | const videoQuotaUsed = this.get('videoQuotaUsed') |
781 | const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') | 779 | const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') |
782 | const videosCount = this.get('videosCount') | 780 | const videosCount = this.get('videosCount') |
783 | const [ videoAbusesCount, videoAbusesAcceptedCount ] = (this.get('videoAbusesCount') as string || ':').split(':') | 781 | const [ abusesCount, abusesAcceptedCount ] = (this.get('abusesCount') as string || ':').split(':') |
784 | const videoAbusesCreatedCount = this.get('videoAbusesCreatedCount') | 782 | const abusesCreatedCount = this.get('abusesCreatedCount') |
785 | const videoCommentsCount = this.get('videoCommentsCount') | 783 | const videoCommentsCount = this.get('videoCommentsCount') |
786 | 784 | ||
787 | const json: User = { | 785 | const json: User = { |
@@ -815,14 +813,14 @@ export class UserModel extends Model<UserModel> { | |||
815 | videosCount: videosCount !== undefined | 813 | videosCount: videosCount !== undefined |
816 | ? parseInt(videosCount + '', 10) | 814 | ? parseInt(videosCount + '', 10) |
817 | : undefined, | 815 | : undefined, |
818 | videoAbusesCount: videoAbusesCount | 816 | abusesCount: abusesCount |
819 | ? parseInt(videoAbusesCount, 10) | 817 | ? parseInt(abusesCount, 10) |
820 | : undefined, | 818 | : undefined, |
821 | videoAbusesAcceptedCount: videoAbusesAcceptedCount | 819 | abusesAcceptedCount: abusesAcceptedCount |
822 | ? parseInt(videoAbusesAcceptedCount, 10) | 820 | ? parseInt(abusesAcceptedCount, 10) |
823 | : undefined, | 821 | : undefined, |
824 | videoAbusesCreatedCount: videoAbusesCreatedCount !== undefined | 822 | abusesCreatedCount: abusesCreatedCount !== undefined |
825 | ? parseInt(videoAbusesCreatedCount + '', 10) | 823 | ? parseInt(abusesCreatedCount + '', 10) |
826 | : undefined, | 824 | : undefined, |
827 | videoCommentsCount: videoCommentsCount !== undefined | 825 | videoCommentsCount: videoCommentsCount !== undefined |
828 | ? parseInt(videoCommentsCount + '', 10) | 826 | ? parseInt(videoCommentsCount + '', 10) |
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts index 9cee64229..03a3cdf81 100644 --- a/server/models/video/video-channel.ts +++ b/server/models/video/video-channel.ts | |||
@@ -61,6 +61,7 @@ type AvailableWithStatsOptions = { | |||
61 | } | 61 | } |
62 | 62 | ||
63 | export type SummaryOptions = { | 63 | export type SummaryOptions = { |
64 | actorRequired?: boolean // Default: true | ||
64 | withAccount?: boolean // Default: false | 65 | withAccount?: boolean // Default: false |
65 | withAccountBlockerIds?: number[] | 66 | withAccountBlockerIds?: number[] |
66 | } | 67 | } |
@@ -121,7 +122,7 @@ export type SummaryOptions = { | |||
121 | { | 122 | { |
122 | attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], | 123 | attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], |
123 | model: ActorModel.unscoped(), | 124 | model: ActorModel.unscoped(), |
124 | required: true, | 125 | required: options.actorRequired ?? true, |
125 | include: [ | 126 | include: [ |
126 | { | 127 | { |
127 | attributes: [ 'host' ], | 128 | attributes: [ 'host' ], |
diff --git a/server/tests/api/check-params/user-notifications.ts b/server/tests/api/check-params/user-notifications.ts index 2048fa667..883b1d29c 100644 --- a/server/tests/api/check-params/user-notifications.ts +++ b/server/tests/api/check-params/user-notifications.ts | |||
@@ -164,7 +164,7 @@ describe('Test user notifications API validators', function () { | |||
164 | const correctFields: UserNotificationSetting = { | 164 | const correctFields: UserNotificationSetting = { |
165 | newVideoFromSubscription: UserNotificationSettingValue.WEB, | 165 | newVideoFromSubscription: UserNotificationSettingValue.WEB, |
166 | newCommentOnMyVideo: UserNotificationSettingValue.WEB, | 166 | newCommentOnMyVideo: UserNotificationSettingValue.WEB, |
167 | videoAbuseAsModerator: UserNotificationSettingValue.WEB, | 167 | abuseAsModerator: UserNotificationSettingValue.WEB, |
168 | videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB, | 168 | videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB, |
169 | blacklistOnMyVideo: UserNotificationSettingValue.WEB, | 169 | blacklistOnMyVideo: UserNotificationSettingValue.WEB, |
170 | myVideoImportFinished: UserNotificationSettingValue.WEB, | 170 | myVideoImportFinished: UserNotificationSettingValue.WEB, |
diff --git a/server/tests/api/ci-4.sh b/server/tests/api/ci-4.sh index 14a014f07..4998de364 100644 --- a/server/tests/api/ci-4.sh +++ b/server/tests/api/ci-4.sh | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | set -eu | 3 | set -eu |
4 | 4 | ||
5 | activitypubFiles=$(find server/tests/api/moderation -type f | grep -v index.ts | xargs echo) | ||
5 | redundancyFiles=$(find server/tests/api/redundancy -type f | grep -v index.ts | xargs echo) | 6 | redundancyFiles=$(find server/tests/api/redundancy -type f | grep -v index.ts | xargs echo) |
6 | activitypubFiles=$(find server/tests/api/activitypub -type f | grep -v index.ts | xargs echo) | 7 | activitypubFiles=$(find server/tests/api/activitypub -type f | grep -v index.ts | xargs echo) |
7 | 8 | ||
diff --git a/server/tests/api/index.ts b/server/tests/api/index.ts index bac77ab2e..b62e2f5f7 100644 --- a/server/tests/api/index.ts +++ b/server/tests/api/index.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | // Order of the tests we want to execute | 1 | // Order of the tests we want to execute |
2 | import './activitypub' | 2 | import './activitypub' |
3 | import './check-params' | 3 | import './check-params' |
4 | import './moderation' | ||
4 | import './notifications' | 5 | import './notifications' |
5 | import './redundancy' | 6 | import './redundancy' |
6 | import './search' | 7 | import './search' |
diff --git a/server/tests/api/moderation/abuses.ts b/server/tests/api/moderation/abuses.ts new file mode 100644 index 000000000..28c5a5531 --- /dev/null +++ b/server/tests/api/moderation/abuses.ts | |||
@@ -0,0 +1,384 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | ||
2 | |||
3 | import 'mocha' | ||
4 | import * as chai from 'chai' | ||
5 | import { Abuse, AbusePredefinedReasonsString, AbuseState } from '@shared/models' | ||
6 | import { | ||
7 | cleanupTests, | ||
8 | createUser, | ||
9 | deleteVideoAbuse, | ||
10 | flushAndRunMultipleServers, | ||
11 | getVideoAbusesList, | ||
12 | getVideosList, | ||
13 | removeVideo, | ||
14 | reportVideoAbuse, | ||
15 | ServerInfo, | ||
16 | setAccessTokensToServers, | ||
17 | updateVideoAbuse, | ||
18 | uploadVideo, | ||
19 | userLogin | ||
20 | } from '../../../../shared/extra-utils/index' | ||
21 | import { doubleFollow } from '../../../../shared/extra-utils/server/follows' | ||
22 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | ||
23 | import { | ||
24 | addAccountToServerBlocklist, | ||
25 | addServerToServerBlocklist, | ||
26 | removeAccountFromServerBlocklist, | ||
27 | removeServerFromServerBlocklist | ||
28 | } from '../../../../shared/extra-utils/users/blocklist' | ||
29 | |||
30 | const expect = chai.expect | ||
31 | |||
32 | describe('Test abuses', function () { | ||
33 | let servers: ServerInfo[] = [] | ||
34 | let abuseServer2: Abuse | ||
35 | |||
36 | before(async function () { | ||
37 | this.timeout(50000) | ||
38 | |||
39 | // Run servers | ||
40 | servers = await flushAndRunMultipleServers(2) | ||
41 | |||
42 | // Get the access tokens | ||
43 | await setAccessTokensToServers(servers) | ||
44 | |||
45 | // Server 1 and server 2 follow each other | ||
46 | await doubleFollow(servers[0], servers[1]) | ||
47 | |||
48 | // Upload some videos on each servers | ||
49 | const video1Attributes = { | ||
50 | name: 'my super name for server 1', | ||
51 | description: 'my super description for server 1' | ||
52 | } | ||
53 | await uploadVideo(servers[0].url, servers[0].accessToken, video1Attributes) | ||
54 | |||
55 | const video2Attributes = { | ||
56 | name: 'my super name for server 2', | ||
57 | description: 'my super description for server 2' | ||
58 | } | ||
59 | await uploadVideo(servers[1].url, servers[1].accessToken, video2Attributes) | ||
60 | |||
61 | // Wait videos propagation, server 2 has transcoding enabled | ||
62 | await waitJobs(servers) | ||
63 | |||
64 | const res = await getVideosList(servers[0].url) | ||
65 | const videos = res.body.data | ||
66 | |||
67 | expect(videos.length).to.equal(2) | ||
68 | |||
69 | servers[0].video = videos.find(video => video.name === 'my super name for server 1') | ||
70 | servers[1].video = videos.find(video => video.name === 'my super name for server 2') | ||
71 | }) | ||
72 | |||
73 | it('Should not have video abuses', async function () { | ||
74 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
75 | |||
76 | expect(res.body.total).to.equal(0) | ||
77 | expect(res.body.data).to.be.an('array') | ||
78 | expect(res.body.data.length).to.equal(0) | ||
79 | }) | ||
80 | |||
81 | it('Should report abuse on a local video', async function () { | ||
82 | this.timeout(15000) | ||
83 | |||
84 | const reason = 'my super bad reason' | ||
85 | await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[0].video.id, reason) | ||
86 | |||
87 | // We wait requests propagation, even if the server 1 is not supposed to make a request to server 2 | ||
88 | await waitJobs(servers) | ||
89 | }) | ||
90 | |||
91 | it('Should have 1 video abuses on server 1 and 0 on server 2', async function () { | ||
92 | const res1 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
93 | |||
94 | expect(res1.body.total).to.equal(1) | ||
95 | expect(res1.body.data).to.be.an('array') | ||
96 | expect(res1.body.data.length).to.equal(1) | ||
97 | |||
98 | const abuse: Abuse = res1.body.data[0] | ||
99 | expect(abuse.reason).to.equal('my super bad reason') | ||
100 | expect(abuse.reporterAccount.name).to.equal('root') | ||
101 | expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port) | ||
102 | expect(abuse.video.id).to.equal(servers[0].video.id) | ||
103 | expect(abuse.video.channel).to.exist | ||
104 | expect(abuse.count).to.equal(1) | ||
105 | expect(abuse.nth).to.equal(1) | ||
106 | expect(abuse.countReportsForReporter).to.equal(1) | ||
107 | expect(abuse.countReportsForReportee).to.equal(1) | ||
108 | |||
109 | const res2 = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | ||
110 | expect(res2.body.total).to.equal(0) | ||
111 | expect(res2.body.data).to.be.an('array') | ||
112 | expect(res2.body.data.length).to.equal(0) | ||
113 | }) | ||
114 | |||
115 | it('Should report abuse on a remote video', async function () { | ||
116 | this.timeout(10000) | ||
117 | |||
118 | const reason = 'my super bad reason 2' | ||
119 | await reportVideoAbuse(servers[0].url, servers[0].accessToken, servers[1].video.id, reason) | ||
120 | |||
121 | // We wait requests propagation | ||
122 | await waitJobs(servers) | ||
123 | }) | ||
124 | |||
125 | it('Should have 2 video abuses on server 1 and 1 on server 2', async function () { | ||
126 | const res1 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
127 | expect(res1.body.total).to.equal(2) | ||
128 | expect(res1.body.data).to.be.an('array') | ||
129 | expect(res1.body.data.length).to.equal(2) | ||
130 | |||
131 | const abuse1: Abuse = res1.body.data[0] | ||
132 | expect(abuse1.reason).to.equal('my super bad reason') | ||
133 | expect(abuse1.reporterAccount.name).to.equal('root') | ||
134 | expect(abuse1.reporterAccount.host).to.equal('localhost:' + servers[0].port) | ||
135 | expect(abuse1.video.id).to.equal(servers[0].video.id) | ||
136 | expect(abuse1.state.id).to.equal(AbuseState.PENDING) | ||
137 | expect(abuse1.state.label).to.equal('Pending') | ||
138 | expect(abuse1.moderationComment).to.be.null | ||
139 | expect(abuse1.count).to.equal(1) | ||
140 | expect(abuse1.nth).to.equal(1) | ||
141 | |||
142 | const abuse2: Abuse = res1.body.data[1] | ||
143 | expect(abuse2.reason).to.equal('my super bad reason 2') | ||
144 | expect(abuse2.reporterAccount.name).to.equal('root') | ||
145 | expect(abuse2.reporterAccount.host).to.equal('localhost:' + servers[0].port) | ||
146 | expect(abuse2.video.id).to.equal(servers[1].video.id) | ||
147 | expect(abuse2.state.id).to.equal(AbuseState.PENDING) | ||
148 | expect(abuse2.state.label).to.equal('Pending') | ||
149 | expect(abuse2.moderationComment).to.be.null | ||
150 | |||
151 | const res2 = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | ||
152 | expect(res2.body.total).to.equal(1) | ||
153 | expect(res2.body.data).to.be.an('array') | ||
154 | expect(res2.body.data.length).to.equal(1) | ||
155 | |||
156 | abuseServer2 = res2.body.data[0] | ||
157 | expect(abuseServer2.reason).to.equal('my super bad reason 2') | ||
158 | expect(abuseServer2.reporterAccount.name).to.equal('root') | ||
159 | expect(abuseServer2.reporterAccount.host).to.equal('localhost:' + servers[0].port) | ||
160 | expect(abuseServer2.state.id).to.equal(AbuseState.PENDING) | ||
161 | expect(abuseServer2.state.label).to.equal('Pending') | ||
162 | expect(abuseServer2.moderationComment).to.be.null | ||
163 | }) | ||
164 | |||
165 | it('Should update the state of a video abuse', async function () { | ||
166 | const body = { state: AbuseState.REJECTED } | ||
167 | await updateVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id, body) | ||
168 | |||
169 | const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | ||
170 | expect(res.body.data[0].state.id).to.equal(AbuseState.REJECTED) | ||
171 | }) | ||
172 | |||
173 | it('Should add a moderation comment', async function () { | ||
174 | const body = { state: AbuseState.ACCEPTED, moderationComment: 'It is valid' } | ||
175 | await updateVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id, body) | ||
176 | |||
177 | const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | ||
178 | expect(res.body.data[0].state.id).to.equal(AbuseState.ACCEPTED) | ||
179 | expect(res.body.data[0].moderationComment).to.equal('It is valid') | ||
180 | }) | ||
181 | |||
182 | it('Should hide video abuses from blocked accounts', async function () { | ||
183 | this.timeout(10000) | ||
184 | |||
185 | { | ||
186 | await reportVideoAbuse(servers[1].url, servers[1].accessToken, servers[0].video.uuid, 'will mute this') | ||
187 | await waitJobs(servers) | ||
188 | |||
189 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
190 | expect(res.body.total).to.equal(3) | ||
191 | } | ||
192 | |||
193 | const accountToBlock = 'root@localhost:' + servers[1].port | ||
194 | |||
195 | { | ||
196 | await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) | ||
197 | |||
198 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
199 | expect(res.body.total).to.equal(2) | ||
200 | |||
201 | const abuse = res.body.data.find(a => a.reason === 'will mute this') | ||
202 | expect(abuse).to.be.undefined | ||
203 | } | ||
204 | |||
205 | { | ||
206 | await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) | ||
207 | |||
208 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
209 | expect(res.body.total).to.equal(3) | ||
210 | } | ||
211 | }) | ||
212 | |||
213 | it('Should hide video abuses from blocked servers', async function () { | ||
214 | const serverToBlock = servers[1].host | ||
215 | |||
216 | { | ||
217 | await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host) | ||
218 | |||
219 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
220 | expect(res.body.total).to.equal(2) | ||
221 | |||
222 | const abuse = res.body.data.find(a => a.reason === 'will mute this') | ||
223 | expect(abuse).to.be.undefined | ||
224 | } | ||
225 | |||
226 | { | ||
227 | await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, serverToBlock) | ||
228 | |||
229 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
230 | expect(res.body.total).to.equal(3) | ||
231 | } | ||
232 | }) | ||
233 | |||
234 | it('Should keep the video abuse when deleting the video', async function () { | ||
235 | this.timeout(10000) | ||
236 | |||
237 | await removeVideo(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid) | ||
238 | |||
239 | await waitJobs(servers) | ||
240 | |||
241 | const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | ||
242 | expect(res.body.total).to.equal(2, "wrong number of videos returned") | ||
243 | expect(res.body.data.length).to.equal(2, "wrong number of videos returned") | ||
244 | expect(res.body.data[0].id).to.equal(abuseServer2.id, "wrong origin server id for first video") | ||
245 | |||
246 | const abuse: Abuse = res.body.data[0] | ||
247 | expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id") | ||
248 | expect(abuse.video.channel).to.exist | ||
249 | expect(abuse.video.deleted).to.be.true | ||
250 | }) | ||
251 | |||
252 | it('Should include counts of reports from reporter and reportee', async function () { | ||
253 | this.timeout(10000) | ||
254 | |||
255 | // register a second user to have two reporters/reportees | ||
256 | const user = { username: 'user2', password: 'password' } | ||
257 | await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, ...user }) | ||
258 | const userAccessToken = await userLogin(servers[0], user) | ||
259 | |||
260 | // upload a third video via this user | ||
261 | const video3Attributes = { | ||
262 | name: 'my second super name for server 1', | ||
263 | description: 'my second super description for server 1' | ||
264 | } | ||
265 | await uploadVideo(servers[0].url, userAccessToken, video3Attributes) | ||
266 | |||
267 | const res1 = await getVideosList(servers[0].url) | ||
268 | const videos = res1.body.data | ||
269 | const video3 = videos.find(video => video.name === 'my second super name for server 1') | ||
270 | |||
271 | // resume with the test | ||
272 | const reason3 = 'my super bad reason 3' | ||
273 | await reportVideoAbuse(servers[0].url, servers[0].accessToken, video3.id, reason3) | ||
274 | const reason4 = 'my super bad reason 4' | ||
275 | await reportVideoAbuse(servers[0].url, userAccessToken, servers[0].video.id, reason4) | ||
276 | |||
277 | const res2 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
278 | |||
279 | { | ||
280 | for (const abuse of res2.body.data as Abuse[]) { | ||
281 | if (abuse.video.id === video3.id) { | ||
282 | expect(abuse.count).to.equal(1, "wrong reports count for video 3") | ||
283 | expect(abuse.nth).to.equal(1, "wrong report position in report list for video 3") | ||
284 | expect(abuse.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse") | ||
285 | expect(abuse.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse") | ||
286 | } | ||
287 | if (abuse.video.id === servers[0].video.id) { | ||
288 | expect(abuse.countReportsForReportee).to.equal(3, "wrong reports count for reporter on video 1 abuse") | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | }) | ||
293 | |||
294 | it('Should list predefined reasons as well as timestamps for the reported video', async function () { | ||
295 | this.timeout(10000) | ||
296 | |||
297 | const reason5 = 'my super bad reason 5' | ||
298 | const predefinedReasons5: AbusePredefinedReasonsString[] = [ 'violentOrRepulsive', 'captions' ] | ||
299 | const createdAbuse = (await reportVideoAbuse( | ||
300 | servers[0].url, | ||
301 | servers[0].accessToken, | ||
302 | servers[0].video.id, | ||
303 | reason5, | ||
304 | predefinedReasons5, | ||
305 | 1, | ||
306 | 5 | ||
307 | )).body.abuse | ||
308 | |||
309 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
310 | |||
311 | { | ||
312 | const abuse = (res.body.data as Abuse[]).find(a => a.id === createdAbuse.id) | ||
313 | expect(abuse.reason).to.equals(reason5) | ||
314 | expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported") | ||
315 | expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported") | ||
316 | expect(abuse.video.endAt).to.equal(5, "ending timestamp doesn't match the one reported") | ||
317 | } | ||
318 | }) | ||
319 | |||
320 | it('Should delete the video abuse', async function () { | ||
321 | this.timeout(10000) | ||
322 | |||
323 | await deleteVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id) | ||
324 | |||
325 | await waitJobs(servers) | ||
326 | |||
327 | { | ||
328 | const res = await getVideoAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | ||
329 | expect(res.body.total).to.equal(1) | ||
330 | expect(res.body.data.length).to.equal(1) | ||
331 | expect(res.body.data[0].id).to.not.equal(abuseServer2.id) | ||
332 | } | ||
333 | |||
334 | { | ||
335 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | ||
336 | expect(res.body.total).to.equal(6) | ||
337 | } | ||
338 | }) | ||
339 | |||
340 | it('Should list and filter video abuses', async function () { | ||
341 | async function list (query: Omit<Parameters<typeof getVideoAbusesList>[0], 'url' | 'token'>) { | ||
342 | const options = { | ||
343 | url: servers[0].url, | ||
344 | token: servers[0].accessToken | ||
345 | } | ||
346 | |||
347 | Object.assign(options, query) | ||
348 | |||
349 | const res = await getVideoAbusesList(options) | ||
350 | |||
351 | return res.body.data as Abuse[] | ||
352 | } | ||
353 | |||
354 | expect(await list({ id: 56 })).to.have.lengthOf(0) | ||
355 | expect(await list({ id: 1 })).to.have.lengthOf(1) | ||
356 | |||
357 | expect(await list({ search: 'my super name for server 1' })).to.have.lengthOf(4) | ||
358 | expect(await list({ search: 'aaaaaaaaaaaaaaaaaaaaaaaaaa' })).to.have.lengthOf(0) | ||
359 | |||
360 | expect(await list({ searchVideo: 'my second super name for server 1' })).to.have.lengthOf(1) | ||
361 | |||
362 | expect(await list({ searchVideoChannel: 'root' })).to.have.lengthOf(4) | ||
363 | expect(await list({ searchVideoChannel: 'aaaa' })).to.have.lengthOf(0) | ||
364 | |||
365 | expect(await list({ searchReporter: 'user2' })).to.have.lengthOf(1) | ||
366 | expect(await list({ searchReporter: 'root' })).to.have.lengthOf(5) | ||
367 | |||
368 | expect(await list({ searchReportee: 'root' })).to.have.lengthOf(5) | ||
369 | expect(await list({ searchReportee: 'aaaa' })).to.have.lengthOf(0) | ||
370 | |||
371 | expect(await list({ videoIs: 'deleted' })).to.have.lengthOf(1) | ||
372 | expect(await list({ videoIs: 'blacklisted' })).to.have.lengthOf(0) | ||
373 | |||
374 | expect(await list({ state: AbuseState.ACCEPTED })).to.have.lengthOf(0) | ||
375 | expect(await list({ state: AbuseState.PENDING })).to.have.lengthOf(6) | ||
376 | |||
377 | expect(await list({ predefinedReason: 'violentOrRepulsive' })).to.have.lengthOf(1) | ||
378 | expect(await list({ predefinedReason: 'serverRules' })).to.have.lengthOf(0) | ||
379 | }) | ||
380 | |||
381 | after(async function () { | ||
382 | await cleanupTests(servers) | ||
383 | }) | ||
384 | }) | ||
diff --git a/server/tests/api/users/blocklist.ts b/server/tests/api/moderation/blocklist.ts index 8c9107a50..8c9107a50 100644 --- a/server/tests/api/users/blocklist.ts +++ b/server/tests/api/moderation/blocklist.ts | |||
diff --git a/server/tests/api/moderation/index.ts b/server/tests/api/moderation/index.ts new file mode 100644 index 000000000..cb018d88e --- /dev/null +++ b/server/tests/api/moderation/index.ts | |||
@@ -0,0 +1,2 @@ | |||
1 | export * from './abuses' | ||
2 | export * from './blocklist' | ||
diff --git a/server/tests/api/notifications/moderation-notifications.ts b/server/tests/api/notifications/moderation-notifications.ts index b90732a7a..a27681603 100644 --- a/server/tests/api/notifications/moderation-notifications.ts +++ b/server/tests/api/notifications/moderation-notifications.ts | |||
@@ -11,7 +11,7 @@ import { | |||
11 | MockInstancesIndex, | 11 | MockInstancesIndex, |
12 | registerUser, | 12 | registerUser, |
13 | removeVideoFromBlacklist, | 13 | removeVideoFromBlacklist, |
14 | reportVideoAbuse, | 14 | reportAbuse, |
15 | unfollow, | 15 | unfollow, |
16 | updateCustomConfig, | 16 | updateCustomConfig, |
17 | updateCustomSubConfig, | 17 | updateCustomSubConfig, |
@@ -74,12 +74,12 @@ describe('Test moderation notifications', function () { | |||
74 | 74 | ||
75 | const name = 'video for abuse ' + uuidv4() | 75 | const name = 'video for abuse ' + uuidv4() |
76 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) | 76 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) |
77 | const uuid = resVideo.body.video.uuid | 77 | const video = resVideo.body.video |
78 | 78 | ||
79 | await reportVideoAbuse(servers[0].url, servers[0].accessToken, uuid, 'super reason') | 79 | await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: video.id, reason: 'super reason' }) |
80 | 80 | ||
81 | await waitJobs(servers) | 81 | await waitJobs(servers) |
82 | await checkNewVideoAbuseForModerators(baseParams, uuid, name, 'presence') | 82 | await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence') |
83 | }) | 83 | }) |
84 | 84 | ||
85 | it('Should send a notification to moderators on remote video abuse', async function () { | 85 | it('Should send a notification to moderators on remote video abuse', async function () { |
@@ -87,14 +87,14 @@ describe('Test moderation notifications', function () { | |||
87 | 87 | ||
88 | const name = 'video for abuse ' + uuidv4() | 88 | const name = 'video for abuse ' + uuidv4() |
89 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) | 89 | const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name }) |
90 | const uuid = resVideo.body.video.uuid | 90 | const video = resVideo.body.video |
91 | 91 | ||
92 | await waitJobs(servers) | 92 | await waitJobs(servers) |
93 | 93 | ||
94 | await reportVideoAbuse(servers[1].url, servers[1].accessToken, uuid, 'super reason') | 94 | await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId: video.id, reason: 'super reason' }) |
95 | 95 | ||
96 | await waitJobs(servers) | 96 | await waitJobs(servers) |
97 | await checkNewVideoAbuseForModerators(baseParams, uuid, name, 'presence') | 97 | await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence') |
98 | }) | 98 | }) |
99 | }) | 99 | }) |
100 | 100 | ||
diff --git a/server/tests/api/server/email.ts b/server/tests/api/server/email.ts index 95b64a459..9c3299618 100644 --- a/server/tests/api/server/email.ts +++ b/server/tests/api/server/email.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | ||
5 | import { | 5 | import { |
6 | addVideoToBlacklist, | 6 | addVideoToBlacklist, |
7 | askResetPassword, | 7 | askResetPassword, |
@@ -11,7 +11,7 @@ import { | |||
11 | createUser, | 11 | createUser, |
12 | flushAndRunServer, | 12 | flushAndRunServer, |
13 | removeVideoFromBlacklist, | 13 | removeVideoFromBlacklist, |
14 | reportVideoAbuse, | 14 | reportAbuse, |
15 | resetPassword, | 15 | resetPassword, |
16 | ServerInfo, | 16 | ServerInfo, |
17 | setAccessTokensToServers, | 17 | setAccessTokensToServers, |
@@ -30,10 +30,15 @@ describe('Test emails', function () { | |||
30 | let userId: number | 30 | let userId: number |
31 | let userId2: number | 31 | let userId2: number |
32 | let userAccessToken: string | 32 | let userAccessToken: string |
33 | |||
33 | let videoUUID: string | 34 | let videoUUID: string |
35 | let videoId: number | ||
36 | |||
34 | let videoUserUUID: string | 37 | let videoUserUUID: string |
38 | |||
35 | let verificationString: string | 39 | let verificationString: string |
36 | let verificationString2: string | 40 | let verificationString2: string |
41 | |||
37 | const emails: object[] = [] | 42 | const emails: object[] = [] |
38 | const user = { | 43 | const user = { |
39 | username: 'user_1', | 44 | username: 'user_1', |
@@ -76,6 +81,7 @@ describe('Test emails', function () { | |||
76 | } | 81 | } |
77 | const res = await uploadVideo(server.url, server.accessToken, attributes) | 82 | const res = await uploadVideo(server.url, server.accessToken, attributes) |
78 | videoUUID = res.body.video.uuid | 83 | videoUUID = res.body.video.uuid |
84 | videoId = res.body.video.id | ||
79 | } | 85 | } |
80 | }) | 86 | }) |
81 | 87 | ||
@@ -179,7 +185,7 @@ describe('Test emails', function () { | |||
179 | this.timeout(10000) | 185 | this.timeout(10000) |
180 | 186 | ||
181 | const reason = 'my super bad reason' | 187 | const reason = 'my super bad reason' |
182 | await reportVideoAbuse(server.url, server.accessToken, videoUUID, reason) | 188 | await reportAbuse({ url: server.url, token: server.accessToken, videoId, reason }) |
183 | 189 | ||
184 | await waitJobs(server) | 190 | await waitJobs(server) |
185 | expect(emails).to.have.lengthOf(3) | 191 | expect(emails).to.have.lengthOf(3) |
diff --git a/server/tests/api/users/index.ts b/server/tests/api/users/index.ts index fcd022429..a244a6edb 100644 --- a/server/tests/api/users/index.ts +++ b/server/tests/api/users/index.ts | |||
@@ -1,5 +1,4 @@ | |||
1 | import './users-verification' | ||
2 | import './blocklist' | ||
3 | import './user-subscriptions' | 1 | import './user-subscriptions' |
4 | import './users' | 2 | import './users' |
5 | import './users-multiple-servers' | 3 | import './users-multiple-servers' |
4 | import './users-verification' | ||
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index 88b68d977..ea74bde6a 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -1,8 +1,9 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import * as chai from 'chai' | ||
4 | import 'mocha' | 3 | import 'mocha' |
5 | import { MyUser, User, UserRole, Video, AbuseState, AbuseUpdate, VideoPlaylistType } from '@shared/models' | 4 | import * as chai from 'chai' |
5 | import { AbuseState, AbuseUpdate, MyUser, User, UserRole, Video, VideoPlaylistType } from '@shared/models' | ||
6 | import { CustomConfig } from '@shared/models/server' | ||
6 | import { | 7 | import { |
7 | addVideoCommentThread, | 8 | addVideoCommentThread, |
8 | blockUser, | 9 | blockUser, |
@@ -10,6 +11,7 @@ import { | |||
10 | createUser, | 11 | createUser, |
11 | deleteMe, | 12 | deleteMe, |
12 | flushAndRunServer, | 13 | flushAndRunServer, |
14 | getAbusesList, | ||
13 | getAccountRatings, | 15 | getAccountRatings, |
14 | getBlacklistedVideosList, | 16 | getBlacklistedVideosList, |
15 | getCustomConfig, | 17 | getCustomConfig, |
@@ -19,7 +21,6 @@ import { | |||
19 | getUserInformation, | 21 | getUserInformation, |
20 | getUsersList, | 22 | getUsersList, |
21 | getUsersListPaginationAndSort, | 23 | getUsersListPaginationAndSort, |
22 | getVideoAbusesList, | ||
23 | getVideoChannel, | 24 | getVideoChannel, |
24 | getVideosList, | 25 | getVideosList, |
25 | installPlugin, | 26 | installPlugin, |
@@ -29,15 +30,15 @@ import { | |||
29 | registerUserWithChannel, | 30 | registerUserWithChannel, |
30 | removeUser, | 31 | removeUser, |
31 | removeVideo, | 32 | removeVideo, |
32 | reportVideoAbuse, | 33 | reportAbuse, |
33 | ServerInfo, | 34 | ServerInfo, |
34 | testImage, | 35 | testImage, |
35 | unblockUser, | 36 | unblockUser, |
37 | updateAbuse, | ||
36 | updateCustomSubConfig, | 38 | updateCustomSubConfig, |
37 | updateMyAvatar, | 39 | updateMyAvatar, |
38 | updateMyUser, | 40 | updateMyUser, |
39 | updateUser, | 41 | updateUser, |
40 | updateVideoAbuse, | ||
41 | uploadVideo, | 42 | uploadVideo, |
42 | userLogin, | 43 | userLogin, |
43 | waitJobs | 44 | waitJobs |
@@ -46,7 +47,6 @@ import { follow } from '../../../../shared/extra-utils/server/follows' | |||
46 | import { logout, serverLogin, setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' | 47 | import { logout, serverLogin, setAccessTokensToServers } from '../../../../shared/extra-utils/users/login' |
47 | import { getMyVideos } from '../../../../shared/extra-utils/videos/videos' | 48 | import { getMyVideos } from '../../../../shared/extra-utils/videos/videos' |
48 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | 49 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' |
49 | import { CustomConfig } from '@shared/models/server' | ||
50 | 50 | ||
51 | const expect = chai.expect | 51 | const expect = chai.expect |
52 | 52 | ||
@@ -302,10 +302,10 @@ describe('Test users', function () { | |||
302 | expect(userGet.videosCount).to.equal(0) | 302 | expect(userGet.videosCount).to.equal(0) |
303 | expect(userGet.videoCommentsCount).to.be.a('number') | 303 | expect(userGet.videoCommentsCount).to.be.a('number') |
304 | expect(userGet.videoCommentsCount).to.equal(0) | 304 | expect(userGet.videoCommentsCount).to.equal(0) |
305 | expect(userGet.videoAbusesCount).to.be.a('number') | 305 | expect(userGet.abusesCount).to.be.a('number') |
306 | expect(userGet.videoAbusesCount).to.equal(0) | 306 | expect(userGet.abusesCount).to.equal(0) |
307 | expect(userGet.videoAbusesAcceptedCount).to.be.a('number') | 307 | expect(userGet.abusesAcceptedCount).to.be.a('number') |
308 | expect(userGet.videoAbusesAcceptedCount).to.equal(0) | 308 | expect(userGet.abusesAcceptedCount).to.equal(0) |
309 | }) | 309 | }) |
310 | }) | 310 | }) |
311 | 311 | ||
@@ -895,9 +895,9 @@ describe('Test users', function () { | |||
895 | 895 | ||
896 | expect(user.videosCount).to.equal(0) | 896 | expect(user.videosCount).to.equal(0) |
897 | expect(user.videoCommentsCount).to.equal(0) | 897 | expect(user.videoCommentsCount).to.equal(0) |
898 | expect(user.videoAbusesCount).to.equal(0) | 898 | expect(user.abusesCount).to.equal(0) |
899 | expect(user.videoAbusesCreatedCount).to.equal(0) | 899 | expect(user.abusesCreatedCount).to.equal(0) |
900 | expect(user.videoAbusesAcceptedCount).to.equal(0) | 900 | expect(user.abusesAcceptedCount).to.equal(0) |
901 | }) | 901 | }) |
902 | 902 | ||
903 | it('Should report correct videos count', async function () { | 903 | it('Should report correct videos count', async function () { |
@@ -924,26 +924,26 @@ describe('Test users', function () { | |||
924 | expect(user.videoCommentsCount).to.equal(1) | 924 | expect(user.videoCommentsCount).to.equal(1) |
925 | }) | 925 | }) |
926 | 926 | ||
927 | it('Should report correct video abuses counts', async function () { | 927 | it('Should report correct abuses counts', async function () { |
928 | const reason = 'my super bad reason' | 928 | const reason = 'my super bad reason' |
929 | await reportVideoAbuse(server.url, user17AccessToken, videoId, reason) | 929 | await reportAbuse({ url: server.url, token: user17AccessToken, videoId, reason }) |
930 | 930 | ||
931 | const res1 = await getVideoAbusesList({ url: server.url, token: server.accessToken }) | 931 | const res1 = await getAbusesList({ url: server.url, token: server.accessToken }) |
932 | const abuseId = res1.body.data[0].id | 932 | const abuseId = res1.body.data[0].id |
933 | 933 | ||
934 | const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) | 934 | const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) |
935 | const user2: User = res2.body | 935 | const user2: User = res2.body |
936 | 936 | ||
937 | expect(user2.videoAbusesCount).to.equal(1) // number of incriminations | 937 | expect(user2.abusesCount).to.equal(1) // number of incriminations |
938 | expect(user2.videoAbusesCreatedCount).to.equal(1) // number of reports created | 938 | expect(user2.abusesCreatedCount).to.equal(1) // number of reports created |
939 | 939 | ||
940 | const body: AbuseUpdate = { state: AbuseState.ACCEPTED } | 940 | const body: AbuseUpdate = { state: AbuseState.ACCEPTED } |
941 | await updateVideoAbuse(server.url, server.accessToken, videoId, abuseId, body) | 941 | await updateAbuse(server.url, server.accessToken, abuseId, body) |
942 | 942 | ||
943 | const res3 = await getUserInformation(server.url, server.accessToken, user17Id, true) | 943 | const res3 = await getUserInformation(server.url, server.accessToken, user17Id, true) |
944 | const user3: User = res3.body | 944 | const user3: User = res3.body |
945 | 945 | ||
946 | expect(user3.videoAbusesAcceptedCount).to.equal(1) // number of reports created accepted | 946 | expect(user3.abusesAcceptedCount).to.equal(1) // number of reports created accepted |
947 | }) | 947 | }) |
948 | }) | 948 | }) |
949 | 949 | ||
diff --git a/server/tests/api/videos/video-abuse.ts b/server/tests/api/videos/video-abuse.ts index 20975aa4a..baeb543e0 100644 --- a/server/tests/api/videos/video-abuse.ts +++ b/server/tests/api/videos/video-abuse.ts | |||
@@ -103,8 +103,8 @@ describe('Test video abuses', function () { | |||
103 | expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port) | 103 | expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
104 | expect(abuse.video.id).to.equal(servers[0].video.id) | 104 | expect(abuse.video.id).to.equal(servers[0].video.id) |
105 | expect(abuse.video.channel).to.exist | 105 | expect(abuse.video.channel).to.exist |
106 | expect(abuse.count).to.equal(1) | 106 | expect(abuse.video.countReports).to.equal(1) |
107 | expect(abuse.nth).to.equal(1) | 107 | expect(abuse.video.nthReport).to.equal(1) |
108 | expect(abuse.countReportsForReporter).to.equal(1) | 108 | expect(abuse.countReportsForReporter).to.equal(1) |
109 | expect(abuse.countReportsForReportee).to.equal(1) | 109 | expect(abuse.countReportsForReportee).to.equal(1) |
110 | 110 | ||
@@ -138,8 +138,8 @@ describe('Test video abuses', function () { | |||
138 | expect(abuse1.state.id).to.equal(AbuseState.PENDING) | 138 | expect(abuse1.state.id).to.equal(AbuseState.PENDING) |
139 | expect(abuse1.state.label).to.equal('Pending') | 139 | expect(abuse1.state.label).to.equal('Pending') |
140 | expect(abuse1.moderationComment).to.be.null | 140 | expect(abuse1.moderationComment).to.be.null |
141 | expect(abuse1.count).to.equal(1) | 141 | expect(abuse1.video.countReports).to.equal(1) |
142 | expect(abuse1.nth).to.equal(1) | 142 | expect(abuse1.video.nthReport).to.equal(1) |
143 | 143 | ||
144 | const abuse2: Abuse = res1.body.data[1] | 144 | const abuse2: Abuse = res1.body.data[1] |
145 | expect(abuse2.reason).to.equal('my super bad reason 2') | 145 | expect(abuse2.reason).to.equal('my super bad reason 2') |
@@ -281,8 +281,8 @@ describe('Test video abuses', function () { | |||
281 | { | 281 | { |
282 | for (const abuse of res2.body.data as Abuse[]) { | 282 | for (const abuse of res2.body.data as Abuse[]) { |
283 | if (abuse.video.id === video3.id) { | 283 | if (abuse.video.id === video3.id) { |
284 | expect(abuse.count).to.equal(1, "wrong reports count for video 3") | 284 | expect(abuse.video.countReports).to.equal(1, "wrong reports count for video 3") |
285 | expect(abuse.nth).to.equal(1, "wrong report position in report list for video 3") | 285 | expect(abuse.video.nthReport).to.equal(1, "wrong report position in report list for video 3") |
286 | expect(abuse.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse") | 286 | expect(abuse.countReportsForReportee).to.equal(1, "wrong reports count for reporter on video 3 abuse") |
287 | expect(abuse.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse") | 287 | expect(abuse.countReportsForReporter).to.equal(3, "wrong reports count for reportee on video 3 abuse") |
288 | } | 288 | } |
diff --git a/server/types/models/moderation/abuse.ts b/server/types/models/moderation/abuse.ts index 8e12be874..a0bf4b08f 100644 --- a/server/types/models/moderation/abuse.ts +++ b/server/types/models/moderation/abuse.ts | |||
@@ -98,5 +98,6 @@ export type MAbuseFull = | |||
98 | export type MAbuseFormattable = | 98 | export type MAbuseFormattable = |
99 | MAbuse & | 99 | MAbuse & |
100 | Use<'ReporterAccount', MAccountFormattable> & | 100 | Use<'ReporterAccount', MAccountFormattable> & |
101 | Use<'FlaggedAccount', MAccountFormattable> & | ||
101 | Use<'VideoAbuse', MVideoAbuseFormattable> & | 102 | Use<'VideoAbuse', MVideoAbuseFormattable> & |
102 | Use<'VideoCommentAbuse', MCommentAbuseFormattable> | 103 | Use<'VideoCommentAbuse', MCommentAbuseFormattable> |
diff --git a/shared/extra-utils/users/user-notifications.ts b/shared/extra-utils/users/user-notifications.ts index 62f3418c5..4a5bc30fe 100644 --- a/shared/extra-utils/users/user-notifications.ts +++ b/shared/extra-utils/users/user-notifications.ts | |||
@@ -516,7 +516,7 @@ function getAllNotificationsSettings () { | |||
516 | return { | 516 | return { |
517 | newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 517 | newVideoFromSubscription: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
518 | newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 518 | newCommentOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
519 | videoAbuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 519 | abuseAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
520 | videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 520 | videoAutoBlacklistAsModerator: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
521 | blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 521 | blacklistOnMyVideo: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
522 | myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, | 522 | myVideoImportFinished: UserNotificationSettingValue.WEB | UserNotificationSettingValue.EMAIL, |
diff --git a/shared/models/moderation/abuse/abuse.model.ts b/shared/models/moderation/abuse/abuse.model.ts index a120803e6..086911ad5 100644 --- a/shared/models/moderation/abuse/abuse.model.ts +++ b/shared/models/moderation/abuse/abuse.model.ts | |||
@@ -18,6 +18,9 @@ export interface VideoAbuse { | |||
18 | 18 | ||
19 | thumbnailPath?: string | 19 | thumbnailPath?: string |
20 | channel?: VideoChannel | 20 | channel?: VideoChannel |
21 | |||
22 | countReports: number | ||
23 | nthReport: number | ||
21 | } | 24 | } |
22 | 25 | ||
23 | export interface VideoCommentAbuse { | 26 | export interface VideoCommentAbuse { |
@@ -36,9 +39,12 @@ export interface VideoCommentAbuse { | |||
36 | 39 | ||
37 | export interface Abuse { | 40 | export interface Abuse { |
38 | id: number | 41 | id: number |
42 | |||
39 | reason: string | 43 | reason: string |
40 | predefinedReasons?: AbusePredefinedReasonsString[] | 44 | predefinedReasons?: AbusePredefinedReasonsString[] |
45 | |||
41 | reporterAccount: Account | 46 | reporterAccount: Account |
47 | flaggedAccount: Account | ||
42 | 48 | ||
43 | state: VideoConstant<AbuseState> | 49 | state: VideoConstant<AbuseState> |
44 | moderationComment?: string | 50 | moderationComment?: string |
@@ -49,13 +55,18 @@ export interface Abuse { | |||
49 | createdAt: Date | 55 | createdAt: Date |
50 | updatedAt: Date | 56 | updatedAt: Date |
51 | 57 | ||
52 | // FIXME: deprecated in 2.3, remove this | ||
53 | startAt: null | ||
54 | endAt: null | ||
55 | |||
56 | count?: number | ||
57 | nth?: number | ||
58 | |||
59 | countReportsForReporter?: number | 58 | countReportsForReporter?: number |
60 | countReportsForReportee?: number | 59 | countReportsForReportee?: number |
60 | |||
61 | // FIXME: deprecated in 2.3, remove the following properties | ||
62 | |||
63 | // // @deprecated | ||
64 | // startAt: null | ||
65 | // // @deprecated | ||
66 | // endAt: null | ||
67 | |||
68 | // // @deprecated | ||
69 | // count?: number | ||
70 | // // @deprecated | ||
71 | // nth?: number | ||
61 | } | 72 | } |
diff --git a/shared/models/users/user-notification-setting.model.ts b/shared/models/users/user-notification-setting.model.ts index 451f40d58..4e2230a76 100644 --- a/shared/models/users/user-notification-setting.model.ts +++ b/shared/models/users/user-notification-setting.model.ts | |||
@@ -7,7 +7,7 @@ export enum UserNotificationSettingValue { | |||
7 | export interface UserNotificationSetting { | 7 | export interface UserNotificationSetting { |
8 | newVideoFromSubscription: UserNotificationSettingValue | 8 | newVideoFromSubscription: UserNotificationSettingValue |
9 | newCommentOnMyVideo: UserNotificationSettingValue | 9 | newCommentOnMyVideo: UserNotificationSettingValue |
10 | videoAbuseAsModerator: UserNotificationSettingValue | 10 | abuseAsModerator: UserNotificationSettingValue |
11 | videoAutoBlacklistAsModerator: UserNotificationSettingValue | 11 | videoAutoBlacklistAsModerator: UserNotificationSettingValue |
12 | blacklistOnMyVideo: UserNotificationSettingValue | 12 | blacklistOnMyVideo: UserNotificationSettingValue |
13 | myVideoPublished: UserNotificationSettingValue | 13 | myVideoPublished: UserNotificationSettingValue |
diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts index 6c959ceea..859736b2f 100644 --- a/shared/models/users/user.model.ts +++ b/shared/models/users/user.model.ts | |||
@@ -31,10 +31,13 @@ export interface User { | |||
31 | videoQuotaDaily: number | 31 | videoQuotaDaily: number |
32 | videoQuotaUsed?: number | 32 | videoQuotaUsed?: number |
33 | videoQuotaUsedDaily?: number | 33 | videoQuotaUsedDaily?: number |
34 | |||
34 | videosCount?: number | 35 | videosCount?: number |
35 | videoAbusesCount?: number | 36 | |
36 | videoAbusesAcceptedCount?: number | 37 | abusesCount?: number |
37 | videoAbusesCreatedCount?: number | 38 | abusesAcceptedCount?: number |
39 | abusesCreatedCount?: number | ||
40 | |||
38 | videoCommentsCount? : number | 41 | videoCommentsCount? : number |
39 | 42 | ||
40 | theme: string | 43 | theme: string |
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 79f75063f..03e60925b 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml | |||
@@ -893,7 +893,7 @@ paths: | |||
893 | $ref: '#/components/schemas/NotificationSettingValue' | 893 | $ref: '#/components/schemas/NotificationSettingValue' |
894 | newCommentOnMyVideo: | 894 | newCommentOnMyVideo: |
895 | $ref: '#/components/schemas/NotificationSettingValue' | 895 | $ref: '#/components/schemas/NotificationSettingValue' |
896 | videoAbuseAsModerator: | 896 | abuseAsModerator: |
897 | $ref: '#/components/schemas/NotificationSettingValue' | 897 | $ref: '#/components/schemas/NotificationSettingValue' |
898 | videoAutoBlacklistAsModerator: | 898 | videoAutoBlacklistAsModerator: |
899 | $ref: '#/components/schemas/NotificationSettingValue' | 899 | $ref: '#/components/schemas/NotificationSettingValue' |
@@ -1618,7 +1618,7 @@ paths: | |||
1618 | type: object | 1618 | type: object |
1619 | properties: | 1619 | properties: |
1620 | state: | 1620 | state: |
1621 | $ref: '#/components/schemas/VideoAbuseStateSet' | 1621 | $ref: '#/components/schemas/AbuseStateSet' |
1622 | moderationComment: | 1622 | moderationComment: |
1623 | type: string | 1623 | type: string |
1624 | description: Update the report comment visible only to the moderation team | 1624 | description: Update the report comment visible only to the moderation team |
@@ -3584,20 +3584,20 @@ components: | |||
3584 | label: | 3584 | label: |
3585 | type: string | 3585 | type: string |
3586 | 3586 | ||
3587 | VideoAbuseStateSet: | 3587 | AbuseStateSet: |
3588 | type: integer | 3588 | type: integer |
3589 | enum: | 3589 | enum: |
3590 | - 1 | 3590 | - 1 |
3591 | - 2 | 3591 | - 2 |
3592 | - 3 | 3592 | - 3 |
3593 | description: 'The video playlist privacy (Pending = `1`, Rejected = `2`, Accepted = `3`)' | 3593 | description: 'The video playlist privacy (Pending = `1`, Rejected = `2`, Accepted = `3`)' |
3594 | VideoAbuseStateConstant: | 3594 | AbuseStateConstant: |
3595 | properties: | 3595 | properties: |
3596 | id: | 3596 | id: |
3597 | $ref: '#/components/schemas/VideoAbuseStateSet' | 3597 | $ref: '#/components/schemas/AbuseStateSet' |
3598 | label: | 3598 | label: |
3599 | type: string | 3599 | type: string |
3600 | VideoAbusePredefinedReasons: | 3600 | AbusePredefinedReasons: |
3601 | type: array | 3601 | type: array |
3602 | items: | 3602 | items: |
3603 | type: string | 3603 | type: string |
@@ -3960,11 +3960,11 @@ components: | |||
3960 | type: string | 3960 | type: string |
3961 | example: The video is a spam | 3961 | example: The video is a spam |
3962 | predefinedReasons: | 3962 | predefinedReasons: |
3963 | $ref: '#/components/schemas/VideoAbusePredefinedReasons' | 3963 | $ref: '#/components/schemas/AbusePredefinedReasons' |
3964 | reporterAccount: | 3964 | reporterAccount: |
3965 | $ref: '#/components/schemas/Account' | 3965 | $ref: '#/components/schemas/Account' |
3966 | state: | 3966 | state: |
3967 | $ref: '#/components/schemas/VideoAbuseStateConstant' | 3967 | $ref: '#/components/schemas/AbuseStateConstant' |
3968 | moderationComment: | 3968 | moderationComment: |
3969 | type: string | 3969 | type: string |
3970 | example: Decided to ban the server since it spams us regularly | 3970 | example: Decided to ban the server since it spams us regularly |
@@ -4690,11 +4690,11 @@ components: | |||
4690 | description: The user daily video quota | 4690 | description: The user daily video quota |
4691 | videosCount: | 4691 | videosCount: |
4692 | type: integer | 4692 | type: integer |
4693 | videoAbusesCount: | 4693 | abusesCount: |
4694 | type: integer | 4694 | type: integer |
4695 | videoAbusesAcceptedCount: | 4695 | abusesAcceptedCount: |
4696 | type: integer | 4696 | type: integer |
4697 | videoAbusesCreatedCount: | 4697 | abusesCreatedCount: |
4698 | type: integer | 4698 | type: integer |
4699 | videoCommentsCount: | 4699 | videoCommentsCount: |
4700 | type: integer | 4700 | type: integer |