X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;ds=sidebyside;f=client%2Fsrc%2Fapp%2F%2Badmin%2Fmoderation%2Fvideo-abuse-list%2Fvideo-abuse-list.component.ts;h=b0a655e714d154f53a03b497ab8215cdddb54738;hb=67ed6552b831df66713bac9e672738796128d33f;hp=5e48cf24fd8e220469e69b6deb641b509fd8bb45;hpb=62068f4153cb1e67fe30a7f92947c3f2ec058c73;p=github%2FChocobozzz%2FPeerTube.git diff --git a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts index 5e48cf24f..b0a655e71 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts +++ b/client/src/app/+admin/moderation/video-abuse-list/video-abuse-list.component.ts @@ -1,32 +1,41 @@ -import { Component, OnInit, ViewChild } from '@angular/core' -import { Account } from '@app/shared/account/account.model' -import { Notifier } from '@app/core' import { SortMeta } from 'primeng/api' -import { VideoAbuse, VideoAbuseState } from '../../../../../../shared' -import { RestPagination, RestTable, VideoAbuseService, VideoBlacklistService } from '../../../shared' +import { filter } from 'rxjs/operators' +import { buildVideoEmbed, buildVideoLink } from 'src/assets/player/utils' +import { environment } from 'src/environments/environment' +import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core' +import { DomSanitizer } from '@angular/platform-browser' +import { ActivatedRoute, Params, Router } from '@angular/router' +import { ConfirmService, MarkdownService, Notifier, RestPagination, RestTable } from '@app/core' +import { Account, Actor, DropdownAction, Video, VideoService } from '@app/shared/shared-main' +import { BlocklistService, VideoAbuseService, VideoBlockService } from '@app/shared/shared-moderation' import { I18n } from '@ngx-translate/i18n-polyfill' -import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' -import { ConfirmService } from '../../../core/index' +import { VideoAbuse, VideoAbuseState } from '@shared/models' import { ModerationCommentModalComponent } from './moderation-comment-modal.component' -import { Video } from '../../../shared/video/video.model' -import { MarkdownService } from '@app/shared/renderer' -import { Actor } from '@app/shared/actor/actor.model' -import { buildVideoLink, buildVideoEmbed } from 'src/assets/player/utils' -import { getAbsoluteAPIUrl } from '@app/shared/misc/utils' -import { DomSanitizer } from '@angular/platform-browser' -import { BlocklistService } from '@app/shared/blocklist' + +export type ProcessedVideoAbuse = VideoAbuse & { + moderationCommentHtml?: string, + reasonHtml?: string + embedHtml?: string + updatedAt?: Date + // override bare server-side definitions with rich client-side definitions + reporterAccount: Account + video: VideoAbuse['video'] & { + channel: VideoAbuse['video']['channel'] & { + ownerAccount: Account + } + } +} @Component({ selector: 'my-video-abuse-list', templateUrl: './video-abuse-list.component.html', - styleUrls: [ '../moderation.component.scss'] + styleUrls: [ '../moderation.component.scss', './video-abuse-list.component.scss' ] }) -export class VideoAbuseListComponent extends RestTable implements OnInit { +export class VideoAbuseListComponent extends RestTable implements OnInit, AfterViewInit { @ViewChild('moderationCommentModal', { static: true }) moderationCommentModal: ModerationCommentModalComponent - videoAbuses: (VideoAbuse & { moderationCommentHtml?: string, reasonHtml?: string })[] = [] + videoAbuses: ProcessedVideoAbuse[] = [] totalRecords = 0 - rowsPerPage = 10 sort: SortMeta = { field: 'createdAt', order: 1 } pagination: RestPagination = { count: this.rowsPerPage, start: 0 } @@ -36,11 +45,14 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { private notifier: Notifier, private videoAbuseService: VideoAbuseService, private blocklistService: BlocklistService, - private videoBlacklistService: VideoBlacklistService, + private videoService: VideoService, + private videoBlocklistService: VideoBlockService, private confirmService: ConfirmService, private i18n: I18n, private markdownRenderer: MarkdownService, - private sanitizer: DomSanitizer + private sanitizer: DomSanitizer, + private route: ActivatedRoute, + private router: Router ) { super() @@ -78,19 +90,100 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { [ { label: this.i18n('Actions for the video'), - isHeader: true + isHeader: true, + isDisplayed: videoAbuse => !videoAbuse.video.deleted + }, + { + label: this.i18n('Block video'), + isDisplayed: videoAbuse => !videoAbuse.video.deleted && !videoAbuse.video.blacklisted, + handler: videoAbuse => { + this.videoBlocklistService.blockVideo(videoAbuse.video.id, undefined, true) + .subscribe( + () => { + this.notifier.success(this.i18n('Video blocked.')) + + this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) + }, + + err => this.notifier.error(err.message) + ) + } }, { - label: this.i18n('Blacklist video'), + label: this.i18n('Unblock video'), + isDisplayed: videoAbuse => !videoAbuse.video.deleted && videoAbuse.video.blacklisted, handler: videoAbuse => { - this.videoBlacklistService.blacklistVideo(videoAbuse.video.id, undefined, true) + this.videoBlocklistService.unblockVideo(videoAbuse.video.id) .subscribe( () => { - this.notifier.success(this.i18n('Video blacklisted.')) + this.notifier.success(this.i18n('Video unblocked.')) this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) }, + err => this.notifier.error(err.message) + ) + } + }, + { + label: this.i18n('Delete video'), + isDisplayed: videoAbuse => !videoAbuse.video.deleted, + handler: async videoAbuse => { + const res = await this.confirmService.confirm( + this.i18n('Do you really want to delete this video?'), + this.i18n('Delete') + ) + if (res === false) return + + this.videoService.removeVideo(videoAbuse.video.id) + .subscribe( + () => { + this.notifier.success(this.i18n('Video deleted.')) + + this.updateVideoAbuseState(videoAbuse, VideoAbuseState.ACCEPTED) + }, + + err => this.notifier.error(err.message) + ) + } + } + ], + [ + { + label: this.i18n('Actions for the reporter'), + isHeader: true + }, + { + label: this.i18n('Mute reporter'), + handler: async videoAbuse => { + const account = videoAbuse.reporterAccount as Account + + this.blocklistService.blockAccountByInstance(account) + .subscribe( + () => { + this.notifier.success( + this.i18n('Account {{nameWithHost}} muted by the instance.', { nameWithHost: account.nameWithHost }) + ) + + account.mutedByInstance = true + }, + + err => this.notifier.error(err.message) + ) + } + }, + { + label: this.i18n('Mute server'), + isDisplayed: videoAbuse => !videoAbuse.reporterAccount.userId, + handler: async videoAbuse => { + this.blocklistService.blockServerByInstance(videoAbuse.reporterAccount.host) + .subscribe( + () => { + this.notifier.success( + this.i18n('Server {{host}} muted by the instance.', { host: videoAbuse.reporterAccount.host }) + ) + }, + err => this.notifier.error(err.message) ) } @@ -101,6 +194,18 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { ngOnInit () { this.initialize() + + this.route.queryParams + .pipe(filter(params => params.search !== undefined && params.search !== null)) + .subscribe(params => { + this.search = params.search + this.setTableFilter(params.search) + this.loadData() + }) + } + + ngAfterViewInit () { + if (this.search) this.setTableFilter(this.search) } getIdentifier () { @@ -115,10 +220,25 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { this.loadData() } - createByString (account: Account) { - return Account.CREATE_BY_STRING(account.name, account.host) + /* Table filter functions */ + onAbuseSearch (event: Event) { + this.onSearch(event) + this.setQueryParams((event.target as HTMLInputElement).value) + } + + setQueryParams (search: string) { + const queryParams: Params = {} + if (search) Object.assign(queryParams, { search }) + this.router.navigate([ '/admin/moderation/video-abuses/list' ], { queryParams }) } + resetTableFilter () { + this.setTableFilter('') + this.setQueryParams('') + this.resetSearch() + } + /* END Table filter functions */ + isVideoAbuseAccepted (videoAbuse: VideoAbuse) { return videoAbuse.state.id === VideoAbuseState.ACCEPTED } @@ -132,12 +252,15 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { } getVideoEmbed (videoAbuse: VideoAbuse) { - const absoluteAPIUrl = 'http://localhost:9000' || getAbsoluteAPIUrl() - const embedUrl = buildVideoLink({ - baseUrl: absoluteAPIUrl + '/videos/embed/' + videoAbuse.video.uuid, - warningTitle: false - }) - return buildVideoEmbed(embedUrl) + return buildVideoEmbed( + buildVideoLink({ + baseUrl: `${environment.embedUrl}/videos/embed/${videoAbuse.video.uuid}`, + title: false, + warningTitle: false, + startTime: videoAbuse.startAt, + stopTime: videoAbuse.endAt + }) + ) } switchToDefaultAvatar ($event: Event) { @@ -165,29 +288,37 @@ export class VideoAbuseListComponent extends RestTable implements OnInit { err => this.notifier.error(err.message) ) - } protected loadData () { - return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort) - .subscribe( - async resultList => { - this.totalRecords = resultList.total + return this.videoAbuseService.getVideoAbuses({ + pagination: this.pagination, + sort: this.sort, + search: this.search + }).subscribe( + async resultList => { + this.totalRecords = resultList.total + const videoAbuses = [] + + for (const abuse of resultList.data) { + Object.assign(abuse, { + reasonHtml: await this.toHtml(abuse.reason), + moderationCommentHtml: await this.toHtml(abuse.moderationComment), + embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse)), + reporterAccount: new Account(abuse.reporterAccount) + }) - this.videoAbuses = resultList.data + if (abuse.video.channel?.ownerAccount) abuse.video.channel.ownerAccount = new Account(abuse.video.channel.ownerAccount) + if (abuse.updatedAt === abuse.createdAt) delete abuse.updatedAt - for (const abuse of this.videoAbuses) { - Object.assign(abuse, { - reasonHtml: await this.toHtml(abuse.reason), - moderationCommentHtml: await this.toHtml(abuse.moderationComment), - embedHtml: this.sanitizer.bypassSecurityTrustHtml(this.getVideoEmbed(abuse)) - }) - } + videoAbuses.push(abuse as ProcessedVideoAbuse) + } - }, + this.videoAbuses = videoAbuses + }, - err => this.notifier.error(err.message) - ) + err => this.notifier.error(err.message) + ) } private toHtml (text: string) {