Skip to content

Commit

Permalink
move API and AP object from number to string enum
Browse files Browse the repository at this point in the history
  • Loading branch information
rigelk committed Jun 21, 2020
1 parent 5ff6adf commit 17afa20
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
<div *ngIf="getPredefinedReasons()" class="mt-2 d-flex">
<span class="col-3"></span>
<span class="col-9">
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip bg-secondary text-light" *ngFor="let reason of getPredefinedReasons()">
<a [routerLink]="[ '/admin/moderation/video-abuses/list' ]" [queryParams]="{ 'search': 'tag:' + reason.id }" class="chip rectangular bg-secondary text-light" *ngFor="let reason of getPredefinedReasons()">
<div>{{ reason.label }}</div>
</a>
</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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')
}
}

Expand Down
13 changes: 1 addition & 12 deletions client/src/app/shared/video-abuse/video-abuse.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 5 additions & 5 deletions client/src/app/shared/video/modals/video-report.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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({
Expand Down
11 changes: 9 additions & 2 deletions client/src/sass/include/_mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down
8 changes: 3 additions & 5 deletions server/controllers/api/videos/abuse.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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()
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
19 changes: 14 additions & 5 deletions server/helpers/custom-validators/video-abuses.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
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

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) {
Expand All @@ -35,7 +42,9 @@ function isAbuseVideoIsValid (value: VideoAbuseVideoIs) {

export {
isVideoAbuseReasonValid,
isVideoAbusePredefinedReasonValid,
isVideoAbusePredefinedReasonsValid,
isVideoAbuseTimestampValid,
isVideoAbuseModerationCommentValid,
isVideoAbuseStateValid,
isAbuseVideoIsValid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ async function up (utils: {
queryInterface: Sequelize.QueryInterface
sequelize: Sequelize.Sequelize
}): Promise<void> {
await utils.queryInterface.addColumn('videoAbuse', 'predefinedReasons', {
type: Sequelize.ARRAY(Sequelize.INTEGER),
allowNull: true
})

await utils.queryInterface.addColumn('videoAbuse', 'startAt', {
type: Sequelize.INTEGER,
allowNull: true
Expand Down
9 changes: 7 additions & 2 deletions server/lib/activitypub/process/process-flag.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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
Expand Down
15 changes: 10 additions & 5 deletions server/middlewares/validators/videos/video-abuses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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) => {
Expand Down Expand Up @@ -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'),
Expand Down
31 changes: 24 additions & 7 deletions server/models/video/video-abuse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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'
Expand Down Expand Up @@ -270,7 +277,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
@AllowNull(true)
@Default(null)
@Column(DataType.ARRAY(DataType.INTEGER))
predefinedReasons: number[]
predefinedReasons: VideoAbusePredefinedReasons[]

@AllowNull(true)
@Default(null)
Expand Down Expand Up @@ -335,7 +342,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
user?: MUserAccountId

id?: number
predefinedReasonId?: number
predefinedReason?: VideoAbusePredefinedReasonsString
state?: VideoAbuseState
videoIs?: VideoAbuseVideoIs

Expand All @@ -354,7 +361,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
serverAccountId,
state,
videoIs,
predefinedReasonId,
predefinedReason,
searchReportee,
searchVideo,
searchVideoChannel,
Expand All @@ -363,6 +370,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
} = parameters

const userAccountId = user ? user.Account.id : undefined
const predefinedReasonId = predefinedReason ? VideoAbusePredefinedReasonsMap[predefinedReason] : undefined

const query = {
offset: start,
Expand Down Expand Up @@ -397,6 +405,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
}

toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
const predefinedReasons = VideoAbuseModel.getPredefinedReasonsStrings(this.predefinedReasons || [])
const countReportsForVideo = this.get('countReportsForVideo') as number
const nthReportForVideo = this.get('nthReportForVideo') as number
const countReportsForReporterVideo = this.get('countReportsForReporter__video') as number
Expand All @@ -411,7 +420,7 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
return {
id: this.id,
reason: this.reason,
predefinedReasons: this.predefinedReasons,
predefinedReasons,
reporterAccount: this.Account.toFormattedJSON(),
state: {
id: this.state,
Expand Down Expand Up @@ -440,16 +449,18 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
}

toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
const predefinedReasons = VideoAbuseModel.getPredefinedReasonsStrings(this.predefinedReasons || [])

const startAt = this.startAt
const endAt = this.endAt

return {
type: 'Flag' as 'Flag',
content: this.reason,
object: this.Video.url,
tag: this.predefinedReasons.map(r => ({
tag: predefinedReasons.map(r => ({
type: 'Hashtag' as 'Hashtag',
name: r.toString()
name: r
})),
startAt,
endAt
Expand All @@ -459,4 +470,10 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
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)
}
}
Loading

0 comments on commit 17afa20

Please sign in to comment.