From 7ccddd7b5250bd25a917a6e77e58b87b9484a2a4 Mon Sep 17 00:00:00 2001 From: Josh Morel Date: Tue, 2 Apr 2019 05:26:47 -0400 Subject: add quarantine videos feature (#1637) * add quarantine videos feature * increase Notification settings test timeout to 20000ms. was completing 7000 locally but timing out after 10000 on travis * fix quarantine video test issues -propagate misspelling -remove skip from server/tests/client.ts * WIP use blacklist for moderator video approval instead of video.quarantine boolean * finish auto-blacklist feature --- client/src/app/+admin/admin.module.ts | 8 +- .../edit-custom-config.component.html | 17 ++++ .../edit-custom-config.component.ts | 7 ++ client/src/app/+admin/moderation/index.ts | 1 + .../+admin/moderation/moderation.component.html | 4 +- .../app/+admin/moderation/moderation.component.ts | 11 ++- .../src/app/+admin/moderation/moderation.routes.ts | 17 ++++ .../moderation/video-auto-blacklist-list/index.ts | 1 + .../video-auto-blacklist-list.component.html | 49 ++++++++++ .../video-auto-blacklist-list.component.scss | 94 +++++++++++++++++++ .../video-auto-blacklist-list.component.ts | 100 +++++++++++++++++++++ .../video-blacklist-list.component.ts | 13 ++- 12 files changed, 315 insertions(+), 7 deletions(-) create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss create mode 100644 client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts (limited to 'client/src/app/+admin') diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index f7f347105..282d59634 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts @@ -11,7 +11,12 @@ import { JobsComponent } from './jobs/job.component' import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' import { JobService } from './jobs/shared/job.service' import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users' -import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation' +import { + ModerationCommentModalComponent, + VideoAbuseListComponent, + VideoBlacklistListComponent, + VideoAutoBlacklistListComponent +} from './moderation' import { ModerationComponent } from '@app/+admin/moderation/moderation.component' import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' @@ -42,6 +47,7 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f ModerationComponent, VideoBlacklistListComponent, VideoAbuseListComponent, + VideoAutoBlacklistListComponent, ModerationCommentModalComponent, InstanceServerBlocklistComponent, InstanceAccountBlocklistComponent, diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index 6b654c67d..00a0d98f8 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html @@ -161,6 +161,23 @@ +
Auto-blacklist
+ + + + + +
+ +
+ +
+
+
+
Administrator
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index 45605e0fe..d8eb55da7 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts @@ -117,6 +117,13 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { threads: this.customConfigValidatorsService.TRANSCODING_THREADS, allowAdditionalExtensions: null, resolutions: {} + }, + autoBlacklist: { + videos: { + ofUsers: { + enabled: null + } + } } } diff --git a/client/src/app/+admin/moderation/index.ts b/client/src/app/+admin/moderation/index.ts index 66e2c6a39..3c683a28c 100644 --- a/client/src/app/+admin/moderation/index.ts +++ b/client/src/app/+admin/moderation/index.ts @@ -1,4 +1,5 @@ export * from './video-abuse-list' +export * from './video-auto-blacklist-list' export * from './video-blacklist-list' export * from './moderation.component' export * from './moderation.routes' diff --git a/client/src/app/+admin/moderation/moderation.component.html b/client/src/app/+admin/moderation/moderation.component.html index 01457936c..b70027957 100644 --- a/client/src/app/+admin/moderation/moderation.component.html +++ b/client/src/app/+admin/moderation/moderation.component.html @@ -4,7 +4,9 @@
Video abuses - Blacklisted videos + {{ autoBlacklistVideosEnabled ? 'Manually blacklisted videos' : 'Blacklisted videos' }} + + Auto-blacklisted videos Muted accounts diff --git a/client/src/app/+admin/moderation/moderation.component.ts b/client/src/app/+admin/moderation/moderation.component.ts index 2b2618933..47154af3f 100644 --- a/client/src/app/+admin/moderation/moderation.component.ts +++ b/client/src/app/+admin/moderation/moderation.component.ts @@ -1,13 +1,20 @@ import { Component } from '@angular/core' import { UserRight } from '../../../../../shared' -import { AuthService } from '@app/core/auth/auth.service' +import { AuthService, ServerService } from '@app/core' @Component({ templateUrl: './moderation.component.html', styleUrls: [ './moderation.component.scss' ] }) export class ModerationComponent { - constructor (private auth: AuthService) {} + autoBlacklistVideosEnabled: boolean + + constructor ( + private auth: AuthService, + private serverService: ServerService + ) { + this.autoBlacklistVideosEnabled = this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled + } hasVideoAbusesRight () { return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) diff --git a/client/src/app/+admin/moderation/moderation.routes.ts b/client/src/app/+admin/moderation/moderation.routes.ts index 6f6dde290..a024f2bee 100644 --- a/client/src/app/+admin/moderation/moderation.routes.ts +++ b/client/src/app/+admin/moderation/moderation.routes.ts @@ -3,6 +3,7 @@ import { UserRight } from '../../../../../shared' import { UserRightGuard } from '@app/core' import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list' +import { VideoAutoBlacklistListComponent } from '@app/+admin/moderation/video-auto-blacklist-list' import { ModerationComponent } from '@app/+admin/moderation/moderation.component' import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' @@ -26,6 +27,11 @@ export const ModerationRoutes: Routes = [ redirectTo: 'video-blacklist/list', pathMatch: 'full' }, + { + path: 'video-auto-blacklist', + redirectTo: 'video-auto-blacklist/list', + pathMatch: 'full' + }, { path: 'video-abuses/list', component: VideoAbuseListComponent, @@ -37,6 +43,17 @@ export const ModerationRoutes: Routes = [ } } }, + { + path: 'video-auto-blacklist/list', + component: VideoAutoBlacklistListComponent, + canActivate: [ UserRightGuard ], + data: { + userRight: UserRight.MANAGE_VIDEO_BLACKLIST, + meta: { + title: 'Auto-blacklisted videos' + } + } + }, { path: 'video-blacklist/list', component: VideoBlacklistListComponent, diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts b/client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts new file mode 100644 index 000000000..e3522f68c --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts @@ -0,0 +1 @@ +export * from './video-auto-blacklist-list.component' diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html new file mode 100644 index 000000000..fe579ffd7 --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html @@ -0,0 +1,49 @@ +
No results.
+
+
+
+
+ +
+ + +
+ {{ video.name }} +
{{ video.account.displayName }}
+
{{ video.publishedAt | myFromNow }}
+
Privacy: {{ video.privacy.label }}
+
Sensitve: {{ video.nsfw }}
+
+ + +
+
+ + Cancel + + + + + Unblacklist + +
+
+ +
+ +
+
+ +
\ No newline at end of file diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss new file mode 100644 index 000000000..a73c17eb9 --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss @@ -0,0 +1,94 @@ +@import '_variables'; +@import '_mixins'; + +.action-selection-mode { + width: 194px; + display: flex; + justify-content: flex-end; + + .action-selection-mode-child { + position: fixed; + + .action-button { + display: inline-block; + } + + .action-button-cancel-selection { + @include peertube-button; + @include grey-button; + + margin-right: 10px; + } + + .action-button-unblacklist-selection { + @include peertube-button; + @include orange-button; + @include button-with-icon(21px); + + my-global-icon { + @include apply-svg-color(#fff); + } + } + } +} + +.video { + @include row-blocks; + + &:first-child { + margin-top: 47px; + } + + .checkbox-container { + display: flex; + align-items: center; + margin-right: 20px; + margin-left: 12px; + } + + my-video-thumbnail { + margin-right: 10px; + } + + .video-info { + flex-grow: 1; + + .video-info-name { + @include disable-default-a-behaviour; + + color: var(--mainForegroundColor); + display: block; + width: fit-content; + font-size: 16px; + font-weight: $font-semibold; + } + } + + .video-buttons { + min-width: 190px; + } +} + +@media screen and (max-width: $small-view) { + .video { + flex-direction: column; + height: auto; + text-align: center; + + .video-info-name { + margin: auto; + } + + input[type=checkbox] { + display: none; + } + + my-video-thumbnail { + margin-right: 0; + } + + .video-buttons { + margin-top: 10px; + } + } +} diff --git a/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts new file mode 100644 index 000000000..b79f574c9 --- /dev/null +++ b/client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts @@ -0,0 +1,100 @@ +import { Component, OnInit, OnDestroy } from '@angular/core' +import { Location } from '@angular/common' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { Router, ActivatedRoute } from '@angular/router' +import { AbstractVideoList } from '@app/shared/video/abstract-video-list' +import { ComponentPagination } from '@app/shared/rest/component-pagination.model' +import { Notifier, AuthService } from '@app/core' +import { Video } from '@shared/models' +import { VideoBlacklistService } from '@app/shared' +import { immutableAssign } from '@app/shared/misc/utils' +import { ScreenService } from '@app/shared/misc/screen.service' + +@Component({ + selector: 'my-video-auto-blacklist-list', + templateUrl: './video-auto-blacklist-list.component.html', + styleUrls: [ './video-auto-blacklist-list.component.scss' ] +}) +export class VideoAutoBlacklistListComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage: string + currentRoute = '/admin/moderation/video-auto-blacklist/list' + checkedVideos: { [ id: number ]: boolean } = {} + pagination: ComponentPagination = { + currentPage: 1, + itemsPerPage: 5, + totalItems: null + } + + protected baseVideoWidth = -1 + protected baseVideoHeight = 155 + + constructor ( + protected router: Router, + protected route: ActivatedRoute, + protected i18n: I18n, + protected notifier: Notifier, + protected location: Location, + protected authService: AuthService, + protected screenService: ScreenService, + private videoBlacklistService: VideoBlacklistService, + ) { + super() + + this.titlePage = this.i18n('Auto-blacklisted videos') + } + + ngOnInit () { + super.ngOnInit() + } + + ngOnDestroy () { + super.ngOnDestroy() + } + + abortSelectionMode () { + this.checkedVideos = {} + } + + isInSelectionMode () { + return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true) + } + + getVideosObservable (page: number) { + const newPagination = immutableAssign(this.pagination, { currentPage: page }) + + return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination) + } + + generateSyndicationList () { + throw new Error('Method not implemented.') + } + + removeVideoFromBlacklist (entry: Video) { + this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe( + () => { + this.notifier.success(this.i18n('Video {{name}} removed from blacklist.', { name: entry.name })) + this.reloadVideos() + }, + + error => this.notifier.error(error.message) + ) + } + + removeSelectedVideosFromBlacklist () { + const toReleaseVideosIds = Object.keys(this.checkedVideos) + .filter(k => this.checkedVideos[ k ] === true) + .map(k => parseInt(k, 10)) + + this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe( + () => { + this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length })) + + this.abortSelectionMode() + this.reloadVideos() + }, + + error => this.notifier.error(error.message) + ) + } + +} diff --git a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts index 5443d816d..f4bce7c48 100644 --- a/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts +++ b/client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core' import { SortMeta } from 'primeng/components/common/sortmeta' -import { Notifier } from '@app/core' +import { Notifier, ServerService } from '@app/core' import { ConfirmService } from '../../../core' import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared' -import { VideoBlacklist } from '../../../../../../shared' +import { VideoBlacklist, VideoBlacklistType } from '../../../../../../shared' import { I18n } from '@ngx-translate/i18n-polyfill' import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' import { Video } from '../../../shared/video/video.model' @@ -20,11 +20,13 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { rowsPerPage = 10 sort: SortMeta = { field: 'createdAt', order: 1 } pagination: RestPagination = { count: this.rowsPerPage, start: 0 } + listBlacklistTypeFilter: VideoBlacklistType = undefined videoBlacklistActions: DropdownAction[] = [] constructor ( private notifier: Notifier, + private serverService: ServerService, private confirmService: ConfirmService, private videoBlacklistService: VideoBlacklistService, private markdownRenderer: MarkdownService, @@ -32,6 +34,11 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { ) { super() + // don't filter if auto-blacklist not enabled as this will be only list + if (this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled) { + this.listBlacklistTypeFilter = VideoBlacklistType.MANUAL + } + this.videoBlacklistActions = [ { label: this.i18n('Unblacklist'), @@ -77,7 +84,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit { } protected loadData () { - this.videoBlacklistService.listBlacklist(this.pagination, this.sort) + this.videoBlacklistService.listBlacklist(this.pagination, this.sort, this.listBlacklistTypeFilter) .subscribe( async resultList => { this.totalRecords = resultList.total -- cgit v1.2.3