aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app
diff options
context:
space:
mode:
authorJosh Morel <morel.josh@hotmail.com>2019-04-02 05:26:47 -0400
committerChocobozzz <chocobozzz@cpy.re>2019-04-02 11:26:47 +0200
commit7ccddd7b5250bd25a917a6e77e58b87b9484a2a4 (patch)
treee75dc991369c1768804fefa114eb2a832881087f /client/src/app
parent12fed49ebab0c414713d57ea316b6488ae6bef99 (diff)
downloadPeerTube-7ccddd7b5250bd25a917a6e77e58b87b9484a2a4.tar.gz
PeerTube-7ccddd7b5250bd25a917a6e77e58b87b9484a2a4.tar.zst
PeerTube-7ccddd7b5250bd25a917a6e77e58b87b9484a2a4.zip
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
Diffstat (limited to 'client/src/app')
-rw-r--r--client/src/app/+admin/admin.module.ts8
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html17
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts7
-rw-r--r--client/src/app/+admin/moderation/index.ts1
-rw-r--r--client/src/app/+admin/moderation/moderation.component.html4
-rw-r--r--client/src/app/+admin/moderation/moderation.component.ts11
-rw-r--r--client/src/app/+admin/moderation/moderation.routes.ts17
-rw-r--r--client/src/app/+admin/moderation/video-auto-blacklist-list/index.ts1
-rw-r--r--client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.html49
-rw-r--r--client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.scss94
-rw-r--r--client/src/app/+admin/moderation/video-auto-blacklist-list/video-auto-blacklist-list.component.ts100
-rw-r--r--client/src/app/+admin/moderation/video-blacklist-list/video-blacklist-list.component.ts13
-rw-r--r--client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts3
-rw-r--r--client/src/app/+my-account/my-account-videos/my-account-videos.component.scss1
-rw-r--r--client/src/app/core/server/server.service.ts7
-rw-r--r--client/src/app/shared/users/user-notification.model.ts6
-rw-r--r--client/src/app/shared/users/user-notifications.component.html8
-rw-r--r--client/src/app/shared/video-blacklist/video-blacklist.service.ts51
18 files changed, 381 insertions, 17 deletions
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'
11import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' 11import { JobsListComponent } from './jobs/jobs-list/jobs-list.component'
12import { JobService } from './jobs/shared/job.service' 12import { JobService } from './jobs/shared/job.service'
13import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users' 13import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users'
14import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation' 14import {
15 ModerationCommentModalComponent,
16 VideoAbuseListComponent,
17 VideoBlacklistListComponent,
18 VideoAutoBlacklistListComponent
19} from './moderation'
15import { ModerationComponent } from '@app/+admin/moderation/moderation.component' 20import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
16import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' 21import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component'
17import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service' 22import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service'
@@ -42,6 +47,7 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f
42 ModerationComponent, 47 ModerationComponent,
43 VideoBlacklistListComponent, 48 VideoBlacklistListComponent,
44 VideoAbuseListComponent, 49 VideoAbuseListComponent,
50 VideoAutoBlacklistListComponent,
45 ModerationCommentModalComponent, 51 ModerationCommentModalComponent,
46 InstanceServerBlocklistComponent, 52 InstanceServerBlocklistComponent,
47 InstanceAccountBlocklistComponent, 53 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 @@
161 </ng-container> 161 </ng-container>
162 </ng-container> 162 </ng-container>
163 163
164 <div i18n class="inner-form-title">Auto-blacklist</div>
165
166 <ng-container formGroupName="autoBlacklist">
167 <ng-container formGroupName="videos">
168 <ng-container formGroupName="ofUsers">
169
170 <div class="form-group">
171 <my-peertube-checkbox
172 inputName="autoBlacklistVideosOfUsersEnabled" formControlName="enabled"
173 i18n-labelText labelText="New videos of users automatically blacklisted enabled"
174 ></my-peertube-checkbox>
175 </div>
176
177 </ng-container>
178 </ng-container>
179 </ng-container>
180
164 <div i18n class="inner-form-title">Administrator</div> 181 <div i18n class="inner-form-title">Administrator</div>
165 182
166 <div class="form-group" formGroupName="admin"> 183 <div class="form-group" formGroupName="admin">
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 {
117 threads: this.customConfigValidatorsService.TRANSCODING_THREADS, 117 threads: this.customConfigValidatorsService.TRANSCODING_THREADS,
118 allowAdditionalExtensions: null, 118 allowAdditionalExtensions: null,
119 resolutions: {} 119 resolutions: {}
120 },
121 autoBlacklist: {
122 videos: {
123 ofUsers: {
124 enabled: null
125 }
126 }
120 } 127 }
121 } 128 }
122 129
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 @@
1export * from './video-abuse-list' 1export * from './video-abuse-list'
2export * from './video-auto-blacklist-list'
2export * from './video-blacklist-list' 3export * from './video-blacklist-list'
3export * from './moderation.component' 4export * from './moderation.component'
4export * from './moderation.routes' 5export * 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 @@
4 <div class="admin-sub-nav"> 4 <div class="admin-sub-nav">
5 <a *ngIf="hasVideoAbusesRight()" i18n routerLink="video-abuses/list" routerLinkActive="active">Video abuses</a> 5 <a *ngIf="hasVideoAbusesRight()" i18n routerLink="video-abuses/list" routerLinkActive="active">Video abuses</a>
6 6
7 <a *ngIf="hasVideoBlacklistRight()" i18n routerLink="video-blacklist/list" routerLinkActive="active">Blacklisted videos</a> 7 <a *ngIf="hasVideoBlacklistRight()" i18n routerLink="video-blacklist/list" routerLinkActive="active">{{ autoBlacklistVideosEnabled ? 'Manually blacklisted videos' : 'Blacklisted videos' }}</a>
8
9 <a *ngIf="autoBlacklistVideosEnabled && hasVideoBlacklistRight()" i18n routerLink="video-auto-blacklist/list" routerLinkActive="active">Auto-blacklisted videos</a>
8 10
9 <a *ngIf="hasAccountsBlocklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</a> 11 <a *ngIf="hasAccountsBlocklistRight()" i18n routerLink="blocklist/accounts" routerLinkActive="active">Muted accounts</a>
10 12
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 @@
1import { Component } from '@angular/core' 1import { Component } from '@angular/core'
2import { UserRight } from '../../../../../shared' 2import { UserRight } from '../../../../../shared'
3import { AuthService } from '@app/core/auth/auth.service' 3import { AuthService, ServerService } from '@app/core'
4 4
5@Component({ 5@Component({
6 templateUrl: './moderation.component.html', 6 templateUrl: './moderation.component.html',
7 styleUrls: [ './moderation.component.scss' ] 7 styleUrls: [ './moderation.component.scss' ]
8}) 8})
9export class ModerationComponent { 9export class ModerationComponent {
10 constructor (private auth: AuthService) {} 10 autoBlacklistVideosEnabled: boolean
11
12 constructor (
13 private auth: AuthService,
14 private serverService: ServerService
15 ) {
16 this.autoBlacklistVideosEnabled = this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled
17 }
11 18
12 hasVideoAbusesRight () { 19 hasVideoAbusesRight () {
13 return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) 20 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'
3import { UserRightGuard } from '@app/core' 3import { UserRightGuard } from '@app/core'
4import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list' 4import { VideoAbuseListComponent } from '@app/+admin/moderation/video-abuse-list'
5import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list' 5import { VideoBlacklistListComponent } from '@app/+admin/moderation/video-blacklist-list'
6import { VideoAutoBlacklistListComponent } from '@app/+admin/moderation/video-auto-blacklist-list'
6import { ModerationComponent } from '@app/+admin/moderation/moderation.component' 7import { ModerationComponent } from '@app/+admin/moderation/moderation.component'
7import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist' 8import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } from '@app/+admin/moderation/instance-blocklist'
8 9
@@ -27,6 +28,11 @@ export const ModerationRoutes: Routes = [
27 pathMatch: 'full' 28 pathMatch: 'full'
28 }, 29 },
29 { 30 {
31 path: 'video-auto-blacklist',
32 redirectTo: 'video-auto-blacklist/list',
33 pathMatch: 'full'
34 },
35 {
30 path: 'video-abuses/list', 36 path: 'video-abuses/list',
31 component: VideoAbuseListComponent, 37 component: VideoAbuseListComponent,
32 canActivate: [ UserRightGuard ], 38 canActivate: [ UserRightGuard ],
@@ -38,6 +44,17 @@ export const ModerationRoutes: Routes = [
38 } 44 }
39 }, 45 },
40 { 46 {
47 path: 'video-auto-blacklist/list',
48 component: VideoAutoBlacklistListComponent,
49 canActivate: [ UserRightGuard ],
50 data: {
51 userRight: UserRight.MANAGE_VIDEO_BLACKLIST,
52 meta: {
53 title: 'Auto-blacklisted videos'
54 }
55 }
56 },
57 {
41 path: 'video-blacklist/list', 58 path: 'video-blacklist/list',
42 component: VideoBlacklistListComponent, 59 component: VideoBlacklistListComponent,
43 canActivate: [ UserRightGuard ], 60 canActivate: [ UserRightGuard ],
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 @@
1<div i18n *ngIf="pagination.totalItems === 0">No results.</div>
2<div
3 myInfiniteScroller
4 [pageHeight]="pageHeight"
5 (nearOfTop)="onNearOfTop()"
6 (nearOfBottom)="onNearOfBottom()"
7 (pageChanged)="onPageChanged($event)"
8 class="videos" #videosElement
9>
10 <div *ngFor="let videos of videoPages; let i = index" class="videos-page">
11 <div class="video" *ngFor="let video of videos; let j = index">
12 <div class="checkbox-container">
13 <my-peertube-checkbox [inputName]="'video-check-' + video.id" [(ngModel)]="checkedVideos[video.id]"></my-peertube-checkbox>
14 </div>
15 <my-video-thumbnail [video]="video"></my-video-thumbnail>
16
17 <div class="video-info">
18 <a class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
19 <div>{{ video.account.displayName }}</div>
20 <div>{{ video.publishedAt | myFromNow }}</div>
21 <div><span i18n>Privacy: </span><span>{{ video.privacy.label }}</span></div>
22 <div><span i18n>Sensitve: </span><span> {{ video.nsfw }}</span></div>
23 </div>
24
25 <!-- Display only once -->
26 <div class="action-selection-mode" *ngIf="isInSelectionMode() === true && i === 0 && j === 0">
27 <div class="action-selection-mode-child">
28 <span i18n class="action-button action-button-cancel-selection" (click)="abortSelectionMode()">
29 Cancel
30 </span>
31
32 <span class="action-button action-button-unblacklist-selection" (click)="removeSelectedVideosFromBlacklist()">
33 <my-global-icon iconName="tick"></my-global-icon>
34 <ng-container i18n>Unblacklist</ng-container>
35 </span>
36 </div>
37 </div>
38
39 <div class="video-buttons" *ngIf="isInSelectionMode() === false">
40 <my-button
41 i18n-label
42 label="Unblacklist"
43 icon="tick"
44 (click)="removeVideoFromBlacklist(video)"
45 ></my-button>
46 </div>
47 </div>
48
49</div> \ 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 @@
1@import '_variables';
2@import '_mixins';
3
4.action-selection-mode {
5 width: 194px;
6 display: flex;
7 justify-content: flex-end;
8
9 .action-selection-mode-child {
10 position: fixed;
11
12 .action-button {
13 display: inline-block;
14 }
15
16 .action-button-cancel-selection {
17 @include peertube-button;
18 @include grey-button;
19
20 margin-right: 10px;
21 }
22
23 .action-button-unblacklist-selection {
24 @include peertube-button;
25 @include orange-button;
26 @include button-with-icon(21px);
27
28 my-global-icon {
29 @include apply-svg-color(#fff);
30 }
31 }
32 }
33}
34
35.video {
36 @include row-blocks;
37
38 &:first-child {
39 margin-top: 47px;
40 }
41
42 .checkbox-container {
43 display: flex;
44 align-items: center;
45 margin-right: 20px;
46 margin-left: 12px;
47 }
48
49 my-video-thumbnail {
50 margin-right: 10px;
51 }
52
53 .video-info {
54 flex-grow: 1;
55
56 .video-info-name {
57 @include disable-default-a-behaviour;
58
59 color: var(--mainForegroundColor);
60 display: block;
61 width: fit-content;
62 font-size: 16px;
63 font-weight: $font-semibold;
64 }
65 }
66
67 .video-buttons {
68 min-width: 190px;
69 }
70}
71
72@media screen and (max-width: $small-view) {
73 .video {
74 flex-direction: column;
75 height: auto;
76 text-align: center;
77
78 .video-info-name {
79 margin: auto;
80 }
81
82 input[type=checkbox] {
83 display: none;
84 }
85
86 my-video-thumbnail {
87 margin-right: 0;
88 }
89
90 .video-buttons {
91 margin-top: 10px;
92 }
93 }
94}
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 @@
1import { Component, OnInit, OnDestroy } from '@angular/core'
2import { Location } from '@angular/common'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { Router, ActivatedRoute } from '@angular/router'
5import { AbstractVideoList } from '@app/shared/video/abstract-video-list'
6import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
7import { Notifier, AuthService } from '@app/core'
8import { Video } from '@shared/models'
9import { VideoBlacklistService } from '@app/shared'
10import { immutableAssign } from '@app/shared/misc/utils'
11import { ScreenService } from '@app/shared/misc/screen.service'
12
13@Component({
14 selector: 'my-video-auto-blacklist-list',
15 templateUrl: './video-auto-blacklist-list.component.html',
16 styleUrls: [ './video-auto-blacklist-list.component.scss' ]
17})
18export class VideoAutoBlacklistListComponent extends AbstractVideoList implements OnInit, OnDestroy {
19 titlePage: string
20 currentRoute = '/admin/moderation/video-auto-blacklist/list'
21 checkedVideos: { [ id: number ]: boolean } = {}
22 pagination: ComponentPagination = {
23 currentPage: 1,
24 itemsPerPage: 5,
25 totalItems: null
26 }
27
28 protected baseVideoWidth = -1
29 protected baseVideoHeight = 155
30
31 constructor (
32 protected router: Router,
33 protected route: ActivatedRoute,
34 protected i18n: I18n,
35 protected notifier: Notifier,
36 protected location: Location,
37 protected authService: AuthService,
38 protected screenService: ScreenService,
39 private videoBlacklistService: VideoBlacklistService,
40 ) {
41 super()
42
43 this.titlePage = this.i18n('Auto-blacklisted videos')
44 }
45
46 ngOnInit () {
47 super.ngOnInit()
48 }
49
50 ngOnDestroy () {
51 super.ngOnDestroy()
52 }
53
54 abortSelectionMode () {
55 this.checkedVideos = {}
56 }
57
58 isInSelectionMode () {
59 return Object.keys(this.checkedVideos).some(k => this.checkedVideos[k] === true)
60 }
61
62 getVideosObservable (page: number) {
63 const newPagination = immutableAssign(this.pagination, { currentPage: page })
64
65 return this.videoBlacklistService.getAutoBlacklistedAsVideoList(newPagination)
66 }
67
68 generateSyndicationList () {
69 throw new Error('Method not implemented.')
70 }
71
72 removeVideoFromBlacklist (entry: Video) {
73 this.videoBlacklistService.removeVideoFromBlacklist(entry.id).subscribe(
74 () => {
75 this.notifier.success(this.i18n('Video {{name}} removed from blacklist.', { name: entry.name }))
76 this.reloadVideos()
77 },
78
79 error => this.notifier.error(error.message)
80 )
81 }
82
83 removeSelectedVideosFromBlacklist () {
84 const toReleaseVideosIds = Object.keys(this.checkedVideos)
85 .filter(k => this.checkedVideos[ k ] === true)
86 .map(k => parseInt(k, 10))
87
88 this.videoBlacklistService.removeVideoFromBlacklist(toReleaseVideosIds).subscribe(
89 () => {
90 this.notifier.success(this.i18n('{{num}} videos removed from blacklist.', { num: toReleaseVideosIds.length }))
91
92 this.abortSelectionMode()
93 this.reloadVideos()
94 },
95
96 error => this.notifier.error(error.message)
97 )
98 }
99
100}
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 @@
1import { Component, OnInit } from '@angular/core' 1import { Component, OnInit } from '@angular/core'
2import { SortMeta } from 'primeng/components/common/sortmeta' 2import { SortMeta } from 'primeng/components/common/sortmeta'
3import { Notifier } from '@app/core' 3import { Notifier, ServerService } from '@app/core'
4import { ConfirmService } from '../../../core' 4import { ConfirmService } from '../../../core'
5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared' 5import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
6import { VideoBlacklist } from '../../../../../../shared' 6import { VideoBlacklist, VideoBlacklistType } from '../../../../../../shared'
7import { I18n } from '@ngx-translate/i18n-polyfill' 7import { I18n } from '@ngx-translate/i18n-polyfill'
8import { DropdownAction } from '../../../shared/buttons/action-dropdown.component' 8import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
9import { Video } from '../../../shared/video/video.model' 9import { Video } from '../../../shared/video/video.model'
@@ -20,11 +20,13 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
20 rowsPerPage = 10 20 rowsPerPage = 10
21 sort: SortMeta = { field: 'createdAt', order: 1 } 21 sort: SortMeta = { field: 'createdAt', order: 1 }
22 pagination: RestPagination = { count: this.rowsPerPage, start: 0 } 22 pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
23 listBlacklistTypeFilter: VideoBlacklistType = undefined
23 24
24 videoBlacklistActions: DropdownAction<VideoBlacklist>[] = [] 25 videoBlacklistActions: DropdownAction<VideoBlacklist>[] = []
25 26
26 constructor ( 27 constructor (
27 private notifier: Notifier, 28 private notifier: Notifier,
29 private serverService: ServerService,
28 private confirmService: ConfirmService, 30 private confirmService: ConfirmService,
29 private videoBlacklistService: VideoBlacklistService, 31 private videoBlacklistService: VideoBlacklistService,
30 private markdownRenderer: MarkdownService, 32 private markdownRenderer: MarkdownService,
@@ -32,6 +34,11 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
32 ) { 34 ) {
33 super() 35 super()
34 36
37 // don't filter if auto-blacklist not enabled as this will be only list
38 if (this.serverService.getConfig().autoBlacklist.videos.ofUsers.enabled) {
39 this.listBlacklistTypeFilter = VideoBlacklistType.MANUAL
40 }
41
35 this.videoBlacklistActions = [ 42 this.videoBlacklistActions = [
36 { 43 {
37 label: this.i18n('Unblacklist'), 44 label: this.i18n('Unblacklist'),
@@ -77,7 +84,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
77 } 84 }
78 85
79 protected loadData () { 86 protected loadData () {
80 this.videoBlacklistService.listBlacklist(this.pagination, this.sort) 87 this.videoBlacklistService.listBlacklist(this.pagination, this.sort, this.listBlacklistTypeFilter)
81 .subscribe( 88 .subscribe(
82 async resultList => { 89 async resultList => {
83 this.totalRecords = resultList.total 90 this.totalRecords = resultList.total
diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
index 8d4f2c837..67ddf54da 100644
--- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
+++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts
@@ -31,10 +31,12 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
31 private serverService: ServerService, 31 private serverService: ServerService,
32 private notifier: Notifier 32 private notifier: Notifier
33 ) { 33 ) {
34
34 this.labelNotifications = { 35 this.labelNotifications = {
35 newVideoFromSubscription: this.i18n('New video from your subscriptions'), 36 newVideoFromSubscription: this.i18n('New video from your subscriptions'),
36 newCommentOnMyVideo: this.i18n('New comment on your video'), 37 newCommentOnMyVideo: this.i18n('New comment on your video'),
37 videoAbuseAsModerator: this.i18n('New video abuse'), 38 videoAbuseAsModerator: this.i18n('New video abuse'),
39 videoAutoBlacklistAsModerator: this.i18n('Video auto-blacklisted waiting review'),
38 blacklistOnMyVideo: this.i18n('One of your video is blacklisted/unblacklisted'), 40 blacklistOnMyVideo: this.i18n('One of your video is blacklisted/unblacklisted'),
39 myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'), 41 myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'),
40 myVideoImportFinished: this.i18n('Video import finished'), 42 myVideoImportFinished: this.i18n('Video import finished'),
@@ -46,6 +48,7 @@ export class MyAccountNotificationPreferencesComponent implements OnInit {
46 48
47 this.rightNotifications = { 49 this.rightNotifications = {
48 videoAbuseAsModerator: UserRight.MANAGE_VIDEO_ABUSES, 50 videoAbuseAsModerator: UserRight.MANAGE_VIDEO_ABUSES,
51 videoAutoBlacklistAsModerator: UserRight.MANAGE_VIDEO_BLACKLIST,
49 newUserRegistration: UserRight.MANAGE_USERS 52 newUserRegistration: UserRight.MANAGE_USERS
50 } 53 }
51 54
diff --git a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
index f6b5faa45..d2df6f290 100644
--- a/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
+++ b/client/src/app/+my-account/my-account-videos/my-account-videos.component.scss
@@ -82,6 +82,7 @@
82 } 82 }
83 } 83 }
84 } 84 }
85
85 } 86 }
86 } 87 }
87 88
diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts
index acaca8a01..b0c5d1130 100644
--- a/client/src/app/core/server/server.service.ts
+++ b/client/src/app/core/server/server.service.ts
@@ -98,6 +98,13 @@ export class ServerService {
98 videos: { 98 videos: {
99 intervalDays: 0 99 intervalDays: 0
100 } 100 }
101 },
102 autoBlacklist: {
103 videos: {
104 ofUsers: {
105 enabled: false
106 }
107 }
101 } 108 }
102 } 109 }
103 private videoCategories: Array<VideoConstant<number>> = [] 110 private videoCategories: Array<VideoConstant<number>> = []
diff --git a/client/src/app/shared/users/user-notification.model.ts b/client/src/app/shared/users/user-notification.model.ts
index 097830752..7d0eb5ea2 100644
--- a/client/src/app/shared/users/user-notification.model.ts
+++ b/client/src/app/shared/users/user-notification.model.ts
@@ -54,6 +54,7 @@ export class UserNotification implements UserNotificationServer {
54 videoUrl?: string 54 videoUrl?: string
55 commentUrl?: any[] 55 commentUrl?: any[]
56 videoAbuseUrl?: string 56 videoAbuseUrl?: string
57 videoAutoBlacklistUrl?: string
57 accountUrl?: string 58 accountUrl?: string
58 videoImportIdentifier?: string 59 videoImportIdentifier?: string
59 videoImportUrl?: string 60 videoImportUrl?: string
@@ -107,6 +108,11 @@ export class UserNotification implements UserNotificationServer {
107 this.videoUrl = this.buildVideoUrl(this.videoAbuse.video) 108 this.videoUrl = this.buildVideoUrl(this.videoAbuse.video)
108 break 109 break
109 110
111 case UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS:
112 this.videoAutoBlacklistUrl = '/admin/moderation/video-auto-blacklist/list'
113 this.videoUrl = this.buildVideoUrl(this.video)
114 break
115
110 case UserNotificationType.BLACKLIST_ON_MY_VIDEO: 116 case UserNotificationType.BLACKLIST_ON_MY_VIDEO:
111 this.videoUrl = this.buildVideoUrl(this.videoBlacklist.video) 117 this.videoUrl = this.buildVideoUrl(this.videoBlacklist.video)
112 break 118 break
diff --git a/client/src/app/shared/users/user-notifications.component.html b/client/src/app/shared/users/user-notifications.component.html
index 1c0af1bb0..6d2f2750e 100644
--- a/client/src/app/shared/users/user-notifications.component.html
+++ b/client/src/app/shared/users/user-notifications.component.html
@@ -36,6 +36,14 @@
36 </div> 36 </div>
37 </ng-container> 37 </ng-container>
38 38
39 <ng-container i18n *ngSwitchCase="UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS">
40 <my-global-icon iconName="no"></my-global-icon>
41
42 <div class="message">
43 The recently added video <a (click)="markAsRead(notification)" [routerLink]="notification.videoUrl">{{ notification.video.name }}</a> has been <a (click)="markAsRead(notification)" [routerLink]="notification.videoAutoBlacklistUrl">auto-blacklisted</a>
44 </div>
45 </ng-container>
46
39 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_COMMENT_ON_MY_VIDEO"> 47 <ng-container i18n *ngSwitchCase="UserNotificationType.NEW_COMMENT_ON_MY_VIDEO">
40 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" /> 48 <img alt="" aria-labelledby="avatar" class="avatar" [src]="notification.comment.account.avatarUrl" />
41 49
diff --git a/client/src/app/shared/video-blacklist/video-blacklist.service.ts b/client/src/app/shared/video-blacklist/video-blacklist.service.ts
index 94e46d7c2..a9eab9b6f 100644
--- a/client/src/app/shared/video-blacklist/video-blacklist.service.ts
+++ b/client/src/app/shared/video-blacklist/video-blacklist.service.ts
@@ -1,11 +1,13 @@
1import { catchError, map } from 'rxjs/operators' 1import { catchError, map, concatMap, toArray } from 'rxjs/operators'
2import { HttpClient, HttpParams } from '@angular/common/http' 2import { HttpClient, HttpParams } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { SortMeta } from 'primeng/components/common/sortmeta' 4import { SortMeta } from 'primeng/components/common/sortmeta'
5import { Observable } from 'rxjs' 5import { from as observableFrom, Observable } from 'rxjs'
6import { VideoBlacklist, ResultList } from '../../../../../shared' 6import { VideoBlacklist, VideoBlacklistType, ResultList } from '../../../../../shared'
7import { Video } from '../video/video.model'
7import { environment } from '../../../environments/environment' 8import { environment } from '../../../environments/environment'
8import { RestExtractor, RestPagination, RestService } from '../rest' 9import { RestExtractor, RestPagination, RestService } from '../rest'
10import { ComponentPagination } from '../rest/component-pagination.model'
9 11
10@Injectable() 12@Injectable()
11export class VideoBlacklistService { 13export class VideoBlacklistService {
@@ -17,10 +19,14 @@ export class VideoBlacklistService {
17 private restExtractor: RestExtractor 19 private restExtractor: RestExtractor
18 ) {} 20 ) {}
19 21
20 listBlacklist (pagination: RestPagination, sort: SortMeta): Observable<ResultList<VideoBlacklist>> { 22 listBlacklist (pagination: RestPagination, sort: SortMeta, type?: VideoBlacklistType): Observable<ResultList<VideoBlacklist>> {
21 let params = new HttpParams() 23 let params = new HttpParams()
22 params = this.restService.addRestGetParams(params, pagination, sort) 24 params = this.restService.addRestGetParams(params, pagination, sort)
23 25
26 if (type) {
27 params = params.set('type', type.toString())
28 }
29
24 return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params }) 30 return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params })
25 .pipe( 31 .pipe(
26 map(res => this.restExtractor.convertResultListDateToHuman(res)), 32 map(res => this.restExtractor.convertResultListDateToHuman(res)),
@@ -28,12 +34,37 @@ export class VideoBlacklistService {
28 ) 34 )
29 } 35 }
30 36
31 removeVideoFromBlacklist (videoId: number) { 37 getAutoBlacklistedAsVideoList (videoPagination: ComponentPagination): Observable<{ videos: Video[], totalVideos: number}> {
32 return this.authHttp.delete(VideoBlacklistService.BASE_VIDEOS_URL + videoId + '/blacklist') 38 const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
33 .pipe( 39
34 map(this.restExtractor.extractDataBool), 40 // prioritize first created since waiting longest
35 catchError(res => this.restExtractor.handleError(res)) 41 const AUTO_BLACKLIST_SORT = 'createdAt'
36 ) 42
43 let params = new HttpParams()
44 params = this.restService.addRestGetParams(params, pagination, AUTO_BLACKLIST_SORT)
45
46 params = params.set('type', VideoBlacklistType.AUTO_BEFORE_PUBLISHED.toString())
47
48 return this.authHttp.get<ResultList<VideoBlacklist>>(VideoBlacklistService.BASE_VIDEOS_URL + 'blacklist', { params })
49 .pipe(
50 map(res => {
51 const videos = res.data.map(videoBlacklist => new Video(videoBlacklist.video))
52 const totalVideos = res.total
53 return { videos, totalVideos }
54 }),
55 catchError(res => this.restExtractor.handleError(res))
56 )
57 }
58
59 removeVideoFromBlacklist (videoIdArgs: number | number[]) {
60 const videoIds = Array.isArray(videoIdArgs) ? videoIdArgs : [ videoIdArgs ]
61
62 return observableFrom(videoIds)
63 .pipe(
64 concatMap(id => this.authHttp.delete(VideoBlacklistService.BASE_VIDEOS_URL + id + '/blacklist')),
65 toArray(),
66 catchError(err => this.restExtractor.handleError(err))
67 )
37 } 68 }
38 69
39 blacklistVideo (videoId: number, reason: string, unfederate: boolean) { 70 blacklistVideo (videoId: number, reason: string, unfederate: boolean) {