diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
index f8ef78c815d..5512bb1dea8 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
+++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.html
@@ -60,7 +60,7 @@
-
+
{{ reason.label }}
diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.ts b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.ts
index b79580868a8..13485124f21 100644
--- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.ts
+++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-details.component.ts
@@ -1,6 +1,6 @@
import { Component, Input } from '@angular/core'
import { Actor } from '@app/shared/actor/actor.model'
-import { VideoAbusePredefinedReasons } from '../../../../../../shared/models/videos/abuse/video-abuse-reason.model'
+import { VideoAbusePredefinedReasonsString } from '../../../../../../shared/models/videos/abuse/video-abuse-reason.model'
import { ProcessedVideoAbuse } from './video-abuse-list.component'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { durationToString } from '@app/shared/misc/utils'
@@ -13,20 +13,20 @@ import { durationToString } from '@app/shared/misc/utils'
export class VideoAbuseDetailsComponent {
@Input() videoAbuse: ProcessedVideoAbuse
- private predefinedReasonsTranslations: { [key: number]: string }
+ private predefinedReasonsTranslations: { [key in VideoAbusePredefinedReasonsString]: string }
constructor (
private i18n: I18n
) {
this.predefinedReasonsTranslations = {
- [VideoAbusePredefinedReasons.VIOLENT_OR_REPULSIVE]: this.i18n('Violent or Repulsive'),
- [VideoAbusePredefinedReasons.HATEFUL_OR_ABUSIVE]: this.i18n('Hateful or Abusive'),
- [VideoAbusePredefinedReasons.SPAM_OR_MISLEADING]: this.i18n('Spam or Misleading'),
- [VideoAbusePredefinedReasons.PRIVACY]: this.i18n('Privacy'),
- [VideoAbusePredefinedReasons.RIGHTS]: this.i18n('Rights'),
- [VideoAbusePredefinedReasons.SERVER_RULES]: this.i18n('Server rules'),
- [VideoAbusePredefinedReasons.THUMBNAILS]: this.i18n('Thumbnails'),
- [VideoAbusePredefinedReasons.CAPTIONS]: this.i18n('Captions')
+ violentOrRepulsive: this.i18n('Violent or Repulsive'),
+ hatefulOrAbusive: this.i18n('Hateful or Abusive'),
+ spamOrMisleading: this.i18n('Spam or Misleading'),
+ privacy: this.i18n('Privacy'),
+ rights: this.i18n('Rights'),
+ serverRules: this.i18n('Server rules'),
+ thumbnails: this.i18n('Thumbnails'),
+ captions: this.i18n('Captions')
}
}
diff --git a/client/src/app/shared/video-abuse/video-abuse.service.ts b/client/src/app/shared/video-abuse/video-abuse.service.ts
index 0b102a88d5f..43f4674b14b 100644
--- a/client/src/app/shared/video-abuse/video-abuse.service.ts
+++ b/client/src/app/shared/video-abuse/video-abuse.service.ts
@@ -53,18 +53,7 @@ export class VideoAbuseService {
},
searchReporter: { prefix: 'reporter:' },
searchReportee: { prefix: 'reportee:' },
- predefinedReasonId: {
- prefix: 'tag:',
- handler: v => {
- try {
- const id = parseInt(v, 10)
- return id
- } catch (e) {
- console.error('Cannot parse predefinedReasonId in search.', e)
- return undefined
- }
- }
- }
+ predefinedReason: { prefix: 'tag:' }
})
params = this.restService.addObjectParams(params, filters)
diff --git a/client/src/app/shared/video/modals/video-report.component.ts b/client/src/app/shared/video/modals/video-report.component.ts
index 39085a069a7..0a01a4a179a 100644
--- a/client/src/app/shared/video/modals/video-report.component.ts
+++ b/client/src/app/shared/video/modals/video-report.component.ts
@@ -10,8 +10,8 @@ import { VideoAbuseService } from '@app/shared/video-abuse'
import { Video } from '@app/shared/video/video.model'
import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
-import { VideoAbusePredefinedReasonsIn } from '@shared/models/videos/abuse/video-abuse-reason.model'
-import { mapValues, pickBy, keys } from 'lodash-es'
+import { VideoAbusePredefinedReasonsString, VideoAbusePredefinedReasonsMap } from '@shared/models/videos/abuse/video-abuse-reason.model'
+import { mapValues, pickBy } from 'lodash-es'
@Component({
selector: 'my-video-report',
@@ -24,7 +24,7 @@ export class VideoReportComponent extends FormReactive implements OnInit {
@ViewChild('modal', { static: true }) modal: NgbModal
error: string = null
- predefinedReasons: { id: VideoAbusePredefinedReasonsIn, label: string, description?: string, help?: string }[] = []
+ predefinedReasons: { id: VideoAbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = []
embedHtml: SafeHtml
private openedModal: NgbModalRef
@@ -72,7 +72,7 @@ export class VideoReportComponent extends FormReactive implements OnInit {
ngOnInit () {
this.buildForm({
reason: this.videoAbuseValidatorsService.VIDEO_ABUSE_REASON,
- predefinedReasons: mapValues(VideoAbusePredefinedReasonsIn, r => null),
+ predefinedReasons: mapValues(VideoAbusePredefinedReasonsMap, r => null),
timestamp: {
hasStart: null,
startAt: null,
@@ -138,7 +138,7 @@ export class VideoReportComponent extends FormReactive implements OnInit {
report () {
const reason = this.form.get('reason').value
- const predefinedReasons = keys(pickBy(this.form.get('predefinedReasons').value)) as VideoAbusePredefinedReasonsIn[]
+ const predefinedReasons = Object.keys(pickBy(this.form.get('predefinedReasons').value)) as VideoAbusePredefinedReasonsString[]
const { hasStart, startAt, hasEnd, endAt } = this.form.get('timestamp').value
this.videoAbuseService.reportVideo({
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss
index eb80ea0e32b..6a1deac7677 100644
--- a/client/src/sass/include/_mixins.scss
+++ b/client/src/sass/include/_mixins.scss
@@ -804,10 +804,12 @@
}
@mixin chip {
+ --chip-radius: 5rem;
+ --chip-padding: .2rem .4rem;
$avatar-height: 1.2rem;
align-items: center;
- border-radius: 5rem;
+ border-radius: var(--chip-radius);
display: inline-flex;
font-size: 90%;
color: pvar(--mainForegroundColor);
@@ -816,12 +818,17 @@
margin: .1rem;
max-width: 320px;
overflow: hidden;
- padding: .2rem .4rem;
+ padding: var(--chip-padding);
text-decoration: none;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
+ &.rectangular {
+ --chip-radius: .2rem;
+ --chip-padding: .2rem .3rem;
+ }
+
.avatar {
margin-left: -.4rem;
margin-right: .2rem;
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index ff34da9d6be..90f0f16243b 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -1,5 +1,5 @@
import * as express from 'express'
-import { UserRight, VideoAbuseCreate, VideoAbuseState, VideoAbuse } from '../../../../shared'
+import { UserRight, VideoAbuseCreate, VideoAbuseState, VideoAbuse, VideoAbusePredefinedReasonsMap } from '../../../../shared'
import { logger } from '../../../helpers/logger'
import { getFormattedObjects } from '../../../helpers/utils'
import { sequelizeTypescript } from '../../../initializers/database'
@@ -25,8 +25,6 @@ import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag'
import { MVideoAbuseAccountVideo } from '../../../types/models/video'
import { getServerActor } from '@server/models/application/application'
import { MAccountDefault } from '@server/types/models'
-import { keys, pickBy } from 'lodash'
-import { VideoAbusePredefinedReasonsIn } from '@shared/models/videos/abuse/video-abuse-reason.model'
const auditLogger = auditLoggerFactory('abuse')
const abuseVideoRouter = express.Router()
@@ -76,7 +74,7 @@ async function listVideoAbuses (req: express.Request, res: express.Response) {
count: req.query.count,
sort: req.query.sort,
id: req.query.id,
- predefinedReasonId: req.query.predefinedReasonId,
+ predefinedReason: req.query.predefinedReason,
search: req.query.search,
state: req.query.state,
videoIs: req.query.videoIs,
@@ -126,7 +124,7 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
const videoAbuseInstance = await sequelizeTypescript.transaction(async t => {
reporterAccount = await AccountModel.load(res.locals.oauth.token.User.Account.id, t)
- const predefinedReasons = body.predefinedReasons.map(r => VideoAbusePredefinedReasonsIn[r])
+ const predefinedReasons = body.predefinedReasons?.map(r => VideoAbusePredefinedReasonsMap[r])
const abuseToCreate = {
reporterAccountId: reporterAccount.id,
diff --git a/server/helpers/custom-validators/video-abuses.ts b/server/helpers/custom-validators/video-abuses.ts
index d3a83e040ad..061353b9ebd 100644
--- a/server/helpers/custom-validators/video-abuses.ts
+++ b/server/helpers/custom-validators/video-abuses.ts
@@ -1,10 +1,9 @@
import validator from 'validator'
-import { keys } from 'lodash'
import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
-import { exists } from './misc'
+import { exists, isArray } from './misc'
import { VideoAbuseVideoIs } from '@shared/models/videos/abuse/video-abuse-video-is.type'
-import { VideoAbusePredefinedReasonsIn } from '@shared/models/videos/abuse/video-abuse-reason.model'
+import { VideoAbusePredefinedReasonsString, VideoAbusePredefinedReasonsMap } from '@shared/models/videos/abuse/video-abuse-reason.model'
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
@@ -12,8 +11,16 @@ function isVideoAbuseReasonValid (value: string) {
return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
}
-function isVideoAbusePredefinedReasonsValid (value: VideoAbusePredefinedReasonsIn[]) {
- return exists(value) && value.every(element => keys(VideoAbusePredefinedReasonsIn).includes(element))
+function isVideoAbusePredefinedReasonValid (value: VideoAbusePredefinedReasonsString) {
+ return exists(value) && value in VideoAbusePredefinedReasonsMap
+}
+
+function isVideoAbusePredefinedReasonsValid (value: VideoAbusePredefinedReasonsString[]) {
+ return exists(value) && isArray(value) && value.every(v => v in VideoAbusePredefinedReasonsMap)
+}
+
+function isVideoAbuseTimestampValid (value: number) {
+ return value === null || (exists(value) && validator.isInt('' + value, { min: 0 }))
}
function isVideoAbuseModerationCommentValid (value: string) {
@@ -35,7 +42,9 @@ function isAbuseVideoIsValid (value: VideoAbuseVideoIs) {
export {
isVideoAbuseReasonValid,
+ isVideoAbusePredefinedReasonValid,
isVideoAbusePredefinedReasonsValid,
+ isVideoAbuseTimestampValid,
isVideoAbuseModerationCommentValid,
isVideoAbuseStateValid,
isAbuseVideoIsValid
diff --git a/server/initializers/migrations/0515-video-abuse-reason-timestamps.ts b/server/initializers/migrations/0515-video-abuse-reason-timestamps.ts
index 964a5f588b7..c583356171d 100644
--- a/server/initializers/migrations/0515-video-abuse-reason-timestamps.ts
+++ b/server/initializers/migrations/0515-video-abuse-reason-timestamps.ts
@@ -5,6 +5,11 @@ async function up (utils: {
queryInterface: Sequelize.QueryInterface
sequelize: Sequelize.Sequelize
}): Promise
{
+ await utils.queryInterface.addColumn('videoAbuse', 'predefinedReasons', {
+ type: Sequelize.ARRAY(Sequelize.INTEGER),
+ allowNull: true
+ })
+
await utils.queryInterface.addColumn('videoAbuse', 'startAt', {
type: Sequelize.INTEGER,
allowNull: true
diff --git a/server/lib/activitypub/process/process-flag.ts b/server/lib/activitypub/process/process-flag.ts
index 7de75d7962a..42a86c120db 100644
--- a/server/lib/activitypub/process/process-flag.ts
+++ b/server/lib/activitypub/process/process-flag.ts
@@ -1,4 +1,9 @@
-import { ActivityCreate, ActivityFlag, VideoAbuseState } from '../../../../shared'
+import {
+ ActivityCreate,
+ ActivityFlag,
+ VideoAbuseState,
+ VideoAbusePredefinedReasonsMap
+} from '../../../../shared'
import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects'
import { retryTransactionWrapper } from '../../../helpers/database-utils'
import { logger } from '../../../helpers/logger'
@@ -39,7 +44,7 @@ async function processCreateVideoAbuse (activity: ActivityCreate | ActivityFlag,
const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: object })
const reporterAccount = await sequelizeTypescript.transaction(async t => AccountModel.load(account.id, t))
const tags = Array.isArray(flag.tag) ? flag.tag : []
- const predefinedReasons = tags.map(tag => parseInt(tag.name, 10))
+ const predefinedReasons = tags.map(tag => VideoAbusePredefinedReasonsMap[tag.name])
.filter(v => !isNaN(v))
const startAt = flag.startAt
const endAt = flag.endAt
diff --git a/server/middlewares/validators/videos/video-abuses.ts b/server/middlewares/validators/videos/video-abuses.ts
index d0b2f7f0336..33bd4a91239 100644
--- a/server/middlewares/validators/videos/video-abuses.ts
+++ b/server/middlewares/validators/videos/video-abuses.ts
@@ -6,7 +6,9 @@ import {
isVideoAbuseModerationCommentValid,
isVideoAbuseReasonValid,
isVideoAbuseStateValid,
- isVideoAbusePredefinedReasonsValid
+ isVideoAbusePredefinedReasonsValid,
+ isVideoAbusePredefinedReasonValid,
+ isVideoAbuseTimestampValid
} from '../../../helpers/custom-validators/video-abuses'
import { logger } from '../../../helpers/logger'
import { doesVideoAbuseExist, doesVideoExist } from '../../../helpers/middlewares'
@@ -27,11 +29,13 @@ const videoAbuseReportValidator = [
.withMessage('Should have a valid list of predefined reasons'),
body('startAt')
.optional()
- .custom(toIntOrNull)
+ .customSanitizer(toIntOrNull)
+ .custom(isVideoAbuseTimestampValid)
.withMessage('Should have valid starting time value'),
body('endAt')
.optional()
- .custom(toIntOrNull)
+ .customSanitizer(toIntOrNull)
+ .custom(isVideoAbuseTimestampValid)
.withMessage('Should have valid ending time value'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
@@ -82,9 +86,10 @@ const videoAbuseListValidator = [
query('id')
.optional()
.custom(isIdValid).withMessage('Should have a valid id'),
- query('predefinedReasonId')
+ query('predefinedReason')
.optional()
- .custom(isIdValid).withMessage('Should have a valid predefinedReasonId'),
+ .custom(isVideoAbusePredefinedReasonValid)
+ .withMessage('Should have a valid predefinedReason'),
query('search')
.optional()
.custom(exists).withMessage('Should have a valid search'),
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index 7579b575a3b..9c6e0317592 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -15,7 +15,13 @@ import {
UpdatedAt
} from 'sequelize-typescript'
import { VideoAbuseVideoIs } from '@shared/models/videos/abuse/video-abuse-video-is.type'
-import { VideoAbuseState, VideoDetails } from '../../../shared'
+import {
+ VideoAbuseState,
+ VideoDetails,
+ VideoAbusePredefinedReasons,
+ VideoAbusePredefinedReasonsString,
+ VideoAbusePredefinedReasonsMap
+} from '../../../shared'
import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
import { VideoAbuse } from '../../../shared/models/videos'
import {
@@ -31,6 +37,7 @@ import { ThumbnailModel } from './thumbnail'
import { VideoModel } from './video'
import { VideoBlacklistModel } from './video-blacklist'
import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from './video-channel'
+import { invert } from 'lodash'
export enum ScopeNames {
FOR_API = 'FOR_API'
@@ -270,7 +277,7 @@ export class VideoAbuseModel extends Model {
@AllowNull(true)
@Default(null)
@Column(DataType.ARRAY(DataType.INTEGER))
- predefinedReasons: number[]
+ predefinedReasons: VideoAbusePredefinedReasons[]
@AllowNull(true)
@Default(null)
@@ -335,7 +342,7 @@ export class VideoAbuseModel extends Model {
user?: MUserAccountId
id?: number
- predefinedReasonId?: number
+ predefinedReason?: VideoAbusePredefinedReasonsString
state?: VideoAbuseState
videoIs?: VideoAbuseVideoIs
@@ -354,7 +361,7 @@ export class VideoAbuseModel extends Model {
serverAccountId,
state,
videoIs,
- predefinedReasonId,
+ predefinedReason,
searchReportee,
searchVideo,
searchVideoChannel,
@@ -363,6 +370,7 @@ export class VideoAbuseModel extends Model {
} = parameters
const userAccountId = user ? user.Account.id : undefined
+ const predefinedReasonId = predefinedReason ? VideoAbusePredefinedReasonsMap[predefinedReason] : undefined
const query = {
offset: start,
@@ -397,6 +405,7 @@ export class VideoAbuseModel extends Model {
}
toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
+ const predefinedReasons = VideoAbuseModel.getPredefinedReasonsStrings(this.predefinedReasons || [])
const countReportsForVideo = this.get('countReportsForVideo') as number
const nthReportForVideo = this.get('nthReportForVideo') as number
const countReportsForReporterVideo = this.get('countReportsForReporter__video') as number
@@ -411,7 +420,7 @@ export class VideoAbuseModel extends Model {
return {
id: this.id,
reason: this.reason,
- predefinedReasons: this.predefinedReasons,
+ predefinedReasons,
reporterAccount: this.Account.toFormattedJSON(),
state: {
id: this.state,
@@ -440,6 +449,8 @@ export class VideoAbuseModel extends Model {
}
toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
+ const predefinedReasons = VideoAbuseModel.getPredefinedReasonsStrings(this.predefinedReasons || [])
+
const startAt = this.startAt
const endAt = this.endAt
@@ -447,9 +458,9 @@ export class VideoAbuseModel extends Model {
type: 'Flag' as 'Flag',
content: this.reason,
object: this.Video.url,
- tag: this.predefinedReasons.map(r => ({
+ tag: predefinedReasons.map(r => ({
type: 'Hashtag' as 'Hashtag',
- name: r.toString()
+ name: r
})),
startAt,
endAt
@@ -459,4 +470,10 @@ export class VideoAbuseModel extends Model {
private static getStateLabel (id: number) {
return VIDEO_ABUSE_STATES[id] || 'Unknown'
}
+
+ private static getPredefinedReasonsStrings (predefinedReasons: VideoAbusePredefinedReasons[]): VideoAbusePredefinedReasonsString[] {
+ return predefinedReasons
+ .filter(r => r in VideoAbusePredefinedReasons)
+ .map(r => invert(VideoAbusePredefinedReasonsMap)[r] as VideoAbusePredefinedReasonsString)
+ }
}
diff --git a/server/tests/api/videos/video-abuse.ts b/server/tests/api/videos/video-abuse.ts
index a96be97f636..d8706502548 100644
--- a/server/tests/api/videos/video-abuse.ts
+++ b/server/tests/api/videos/video-abuse.ts
@@ -2,7 +2,7 @@
import * as chai from 'chai'
import 'mocha'
-import { VideoAbuse, VideoAbuseState } from '../../../../shared/models/videos'
+import { VideoAbuse, VideoAbuseState, VideoAbusePredefinedReasonsString } from '../../../../shared/models/videos'
import {
cleanupTests,
deleteVideoAbuse,
@@ -291,6 +291,33 @@ describe('Test video abuses', function () {
}
})
+ it('Should list predefined reasons as well as timestamps for the reported video', async function () {
+ this.timeout(10000)
+
+ const reason5 = 'my super bad reason 5'
+ const predefinedReasons5: VideoAbusePredefinedReasonsString[] = [ 'violentOrRepulsive', 'captions' ]
+ const createdAbuse = (await reportVideoAbuse(
+ servers[0].url,
+ servers[0].accessToken,
+ servers[0].video.id,
+ reason5,
+ predefinedReasons5,
+ 1,
+ 5
+ )).body as VideoAbuse
+
+ const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
+
+ for (const abuse of res.body.data as VideoAbuse[]) {
+ if (abuse.id === createdAbuse.id) {
+ expect(abuse.reason).to.equals(reason5)
+ expect(abuse.predefinedReasons).to.equals(predefinedReasons5, "predefined reasons do not match the one reported")
+ expect(abuse.startAt).to.equal(1, "starting timestamp doesn't match the one reported")
+ expect(abuse.endAt).to.equal(5, "ending timestamp doesn't match the one reported")
+ }
+ }
+ })
+
it('Should delete the video abuse', async function () {
this.timeout(10000)
@@ -307,7 +334,7 @@ describe('Test video abuses', function () {
{
const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken })
- expect(res.body.total).to.equal(5)
+ expect(res.body.total).to.equal(6)
}
})
@@ -328,25 +355,28 @@ describe('Test video abuses', function () {
expect(await list({ id: 56 })).to.have.lengthOf(0)
expect(await list({ id: 1 })).to.have.lengthOf(1)
- expect(await list({ search: 'my super name for server 1' })).to.have.lengthOf(3)
+ expect(await list({ search: 'my super name for server 1' })).to.have.lengthOf(4)
expect(await list({ search: 'aaaaaaaaaaaaaaaaaaaaaaaaaa' })).to.have.lengthOf(0)
expect(await list({ searchVideo: 'my second super name for server 1' })).to.have.lengthOf(1)
- expect(await list({ searchVideoChannel: 'root' })).to.have.lengthOf(3)
+ expect(await list({ searchVideoChannel: 'root' })).to.have.lengthOf(4)
expect(await list({ searchVideoChannel: 'aaaa' })).to.have.lengthOf(0)
expect(await list({ searchReporter: 'user2' })).to.have.lengthOf(1)
- expect(await list({ searchReporter: 'root' })).to.have.lengthOf(4)
+ expect(await list({ searchReporter: 'root' })).to.have.lengthOf(5)
- expect(await list({ searchReportee: 'root' })).to.have.lengthOf(3)
+ expect(await list({ searchReportee: 'root' })).to.have.lengthOf(4)
expect(await list({ searchReportee: 'aaaa' })).to.have.lengthOf(0)
expect(await list({ videoIs: 'deleted' })).to.have.lengthOf(1)
expect(await list({ videoIs: 'blacklisted' })).to.have.lengthOf(0)
expect(await list({ state: VideoAbuseState.ACCEPTED })).to.have.lengthOf(0)
- expect(await list({ state: VideoAbuseState.PENDING })).to.have.lengthOf(5)
+ expect(await list({ state: VideoAbuseState.PENDING })).to.have.lengthOf(6)
+
+ expect(await list({ predefinedReason: 'violentOrRepulsive' })).to.have.lengthOf(1)
+ expect(await list({ predefinedReason: 'serverRules' })).to.have.lengthOf(0)
})
after(async function () {
diff --git a/shared/extra-utils/videos/video-abuses.ts b/shared/extra-utils/videos/video-abuses.ts
index 81582bfc7fd..ff006672ad9 100644
--- a/shared/extra-utils/videos/video-abuses.ts
+++ b/shared/extra-utils/videos/video-abuses.ts
@@ -1,17 +1,26 @@
import * as request from 'supertest'
import { VideoAbuseUpdate } from '../../models/videos/abuse/video-abuse-update.model'
import { makeDeleteRequest, makePutBodyRequest, makeGetRequest } from '../requests/requests'
-import { VideoAbuseState } from '@shared/models'
+import { VideoAbuseState, VideoAbusePredefinedReasonsString } from '@shared/models'
import { VideoAbuseVideoIs } from '@shared/models/videos/abuse/video-abuse-video-is.type'
-function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 200) {
+function reportVideoAbuse (
+ url: string,
+ token: string,
+ videoId: number | string,
+ reason: string,
+ predefinedReasons?: VideoAbusePredefinedReasonsString[],
+ startAt?: number,
+ endAt?: number,
+ specialStatus = 200
+) {
const path = '/api/v1/videos/' + videoId + '/abuse'
return request(url)
.post(path)
.set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + token)
- .send({ reason })
+ .send({ reason, predefinedReasons, startAt, endAt })
.expect(specialStatus)
}
@@ -19,6 +28,7 @@ function getVideoAbusesList (options: {
url: string
token: string
id?: number
+ predefinedReason?: VideoAbusePredefinedReasonsString
search?: string
state?: VideoAbuseState
videoIs?: VideoAbuseVideoIs
@@ -31,6 +41,7 @@ function getVideoAbusesList (options: {
url,
token,
id,
+ predefinedReason,
search,
state,
videoIs,
@@ -44,6 +55,7 @@ function getVideoAbusesList (options: {
const query = {
sort: 'createdAt',
id,
+ predefinedReason,
search,
state,
videoIs,
diff --git a/shared/models/activitypub/objects/common-objects.ts b/shared/models/activitypub/objects/common-objects.ts
index 704979e5940..096d422eab1 100644
--- a/shared/models/activitypub/objects/common-objects.ts
+++ b/shared/models/activitypub/objects/common-objects.ts
@@ -1,3 +1,5 @@
+import { VideoAbusePredefinedReasonsString } from '@shared/models/videos'
+
export interface ActivityIdentifierObject {
identifier: string
name: string
@@ -83,7 +85,7 @@ export interface ActivityMentionObject {
export interface ActivityFlagReasonObject {
type: 'Hashtag'
- name: string
+ name: VideoAbusePredefinedReasonsString
}
export type ActivityTagObject =
diff --git a/shared/models/videos/abuse/video-abuse-create.model.ts b/shared/models/videos/abuse/video-abuse-create.model.ts
index 54f3637b585..c93cb8b2c42 100644
--- a/shared/models/videos/abuse/video-abuse-create.model.ts
+++ b/shared/models/videos/abuse/video-abuse-create.model.ts
@@ -1,8 +1,8 @@
-import { VideoAbusePredefinedReasonsIn } from './video-abuse-reason.model'
+import { VideoAbusePredefinedReasonsString } from './video-abuse-reason.model'
export interface VideoAbuseCreate {
reason: string
- predefinedReasons?: VideoAbusePredefinedReasonsIn[]
+ predefinedReasons?: VideoAbusePredefinedReasonsString[]
startAt?: number
endAt?: number
}
diff --git a/shared/models/videos/abuse/video-abuse-reason.model.ts b/shared/models/videos/abuse/video-abuse-reason.model.ts
index 8dd473227b1..e514471b9dc 100644
--- a/shared/models/videos/abuse/video-abuse-reason.model.ts
+++ b/shared/models/videos/abuse/video-abuse-reason.model.ts
@@ -1,5 +1,5 @@
export enum VideoAbusePredefinedReasons {
- VIOLENT_OR_REPULSIVE,
+ VIOLENT_OR_REPULSIVE = 1,
HATEFUL_OR_ABUSIVE,
SPAM_OR_MISLEADING,
PRIVACY,
@@ -9,7 +9,7 @@ export enum VideoAbusePredefinedReasons {
CAPTIONS
}
-export type VideoAbusePredefinedReasonsIn =
+export type VideoAbusePredefinedReasonsString =
'violentOrRepulsive' |
'hatefulOrAbusive' |
'spamOrMisleading' |
@@ -19,7 +19,9 @@ export type VideoAbusePredefinedReasonsIn =
'thumbnails' |
'captions'
-export const VideoAbusePredefinedReasonsIn = {
+export const VideoAbusePredefinedReasonsMap: {
+ [key in VideoAbusePredefinedReasonsString]: VideoAbusePredefinedReasons
+} = {
violentOrRepulsive: VideoAbusePredefinedReasons.VIOLENT_OR_REPULSIVE,
hatefulOrAbusive: VideoAbusePredefinedReasons.HATEFUL_OR_ABUSIVE,
spamOrMisleading: VideoAbusePredefinedReasons.SPAM_OR_MISLEADING,
diff --git a/shared/models/videos/abuse/video-abuse.model.ts b/shared/models/videos/abuse/video-abuse.model.ts
index e948859771e..38605dcac05 100644
--- a/shared/models/videos/abuse/video-abuse.model.ts
+++ b/shared/models/videos/abuse/video-abuse.model.ts
@@ -2,11 +2,12 @@ import { Account } from '../../actors/index'
import { VideoConstant } from '../video-constant.model'
import { VideoAbuseState } from './video-abuse-state.model'
import { VideoChannel } from '../channel/video-channel.model'
+import { VideoAbusePredefinedReasonsString } from './video-abuse-reason.model'
export interface VideoAbuse {
id: number
reason: string
- predefinedReasons?: number[]
+ predefinedReasons?: VideoAbusePredefinedReasonsString[]
reporterAccount: Account
state: VideoConstant