From d95d15598847c7f020aa056e7e6e0c02d2bbf732 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 1 Jul 2020 16:05:30 +0200 Subject: Use 3 tables to represent abuses --- .../form-validators/abuse-validators.service.ts | 30 +++++++ .../shared/shared-forms/form-validators/index.ts | 2 +- .../video-abuse-validators.service.ts | 30 ------- .../app/shared/shared-forms/shared-form.module.ts | 4 +- .../app/shared/shared-main/account/actor.model.ts | 2 +- .../shared-main/users/user-notification.model.ts | 26 ++++-- .../users/user-notifications.component.html | 8 +- .../app/shared/shared-moderation/abuse.service.ts | 98 ++++++++++++++++++++++ client/src/app/shared/shared-moderation/index.ts | 2 +- .../shared-moderation/shared-moderation.module.ts | 4 +- .../shared-moderation/video-abuse.service.ts | 98 ---------------------- .../shared-moderation/video-report.component.ts | 29 ++++--- 12 files changed, 174 insertions(+), 159 deletions(-) create mode 100644 client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts delete mode 100644 client/src/app/shared/shared-forms/form-validators/video-abuse-validators.service.ts create mode 100644 client/src/app/shared/shared-moderation/abuse.service.ts delete mode 100644 client/src/app/shared/shared-moderation/video-abuse.service.ts (limited to 'client/src/app/shared') diff --git a/client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts new file mode 100644 index 000000000..739115e19 --- /dev/null +++ b/client/src/app/shared/shared-forms/form-validators/abuse-validators.service.ts @@ -0,0 +1,30 @@ +import { I18n } from '@ngx-translate/i18n-polyfill' +import { Validators } from '@angular/forms' +import { Injectable } from '@angular/core' +import { BuildFormValidator } from './form-validator.service' + +@Injectable() +export class AbuseValidatorsService { + readonly ABUSE_REASON: BuildFormValidator + readonly ABUSE_MODERATION_COMMENT: BuildFormValidator + + constructor (private i18n: I18n) { + this.ABUSE_REASON = { + VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], + MESSAGES: { + 'required': this.i18n('Report reason is required.'), + 'minlength': this.i18n('Report reason must be at least 2 characters long.'), + 'maxlength': this.i18n('Report reason cannot be more than 3000 characters long.') + } + } + + this.ABUSE_MODERATION_COMMENT = { + VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], + MESSAGES: { + 'required': this.i18n('Moderation comment is required.'), + 'minlength': this.i18n('Moderation comment must be at least 2 characters long.'), + 'maxlength': this.i18n('Moderation comment cannot be more than 3000 characters long.') + } + } + } +} diff --git a/client/src/app/shared/shared-forms/form-validators/index.ts b/client/src/app/shared/shared-forms/form-validators/index.ts index 8b71841a9..b06a326ff 100644 --- a/client/src/app/shared/shared-forms/form-validators/index.ts +++ b/client/src/app/shared/shared-forms/form-validators/index.ts @@ -1,3 +1,4 @@ +export * from './abuse-validators.service' export * from './batch-domains-validators.service' export * from './custom-config-validators.service' export * from './form-validator.service' @@ -6,7 +7,6 @@ export * from './instance-validators.service' export * from './login-validators.service' export * from './reset-password-validators.service' export * from './user-validators.service' -export * from './video-abuse-validators.service' export * from './video-accept-ownership-validators.service' export * from './video-block-validators.service' export * from './video-captions-validators.service' diff --git a/client/src/app/shared/shared-forms/form-validators/video-abuse-validators.service.ts b/client/src/app/shared/shared-forms/form-validators/video-abuse-validators.service.ts deleted file mode 100644 index aae56d607..000000000 --- a/client/src/app/shared/shared-forms/form-validators/video-abuse-validators.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { I18n } from '@ngx-translate/i18n-polyfill' -import { Validators } from '@angular/forms' -import { Injectable } from '@angular/core' -import { BuildFormValidator } from './form-validator.service' - -@Injectable() -export class VideoAbuseValidatorsService { - readonly VIDEO_ABUSE_REASON: BuildFormValidator - readonly VIDEO_ABUSE_MODERATION_COMMENT: BuildFormValidator - - constructor (private i18n: I18n) { - this.VIDEO_ABUSE_REASON = { - VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], - MESSAGES: { - 'required': this.i18n('Report reason is required.'), - 'minlength': this.i18n('Report reason must be at least 2 characters long.'), - 'maxlength': this.i18n('Report reason cannot be more than 3000 characters long.') - } - } - - this.VIDEO_ABUSE_MODERATION_COMMENT = { - VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ], - MESSAGES: { - 'required': this.i18n('Moderation comment is required.'), - 'minlength': this.i18n('Moderation comment must be at least 2 characters long.'), - 'maxlength': this.i18n('Moderation comment cannot be more than 3000 characters long.') - } - } - } -} diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts index e82fa97d4..ba33704cf 100644 --- a/client/src/app/shared/shared-forms/shared-form.module.ts +++ b/client/src/app/shared/shared-forms/shared-form.module.ts @@ -11,7 +11,7 @@ import { LoginValidatorsService, ResetPasswordValidatorsService, UserValidatorsService, - VideoAbuseValidatorsService, + AbuseValidatorsService, VideoAcceptOwnershipValidatorsService, VideoBlockValidatorsService, VideoCaptionsValidatorsService, @@ -69,7 +69,7 @@ import { TimestampInputComponent } from './timestamp-input.component' LoginValidatorsService, ResetPasswordValidatorsService, UserValidatorsService, - VideoAbuseValidatorsService, + AbuseValidatorsService, VideoAcceptOwnershipValidatorsService, VideoBlockValidatorsService, VideoCaptionsValidatorsService, diff --git a/client/src/app/shared/shared-main/account/actor.model.ts b/client/src/app/shared/shared-main/account/actor.model.ts index 5fc7989dd..0fa161ce6 100644 --- a/client/src/app/shared/shared-main/account/actor.model.ts +++ b/client/src/app/shared/shared-main/account/actor.model.ts @@ -14,7 +14,7 @@ export abstract class Actor implements ActorServer { avatarUrl: string - static GET_ACTOR_AVATAR_URL (actor: { avatar?: Avatar }) { + static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) { if (actor?.avatar?.url) return actor.avatar.url if (actor && actor.avatar) { diff --git a/client/src/app/shared/shared-main/users/user-notification.model.ts b/client/src/app/shared/shared-main/users/user-notification.model.ts index de25d3ab9..389a242fd 100644 --- a/client/src/app/shared/shared-main/users/user-notification.model.ts +++ b/client/src/app/shared/shared-main/users/user-notification.model.ts @@ -25,9 +25,20 @@ export class UserNotification implements UserNotificationServer { video: VideoInfo } - videoAbuse?: { + abuse?: { id: number - video: VideoInfo + + video?: VideoInfo + + comment?: { + threadId: number + + video: { + uuid: string + } + } + + account?: ActorInfo } videoBlacklist?: { @@ -55,7 +66,7 @@ export class UserNotification implements UserNotificationServer { // Additional fields videoUrl?: string commentUrl?: any[] - videoAbuseUrl?: string + abuseUrl?: string videoAutoBlacklistUrl?: string accountUrl?: string videoImportIdentifier?: string @@ -78,7 +89,7 @@ export class UserNotification implements UserNotificationServer { this.comment = hash.comment if (this.comment) this.setAvatarUrl(this.comment.account) - this.videoAbuse = hash.videoAbuse + this.abuse = hash.abuse this.videoBlacklist = hash.videoBlacklist @@ -108,8 +119,9 @@ export class UserNotification implements UserNotificationServer { break case UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS: - this.videoAbuseUrl = '/admin/moderation/video-abuses/list' - this.videoUrl = this.buildVideoUrl(this.videoAbuse.video) + this.abuseUrl = '/admin/moderation/abuses/list' + + if (this.abuse.video) this.videoUrl = this.buildVideoUrl(this.abuse.video) break case UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS: @@ -178,7 +190,7 @@ export class UserNotification implements UserNotificationServer { return videoImport.targetUrl || videoImport.magnetUri || videoImport.torrentName } - private setAvatarUrl (actor: { avatarUrl?: string, avatar?: Avatar }) { + private setAvatarUrl (actor: { avatarUrl?: string, avatar?: { url?: string, path: string } }) { actor.avatarUrl = Actor.GET_ACTOR_AVATAR_URL(actor) } } diff --git a/client/src/app/shared/shared-main/users/user-notifications.component.html b/client/src/app/shared/shared-main/users/user-notifications.component.html index d5be1470e..8d31eab0d 100644 --- a/client/src/app/shared/shared-main/users/user-notifications.component.html +++ b/client/src/app/shared/shared-main/users/user-notifications.component.html @@ -19,7 +19,7 @@ - +
The notification concerns a video now unavailable
@@ -46,7 +46,7 @@ @@ -65,7 +65,7 @@ - + @@ -73,7 +73,7 @@ - +
The notification concerns a comment now unavailable
diff --git a/client/src/app/shared/shared-moderation/abuse.service.ts b/client/src/app/shared/shared-moderation/abuse.service.ts new file mode 100644 index 000000000..f45018d5c --- /dev/null +++ b/client/src/app/shared/shared-moderation/abuse.service.ts @@ -0,0 +1,98 @@ +import { omit } from 'lodash-es' +import { SortMeta } from 'primeng/api' +import { Observable } from 'rxjs' +import { catchError, map } from 'rxjs/operators' +import { HttpClient, HttpParams } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { RestExtractor, RestPagination, RestService } from '@app/core' +import { AbuseUpdate, ResultList, Abuse, AbuseCreate, AbuseState } from '@shared/models' +import { environment } from '../../../environments/environment' + +@Injectable() +export class AbuseService { + private static BASE_ABUSE_URL = environment.apiUrl + '/api/v1/abuses' + + constructor ( + private authHttp: HttpClient, + private restService: RestService, + private restExtractor: RestExtractor + ) {} + + getAbuses (options: { + pagination: RestPagination, + sort: SortMeta, + search?: string + }): Observable> { + const { pagination, sort, search } = options + const url = AbuseService.BASE_ABUSE_URL + 'abuse' + + let params = new HttpParams() + params = this.restService.addRestGetParams(params, pagination, sort) + + if (search) { + const filters = this.restService.parseQueryStringFilter(search, { + id: { prefix: '#' }, + state: { + prefix: 'state:', + handler: v => { + if (v === 'accepted') return AbuseState.ACCEPTED + if (v === 'pending') return AbuseState.PENDING + if (v === 'rejected') return AbuseState.REJECTED + + return undefined + } + }, + videoIs: { + prefix: 'videoIs:', + handler: v => { + if (v === 'deleted') return v + if (v === 'blacklisted') return v + + return undefined + } + }, + searchReporter: { prefix: 'reporter:' }, + searchReportee: { prefix: 'reportee:' }, + predefinedReason: { prefix: 'tag:' } + }) + + params = this.restService.addObjectParams(params, filters) + } + + return this.authHttp.get>(url, { params }) + .pipe( + catchError(res => this.restExtractor.handleError(res)) + ) + } + + reportVideo (parameters: AbuseCreate) { + const url = AbuseService.BASE_ABUSE_URL + + const body = omit(parameters, [ 'id' ]) + + return this.authHttp.post(url, body) + .pipe( + map(this.restExtractor.extractDataBool), + catchError(res => this.restExtractor.handleError(res)) + ) + } + + updateAbuse (abuse: Abuse, abuseUpdate: AbuseUpdate) { + const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id + + return this.authHttp.put(url, abuseUpdate) + .pipe( + map(this.restExtractor.extractDataBool), + catchError(res => this.restExtractor.handleError(res)) + ) + } + + removeAbuse (abuse: Abuse) { + const url = AbuseService.BASE_ABUSE_URL + '/' + abuse.id + + return this.authHttp.delete(url) + .pipe( + map(this.restExtractor.extractDataBool), + catchError(res => this.restExtractor.handleError(res)) + ) + }} diff --git a/client/src/app/shared/shared-moderation/index.ts b/client/src/app/shared/shared-moderation/index.ts index 8e74254f6..d6c4a10be 100644 --- a/client/src/app/shared/shared-moderation/index.ts +++ b/client/src/app/shared/shared-moderation/index.ts @@ -1,3 +1,4 @@ +export * from './abuse.service' export * from './account-block.model' export * from './account-blocklist.component' export * from './batch-domains-modal.component' @@ -6,7 +7,6 @@ export * from './bulk.service' export * from './server-blocklist.component' export * from './user-ban-modal.component' export * from './user-moderation-dropdown.component' -export * from './video-abuse.service' export * from './video-block.component' export * from './video-block.service' export * from './video-report.component' diff --git a/client/src/app/shared/shared-moderation/shared-moderation.module.ts b/client/src/app/shared/shared-moderation/shared-moderation.module.ts index f7e64dfa3..742193e58 100644 --- a/client/src/app/shared/shared-moderation/shared-moderation.module.ts +++ b/client/src/app/shared/shared-moderation/shared-moderation.module.ts @@ -8,7 +8,7 @@ import { BlocklistService } from './blocklist.service' import { BulkService } from './bulk.service' import { UserBanModalComponent } from './user-ban-modal.component' import { UserModerationDropdownComponent } from './user-moderation-dropdown.component' -import { VideoAbuseService } from './video-abuse.service' +import { AbuseService } from './abuse.service' import { VideoBlockComponent } from './video-block.component' import { VideoBlockService } from './video-block.service' import { VideoReportComponent } from './video-report.component' @@ -39,7 +39,7 @@ import { VideoReportComponent } from './video-report.component' providers: [ BlocklistService, BulkService, - VideoAbuseService, + AbuseService, VideoBlockService ] }) diff --git a/client/src/app/shared/shared-moderation/video-abuse.service.ts b/client/src/app/shared/shared-moderation/video-abuse.service.ts deleted file mode 100644 index 44dea44a5..000000000 --- a/client/src/app/shared/shared-moderation/video-abuse.service.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { omit } from 'lodash-es' -import { SortMeta } from 'primeng/api' -import { Observable } from 'rxjs' -import { catchError, map } from 'rxjs/operators' -import { HttpClient, HttpParams } from '@angular/common/http' -import { Injectable } from '@angular/core' -import { RestExtractor, RestPagination, RestService } from '@app/core' -import { ResultList, VideoAbuse, VideoAbuseCreate, VideoAbuseState, VideoAbuseUpdate } from '@shared/models' -import { environment } from '../../../environments/environment' - -@Injectable() -export class VideoAbuseService { - private static BASE_VIDEO_ABUSE_URL = environment.apiUrl + '/api/v1/videos/' - - constructor ( - private authHttp: HttpClient, - private restService: RestService, - private restExtractor: RestExtractor - ) {} - - getVideoAbuses (options: { - pagination: RestPagination, - sort: SortMeta, - search?: string - }): Observable> { - const { pagination, sort, search } = options - const url = VideoAbuseService.BASE_VIDEO_ABUSE_URL + 'abuse' - - let params = new HttpParams() - params = this.restService.addRestGetParams(params, pagination, sort) - - if (search) { - const filters = this.restService.parseQueryStringFilter(search, { - id: { prefix: '#' }, - state: { - prefix: 'state:', - handler: v => { - if (v === 'accepted') return VideoAbuseState.ACCEPTED - if (v === 'pending') return VideoAbuseState.PENDING - if (v === 'rejected') return VideoAbuseState.REJECTED - - return undefined - } - }, - videoIs: { - prefix: 'videoIs:', - handler: v => { - if (v === 'deleted') return v - if (v === 'blacklisted') return v - - return undefined - } - }, - searchReporter: { prefix: 'reporter:' }, - searchReportee: { prefix: 'reportee:' }, - predefinedReason: { prefix: 'tag:' } - }) - - params = this.restService.addObjectParams(params, filters) - } - - return this.authHttp.get>(url, { params }) - .pipe( - catchError(res => this.restExtractor.handleError(res)) - ) - } - - reportVideo (parameters: { id: number } & VideoAbuseCreate) { - const url = VideoAbuseService.BASE_VIDEO_ABUSE_URL + parameters.id + '/abuse' - - const body = omit(parameters, [ 'id' ]) - - return this.authHttp.post(url, body) - .pipe( - map(this.restExtractor.extractDataBool), - catchError(res => this.restExtractor.handleError(res)) - ) - } - - updateVideoAbuse (videoAbuse: VideoAbuse, abuseUpdate: VideoAbuseUpdate) { - const url = VideoAbuseService.BASE_VIDEO_ABUSE_URL + videoAbuse.video.uuid + '/abuse/' + videoAbuse.id - - return this.authHttp.put(url, abuseUpdate) - .pipe( - map(this.restExtractor.extractDataBool), - catchError(res => this.restExtractor.handleError(res)) - ) - } - - removeVideoAbuse (videoAbuse: VideoAbuse) { - const url = VideoAbuseService.BASE_VIDEO_ABUSE_URL + videoAbuse.video.uuid + '/abuse/' + videoAbuse.id - - return this.authHttp.delete(url) - .pipe( - map(this.restExtractor.extractDataBool), - catchError(res => this.restExtractor.handleError(res)) - ) - }} diff --git a/client/src/app/shared/shared-moderation/video-report.component.ts b/client/src/app/shared/shared-moderation/video-report.component.ts index 11c805636..b8d9f8d27 100644 --- a/client/src/app/shared/shared-moderation/video-report.component.ts +++ b/client/src/app/shared/shared-moderation/video-report.component.ts @@ -3,13 +3,13 @@ import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils' import { Component, Input, OnInit, ViewChild } from '@angular/core' import { DomSanitizer, SafeHtml } from '@angular/platform-browser' import { Notifier } from '@app/core' -import { FormReactive, FormValidatorService, VideoAbuseValidatorsService } from '@app/shared/shared-forms' +import { AbuseValidatorsService, FormReactive, FormValidatorService } from '@app/shared/shared-forms' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref' import { I18n } from '@ngx-translate/i18n-polyfill' -import { videoAbusePredefinedReasonsMap, VideoAbusePredefinedReasonsString } from '@shared/models/videos/abuse/video-abuse-reason.model' +import { abusePredefinedReasonsMap, AbusePredefinedReasonsString } from '@shared/models' import { Video } from '../shared-main' -import { VideoAbuseService } from './video-abuse.service' +import { AbuseService } from './abuse.service' @Component({ selector: 'my-video-report', @@ -22,7 +22,7 @@ export class VideoReportComponent extends FormReactive implements OnInit { @ViewChild('modal', { static: true }) modal: NgbModal error: string = null - predefinedReasons: { id: VideoAbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = [] + predefinedReasons: { id: AbusePredefinedReasonsString, label: string, description?: string, help?: string }[] = [] embedHtml: SafeHtml private openedModal: NgbModalRef @@ -30,8 +30,8 @@ export class VideoReportComponent extends FormReactive implements OnInit { constructor ( protected formValidatorService: FormValidatorService, private modalService: NgbModal, - private videoAbuseValidatorsService: VideoAbuseValidatorsService, - private videoAbuseService: VideoAbuseService, + private abuseValidatorsService: AbuseValidatorsService, + private abuseService: AbuseService, private notifier: Notifier, private sanitizer: DomSanitizer, private i18n: I18n @@ -69,8 +69,8 @@ export class VideoReportComponent extends FormReactive implements OnInit { ngOnInit () { this.buildForm({ - reason: this.videoAbuseValidatorsService.VIDEO_ABUSE_REASON, - predefinedReasons: mapValues(videoAbusePredefinedReasonsMap, r => null), + reason: this.abuseValidatorsService.ABUSE_REASON, + predefinedReasons: mapValues(abusePredefinedReasonsMap, r => null), timestamp: { hasStart: null, startAt: null, @@ -136,15 +136,18 @@ export class VideoReportComponent extends FormReactive implements OnInit { report () { const reason = this.form.get('reason').value - const predefinedReasons = Object.keys(pickBy(this.form.get('predefinedReasons').value)) as VideoAbusePredefinedReasonsString[] + const predefinedReasons = Object.keys(pickBy(this.form.get('predefinedReasons').value)) as AbusePredefinedReasonsString[] const { hasStart, startAt, hasEnd, endAt } = this.form.get('timestamp').value - this.videoAbuseService.reportVideo({ - id: this.video.id, + this.abuseService.reportVideo({ + accountId: this.video.account.id, reason, predefinedReasons, - startAt: hasStart && startAt ? startAt : undefined, - endAt: hasEnd && endAt ? endAt : undefined + video: { + id: this.video.id, + startAt: hasStart && startAt ? startAt : undefined, + endAt: hasEnd && endAt ? endAt : undefined + } }).subscribe( () => { this.notifier.success(this.i18n('Video reported.')) -- cgit v1.2.3